Index: ps/trunk/source/gui/CInput.cpp =================================================================== --- ps/trunk/source/gui/CInput.cpp (revision 1517) +++ ps/trunk/source/gui/CInput.cpp (revision 1518) @@ -1,829 +1,829 @@ /* 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_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; - GUI::GetSetting(this, "sprite", sprite); + CGUISpriteInstance *sprite; + GUI::GetSettingPointer(this, "sprite", sprite); - GetGUI()->DrawSprite(sprite, bz, m_CachedActualSize); + GetGUI()->DrawSprite(*sprite, 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/CButton.cpp =================================================================== --- ps/trunk/source/gui/CButton.cpp (revision 1517) +++ ps/trunk/source/gui/CButton.cpp (revision 1518) @@ -1,143 +1,143 @@ /* 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_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; + CGUISpriteInstance *sprite, *sprite_over, *sprite_pressed, *sprite_disabled; - GUI::GetSetting(this, "sprite", sprite); - GUI::GetSetting(this, "sprite-over", sprite_over); - GUI::GetSetting(this, "sprite-pressed", sprite_pressed); - GUI::GetSetting(this, "sprite-disabled", sprite_disabled); + 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); DrawButton(m_CachedActualSize, bz, - sprite, - sprite_over, - sprite_pressed, - sprite_disabled); + *sprite, + *sprite_over, + *sprite_pressed, + *sprite_disabled); CColor color = ChooseColor(); IGUITextOwner::Draw(0, color, m_TextPos, bz+0.1f); } Index: ps/trunk/source/gui/scripting/JSInterface_IGUIObject.cpp =================================================================== --- ps/trunk/source/gui/scripting/JSInterface_IGUIObject.cpp (revision 1517) +++ ps/trunk/source/gui/scripting/JSInterface_IGUIObject.cpp (revision 1518) @@ -1,433 +1,433 @@ // $Id$ #include "precompiled.h" #include "JSInterface_IGUIObject.h" #include "JSInterface_GUITypes.h" #include "ps/StringConvert.h" JSClass JSI_IGUIObject::JSI_class = { "GUIObject", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, JSI_IGUIObject::getProperty, JSI_IGUIObject::setProperty, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, NULL, NULL, NULL, NULL }; JSPropertySpec JSI_IGUIObject::JSI_props[] = { { 0 } }; JSFunctionSpec JSI_IGUIObject::JSI_methods[] = { { "toString", JSI_IGUIObject::toString, 0, 0, 0 }, { "getByName", JSI_IGUIObject::getByName, 1, 0, 0 }, { 0 } }; JSBool JSI_IGUIObject::getProperty(JSContext* cx, JSObject* obj, jsval id, jsval* vp) { CStr propName = JS_GetStringBytes(JS_ValueToString(cx, id)); // Skip some things which are known to be functions rather than properties. // ("constructor" *must* be here, else it'll try to GetSettingType before // the private IGUIObject* has been set (and thus crash). The others are // just for efficiency, and to allow correct reporting of attempts to access // nonexistent properties.) if (propName == "constructor" || propName == "prototype" || propName == "toString" || propName == "getByName" ) return JS_TRUE; IGUIObject* e = (IGUIObject*)JS_GetPrivate(cx, obj); // Handle the "parent" property specially if (propName == "parent") { IGUIObject* parent = e->GetParent(); if (parent) { // If the object isn't parentless, return a new object JSObject* entity = JS_NewObject(cx, &JSI_IGUIObject::JSI_class, NULL, NULL); JS_SetPrivate(cx, entity, parent); *vp = OBJECT_TO_JSVAL(entity); } else { // Return null if there's no parent *vp = JSVAL_NULL; } return JS_TRUE; } // Also handle "name" specially else if (propName == "name") { *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, e->GetName())); return JS_TRUE; } // Handle all other properties else { EGUISettingType Type; if (e->GetSettingType(propName, Type) != PS_OK) { // Potentially a function, but they should have been // individually handled above, so complain about it JS_ReportError(cx, "Invalid GUIObject property '%s'", propName.c_str()); return JS_FALSE; } // (All the cases are in {...} to avoid scoping problems) switch (Type) { case GUIST_bool: { bool value; GUI::GetSetting(e, propName, value); *vp = value ? JSVAL_TRUE : JSVAL_FALSE; break; } case GUIST_int: { int value; GUI::GetSetting(e, propName, value); *vp = INT_TO_JSVAL(value); break; } case GUIST_float: { float value; GUI::GetSetting(e, propName, value); // Create a garbage-collectable double *vp = DOUBLE_TO_JSVAL(JS_NewDouble(cx, value) ); break; } case GUIST_CColor: { CColor colour; GUI::GetSetting(e, propName, colour); JSObject* obj = JS_NewObject(cx, &JSI_GUIColor::JSI_class, NULL, NULL); // Attempt to minimise ugliness through macrosity #define P(x) jsval x = DOUBLE_TO_JSVAL(JS_NewDouble(cx, colour.x)); JS_SetProperty(cx, obj, #x, &x) P(r); P(g); P(b); P(a); #undef P *vp = OBJECT_TO_JSVAL(obj); break; } case GUIST_CClientArea: { CClientArea area; GUI::GetSetting(e, propName, area); JSObject* obj = JS_NewObject(cx, &JSI_GUISize::JSI_class, NULL, NULL); JS_AddRoot(cx, &obj); try { #define P(x, y, z) g_ScriptingHost.SetObjectProperty_Double(obj, #z, area.x.y) P(pixel, left, left); P(pixel, top, top); P(pixel, right, right); P(pixel, bottom, bottom); P(percent, left, rleft); P(percent, top, rtop); P(percent, right, rright); P(percent, bottom, rbottom); #undef P } catch (PSERROR_Scripting_ConversionFailed) { debug_warn("Error creating size object!"); JS_RemoveRoot(cx, &obj); break; } *vp = OBJECT_TO_JSVAL(obj); JS_RemoveRoot(cx, &obj); break; } case GUIST_CGUIString: { CGUIString value; GUI::GetSetting(e, propName, value); JSString* s = StringConvert::wchars_to_jsstring(cx, value.GetRawString().c_str()); *vp = STRING_TO_JSVAL(s); break; } case GUIST_CStr: { CStr value; GUI::GetSetting(e, propName, value); *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, value)); break; } case GUIST_CStrW: { CStrW value; GUI::GetSetting(e, propName, value); *vp = STRING_TO_JSVAL(JS_NewUCStringCopyZ(cx, value.utf16().c_str())); break; } case GUIST_CGUISpriteInstance: { - CGUISpriteInstance value; - GUI::GetSetting(e, propName, value); - *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, value.GetName())); + CGUISpriteInstance *value; + GUI::GetSettingPointer(e, propName, value); + *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, value->GetName())); break; } // TODO Gee: (2004-09-01) EAlign and EVAlign too. default: JS_ReportError(cx, "Setting '%s' uses an unimplemented type", propName.c_str()); debug_warn("This shouldn't happen"); return JS_FALSE; } return JS_TRUE; } } JSBool JSI_IGUIObject::setProperty(JSContext* cx, JSObject* obj, jsval id, jsval* vp) { IGUIObject* e = (IGUIObject*)JS_GetPrivate(cx, obj); CStr propName = g_ScriptingHost.ValueToString(id); if (propName == "name") { CStr propValue = JS_GetStringBytes(JS_ValueToString(cx, *vp)); e->SetName(propValue); return JS_TRUE; } EGUISettingType Type; if (e->GetSettingType(propName, Type) != PS_OK) { JS_ReportError(cx, "Invalid setting '%s'", propName.c_str()); return JS_TRUE; } switch (Type) { case GUIST_CStr: { CStr value (JS_GetStringBytes(JS_ValueToString(cx, *vp))); GUI::SetSetting(e, propName, value); break; } case GUIST_CStrW: { utf16string value (JS_GetStringChars(JS_ValueToString(cx, *vp))); GUI::SetSetting(e, propName, value); break; } case GUIST_CGUISpriteInstance: { CStr value (JS_GetStringBytes(JS_ValueToString(cx, *vp))); GUI::SetSetting(e, propName, value); break; } case GUIST_CGUIString: { std::wstring value; StringConvert::jsstring_to_wstring(JS_ValueToString(cx, *vp), value); CGUIString str; str.SetValue(value); GUI::SetSetting(e, propName, str); break; } case GUIST_int: { int32 value; if (JS_ValueToInt32(cx, *vp, &value) == JS_TRUE) GUI::SetSetting(e, propName, value); else { JS_ReportError(cx, "Cannot convert value to int"); return JS_FALSE; } break; } case GUIST_float: { jsdouble value; if (JS_ValueToNumber(cx, *vp, &value) == JS_TRUE) GUI::SetSetting(e, propName, (float)value); else { JS_ReportError(cx, "Cannot convert value to float"); return JS_FALSE; } break; } case GUIST_bool: { JSBool value; if (JS_ValueToBoolean(cx, *vp, &value) == JS_TRUE) GUI::SetSetting(e, propName, value||0); // ||0 to avoid int-to-bool compiler warnings else { JS_ReportError(cx, "Cannot convert value to bool"); return JS_FALSE; } break; } case GUIST_CClientArea: { if (JSVAL_IS_STRING(*vp)) { if (e->SetSetting(propName, JS_GetStringBytes(JS_ValueToString(cx, *vp))) != PS_OK) { JS_ReportError(cx, "Invalid value for setting '%s'", propName.c_str()); return JS_FALSE; } } else if (JSVAL_IS_OBJECT(*vp) && JS_GetClass(JSVAL_TO_OBJECT(*vp)) == &JSI_GUISize::JSI_class) { CClientArea area; GUI::GetSetting(e, propName, area); JSObject* obj = JSVAL_TO_OBJECT(*vp); #define P(x, y, z) area.x.y = (float)g_ScriptingHost.GetObjectProperty_Double(obj, #z) P(pixel, left, left); P(pixel, top, top); P(pixel, right, right); P(pixel, bottom, bottom); P(percent, left, rleft); P(percent, top, rtop); P(percent, right, rright); P(percent, bottom, rbottom); #undef P GUI::SetSetting(e, propName, area); } else { JS_ReportError(cx, "Size only accepts strings or GUISize objects"); return JS_FALSE; } break; } case GUIST_CColor: { if (JSVAL_IS_STRING(*vp)) { if (e->SetSetting(propName, JS_GetStringBytes(JS_ValueToString(cx, *vp))) != PS_OK) { JS_ReportError(cx, "Invalid value for setting '%s'", propName.c_str()); return JS_FALSE; } } else if (JSVAL_IS_OBJECT(*vp) && JS_GetClass(JSVAL_TO_OBJECT(*vp)) == &JSI_GUIColor::JSI_class) { CColor colour; JSObject* obj = JSVAL_TO_OBJECT(*vp); jsval t; double s; #define PROP(x) JS_GetProperty(cx, obj, #x, &t); \ JS_ValueToNumber(cx, t, &s); \ colour.x = (float)s PROP(r); PROP(g); PROP(b); PROP(a); #undef PROP GUI::SetSetting(e, propName, colour); } else { JS_ReportError(cx, "Color only accepts strings or GUIColor objects"); return JS_FALSE; } break; } // TODO Gee: (2004-09-01) EAlign and EVAlign too. default: JS_ReportError(cx, "Setting '%s' uses an unimplemented type", propName.c_str()); break; } return JS_TRUE; } JSBool JSI_IGUIObject::construct(JSContext* cx, JSObject* obj, unsigned int argc, jsval* argv, jsval* UNUSEDPARAM(rval)) { if (argc == 0) { JS_ReportError(cx, "GUIObject has no default constructor"); return JS_FALSE; } assert(argc == 1); // Store the IGUIObject in the JS object's 'private' area IGUIObject* guiObject = (IGUIObject*)JSVAL_TO_PRIVATE(argv[0]); JS_SetPrivate(cx, obj, guiObject); return JS_TRUE; } JSBool JSI_IGUIObject::getByName(JSContext* cx, JSObject* UNUSEDPARAM(obj), unsigned int argc, jsval* argv, jsval* rval) { assert(argc == 1); CStr objectName = JS_GetStringBytes(JS_ValueToString(cx, argv[0])); IGUIObject* guiObject = g_GUI.FindObjectByName(objectName); if (!guiObject) { // Not found - return null *rval = JSVAL_NULL; return JS_TRUE; } JSObject* entity = JS_NewObject(cx, &JSI_IGUIObject::JSI_class, NULL, NULL); JS_SetPrivate(cx, entity, guiObject); *rval = OBJECT_TO_JSVAL(entity); return JS_TRUE; } void JSI_IGUIObject::init() { g_ScriptingHost.DefineCustomObjectType(&JSI_class, construct, 1, JSI_props, JSI_methods, NULL, NULL); } JSBool JSI_IGUIObject::toString(JSContext* cx, JSObject* obj, uintN UNUSEDPARAM(argc), jsval* UNUSEDPARAM(argv), jsval* rval) { IGUIObject* e = (IGUIObject*)JS_GetPrivate( cx, obj ); char buffer[256]; snprintf(buffer, 256, "[GUIObject: %s]", (const TCHAR*)e->GetName()); buffer[255] = 0; *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, buffer)); return JS_TRUE; } Index: ps/trunk/source/gui/GUIutil.h =================================================================== --- ps/trunk/source/gui/GUIutil.h (revision 1517) +++ ps/trunk/source/gui/GUIutil.h (revision 1518) @@ -1,481 +1,432 @@ /* 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 { // Texture name of icon CStr m_TextureName; // Size CSize m_Size; }; /** * @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) - { - if (pObject == NULL) - return PS_OBJECT_FAIL; - - if (!pObject->SettingExists(Setting)) - return PS_SETTING_FAIL; - - if (!pObject->m_Settings.find(Setting)->second.m_pSetting) - return PS_FAIL; - -#ifndef NDEBUG - CheckType(pObject, Setting); -#endif - - // Get value - Value = *(T*)pObject->m_Settings.find(Setting)->second.m_pSetting; - - return PS_OK; - } + 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) - { - if (pObject == NULL) - return PS_OBJECT_FAIL; - - if (!pObject->SettingExists(Setting)) - return PS_SETTING_FAIL; - -#ifndef NDEBUG - CheckType(pObject, Setting); -#endif - - // Set value - *(T*)pObject->m_Settings[Setting].m_pSetting = Value; - - // - // Some settings needs special attention at change - // - - // If setting was "size", we need to re-cache itself and all children - if (Setting == CStr("size")) - { - RecurseObject(0, pObject, &IGUIObject::UpdateCachedSize); - } - else - if (Setting == CStr("hidden")) - { - // Hiding an object requires us to reset it and all children - QueryResetting(pObject); - //RecurseObject(0, pObject, IGUIObject::ResetStates); - } - - HandleMessage(pObject, SGUIMessage(GUIM_SETTINGS_UPDATED, Setting)); - - return PS_OK; - } + 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 1517) +++ ps/trunk/source/gui/CText.cpp (revision 1518) @@ -1,181 +1,181 @@ /* 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_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; - GUI::GetSetting(this, "sprite", sprite); + CGUISpriteInstance *sprite; + GUI::GetSettingPointer(this, "sprite", sprite); - GetGUI()->DrawSprite(sprite, bz, m_CachedActualSize); + GetGUI()->DrawSprite(*sprite, 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/GUIRenderer.cpp =================================================================== --- ps/trunk/source/gui/GUIRenderer.cpp (revision 1517) +++ ps/trunk/source/gui/GUIRenderer.cpp (revision 1518) @@ -1,210 +1,213 @@ #include "precompiled.h" #include "GUIRenderer.h" #include "lib/ogl.h" #include "lib/res/h_mgr.h" #include "ps/CLogger.h" #define LOG_CATEGORY "gui" using namespace GUIRenderer; // Copyable texture Handle, for use in STL containers where the Handle should // be freed when it's finished with. Handle_rfcnt_tex::Handle_rfcnt_tex() : h(0) { } Handle_rfcnt_tex::Handle_rfcnt_tex(Handle h_) : h(h_) { } Handle_rfcnt_tex::Handle_rfcnt_tex(const Handle_rfcnt_tex& that) { h = that.h; if (h) h_add_ref(h); } Handle_rfcnt_tex::~Handle_rfcnt_tex() { if (h) tex_free(h); } Handle_rfcnt_tex& Handle_rfcnt_tex::operator=(Handle h_) { h = h_; return *this; } // Functions to perform drawing-related actions: void GUIRenderer::UpdateDrawCallCache(DrawCalls &Calls, CStr &SpriteName, CRect &Size, std::map &Sprites) { Calls.clear(); std::map::iterator it (Sprites.find(SpriteName)); if (it == Sprites.end()) { // Sprite not found. Check whether this a special sprite: // stretched:filename.ext // filename.ext // and if so, try to create it as a new sprite. // TODO: Implement this. // Otherwise, just complain and give up: LOG(ERROR, LOG_CATEGORY, "Trying to use a sprite that doesn't exist (\"%s\").", (const char*)SpriteName); return; } Calls.reserve(it->second.m_Images.size()); // Iterate through all the sprite's images std::vector::const_iterator cit; for (cit = it->second.m_Images.begin(); cit != it->second.m_Images.end(); ++cit) { SDrawCall Call; + + CRect real = cit->m_Size.GetClientArea(Size); + Call.m_Vertices = real; + if (cit->m_TextureName.Length()) { Handle h = tex_load(cit->m_TextureName); if (h <= 0) { LOG(ERROR, LOG_CATEGORY, "Error reading texture '%s': %lld", (const char*)cit->m_TextureName, h); return; } int err = tex_upload(h); if (err < 0) { LOG(ERROR, LOG_CATEGORY, "Error uploading texture '%s': %d", (const char*)cit->m_TextureName, err); return; } Call.m_TexHandle = h; int fmt, t_w, t_h; tex_info(h, &t_w, &t_h, &fmt, NULL, NULL); Call.m_EnableBlending = (fmt == GL_RGBA || fmt == GL_BGRA); - CRect real = cit->m_Size.GetClientArea(Size); - // Get the screen position/size of a single tiling of the texture CRect TexSize = cit->m_TextureSize.GetClientArea(real); CRect TexCoords; TexCoords.left = (TexSize.left - real.left) / TexSize.GetWidth(); TexCoords.right = TexCoords.left + real.GetWidth() / TexSize.GetWidth(); // 'Bottom' is actually the top in screen-space (I think), // because the GUI puts (0,0) at the top-left TexCoords.bottom = (TexSize.bottom - real.bottom) / TexSize.GetHeight(); TexCoords.top = TexCoords.bottom + real.GetHeight() / TexSize.GetHeight(); if (cit->m_TexturePlacementInFile != CRect()) { // Save the width/height, because we'll change the values one at a time and need // to be able to use the unchanged width/height float width = TexCoords.GetWidth(), height = TexCoords.GetHeight(); float fTW=(float)t_w, fTH=(float)t_h; // notice left done after right, so that left is still unchanged, that is important. TexCoords.right = TexCoords.left + width * cit->m_TexturePlacementInFile.right/fTW; TexCoords.left += width * cit->m_TexturePlacementInFile.left/fTW; TexCoords.bottom = TexCoords.top + height * cit->m_TexturePlacementInFile.bottom/fTH; TexCoords.top += height * cit->m_TexturePlacementInFile.top/fTH; } Call.m_TexCoords = TexCoords; - Call.m_Vertices = real; } else { Call.m_TexHandle = 0; + Call.m_EnableBlending = !(fabs(cit->m_BackColor.a - 1.0f) < 0.0000001f); } Call.m_BackColor = cit->m_BackColor; Call.m_BorderColor = cit->m_Border ? cit->m_BorderColor : CColor(); Call.m_DeltaZ = cit->m_DeltaZ; Calls.push_back(Call); } } void GUIRenderer::Draw(DrawCalls &Calls) { // Iterate through each DrawCall, and execute whatever drawing code is being called for (DrawCalls::const_iterator cit = Calls.begin(); cit != Calls.end(); ++cit) { glColor4f(cit->m_BackColor.r, cit->m_BackColor.g, cit->m_BackColor.b, cit->m_BackColor.a); if (cit->m_EnableBlending) { glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); } - else - { - glDisable(GL_BLEND); - } if (cit->m_TexHandle.h) { // TODO: Handle the GL state in a nicer way glEnable(GL_TEXTURE_2D); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); tex_bind(cit->m_TexHandle.h); glBegin(GL_QUADS); glTexCoord2f(cit->m_TexCoords.right,cit->m_TexCoords.bottom); glVertex3f(cit->m_Vertices.right, cit->m_Vertices.bottom, cit->m_DeltaZ); glTexCoord2f(cit->m_TexCoords.left, cit->m_TexCoords.bottom); glVertex3f(cit->m_Vertices.left, cit->m_Vertices.bottom, cit->m_DeltaZ); glTexCoord2f(cit->m_TexCoords.left, cit->m_TexCoords.top); glVertex3f(cit->m_Vertices.left, cit->m_Vertices.top, cit->m_DeltaZ); glTexCoord2f(cit->m_TexCoords.right,cit->m_TexCoords.top); glVertex3f(cit->m_Vertices.right, cit->m_Vertices.top, cit->m_DeltaZ); glEnd(); - glDisable(GL_TEXTURE_2D); - } else { + glDisable(GL_TEXTURE_2D); glBegin(GL_QUADS); glVertex3f(cit->m_Vertices.right, cit->m_Vertices.bottom, cit->m_DeltaZ); glVertex3f(cit->m_Vertices.left, cit->m_Vertices.bottom, cit->m_DeltaZ); glVertex3f(cit->m_Vertices.left, cit->m_Vertices.top, cit->m_DeltaZ); glVertex3f(cit->m_Vertices.right, cit->m_Vertices.top, cit->m_DeltaZ); glEnd(); if (cit->m_BorderColor != CColor()) { glColor4f(cit->m_BorderColor.r, cit->m_BorderColor.g, cit->m_BorderColor.b, cit->m_BorderColor.a); glBegin(GL_LINE_LOOP); glVertex3f(cit->m_Vertices.left, cit->m_Vertices.top+1.f, cit->m_DeltaZ); glVertex3f(cit->m_Vertices.right-1.f, cit->m_Vertices.top+1.f, cit->m_DeltaZ); glVertex3f(cit->m_Vertices.right-1.f, cit->m_Vertices.bottom, cit->m_DeltaZ); glVertex3f(cit->m_Vertices.left, cit->m_Vertices.bottom, cit->m_DeltaZ); glEnd(); } } + + if (cit->m_EnableBlending) + { + glDisable(GL_BLEND); + } + } } Index: ps/trunk/source/gui/GUItypes.h =================================================================== --- ps/trunk/source/gui/GUItypes.h (nonexistent) +++ ps/trunk/source/gui/GUItypes.h (revision 1518) @@ -0,0 +1,44 @@ +// This file is used by all bits of GUI code that need to repeat some code +// for a variety of types (to avoid duplicating the list of types). Just do +// #define TYPE(T) your_code_involving_T; +// #include "GUItypes.h" +// #undef TYPE +// +// If you want to exclude a particular type, define e.g. GUITYPE_IGNORE_CStr + +// File generated by: +// perl -e"print qq{#ifndef GUITYPE_IGNORE_$_\nTYPE($_)\n#endif\n} for qw(bool int float CColor CClientArea CGUIString CGUISpriteInstance CStr CStrW EAlign EVAlign)" + +#ifndef GUITYPE_IGNORE_bool +TYPE(bool) +#endif +#ifndef GUITYPE_IGNORE_int +TYPE(int) +#endif +#ifndef GUITYPE_IGNORE_float +TYPE(float) +#endif +#ifndef GUITYPE_IGNORE_CColor +TYPE(CColor) +#endif +#ifndef GUITYPE_IGNORE_CClientArea +TYPE(CClientArea) +#endif +#ifndef GUITYPE_IGNORE_CGUIString +TYPE(CGUIString) +#endif +#ifndef GUITYPE_IGNORE_CGUISpriteInstance +TYPE(CGUISpriteInstance) +#endif +#ifndef GUITYPE_IGNORE_CStr +TYPE(CStr) +#endif +#ifndef GUITYPE_IGNORE_CStrW +TYPE(CStrW) +#endif +#ifndef GUITYPE_IGNORE_EAlign +TYPE(EAlign) +#endif +#ifndef GUITYPE_IGNORE_EVAlign +TYPE(EVAlign) +#endif \ No newline at end of file Index: ps/trunk/source/gui/GUIutil.cpp =================================================================== --- ps/trunk/source/gui/GUIutil.cpp (revision 1517) +++ ps/trunk/source/gui/GUIutil.cpp (revision 1518) @@ -1,411 +1,497 @@ /* GUI utilities by Gustav Larsson gee@pyro.nu */ #include "precompiled.h" #include "GUI.h" #include "Parser.h" #include "i18n.h" using namespace std; template <> bool __ParseString(const CStr& Value, bool &Output) { if (Value == "true") Output = true; else if (Value == "false") Output = false; else return false; return true; } template <> bool __ParseString(const CStr& Value, int &Output) { Output = Value.ToInt(); return true; } template <> bool __ParseString(const CStr& Value, float &Output) { Output = Value.ToFloat(); return true; } template <> bool __ParseString(const CStr& Value, CRect &Output) { // Use the parser to parse the values CParser& parser (CParserCache::Get("_$value_$value_$value_$value_")); string str = (const TCHAR*)Value; CParserLine line; line.ParseString(parser, str); if (!line.m_ParseOK) { // Parsing failed return false; } float values[4]; for (int i=0; i<4; ++i) { if (!line.GetArgFloat(i, values[i])) { // Parsing failed return false; } } // Finally the rectangle values Output = CRect(values[0], values[1], values[2], values[3]); return true; } template <> bool __ParseString(const CStr& Value, CClientArea &Output) { return Output.SetClientArea(Value); } template <> bool __ParseString(const CStr& Value, CColor &Output) { // Use the parser to parse the values CParser& parser (CParserCache::Get("_$value_$value_$value_[$value_]")); string str = (const TCHAR*)Value; CParserLine line; line.ParseString(parser, str); if (!line.m_ParseOK) { // TODO Gee: Parsing failed return false; } float values[4]; values[3] = 255.f; // default for (int i=0; i<(int)line.GetArgCount(); ++i) { if (!line.GetArgFloat(i, values[i])) { // Parsing failed return false; } } Output.r = values[0]/255.f; Output.g = values[1]/255.f; Output.b = values[2]/255.f; Output.a = values[3]/255.f; return true; } template <> bool __ParseString(const CStr& Value, CSize &Output) { // Use the parser to parse the values CParser& parser (CParserCache::Get("_$value_$value_")); string str = (const TCHAR*)Value; CParserLine line; line.ParseString(parser, str); if (!line.m_ParseOK) { // Parsing failed return false; } float x, y; // x if (!line.GetArgFloat(0, x)) { // TODO Gee: Parsing failed return false; } // y if (!line.GetArgFloat(1, y)) { // TODO Gee: Parsing failed return false; } Output.cx = x; Output.cy = y; return true; } template <> bool __ParseString(const CStr &Value, EAlign &Output) { if (Value == "left") Output = EAlign_Left; else if (Value == "center") Output = EAlign_Center; else if (Value == "right") Output = EAlign_Right; else return false; return true; } template <> bool __ParseString(const CStr &Value, EVAlign &Output) { if (Value == "top") Output = EVAlign_Top; else if (Value == "center") Output = EVAlign_Center; else if (Value == "bottom") Output = EVAlign_Bottom; else return false; return true; } template <> bool __ParseString(const CStr& Value, CGUIString &Output) { // Translate the Value and retrieve the localised string in // Unicode. Output.SetValue(translate((CStrW)Value)); return true; } template <> +bool __ParseString(const CStr& Value, CStr &Output) +{ + // Do very little. + Output = Value; + return true; +} + +template <> bool __ParseString(const CStr& Value, CStrW &Output) { // Translate the Value and retrieve the localised string in // Unicode. Output = translate((CStrW)Value); return true; } template <> bool __ParseString(const CStr& Value, CGUISpriteInstance &Output) { Output = Value; return true; } //-------------------------------------------------------- // Help Classes/Structs for the GUI implementation //-------------------------------------------------------- CClientArea::CClientArea() : pixel(0.f,0.f,0.f,0.f), percent(0.f,0.f,0.f,0.f) { } CClientArea::CClientArea(const CStr& Value) { SetClientArea(Value); } CRect CClientArea::GetClientArea(const CRect &parent) const { // If it's a 0 0 100% 100% we need no calculations if (percent == CRect(0.f,0.f,100.f,100.f) && pixel == CRect(0.f,0.f,0.f,0.f)) return parent; CRect client; // This should probably be cached and not calculated all the time for every object. client.left = parent.left + (parent.right-parent.left)*percent.left/100.f + pixel.left; client.top = parent.top + (parent.bottom-parent.top)*percent.top/100.f + pixel.top; client.right = parent.left + (parent.right-parent.left)*percent.right/100.f + pixel.right; client.bottom = parent.top + (parent.bottom-parent.top)*percent.bottom/100.f + pixel.bottom; return client; } bool CClientArea::SetClientArea(const CStr& Value) { // Get value in STL string format string _Value = (const TCHAR*)Value; // This might lack incredible speed, but since all XML files // are read at startup, reading 100 client areas will be // negligible in the loading time. // Setup parser to parse the value // One of the for values: // will give outputs like (in argument): // (200) <== no percent, just the first $value // (200) (percent) <== just the percent // (200) (percent) (100) <== percent PLUS pixel // (200) (percent) (-100) <== percent MINUS pixel // (200) (percent) (100) (-100) <== Both PLUS and MINUS are used, INVALID /* string one_value = "_[-_$arg(_minus)]$value[$arg(percent)%_[+_$value]_[-_$arg(_minus)$value]_]"; string four_values = one_value + "$arg(delim)" + one_value + "$arg(delim)" + one_value + "$arg(delim)" + one_value + "$arg(delim)_"; // it's easier to just end with another delimiter */ const char* four_values = "_[-_$arg(_minus)]$value[$arg(percent)%_[+_$value]_[-_$arg(_minus)$value]_]" "$arg(delim)" "_[-_$arg(_minus)]$value[$arg(percent)%_[+_$value]_[-_$arg(_minus)$value]_]" "$arg(delim)" "_[-_$arg(_minus)]$value[$arg(percent)%_[+_$value]_[-_$arg(_minus)$value]_]" "$arg(delim)" "_[-_$arg(_minus)]$value[$arg(percent)%_[+_$value]_[-_$arg(_minus)$value]_]" "$arg(delim)" "_"; CParser& parser (CParserCache::Get(four_values)); CParserLine line; line.ParseString(parser, _Value); if (!line.m_ParseOK) return false; int arg_count[4]; // argument counts for the four values int arg_start[4] = {0,0,0,0}; // location of first argument, [0] is alwasy 0 // Divide into the four piles (delimiter is an argument named "delim") for (int i=0, valuenr=0; i<(int)line.GetArgCount(); ++i) { string str; line.GetArgString(i, str); if (str == "delim") { if (valuenr==0) { arg_count[0] = i; arg_start[1] = i+1; } else { if (valuenr!=3) { assert(valuenr <= 2); arg_start[valuenr+1] = i+1; arg_count[valuenr] = arg_start[valuenr+1] - arg_start[valuenr] - 1; } else arg_count[3] = (int)line.GetArgCount() - arg_start[valuenr] - 1; } ++valuenr; } } // Iterate argument // This is the scheme: // 1 argument = Just pixel value // 2 arguments = Just percent value // 3 arguments = percent and pixel // 4 arguments = INVALID // Default to 0 float values[4][2] = {{0.f,0.f},{0.f,0.f},{0.f,0.f},{0.f,0.f}}; for (int v=0; v<4; ++v) { if (arg_count[v] == 1) { string str; line.GetArgString(arg_start[v], str); if (!line.GetArgFloat(arg_start[v], values[v][1])) return false; } else if (arg_count[v] == 2) { if (!line.GetArgFloat(arg_start[v], values[v][0])) return false; } else if (arg_count[v] == 3) { if (!line.GetArgFloat(arg_start[v], values[v][0]) || !line.GetArgFloat(arg_start[v]+2, values[v][1])) return false; } else return false; } // Now store the values[][] in the right place pixel.left = values[0][1]; pixel.top = values[1][1]; pixel.right = values[2][1]; pixel.bottom = values[3][1]; percent.left = values[0][0]; percent.top = values[1][0]; percent.right = values[2][0]; percent.bottom = values[3][0]; return true; } //-------------------------------------------------------- // Utilities implementation //-------------------------------------------------------- IGUIObject * CInternalCGUIAccessorBase::GetObjectPointer(CGUI &GUIinstance, const CStr& Object) { // if (!GUIinstance.ObjectExists(Object)) // return NULL; return GUIinstance.m_pAllObjects.find(Object)->second; } const IGUIObject * CInternalCGUIAccessorBase::GetObjectPointer(const CGUI &GUIinstance, const CStr& Object) { // if (!GUIinstance.ObjectExists(Object)) // return NULL; return GUIinstance.m_pAllObjects.find(Object)->second; } void CInternalCGUIAccessorBase::QueryResetting(IGUIObject *pObject) { GUI<>::RecurseObject(0, pObject, &IGUIObject::ResetStates); } void CInternalCGUIAccessorBase::HandleMessage(IGUIObject *pObject, const SGUIMessage &message) { pObject->HandleMessage(message); } + #ifndef NDEBUG #define TYPE(T) \ template<> void CheckType(const IGUIObject* obj, const CStr& setting) { \ if (((IGUIObject*)obj)->m_Settings[setting].m_Type != GUIST_##T) \ { \ debug_warn("EXCESSIVELY FATAL ERROR: Inconsistent types in GUI"); \ throw "EXCESSIVELY FATAL ERROR: Inconsistent types in GUI"; /* TODO: better reporting */ \ } \ } - TYPE(bool) - TYPE(int) - TYPE(float) - TYPE(CClientArea) - TYPE(CStr) - TYPE(CStrW) - TYPE(CColor) - TYPE(CGUIString) - TYPE(CGUISpriteInstance) - TYPE(EAlign) - TYPE(EVAlign) + #include "GUItypes.h" #undef TYPE #endif + + +//-------------------------------------------------------------------- + +template +PS_RESULT GUI::GetSettingPointer(const IGUIObject *pObject, const CStr& Setting, T* &Value) +{ + if (pObject == NULL) + return PS_OBJECT_FAIL; + + if (!pObject->SettingExists(Setting)) + return PS_SETTING_FAIL; + + if (!pObject->m_Settings.find(Setting)->second.m_pSetting) + return PS_FAIL; + +#ifndef NDEBUG + CheckType(pObject, Setting); +#endif + + // Get value + Value = (T*)pObject->m_Settings.find(Setting)->second.m_pSetting; + + return PS_OK; +} + +template +PS_RESULT GUI::GetSetting(const IGUIObject *pObject, const CStr& Setting, T &Value) +{ + T* v; + PS_RESULT ret = GetSettingPointer(pObject, Setting, v); + if (ret == PS_OK) + Value = *v; + return ret; +} + +template +PS_RESULT GUI::SetSetting(IGUIObject *pObject, const CStr& Setting, const T &Value) +{ + if (pObject == NULL) + return PS_OBJECT_FAIL; + + if (!pObject->SettingExists(Setting)) + return PS_SETTING_FAIL; + +#ifndef NDEBUG + CheckType(pObject, Setting); +#endif + + // Set value + *(T*)pObject->m_Settings[Setting].m_pSetting = Value; + + // + // Some settings needs special attention at change + // + + // If setting was "size", we need to re-cache itself and all children + if (Setting == CStr("size")) + { + RecurseObject(0, pObject, &IGUIObject::UpdateCachedSize); + } + else + if (Setting == CStr("hidden")) + { + // Hiding an object requires us to reset it and all children + QueryResetting(pObject); + //RecurseObject(0, pObject, IGUIObject::ResetStates); + } + + HandleMessage(pObject, SGUIMessage(GUIM_SETTINGS_UPDATED, Setting)); + + return PS_OK; +} + +#define TYPE(T) \ + template PS_RESULT GUI::GetSettingPointer(const IGUIObject *pObject, const CStr& Setting, T* &Value); \ + template PS_RESULT GUI::GetSetting(const IGUIObject *pObject, const CStr& Setting, T &Value); \ + template PS_RESULT GUI::SetSetting(IGUIObject *pObject, const CStr& Setting, const T &Value); +#define GUITYPE_IGNORE_CGUISpriteInstance +#include "GUItypes.h" + +// Don't instantiate GetSetting - this will cause linker errors if +// you attempt to retrieve a sprite using GetSetting, since that copies the sprite +// and will mess up the caching performed by DrawSprite. You have to use GetSettingPointer +// instead. (This is mainly useful to stop me accidentally using the wrong function.) +template PS_RESULT GUI::GetSettingPointer(const IGUIObject *pObject, const CStr& Setting, CGUISpriteInstance* &Value); +template PS_RESULT GUI::SetSetting(IGUIObject *pObject, const CStr& Setting, const CGUISpriteInstance &Value); Index: ps/trunk/source/gui/IGUIObject.cpp =================================================================== --- ps/trunk/source/gui/IGUIObject.cpp (revision 1517) +++ ps/trunk/source/gui/IGUIObject.cpp (revision 1518) @@ -1,512 +1,483 @@ /* IGUIObject by Gustav Larsson gee@pyro.nu */ #include "precompiled.h" #include "GUI.h" ///// janwas: again, including etiquette? #include "Parser.h" #include ///// #include "gui/scripting/JSInterface_IGUIObject.h" #include "gui/scripting/JSInterface_GUITypes.h" extern int g_xres, g_yres; using namespace std; //------------------------------------------------------------------- // Implementation Macros //------------------------------------------------------------------- //------------------------------------------------------------------- // Constructor / Destructor //------------------------------------------------------------------- IGUIObject::IGUIObject() : m_pGUI(NULL), m_pParent(NULL), m_MouseHovering(false) { AddSetting(GUIST_bool, "enabled"); AddSetting(GUIST_bool, "hidden"); AddSetting(GUIST_CClientArea, "size"); AddSetting(GUIST_CStr, "style"); AddSetting(GUIST_CStr, "hotkey" ); AddSetting(GUIST_float, "z"); AddSetting(GUIST_bool, "absolute"); AddSetting(GUIST_bool, "ghost"); // Setup important defaults GUI::SetSetting(this, "hidden", false); GUI::SetSetting(this, "ghost", false); GUI::SetSetting(this, "enabled", true); GUI::SetSetting(this, "absolute", true); bool hidden=true; GUI::GetSetting(this, "hidden", hidden); } IGUIObject::~IGUIObject() { // delete() needs to know the type of the variable - never delete a void* -#define TYPE(t) case GUIST_##t: delete (t*)it->second.m_pSetting; break +#define TYPE(t) case GUIST_##t: delete (t*)it->second.m_pSetting; break; map::iterator it; for (it = m_Settings.begin(); it != m_Settings.end(); ++it) switch (it->second.m_Type) { - TYPE(bool); - TYPE(int); - TYPE(float); - TYPE(CColor); - TYPE(CClientArea); - TYPE(CGUIString); - TYPE(CGUISpriteInstance); - TYPE(CStr); - TYPE(CStrW); - TYPE(EAlign); - TYPE(EVAlign); + #include "GUItypes.h" default: debug_warn("Invalid setting type"); } #undef TYPE } //------------------------------------------------------------------- // Functions //------------------------------------------------------------------- void IGUIObject::AddChild(IGUIObject *pChild) { // // assert(pChild); pChild->SetParent(this); m_Children.push_back(pChild); // If this (not the child) object is already attached // to a CGUI, it pGUI pointer will be non-null. // This will mean we'll have to check if we're using // names already used. if (pChild->GetGUI()) { try { // Atomic function, if it fails it won't // have changed anything //UpdateObjects(); pChild->GetGUI()->UpdateObjects(); } catch (PS_RESULT e) { // If anything went wrong, reverse what we did and throw // an exception telling it never added a child m_Children.erase( m_Children.end()-1 ); // We'll throw the same exception for easier // error handling throw e; } } // else do nothing } void IGUIObject::AddToPointersMap(map_pObjects &ObjectMap) { // Just don't do anything about the top node if (m_pParent == NULL) return; // Now actually add this one // notice we won't add it if it's doesn't have any parent // (i.e. being the base object) if (m_Name == string()) { throw PS_NEEDS_NAME; } if (ObjectMap.count(m_Name) > 0) { throw PS_NAME_AMBIGUITY; } else { ObjectMap[m_Name] = this; } } void IGUIObject::Destroy() { // Is there anything besides the children to destroy? } // Notice if using this, the naming convention of GUIST_ should be strict. -#define CASE_TYPE(type) \ +#define TYPE(type) \ case GUIST_##type: \ m_Settings[Name].m_pSetting = new type(); \ break; void IGUIObject::AddSetting(const EGUISettingType &Type, const CStr& Name) { // Is name already taken? if (m_Settings.count(Name) >= 1) return; // Construct, and set type m_Settings[Name].m_Type = Type; switch (Type) { // Construct the setting. - CASE_TYPE(bool) - CASE_TYPE(int) - CASE_TYPE(float) - CASE_TYPE(CClientArea) - CASE_TYPE(CStr) - CASE_TYPE(CStrW) - CASE_TYPE(CColor) - CASE_TYPE(CGUIString) - CASE_TYPE(CGUISpriteInstance) - CASE_TYPE(EAlign) - CASE_TYPE(EVAlign) + #include "GUItypes.h" default: debug_warn("IGUIObject::AddSetting failed, type not recognized!"); break; } } +#undef TYPE + bool IGUIObject::MouseOver() { if(!GetGUI()) throw PS_NEEDS_PGUI; return m_CachedActualSize.PointInside(GetMousePos()); } CPos IGUIObject::GetMousePos() const { return ((GetGUI())?(GetGUI()->m_MousePos):CPos()); } void IGUIObject::UpdateMouseOver(IGUIObject * const &pMouseOver) { // Check if this is the object being hovered. if (pMouseOver == this) { if (!m_MouseHovering) { // It wasn't hovering, so that must mean it just entered HandleMessage(GUIM_MOUSE_ENTER); ScriptEvent("mouseenter"); } // Either way, set to true m_MouseHovering = true; // call mouse over HandleMessage(GUIM_MOUSE_OVER); ScriptEvent("mouseover"); } else // Some other object (or none) is hovered { if (m_MouseHovering) { m_MouseHovering = false; HandleMessage(GUIM_MOUSE_LEAVE); ScriptEvent("mouseleave"); } } } bool IGUIObject::SettingExists(const CStr& Setting) const { // Because GetOffsets will direct dynamically defined // classes with polymorphism to respective ms_SettingsInfo // we need to make no further updates on this function // in derived classes. //return (GetSettingsInfo().count(Setting) >= 1); return (m_Settings.count(Setting) >= 1); } -#define ADD_TYPE(type) \ +#define TYPE(type) \ else \ if (set.m_Type == GUIST_##type) \ { \ type _Value; \ if (!GUI::ParseString(Value, _Value)) \ return PS_FAIL; \ \ GUI::SetSetting(this, Setting, _Value); \ } PS_RESULT IGUIObject::SetSetting(const CStr& Setting, const CStr& Value) { if (!SettingExists(Setting)) { return PS_FAIL; } // Get setting SGUISetting set = m_Settings[Setting]; - if (set.m_Type == GUIST_CStr) - { - GUI::SetSetting(this, Setting, Value); - } - ADD_TYPE(CStrW) - ADD_TYPE(bool) - ADD_TYPE(float) - ADD_TYPE(int) - ADD_TYPE(CColor) - ADD_TYPE(CClientArea) - ADD_TYPE(CGUIString) - ADD_TYPE(CGUISpriteInstance) - ADD_TYPE(EAlign) - ADD_TYPE(EVAlign) + if (0); + // else... +#include "GUItypes.h" else { return PS_FAIL; } return PS_OK; } -#undef ADD_TYPE +#undef TYPE PS_RESULT IGUIObject::GetSettingType(const CStr& Setting, EGUISettingType &Type) const { if (!SettingExists(Setting)) return PS_SETTING_FAIL; if (m_Settings.find(Setting) == m_Settings.end()) return PS_FAIL; Type = m_Settings.find(Setting)->second.m_Type; return PS_OK; } void IGUIObject::ChooseMouseOverAndClosest(IGUIObject* &pObject) { if (MouseOver()) { // Check if we've got competition at all if (pObject == NULL) { pObject = this; return; } // Or if it's closer if (GetBufferedZ() >= pObject->GetBufferedZ()) { pObject = this; return; } } } IGUIObject *IGUIObject::GetParent() const { // Important, we're not using GetParent() for these // checks, that could screw it up if (m_pParent) { if (m_pParent->m_pParent == NULL) return NULL; } return m_pParent; } void IGUIObject::UpdateCachedSize() { bool absolute; GUI::GetSetting(this, "absolute", absolute); CClientArea ca; GUI::GetSetting(this, "size", ca); // If absolute="false" and the object has got a parent, // use its cached size instead of the screen. Notice // it must have just been cached for it to work. if (absolute == false && m_pParent) m_CachedActualSize = ca.GetClientArea(m_pParent->m_CachedActualSize); else m_CachedActualSize = ca.GetClientArea(CRect(0.f, 0.f, (float)g_xres, (float)g_yres)); } void IGUIObject::LoadStyle(CGUI &GUIinstance, const CStr& StyleName) { // Fetch style if (GUIinstance.m_Styles.count(StyleName)==1) { LoadStyle(GUIinstance.m_Styles[StyleName]); } else { debug_warn("IGUIObject::LoadStyle failed"); } } void IGUIObject::LoadStyle(const SGUIStyle &Style) { // Iterate settings, it won't be able to set them all probably, but that doesn't matter std::map::const_iterator cit; for (cit = Style.m_SettingsDefaults.begin(); cit != Style.m_SettingsDefaults.end(); ++cit) { // Try set setting in object SetSetting(cit->first, cit->second); // It doesn't matter if it fail, it's not suppose to be able to set every setting. // since it's generic. // The beauty with styles is that it can contain more settings // than exists for the objects using it. So if the SetSetting // fails, don't care. } } float IGUIObject::GetBufferedZ() const { bool absolute; GUI::GetSetting(this, "absolute", absolute); float Z; GUI::GetSetting(this, "z", Z); if (absolute) return Z; else { if (GetParent()) return GetParent()->GetBufferedZ() + Z; else { debug_warn("IGUIObject::LoadStyle failed"); // TODO Gee: Error, no object should be relative without a parent! return Z; } } } // TODO Gee: keep this function and all??? void IGUIObject::CheckSettingsValidity() { bool hidden; GUI::GetSetting(this, "hidden", hidden); // If we hide an object, reset many of its parts if (hidden) { // Simulate that no object is hovered for this object and all its children // why? because it's try { GUI::RecurseObject(0, this, &IGUIObject::UpdateMouseOver, NULL); } catch (...) {} } try { // Send message to itself HandleMessage(GUIM_SETTINGS_UPDATED); ScriptEvent("update"); } catch (...) { } } void IGUIObject::RegisterScriptHandler(const CStr& Action, const CStr& Code, CGUI* pGUI) { const int paramCount = 1; const char* paramNames[paramCount] = { "mouse" }; // Location to report errors from CStr CodeName = GetName()+" "+Action; JSFunction* func = JS_CompileFunction(g_ScriptingHost.getContext(), pGUI->m_ScriptObject, NULL, paramCount, paramNames, (const char*)Code, Code.Length(), CodeName, 0); m_ScriptHandlers[Action] = func; } void IGUIObject::ScriptEvent(const CStr& Action) { map::iterator it = m_ScriptHandlers.find(Action); if (it == m_ScriptHandlers.end()) return; // PRIVATE_TO_JSVAL assumes two-byte alignment, // so make sure that's always true assert(! ((jsval)this & JSVAL_INT)); // The IGUIObject needs to be stored inside the script's object jsval guiObject = PRIVATE_TO_JSVAL(this); // Make a 'this', allowing access to the IGUIObject JSObject* jsGuiObject = JS_ConstructObjectWithArguments(g_ScriptingHost.getContext(), &JSI_IGUIObject::JSI_class, NULL, m_pGUI->m_ScriptObject, 1, &guiObject); // assert(jsGuiObject); // TODO (URGENT): Work out why this fails // Prevent it from being garbage-collected before // it's passed into the function JS_AddRoot(g_ScriptingHost.getContext(), &jsGuiObject); // Set up the 'mouse' parameter jsval mouseParams[3]; mouseParams[0] = INT_TO_JSVAL(m_pGUI->m_MousePos.x); mouseParams[1] = INT_TO_JSVAL(m_pGUI->m_MousePos.y); mouseParams[2] = INT_TO_JSVAL(m_pGUI->m_MouseButtons); JSObject* mouseObj = JS_ConstructObjectWithArguments(g_ScriptingHost.getContext(), &JSI_GUIMouse::JSI_class, NULL, m_pGUI->m_ScriptObject, 3, mouseParams); assert(mouseObj); // TODO need better error handling // Don't garbage collect the mouse JS_AddRoot(g_ScriptingHost.getContext(), &mouseObj); const int paramCount = 1; jsval paramData[paramCount]; paramData[0] = OBJECT_TO_JSVAL(mouseObj); jsval result; JSBool ok = JS_CallFunction(g_ScriptingHost.getContext(), jsGuiObject, it->second, 1, paramData, &result); if (!ok) { JS_ReportError(g_ScriptingHost.getContext(), "Errors executing script action \"%s\"", Action.c_str()); } // Allow the temporary parameters to be garbage-collected JS_RemoveRoot(g_ScriptingHost.getContext(), &mouseObj); JS_RemoveRoot(g_ScriptingHost.getContext(), &jsGuiObject); } CStr IGUIObject::GetPresentableName() const { // __internal(), must be at least 13 letters to be able to be // an internal name if (m_Name.Length() <= 12) return m_Name; if (m_Name.GetSubstring(0, 10) == CStr("__internal")) return CStr("[unnamed object]"); else return m_Name; } void IGUIObject::SetFocus() { GetGUI()->m_FocusedObject = this; } bool IGUIObject::IsFocused() const { return GetGUI()->m_FocusedObject == this; } Index: ps/trunk/source/gui/CGUIScrollBarVertical.cpp =================================================================== --- ps/trunk/source/gui/CGUIScrollBarVertical.cpp (revision 1517) +++ ps/trunk/source/gui/CGUIScrollBarVertical.cpp (revision 1518) @@ -1,190 +1,190 @@ /* IGUIScrollBar by Gustav Larsson gee@pyro.nu */ #include "precompiled.h" #include "GUI.h" using namespace std; CGUIScrollBarVertical::CGUIScrollBarVertical() { } CGUIScrollBarVertical::~CGUIScrollBarVertical() { } void CGUIScrollBarVertical::SetPosFromMousePos(const CPos &mouse) { if (!GetStyle()) return; m_Pos = (m_PosWhenPressed + m_ScrollRange*(mouse.y-m_BarPressedAtPos.y)/(m_Length-GetStyle()->m_Width*2)); } void CGUIScrollBarVertical::Draw() { if (!GetStyle()) { // TODO Gee: Report in error log return; } if (GetGUI()) { CRect outline = GetOuterRect(); // Draw background GetGUI()->DrawSprite(GetStyle()->m_SpriteBackVertical, m_Z+0.1f, CRect( outline.left, outline.top+(m_UseEdgeButtons?GetStyle()->m_Width:0), outline.right, outline.bottom-(m_UseEdgeButtons?GetStyle()->m_Width:0)) ); if (m_UseEdgeButtons) { // Get Appropriate sprites - CGUISpriteInstance button_top, button_bottom; + CGUISpriteInstance *button_top, *button_bottom; // figure out what sprite to use for top button if (m_ButtonMinusHovered) { if (m_ButtonMinusPressed) - button_top = GUI<>::FallBackSprite(GetStyle()->m_SpriteButtonTopPressed, GetStyle()->m_SpriteButtonTop); + button_top = &GUI<>::FallBackSprite(GetStyle()->m_SpriteButtonTopPressed, GetStyle()->m_SpriteButtonTop); else - button_top = GUI<>::FallBackSprite(GetStyle()->m_SpriteButtonTopOver, GetStyle()->m_SpriteButtonTop); + button_top = &GUI<>::FallBackSprite(GetStyle()->m_SpriteButtonTopOver, GetStyle()->m_SpriteButtonTop); } - else button_top = GetStyle()->m_SpriteButtonTop; + else button_top = &GetStyle()->m_SpriteButtonTop; // figure out what sprite to use for top button if (m_ButtonPlusHovered) { if (m_ButtonPlusPressed) - button_bottom = GUI<>::FallBackSprite(GetStyle()->m_SpriteButtonBottomPressed, GetStyle()->m_SpriteButtonBottom); + button_bottom = &GUI<>::FallBackSprite(GetStyle()->m_SpriteButtonBottomPressed, GetStyle()->m_SpriteButtonBottom); else - button_bottom = GUI<>::FallBackSprite(GetStyle()->m_SpriteButtonBottomOver, GetStyle()->m_SpriteButtonBottom); + button_bottom = &GUI<>::FallBackSprite(GetStyle()->m_SpriteButtonBottomOver, GetStyle()->m_SpriteButtonBottom); } - else button_bottom = GetStyle()->m_SpriteButtonBottom; + else button_bottom = &GetStyle()->m_SpriteButtonBottom; // Draw top button - GetGUI()->DrawSprite(button_top, + GetGUI()->DrawSprite(*button_top, m_Z+0.2f, CRect(outline.left, outline.top, outline.right, outline.top+GetStyle()->m_Width) ); // Draw bottom button - GetGUI()->DrawSprite(button_bottom, + GetGUI()->DrawSprite(*button_bottom, m_Z+0.2f, CRect(outline.left, outline.bottom-GetStyle()->m_Width, outline.right, outline.bottom) ); } // Draw bar if (m_BarPressed) GetGUI()->DrawSprite(GUI<>::FallBackSprite(GetStyle()->m_SpriteBarVerticalPressed, GetStyle()->m_SpriteBarVertical), m_Z+0.2f, GetBarRect()); else if (m_BarHovered) GetGUI()->DrawSprite(GUI<>::FallBackSprite(GetStyle()->m_SpriteBarVerticalOver, GetStyle()->m_SpriteBarVertical), m_Z+0.2f, GetBarRect()); else GetGUI()->DrawSprite(GetStyle()->m_SpriteBarVertical, m_Z+0.2f, GetBarRect()); } } void CGUIScrollBarVertical::HandleMessage(const SGUIMessage &Message) { IGUIScrollBar::HandleMessage(Message); } CRect CGUIScrollBarVertical::GetBarRect() const { CRect ret; if (!GetStyle()) return ret; float size; float from, to; // is edge buttons used? if (m_UseEdgeButtons) { size = (m_Length-GetStyle()->m_Width*2.f)*m_BarSize; if (size < GetStyle()->m_MinimumBarSize) size = GetStyle()->m_MinimumBarSize; from = m_Y+GetStyle()->m_Width; to = m_Y+m_Length-GetStyle()->m_Width-size; } else { size = m_Length*m_BarSize; if (size < GetStyle()->m_MinimumBarSize) size = GetStyle()->m_MinimumBarSize; from = m_Y; to = m_Y+m_Length-size; } // Setup rectangle ret.top = (from + (to-from)*(m_Pos/(max(1.f, m_ScrollRange - m_ScrollSpace)))); ret.bottom = ret.top+size; ret.right = m_X + ((m_RightAligned)?(0.f):(GetStyle()->m_Width)); ret.left = ret.right - GetStyle()->m_Width; return ret; } CRect CGUIScrollBarVertical::GetOuterRect() const { CRect ret; if (!GetStyle()) return ret; ret.top = m_Y; ret.bottom = m_Y+m_Length; ret.right = m_X + ((m_RightAligned)?(0):(GetStyle()->m_Width)); ret.left = ret.right - GetStyle()->m_Width; return ret; } bool CGUIScrollBarVertical::HoveringButtonMinus(const CPos &mouse) { if (!GetStyle()) return false; float StartX = (m_RightAligned)?(m_X-GetStyle()->m_Width):(m_X); return (mouse.x >= StartX && mouse.x <= StartX + GetStyle()->m_Width && mouse.y >= m_Y && mouse.y <= m_Y + GetStyle()->m_Width); } bool CGUIScrollBarVertical::HoveringButtonPlus(const CPos &mouse) { if (!GetStyle()) return false; float StartX = (m_RightAligned)?(m_X-GetStyle()->m_Width):(m_X); return (mouse.x > StartX && mouse.x < StartX + GetStyle()->m_Width && mouse.y > m_Y + m_Length - GetStyle()->m_Width && mouse.y < m_Y + m_Length); } Index: ps/trunk/source/gui/CImage.cpp =================================================================== --- ps/trunk/source/gui/CImage.cpp (revision 1517) +++ ps/trunk/source/gui/CImage.cpp (revision 1518) @@ -1,38 +1,38 @@ /* CImage by Gustav Larsson gee@pyro.nu */ #include "precompiled.h" #include "GUI.h" #include "CImage.h" #include "ogl.h" using namespace std; //------------------------------------------------------------------- // Constructor / Destructor //------------------------------------------------------------------- CImage::CImage() { AddSetting(GUIST_CGUISpriteInstance, "sprite"); } CImage::~CImage() { } void CImage::Draw() { if (GetGUI()) { float bz = GetBufferedZ(); - CGUISpriteInstance sprite; - GUI::GetSetting(this, "sprite", sprite); + CGUISpriteInstance *sprite; + GUI::GetSettingPointer(this, "sprite", sprite); - GetGUI()->DrawSprite(sprite, bz, m_CachedActualSize); + GetGUI()->DrawSprite(*sprite, bz, m_CachedActualSize); } } Index: ps/trunk/source/gui/CCheckBox.cpp =================================================================== --- ps/trunk/source/gui/CCheckBox.cpp (revision 1517) +++ ps/trunk/source/gui/CCheckBox.cpp (revision 1518) @@ -1,153 +1,153 @@ /* CCheckBox by Gustav Larsson gee@pyro.nu */ #include "precompiled.h" #include "GUI.h" #include "CCheckBox.h" using namespace std; //------------------------------------------------------------------- // Constructor / Destructor //------------------------------------------------------------------- CCheckBox::CCheckBox() { /* bool m_Checked; CStr m_Font; CGUISpriteInstance m_Sprite; CGUISpriteInstance m_SpriteDisabled; CGUISpriteInstance m_SpriteOver; CGUISpriteInstance m_SpritePressed; CGUISpriteInstance m_Sprite2; CGUISpriteInstance m_Sprite2Disabled; CGUISpriteInstance m_Sprite2Over; CGUISpriteInstance m_Sprite2Pressed; int m_SquareSide; EAlign m_TextAlign; CColor m_TextColor; CColor m_TextColorDisabled; CColor m_TextColorOver; CColor m_TextColorPressed; EVAlign m_TextValign; CStr m_ToolTip; CStr m_ToolTipStyle; */ AddSetting(GUIST_CGUIString, "caption"); AddSetting(GUIST_bool, "checked"); AddSetting(GUIST_CGUISpriteInstance,"sprite"); AddSetting(GUIST_CGUISpriteInstance,"sprite-over"); AddSetting(GUIST_CGUISpriteInstance,"sprite-pressed"); AddSetting(GUIST_CGUISpriteInstance,"sprite-disabled"); AddSetting(GUIST_CGUISpriteInstance,"sprite2"); AddSetting(GUIST_CGUISpriteInstance,"sprite2-over"); AddSetting(GUIST_CGUISpriteInstance,"sprite2-pressed"); AddSetting(GUIST_CGUISpriteInstance,"sprite2-disabled"); AddSetting(GUIST_int, "square-side"); // Add text AddText(new SGUIText()); } CCheckBox::~CCheckBox() { } void CCheckBox::SetupText() { if (!GetGUI()) return; assert(m_GeneratedTexts.size()>=1); CStr font; CGUIString caption; //int square_side; GUI::GetSetting(this, "caption", caption); //GUI::GetSetting(this, "square-side", square_side); // TODO Gee: Establish buffer zones // TODO Gee: research if even "default" should be hardcoded. *m_GeneratedTexts[0] = GetGUI()->GenerateText(caption, CStr("default"), m_CachedActualSize.GetWidth()-20, 0); // Set position of text // TODO Gee: Big TODO // m_TextPos.x = m_CachedActualSize.left + 20; // m_TextPos.y = m_CachedActualSize.top; } void CCheckBox::HandleMessage(const SGUIMessage &Message) { // Important IGUIButtonBehavior::HandleMessage(Message); switch (Message.type) { case GUIM_PRESSED: { bool checked; GUI::GetSetting(this, "checked", checked); checked = !checked; GUI::SetSetting(this, "checked", checked); } break; default: break; } } void CCheckBox::Draw() { ////////// Gee: janwas, this is just temp to see it glDisable(GL_TEXTURE_2D); ////////// int square_side; GUI::GetSetting(this, "square-side", square_side); float bz = GetBufferedZ(); // Get square // TODO Gee: edit below when CRect has got "height()" float middle = (m_CachedActualSize.bottom - m_CachedActualSize.top)/2; CRect rect; rect.left = m_CachedActualSize.left + middle - square_side/2; rect.right = rect.left + square_side; rect.top = m_CachedActualSize.top + middle - square_side/2; rect.bottom = rect.top + square_side; bool checked; GUI::GetSetting(this, "checked", checked); - CGUISpriteInstance sprite, sprite_over, sprite_pressed, sprite_disabled; + CGUISpriteInstance *sprite, *sprite_over, *sprite_pressed, *sprite_disabled; if (checked) { - GUI::GetSetting(this, "sprite2", sprite); - GUI::GetSetting(this, "sprite2-over", sprite_over); - GUI::GetSetting(this, "sprite2-pressed", sprite_pressed); - GUI::GetSetting(this, "sprite2-disabled", sprite_disabled); + GUI::GetSettingPointer(this, "sprite2", sprite); + GUI::GetSettingPointer(this, "sprite2-over", sprite_over); + GUI::GetSettingPointer(this, "sprite2-pressed", sprite_pressed); + GUI::GetSettingPointer(this, "sprite2-disabled", sprite_disabled); } else { - GUI::GetSetting(this, "sprite", sprite); - GUI::GetSetting(this, "sprite-over", sprite_over); - GUI::GetSetting(this, "sprite-pressed", sprite_pressed); - GUI::GetSetting(this, "sprite-disabled", sprite_disabled); + 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); } - DrawButton( rect, - bz, - sprite, - sprite_over, - sprite_pressed, - sprite_disabled); + DrawButton(rect, + bz, + *sprite, + *sprite_over, + *sprite_pressed, + *sprite_disabled); CColor color = ChooseColor(); // IGUITextOwner::Draw(0, color, m_TextPos, bz+0.1f); } Index: ps/trunk/source/gui/CProgressBar.cpp =================================================================== --- ps/trunk/source/gui/CProgressBar.cpp (revision 1517) +++ ps/trunk/source/gui/CProgressBar.cpp (revision 1518) @@ -1,75 +1,75 @@ /* CProgressBar by Gustav Larsson gee@pyro.nu */ #include "precompiled.h" #include "GUI.h" #include "CProgressBar.h" #include "ogl.h" using namespace std; //------------------------------------------------------------------- // Constructor / Destructor //------------------------------------------------------------------- CProgressBar::CProgressBar() { AddSetting(GUIST_CGUISpriteInstance, "sprite-background"); AddSetting(GUIST_CGUISpriteInstance, "sprite-bar"); AddSetting(GUIST_float, "caption"); // aka value from 0 to 100 } CProgressBar::~CProgressBar() { } void CProgressBar::HandleMessage(const SGUIMessage &Message) { // Important IGUIObject::HandleMessage(Message); switch (Message.type) { case GUIM_SETTINGS_UPDATED: // Update scroll-bar // TODO Gee: (2004-09-01) Is this really updated each time it should? if (Message.value == CStr("caption")) { float value; GUI::GetSetting(this, "caption", value); if (value > 100.f) GUI::SetSetting(this, "caption", 100.f); else if (value < 0.f) GUI::SetSetting(this, "caption", 0.f); } break; default: break; } } void CProgressBar::Draw() { if (GetGUI()) { float bz = GetBufferedZ(); - CGUISpriteInstance sprite_background, sprite_bar; + CGUISpriteInstance *sprite_background, *sprite_bar; float value; - GUI::GetSetting(this, "sprite-background", sprite_background); - GUI::GetSetting(this, "sprite-bar", sprite_bar); + GUI::GetSettingPointer(this, "sprite-background", sprite_background); + GUI::GetSettingPointer(this, "sprite-bar", sprite_bar); GUI::GetSetting(this, "caption", value); - GetGUI()->DrawSprite(sprite_background, bz, m_CachedActualSize); + GetGUI()->DrawSprite(*sprite_background, bz, m_CachedActualSize); // Get size of bar (notice it is drawn slightly closer, to appear above the background) CRect bar_size(m_CachedActualSize.left, m_CachedActualSize.top, m_CachedActualSize.left+m_CachedActualSize.GetWidth()*(value/100.f), m_CachedActualSize.bottom); - GetGUI()->DrawSprite(sprite_bar, bz+0.01f, bar_size); + GetGUI()->DrawSprite(*sprite_bar, bz+0.01f, bar_size); } }