From 168154b52db1b556300cbc98fd91220aedf58dda Mon Sep 17 00:00:00 2001 From: "jean.chalard" Date: Sun, 28 Feb 2010 12:36:09 +0000 Subject: [PATCH] J : * Add the confirmation screen for bindings. - Add support for asking confirmations before exiting the menu. - Add support for cancelling saving of options. - Add support for requesting focus when the parent allows to yield. * Recenter most options' text. * Change the background color of the selected options (else, there is no way of knowing where is the cursor when both option text and option value are icons). * Change symbol names to their equivalent PSP icon use in wagic. --- projects/mtg/include/GameStateOptions.h | 8 +- projects/mtg/include/WGui.h | 36 +++++- projects/mtg/src/GameStateOptions.cpp | 23 +++- projects/mtg/src/OptionItem.cpp | 42 +++--- projects/mtg/src/TranslateKeys.cpp | 75 +++++++++-- projects/mtg/src/WGui.cpp | 162 ++++++++++++++++++++++-- 6 files changed, 296 insertions(+), 50 deletions(-) diff --git a/projects/mtg/include/GameStateOptions.h b/projects/mtg/include/GameStateOptions.h index 011924ae6..ff7ed1da0 100644 --- a/projects/mtg/include/GameStateOptions.h +++ b/projects/mtg/include/GameStateOptions.h @@ -5,9 +5,6 @@ #include #include "../include/GameState.h" -#define SHOW_OPTIONS 1 -#define SHOW_OPTIONS_MENU 2 - class GameApp; class WGuiTabMenu; class SimpleMenu; @@ -19,6 +16,11 @@ struct KeybGrabber { class GameStateOptions: public GameState, public JGuiListener { private: + enum { + SHOW_OPTIONS, + SHOW_OPTIONS_MENU, + SAVE + }; float timer; bool mReload; KeybGrabber* grabber; diff --git a/projects/mtg/include/WGui.h b/projects/mtg/include/WGui.h index 07c8c8b99..7b066bbf4 100644 --- a/projects/mtg/include/WGui.h +++ b/projects/mtg/include/WGui.h @@ -1,5 +1,6 @@ #ifndef _WGUI_H_ #define _WGUI_H_ +#include class hgeDistortionMesh; class GameStateOptions; @@ -35,19 +36,27 @@ protected: //Complete item interface class WGuiBase: public JGuiListener { public: + typedef enum { + CONFIRM_NEED, // Still needs confirmation + CONFIRM_OK, // Is okay (no need to confirm, or has been confirmed) + CONFIRM_CANCEL, // Is not okay, must cancel save + } CONFIRM_TYPE; + WGuiBase() {}; virtual ~WGuiBase() {}; virtual bool Selectable() {return true;}; virtual bool isModal() {return false;}; virtual bool Visible() {return true;}; - + virtual bool Changed() {return false;}; virtual void confirmChange(bool confirmed) {}; + virtual CONFIRM_TYPE needsConfirm(); + virtual bool yieldFocus(); virtual PIXEL_TYPE getColor(int type); virtual float getMargin(int type) {return 4;}; - - virtual void Entering(JButton key)=0; + + virtual void Entering(JButton key)=0; virtual bool Leaving(JButton key)=0; virtual void Update(float dt)=0; @@ -84,6 +93,8 @@ public: virtual void subBack(WGuiBase * item) {}; virtual bool CheckUserInput(JButton key) {return false;}; + protected: + vector items; }; //This is our base class for concrete items. @@ -176,7 +187,9 @@ public: virtual bool Visible() {return it->Visible();}; virtual bool Changed() {return it->Changed();}; virtual void confirmChange(bool confirmed) {it->confirmChange(confirmed);}; - + virtual CONFIRM_TYPE needsConfirm() { return it->needsConfirm();}; + virtual bool yieldFocus() {return it->yieldFocus();}; + virtual void Entering(JButton key) {it->Entering(key);}; virtual bool Leaving(JButton key) {return it->Leaving(key);}; virtual void Update(float dt) {it->Update(dt);}; @@ -235,6 +248,7 @@ public: WGuiSplit(WGuiBase* _left,WGuiBase* _right); virtual ~WGuiSplit(); + virtual bool yieldFocus(); virtual void Reload(); virtual void Overlay(); virtual void Underlay(); @@ -352,6 +366,7 @@ public: virtual ~WGuiMenu(); WGuiMenu(JButton next, JButton prev, bool mDPad = false, WSyncable * syncme=NULL); + virtual bool yieldFocus(); virtual void Render(); virtual void Reload(); virtual void Update(float dt); @@ -364,6 +379,7 @@ public: virtual bool CheckUserInput(JButton key); WGuiBase * Current(); virtual int getSelected() {return currentItem;}; + virtual void setSelected(vector::iterator& it); virtual bool nextItem(); virtual bool prevItem(); virtual bool isModal(); @@ -376,7 +392,6 @@ protected: virtual bool isButtonDir(JButton key, int dir); //For the DPad override. JButton buttonNext, buttonPrev; bool mDPad; - vector items; int currentItem; JButton held; WSyncable * sync; @@ -494,9 +509,20 @@ class WGuiKeyBinder : public WGuiList { virtual bool CheckUserInput(JButton); virtual void setData(); virtual void Update(float); + virtual void Render(); + virtual CONFIRM_TYPE needsConfirm(); + virtual void ButtonPressed(int controllerId, int controlId); + virtual bool yieldFocus(); protected: GameStateOptions* parent; + SimpleMenu* confirmMenu; bool modal; + CONFIRM_TYPE confirmed; + LocalKeySym confirmingKey; + JButton confirmingButton; + set confirmedKeys; + set confirmedButtons; + string confirmationString; }; #endif diff --git a/projects/mtg/src/GameStateOptions.cpp b/projects/mtg/src/GameStateOptions.cpp index fb66caebf..da70736c2 100644 --- a/projects/mtg/src/GameStateOptions.cpp +++ b/projects/mtg/src/GameStateOptions.cpp @@ -115,6 +115,24 @@ void GameStateOptions::Update(float dt) } else switch(mState){ default: + case SAVE: + switch (optionsTabs->needsConfirm()) + { + case WGuiBase::CONFIRM_CANCEL: + mState = SHOW_OPTIONS; + break; + case WGuiBase::CONFIRM_OK: + optionsTabs->save(); + JSoundSystem::GetInstance()->SetSfxVolume(options[Options::SFXVOLUME].number); + JSoundSystem::GetInstance()->SetMusicVolume(options[Options::MUSICVOLUME].number); + mParent->DoTransition(TRANSITION_FADE,GAME_STATE_MENU); + mState = SHOW_OPTIONS; + break; + case WGuiBase::CONFIRM_NEED: + optionsTabs->yieldFocus(); + break; + } + // Note : No break here : must continue to continue updating the menu elements. case SHOW_OPTIONS: { JGE* j = JGE::GetInstance(); JButton key; @@ -217,10 +235,9 @@ void GameStateOptions::ButtonPressed(int controllerId, int controlId) if(controllerId == -102) switch (controlId){ case 1: - optionsTabs->save(); + mState = SAVE; + break; //Set Audio volume - JSoundSystem::GetInstance()->SetSfxVolume(options[Options::SFXVOLUME].number); - JSoundSystem::GetInstance()->SetMusicVolume(options[Options::MUSICVOLUME].number); case 2: mParent->DoTransition(TRANSITION_FADE,GAME_STATE_MENU); break; diff --git a/projects/mtg/src/OptionItem.cpp b/projects/mtg/src/OptionItem.cpp index dc440922c..3b9df43aa 100644 --- a/projects/mtg/src/OptionItem.cpp +++ b/projects/mtg/src/OptionItem.cpp @@ -21,7 +21,7 @@ void OptionInteger::Render(){ mFont->SetColor(getColor(WGuiColor::TEXT)); JRenderer * renderer = JRenderer::GetInstance(); - mFont->DrawString(_(displayValue).c_str(),x,y); + mFont->DrawString(_(displayValue).c_str(), x + 2, y + 3); char buf[512]; if (maxValue == 1){ if (value) @@ -34,7 +34,7 @@ void OptionInteger::Render(){ else sprintf(buf, "%i", value); } - mFont->DrawString(buf,width -10 ,y,JGETEXT_RIGHT); + mFont->DrawString(buf, width - 5, y + 3, JGETEXT_RIGHT); } OptionInteger::OptionInteger(int _id, string _displayValue, int _maxValue, int _increment, int _defV, string _sDef, int _minValue): OptionItem(_id, _displayValue){ @@ -72,12 +72,12 @@ void OptionSelect::Render(){ mFont->SetColor(getColor(WGuiColor::TEXT)); JRenderer * renderer = JRenderer::GetInstance(); - mFont->DrawString(_(displayValue).c_str(),x,y); + mFont->DrawString(_(displayValue).c_str(), x, y + 2); if (value < selections.size()) - mFont->DrawString(_(selections[value]).c_str(),x+width-10,y,JGETEXT_RIGHT); + mFont->DrawString(_(selections[value]).c_str(), x + width - 10, y + 2, JGETEXT_RIGHT); else - mFont->DrawString(_("Unset").c_str(),x+width-10,y,JGETEXT_RIGHT); + mFont->DrawString(_("Unset").c_str(),x + width - 10, y + 2, JGETEXT_RIGHT); } void OptionSelect::setData(){ @@ -181,10 +181,10 @@ void OptionProfile::Render(){ } mFont->SetColor(getColor(WGuiColor::TEXT_HEADER)); - mFont->DrawString(selections[value].c_str(),pX,pY,JGETEXT_LEFT); + mFont->DrawString(selections[value].c_str(), pX, pY + 2, JGETEXT_LEFT); mFont->SetScale(.8); mFont->SetColor(getColor(WGuiColor::TEXT_BODY)); - mFont->DrawString(preview.c_str(),pX,pY+spacing,JGETEXT_LEFT); + mFont->DrawString(preview.c_str(), pX, pY + spacing + 2, JGETEXT_LEFT); mFont->SetScale(1); } @@ -401,13 +401,13 @@ void OptionTheme::Render(){ renderer->RenderQuad(q,x, y,0,scale,scale); } - mFont->DrawString(buf,x,y); + mFont->DrawString(buf, x + 2, y + 2); if(bChecked && author.size()){ mFont->SetColor(getColor(WGuiColor::TEXT_BODY)); mFont->SetScale(.8); float hi = mFont->GetHeight(); sprintf(buf,_("Artist: %s").c_str(),author.c_str()); - mFont->DrawString(buf,x,y+getHeight()-hi); + mFont->DrawString(buf, x + 2, y + getHeight() - hi); mFont->SetScale(1); } } @@ -447,14 +447,17 @@ void OptionKey::Render() { { const KeyRep& rep = translateKey(from); if (rep.second) - renderer->RenderQuad(rep.second, x + 4, y + 2, 0, 16.0 / rep.second->mHeight, 16.0 / rep.second->mHeight); + renderer->RenderQuad(rep.second, x + 4, y + 3, 0, 16.0 / rep.second->mHeight, 16.0 / rep.second->mHeight); else - mFont->DrawString(rep.first, x + 4, y + 2, JGETEXT_LEFT); + mFont->DrawString(rep.first, x + 4, y + 3, JGETEXT_LEFT); const KeyRep& rep2 = translateKey(to); if (rep2.second) - renderer->RenderQuad(rep2.second, x + 4, y + 2, 0, 16.0 / rep2.second->mHeight, 16.0 / rep2.second->mHeight); + { + float ratio = 16.0 / rep2.second->mHeight; + renderer->RenderQuad(rep2.second, x + width - (ratio * rep2.second->mWidth) - 2, y + 3, 0, ratio, ratio); + } else - mFont->DrawString(rep2.first, width - 4, y + 2, JGETEXT_RIGHT); + mFont->DrawString(rep2.first, width - 4, y + 3, JGETEXT_RIGHT); } } bool OptionKey::CheckUserInput(JButton key) { @@ -468,11 +471,14 @@ bool OptionKey::CheckUserInput(JButton key) { return false; } -static JButton btnList[] = {JGE_BTN_MENU, JGE_BTN_CTRL, JGE_BTN_RIGHT, - JGE_BTN_LEFT, JGE_BTN_UP, JGE_BTN_DOWN, - JGE_BTN_OK, JGE_BTN_CANCEL, JGE_BTN_PRI, - JGE_BTN_SEC, JGE_BTN_PREV, JGE_BTN_NEXT, - JGE_BTN_NONE}; +static const JButton btnList[] = {JGE_BTN_MENU, JGE_BTN_CTRL, JGE_BTN_RIGHT, + JGE_BTN_LEFT, JGE_BTN_UP, JGE_BTN_DOWN, + JGE_BTN_OK, JGE_BTN_CANCEL, JGE_BTN_PRI, + JGE_BTN_SEC, JGE_BTN_PREV, JGE_BTN_NEXT, +#ifdef LINUX + JGE_BTN_FULLSCREEN, +#endif + JGE_BTN_NONE}; void OptionKey::KeyPressed(LocalKeySym key) { from = key; g->UngrabKeyboard(this); diff --git a/projects/mtg/src/TranslateKeys.cpp b/projects/mtg/src/TranslateKeys.cpp index 4115c02d7..61d526425 100644 --- a/projects/mtg/src/TranslateKeys.cpp +++ b/projects/mtg/src/TranslateKeys.cpp @@ -82,19 +82,19 @@ const KeyRep& translateKey(LocalKeySym key) { { if (fattable.end() == fattable.find(PSP_CTRL_SELECT)) { - fattable[PSP_CTRL_SELECT] = make_pair(_("Select"), static_cast(NULL)); - fattable[PSP_CTRL_START] = make_pair(_("Start"), static_cast(NULL)); - fattable[PSP_CTRL_UP] = make_pair(_("Up"), static_cast(NULL)); - fattable[PSP_CTRL_RIGHT] = make_pair(_("Right"), static_cast(NULL)); - fattable[PSP_CTRL_DOWN] = make_pair(_("Down"), static_cast(NULL)); - fattable[PSP_CTRL_LEFT] = make_pair(_("Left"), static_cast(NULL)); - fattable[PSP_CTRL_LTRIGGER] = make_pair(_("Left trigger"), static_cast(NULL)); - fattable[PSP_CTRL_RTRIGGER] = make_pair(_("Right trigger"), static_cast(NULL)); - fattable[PSP_CTRL_TRIANGLE] = make_pair(_("Triangle"), static_cast(NULL)); - fattable[PSP_CTRL_CIRCLE] = make_pair(_("Circle"), static_cast(NULL)); - fattable[PSP_CTRL_CROSS] = make_pair(_("Cross"), static_cast(NULL)); - fattable[PSP_CTRL_SQUARE] = make_pair(_("Square"), static_cast(NULL)); - fattable[PSP_CTRL_HOLD] = make_pair(_("Hold"), static_cast(NULL)); + fattable[PSP_CTRL_SELECT] = make_pair(_("Select"), static_cast(NULL)); + fattable[PSP_CTRL_START] = make_pair(_("Start"), static_cast(NULL)); + fattable[PSP_CTRL_UP] = make_pair(_("Up"), static_cast(NULL)); + fattable[PSP_CTRL_RIGHT] = make_pair(_("Right"), static_cast(NULL)); + fattable[PSP_CTRL_DOWN] = make_pair(_("Down"), static_cast(NULL)); + fattable[PSP_CTRL_LEFT] = make_pair(_("Left"), static_cast(NULL)); + fattable[PSP_CTRL_LTRIGGER] = make_pair(_("Left trigger"), static_cast(NULL)); + fattable[PSP_CTRL_RTRIGGER] = make_pair(_("Right trigger"), static_cast(NULL)); + fattable[PSP_CTRL_TRIANGLE] = make_pair(_("Triangle"), static_cast(NULL)); + fattable[PSP_CTRL_CIRCLE] = make_pair(_("Circle"), static_cast(NULL)); + fattable[PSP_CTRL_CROSS] = make_pair(_("Cross"), static_cast(NULL)); + fattable[PSP_CTRL_SQUARE] = make_pair(_("Square"), static_cast(NULL)); + fattable[PSP_CTRL_HOLD] = make_pair(_("Hold"), static_cast(NULL)); } else { @@ -128,6 +128,7 @@ const KeyRep& translateKey(LocalKeySym key) { #endif const KeyRep& translateKey(JButton key) { + /* { map::iterator res; if ((res = slimtable.find(key)) != slimtable.end()) @@ -152,4 +153,52 @@ const KeyRep& translateKey(JButton key) { slimtable[JGE_BTN_NEXT] = make_pair(_("Open hand/Next item"), static_cast(NULL)); return slimtable[key]; + */ + + map::iterator res; + if ((res = slimtable.find(key)) == slimtable.end()) + { + if (slimtable.end() == slimtable.find(JGE_BTN_CTRL)) + { + slimtable[JGE_BTN_NONE] = make_pair(_("Delete this binding"), static_cast(NULL)); + slimtable[JGE_BTN_CTRL] = make_pair(_("Select"), static_cast(NULL)); + slimtable[JGE_BTN_MENU] = make_pair(_("Start"), static_cast(NULL)); + slimtable[JGE_BTN_UP] = make_pair(_("Up"), static_cast(NULL)); + slimtable[JGE_BTN_RIGHT] = make_pair(_("Right"), static_cast(NULL)); + slimtable[JGE_BTN_DOWN] = make_pair(_("Down"), static_cast(NULL)); + slimtable[JGE_BTN_LEFT] = make_pair(_("Left"), static_cast(NULL)); + slimtable[JGE_BTN_PREV] = make_pair(_("Left trigger"), static_cast(NULL)); + slimtable[JGE_BTN_NEXT] = make_pair(_("Right trigger"), static_cast(NULL)); + slimtable[JGE_BTN_CANCEL] = make_pair(_("Triangle"), static_cast(NULL)); + slimtable[JGE_BTN_OK] = make_pair(_("Circle"), static_cast(NULL)); + slimtable[JGE_BTN_SEC] = make_pair(_("Cross"), static_cast(NULL)); + slimtable[JGE_BTN_PRI] = make_pair(_("Square"), static_cast(NULL)); + slimtable[JGE_BTN_FULLSCREEN] = make_pair(_("Fullscreen"), static_cast(NULL)); + } + else + { + char* str = new char[11]; + sprintf(str, "%d", key); + slimtable[key] = make_pair(str, static_cast(static_cast(NULL))); + } + res = slimtable.find(key); + } + KeyRep& k = res->second; + switch(key) + { + case JGE_BTN_CTRL : k.second = resources.RetrieveQuad("iconspsp.png", (float)2*32, 32, 64, 32, "PSP_CTRL_SELECT", RETRIEVE_NORMAL); break; + case JGE_BTN_MENU : k.second = resources.RetrieveQuad("iconspsp.png", (float)0*32, 32, 64, 32, "PSP_CTRL_START", RETRIEVE_NORMAL); break; + case JGE_BTN_UP : k.second = resources.RetrieveQuad("iconspsp.png", (float)0*32, 0, 32, 32, "PSP_CTRL_UP", RETRIEVE_NORMAL); break; + case JGE_BTN_RIGHT : k.second = resources.RetrieveQuad("iconspsp.png", (float)3*32, 0, 32, 32, "PSP_CTRL_RIGHT", RETRIEVE_NORMAL); break; + case JGE_BTN_DOWN : k.second = resources.RetrieveQuad("iconspsp.png", (float)1*32, 0, 32, 32, "PSP_CTRL_DOWN", RETRIEVE_NORMAL); break; + case JGE_BTN_LEFT : k.second = resources.RetrieveQuad("iconspsp.png", (float)2*32, 0, 32, 32, "PSP_CTRL_LEFT", RETRIEVE_NORMAL); break; + case JGE_BTN_PREV : k.second = resources.RetrieveQuad("iconspsp.png", (float)6*32, 32, 64, 32, "PSP_CTRL_LTRIGGER", RETRIEVE_NORMAL); break; + case JGE_BTN_NEXT : k.second = resources.RetrieveQuad("iconspsp.png", (float)8*32, 32, 64, 32, "PSP_CTRL_RTRIGGER", RETRIEVE_NORMAL); break; + case JGE_BTN_CANCEL : k.second = resources.RetrieveQuad("iconspsp.png", (float)5*32, 0, 32, 32, "PSP_CTRL_TRIANGLE", RETRIEVE_NORMAL); break; + case JGE_BTN_OK : k.second = resources.RetrieveQuad("iconspsp.png", (float)4*32, 0, 32, 32, "PSP_CTRL_CIRCLE", RETRIEVE_NORMAL); break; + case JGE_BTN_SEC : k.second = resources.RetrieveQuad("iconspsp.png", (float)7*32, 0, 32, 32, "PSP_CTRL_CROSS", RETRIEVE_NORMAL); break; + case JGE_BTN_PRI : k.second = resources.RetrieveQuad("iconspsp.png", (float)6*32, 0, 32, 32, "PSP_CTRL_SQUARE", RETRIEVE_NORMAL); break; + default: /* Unknown key : no icon */ ; + } + return k; } diff --git a/projects/mtg/src/WGui.cpp b/projects/mtg/src/WGui.cpp index 06e7ebcd3..5c9e552c8 100644 --- a/projects/mtg/src/WGui.cpp +++ b/projects/mtg/src/WGui.cpp @@ -3,6 +3,8 @@ #include "../include/PlayerData.h" #include "../include/Translate.h" #include "../include/Subtypes.h" +#include "../include/TranslateKeys.h" +#include #include #include @@ -24,7 +26,10 @@ PIXEL_TYPE WGuiBase::getColor(int type){ return ARGB(255,255,255,255); } else - return ARGB(150,50,50,50); + if(hasFocus()) + return ARGB(150,200,200,200); + else + return ARGB(150,50,50,50); } return ARGB(150,50,50,50); } @@ -38,6 +43,21 @@ void WGuiBase::renderBack(WGuiBase * it){ else subBack(it); } +WGuiBase::CONFIRM_TYPE WGuiBase::needsConfirm() { + for (vector::iterator it = items.begin(); it != items.end(); ++it) { + switch((*it)->needsConfirm()) { + case CONFIRM_NEED: return CONFIRM_NEED; + case CONFIRM_CANCEL: return CONFIRM_CANCEL; + case CONFIRM_OK: /* Nothing special : continue iteration */ ; + } + } + return CONFIRM_OK; +} +bool WGuiBase::yieldFocus() { + for (vector::iterator it = items.begin(); it != items.end(); ++it) + if ((*it)->yieldFocus()) { return true; } + return false; +} //WGuiItem @@ -198,6 +218,23 @@ void WGuiMenu::subBack(WGuiBase * item){ renderer->FillRoundRect(item->getX(),item->getY(),item->getWidth()-4,item->getHeight()-2,2,item->getColor(WGuiColor::BACK)); } + +void WGuiMenu::setSelected(vector::iterator& it) { + int c = it - items.begin(); + if (c != currentItem) + { + items[currentItem]->Leaving(JGE_BTN_NONE); + currentItem = c; + items[currentItem]->Entering(JGE_BTN_NONE); + } +} +bool WGuiMenu::yieldFocus() { + for (vector::iterator it = items.begin(); it != items.end(); ++it) + if ((*it)->yieldFocus()) { setSelected(it); return true; } + return false; +} + + //WGuiList WGuiList::WGuiList(string name, WSyncable * syncme): WGuiMenu(JGE_BTN_DOWN, JGE_BTN_UP, false, syncme){ failMsg = "NO OPTIONS AVAILABLE"; @@ -367,10 +404,10 @@ void WDecoEnum::Render() JLBFont * mFont = resources.GetJLBFont(Constants::OPTION_FONT); mFont->SetColor(getColor(WGuiColor::TEXT)); JRenderer * renderer = JRenderer::GetInstance(); - mFont->DrawString(_(getDisplay()).c_str(),getX(),getY()); + mFont->DrawString(_(getDisplay()).c_str(), getX() + 2, getY() + 3); OptionInteger* opt = dynamic_cast(it); if(opt) - mFont->DrawString(_(lookupVal(opt->value)).c_str(), getWidth() -10, getY(), JGETEXT_RIGHT); + mFont->DrawString(_(lookupVal(opt->value)).c_str(), getWidth() - 5, getY() + 3, JGETEXT_RIGHT); } WDecoEnum::WDecoEnum(WGuiBase * _it, EnumDefinition *_edef) : WGuiDeco(_it) {edef = _edef;} @@ -692,6 +729,11 @@ void WGuiSplit::confirmChange(bool confirmed){ right->confirmChange(confirmed); left->confirmChange(confirmed); } +bool WGuiSplit::yieldFocus() { + if (right->yieldFocus()) { bRight = true; return true; } + if (left->yieldFocus()) { bRight = false; return true; } + return false; +} //WGuiMenu WGuiMenu::WGuiMenu(JButton next = JGE_BTN_RIGHT, JButton prev = JGE_BTN_LEFT, bool m, WSyncable * syncme): WGuiItem(""){ @@ -1767,7 +1809,7 @@ string WGuiFilterItem::getCode(){ return mCode; } -WGuiKeyBinder::WGuiKeyBinder(string name, GameStateOptions* parent) : WGuiList(name), parent(parent), modal(false) { +WGuiKeyBinder::WGuiKeyBinder(string name, GameStateOptions* parent) : WGuiList(name), parent(parent), confirmMenu(NULL), modal(false), confirmed(CONFIRM_NEED), confirmingKey(LOCAL_KEY_NONE), confirmingButton(JGE_BTN_NONE), confirmationString("") { JGE* j = JGE::GetInstance(); JGE::keybindings_it start = j->KeyBindings_begin(), end = j->KeyBindings_end(); @@ -1783,27 +1825,131 @@ void WGuiKeyBinder::Update(float dt) { if (0 == currentItem) ++currentItem; } for (vector::iterator it = items.begin(); it != items.end(); ++it) (*it)->Update(dt); + if (confirmMenu) confirmMenu->Update(dt); } bool WGuiKeyBinder::isModal() { for (vector::iterator it = items.begin(); it != items.end(); ++it) if ((*it)->isModal()) return true; return modal; } -bool WGuiKeyBinder::CheckUserInput(JButton key) -{ +bool WGuiKeyBinder::CheckUserInput(JButton key) { + if (confirmMenu) + return confirmMenu->CheckUserInput(key); if (!items[currentItem]->CheckUserInput(key)) return WGuiList::CheckUserInput(key); if (!items[currentItem]->Selectable()) nextItem(); return true; } -void WGuiKeyBinder::setData(){ +void WGuiKeyBinder::setData() { JGE* j = JGE::GetInstance(); j->ClearBindings(); for (vector::iterator it = items.begin(); it != items.end(); ++it) { - OptionKey* o = dynamic_cast(*it); + OptionKey* o = static_cast(*it); if (o && LOCAL_KEY_NONE != o->from && JGE_BTN_NONE != o->to) j->BindKey(o->from, o->to); } j->ResetInput(); } + +static const JButton btnToCheck[] = {JGE_BTN_MENU, JGE_BTN_CTRL, JGE_BTN_RIGHT, + JGE_BTN_LEFT, JGE_BTN_UP, JGE_BTN_DOWN, + JGE_BTN_OK, JGE_BTN_CANCEL, JGE_BTN_PRI, + JGE_BTN_SEC, JGE_BTN_PREV, JGE_BTN_NEXT }; + +#define C(o) (static_cast(o)) +WGuiBase::CONFIRM_TYPE WGuiKeyBinder::needsConfirm() { + if (CONFIRM_CANCEL == confirmed) { confirmedKeys.clear(); confirmedButtons.clear(); confirmed = CONFIRM_NEED; return CONFIRM_CANCEL; } + if (confirmMenu) return CONFIRM_NEED; + + // Check whether any key is bound to two functions. + confirmingKey = LOCAL_KEY_NONE; + for (vector::iterator it = items.begin(); it != items.end(); ++it) { + if (!(*it)->Visible()) continue; + + vector boundFunctionsList; + for (vector::iterator jt = it + 1; jt != items.end(); ++jt) { + if (!(*jt)->Visible()) continue; + if (C(*it)->from == C(*jt)->from) + if (confirmedKeys.end() == find(confirmedKeys.begin(), confirmedKeys.end(), C(*it)->from)) { + confirmingKey = C(*it)->from; + if (boundFunctionsList.empty()) boundFunctionsList.push_back(C(*it)->to); + boundFunctionsList.push_back(C(*jt)->to); + } + } + + if (LOCAL_KEY_NONE != confirmingKey) { + // There is a conflict. Generate the error message... + char s[1024]; + snprintf(s, 1024, _("Warning : the %s key is bound to\n%i different functions:").c_str(), translateKey(confirmingKey).first.c_str(), boundFunctionsList.size()); + stringstream ss; + ss << s << "\n"; + vector::iterator jt = boundFunctionsList.begin(); + ss << translateKey(*jt).first.c_str(); + for (++jt; jt != boundFunctionsList.end(); ++jt) ss << ", " << translateKey(*jt).first.c_str(); + confirmationString = ss.str(); + + // Then create the menu. + confirmMenu = NEW SimpleMenu(0, this, Constants::MENU_FONT, 40, 130, "Conflict"); + confirmMenu->Add(1, _("Cancel and return to the options menu").c_str()); + confirmMenu->Add(2, _("This is okay, validate and save").c_str()); + return CONFIRM_NEED; + } + } + + // Check whether any button has no key associated to it. + confirmingButton = JGE_BTN_NONE; + for (signed int i = (sizeof(btnToCheck) / sizeof(btnToCheck[0])) - 1; i >= 0; --i) { + if (confirmedButtons.end() != find(confirmedButtons.begin(), confirmedButtons.end(), btnToCheck[i])) continue; + bool found = false; + for (vector::iterator it = items.begin(); it != items.end(); ++it) + if (btnToCheck[i] == C(*it)->to) { found = true; break; } + if (found) continue; + + char s[1024]; + snprintf(s, 1024, _("Warning : no key is associated to\nthe %s function.\nThis may make the game unusable.").c_str(), translateKey(btnToCheck[i]).first.c_str()); + confirmationString = s; + + confirmingButton = btnToCheck[i]; + confirmMenu = NEW SimpleMenu(1, this, Constants::MENU_FONT, 40, 130, "Binding missing"); + confirmMenu->Add(1, _("Cancel and return to the options menu").c_str()); + confirmMenu->Add(2, _("This is okay, validate and save").c_str()); + return CONFIRM_NEED; + } + + return CONFIRM_OK; +} +void WGuiKeyBinder::ButtonPressed(int controllerId, int controlId) { + if (2 == controlId) + switch(controllerId) { + case 0: confirmedKeys.insert(confirmingKey); break; + case 1: confirmedButtons.insert(confirmingButton); break; + } + else + confirmed = CONFIRM_CANCEL; + SAFE_DELETE(confirmMenu); + confirmMenu = NULL; +} +void WGuiKeyBinder::Render() { + WGuiList::Render(); + if (confirmMenu) { + JRenderer * renderer = JRenderer::GetInstance(); + JLBFont * mFont = resources.GetJLBFont(Constants::OPTION_FONT); + mFont->SetColor(ARGB(255, 255, 0, 0)); + renderer->FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, ARGB(230, 255, 240, 240)); + + size_t pos = 0; + u32 y = 20; + do { + size_t t = confirmationString.find_first_of("\n", pos); + string s = confirmationString.substr(pos, t - pos); + pos = (string::npos == t) ? t : t + 1; + mFont->DrawString(s, SCREEN_WIDTH / 2, y, JGETEXT_CENTER); + y += 20; + } while (pos != string::npos); + confirmMenu->Render(); + } +} +bool WGuiKeyBinder::yieldFocus() { + return true; +}