Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
146 KB
Referenced Files
None
Subscribers
None
diff --git a/src/GUIObject.cpp b/src/GUIObject.cpp
index e10bf53..c80bdf6 100644
--- a/src/GUIObject.cpp
+++ b/src/GUIObject.cpp
@@ -1,964 +1,1184 @@
/*
* Copyright (C) 2011-2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Functions.h"
#include "GUIObject.h"
#include "ThemeManager.h"
+#include "InputManager.h"
+#include "GUISpinBox.h"
+#include "GUIListBox.h"
+#include "GUISlider.h"
#include <algorithm>
#include <iostream>
#include <list>
#include <SDL_ttf.h>
#include "Render.h"
using namespace std;
//Set the GUIObjectRoot to NULL.
GUIObject* GUIObjectRoot=NULL;
//Initialise the event queue.
list<GUIEvent> GUIEventQueue;
void GUIObjectHandleEvents(ImageManager& imageManager, SDL_Renderer& renderer, bool kill){
//NOTE: This was already not doing anything so commenting it for now.
/*
//Check if user resizes the window.
if(event.type==SDL_VIDEORESIZE){
//onVideoResize();
//Don't let other objects process this event (?)
return;
}*/
//Make sure that GUIObjectRoot isn't null.
if(GUIObjectRoot)
GUIObjectRoot->handleEvents(renderer);
//Check for SDL_QUIT.
if(event.type==SDL_QUIT && kill){
//We get a quit event so enter the exit state.
setNextState(STATE_EXIT);
delete GUIObjectRoot;
GUIObjectRoot=NULL;
return;
}
//Keep calling events until there are none left.
while(!GUIEventQueue.empty()){
//Get one event and remove it from the queue.
GUIEvent e=GUIEventQueue.front();
GUIEventQueue.pop_front();
//If an eventCallback exist call it.
if(e.eventCallback){
e.eventCallback->GUIEventCallback_OnEvent(imageManager,renderer,e.name,e.obj,e.eventType);
}
}
//We empty the event queue just to be sure.
GUIEventQueue.clear();
}
+GUIObject::GUIObject(ImageManager& imageManager, SDL_Renderer& renderer, int left, int top, int width, int height,
+ const char* caption, int value,
+ bool enabled, bool visible, int gravity) :
+ left(left), top(top), width(width), height(height),
+ gravity(gravity), value(value),
+ enabled(enabled), visible(visible),
+ eventCallback(NULL), state(0),
+ cachedEnabled(enabled), gravityX(0)
+{
+ //Make sure that caption isn't NULL before setting it.
+ if (caption){
+ GUIObject::caption = caption;
+ //And set the cached caption.
+ cachedCaption = caption;
+ }
+
+ if (width <= 0)
+ autoWidth = true;
+ else
+ autoWidth = false;
+
+ inDialog = false;
+
+ //Load the gui images.
+ bmGuiTex = imageManager.loadTexture(getDataPath() + "gfx/gui.png", renderer);
+}
+
GUIObject::~GUIObject(){
//We need to delete every child we have.
for(unsigned int i=0;i<childControls.size();i++){
delete childControls[i];
}
//Deleted the childs now empty the childControls vector.
childControls.clear();
}
+void GUIObject::addChild(GUIObject* obj){
+ //Add widget add a child
+ childControls.push_back(obj);
+
+ //Copy inDialog boolean from parent.
+ obj->inDialog = inDialog;
+}
+
+GUIObject* GUIObject::getChild(std::string name){
+ //Look for a child with the name.
+ for (unsigned int i = 0; i<childControls.size(); i++)
+ if (childControls[i]->name == name)
+ return childControls[i];
+
+ //Not found so return NULL.
+ return NULL;
+}
+
bool GUIObject::handleEvents(SDL_Renderer& renderer,int x,int y,bool enabled,bool visible,bool processed){
//Boolean if the event is processed.
bool b=processed;
//The GUIObject is only enabled when its parent are enabled.
enabled=enabled && this->enabled;
//The GUIObject is only enabled when its parent are enabled.
visible=visible && this->visible;
//Get the absolute position.
x+=left-gravityX;
y+=top;
//Also let the children handle their events.
for(unsigned int i=0;i<childControls.size();i++){
bool b1=childControls[i]->handleEvents(renderer,x,y,enabled,visible,b);
//The event is processed when either our or the childs is true (or both).
b=b||b1;
}
return b;
}
void GUIObject::render(SDL_Renderer& renderer, int x,int y,bool draw){
//There's no need drawing the GUIObject when it's invisible.
if(!visible)
return;
//Get the absolute x and y location.
x+=left;
y+=top;
//We now need to draw all the children of the GUIObject.
for(unsigned int i=0;i<childControls.size();i++){
childControls[i]->render(renderer,x,y,draw);
}
}
void GUIObject::refreshCache(bool enabled) {
//Check if the enabled state changed or the caption, if so we need to clear the (old) cache.
if(enabled!=cachedEnabled || caption.compare(cachedCaption)!=0 || width<=0){
//TODO: Only change alpha if only enabled changes.
//Free the cache.
cacheTex.reset(nullptr);
//And cache the new values.
cachedEnabled=enabled;
cachedCaption=caption;
//Finally resize the widget
if(autoWidth)
width=-1;
}
}
+int GUIObject::getSelectedControl() {
+ for (int i = 0; i < (int)childControls.size(); i++) {
+ GUIObject *obj = childControls[i];
+ if (obj && obj->visible && obj->enabled && obj->state) {
+ if (dynamic_cast<GUIButton*>(obj) || dynamic_cast<GUICheckBox*>(obj)
+ || dynamic_cast<GUITextBox*>(obj) || dynamic_cast<GUISpinBox*>(obj)
+ || dynamic_cast<GUISingleLineListBox*>(obj)
+ || dynamic_cast<GUISlider*>(obj)
+ )
+ {
+ return i;
+ }
+ }
+ }
+
+ return -1;
+}
+
+void GUIObject::setSelectedControl(int index) {
+ for (int i = 0; i < (int)childControls.size(); i++) {
+ GUIObject *obj = childControls[i];
+ if (obj && obj->visible && obj->enabled) {
+ if (dynamic_cast<GUIButton*>(obj) || dynamic_cast<GUICheckBox*>(obj)) {
+ //It's a button.
+ obj->state = (i == index) ? 1 : 0;
+ } else if (dynamic_cast<GUITextBox*>(obj) || dynamic_cast<GUISpinBox*>(obj)) {
+ //It's a text box.
+ obj->state = (i == index) ? 2 : 0;
+ } else if (dynamic_cast<GUISingleLineListBox*>(obj)) {
+ //It's a single line list box.
+ obj->state = (i == index) ? 0x100 : 0;
+ } else if (dynamic_cast<GUISlider*>(obj)) {
+ //It's a slider.
+ obj->state = (i == index) ? 0x10000 : 0;
+ }
+ }
+ }
+}
+
+int GUIObject::selectNextControl(int direction, int selected) {
+ //Get the index of currently selected control.
+ if (selected == 0x80000000) {
+ selected = getSelectedControl();
+ }
+
+ //Find the next control.
+ for (int i = 0; i < (int)childControls.size(); i++) {
+ if (selected < 0) {
+ selected = 0;
+ } else {
+ selected += direction;
+ if (selected >= (int)childControls.size()) {
+ selected -= childControls.size();
+ } else if (selected < 0) {
+ selected += childControls.size();
+ }
+ }
+
+ GUIObject *obj = childControls[selected];
+ if (obj && obj->visible && obj->enabled) {
+ if (dynamic_cast<GUIButton*>(obj) || dynamic_cast<GUICheckBox*>(obj)
+ || dynamic_cast<GUITextBox*>(obj) || dynamic_cast<GUISpinBox*>(obj)
+ || dynamic_cast<GUISingleLineListBox*>(obj)
+ || dynamic_cast<GUISlider*>(obj)
+ )
+ {
+ setSelectedControl(selected);
+ return selected;
+ }
+ }
+ }
+
+ return -1;
+}
+
+bool GUIObject::handleKeyboardNavigationEvents(ImageManager& imageManager, SDL_Renderer& renderer, int keyboardNavigationMode) {
+ if (keyboardNavigationMode == 0) return false;
+
+ //Check operation on focused control. These have higher priority.
+ if (isKeyboardOnly) {
+ //Check enter key.
+ if ((keyboardNavigationMode & 8) != 0 && inputMgr.isKeyDownEvent(INPUTMGR_SELECT)) {
+ int index = getSelectedControl();
+ if (index >= 0) {
+ GUIObject *obj = childControls[index];
+
+ if (dynamic_cast<GUIButton*>(obj)) {
+ //It's a button.
+ if (obj->eventCallback) {
+ obj->eventCallback->GUIEventCallback_OnEvent(imageManager, renderer, obj->name, obj, GUIEventClick);
+ }
+ return true;
+ }
+ if (dynamic_cast<GUICheckBox*>(obj)) {
+ //It's a check box.
+ obj->value = obj->value ? 0 : 1;
+ if (obj->eventCallback) {
+ obj->eventCallback->GUIEventCallback_OnEvent(imageManager, renderer, obj->name, obj, GUIEventClick);
+ }
+ return true;
+ }
+ }
+ }
+
+ //Check left/right key.
+ if ((keyboardNavigationMode & 16) != 0 && (inputMgr.isKeyDownEvent(INPUTMGR_LEFT) || inputMgr.isKeyDownEvent(INPUTMGR_RIGHT))) {
+ int index = getSelectedControl();
+ if (index >= 0) {
+ GUIObject *obj = childControls[index];
+
+ auto sllb = dynamic_cast<GUISingleLineListBox*>(obj);
+ if (sllb) {
+ //It's a single line list box.
+ int newValue = sllb->value + (inputMgr.isKeyDownEvent(INPUTMGR_RIGHT) ? 1 : -1);
+ if (newValue >= (int)sllb->item.size()) {
+ newValue -= sllb->item.size();
+ } else if (newValue < 0) {
+ newValue += sllb->item.size();
+ }
+
+ if (sllb->value != newValue) {
+ sllb->value = newValue;
+ if (obj->eventCallback) {
+ obj->eventCallback->GUIEventCallback_OnEvent(imageManager, renderer, obj->name, obj, GUIEventClick);
+ }
+ }
+ return true;
+ }
+
+ auto slider = dynamic_cast<GUISlider*>(obj);
+ if (slider) {
+ //It's a slider.
+ int newValue = slider->value + (inputMgr.isKeyDownEvent(INPUTMGR_RIGHT) ? slider->largeChange : -slider->largeChange);
+ newValue = clamp(newValue, slider->minValue, slider->maxValue);
+
+ if (slider->value != newValue) {
+ slider->value = newValue;
+ if (obj->eventCallback) {
+ obj->eventCallback->GUIEventCallback_OnEvent(imageManager, renderer, obj->name, obj, GUIEventChange);
+ }
+ }
+ return true;
+ }
+ }
+
+ }
+ }
+
+ //Check focus movement
+ int m = SDL_GetModState();
+ if (((keyboardNavigationMode & 1) != 0 && inputMgr.isKeyDownEvent(INPUTMGR_RIGHT))
+ || ((keyboardNavigationMode & 2) != 0 && inputMgr.isKeyDownEvent(INPUTMGR_DOWN))
+ || ((keyboardNavigationMode & 4) != 0 && inputMgr.isKeyDownEvent(INPUTMGR_TAB) && (m & KMOD_SHIFT) == 0)
+ )
+ {
+ isKeyboardOnly = true;
+ selectNextControl(1);
+ return true;
+ } else if (((keyboardNavigationMode & 1) != 0 && inputMgr.isKeyDownEvent(INPUTMGR_LEFT))
+ || ((keyboardNavigationMode & 2) != 0 && inputMgr.isKeyDownEvent(INPUTMGR_UP))
+ || ((keyboardNavigationMode & 4) != 0 && inputMgr.isKeyDownEvent(INPUTMGR_TAB) && (m & KMOD_SHIFT) != 0)
+ )
+ {
+ isKeyboardOnly = true;
+ selectNextControl(-1);
+ return true;
+ }
+
+ return false;
+}
+
//////////////GUIButton///////////////////////////////////////////////////////////////////
bool GUIButton::handleEvents(SDL_Renderer& renderer,int x,int y,bool enabled,bool visible,bool processed){
//Boolean if the event is processed.
bool b=processed;
//The widget is only enabled when its parent are enabled.
enabled=enabled && this->enabled;
//The widget is only enabled when its parent are enabled.
visible=visible && this->visible;
//Get the absolute position.
x+=left-gravityX;
y+=top;
//We don't update button state under keyboard only mode.
if (!isKeyboardOnly) {
//Set state to 0.
state = 0;
//Only check for events when the object is both enabled and visible.
if (enabled && visible) {
//The mouse location (x=i, y=j) and the mouse button (k).
int i, j, k;
k = SDL_GetMouseState(&i, &j);
//Check if the mouse is inside the widget.
if (i >= x && i < x + width && j >= y && j < y + height) {
//We have hover so set state to one.
state = 1;
//Check for a mouse button press.
if (k&SDL_BUTTON(1))
state = 2;
//Check if there's a mouse press and the event hasn't been already processed.
if (event.type == SDL_MOUSEBUTTONUP && event.button.button == SDL_BUTTON_LEFT && !b) {
//If event callback is configured then add an event to the queue.
if (eventCallback) {
GUIEvent e = { eventCallback, name, this, GUIEventClick };
GUIEventQueue.push_back(e);
}
//Event has been processed.
b = true;
}
}
}
}
//Also let the children handle their events.
for(unsigned int i=0;i<childControls.size();i++){
bool b1=childControls[i]->handleEvents(renderer,x,y,enabled,visible,b);
//The event is processed when either our or the childs is true (or both).
b=b||b1;
}
return b;
}
void GUIButton::render(SDL_Renderer& renderer, int x,int y,bool draw){
//There's no need drawing the widget when it's invisible.
if(!visible)
return;
//Get the absolute x and y location.
x+=left;
y+=top;
refreshCache(enabled);
//Get the text and make sure it isn't empty.
const char* lp=caption.c_str();
if(lp!=NULL && lp[0]){
//Update cache if needed.
if(!cacheTex){
SDL_Color color = objThemes.getTextColor(inDialog);
if(!smallFont) {
cacheTex = textureFromText(renderer, *fontGUI, lp, color);
} else {
cacheTex = textureFromText(renderer, *fontGUISmall, lp, color);
}
//Make the widget transparent if it's disabled.
if(!enabled) {
SDL_SetTextureAlphaMod(cacheTex.get(), 128);
}
//Calculate proper size for the widget.
if(width<=0){
width=textureWidth(*cacheTex)+50;
if(gravity==GUIGravityCenter){
gravityX=int(width/2);
}else if(gravity==GUIGravityRight){
gravityX=width;
}else{
gravityX=0;
}
}
}
if(draw){
//Center the text both vertically as horizontally.
const SDL_Rect size = rectFromTexture(*cacheTex);
const int drawX=x-gravityX+(width-size.w)/2;
const int drawY=y+(height-size.h)/2-GUI_FONT_RAISE;
//Check if the arrows don't fall of.
if(size.w+32<=width){
if(state==1){
if(inDialog){
applyTexture(x-gravityX+(width-size.w)/2+4+size.w+5,y+2,*arrowLeft2,renderer);
applyTexture(x-gravityX+(width-size.w)/2-25,y+2,*arrowRight2,renderer);
}else{
applyTexture(x-gravityX+(width-size.w)/2+4+size.w+5,y+2,*arrowLeft1,renderer);
applyTexture(x-gravityX+(width-size.w)/2-25,y+2,*arrowRight1,renderer);
}
}else if(state==2){
if(inDialog){
applyTexture(x-gravityX+(width-size.w)/2+4+size.w,y+2,*arrowLeft2,renderer);
applyTexture(x-gravityX+(width-size.w)/2-20,y+2,*arrowRight2,renderer);
}else{
applyTexture(x-gravityX+(width-size.w)/2+4+size.w,y+2,*arrowLeft1,renderer);
applyTexture(x-gravityX+(width-size.w)/2-20,y+2,arrowRight1,renderer);
}
}
}
//Draw the text.
applyTexture(drawX, drawY, *cacheTex, renderer);
}
}
}
//////////////GUICheckBox///////////////////////////////////////////////////////////////////
bool GUICheckBox::handleEvents(SDL_Renderer&,int x,int y,bool enabled,bool visible,bool processed){
//Boolean if the event is processed.
bool b=processed;
//The widget is only enabled when its parent are enabled.
enabled=enabled && this->enabled;
//The widget is only enabled when its parent are enabled.
visible=visible && this->visible;
//Get the absolute position.
x+=left-gravityX;
y+=top;
//We don't update state under keyboard only mode.
if (!isKeyboardOnly) {
//Set state to 0.
state = 0;
//Only check for events when the object is both enabled and visible.
if (enabled&&visible){
//The mouse location (x=i, y=j) and the mouse button (k).
int i, j, k;
k = SDL_GetMouseState(&i, &j);
//Check if the mouse is inside the widget.
if (i >= x && i < x + width && j >= y && j < y + height){
//We have hover so set state to one.
state = 1;
//Check for a mouse button press.
if (k&SDL_BUTTON(1))
state = 2;
//Check if there's a mouse press and the event hasn't been already processed.
if (event.type == SDL_MOUSEBUTTONUP && event.button.button == SDL_BUTTON_LEFT && !b){
//It's a checkbox so toggle the value.
value = value ? 0 : 1;
//If event callback is configured then add an event to the queue.
if (eventCallback){
GUIEvent e = { eventCallback, name, this, GUIEventClick };
GUIEventQueue.push_back(e);
}
//Event has been processed.
b = true;
}
}
}
}
return b;
}
void GUICheckBox::render(SDL_Renderer& renderer, int x,int y,bool draw){
//There's no need drawing the widget when it's invisible.
if(!visible)
return;
//Get the absolute x and y location.
x+=left;
y+=top;
refreshCache(enabled);
//Draw the highlight in keyboard only mode.
if (isKeyboardOnly && state && draw) {
drawGUIBox(x, y, width, height, renderer, 0xFFFFFF40);
}
//Get the text.
const char* lp=caption.c_str();
//Make sure it isn't empty.
if(lp!=NULL && lp[0]){
//Update the cache if needed.
if(!cacheTex){
SDL_Color color = objThemes.getTextColor(inDialog);
cacheTex=textureFromText(renderer,*fontText,lp,color);
}
if(draw){
//Calculate the location, center it vertically.
const int drawX=x;
const int drawY=y+(height - textureHeight(*cacheTex))/2;
//Draw the text
applyTexture(drawX, drawY, *cacheTex, renderer);
}
}
if(draw){
//Draw the check (or not).
//value*16 determines where in the gui textures we draw from.
//if(value==1||value==2)
// r1.x=value*16;
const SDL_Rect srcRect={value*16,0,16,16};
const SDL_Rect dstRect={x+width-20, y+(height-16)/2, 16, 16};
//Get the right image depending on the state of the object.
SDL_RenderCopy(&renderer, bmGuiTex.get(), &srcRect, &dstRect);
}
}
//////////////GUILabel///////////////////////////////////////////////////////////////////
bool GUILabel::handleEvents(SDL_Renderer&,int ,int ,bool ,bool ,bool processed){
return processed;
}
void GUILabel::render(SDL_Renderer& renderer, int x,int y,bool draw){
//There's no need drawing the widget when it's invisible.
if(!visible)
return;
//Check if the enabled state changed or the caption, if so we need to clear the (old) cache.
refreshCache(enabled);
//Get the absolute x and y location.
x+=left;
y+=top;
//Rectangle the size of the widget.
SDL_Rect r;
r.x=x;
r.y=y;
r.w=width;
r.h=height;
//Get the caption and make sure it isn't empty.
const char* lp=caption.c_str();
if(lp!=NULL && lp[0]){
//Update cache if needed.
if(!cacheTex){
SDL_Color color = objThemes.getTextColor(inDialog);
cacheTex=textureFromText(renderer, *fontText, lp, color);
if(width<=0)
width=textureWidth(*cacheTex);
}
//Align the text properly and draw it.
if(draw){
const SDL_Rect size = rectFromTexture(*cacheTex);
if(gravity==GUIGravityCenter)
gravityX=(width-size.w)/2;
else if(gravity==GUIGravityRight)
gravityX=width-size.w;
else
gravityX=0;
r.y=y+(height - size.h)/2;
r.x+=gravityX;
applyTexture(r.x, r.y, cacheTex, renderer);
}
}
}
//////////////GUITextBox///////////////////////////////////////////////////////////////////
void GUITextBox::backspaceChar(){
//We need to remove a character so first make sure that there is text.
if(caption.length()>0){
if(highlightStart==highlightEnd&&highlightStart>0){
int advance = 0;
// this is proper UTF-8 support
int ch = utf8ReadBackward(caption.c_str(), highlightStart); // we obtain new highlightStart from this
if (ch > 0) TTF_GlyphMetrics(fontText, ch, NULL, NULL, NULL, NULL, &advance);
highlightEndX = highlightStartX = highlightEndX - advance;
caption.erase(highlightStart, highlightEnd - highlightStart);
highlightEnd = highlightStart;
} else if (highlightStart<highlightEnd){
caption.erase(highlightStart,highlightEnd-highlightStart);
highlightEnd=highlightStart;
highlightEndX=highlightStartX;
}else{
caption.erase(highlightEnd,highlightStart-highlightEnd);
highlightStart=highlightEnd;
highlightStartX=highlightEndX;
}
//If there is an event callback then call it.
if(eventCallback){
GUIEvent e={eventCallback,name,this,GUIEventChange};
GUIEventQueue.push_back(e);
}
}
}
void GUITextBox::deleteChar(){
//We need to remove a character so first make sure that there is text.
if(caption.length()>0){
if(highlightStart==highlightEnd){
// this is proper utf8 support
int i = highlightEnd;
utf8ReadForward(caption.c_str(), i);
if (i > highlightEnd) caption.erase(highlightEnd, i - highlightEnd);
highlightStart=highlightEnd;
highlightStartX=highlightEndX;
}else if(highlightStart<highlightEnd){
caption.erase(highlightStart,highlightEnd-highlightStart);
highlightEnd=highlightStart;
highlightEndX=highlightStartX;
}else{
caption.erase(highlightEnd,highlightStart-highlightEnd);
highlightStart=highlightEnd;
highlightStartX=highlightEndX;
}
//If there is an event callback then call it.
if(eventCallback){
GUIEvent e={eventCallback,name,this,GUIEventChange};
GUIEventQueue.push_back(e);
}
}
}
void GUITextBox::moveCarrotLeft(){
if(highlightEnd>0){
int advance = 0;
// this is proper UTF-8 support
int ch = utf8ReadBackward(caption.c_str(), highlightEnd); // we obtain new highlightEnd from this
if (ch > 0) TTF_GlyphMetrics(fontText, ch, NULL, NULL, NULL, NULL, &advance);
if(SDL_GetModState() & KMOD_SHIFT){
highlightEndX-=advance;
}else{
highlightStart=highlightEnd;
highlightStartX=highlightEndX=highlightEndX-advance;
}
}else{
if((SDL_GetModState() & KMOD_SHIFT)==0){
highlightStart=highlightEnd;
highlightStartX=highlightEndX;
}
}
tick=15;
}
void GUITextBox::moveCarrotRight(){
if(highlightEnd<caption.length()){
int advance = 0;
// this is proper UTF-8 support
int ch = utf8ReadForward(caption.c_str(), highlightEnd); // we obtain new highlightEnd from this
if (ch > 0) TTF_GlyphMetrics(fontText, ch, NULL, NULL, NULL, NULL, &advance);
if(SDL_GetModState() & KMOD_SHIFT){
highlightEndX+=advance;
}else{
highlightStartX=highlightEndX=highlightEndX+advance;
highlightStart=highlightEnd;
}
}else{
if((SDL_GetModState() & KMOD_SHIFT)==0){
highlightStart=highlightEnd;
highlightStartX=highlightEndX;
}
}
tick=15;
}
void GUITextBox::inputText(const char* s) {
int m = strlen(s);
if (m > 0){
if (highlightStart == highlightEnd) {
caption.insert((size_t)highlightStart, s);
highlightStart += m;
highlightEnd = highlightStart;
} else if (highlightStart < highlightEnd) {
caption.erase(highlightStart, highlightEnd - highlightStart);
caption.insert((size_t)highlightStart, s);
highlightStart += m;
highlightEnd = highlightStart;
highlightEndX = highlightStartX;
} else {
caption.erase(highlightEnd, highlightStart - highlightEnd);
caption.insert((size_t)highlightEnd, s);
highlightEnd += m;
highlightStart = highlightEnd;
highlightStartX = highlightEndX;
}
int advance = 0;
for (int i = 0;;) {
int a = 0;
int ch = utf8ReadForward(s, i);
if (ch <= 0) break;
TTF_GlyphMetrics(fontText, ch, NULL, NULL, NULL, NULL, &a);
advance += a;
}
highlightStartX = highlightEndX = highlightStartX + advance;
//If there is an event callback then call it.
if (eventCallback){
GUIEvent e = { eventCallback, name, this, GUIEventChange };
GUIEventQueue.push_back(e);
}
}
}
bool GUITextBox::handleEvents(SDL_Renderer&,int x,int y,bool enabled,bool visible,bool processed){
//Boolean if the event is processed.
bool b=processed;
//The widget is only enabled when its parent are enabled.
enabled=enabled && this->enabled;
//The widget is only enabled when its parent are enabled.
visible=visible && this->visible;
//Get the absolute position.
x+=left-gravityX;
y+=top;
//NOTE: We don't reset the state to have a "focus" effect.
//Only check for events when the object is both enabled and visible.
if(enabled&&visible){
//Check if there's a key press and the event hasn't been already processed.
if(state==2 && event.type==SDL_KEYDOWN && !b){
//Get the keycode.
SDL_Keycode key=event.key.keysym.sym;
if ((event.key.keysym.mod & KMOD_CTRL) == 0) {
//Check if the key is supported.
if (event.key.keysym.sym == SDLK_BACKSPACE){
backspaceChar();
} else if (event.key.keysym.sym == SDLK_DELETE){
deleteChar();
} else if (event.key.keysym.sym == SDLK_RIGHT){
moveCarrotRight();
} else if (event.key.keysym.sym == SDLK_LEFT){
moveCarrotLeft();
}
} else {
//Check hotkey.
if (event.key.keysym.sym == SDLK_a) {
//Select all.
highlightStart = 0;
highlightStartX = 0;
highlightEnd = caption.size();
highlightEndX = 0;
if (highlightEnd > 0) {
TTF_SizeUTF8(fontText, caption.c_str(), &highlightEndX, NULL);
}
} else if (event.key.keysym.sym == SDLK_x || event.key.keysym.sym == SDLK_c) {
//Cut or copy.
int start = highlightStart, end = highlightEnd;
if (start > end) std::swap(start, end);
if (start < end) {
SDL_SetClipboardText(caption.substr(start, end - start).c_str());
if (event.key.keysym.sym == SDLK_x) {
//Cut.
backspaceChar();
}
}
} else if (event.key.keysym.sym == SDLK_v) {
//Paste.
if (SDL_HasClipboardText()) {
char *s = SDL_GetClipboardText();
inputText(s);
SDL_free(s);
}
}
}
//The event has been processed.
b = true;
} else if (state == 2 && event.type == SDL_TEXTINPUT && !b){
inputText(event.text.text);
//The event has been processed.
b = true;
} else if (state == 2 && event.type == SDL_TEXTEDITING && !b){
// TODO: process SDL_TEXTEDITING event
}
//Only process mouse event when not in keyboard only mode
if (!isKeyboardOnly) {
//The mouse location (x=i, y=j) and the mouse button (k).
int i, j, k;
k = SDL_GetMouseState(&i, &j);
//Check if the mouse is inside the widget.
if (i >= x && i < x + width && j >= y && j < y + height){
//We can only increase our state. (nothing->hover->focus).
if (state != 2){
state = 1;
}
//Also update the cursor type.
currentCursor = CURSOR_CARROT;
//Move carrot and highlightning according to mouse input.
int clickX = i - x - 2;
int finalPos = 0;
int finalX = 0;
if (cacheTex&&!caption.empty()){
finalPos = caption.length();
for (int i = 0;;){
int advance = 0;
// this is proper UTF-8 support
int i0 = i;
int ch = utf8ReadForward(caption.c_str(), i);
if (ch <= 0) break;
TTF_GlyphMetrics(fontText, ch, NULL, NULL, NULL, NULL, &advance);
finalX += advance;
if (clickX < finalX - advance / 2){
finalPos = i0;
finalX -= advance;
break;
}
}
}
if (event.type == SDL_MOUSEBUTTONUP){
state = 2;
highlightEnd = finalPos;
highlightEndX = finalX;
} else if (event.type == SDL_MOUSEBUTTONDOWN){
state = 2;
highlightStart = highlightEnd = finalPos;
highlightStartX = highlightEndX = finalX;
} else if (event.type == SDL_MOUSEMOTION && (k&SDL_BUTTON(1))){
state = 2;
highlightEnd = finalPos;
highlightEndX = finalX;
}
} else{
//The mouse is outside the TextBox.
//If we don't have focus but only hover we lose it.
if (state == 1){
state = 0;
}
//If it's a click event outside the textbox then we blur.
if (event.type == SDL_MOUSEBUTTONUP && event.button.button == SDL_BUTTON_LEFT){
//Set state to 0.
state = 0;
}
}
}
}
return b;
}
void GUITextBox::render(SDL_Renderer& renderer, int x,int y,bool draw){
//There's no need drawing the widget when it's invisible.
if(!visible)
return;
//Get the absolute x and y location.
x+=left;
y+=top;
//Check if the enabled state changed or the caption, if so we need to clear the (old) cache.
refreshCache(enabled);
if(draw){
//Default background opacity
int clr=50;
//If hovering or focused make background more visible.
if(state==1)
clr=128;
else if (state==2)
clr=100;
//Draw the box.
Uint32 color=0xFFFFFF00|clr;
drawGUIBox(x,y,width,height,renderer,color);
}
//Rectangle used for drawing.
SDL_Rect r{0,0,0,0};
//Get the text and make sure it isn't empty.
const char* lp=caption.c_str();
if(lp!=NULL && lp[0]){
if(!cacheTex) {
//Draw the text.
cacheTex=textureFromText(renderer,*fontText,lp,objThemes.getTextColor(true));
}
if(draw){
//Only draw the carrot and highlight when focus.
if(state==2){
//Place the highlighted area.
r.x=x+4;
r.y=y+3;
r.h=height-6;
if(highlightStart<highlightEnd){
r.x+=highlightStartX;
r.w=highlightEndX-highlightStartX;
}else{
r.x+=highlightEndX;
r.w=highlightStartX-highlightEndX;
}
//Draw the area.
//SDL_FillRect(screen,&r,SDL_MapRGB(screen->format,128,128,128));
SDL_SetRenderDrawColor(&renderer, 128,128,128,255);
SDL_RenderFillRect(&renderer, &r);
//Ticking carrot.
if(tick<16){
//Show carrot: 15->0.
r.x=x+highlightEndX+2;
r.y=y+3;
r.h=height-6;
r.w=2;
//SDL_FillRect(screen,&r,SDL_MapRGB(screen->format,0,0,0));
SDL_SetRenderDrawColor(&renderer,0,0,0,255);
SDL_RenderFillRect(&renderer, &r);
//Reset: 32 or count down.
if(tick<=0)
tick=32;
else
tick--;
}else{
//Hide carrot: 32->16.
tick--;
}
}
//Calculate the location, center it vertically.
SDL_Rect dstRect=rectFromTexture(*cacheTex);
dstRect.x=x+4;
dstRect.y=y+(height-dstRect.h)/2;
dstRect.w=std::min(width-2, dstRect.w);
//Draw the text.
const SDL_Rect srcRect={0,0,width-2,25};
SDL_RenderCopy(&renderer, cacheTex.get(), &srcRect, &dstRect);
}
}else{
//Only draw the carrot when focus.
if(state==2&&draw){
//Ticking carrot.
if (tick<16){
//Show carrot: 15->0.
r.x = x + 4;
r.y = y + 4;
r.w = 2;
r.h = height - 8;
//SDL_FillRect(screen,&r,SDL_MapRGB(screen->format,0,0,0));
SDL_SetRenderDrawColor(&renderer, 0, 0, 0, 255);
SDL_RenderFillRect(&renderer, &r);
//Reset: 32 or count down.
if (tick <= 0)
tick = 32;
else
tick--;
} else{
//Hide carrot: 32->16.
tick--;
}
}
}
}
//////////////GUIFrame///////////////////////////////////////////////////////////////////
bool GUIFrame::handleEvents(SDL_Renderer& renderer,int x,int y,bool enabled,bool visible,bool processed){
//Boolean if the event is processed.
bool b=processed;
//The widget is only enabled when its parent are enabled.
enabled=enabled && this->enabled;
//The widget is only enabled when its parent are enabled.
visible=visible && this->visible;
//Get the absolute position.
x+=left;
y+=top;
//Also let the children handle their events.
for(unsigned int i=0;i<childControls.size();i++){
bool b1=childControls[i]->handleEvents(renderer,x,y,enabled,visible,b);
//The event is processed when either our or the childs is true (or both).
b=b||b1;
}
return b;
}
void GUIFrame::render(SDL_Renderer& renderer, int x,int y,bool draw){
//There's no need drawing this widget when it's invisible.
if(!visible)
return;
//Get the absolute x and y location.
x+=left;
y+=top;
//Check if the enabled state changed or the caption, if so we need to clear the (old) cache.
if(enabled!=cachedEnabled || caption.compare(cachedCaption)!=0 || width<=0){
//Free the cache.
cacheTex.reset(nullptr);
//And cache the new values.
cachedEnabled=enabled;
cachedCaption=caption;
//Finally resize the widget.
if(autoWidth)
width=-1;
}
//Draw fill and borders.
if(draw){
Uint32 color=0xDDDDDDFF;
drawGUIBox(x,y,width,height,renderer,color);
}
//Get the title text and make sure it isn't empty.
const char* lp=caption.c_str();
if(lp!=NULL && lp[0]){
//Update cache if needed.
if(!cacheTex) {
cacheTex = textureFromText(renderer, *fontGUI, lp, objThemes.getTextColor(true));
}
//Draw the text.
if(draw) {
applyTexture(x+(width-textureWidth(*cacheTex))/2, y+6-GUI_FONT_RAISE, *cacheTex, renderer);
}
}
//We now need to draw all the children.
for(unsigned int i=0;i<childControls.size();i++){
childControls[i]->render(renderer,x,y,draw);
}
}
//////////////GUIImage///////////////////////////////////////////////////////////////////
GUIImage::~GUIImage(){
}
bool GUIImage::handleEvents(SDL_Renderer&,int ,int ,bool ,bool ,bool processed){
return processed;
}
void GUIImage::fitToImage(){
const SDL_Rect imageSize = rectFromTexture(*image);
//Increase or decrease the width and height to fully show the image.
if(clip.w!=0) {
width=clip.w;
} else {
width=imageSize.w;
}
if(clip.h!=0) {
height=clip.h;
} else {
height=imageSize.h;
}
}
void GUIImage::render(SDL_Renderer& renderer, int x,int y,bool draw){
//There's no need drawing the widget when it's invisible.
//Also make sure the image isn't null.
if(!visible || !image)
return;
//Get the absolute x and y location.
x+=left;
y+=top;
//Create a clip rectangle.
SDL_Rect r=clip;
//The width and height are capped by the GUIImage itself.
if(r.w>width || r.w==0) {
r.w=width;
}
if(r.h>height || r.h==0) {
r.h=height;
}
const SDL_Rect dstRect={x,y,r.w,r.h};
SDL_RenderCopy(&renderer, image.get(), &r, &dstRect);
}
diff --git a/src/GUIObject.h b/src/GUIObject.h
index ba86e50..792dbc4 100644
--- a/src/GUIObject.h
+++ b/src/GUIObject.h
@@ -1,391 +1,380 @@
/*
* Copyright (C) 2011-2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GUIOBJECT_H
#define GUIOBJECT_H
#include "FileManager.h"
#include "ImageManager.h"
#include "Render.h"
#include <string>
#include <vector>
#include <list>
//Widget gravity properties
const int GUIGravityLeft=0;
const int GUIGravityCenter=1;
const int GUIGravityRight=2;
//The event id's.
//A click event used for e.g. buttons.
const int GUIEventClick=0;
//A change event used for e.g. textboxes.
const int GUIEventChange=1;
struct SDL_Renderer;
class GUIObject;
//Class that is used as event callback.
class GUIEventCallback{
public:
//This method is called when an event is fired.
//name: The name of the event.
//obj: Pointer to the GUIObject which caused this event.
//eventType: The type of event as defined above.
virtual void GUIEventCallback_OnEvent(ImageManager& imageManager, SDL_Renderer& renderer, std::string name,GUIObject* obj,int eventType)=0;
};
//Class containing the
class GUIObject{
public:
//The relative x location of the GUIObject.
int left;
//The relative y location of the GUIObject.
int top;
//The width of the GUIObject.
int width;
//The height of the GUIObject.
int height;
//The type of the GUIObject.
int type;
//The value of the GUIObject.
//It depends on the type of GUIObject what it means.
int value;
//The name of the GUIObject.
std::string name;
//The caption of the GUIObject.
//It depends on the type of GUIObject what it is.
std::string caption;
//Boolean if the GUIObject is enabled.
bool enabled;
//Boolean if the GUIObject is visible.
bool visible;
//Vector containing the children of the GUIObject.
std::vector<GUIObject*> childControls;
//Event callback used to invoke events.
GUIEventCallback* eventCallback;
//Widget's gravity to centering
int gravity;
int gravityX;
bool autoWidth;
//Is the parent widget a dialog?
bool inDialog;
//The state of the GUIObject.
//It depends on the type of GUIObject where it's used for.
int state;
protected:
//Texture containing different gui images.
SharedTexture bmGuiTex;
//Surface that can be used to cache rendered text.
TexturePtr cacheTex;
//String containing the old caption to detect if it changed.
std::string cachedCaption;
//Boolean containing the previous enabled state.
bool cachedEnabled;
public:
//Constructor.
//left: The relative x location of the GUIObject.
//top: The relative y location of the GUIObject.
//witdh: The width of the GUIObject.
//height: The height of the GUIObject.
//caption: The text on the GUIObject.
//value: The value of the GUIObject.
//enabled: Boolean if the GUIObject is enabled or not.
//visible: Boolean if the GUIObject is visisble or not.
//gravity: The way the GUIObject needs to be aligned.
- GUIObject(ImageManager& imageManager, SDL_Renderer& renderer,int left=0,int top=0,int width=0,int height=0,
- const char* caption=NULL,int value=0,
- bool enabled=true,bool visible=true,int gravity=0):
- left(left),top(top),width(width),height(height),
- gravity(gravity),value(value),
- enabled(enabled),visible(visible),
- eventCallback(NULL),state(0),
- cachedEnabled(enabled),gravityX(0)
- {
- //Make sure that caption isn't NULL before setting it.
- if(caption){
- GUIObject::caption=caption;
- //And set the cached caption.
- cachedCaption=caption;
- }
-
- if(width<=0)
- autoWidth=true;
- else
- autoWidth=false;
-
- inDialog=false;
-
- //Load the gui images.
- bmGuiTex=imageManager.loadTexture(getDataPath()+"gfx/gui.png", renderer);
- }
+ GUIObject(ImageManager& imageManager, SDL_Renderer& renderer, int left = 0, int top = 0, int width = 0, int height = 0,
+ const char* caption = NULL, int value = 0,
+ bool enabled = true, bool visible = true, int gravity = 0);
+
//Destructor.
virtual ~GUIObject();
//Method used to handle mouse and/or key events.
//x: The x mouse location.
//y: The y mouse location.
//enabled: Boolean if the parent is enabled or not.
//visible: Boolean if the parent is visible or not.
//processed: Boolean if the event has been processed (by the parent) or not.
//Returns: Boolean if the event is processed by the child.
virtual bool handleEvents(SDL_Renderer&renderer, int x=0, int y=0, bool enabled=true, bool visible=true, bool processed=false);
+
//Method that will render the GUIObject.
//x: The x location to draw the GUIObject. (x+left)
//y: The y location to draw the GUIObject. (y+top)
//draw: Draw widget or just update it without drawing
virtual void render(SDL_Renderer& renderer, int x=0,int y=0,bool draw=true);
- void addChild(GUIObject* obj){
- //Add widget add a child
- childControls.push_back(obj);
-
- //Copy inDialog boolean from parent.
- obj->inDialog=inDialog;
- }
+ void addChild(GUIObject* obj);
//Method for getting a child from a GUIObject.
//NOTE: This method doesn't search recursively.
//name: The name of the child to return.
//Returns: Pointer to the requested child, NULL otherwise.
- GUIObject* getChild(std::string name){
- //Look for a child with the name.
- for(unsigned int i=0;i<childControls.size();i++)
- if(childControls[i]->name==name)
- return childControls[i];
-
- //Not found so return NULL.
- return NULL;
- }
+ GUIObject* getChild(std::string name);
//Check if the caption or status has changed, or if the width is <0 and
//recreate the cached texture if so.
void refreshCache(bool enabled);
+
+ //Experimental function to get the index of selected child control in keyboard only mode.
+ //Return value: the index of selected child control. -1 means nothing selected.
+ int getSelectedControl();
+
+ //Experimental function to set the index of selected child control in keyboard only mode.
+ void setSelectedControl(int index);
+
+ //Experimental function to move the focus in keyboard only mode.
+ //direction: the move direction, 1 or -1.
+ //selected: currently selected control (optional). Default value means obtain currently selected control automatically.
+ //Return value: the index of newly selected child control. -1 means nothing selected.
+ int selectNextControl(int direction, int selected = 0x80000000);
+
+ //Experimental function to process keyboard navigation events.
+ //NOTE: This function need to be called manually.
+ //keyboardNavigationMode: a bit-field flags consists of
+ //1=left/right for focus movement
+ //2=up/down for focus movement
+ //4=tab/shift+tab for focus movement
+ //8=return for individual controls
+ //16=left/right for individual controls
+ //Return value: if this event is processed.
+ bool handleKeyboardNavigationEvents(ImageManager& imageManager, SDL_Renderer& renderer, int keyboardNavigationMode);
};
//Method used to handle the GUIEvents from the GUIEventQueue.
//kill: Boolean if an SDL_QUIT event may kill the GUIObjectRoot.
void GUIObjectHandleEvents(ImageManager &imageManager, SDL_Renderer &renderer, bool kill=false);
//A structure containing the needed variables to call an event.
struct GUIEvent{
//Event callback used to invoke the event.
GUIEventCallback* eventCallback;
//The name of the event.
std::string name;
//Pointer to the object which invoked the event.
GUIObject* obj;
//The type of event.
int eventType;
};
//List used to queue the gui events.
extern std::list<GUIEvent> GUIEventQueue;
class GUIButton:public GUIObject{
public:
GUIButton(ImageManager& imageManager, SDL_Renderer& renderer,int left=0,int top=0,int width=0,int height=0,
const char* caption=NULL,int value=0,
bool enabled=true,bool visible=true,int gravity=0):
GUIObject(imageManager,renderer,left,top,width,height,caption,value,enabled,visible,gravity),
smallFont(false){ }
//Method used to handle mouse and/or key events.
//x: The x mouse location.
//y: The y mouse location.
//enabled: Boolean if the parent is enabled or not.
//visible: Boolean if the parent is visible or not.
//processed: Boolean if the event has been processed (by the parent) or not.
//Returns: Boolean if the event is processed by the child.
virtual bool handleEvents(SDL_Renderer&renderer, int x=0, int y=0, bool enabled=true, bool visible=true, bool processed=false);
//Method that will render the GUIScrollBar.
//x: The x location to draw the GUIObject. (x+left)
//y: The y location to draw the GUIObject. (y+top)
//draw: Whether displey the widget or not.
virtual void render(SDL_Renderer& renderer, int x=0,int y=0,bool draw=true);
//Boolean if small font is used.
bool smallFont;
};
class GUICheckBox:public GUIObject{
public:
GUICheckBox(ImageManager& imageManager, SDL_Renderer& renderer,int left=0,int top=0,int width=0,int height=0,
const char* caption=NULL,int value=0,
bool enabled=true,bool visible=true,int gravity=0):
GUIObject(imageManager,renderer,left,top,width,height,caption,value,enabled,visible,gravity){}
//Method used to handle mouse and/or key events.
//x: The x mouse location.
//y: The y mouse location.
//enabled: Boolean if the parent is enabled or not.
//visible: Boolean if the parent is visible or not.
//processed: Boolean if the event has been processed (by the parent) or not.
//Returns: Boolean if the event is processed by the child.
virtual bool handleEvents(SDL_Renderer&,int x=0,int y=0,bool enabled=true,bool visible=true,bool processed=false);
//Method that will render the GUIScrollBar.
//x: The x location to draw the GUIObject. (x+left)
//y: The y location to draw the GUIObject. (y+top)
//draw: Whether displey the widget or not.
virtual void render(SDL_Renderer &renderer, int x=0, int y=0, bool draw=true);
};
class GUILabel:public GUIObject{
public:
GUILabel(ImageManager& imageManager, SDL_Renderer& renderer,int left=0,int top=0,int width=0,int height=0,
const char* caption=NULL,int value=0,
bool enabled=true,bool visible=true,int gravity=0):
GUIObject(imageManager,renderer,left,top,width,height,caption,value,enabled,visible,gravity){}
//Method used to handle mouse and/or key events.
//x: The x mouse location.
//y: The y mouse location.
//enabled: Boolean if the parent is enabled or not.
//visible: Boolean if the parent is visible or not.
//processed: Boolean if the event has been processed (by the parent) or not.
//Returns: Boolean if the event is processed by the child.
virtual bool handleEvents(SDL_Renderer&,int =0, int =0, bool =true, bool =true, bool processed=false);
//Method that will render the GUIScrollBar.
//x: The x location to draw the GUIObject. (x+left)
//y: The y location to draw the GUIObject. (y+top)
//draw: Whether displey the widget or not.
virtual void render(SDL_Renderer &renderer, int x=0, int y=0, bool draw=true);
};
class GUITextBox:public GUIObject{
public:
GUITextBox(ImageManager& imageManager, SDL_Renderer& renderer,int left=0,int top=0,int width=0,int height=0,
const char* caption=NULL,int value=0,
bool enabled=true,bool visible=true,int gravity=0):
GUIObject(imageManager,renderer,left,top,width,height,caption,value,enabled,visible,gravity),
highlightStart(0),highlightEnd(0),highlightStartX(0),highlightEndX(0),tick(15){}
//Method used to handle mouse and/or key events.
//x: The x mouse location.
//y: The y mouse location.
//enabled: Boolean if the parent is enabled or not.
//visible: Boolean if the parent is visible or not.
//processed: Boolean if the event has been processed (by the parent) or not.
//Returns: Boolean if the event is processed by the child.
virtual bool handleEvents(SDL_Renderer&,int x=0,int y=0,bool enabled=true,bool visible=true,bool processed=false);
//Method that will render the GUIScrollBar.
//x: The x location to draw the GUIObject. (x+left)
//y: The y location to draw the GUIObject. (y+top)
//draw: Whether displey the widget or not.
virtual void render(SDL_Renderer& renderer, int x=0,int y=0,bool draw=true);
private:
//Text highlights.
int highlightStart;
int highlightEnd;
int highlightStartX;
int highlightEndX;
//Carrot ticking.
int tick;
//Functions for modifying the text.
void backspaceChar();
void deleteChar();
void inputText(const char* s);
//Functions for moving the carrot.
void moveCarrotLeft();
void moveCarrotRight();
};
class GUIFrame:public GUIObject{
public:
GUIFrame(ImageManager& imageManager, SDL_Renderer& renderer,int left=0,int top=0,int width=0,int height=0,
const char* caption=NULL,int value=0,
bool enabled=true,bool visible=true,int gravity=0):
GUIObject(imageManager,renderer,left,top,width,height,caption,value,enabled,visible,gravity){
inDialog=true;
}
//Method used to handle mouse and/or key events.
//x: The x mouse location.
//y: The y mouse location.
//enabled: Boolean if the parent is enabled or not.
//visible: Boolean if the parent is visible or not.
//processed: Boolean if the event has been processed (by the parent) or not.
//Returns: Boolean if the event is processed by the child.
virtual bool handleEvents(SDL_Renderer&renderer, int x=0, int y=0, bool enabled=true, bool visible=true, bool processed=false);
//Method that will render the GUIScrollBar.
//x: The x location to draw the GUIObject. (x+left)
//y: The y location to draw the GUIObject. (y+top)
//draw: Whether displey the widget or not.
virtual void render(SDL_Renderer &renderer, int x=0, int y=0, bool draw=true);
};
//A GUIObject that holds an shared_ptr to a Texture for rendering.
class GUIImage:public GUIObject{
public:
GUIImage(ImageManager& imageManager, SDL_Renderer& renderer,int left=0,int top=0,int width=0,int height=0,
SharedTexture image=nullptr,SDL_Rect clip=SDL_Rect(),
bool enabled=true,bool visible=true):
GUIObject(imageManager,renderer,left,top,width,height,NULL,0,enabled,visible,0),
image(image),clip(clip){ }
//Destructor.
~GUIImage();
//Method used to handle mouse and/or key events.
//x: The x mouse location.
//y: The y mouse location.
//enabled: Boolean if the parent is enabled or not.
//visible: Boolean if the parent is visible or not.
//processed: Boolean if the event has been processed (by the parent) or not.
//Returns: Boolean if the event is processed by the child.
virtual bool handleEvents(SDL_Renderer&,int =0, int =0, bool =true, bool =true, bool processed=false);
//Method that will render the GUIScrollBar.
//x: The x location to draw the GUIObject. (x+left)
//y: The y location to draw the GUIObject. (y+top)
//draw: Whether display the widget or not.
virtual void render(SDL_Renderer &renderer, int x=0, int y=0, bool draw=true);
//Method that will change the dimensions of the GUIImage so that the full image is shown.
//OR in case of a clip rect that the selected section of the image is shown.
void fitToImage();
//Method for setting the image of the widget.
//image: SharedTexture containing the image.
void setImage(SharedTexture texture){
image=texture;
}
//Method for setting the clip rectangle for the GUIImager.
//rect: The new clip rectangle.
void setClipRect(SDL_Rect rect){
clip=rect;
}
private:
//Pointer to the SDL_Texture to draw.
//MAY BE NULL!!
SharedTexture image;
//Optional rectangle for defining the section of the texture that should be drawn.
//NOTE: This doesn't have to correspond with the dimensions of the GUIObject.
SDL_Rect clip;
};
#endif
diff --git a/src/GUIOverlay.cpp b/src/GUIOverlay.cpp
index b2840c5..2c91032 100644
--- a/src/GUIOverlay.cpp
+++ b/src/GUIOverlay.cpp
@@ -1,340 +1,208 @@
/*
* Copyright (C) 2012-2013 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Functions.h"
#include "GameState.h"
#include "Globals.h"
#include "GUIOverlay.h"
#include "InputManager.h"
#include "GUIObject.h"
#include "GUITextArea.h"
-#include "GUISpinBox.h"
-#include "GUIListBox.h"
//#include "StatisticsManager.h"
using namespace std;
GUIOverlay::GUIOverlay(SDL_Renderer& renderer, GUIObject* root,bool dim)
: root(root), dim(dim), keyboardNavigationMode(0)
{
//First keep the pointer to the current GUIObjectRoot and currentState.
parentState=currentState;
tempGUIObjectRoot=GUIObjectRoot;
//Now set the GUIObject root to the new root.
currentState=this;
GUIObjectRoot=root;
//Dim the background.
if(dim){
dimScreen(renderer);
}
}
GUIOverlay::~GUIOverlay(){
//We need to place everything back.
currentState=parentState;
parentState=NULL;
//Delete the GUI if present.
if(GUIObjectRoot)
delete GUIObjectRoot;
//Now put back the parent gui.
GUIObjectRoot=tempGUIObjectRoot;
tempGUIObjectRoot=NULL;
}
void GUIOverlay::enterLoop(ImageManager& imageManager, SDL_Renderer& renderer, bool skipByEscape, bool skipByReturn){
//Keep the last resize event, this is to only process one.
SDL_Event lastResize = {};
while (GUIObjectRoot){
while(SDL_PollEvent(&event)){
//Check for a resize event.
if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
lastResize = event;
continue;
}
//Check if it's mouse event. If it's true then we quit the keyboard-only mode.
if (event.type == SDL_MOUSEMOTION || event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP) {
isKeyboardOnly = false;
}
//Set the cursor type to the default one, the GUI can change that if needed.
currentCursor = CURSOR_POINTER;
//Let the input manager handle the events.
inputMgr.updateState(true);
//Let the currentState handle the events. (???)
currentState->handleEvents(imageManager, renderer);
//Also pass the events to the GUI.
GUIObjectHandleEvents(imageManager, renderer, true);
//Also check for the escape/return button.
if ((skipByEscape && inputMgr.isKeyDownEvent(INPUTMGR_ESCAPE)) || (skipByReturn && inputMgr.isKeyDownEvent(INPUTMGR_SELECT))) {
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
//Process the resize event.
if (lastResize.type == SDL_WINDOWEVENT){
//TODO - used to be SDL_VIDEORESIZE
// so this may trigger on more events than intended
event = lastResize;
onVideoResize(imageManager, renderer);
//After resize we erase the event type
//TODO - used to be SDL_NOEVENT
lastResize.type = SDL_FIRSTEVENT;
}
//update input state (??)
inputMgr.updateState(false);
//Render the gui.
if(GUIObjectRoot)
GUIObjectRoot->render(renderer);
/*//draw new achievements (if any)
statsMgr.render();*/
//display it
flipScreen(renderer);
SDL_Delay(30);
}
//We broke out so clean up.
delete this;
}
-// internal function which is used in keyboard only mode
-static int getSelectedControl() {
- if (GUIObjectRoot == NULL) return -1;
-
- for (int i = 0; i < (int)GUIObjectRoot->childControls.size(); i++) {
- GUIObject *obj = GUIObjectRoot->childControls[i];
- if (obj && obj->visible && obj->enabled && obj->state) {
- if (dynamic_cast<GUIButton*>(obj) || dynamic_cast<GUICheckBox*>(obj)
- || dynamic_cast<GUITextBox*>(obj) || dynamic_cast<GUISpinBox*>(obj)
- || dynamic_cast<GUISingleLineListBox*>(obj)
- )
- {
- return i;
- }
- }
- }
-
- return -1;
-}
-
-// internal function which is used in keyboard only mode
-static void selectNextControl(int direction) {
- if (GUIObjectRoot == NULL) return;
-
- //Get the index of currently selected control.
- int selected = getSelectedControl();
- if (selected >= 0) GUIObjectRoot->childControls[selected]->state = 0;
-
- //Find the next control.
- for (int i = 0; i < (int)GUIObjectRoot->childControls.size(); i++) {
- if (selected < 0) {
- selected = 0;
- } else {
- selected += direction;
- if (selected >= (int)GUIObjectRoot->childControls.size()) {
- selected -= GUIObjectRoot->childControls.size();
- } else if (selected < 0) {
- selected += GUIObjectRoot->childControls.size();
- }
- }
-
- GUIObject *obj = GUIObjectRoot->childControls[selected];
- if (obj && obj->visible && obj->enabled) {
- if (dynamic_cast<GUIButton*>(obj) || dynamic_cast<GUICheckBox*>(obj)) {
- //It's a button.
- obj->state = 1;
- return;
- } else if (dynamic_cast<GUITextBox*>(obj) || dynamic_cast<GUISpinBox*>(obj)) {
- //It's a text box.
- obj->state = 2;
- return;
- } else if (dynamic_cast<GUISingleLineListBox*>(obj)) {
- //It's a single line list box.
- obj->state = 0x100;
- return;
- }
- }
- }
-}
-
void GUIOverlay::handleEvents(ImageManager& imageManager, SDL_Renderer& renderer){
//Check if we need to quit, if so we enter the exit state.
if(event.type==SDL_QUIT){
setNextState(STATE_EXIT);
}
//Experimental code for keyboard navigation.
- if (keyboardNavigationMode) {
- //Check operation on focused control. These have higher priority.
- if (isKeyboardOnly) {
- //Check enter key.
- if ((keyboardNavigationMode & 8) != 0 && inputMgr.isKeyDownEvent(INPUTMGR_SELECT)) {
- int index = getSelectedControl();
- if (index >= 0) {
- GUIObject *obj = GUIObjectRoot->childControls[index];
-
- if (dynamic_cast<GUIButton*>(obj)) {
- //It's a button.
- if (obj->eventCallback) {
- obj->eventCallback->GUIEventCallback_OnEvent(imageManager, renderer, obj->name, obj, GUIEventClick);
- }
- return;
- }
- if (dynamic_cast<GUICheckBox*>(obj)) {
- //It's a check box.
- obj->value = obj->value ? 0 : 1;
- if (obj->eventCallback) {
- obj->eventCallback->GUIEventCallback_OnEvent(imageManager, renderer, obj->name, obj, GUIEventClick);
- }
- return;
- }
- }
- }
- //Check left/right key.
- if ((keyboardNavigationMode & 16) != 0 && (inputMgr.isKeyDownEvent(INPUTMGR_LEFT) || inputMgr.isKeyDownEvent(INPUTMGR_RIGHT))) {
- int index = getSelectedControl();
- if (index >= 0) {
- GUIObject *obj = GUIObjectRoot->childControls[index];
-
- auto sllb = dynamic_cast<GUISingleLineListBox*>(obj);
- if (sllb) {
- //It's a single line list box.
- int newValue = sllb->value + (inputMgr.isKeyDownEvent(INPUTMGR_RIGHT) ? 1 : -1);
- if (newValue >= (int)sllb->item.size()) {
- newValue -= sllb->item.size();
- } else if (newValue < 0) {
- newValue += sllb->item.size();
- }
-
- if (sllb->value != newValue) {
- sllb->value = newValue;
- if (obj->eventCallback) {
- obj->eventCallback->GUIEventCallback_OnEvent(imageManager, renderer, obj->name, obj, GUIEventClick);
- }
- }
- return;
- }
- }
-
- }
- }
-
- //Check focus movement
- int m = SDL_GetModState();
- if (((keyboardNavigationMode & 1) != 0 && inputMgr.isKeyDownEvent(INPUTMGR_RIGHT))
- || ((keyboardNavigationMode & 2) != 0 && inputMgr.isKeyDownEvent(INPUTMGR_DOWN))
- || ((keyboardNavigationMode & 4) != 0 && inputMgr.isKeyDownEvent(INPUTMGR_TAB) && (m & KMOD_SHIFT) == 0)
- )
- {
- isKeyboardOnly = true;
- selectNextControl(1);
- } else if (((keyboardNavigationMode & 1) != 0 && inputMgr.isKeyDownEvent(INPUTMGR_LEFT))
- || ((keyboardNavigationMode & 2) != 0 && inputMgr.isKeyDownEvent(INPUTMGR_UP))
- || ((keyboardNavigationMode & 4) != 0 && inputMgr.isKeyDownEvent(INPUTMGR_TAB) && (m & KMOD_SHIFT) != 0)
- )
- {
- isKeyboardOnly = true;
- selectNextControl(-1);
- }
+ if (GUIObjectRoot && keyboardNavigationMode) {
+ GUIObjectRoot->handleKeyboardNavigationEvents(imageManager, renderer, keyboardNavigationMode);
}
}
//Nothing to do here
void GUIOverlay::logic(ImageManager&, SDL_Renderer&){
//Check if the GUIObjectRoot (of the overlay) is deleted.
if(!GUIObjectRoot)
delete this;
}
void GUIOverlay::render(ImageManager&, SDL_Renderer&){}
void GUIOverlay::resize(ImageManager& imageManager, SDL_Renderer& renderer){
//We recenter the GUI.
GUIObjectRoot->left=(SCREEN_WIDTH-GUIObjectRoot->width)/2;
GUIObjectRoot->top=(SCREEN_HEIGHT-GUIObjectRoot->height)/2;
//Now let the parent state resize.
GUIObjectRoot=tempGUIObjectRoot;
parentState->resize(imageManager, renderer);
//NOTE: After the resize it's likely that the GUIObjectRoot is new so we need to update our tempGUIObjectRoot pointer.
tempGUIObjectRoot=GUIObjectRoot;
//Now render the parentState.
parentState->render(imageManager,renderer);
if(GUIObjectRoot)
GUIObjectRoot->render(renderer);
//And set the GUIObjectRoot back to the overlay gui.
GUIObjectRoot=root;
//Dim the background.
if(dim){
dimScreen(renderer);
}
}
-AddonOverlay::AddonOverlay(SDL_Renderer &renderer, GUIObject* root, GUIButton *cancelButton, GUITextArea *textArea)
+AddonOverlay::AddonOverlay(SDL_Renderer &renderer, GUIObject* root, GUIButton *cancelButton, GUITextArea *textArea, int keyboardNavigationMode)
: GUIOverlay(renderer, root), cancelButton(cancelButton), textArea(textArea)
{
- keyboardNavigationMode = 4 | 8 | 16;
+ this->keyboardNavigationMode = keyboardNavigationMode;
}
void AddonOverlay::handleEvents(ImageManager& imageManager, SDL_Renderer& renderer) {
GUIOverlay::handleEvents(imageManager, renderer);
//Do our own stuff.
//Scroll the text area.
if (textArea) {
if (inputMgr.isKeyDownEvent(INPUTMGR_RIGHT)){
isKeyboardOnly = true;
textArea->scrollScrollbar(20, 0);
} else if (inputMgr.isKeyDownEvent(INPUTMGR_LEFT)){
isKeyboardOnly = true;
textArea->scrollScrollbar(-20, 0);
} else if (inputMgr.isKeyDownEvent(INPUTMGR_UP)){
isKeyboardOnly = true;
textArea->scrollScrollbar(0, -1);
} else if (inputMgr.isKeyDownEvent(INPUTMGR_DOWN)){
isKeyboardOnly = true;
textArea->scrollScrollbar(0, 1);
}
}
//Check escape key.
if (cancelButton && cancelButton->eventCallback && inputMgr.isKeyDownEvent(INPUTMGR_ESCAPE)){
cancelButton->eventCallback->GUIEventCallback_OnEvent(imageManager, renderer, cancelButton->name, cancelButton, GUIEventClick);
return;
}
}
diff --git a/src/GUIOverlay.h b/src/GUIOverlay.h
index 65efe6e..d9f7d9a 100644
--- a/src/GUIOverlay.h
+++ b/src/GUIOverlay.h
@@ -1,92 +1,88 @@
/*
* Copyright (C) 2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GUI_OVERLAY_H
#define GUI_OVERLAY_H
#include <SDL.h>
#include "GameState.h"
#include "GUIObject.h"
//The GUIOverlay state, this is a special state since it doens't appear in the states list.
//It is used to properly handle GUIs that overlay the current state, dialogs for example.
class GUIOverlay : public GameState{
private:
//A pointer to the current state to put back when needed.
GameState* parentState;
//Pointer to the GUI root of the overlay.
GUIObject* root;
//Pointer to the previous GUIObjectRoot.
GUIObject* tempGUIObjectRoot;
//Boolean if the screen should be dimmed.
bool dim;
public:
- //Enables default keyboard navigation code. This is a bit-field flags consists of
- //1=left/right for focus movement
- //2=up/down for focus movement
- //4=tab/shift+tab for focus movement
- //8=return for individual controls
- //16=left/right for individual controls
+ //Enables default keyboard navigation code.
+ //See GUIObject::handleKeyboardNavigationEvents for more info.
int keyboardNavigationMode;
public:
//Constructor.
//root: Pointer to the new GUIObjectRoot.
//dim: Boolean if the parent state should be dimmed.
GUIOverlay(SDL_Renderer &renderer, GUIObject* root, bool dim=true);
//Destructor.
~GUIOverlay();
//Method that can be used to create a "sub gameloop".
//This is usefull in case the GUI that is overlayed is used for userinput which the function needs to return.
//NOTE: This loop should be kept similar to the main loop.
//skip: Boolean if this GUIOverlay can be "skipped", meaning it can be exited quickly by pressing escape or return.
void enterLoop(ImageManager &imageManager, SDL_Renderer& renderer, bool skipByEscape = false, bool skipByReturn = false);
//Inherited from GameState.
void handleEvents(ImageManager&, SDL_Renderer&) override;
void logic(ImageManager&, SDL_Renderer&) override;
void render(ImageManager&, SDL_Renderer&) override;
void resize(ImageManager& imageManager, SDL_Renderer& renderer) override;
};
class GUIButton;
class GUITextArea;
// A subclass of GUIOverlay which has:
// - a fixed keyboard navigation mode (4 | 8 | 16)
// - an optional text area which is scrolled by arrow keys
// - an optional cancel button used to close automatically.
class AddonOverlay : public GUIOverlay {
private:
GUIButton *cancelButton;
GUITextArea *textArea;
public:
- AddonOverlay(SDL_Renderer &renderer, GUIObject* root, GUIButton *cancelButton, GUITextArea *textArea);
+ AddonOverlay(SDL_Renderer &renderer, GUIObject* root, GUIButton *cancelButton, GUITextArea *textArea, int keyboardNavigationMode = 4 | 8 | 16);
void handleEvents(ImageManager& imageManager, SDL_Renderer& renderer) override;
};
#endif
diff --git a/src/GUISlider.cpp b/src/GUISlider.cpp
index b1809b7..8548f62 100644
--- a/src/GUISlider.cpp
+++ b/src/GUISlider.cpp
@@ -1,271 +1,276 @@
/*
* Copyright (C) 2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Functions.h"
#include "GUISlider.h"
using namespace std;
void GUISlider::calcPos(){
//Floats ...
float f,f2;
//The value can't be below the minimum value or above the maximum.
if(value<minValue)
value=minValue;
else if(value>maxValue)
value=maxValue;
//
f=(float)(left+1);
f2=(float)(width-2);
if(largeChange<=0) f2=-1;
if(f2>0){
float f1=0.0f;
valuePerPixel = (maxValue - minValue + largeChange) / f2;
if(valuePerPixel > 0.0001f) f1 = largeChange / valuePerPixel;
if(f1 < 4 && f2 > 4){
valuePerPixel = (maxValue - minValue) / (f2 - 4);
f1 = 4;
}
thumbStart = f + (value - minValue) / valuePerPixel;
thumbEnd = thumbStart + f1;
}else{
valuePerPixel = -1;
thumbStart = f;
thumbEnd = f - 1;
}
}
bool GUISlider::handleEvents(SDL_Renderer&,int x,int y,bool enabled,bool visible,bool processed){
//Boolean if the event is processed.
bool b=processed;
//The GUIObject is only enabled when he and his parent are enabled.
enabled=enabled && this->enabled;
//The GUIObject is only enabled when he and his parent are enabled.
visible=visible && this->visible;
- //Check if the mouse button is released.
- if(event.type==SDL_MOUSEBUTTONUP || !(enabled&&visible)){
- //It so we have lost any focus at all.
- state=0;
- }else if(event.type==SDL_MOUSEMOTION || event.type==SDL_MOUSEBUTTONDOWN){
- //The mouse button is down and it's moving
- int i,j,k,f,f1;
- state&=~0xFF;
- k=SDL_GetMouseState(&i,&j);
- i-=x;
- j-=y;
- bool bInControl_0;
-
- f=left;
- f1=f+width;
- bInControl_0=(j>=top&&j<top+height);
-
- //===
- if((state&0x0000FF00)==0x300&&(k&SDL_BUTTON(1))&&event.type==SDL_MOUSEMOTION&&valuePerPixel>0){
- //drag thumb
- state|=3;
- int val = criticalValue + (int)(((float)i - startDragPos) * valuePerPixel + 0.5f);
- if(val<minValue) val=minValue;
- else if(val>maxValue) val=maxValue;
- if(value!=val){
- value=val;
- changed=true;
- }
- b=true;
- }else if(bInControl_0){
- int f2,f3;
- if(valuePerPixel > 0){
- f2=f+16;
- f3=f1-16;
- }else{
- f2=f3=(f+f1)/2;
- }
- if(i<f){ //do nothing
- }else if(valuePerPixel<=0){ //do nothing
- }else if(i<(int)thumbStart){ //-largechange
- state=(state&~0xFF)|2;
- if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_LEFT) state=(state&~0x0000FF00)|((state&0xFF)<<8);
- else if((state&0x0000FF00)&&((state&0xFF)!=((state>>8)&0xFF))) state&=~0xFF;
- if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_LEFT){
- int val=value-largeChange;
- if(val<minValue) val=minValue;
- if(value!=val){
- value=val;
- changed=true;
- }
- timer=8;
- }
- if(state&0xFF) criticalValue = minValue + (int)(float(i - f2) * valuePerPixel + 0.5f);
- b=true;
- }else if(i<(int)thumbEnd){ //start drag
- state=(state&~0xFF)|3;
- if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_LEFT) state=(state&~0x0000FF00)|((state&0xFF)<<8);
- else if((state&0x0000FF00)&&((state&0xFF)!=((state>>8)&0xFF))) state&=~0xFF;
- if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_LEFT){
- criticalValue=value;
- startDragPos = (float)i;
+ //Check enabled and visible.
+ if (!(enabled && visible)) {
+ state = 0;
+ } else if (isKeyboardOnly) {
+ //Do nothing on keyboard only mode.
+ } else {
+ //Check if the mouse button is released.
+ if (event.type == SDL_MOUSEBUTTONUP){
+ //It so we have lost any focus at all.
+ state = 0;
+ } else if (event.type == SDL_MOUSEMOTION || event.type == SDL_MOUSEBUTTONDOWN){
+ //The mouse button is down and it's moving
+ int i, j, k, f, f1;
+ state &= ~0xFF;
+ k = SDL_GetMouseState(&i, &j);
+ i -= x;
+ j -= y;
+ bool bInControl_0;
+
+ f = left;
+ f1 = f + width;
+ bInControl_0 = (j >= top&&j < top + height);
+
+ //===
+ if ((state & 0x0000FF00) == 0x300 && (k&SDL_BUTTON(1)) && event.type == SDL_MOUSEMOTION && valuePerPixel>0){
+ //drag thumb
+ state |= 3;
+ int val = criticalValue + (int)(((float)i - startDragPos) * valuePerPixel + 0.5f);
+ if (val<minValue) val = minValue;
+ else if (val>maxValue) val = maxValue;
+ if (value != val){
+ value = val;
+ changed = true;
}
- b=true;
- }else if(i<f3){ //+largechange
- state=(state&~0xFF)|4;
- if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_LEFT) state=(state&~0x0000FF00)|((state&0xFF)<<8);
- else if((state&0x0000FF00)&&((state&0xFF)!=((state>>8)&0xFF))) state&=~0xFF;
- if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_LEFT){
- int val=value+largeChange;
- if(val>maxValue) val=maxValue;
- if(value!=val){
- value=val;
- changed=true;
+ b = true;
+ } else if (bInControl_0){
+ if (i < f){ //do nothing
+ } else if (valuePerPixel <= 0){ //do nothing
+ } else if (i < (int)thumbStart){ //-largechange
+ state = (state&~0xFF) | 2;
+ if (event.type == SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_LEFT) state = (state&~0x0000FF00) | ((state & 0xFF) << 8);
+ else if ((state & 0x0000FF00) && ((state & 0xFF) != ((state >> 8) & 0xFF))) state &= ~0xFF;
+ if (event.type == SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_LEFT){
+ int val = value - largeChange;
+ if (val < minValue) val = minValue;
+ if (value != val){
+ value = val;
+ changed = true;
+ }
+ timer = 8;
+ }
+ if (state & 0xFF) criticalValue = minValue + (int)(float(i - f) * valuePerPixel + 0.5f);
+ b = true;
+ } else if (i < (int)thumbEnd){ //start drag
+ state = (state&~0xFF) | 3;
+ if (event.type == SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_LEFT) state = (state&~0x0000FF00) | ((state & 0xFF) << 8);
+ else if ((state & 0x0000FF00) && ((state & 0xFF) != ((state >> 8) & 0xFF))) state &= ~0xFF;
+ if (event.type == SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_LEFT){
+ criticalValue = value;
+ startDragPos = (float)i;
}
- timer=8;
+ b = true;
+ } else if (i < f1){ //+largechange
+ state = (state&~0xFF) | 4;
+ if (event.type == SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_LEFT) state = (state&~0x0000FF00) | ((state & 0xFF) << 8);
+ else if ((state & 0x0000FF00) && ((state & 0xFF) != ((state >> 8) & 0xFF))) state &= ~0xFF;
+ if (event.type == SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_LEFT){
+ int val = value + largeChange;
+ if (val > maxValue) val = maxValue;
+ if (value != val){
+ value = val;
+ changed = true;
+ }
+ timer = 8;
+ }
+ if (state & 0xFF) criticalValue = minValue - largeChange + (int)(float(i - f) * valuePerPixel + 0.5f);
+ b = true;
}
- if(state&0xFF) criticalValue = minValue - largeChange + (int)(float(i - f2) * valuePerPixel + 0.5f);
- b=true;
}
}
}
return b;
}
void GUISlider::renderScrollBarButton(SDL_Renderer &renderer, int index, int x1, int y1, int x2, int y2, int srcleft, int srctop){
//Make sure the button isn't inverted.
if(x2<=x1||y2<=y1)
return;
-
+
//The color.
int clr=-1;
//Rectangle the size of the button.
SDL_Rect r={x1,y1,x2-x1,y2-y1};
//Check
if((state&0xFF)==index){
if(((state>>8)&0xFF)==index){
//Set the color gray.
clr=0xDDDDDDFF;
}else{
//Set the color to lightgray.
clr=0xFFFFFFFF;
}
}
//Draw a box.
drawGUIBox(r.x,r.y,r.w,r.h,renderer,clr);
//Boolean if there should be an image on the button.
const bool b = (x2-x1>=14);
//Check if the image can be drawn.
if(b&&srcleft>=0&&srctop>=0){
//It can thus draw it.
const SDL_Rect srcRect={srcleft,srctop,16,16};
const SDL_Rect dstRect={(x1+x2)/2-8, (y1+y2)/2-8, srcRect.w, srcRect.h};
SDL_RenderCopy(&renderer, bmGuiTex.get(), &srcRect, &dstRect);
}
}
void GUISlider::render(SDL_Renderer &renderer, int x, int y, bool draw){
//There's no use in rendering the scrollbar when invisible.
if(!visible||!draw)
return;
-
+
+ //Draw highlight on keyboard only mode.
+ if (isKeyboardOnly && state) {
+ drawGUIBox(x + left, y + top, width, height, renderer, 0xFFFFFF40);
+ }
+
//Check if the scrollbar is enabled.
if(enabled){
//Check if the state is right.
if((state&0xFF)==((state>>8)&0xFF)){
//Switch the state (change)/
switch(state&0xFF){
case 2:
//It's a lager negative change.
//Check if it's time.
if((--timer)<=0){
if(value<criticalValue)
state&=~0xFF;
else{
//Reduce the value.
int val=value-largeChange;
//Make sure it doesn't go too low.
if(val<minValue)
val=minValue;
if(value!=val){
value=val;
changed=true;
}
//Set the time to two.
timer=2;
}
}
break;
case 4:
//It's a lager positive change.
//Check if it's time.
if((--timer)<=0){
if(value>criticalValue)
state&=~0xFF;
else{
//Increase the value.
int val=value+largeChange;
//Make sure it doesn't go too high.
if(val>maxValue)
val=maxValue;
if(value!=val){
value=val;
changed=true;
}
//Set the time to two.
timer=2;
}
}
break;
}
}
}
//If the scrollbar changed then invoke a GUIEvent.
if(changed){
if(eventCallback){
GUIEvent e={eventCallback,name,this,GUIEventChange};
GUIEventQueue.push_back(e);
}
changed=false;
}
//We calculate the position since it could have changed.
calcPos();
//Now the actual drawing begins.
if(valuePerPixel>0){
//Draw the line the slider moves along.
drawGUIBox(x+left,y+top+(height-4)/2,width,4,renderer,0);
- renderScrollBarButton(renderer,2,x-1+(int)thumbStart,y+top+(height/4),x+1+(int)thumbEnd,y+top+(height/4)*3,16,16);
+ renderScrollBarButton(renderer,3,x-1+(int)thumbStart,y+top+(height/4),x+1+(int)thumbEnd,y+top+(height/4)*3,16,16);
}else{
//There are two buttons so draw them.
int f=left+width/2;
renderScrollBarButton(renderer,1,x+left,y+top,x+1+f,y+top+height,48,0);
renderScrollBarButton(renderer,5,x+f,y+top,x+left+width,y+top+height,64,0);
}
}
diff --git a/src/GUISpinBox.cpp b/src/GUISpinBox.cpp
index 57238e0..89c1821 100644
--- a/src/GUISpinBox.cpp
+++ b/src/GUISpinBox.cpp
@@ -1,354 +1,354 @@
/*
* Copyright (C) 2011-2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Functions.h"
#include "GUISpinBox.h"
#include "ThemeManager.h"
#include <algorithm>
#include <SDL_ttf.h>
bool GUISpinBox::handleEvents(SDL_Renderer&,int x,int y,bool enabled,bool visible,bool processed){
//Boolean if the event is processed.
bool b=processed;
//The GUIObject is only enabled when he and his parent are enabled.
enabled=enabled && this->enabled;
//The GUIObject is only enabled when he and his parent are enabled.
visible=visible && this->visible;
//Get the absolute position.
x+=left-gravityX;
y+=top;
//Reset "key" to stop contant update of "number" in render().
//If the mouse is still on the button, the "key" will be reassigned later.
key=-1;
//Only check for events when the object is both enabled and visible.
if(enabled&&visible){
//Check if there's a key press and the event hasn't been already processed.
if(state==2 && event.type==SDL_KEYDOWN && !b){
//Get the keycode.
SDL_Keycode key=event.key.keysym.sym;
//Check if the key is supported.
if(key>=32&&key<=126){
//Add the key to the text after the carrot.
caption.insert((size_t)value,1,char(key));
value=clamp(value+1,0,caption.length());
//If there is an event callback then call it.
if(eventCallback){
GUIEvent e={eventCallback,name,this,GUIEventChange};
GUIEventQueue.push_back(e);
}
}else if(event.key.keysym.sym==SDLK_BACKSPACE){
//We need to remove a character so first make sure that there is text.
if(caption.length()>0&&value>0){
//Remove the character before the carrot.
value=clamp(value-1,0,caption.length());
caption.erase((size_t)value,1);
//If there is an event callback then call it.
if(eventCallback){
GUIEvent e={eventCallback,name,this,GUIEventChange};
GUIEventQueue.push_back(e);
}
}
}else if(event.key.keysym.sym==SDLK_DELETE){
//We need to remove a character so first make sure that there is text.
if(caption.length()>0){
//Remove the character after the carrot.
value=clamp(value,0,caption.length());
caption.erase((size_t)value,1);
//If there is an event callback then call it.
if(eventCallback){
GUIEvent e={eventCallback,name,this,GUIEventChange};
GUIEventQueue.push_back(e);
}
}
}else if(event.key.keysym.sym==SDLK_RIGHT){
value=clamp(value+1,0,caption.length());
}else if(event.key.keysym.sym==SDLK_LEFT){
value=clamp(value-1,0,caption.length());
}
//The event has been processed.
b=true;
}
//Only process mouse event when not in keyboard only mode
if (!isKeyboardOnly) {
//The mouse location (x=i, y=j) and the mouse button (k).
int i, j, k;
k = SDL_GetMouseState(&i, &j);
//Check if the mouse is inside the GUIObject.
- if (i >= x&&i < x + width&&j >= y&&j < y + height){
+ if (i >= x && i < x + width && j >= y && j < y + height){
//We can only increase our state. (nothing->hover->focus).
if (state != 2){
state = 1;
}
//Also update the cursor type.
if (i < x + width - 16)
currentCursor = CURSOR_CARROT;
//Check for a mouse button press.
if (k&SDL_BUTTON(1)){
//We have focus.
state = 2;
//Handle buttons.
if (i > x + width - 16){
if (j < y + 17){
//Set the key values correct.
this->key = SDLK_UP;
keyHoldTime = 0;
keyTime = 5;
//Update once to prevent a lag.
updateValue(true);
} else{
//Set the key values correct.
this->key = SDLK_DOWN;
keyHoldTime = 0;
keyTime = 5;
//Update once to prevent a lag.
updateValue(false);
}
} else{
//Move caret to the place clicked
int click = i - x;
if (!cacheTex){
value = 0;
} else if (click > textureWidth(*cacheTex)){
value = caption.length();
} else{
unsigned int wid = 0;
for (unsigned int i = 0; i < caption.length(); i++){
int advance;
TTF_GlyphMetrics(fontText, caption[i], NULL, NULL, NULL, NULL, &advance);
wid += advance;
if (click < (int)wid - (int)advance / 2){
value = i;
break;
}
}
}
}
}
//Allow mouse wheel to change value.
if (event.type == SDL_MOUSEWHEEL){
if (event.wheel.y > 0){
updateValue(true);
} else if (event.wheel.y < 0){
updateValue(false);
}
}
} else{
//The mouse is outside the TextBox.
//If we don't have focus but only hover we lose it.
if (state == 1){
state = 0;
update();
}
//If it's a click event outside the textbox then we blur.
if (event.type == SDL_MOUSEBUTTONUP && event.button.button == SDL_BUTTON_LEFT){
//Set state to 0.
state = 0;
update();
}
}
}
}
return b;
}
void GUISpinBox::render(SDL_Renderer &renderer, int x, int y, bool draw){
//FIXME: Logic in the render method since that is update constant.
if(key!=-1){
//Increase the key time.
keyHoldTime++;
//Make sure the deletionTime isn't to short.
if(keyHoldTime>=keyTime){
keyHoldTime=0;
keyTime--;
if(keyTime<1)
keyTime=1;
//Now check the which key it was.
switch(key){
case SDLK_UP:
{
updateValue(true);
break;
}
case SDLK_DOWN:
{
updateValue(false);
break;
}
}
}
}
//There's no need drawing when it's invisible.
if(!visible)
return;
//Get the absolute x and y location.
x+=left;
y+=top;
//Check if the enabled state changed or the caption, if so we need to clear the (old) cache.
if(enabled!=cachedEnabled || caption.compare(cachedCaption)!=0 || width<=0){
//Free the cache.
cacheTex.reset(nullptr);
//And cache the new values.
cachedEnabled=enabled;
cachedCaption=caption;
//Finally resize the widget
if(autoWidth)
width=-1;
}
if(draw){
//Default background opacity.
int clr=50;
//If hovering or focused make background more visible.
if(state==1)
clr=128;
else if (state==2)
clr=100;
//Draw a background box.
const Uint32 color=0xFFFFFF00|clr;
drawGUIBox(x,y,width,height,renderer,color);
SDL_Rect srcRect={80,0,16,16};
//Draw arrow buttons.
SDL_Rect dstRect = {x+width-18, y+1, srcRect.w, srcRect.h};
SDL_RenderCopy(&renderer, bmGuiTex.get(), &srcRect, &dstRect);
srcRect.x=96;
dstRect.y+=16;
SDL_RenderCopy(&renderer, bmGuiTex.get(), &srcRect, &dstRect);
}
if(!caption.empty()){
//Update graphic cache if empty.
if(!cacheTex){
cacheTex=textureFromText(renderer,*fontText,caption.c_str(),objThemes.getTextColor(true));
}
//Cache the render color
Uint8 r,g,b,a;
SDL_GetRenderDrawColor(&renderer, &r,&g,&b,&a);
//Set it to black for drawing the caret.
SDL_SetRenderDrawColor(&renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
if(draw){
const SDL_Rect srcRect={0,0,width-2,25};
SDL_Rect dstRect = rectFromTexture(x+2,
y+(height-textureHeight(*cacheTex))/2,
*cacheTex);
dstRect.w = std::min(dstRect.w, width - 4);
SDL_RenderCopy(&renderer, cacheTex.get(), &srcRect, &dstRect);
//Only draw the caret when focus.
if(state==2){
SDL_Rect caretRect{x, y+4, 2, height-8};
int advance;
for(int n=0;n<value;n++){
TTF_GlyphMetrics(fontText,caption[n],NULL,NULL,NULL,NULL,&advance);
caretRect.x+=advance;
}
//Make sure that the carrot is inside the textbox.
if(caretRect.x<x+width) {
//Draw a box representing the caret.
SDL_RenderFillRect(&renderer, &caretRect);
}
}
}else{
//Only draw the caret when focus.
if(state==2&&draw){
const SDL_Rect caretRect{x+4, y+4, 2, height-8};
SDL_RenderFillRect(&renderer, &caretRect);
}
}
//Reset the render color.
SDL_SetRenderDrawColor(&renderer, r, g, b, a);
}
}
void GUISpinBox::update(){
//Read number from the caption string.
float number=(float)atof(caption.c_str());
//Stay in the limits.
if(number>limitMax){
number=limitMax;
}else if(number<limitMin){
number=limitMin;
}
//Write the number to the caption string.
char str[32];
sprintf(str,format,number);
caption=str;
}
void GUISpinBox::updateValue(bool positive){
//Read number from the caption string.
float number=(float)atof(caption.c_str());
//Apply change.
if(positive)
number+=change;
else
number-=change;
//Stay in the limits.
if(number>limitMax){
number=limitMax;
}else if(number<limitMin){
number=limitMin;
}
//Write the number to the caption string.
char str[32];
sprintf(str,format,number);
caption=str;
// restrict the caret position
value = clamp(value, 0, caption.length());
}
diff --git a/src/LevelEditSelect.cpp b/src/LevelEditSelect.cpp
index 75a118f..e9714ca 100644
--- a/src/LevelEditSelect.cpp
+++ b/src/LevelEditSelect.cpp
@@ -1,815 +1,815 @@
/*
* Copyright (C) 2012-2013 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#include "LevelEditSelect.h"
#include "GameState.h"
#include "Functions.h"
#include "FileManager.h"
#include "Globals.h"
#include "GUIObject.h"
#include "GUIListBox.h"
#include "GUIScrollBar.h"
#include "InputManager.h"
#include "StatisticsManager.h"
#include "Game.h"
#include "GUIOverlay.h"
#include <algorithm>
#include <string>
#include <iostream>
#include "libs/tinyformat/tinyformat.h"
using namespace std;
LevelEditSelect::LevelEditSelect(ImageManager& imageManager, SDL_Renderer& renderer):LevelSelect(imageManager,renderer,_("Map Editor"),LevelPackManager::CUSTOM_PACKS){
//Create the gui.
createGUI(imageManager,renderer, true);
//Set the levelEditGUIObjectRoot.
levelEditGUIObjectRoot=GUIObjectRoot;
//show level list
changePack();
refresh(imageManager, renderer);
}
LevelEditSelect::~LevelEditSelect(){
selectedNumber=NULL;
}
void LevelEditSelect::createGUI(ImageManager& imageManager,SDL_Renderer &renderer, bool initial){
if(initial){
//The levelpack name text field.
levelpackName=new GUITextBox(imageManager,renderer,280,104,240,32);
levelpackName->eventCallback=this;
levelpackName->visible=false;
GUIObjectRoot->addChild(levelpackName);
//Create the six buttons at the bottom of the screen.
newPack = new GUIButton(imageManager, renderer, 0, 0, -1, 32, _("New Levelpack"));
newPack->name = "cmdNewLvlpack";
newPack->eventCallback = this;
GUIObjectRoot->addChild(newPack);
propertiesPack = new GUIButton(imageManager, renderer, 0, 0, -1, 32, _("Pack Properties"), 0, true, true, GUIGravityCenter);
propertiesPack->name = "cmdLvlpackProp";
propertiesPack->eventCallback = this;
GUIObjectRoot->addChild(propertiesPack);
removePack = new GUIButton(imageManager, renderer, 0, 0, -1, 32, _("Remove Pack"), 0, true, true, GUIGravityRight);
removePack->name = "cmdRmLvlpack";
removePack->eventCallback = this;
GUIObjectRoot->addChild(removePack);
move = new GUIButton(imageManager, renderer, 0, 0, -1, 32, _("Move Map"));
move->name = "cmdMoveMap";
move->eventCallback = this;
//NOTE: Set enabled equal to the inverse of initial.
//When resizing the window initial will be false and therefor the move button can stay enabled.
move->enabled = false;
GUIObjectRoot->addChild(move);
remove = new GUIButton(imageManager, renderer, 0, 0, -1, 32, _("Remove Map"), 0, false, true, GUIGravityCenter);
remove->name = "cmdRmMap";
remove->eventCallback = this;
GUIObjectRoot->addChild(remove);
edit = new GUIButton(imageManager, renderer, 0, 0, -1, 32, _("Edit Map"), 0, false, true, GUIGravityRight);
edit->name = "cmdEdit";
edit->eventCallback = this;
GUIObjectRoot->addChild(edit);
}
//Move buttons to default position
const int x1 = int(SCREEN_WIDTH*0.02), x2 = int(SCREEN_WIDTH*0.5), x3 = int(SCREEN_WIDTH*0.98);
const int y1 = SCREEN_HEIGHT - 120, y2 = SCREEN_HEIGHT - 60;
newPack->left = x1; newPack->top = y1; newPack->gravity = GUIGravityLeft;
propertiesPack->left = x2; propertiesPack->top = y1; propertiesPack->gravity = GUIGravityCenter;
removePack->left = x3; removePack->top = y1; removePack->gravity = GUIGravityRight;
move->left = x1; move->top = y2; move->gravity = GUIGravityLeft;
remove->left = x2; remove->top = y2; remove->gravity = GUIGravityCenter;
edit->left = x3; edit->top = y2; edit->gravity = GUIGravityRight;
isVertical = false;
//Reset the font size
newPack->smallFont = false; newPack->width = -1;
propertiesPack->smallFont = false; propertiesPack->width = -1;
removePack->smallFont = false; removePack->width = -1;
move->smallFont = false; move->width = -1;
remove->smallFont = false; remove->width = -1;
edit->smallFont = false; edit->width = -1;
//Now update widgets and then check if they overlap
GUIObjectRoot->render(renderer, 0, 0, false);
if (propertiesPack->left - propertiesPack->gravityX < newPack->left + newPack->width ||
propertiesPack->left - propertiesPack->gravityX + propertiesPack->width > removePack->left - removePack->gravityX)
{
newPack->smallFont = true; newPack->width = -1;
propertiesPack->smallFont = true; propertiesPack->width = -1;
removePack->smallFont = true; removePack->width = -1;
move->smallFont = true; move->width = -1;
remove->smallFont = true; remove->width = -1;
edit->smallFont = true; edit->width = -1;
}
// NOTE: the following code is necessary (e.g. for Germany)
//Check again
GUIObjectRoot->render(renderer, 0, 0, false);
if (propertiesPack->left - propertiesPack->gravityX < newPack->left + newPack->width ||
propertiesPack->left - propertiesPack->gravityX + propertiesPack->width > removePack->left - removePack->gravityX)
{
newPack->left = SCREEN_WIDTH*0.02;
newPack->top = SCREEN_HEIGHT - 140;
newPack->smallFont = false;
newPack->width = -1;
newPack->gravity = GUIGravityLeft;
propertiesPack->left = SCREEN_WIDTH*0.02;
propertiesPack->top = SCREEN_HEIGHT - 100;
propertiesPack->smallFont = false;
propertiesPack->width = -1;
propertiesPack->gravity = GUIGravityLeft;
removePack->left = SCREEN_WIDTH*0.02;
removePack->top = SCREEN_HEIGHT - 60;
removePack->smallFont = false;
removePack->width = -1;
removePack->gravity = GUIGravityLeft;
move->left = SCREEN_WIDTH*0.98;
move->top = SCREEN_HEIGHT - 140;
move->smallFont = false;
move->width = -1;
move->gravity = GUIGravityRight;
remove->left = SCREEN_WIDTH*0.98;
remove->top = SCREEN_HEIGHT - 100;
remove->smallFont = false;
remove->width = -1;
remove->gravity = GUIGravityRight;
edit->left = SCREEN_WIDTH*0.98;
edit->top = SCREEN_HEIGHT - 60;
edit->smallFont = false;
edit->width = -1;
edit->gravity = GUIGravityRight;
isVertical = true;
}
}
void LevelEditSelect::changePack(){
packName=levelpacks->item[levelpacks->value].second;
if(packName=="Custom Levels"){
//Disable some levelpack buttons.
propertiesPack->enabled=false;
removePack->enabled=false;
}else{
//Enable some levelpack buttons.
propertiesPack->enabled=true;
removePack->enabled=true;
}
//Set last levelpack.
getSettings()->setValue("lastlevelpack",levelpacks->getName());
//Now let levels point to the right pack.
levels=getLevelPackManager()->getLevelPack(levelpacks->getName());
//invalidate the tooltip
toolTip.number = -1;
}
void LevelEditSelect::packProperties(ImageManager& imageManager,SDL_Renderer& renderer, bool newPack){
//Open a message popup.
GUIObject* root=new GUIFrame(imageManager,renderer,(SCREEN_WIDTH-600)/2,(SCREEN_HEIGHT-390)/2,600,390,_("Properties"));
GUIObject* obj;
obj=new GUILabel(imageManager,renderer,40,50,240,36,_("Name:"));
root->addChild(obj);
obj=new GUITextBox(imageManager,renderer,60,80,480,36,packName.c_str());
if(newPack)
obj->caption="";
obj->name="LvlpackName";
root->addChild(obj);
obj=new GUILabel(imageManager,renderer,40,120,240,36,_("Description:"));
root->addChild(obj);
obj=new GUITextBox(imageManager,renderer,60,150,480,36,levels->levelpackDescription.c_str());
if(newPack)
obj->caption="";
obj->name="LvlpackDescription";
root->addChild(obj);
obj=new GUILabel(imageManager,renderer,40,190,240,36,_("Congratulation text:"));
root->addChild(obj);
obj=new GUITextBox(imageManager,renderer,60,220,480,36,levels->congratulationText.c_str());
if(newPack)
obj->caption="";
obj->name="LvlpackCongratulation";
root->addChild(obj);
obj = new GUILabel(imageManager, renderer, 40, 260, 240, 36, _("Music list:"));
root->addChild(obj);
obj = new GUITextBox(imageManager, renderer, 60, 290, 480, 36, levels->levelpackMusicList.c_str());
if (newPack)
obj->caption = "";
obj->name = "LvlpackMusic";
root->addChild(obj);
obj=new GUIButton(imageManager,renderer,root->width*0.3,390-44,-1,36,_("OK"),0,true,true,GUIGravityCenter);
obj->name="cfgOK";
obj->eventCallback=this;
root->addChild(obj);
GUIButton *cancelButton = new GUIButton(imageManager, renderer, root->width*0.7, 390 - 44, -1, 36, _("Cancel"), 0, true, true, GUIGravityCenter);
cancelButton->name = "cfgCancel";
cancelButton->eventCallback = this;
root->addChild(cancelButton);
//Create the gui overlay.
//NOTE: We don't need to store a pointer since it will auto cleanup itself.
- new AddonOverlay(renderer, root, cancelButton, NULL);
+ new AddonOverlay(renderer, root, cancelButton, NULL, 2 | 4 | 8 | 16);
if(newPack){
packName.clear();
}
}
void LevelEditSelect::addLevel(ImageManager& imageManager,SDL_Renderer& renderer){
//Open a message popup.
GUIObject* root=new GUIFrame(imageManager,renderer,(SCREEN_WIDTH-600)/2,(SCREEN_HEIGHT-200)/2,600,200,_("Add level"));
GUIObject* obj;
obj=new GUILabel(imageManager,renderer,40,80,240,36,_("File name:"));
root->addChild(obj);
char s[64];
SDL_snprintf(s,64,"map%02d.map",levels->getLevelCount()+1);
obj=new GUITextBox(imageManager,renderer,300,80,240,36,s);
obj->name="LvlFile";
root->addChild(obj);
obj=new GUIButton(imageManager,renderer,root->width*0.3,200-44,-1,36,_("OK"),0,true,true,GUIGravityCenter);
obj->name="cfgAddOK";
obj->eventCallback=this;
root->addChild(obj);
GUIButton *cancelButton = new GUIButton(imageManager, renderer, root->width*0.7, 200 - 44, -1, 36, _("Cancel"), 0, true, true, GUIGravityCenter);
cancelButton->name = "cfgAddCancel";
cancelButton->eventCallback = this;
root->addChild(cancelButton);
//Dim the screen using the tempSurface.
//NOTE: We don't need to store a pointer since it will auto cleanup itself.
- new AddonOverlay(renderer, root, cancelButton, NULL);
+ new AddonOverlay(renderer, root, cancelButton, NULL, 2 | 4 | 8 | 16);
}
void LevelEditSelect::moveLevel(ImageManager& imageManager,SDL_Renderer& renderer){
//Open a message popup.
GUIObject* root=new GUIFrame(imageManager,renderer,(SCREEN_WIDTH-600)/2,(SCREEN_HEIGHT-200)/2,600,200,_("Move level"));
GUIObject* obj;
obj=new GUILabel(imageManager,renderer,40,60,240,36,_("Level: "));
root->addChild(obj);
obj=new GUITextBox(imageManager,renderer,300,60,240,36,"1");
obj->name="MoveLevel";
root->addChild(obj);
obj=new GUISingleLineListBox(imageManager,renderer,root->width*0.5,110,240,36,true,true,GUIGravityCenter);
obj->name="lstPlacement";
vector<string> v;
v.push_back(_("Before"));
v.push_back(_("After"));
v.push_back(_("Swap"));
(dynamic_cast<GUISingleLineListBox*>(obj))->addItems(v);
obj->value=0;
root->addChild(obj);
obj=new GUIButton(imageManager,renderer,root->width*0.3,200-44,-1,36,_("OK"),0,true,true,GUIGravityCenter);
obj->name="cfgMoveOK";
obj->eventCallback=this;
root->addChild(obj);
GUIButton *cancelButton = new GUIButton(imageManager, renderer, root->width*0.7, 200 - 44, -1, 36, _("Cancel"), 0, true, true, GUIGravityCenter);
cancelButton->name = "cfgMoveCancel";
cancelButton->eventCallback = this;
root->addChild(cancelButton);
//Create the gui overlay.
//NOTE: We don't need to store a pointer since it will auto cleanup itself.
- new AddonOverlay(renderer, root, cancelButton, NULL);
+ new AddonOverlay(renderer, root, cancelButton, NULL, 2 | 4 | 8 | 16);
}
void LevelEditSelect::refresh(ImageManager& imageManager, SDL_Renderer& renderer, bool change){
int m=levels->getLevelCount();
if(change){
numbers.clear();
//clear the selected level
if(selectedNumber!=NULL){
selectedNumber=NULL;
}
//Disable the level specific buttons.
move->enabled=false;
remove->enabled=false;
edit->enabled=false;
for(int n=0;n<=m;n++){
numbers.emplace_back(imageManager, renderer);
}
}
for(int n=0;n<m;n++){
SDL_Rect box={(n%LEVELS_PER_ROW)*64+80,(n/LEVELS_PER_ROW)*64+184,0,0};
numbers[n].init(renderer,n,box);
}
SDL_Rect box={(m%LEVELS_PER_ROW)*64+80,(m/LEVELS_PER_ROW)*64+184,0,0};
numbers[m].init(renderer,"+",box,m);
m++; //including the "+" button
if(m>LEVELS_DISPLAYED_IN_SCREEN){
levelScrollBar->maxValue=(m-LEVELS_DISPLAYED_IN_SCREEN+LEVELS_PER_ROW-1)/LEVELS_PER_ROW;
levelScrollBar->visible=true;
}else{
levelScrollBar->maxValue=0;
levelScrollBar->visible=false;
}
if(!levels->levelpackDescription.empty())
levelpackDescription->caption=_CC(levels->getDictionaryManager(),levels->levelpackDescription);
else
levelpackDescription->caption="";
}
void LevelEditSelect::selectNumber(ImageManager& imageManager, SDL_Renderer& renderer, unsigned int number, bool selected){
if (selected) {
if (number >= 0 && number < levels->getLevelCount()) {
levels->setCurrentLevel(number);
setNextState(STATE_LEVEL_EDITOR);
} else {
addLevel(imageManager, renderer);
}
}else{
move->enabled = false;
remove->enabled = false;
edit->enabled = false;
selectedNumber = NULL;
if (number == numbers.size() - 1){
if (isKeyboardOnly) {
selectedNumber = &numbers[number];
} else {
addLevel(imageManager, renderer);
}
} else if (number >= 0 && number < levels->getLevelCount()) {
selectedNumber=&numbers[number];
//Enable the level specific buttons.
//NOTE: We check if 'remove levelpack' is enabled, if not then it's the Levels levelpack.
if(removePack->enabled)
move->enabled=true;
remove->enabled=true;
edit->enabled=true;
}
}
}
void LevelEditSelect::handleEvents(ImageManager& imageManager, SDL_Renderer& renderer){
//Call handleEvents() of base class.
LevelSelect::handleEvents(imageManager, renderer);
if (section == 3) {
//Check focus movement
if (inputMgr.isKeyDownEvent(INPUTMGR_RIGHT)){
isKeyboardOnly = true;
- section2++;
+ section2 += isVertical ? 3 : 1;
} else if (inputMgr.isKeyDownEvent(INPUTMGR_LEFT)){
isKeyboardOnly = true;
- section2--;
+ section2 -= isVertical ? 3 : 1;
} else if (inputMgr.isKeyDownEvent(INPUTMGR_UP)){
isKeyboardOnly = true;
- section2 -= 3;
+ section2 -= isVertical ? 1 : 3;
} else if (inputMgr.isKeyDownEvent(INPUTMGR_DOWN)){
isKeyboardOnly = true;
- section2 += 3;
+ section2 += isVertical ? 1 : 3;
}
if (section2 > 6) section2 -= 6;
else if (section2 < 1) section2 += 6;
//Check if enter is pressed
if (isKeyboardOnly && inputMgr.isKeyDownEvent(INPUTMGR_SELECT) && section2 >= 1 && section2 <= 6) {
GUIButton *buttons[6] = {
newPack, propertiesPack, removePack, move, remove, edit
};
GUIEventCallback_OnEvent(imageManager, renderer, buttons[section2 - 1]->name, buttons[section2 - 1], GUIEventClick);
}
}
}
void LevelEditSelect::render(ImageManager& imageManager,SDL_Renderer &renderer){
//Let the levelselect render.
LevelSelect::render(imageManager,renderer);
//Draw highlight in keyboard only mode.
if (isKeyboardOnly) {
GUIButton *buttons[6] = {
newPack, propertiesPack, removePack, move, remove, edit
};
for (int i = 0; i < 6; i++) {
buttons[i]->state = (section == 3 && section2 - 1 == i) ? 1 : 0;
}
}
}
void LevelEditSelect::resize(ImageManager& imageManager, SDL_Renderer &renderer){
//Let the levelselect resize.
LevelSelect::resize(imageManager, renderer);
//Create the GUI.
createGUI(imageManager,renderer, false);
//NOTE: This is a workaround for buttons failing when resizing.
if(packName=="Custom Levels"){
removePack->enabled=false;
propertiesPack->enabled=false;
}
if(selectedNumber)
selectNumber(imageManager, renderer, selectedNumber->getNumber(),false);
}
void LevelEditSelect::renderTooltip(SDL_Renderer& renderer,unsigned int number,int dy){
if (!toolTip.name || toolTip.number != number) {
SDL_Color fg = objThemes.getTextColor(true);
toolTip.number = number;
if (number < (unsigned int)levels->getLevelCount()){
//Render the name of the level.
toolTip.name = textureFromText(renderer, *fontText, _CC(levels->getDictionaryManager(), levels->getLevelName(number)), fg);
} else {
//Add level button
toolTip.name = textureFromText(renderer, *fontText, _("Add level"), fg);
}
}
//Check if name isn't null.
if(!toolTip.name)
return;
//Now draw a square the size of the three texts combined.
SDL_Rect r=numbers[number].box;
r.y-=dy*64;
const SDL_Rect nameSize = rectFromTexture(*toolTip.name);
r.w=nameSize.w;
r.h=nameSize.h;
//Make sure the tooltip doesn't go outside the window.
if(r.y>SCREEN_HEIGHT-200){
r.y-=nameSize.h+4;
}else{
r.y+=numbers[number].box.h+2;
}
if(r.x+r.w>SCREEN_WIDTH-50)
r.x=SCREEN_WIDTH-50-r.w;
//Draw a rectange
Uint32 color=0xFFFFFFFF;
drawGUIBox(r.x-5,r.y-5,r.w+10,r.h+10,renderer,color);
//Calc the position to draw.
SDL_Rect r2=r;
//Now we render the name if the surface isn't null.
if(toolTip.name){
//Draw the name.
applyTexture(r2.x, r2.y, toolTip.name, renderer);
}
}
void LevelEditSelect::GUIEventCallback_OnEvent(ImageManager& imageManager, SDL_Renderer& renderer, std::string name,GUIObject* obj,int eventType){
//NOTE: We check for the levelpack change to enable/disable some levelpack buttons.
if(name=="cmdLvlPack"){
//We call changepack and return to prevent the LevelSelect to undo what we did.
changePack();
refresh(imageManager, renderer);
return;
}
//Let the level select handle his GUI events.
LevelSelect::GUIEventCallback_OnEvent(imageManager,renderer,name,obj,eventType);
//Check for the edit button.
if(name=="cmdNewLvlpack"){
//Create a new pack.
packProperties(imageManager,renderer, true);
}else if(name=="cmdLvlpackProp"){
//Show the pack properties.
packProperties(imageManager,renderer, false);
}else if(name=="cmdRmLvlpack"){
//Show an "are you sure" message.
if(msgBox(imageManager,renderer,tfm::format(_("Are you sure remove the level pack '%s'?"),packName),MsgBoxYesNo,_("Remove prompt"))==MsgBoxYes){
//Remove the directory.
if(!removeDirectory(levels->levelpackPath.c_str())){
cerr<<"ERROR: Unable to remove levelpack directory "<<levels->levelpackPath<<endl;
}
//Remove it from the vector (levelpack list).
vector<pair<string,string> >::iterator it;
for(it=levelpacks->item.begin();it!=levelpacks->item.end();++it){
if(it->second==packName)
levelpacks->item.erase(it);
}
//Remove it from the levelpackManager.
getLevelPackManager()->removeLevelPack(levels->levelpackPath);
//And call changePack.
levelpacks->value=levelpacks->item.size()-1;
changePack();
refresh(imageManager, renderer);
}
}else if(name=="cmdMoveMap"){
if(selectedNumber!=NULL){
moveLevel(imageManager,renderer);
}
}else if(name=="cmdRmMap"){
if(selectedNumber!=NULL){
//Show an "are you sure" message.
if (msgBox(imageManager, renderer, tfm::format(_("Are you sure remove the map '%s'?"), levels->getLevel(selectedNumber->getNumber())->name), MsgBoxYesNo, _("Remove prompt")) != MsgBoxYes) {
return;
}
if(packName!="Custom Levels"){
if(!removeFile((levels->levelpackPath+"/"+levels->getLevel(selectedNumber->getNumber())->file).c_str())){
cerr<<"ERROR: Unable to remove level "<<(levels->levelpackPath+"/"+levels->getLevel(selectedNumber->getNumber())->file).c_str()<<endl;
}
levels->removeLevel(selectedNumber->getNumber());
levels->saveLevels(levels->levelpackPath+"/levels.lst");
}else{
//This is the levels levelpack so we just remove the file.
if(!removeFile(levels->getLevel(selectedNumber->getNumber())->file.c_str())){
cerr<<"ERROR: Unable to remove level "<<levels->getLevel(selectedNumber->getNumber())->file<<endl;
}
levels->removeLevel(selectedNumber->getNumber());
}
//And refresh the selection screen.
refresh(imageManager, renderer);
}
}else if(name=="cmdEdit"){
if(selectedNumber!=NULL){
levels->setCurrentLevel(selectedNumber->getNumber());
setNextState(STATE_LEVEL_EDITOR);
}
}
//Check for levelpack properties events.
if(name=="cfgOK"){
//Now loop throught the children of the GUIObjectRoot in search of the fields.
for(unsigned int i=0;i<GUIObjectRoot->childControls.size();i++){
if(GUIObjectRoot->childControls[i]->name=="LvlpackName"){
//Check if the name changed.
if(packName!=GUIObjectRoot->childControls[i]->caption){
//Delete the old one.
if(!packName.empty()){
if(!renameDirectory((getUserPath(USER_DATA)+"custom/levelpacks/"+packName).c_str(),(getUserPath(USER_DATA)+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption).c_str())){
cerr<<"ERROR: Unable to move levelpack directory "<<(getUserPath(USER_DATA)+"custom/levelpacks/"+packName)<<" to "<<(getUserPath(USER_DATA)+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption)<<endl;
}
//Remove the old one from the levelpack manager.
getLevelPackManager()->removeLevelPack(levelpacks->getName());
//And the levelpack list.
vector<pair<string,string> >::iterator it1;
for(it1=levelpacks->item.begin();it1!=levelpacks->item.end();++it1){
if(it1!=levelpacks->item.end()){
levelpacks->item.erase(it1);
break;
}
}
}else{
//It's a new levelpack so we need to change the levels array.
LevelPack* pack=new LevelPack;
levels=pack;
//Now create the dirs.
if(!createDirectory((getUserPath(USER_DATA)+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption).c_str())){
cerr<<"ERROR: Unable to create levelpack directory "<<(getUserPath(USER_DATA)+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption)<<endl;
}
if(!createFile((getUserPath(USER_DATA)+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption+"/levels.lst").c_str())){
cerr<<"ERROR: Unable to create levelpack file "<<(getUserPath(USER_DATA)+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption+"/levels.lst")<<endl;
}
}
//And set the new name.
packName=GUIObjectRoot->childControls[i]->caption;
levels->levelpackName=packName;
levels->levelpackPath=(getUserPath(USER_DATA)+"custom/levelpacks/"+packName+"/");
//Also add the levelpack location
getLevelPackManager()->addLevelPack(levels);
levelpacks->addItem(levels->levelpackPath,GUIObjectRoot->childControls[i]->caption);
levelpacks->value=levelpacks->item.size()-1;
//And call changePack.
changePack();
}
}
if(GUIObjectRoot->childControls[i]->name=="LvlpackDescription"){
levels->levelpackDescription=GUIObjectRoot->childControls[i]->caption;
}
if(GUIObjectRoot->childControls[i]->name=="LvlpackCongratulation"){
levels->congratulationText=GUIObjectRoot->childControls[i]->caption;
}
if (GUIObjectRoot->childControls[i]->name == "LvlpackMusic"){
levels->levelpackMusicList = GUIObjectRoot->childControls[i]->caption;
}
}
//Refresh the leveleditselect to show the correct information.
refresh(imageManager, renderer);
//Save the configuration.
levels->saveLevels(getUserPath(USER_DATA)+"custom/levelpacks/"+packName+"/levels.lst");
getSettings()->setValue("lastlevelpack",levels->levelpackPath);
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}else if(name=="cfgCancel"){
//Check if packName is empty, if so it was a new levelpack and we need to revert to an existing one.
if(packName.empty()){
packName=levelpacks->item[levelpacks->value].second;
changePack();
}
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
//Check for add level events.
if(name=="cfgAddOK"){
//Check if the file name isn't null.
//Now loop throught the children of the GUIObjectRoot in search of the fields.
for(unsigned int i=0;i<GUIObjectRoot->childControls.size();i++){
if(GUIObjectRoot->childControls[i]->name=="LvlFile"){
if(GUIObjectRoot->childControls[i]->caption.empty()){
msgBox(imageManager,renderer,_("No file name given for the new level."),MsgBoxOKOnly,_("Missing file name"));
return;
}else{
string tmp_caption = GUIObjectRoot->childControls[i]->caption;
//Replace all spaces with a underline.
size_t j;
for(;(j=tmp_caption.find(" "))!=string::npos;){
tmp_caption.replace(j,1,"_");
}
//If there isn't ".map" extension add it.
size_t found=tmp_caption.find_first_of(".");
if(found!=string::npos)
tmp_caption.replace(tmp_caption.begin()+found+1,tmp_caption.end(),"map");
else if (tmp_caption.substr(found+1)!="map")
tmp_caption.append(".map");
/* Create path and file in it */
string path=(levels->levelpackPath+"/"+tmp_caption);
if(packName=="Custom Levels"){
path=(getUserPath(USER_DATA)+"/custom/levels/"+tmp_caption);
}
//First check if the file doesn't exist already.
FILE* f;
f=fopen(path.c_str(),"rb");
//Check if it exists.
if(f){
//Close the file.
fclose(f);
//Let the currentState render once to prevent multiple GUI overlapping and prevent the screen from going black.
currentState->render(imageManager,renderer);
levelEditGUIObjectRoot->render(renderer);
//Notify the user.
msgBox(imageManager,renderer,string("The file "+tmp_caption+" already exists."),MsgBoxOKOnly,"Error");
return;
}
if(!createFile(path.c_str())){
cerr<<"ERROR: Unable to create level file "<<path<<endl;
}else{
//Update statistics.
statsMgr.newAchievement("create1");
if((++statsMgr.createdLevels)>=50) statsMgr.newAchievement("create50");
}
levels->addLevel(path);
//NOTE: Also add the level to the levels levelpack in case of custom levels.
if(packName=="Custom Levels"){
LevelPack* levelsPack=getLevelPackManager()->getLevelPack("Levels/");
if(levelsPack){
levelsPack->addLevel(path);
levelsPack->setLocked(levelsPack->getLevelCount()-1);
}else{
cerr<<"ERROR: Unable to add level to Levels levelpack"<<endl;
}
}
if(packName!="Custom Levels")
levels->saveLevels(getUserPath(USER_DATA)+"custom/levelpacks/"+packName+"/levels.lst");
refresh(imageManager, renderer);
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
return;
}
}
}
}
}else if(name=="cfgAddCancel"){
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
//Check for move level events.
if(name=="cfgMoveOK"){
//Check if the entered level number is valid.
//Now loop throught the children of the GUIObjectRoot in search of the fields.
int level=0;
int placement=0;
for(unsigned int i=0;i<GUIObjectRoot->childControls.size();i++){
if(GUIObjectRoot->childControls[i]->name=="MoveLevel"){
level=atoi(GUIObjectRoot->childControls[i]->caption.c_str());
if(level<=0 || level>levels->getLevelCount()){
msgBox(imageManager,renderer,_("The entered level number isn't valid!"),MsgBoxOKOnly,_("Illegal number"));
return;
}
}
if(GUIObjectRoot->childControls[i]->name=="lstPlacement"){
placement=GUIObjectRoot->childControls[i]->value;
}
}
//Now we execute the swap/move.
//Check for the place before.
if(placement==0){
//We place the selected level before the entered level.
levels->moveLevel(selectedNumber->getNumber(),level-1);
}else if(placement==1){
//We place the selected level after the entered level.
if(level<selectedNumber->getNumber())
levels->moveLevel(selectedNumber->getNumber(),level);
else
levels->moveLevel(selectedNumber->getNumber(),level+1);
}else if(placement==2){
//We swap the selected level with the entered level.
levels->swapLevel(selectedNumber->getNumber(),level-1);
}
//And save the change.
if(packName!="Custom Levels")
levels->saveLevels(getUserPath(USER_DATA)+"custom/levelpacks/"+packName+"/levels.lst");
refresh(imageManager, renderer);
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}else if(name=="cfgMoveCancel"){
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
}
diff --git a/src/LevelEditSelect.h b/src/LevelEditSelect.h
index cfd09c0..5d446f7 100644
--- a/src/LevelEditSelect.h
+++ b/src/LevelEditSelect.h
@@ -1,96 +1,99 @@
/*
* Copyright (C) 2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LEVELEDITSELECT_H
#define LEVELEDITSELECT_H
#include "LevelSelect.h"
#include "GameState.h"
#include "GUIObject.h"
#include <vector>
#include <string>
//This is the LevelEditSelect state, here you can select levelpacks and levels.
class LevelEditSelect :public LevelSelect{
private:
//Pointer to the GUIObjectRoot of the levelselect main gui.
GUIObject* levelEditGUIObjectRoot;
//Pointer to the new levelpack textfield.
GUIObject* levelpackName;
//Pointer to the new levelpack button.
GUIButton* newPack;
//Pointer to the levelpack properties button.
GUIButton* propertiesPack;
//Pointer to the remove levelpack button.
GUIButton* removePack;
//Pointer to the move map button.
GUIButton* move;
//Pointer to the remove map button.
GUIButton* remove;
//Pointer to the edit map button.
GUIButton* edit;
+
+ //If is vertical mode, then the buttons are 2*3 instead of 3*2
+ bool isVertical;
//String that contains the name of the current levelpack.
std::string packName;
//Method that will create the GUI elements.
//initial: Boolean if it is the first time the gui is created.
void createGUI(ImageManager& imageManager, SDL_Renderer& renderer, bool initial);
//Method that should be called when changing the current levelpack in an abnormal way.
void changePack();
//This method will show a popup with levelpack specific settings.
//newPack: Boolean if it's a new levelpack.
void packProperties(ImageManager& imageManager, SDL_Renderer &renderer, bool newPack);
//This method will show an add level dialog.
void addLevel(ImageManager& imageManager, SDL_Renderer &renderer);
//This method will show an move level dialog.
void moveLevel(ImageManager& imageManager, SDL_Renderer &renderer);
public:
//Constructor.
LevelEditSelect(ImageManager &imageManager, SDL_Renderer& renderer);
//Destructor.
~LevelEditSelect();
//Inherited from LevelSelect.
//change: Boolean if the levelpack changed, if not we only have to rearrange the numbers.
void refresh(ImageManager &imageManager, SDL_Renderer &renderer, bool change=true) override;
void selectNumber(ImageManager &imageManager, SDL_Renderer &renderer, unsigned int number,bool selected) override;
void handleEvents(ImageManager& imageManager, SDL_Renderer& renderer) override;
//Inherited from GameState.
void render(ImageManager&imageManager, SDL_Renderer& renderer) override;
//Inherited from GameState.
void resize(ImageManager &imageManager, SDL_Renderer& renderer) override;
//Inherited from LevelSelect.
void renderTooltip(SDL_Renderer& renderer,unsigned int number,int dy) override;
//GUI events will be handled here.
void GUIEventCallback_OnEvent(ImageManager& imageManager, SDL_Renderer& renderer, std::string name,GUIObject* obj,int eventType) override;
};
#endif
diff --git a/src/OptionsMenu.cpp b/src/OptionsMenu.cpp
index d91daa6..d2454e9 100644
--- a/src/OptionsMenu.cpp
+++ b/src/OptionsMenu.cpp
@@ -1,520 +1,574 @@
/*
* Copyright (C) 2011-2013 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Functions.h"
#include "GameState.h"
#include "OptionsMenu.h"
#include "ThemeManager.h"
#include "GUIListBox.h"
#include "GUISlider.h"
#include "InputManager.h"
#include "LevelPackManager.h"
#include "StatisticsManager.h"
#include "MusicManager.h"
#include "SoundManager.h"
#include <iostream>
#include <sstream>
#include "libs/tinygettext/tinygettext.hpp"
using namespace std;
/////////////////////////OPTIONS_MENU//////////////////////////////////
//Some variables for the options.
static bool fullscreen,internet,fade,quickrec;
static string themeName,languageName;
static int lastLang,lastRes;
static bool useProxy;
static string internetProxy;
static bool restartFlag;
static _res currentRes;
static vector<_res> resolutionList;
Options::Options(ImageManager& imageManager,SDL_Renderer& renderer){
//Render the title.
title=textureFromText(renderer, *fontTitle, _("Settings"), objThemes.getTextColor(false));
//Initialize variables.
lastJumpSound=0;
clearIconHower=false;
-
+ section = 2;
+ section2 = 1;
+
//Load icon image and tooltip text.
clearIcon=imageManager.loadTexture(getDataPath()+"gfx/menu/clear-progress.png",renderer);
/// TRANSLATORS: Used for button which clear any level progress like unlocked levels and highscores.
clearTooltip=textureFromText(renderer, *fontText, _("Clear Progress"), objThemes.getTextColor(true));
//Set some default settings.
fullscreen=getSettings()->getBoolValue("fullscreen");
languageName=getSettings()->getValue("lang");
themeName=processFileName(getSettings()->getValue("theme"));
internet=getSettings()->getBoolValue("internet");
internetProxy=getSettings()->getValue("internet-proxy");
useProxy=!internetProxy.empty();
fade=getSettings()->getBoolValue("fading");
quickrec=getSettings()->getBoolValue("quickrecord");
//Set the restartFlag false.
restartFlag=false;
//Now create the gui.
createGUI(imageManager,renderer);
}
Options::~Options(){
//Delete the GUI.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
void Options::createGUI(ImageManager& imageManager,SDL_Renderer& renderer){
//Variables for positioning
const int columnW=SCREEN_WIDTH*0.3;
const int column1X=SCREEN_WIDTH*0.15;
const int column2X=SCREEN_WIDTH*0.55;
const int lineHeight=40;
//Create the root element of the GUI.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
GUIObjectRoot=new GUIObject(imageManager,renderer,0,0,SCREEN_WIDTH,SCREEN_HEIGHT);
//Single line list for different tabs.
GUISingleLineListBox* listBox=new GUISingleLineListBox(imageManager,renderer,(SCREEN_WIDTH-500)/2,104,500,32);
listBox->addItem(_("General"));
listBox->addItem(_("Controls"));
listBox->value=0;
listBox->name="lstTabs";
listBox->eventCallback=this;
GUIObjectRoot->addChild(listBox);
//Create general tab.
tabGeneral=new GUIObject(imageManager,renderer,0,150,SCREEN_WIDTH,SCREEN_HEIGHT);
GUIObjectRoot->addChild(tabGeneral);
//Now we create GUIObjects for every option.
GUIObject* obj=new GUILabel(imageManager,renderer,column1X,0,columnW,36,_("Music"));
tabGeneral->addChild(obj);
musicSlider=new GUISlider(imageManager,renderer,column2X,0,columnW,36,atoi(getSettings()->getValue("music").c_str()),0,128,15);
musicSlider->name="sldMusic";
musicSlider->eventCallback=this;
tabGeneral->addChild(musicSlider);
obj=new GUILabel(imageManager,renderer,column1X,lineHeight,columnW,36,_("Sound"));
tabGeneral->addChild(obj);
soundSlider=new GUISlider(imageManager,renderer,column2X,lineHeight,columnW,36,atoi(getSettings()->getValue("sound").c_str()),0,128,15);
soundSlider->name="sldSound";
soundSlider->eventCallback=this;
tabGeneral->addChild(soundSlider);
obj=new GUILabel(imageManager,renderer,column1X,2*lineHeight,columnW,36,_("Resolution"));
obj->name="lstResolution";
tabGeneral->addChild(obj);
//Create list with many different resolutions.
resolutions = new GUISingleLineListBox(imageManager,renderer,column2X,2*lineHeight,columnW,36);
resolutions->value=-1;
//Only get the resolution list if it hasn't been done before.
if(resolutionList.empty()){
resolutionList=getResolutionList();
}
//Get current resolution from config file. Thus it can be user defined.
currentRes.w=atoi(getSettings()->getValue("width").c_str());
currentRes.h=atoi(getSettings()->getValue("height").c_str());
for(int i=0; i<(int)resolutionList.size();i++){
//Create a string from width and height and then add it to list.
ostringstream out;
out << resolutionList[i].w << "x" << resolutionList[i].h;
resolutions->addItem(out.str());
//Check if current resolution matches, select it.
if(resolutionList[i].w==currentRes.w && resolutionList[i].h==currentRes.h){
resolutions->value=i;
}
}
//Add current resolution if it isn't already in the list.
if(resolutions->value==-1){
ostringstream out;
out << currentRes.w << "x" << currentRes.h;
resolutions->addItem(out.str());
resolutions->value=resolutions->item.size()-1;
}
lastRes=resolutions->value;
tabGeneral->addChild(resolutions);
obj=new GUILabel(imageManager,renderer,column1X,3*lineHeight,columnW,36,_("Language"));
tabGeneral->addChild(obj);
//Create GUI list with available languages.
langs = new GUISingleLineListBox(imageManager,renderer,column2X,3*lineHeight,columnW,36);
langs->name="lstLanguages";
/// TRANSLATORS: as detect user's language automatically
langs->addItem("",_("Auto-Detect"));
langs->addItem("en","English");
//Get a list of every available language.
set<tinygettext::Language> languages = dictionaryManager->get_languages();
for (set<tinygettext::Language>::iterator s0 = languages.begin(); s0 != languages.end(); ++s0){
//If language in loop is the same in config file, then select it
if(getSettings()->getValue("lang")==s0->str()){
lastLang=distance(languages.begin(),s0)+2;
}
//Add language in loop to list and listbox.
langs->addItem(s0->str(),s0->get_name());
}
//If Auto or English are selected.
if(getSettings()->getValue("lang")==""){
lastLang=0;
}else if(getSettings()->getValue("lang")=="en"){
lastLang=1;
}
langs->value=lastLang;
tabGeneral->addChild(langs);
obj=new GUILabel(imageManager,renderer,column1X,4*lineHeight,columnW,36,_("Theme"));
obj->name="theme";
tabGeneral->addChild(obj);
//Create the theme option gui element.
theme=new GUISingleLineListBox(imageManager,renderer,column2X,4*lineHeight,columnW,36);
theme->name="lstTheme";
//Vector containing the theme locations and names.
vector<pair<string,string> > themes;
vector<string> v=enumAllDirs(getUserPath(USER_DATA)+"themes/");
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
string location=getUserPath(USER_DATA)+"themes/"+*i;
themes.push_back(pair<string,string>(location,*i));
}
vector<string> v2=enumAllDirs(getDataPath()+"themes/");
for(vector<string>::iterator i=v2.begin(); i!=v2.end(); ++i){
string location=getDataPath()+"themes/"+*i;
themes.push_back(pair<string,string>(location,*i));
}
//Try to find the configured theme so we can display it.
int value=-1;
for(vector<pair<string,string> >::iterator i=themes.begin(); i!=themes.end(); ++i){
if(i->first==themeName) {
value=i-themes.begin();
}
}
theme->addItems(themes);
if(value==-1)
value=theme->item.size()-1;
theme->value=value;
//NOTE: We call the event handling method to correctly set the themename.
GUIEventCallback_OnEvent(imageManager,renderer,"lstTheme",theme,GUIEventChange);
theme->eventCallback=this;
tabGeneral->addChild(theme);
//Proxy settings.
obj=new GUILabel(imageManager,renderer,column1X,5*lineHeight,columnW,36,_("Internet proxy"));
obj->name="chkProxy";
obj->eventCallback=this;
tabGeneral->addChild(obj);
obj=new GUITextBox(imageManager,renderer,column2X,5*lineHeight,columnW,36,internetProxy.c_str());
obj->name="txtProxy";
obj->eventCallback=this;
tabGeneral->addChild(obj);
obj=new GUICheckBox(imageManager,renderer,column1X,6*lineHeight,columnW,36,_("Fullscreen"),fullscreen?1:0);
obj->name="chkFullscreen";
obj->eventCallback=this;
tabGeneral->addChild(obj);
+ obj = new GUICheckBox(imageManager, renderer, column1X, 7 * lineHeight, columnW, 36, _("Quick record"), quickrec ? 1 : 0);
+ obj->name = "chkQuickRec";
+ obj->eventCallback = this;
+ tabGeneral->addChild(obj);
+
obj=new GUICheckBox(imageManager,renderer,column2X,6*lineHeight,columnW,36,_("Internet"),internet?1:0);
obj->name="chkInternet";
obj->eventCallback=this;
tabGeneral->addChild(obj);
obj=new GUICheckBox(imageManager,renderer,column2X,7*lineHeight,columnW,36,_("Fade transition"),fade?1:0);
obj->name="chkFade";
obj->eventCallback=this;
tabGeneral->addChild(obj);
- obj=new GUICheckBox(imageManager,renderer,column1X,7*lineHeight,columnW,36,_("Quick record"),quickrec?1:0);
- obj->name="chkQuickRec";
- obj->eventCallback=this;
- tabGeneral->addChild(obj);
-
//Create the controls tab.
tabControls=inputMgr.showConfig(imageManager,renderer,SCREEN_HEIGHT-210);
tabControls->top=140;
tabControls->visible=false;
GUIObjectRoot->addChild(tabControls);
//Save original keys.
for(int i=0;i<INPUTMGR_MAX;i++){
tmpKeys[i]=inputMgr.getKeyCode((InputManagerKeys)i,false);
tmpAlternativeKeys[i]=inputMgr.getKeyCode((InputManagerKeys)i,true);
}
//Create buttons.
- GUIObject*b1=new GUIButton(imageManager,renderer,SCREEN_WIDTH*0.3,SCREEN_HEIGHT-60,-1,36,_("Cancel"),0,true,true,GUIGravityCenter);
- b1->name="cmdBack";
- b1->eventCallback=this;
- GUIObjectRoot->addChild(b1);
+ cmdBack = new GUIButton(imageManager, renderer, SCREEN_WIDTH*0.3, SCREEN_HEIGHT - 60, -1, 36, _("Cancel"), 0, true, true, GUIGravityCenter);
+ cmdBack->name = "cmdBack";
+ cmdBack->eventCallback = this;
+ GUIObjectRoot->addChild(cmdBack);
- GUIObject* b2=new GUIButton(imageManager,renderer,SCREEN_WIDTH*0.7,SCREEN_HEIGHT-60,-1,36,_("Save Changes"),0,true,true,GUIGravityCenter);
- b2->name="cmdSave";
- b2->eventCallback=this;
- GUIObjectRoot->addChild(b2);
+ cmdSave = new GUIButton(imageManager, renderer, SCREEN_WIDTH*0.7, SCREEN_HEIGHT - 60, -1, 36, _("Save Changes"), 0, true, true, GUIGravityCenter);
+ cmdSave->name = "cmdSave";
+ cmdSave->eventCallback = this;
+ GUIObjectRoot->addChild(cmdSave);
}
static string convertInt(int i){
stringstream ss;
ss << i;
return ss.str();
}
void Options::GUIEventCallback_OnEvent(ImageManager& imageManager, SDL_Renderer& renderer, std::string name,GUIObject* obj,int eventType){
//Check what type of event it was.
if(eventType==GUIEventClick){
if(name=="cmdBack"){
//Reset the key changes.
for(int i=0;i<INPUTMGR_MAX;i++){
inputMgr.setKeyCode((InputManagerKeys)i,tmpKeys[i],false);
inputMgr.setKeyCode((InputManagerKeys)i,tmpAlternativeKeys[i],true);
}
//Reset the music volume.
getMusicManager()->setVolume(atoi(getSettings()->getValue("music").c_str()));
Mix_Volume(-1,atoi(getSettings()->getValue("sound").c_str()));
//And goto the main menu.
setNextState(STATE_MENU);
}else if(name=="cmdSave"){
//Save is pressed thus save
char s[64];
sprintf(s,"%d",soundSlider->value);
getSettings()->setValue("sound",s);
sprintf(s,"%d",musicSlider->value);
getSettings()->setValue("music",s);
getMusicManager()->setEnabled(musicSlider->value>0);
Mix_Volume(-1,soundSlider->value);
getSettings()->setValue("fullscreen",fullscreen?"1":"0");
getSettings()->setValue("internet",internet?"1":"0");
getSettings()->setValue("theme",themeName);
getSettings()->setValue("fading",fade?"1":"0");
getSettings()->setValue("quickrecord",quickrec?"1":"0");
//Before loading the theme remove the previous one from the stack.
objThemes.removeTheme();
loadTheme(imageManager,renderer,themeName);
if(!useProxy)
internetProxy.clear();
getSettings()->setValue("internet-proxy",internetProxy);
getSettings()->setValue("lang",langs->getName());
//Is resolution from the list or is it user defined in config file
if(resolutions->value<(int)resolutionList.size()){
getSettings()->setValue("width",convertInt(resolutionList[resolutions->value].w));
getSettings()->setValue("height",convertInt(resolutionList[resolutions->value].h));
}else{
getSettings()->setValue("width",convertInt(currentRes.w));
getSettings()->setValue("height",convertInt(currentRes.h));
}
//Save the key configuration.
inputMgr.saveConfig();
//Save the settings.
saveSettings();
//Before we return check if some .
if(restartFlag || resolutions->value!=lastRes){
//The resolution changed so we need to recreate the screen.
if(!createScreen()){
//Screen creation failed so set to safe settings.
getSettings()->setValue("fullscreen","0");
getSettings()->setValue("width",convertInt(resolutionList[lastRes].w));
getSettings()->setValue("height",convertInt(resolutionList[lastRes].h));
if(!createScreen()){
//Everything fails so quit.
setNextState(STATE_EXIT);
return;
}
}
//The screen is created, now load the (menu) theme.
if(!loadTheme(imageManager,renderer,"")){
//Loading the theme failed so quit.
setNextState(STATE_EXIT);
return;
}
}
if(langs->value!=lastLang){
//We set the language.
language=langs->getName();
dictionaryManager->set_language(tinygettext::Language::from_name(language));
getLevelPackManager()->updateLanguage();
//And reload the font.
if(!loadFonts()){
//Loading failed so quit.
setNextState(STATE_EXIT);
return;
}
}
//Now return to the main menu.
setNextState(STATE_MENU);
}else if(name=="chkFullscreen"){
fullscreen=obj->value?true:false;
//Check if fullscreen changed.
if(fullscreen==getSettings()->getBoolValue("fullscreen")){
//We disable the restart message flag.
restartFlag=false;
}else{
//We set the restart message flag.
restartFlag=true;
}
}else if(name=="chkInternet"){
internet=obj->value?true:false;
}else if(name=="chkProxy"){
useProxy=obj->value?true:false;
}else if(name=="chkFade"){
fade=obj->value?true:false;
}else if(name=="chkQuickRec"){
quickrec=obj->value?true:false;
}
}
if(name=="lstTheme"){
if(theme!=NULL && theme->value>=0 && theme->value<(int)theme->item.size()){
//Convert the themeName to contain %DATA%, etc...
themeName=compressFileName(theme->item[theme->value].first);
}
}else if(name=="txtProxy"){
internetProxy=obj->caption;
//Check if the internetProxy field is empty.
useProxy=!internetProxy.empty();
}else if(name=="sldMusic"){
getMusicManager()->setEnabled(musicSlider->value>0);
getMusicManager()->setVolume(musicSlider->value);
}else if(name=="sldSound"){
Mix_Volume(-1,soundSlider->value);
if(lastJumpSound==0){
getSoundManager()->playSound("jump");
lastJumpSound=15;
}
}
if(name=="lstTabs"){
if(obj->value==0){
tabGeneral->visible=true;
tabControls->visible=false;
}else{
tabGeneral->visible=false;
tabControls->visible=true;
}
}
}
void Options::handleEvents(ImageManager& imageManager, SDL_Renderer& renderer){
- //Get the x and y location of the mouse.
- int x,y;
- SDL_GetMouseState(&x,&y);
-
- //Check icon.
- if(event.type==SDL_MOUSEMOTION || event.type==SDL_MOUSEBUTTONDOWN){
- if(y>=SCREEN_HEIGHT-56&&y<SCREEN_HEIGHT-8&&x>=SCREEN_WIDTH-56)
- clearIconHower=true;
- else
- clearIconHower=false;
+ //Check keyboard navigation.
+ if (tabGeneral && tabGeneral->visible) {
+ if (inputMgr.isKeyDownEvent(INPUTMGR_TAB)) {
+ isKeyboardOnly = true;
+ section = (section == 2) ? 3 : 2;
+
+ //Update selection.
+ if (section == 2) {
+ tabGeneral->selectNextControl(1, -1);
+ } else {
+ tabGeneral->setSelectedControl(-1);
+ }
+ }
+ if (section == 2) {
+ tabGeneral->handleKeyboardNavigationEvents(imageManager, renderer, 2 | 8 | 16);
+ } else if (section == 3) {
+ if (inputMgr.isKeyDownEvent(INPUTMGR_DOWN) || inputMgr.isKeyDownEvent(INPUTMGR_RIGHT)) {
+ isKeyboardOnly = true;
+ section2++;
+ if (section2 > 3) section2 = 1;
+ } else if (inputMgr.isKeyDownEvent(INPUTMGR_UP) || inputMgr.isKeyDownEvent(INPUTMGR_LEFT)) {
+ isKeyboardOnly = true;
+ section2--;
+ if (section2 < 1) section2 = 3;
+ }
+ if (isKeyboardOnly && inputMgr.isKeyDownEvent(INPUTMGR_SELECT) && section == 3) {
+ if (section2 == 1) {
+ GUIEventCallback_OnEvent(imageManager, renderer, cmdBack->name, cmdBack, GUIEventClick);
+ } else if (section2 == 2) {
+ GUIEventCallback_OnEvent(imageManager, renderer, cmdSave->name, cmdSave, GUIEventClick);
+ }
+ }
+ }
+ }
+
+ //Process mouse event only when it's not keyboard only mode.
+ if (!isKeyboardOnly) {
+ //Get the x and y location of the mouse.
+ int x, y;
+ SDL_GetMouseState(&x, &y);
+
+ //Check icon.
+ if (event.type == SDL_MOUSEMOTION || event.type == SDL_MOUSEBUTTONDOWN){
+ if (y >= SCREEN_HEIGHT - 56 && y < SCREEN_HEIGHT - 8 && x >= SCREEN_WIDTH - 56)
+ clearIconHower = true;
+ else
+ clearIconHower = false;
+ }
+ }
+
+ //Update highlight on keyboard only mode.
+ if (isKeyboardOnly) {
+ cmdBack->state = (section == 3 && section2 == 1) ? 1 : 0;
+ cmdSave->state = (section == 3 && section2 == 2) ? 1 : 0;
+ clearIconHower = (section == 3 && section2 == 3);
}
- if(event.type==SDL_MOUSEBUTTONUP && event.button.button==SDL_BUTTON_LEFT && clearIconHower){
+ if ((isKeyboardOnly ? inputMgr.isKeyDownEvent(INPUTMGR_SELECT) :
+ (event.type == SDL_MOUSEBUTTONUP && event.button.button == SDL_BUTTON_LEFT)) && clearIconHower)
+ {
if(msgBox(imageManager,renderer,_("Do you really want to reset level progress?"),MsgBoxYesNo,_("Warning"))==MsgBoxYes){
//We delete the progress folder.
#ifdef WIN32
removeDirectory((getUserPath()+"progress").c_str());
createDirectory((getUserPath()+"progress").c_str());
#else
removeDirectory((getUserPath(USER_DATA)+"/progress").c_str());
createDirectory((getUserPath(USER_DATA)+"/progress").c_str());
#endif
//Reset statistics.
statsMgr.reloadCompletedLevelsAndAchievements();
}
}
//Check if we need to quit, if so enter the exit state.
if(event.type==SDL_QUIT){
setNextState(STATE_EXIT);
}
//Check if the escape button is pressed, if so go back to the main menu.
if (inputMgr.isKeyDownEvent(INPUTMGR_ESCAPE) && (tabControls == NULL || !tabControls->visible)) {
setNextState(STATE_MENU);
}
}
void Options::logic(ImageManager&, SDL_Renderer&){
//Increase the lastJumpSound variable if needed.
if(lastJumpSound!=0){
lastJumpSound--;
}
}
void Options::render(ImageManager&, SDL_Renderer& renderer){
//Draw background.
objThemes.getBackground(true)->draw(renderer);
objThemes.getBackground(true)->updateAnimation();
//Now render the title.
drawTitleTexture(SCREEN_WIDTH, *title, renderer);
//Check if an icon is selected/highlighted and draw tooltip
if(clearIconHower){
const SDL_Rect texSize = rectFromTexture(*clearTooltip);
drawGUIBox(-2,SCREEN_HEIGHT-texSize.h-2,texSize.w+4,texSize.h+4,renderer,0xFFFFFFFF);
applyTexture(0,SCREEN_HEIGHT-texSize.h,clearTooltip,renderer);
}
+ //Draw border of icon if it's keyboard only mode.
+ if (isKeyboardOnly && clearIconHower) {
+ drawGUIBox(SCREEN_WIDTH - 52, SCREEN_HEIGHT - 52, 40, 40, renderer, 0xFFFFFF40);
+ }
+
//Draw icon.
applyTexture(SCREEN_WIDTH-48,SCREEN_HEIGHT-48,*clearIcon,renderer);
//NOTE: The rendering of the GUI is done in Main.
}
void Options::resize(ImageManager& imageManager, SDL_Renderer& renderer){
//Recreate the gui to fit the new resolution.
createGUI(imageManager,renderer);
}
diff --git a/src/OptionsMenu.h b/src/OptionsMenu.h
index 78a5c50..15b8ee4 100644
--- a/src/OptionsMenu.h
+++ b/src/OptionsMenu.h
@@ -1,92 +1,102 @@
/*
* Copyright (C) 2011-2013 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef OPTIONS_MENU_H
#define OPTIONS_MENU_H
#include <SDL.h>
#include "GameState.h"
//Included for the Options menu.
#include "Render.h"
#include "GUIObject.h"
#include "InputManager.h"
#include "ImageManager.h"
class GUISlider;
class GUISingleLineListBox;
//The Options menu.
class Options : public GameState, private GUIEventCallback{
private:
//The title of the options menu.
TexturePtr title;
//Icon.
SharedTexture clearIcon;
bool clearIconHower;
TexturePtr clearTooltip;
//Slider used to set the music volume
GUISlider* musicSlider;
//Slider used to set the sound volume
GUISlider* soundSlider;
//Integer to keep track of the time passed since last playing the test sound.
int lastJumpSound;
//ListBox containing the themes the user can choose out.
GUISingleLineListBox* theme;
//Available languages
GUISingleLineListBox* langs;
//Resolution list
GUISingleLineListBox* resolutions;
//Containers for different tabs.
GUIObject* tabGeneral;
GUIObject* tabControls;
-
+
+ //Buttons.
+ GUIButton *cmdBack, *cmdSave;
+
+ //Selected section for keyboard/gamepad control
+ //1=tab selection (UNSUPPORTED) 2=options selection 3=OK/Cancel, etc.
+ int section;
+
+ //Selected section for keyboard/gamepad control. Only used when section==3.
+ int section2;
+
//Keys.
InputManagerKeyCode tmpKeys[INPUTMGR_MAX], tmpAlternativeKeys[INPUTMGR_MAX];
//GUI events are handled here.
//name: The name of the element that invoked the event.
//obj: Pointer to the object that invoked the event.
//eventType: Integer containing the type of event.
void GUIEventCallback_OnEvent(ImageManager& imageManager, SDL_Renderer& renderer, std::string name,GUIObject* obj,int eventType);
public:
//Constructor.
Options(ImageManager& imageManager,SDL_Renderer& renderer);
//Destructor.
~Options();
//Method that will create the GUI for the options menu.
void createGUI(ImageManager &imageManager, SDL_Renderer &renderer);
//Inherited from GameState.
void handleEvents(ImageManager& imageManager, SDL_Renderer& renderer) override;
void logic(ImageManager&, SDL_Renderer&) override;
void render(ImageManager&, SDL_Renderer& renderer) override;
void resize(ImageManager &imageManager, SDL_Renderer&renderer) override;
};
#endif

File Metadata

Mime Type
text/x-diff
Expires
Fri, May 15, 8:48 PM (30 m, 11 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
63975
Default Alt Text
(146 KB)

Event Timeline