Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
44 KB
Referenced Files
None
Subscribers
None
diff --git a/src/GUIListBox.cpp b/src/GUIListBox.cpp
index c4bbcca..e3c50e5 100644
--- a/src/GUIListBox.cpp
+++ b/src/GUIListBox.cpp
@@ -1,550 +1,565 @@
/*
* 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 "GUIListBox.h"
#include "ThemeManager.h"
using namespace std;
namespace {
inline int tHeight(const SharedTexture& t) {
if(t) {
return rectFromTexture(*t).h;
} else {
return 0;
}
}
inline int tWidth(const SharedTexture& t) {
if(t) {
return rectFromTexture(*t).w;
} else {
return 0;
}
}
}
GUIListBox::GUIListBox(ImageManager& imageManager, SDL_Renderer& renderer,int left,int top,int width,int height,bool enabled,bool visible,int gravity):
GUIObject(imageManager,renderer,left,top,width,height,NULL,-1,enabled,visible,gravity),selectable(true),clickEvents(false){
//Set the state -1.
state=-1;
//Create the scrollbar and add it to the children.
scrollBar=new GUIScrollBar(imageManager,renderer,width-16,0,16,height,1,0,0,0,1,1,true,true);
childControls.push_back(scrollBar);
}
GUIListBox::~GUIListBox(){
//Remove all items and cache.
clearItems();
//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 GUIListBox::scrollScrollbar(int dy) {
if (scrollBar->enabled && dy) {
scrollBar->value = clamp(scrollBar->value + dy, 0, scrollBar->maxValue);
}
}
bool GUIListBox::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 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;
y+=top;
//Update the scrollbar.
if(scrollBar->visible)
b=b||scrollBar->handleEvents(renderer,x,y,enabled,visible,b);
//Set state negative.
state=-1;
//Check if the GUIListBox is visible, enabled and no event has been processed before.
if(enabled&&visible&&!b){
//The mouse location (x=i, y=j) and the mouse button (k).
int i,j;
SDL_GetMouseState(&i,&j);
//Convert the mouse location to a relative location.
i-=x;
j-=y;
//Check if the mouse is inside the GUIListBox.
if(i>=0&&i<width-4&&j>=0&&j<height-4){
//Calculate selected item.
int idx=-1;
int yPos=-firstItemY;
int i=scrollBar->value;
if(yPos!=0) i--;
for(;i<images.size();++i){
const SharedTexture& tex = images.at(i);
if(tex){
yPos+=tHeight(tex);
}
if(j<yPos){
idx=i;
break;
}
}
//If the entry isn't above the max we have an event.
if(idx>=0&&idx<(int)item.size()&&selectable){
state=idx;
//Check if the left mouse button is pressed.
if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button==SDL_BUTTON_LEFT){
//Check if the slected item changed.
if(value!=idx){
value=idx;
//If event callback is configured then add an event to the queue.
if(eventCallback){
GUIEvent e={eventCallback,name,this,GUIEventChange};
GUIEventQueue.push_back(e);
}
}
//After possibly a change event, there will always be a click event.
if(eventCallback && clickEvents){
GUIEvent e={eventCallback,name,this,GUIEventClick};
GUIEventQueue.push_back(e);
}
}
}
//Check for mouse wheel scrolling.
if (event.type == SDL_MOUSEWHEEL && event.wheel.y && scrollBar->enabled) {
scrollScrollbar(event.wheel.y < 0 ? 1 : -1);
}
}
}
//Process child controls event except for the scrollbar.
//That's why i starts at one.
for(unsigned int i=1;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 GUIListBox::render(SDL_Renderer& renderer, int x,int y,bool draw){
if(updateScrollbar){
//Calculate the height of the content.
int maxY=0;
for(const SharedTexture& t: images){
if(t){
maxY+=textureHeight(*t);
} else {
std::cerr << "WARNING: Null texture in GUIListBox!" << std::endl;
}
}
//Check if we need to show the scrollbar for many entries.
if(maxY<height){
scrollBar->maxValue=0;
scrollBar->value=0;
scrollBar->visible=false;
}else{
scrollBar->visible=true;
scrollBar->maxValue=item.size();
int yy=0;
for(int i=images.size()-1;i>0;i--){
yy+=textureHeight(*images.at(i));
if(yy>height)
break;
else
scrollBar->maxValue--;
}
scrollBar->largeChange=item.size()/4;
}
updateScrollbar=false;
}
//Rectangle the size of the GUIObject, used to draw borders.
//SDL_Rect r; //Unused local variable :/
//There's no need drawing the GUIObject when it's invisible.
if(!visible||!draw)
return;
//Get the absolute x and y location.
x+=left;
y+=top;
//Draw the background box.
const SDL_Rect r={x,y,width,height};
SDL_SetRenderDrawColor(&renderer,255,255,255,220);
SDL_RenderFillRect(&renderer, &r);
firstItemY=0;
//Loop through the entries and draw them.
if(scrollBar->value==scrollBar->maxValue&&scrollBar->visible){
int lowNumber=height;
int currentItem=images.size()-1;
while(lowNumber>=0&&currentItem>=0){
const SharedTexture& currentTexture = images.at(currentItem);
lowNumber-=tHeight(currentTexture);//->h;
if(lowNumber>0){
if(selectable){
//Check if the mouse is hovering on current entry. If so draw borders around it.
if(state==currentItem)
drawGUIBox(x,y+lowNumber-1,width,tHeight(currentTexture)+1,renderer,0x00000000);
//Check if the current entry is selected. If so draw a gray background.
if(value==currentItem)
drawGUIBox(x,y+lowNumber-1,width,tHeight(currentTexture)+1,renderer,0xDDDDDDFF);
}
applyTexture(x,y+lowNumber,*currentTexture,renderer);
}else{
// This is the top item that is partially obscured.
if(selectable){
//Check if the mouse is hovering on current entry. If so draw borders around it.
if(state==currentItem)
drawGUIBox(x,y,width,tHeight(currentTexture)+lowNumber+1,renderer,0x00000000);
//Check if the current entry is selected. If so draw a gray background.
if(value==currentItem)
drawGUIBox(x,y,width,tHeight(currentTexture)+lowNumber+1,renderer,0xDDDDDDFF);
}
firstItemY=-lowNumber;
const SDL_Rect clip{ 0, -lowNumber, textureWidth(*currentTexture), textureHeight(*currentTexture) + lowNumber };
const SDL_Rect dstRect{x, y, clip.w, clip.h};
if (clip.w > 0 && clip.h > 0) SDL_RenderCopy(&renderer, currentTexture.get(), &clip, &dstRect);
break;
}
currentItem--;
}
}else{
for(int i=scrollBar->value,j=y+1;i<(int)item.size();i++){
//Check if the current item is out side of the widget.
int yOver=tHeight(images[i]);
if(j+yOver>y+height)
yOver=y+height-j;
if (yOver > 0){
if (selectable){
//Check if the mouse is hovering on current entry. If so draw borders around it.
if (state == i)
drawGUIBox(x, j - 1, width, yOver + 1, renderer, 0x00000000);
//Check if the current entry is selected. If so draw a gray background.
if (value == i)
drawGUIBox(x, j - 1, width, yOver + 1, renderer, 0xDDDDDDFF);
}
//Draw the image.
const SDL_Rect clip{ 0, 0, tWidth(images[i]), yOver };
const SDL_Rect dstRect{ x, j, clip.w, clip.h };
SDL_RenderCopy(&renderer, images[i].get(), &clip, &dstRect);
} else if (yOver<0) {
break;
}
j+=tHeight(images[i]);
}
}
//Draw borders around the whole thing.
drawGUIBox(x,y,width,height,renderer,0x00000000);
//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 GUIListBox::clearItems(){
item.clear();
images.clear();
}
void GUIListBox::addItem(SDL_Renderer &renderer, std::string name, SharedTexture texture) {
if(texture){
images.push_back(texture);
}else if(!texture&&!name.empty()){
auto tex=SharedTexture(textureFromText(renderer, *fontText, name.c_str(), objThemes.getTextColor(true)));
// Make sure we don't create any empty textures.
if(!tex) {
std::cerr << "WARNING: Failed to create texture from text: \"" << name << "\"" << std::endl;
return;
}
images.push_back(tex);
} else {
// If nothing was added, ignore it.
return;
}
item.push_back(name);
updateScrollbar=true;
}
void GUIListBox::updateItem(SDL_Renderer &renderer, int index, string newText, SharedTexture newTexture) {
if(newTexture) {
images.at(index) = newTexture;
} else if (!newTexture&&!newText.empty()) {
auto tex=SharedTexture(textureFromText(renderer, *fontText, newText.c_str(), objThemes.getTextColor(true)));
// Make sure we don't create any empty textures.
if(!tex) {
std::cerr << "WARNING: Failed to update texture at index" << index << " \"" << newText << "\"" << std::endl;
return;
}
images.at(index)=tex;
} else {
return;
}
item.at(index)=newText;
updateScrollbar=true;
}
std::string GUIListBox::getItem(int index){
return item.at(index);
}
GUISingleLineListBox::GUISingleLineListBox(ImageManager& imageManager, SDL_Renderer& renderer, int left, int top, int width, int height, bool enabled, bool visible, int gravity):
GUIObject(imageManager,renderer,left,top,width,height,NULL,-1,enabled,visible,gravity),animation(0){}
void GUISingleLineListBox::addItem(string name,string label){
//Check if the label is set, if not use the name.
if(label.size()==0)
label=name;
item.push_back(pair<string,string>(name,label));
}
void GUISingleLineListBox::addItems(vector<pair<string,string> > items){
vector<pair<string,string> >::iterator it;
for(it=items.begin();it!=items.end();++it){
addItem(it->first,it->second);
}
}
void GUISingleLineListBox::addItems(vector<string> items){
vector<string>::iterator it;
for(it=items.begin();it!=items.end();++it){
addItem(*it);
}
}
string GUISingleLineListBox::getName(unsigned int index){
if(index==-1)
index=value;
if(index<0||index>item.size())
return "";
return item[index].first;
}
bool GUISingleLineListBox::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(s) are enabled.
enabled=enabled && this->enabled;
//The GUIObject is only enabled when he and his parent(s) are enabled.
visible=visible && this->visible;
//Get the absolute position.
x+=left-gravityX;
y+=top;
state&=~0xF;
if(enabled&&visible){
- //The mouse location (x=i, y=j) and the mouse button (k).
- int i,j,k;
- k=SDL_GetMouseState(&i,&j);
-
- //Convert the mouse location to a relative location.
- i-=x;
- j-=y;
-
- //The selected button.
- //0=nothing 1=left 2=right.
- int idx=0;
-
- //Check which button the mouse is above.
- if(i>=0&&i<width&&j>=0&&j<height){
- if(i<26 && i<width/2){
- //The left arrow.
- idx=1;
- }else if(i>=width-26){
- //The right arrow.
- idx=2;
+ //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);
+
+ //Convert the mouse location to a relative location.
+ i -= x;
+ j -= y;
+
+ //The selected button.
+ //0=nothing 1=left 2=right.
+ int idx = 0;
+
+ //Check which button the mouse is above.
+ if (i >= 0 && i < width&&j >= 0 && j < height){
+ if (i < 26 && i < width / 2){
+ //The left arrow.
+ idx = 1;
+ } else if (i >= width - 26){
+ //The right arrow.
+ idx = 2;
+ }
}
- }
-
- //If idx is 0 it means the mous doesn't hover any arrow so reset animation.
- if(idx==0)
- animation=0;
-
- //Check if there's a mouse button press or not.
- if(k&SDL_BUTTON(1)){
- if(((state>>4)&0xF)==idx)
- state|=idx;
- }else{
- state|=idx;
- }
-
- //Check if there's a mouse press.
- if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button==SDL_BUTTON_LEFT && idx){
- state=idx|(idx<<4);
- }else if(event.type==SDL_MOUSEBUTTONUP && event.button.button==SDL_BUTTON_LEFT && idx && ((state>>4)&0xF)==idx){
- int m=(int)item.size();
- if(m>0){
- if(idx==2){
- idx=value+1;
- if(idx<0||idx>=m) idx=0;
- if(idx!=value){
- value=idx;
-
- //If there is an event callback then call it.
- if(eventCallback){
- GUIEvent e={eventCallback,name,this,GUIEventClick};
- GUIEventQueue.push_back(e);
+
+ //If idx is 0 it means the mous doesn't hover any arrow so reset animation.
+ if (idx == 0)
+ animation = 0;
+
+ //Check if there's a mouse button press or not.
+ if (k&SDL_BUTTON(1)){
+ if (((state >> 4) & 0xF) == idx)
+ state |= idx;
+ } else{
+ state |= idx;
+ }
+
+ //Check if there's a mouse press.
+ if (event.type == SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_LEFT && idx){
+ state = idx | (idx << 4);
+ } else if (event.type == SDL_MOUSEBUTTONUP && event.button.button == SDL_BUTTON_LEFT && idx && ((state >> 4) & 0xF) == idx){
+ int m = (int)item.size();
+ if (m > 0){
+ if (idx == 2){
+ idx = value + 1;
+ if (idx < 0 || idx >= m) idx = 0;
+ if (idx != value){
+ value = idx;
+
+ //If there is an event callback then call it.
+ if (eventCallback){
+ GUIEvent e = { eventCallback, name, this, GUIEventClick };
+ GUIEventQueue.push_back(e);
+ }
}
- }
- }else if(idx==1){
- idx=value-1;
- if(idx<0||idx>=m) idx=m-1;
- if(idx!=value){
- value=idx;
-
- //If there is an event callback then call it.
- if(eventCallback){
- GUIEvent e={eventCallback,name,this,GUIEventClick};
- GUIEventQueue.push_back(e);
+ } else if (idx == 1){
+ idx = value - 1;
+ if (idx < 0 || idx >= m) idx = m - 1;
+ if (idx != value){
+ value = idx;
+
+ //If there is an event callback then call it.
+ if (eventCallback){
+ GUIEvent e = { eventCallback, name, this, GUIEventClick };
+ GUIEventQueue.push_back(e);
+ }
}
}
}
}
+ if (event.type == SDL_MOUSEBUTTONUP) state &= 0xF;
}
- if(event.type==SDL_MOUSEBUTTONUP) state&=0xF;
}else{
//Set state zero.
state=0;
}
return b;
}
void GUISingleLineListBox::render(SDL_Renderer& renderer, int x,int y,bool draw){
//Rectangle the size of the GUIObject, used to draw borders.
SDL_Rect r;
//There's no need drawing the GUIObject when it's invisible.
if(!visible)
return;
//NOTE: logic in the render method since it's the only part that gets called every frame.
- if((state&0xF)==0x1 || (state&0xF)==0x2){
- animation++;
- if(animation>20)
- animation=-20;
+ if (!isKeyboardOnly) {
+ if ((state & 0xF) == 0x1 || (state & 0xF) == 0x2){
+ animation++;
+ if (animation > 20)
+ animation = -20;
+ }
}
//Get the absolute x and y location.
x+=left;
y+=top;
-
+
+ gravityX = 0;
+
if(gravity==GUIGravityCenter)
gravityX=int(width/2);
else if(gravity==GUIGravityRight)
gravityX=width;
x-=gravityX;
+ if (isKeyboardOnly && state && draw) {
+ drawGUIBox(x, y, width, height, renderer, 0xFFFFFF40);
+ }
+
//Check if the enabled state changed or the caption, if so we need to clear the (old) cache.
if(enabled!=cachedEnabled || item[value].second.compare(cachedCaption)!=0){
//Free the cache.
cacheTex.reset(nullptr);
//And cache the new values.
cachedEnabled=enabled;
cachedCaption=item[value].second;
}
//Draw the text.
if(value>=0 && value<(int)item.size()){
//Get the text.
const std::string& lp=item[value].second;
//Check if the text is empty or not.
if(!lp.empty()){
if(!cacheTex){
SDL_Color color = objThemes.getTextColor(inDialog);
cacheTex=textureFromText(renderer, *fontGUI, lp.c_str(), color);
//If the text is too wide then we change to smaller font (?)
//NOTE: The width is 32px smaller (2x16px for the arrows).
if(rectFromTexture(*cacheTex).w>width-32){
cacheTex=textureFromText(renderer, *fontGUISmall,lp.c_str(),color);
}
}
if(draw){
//Center the text both vertically as horizontally.
const SDL_Rect textureSize = rectFromTexture(*cacheTex);
r.x=x+(width-textureSize.w)/2;
r.y=y+(height-textureSize.h)/2-GUI_FONT_RAISE;
//Draw the text.
applyTexture(r.x, r.y, cacheTex, renderer);
}
}
}
if(draw){
//Draw the arrows.
r.x=x;
- if((state&0xF)==0x1)
- r.x+=abs(animation/2);
+ if (!isKeyboardOnly) {
+ if ((state & 0xF) == 0x1)
+ r.x += abs(animation / 2);
+ }
r.y=y+4;
if(inDialog)
applyTexture(r.x,r.y,*arrowLeft2,renderer);
else
applyTexture(r.x,r.y,*arrowLeft1,renderer);
r.x=x+width-16;
- if((state&0xF)==0x2)
- r.x-=abs(animation/2);
+ if (!isKeyboardOnly) {
+ if ((state & 0xF) == 0x2)
+ r.x -= abs(animation / 2);
+ }
if(inDialog)
applyTexture(r.x,r.y,*arrowRight2,renderer);
else
applyTexture(r.x,r.y,*arrowRight1,renderer);
}
}
diff --git a/src/GUIOverlay.cpp b/src/GUIOverlay.cpp
index 81e1041..c38165b 100644
--- a/src/GUIOverlay.cpp
+++ b/src/GUIOverlay.cpp
@@ -1,297 +1,332 @@
/*
* 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<GUITextBox*>(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)) {
//It's a button.
obj->state = 1;
return;
- } else if (dynamic_cast<GUITextBox*>(obj)) {
- //It's a button.
+ } 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 (obj->eventCallback) {
- if (dynamic_cast<GUIButton*>(obj)) {
- //It's a button.
+
+ if (dynamic_cast<GUIButton*>(obj)) {
+ //It's a button.
+ if (obj->eventCallback) {
obj->eventCallback->GUIEventCallback_OnEvent(imageManager, renderer, obj->name, obj, GUIEventClick);
- return;
}
+ 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);
}
}
}
//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)
: GUIOverlay(renderer, root), cancelButton(cancelButton), textArea(textArea)
{
keyboardNavigationMode = 4 | 8 | 16;
}
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/LevelSelect.cpp b/src/LevelSelect.cpp
index 24048e1..66766b6 100644
--- a/src/LevelSelect.cpp
+++ b/src/LevelSelect.cpp
@@ -1,450 +1,448 @@
/*
* 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 "GameState.h"
#include "Functions.h"
#include "FileManager.h"
#include "Globals.h"
#include "LevelSelect.h"
#include "GUIObject.h"
#include "GUIListBox.h"
#include "GUIScrollBar.h"
#include "InputManager.h"
#include <stdio.h>
#include <string>
#include <sstream>
#include <iostream>
#include "libs/tinyformat/tinyformat.h"
using namespace std;
////////////////////NUMBER////////////////////////
Number::Number(ImageManager& imageManager, SDL_Renderer& renderer){
image=NULL;
number=0;
medal=0;
selected=false;
locked=false;
//Set the default dimensions.
box.x=0;
box.y=0;
box.h=50;
box.w=50;
//Load the medals image.
medals=imageManager.loadTexture(getDataPath()+"gfx/medals.png", renderer);
//To make sure it can be added to a vector, stop here rather than generate a massive error.
static_assert(std::is_move_constructible<Number>::value, "Not move constructable!");
}
void Number::init(SDL_Renderer& renderer,int number,SDL_Rect box){
//First set the number and update our status.
this->number=number;
//Write our text, number+1 since the counting doens't start with 0, but with 1.
std::stringstream text;
number++;
text<<number;
//Create the text image.
//Also check which font to use, if the number is higher than 100 use the small font.
image = textureFromText(renderer,*fontGUI,text.str().c_str(),objThemes.getTextColor(true));
//Set the new location of the number.
this->box.x=box.x;
this->box.y=box.y;
//Load background blocks.
objThemes.getBlock(TYPE_BLOCK,true)->createInstance(&block);
block.changeState("unlocked");
objThemes.getBlock(TYPE_SHADOW_BLOCK,true)->createInstance(&blockLocked);
blockLocked.changeState("locked");
}
void Number::init(SDL_Renderer& renderer,std::string text,SDL_Rect box,int number_){
//First set the number and update our status.
this->number=number_;
//Create the text image.
image = textureFromText(renderer,*fontGUI,text.c_str(),objThemes.getTextColor(true));
//Set the new location of the number.
this->box.x=box.x;
this->box.y=box.y;
//Load background blocks.
objThemes.getBlock(TYPE_BLOCK,true)->createInstance(&block);
block.changeState("unlocked");
objThemes.getBlock(TYPE_SHADOW_BLOCK,true)->createInstance(&blockLocked);
blockLocked.changeState("locked");
}
void Number::show(SDL_Renderer& renderer, int dy){
//First draw the background, also apply the yOffset(dy).
if(!locked)
block.draw(renderer,box.x,box.y-dy);
else
blockLocked.draw(renderer,box.x,box.y-dy);
//Now draw the text image over the background.
//We draw it centered inside the box.
applyTexture(box.x+25-(textureWidth(*image)/2),box.y-dy,image,renderer);
//Draw the selection mark.
if(selected){
drawGUIBox(box.x,box.y-dy,50,50,renderer,0xFFFFFF23);
}
//Draw the medal.
if(medal>0&&medals){
const SDL_Rect srcRect={(medal-1)*30,0,30,30};
const SDL_Rect dstRect={box.x+30,(box.y+30)-dy,30,30};
SDL_RenderCopy(&renderer,medals.get(),&srcRect,&dstRect);
}
}
void Number::setLocked(bool locked){
this->locked=locked;
}
void Number::setMedal(int medal){
this->medal=medal;
}
/////////////////////LEVEL SELECT/////////////////////
LevelSelect::LevelSelect(ImageManager& imageManager,SDL_Renderer& renderer, const char* titleText, LevelPackManager::LevelPackLists packType){
//clear the selected level
selectedNumber=NULL;
//Calculate the LEVELS_PER_ROW and LEVEL_ROWS if they aren't calculated already.
calcRows();
//Render the title.
title=textureFromText(renderer,*fontTitle,titleText,objThemes.getTextColor(false));
//create GUI (test only)
GUIObject* obj;
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
GUIObjectRoot=new GUIObject(imageManager,renderer,0,0,SCREEN_WIDTH,SCREEN_HEIGHT);
//the level select scroll bar
levelScrollBar=new GUIScrollBar(imageManager,renderer,SCREEN_WIDTH*0.9,184,16,SCREEN_HEIGHT-344,ScrollBarVertical,0,0,0,1,4,true,false);
GUIObjectRoot->addChild(levelScrollBar);
//level pack description
levelpackDescription=new GUILabel(imageManager,renderer,0,140,SCREEN_WIDTH,32,"",0,true,true,GUIGravityCenter);
GUIObjectRoot->addChild(levelpackDescription);
levelpacks=new GUISingleLineListBox(imageManager,renderer,(SCREEN_WIDTH-500)/2,104,500,32);
levelpacks->name="cmdLvlPack";
levelpacks->eventCallback=this;
vector<pair<string,string> > v=getLevelPackManager()->enumLevelPacks(packType);
levelpacks->addItems(v);
levelpacks->value=0;
//Check if we can find the lastlevelpack.
for(vector<pair<string,string> >::iterator i=v.begin(); i!=v.end(); ++i){
if(i->first==getSettings()->getValue("lastlevelpack")){
levelpacks->value=i-v.begin();
}
}
//Load the progress.
levels=getLevelPackManager()->getLevelPack(v[levelpacks->value].first);
levels->loadProgress();
//And add the levelpack single line listbox to the GUIObjectRoot.
GUIObjectRoot->addChild(levelpacks);
obj=new GUIButton(imageManager,renderer,20,20,-1,32,_("Back"));
obj->name="cmdBack";
obj->eventCallback=this;
GUIObjectRoot->addChild(obj);
section=1;
section2 = 1;
}
LevelSelect::~LevelSelect(){
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
levelScrollBar=NULL;
levelpackDescription=NULL;
selectedNumber=NULL;
}
void LevelSelect::calcRows(){
//Calculate the number of rows and the number of levels per row.
LEVELS_PER_ROW=(SCREEN_WIDTH*0.8)/64;
int LEVEL_ROWS=(SCREEN_HEIGHT-344)/64;
LEVELS_DISPLAYED_IN_SCREEN=LEVELS_PER_ROW*LEVEL_ROWS;
}
void LevelSelect::selectNumberKeyboard(ImageManager& imageManager, SDL_Renderer& renderer, int x,int y){
isKeyboardOnly = true;
if(section==2){
//Move selection
int realNumber=-1;
if(selectedNumber)
realNumber=selectedNumber->getNumber()+x+(y*LEVELS_PER_ROW);
int delta = (x + y < 0) ? -1 : 1;
for (;;) {
//If selection is outside of the map grid, change section
if (realNumber<0 || realNumber>(int)numbers.size() - 1){
section = 1;
for (int i = 0; i < (int)numbers.size(); i++){
numbers[i].selected = false;
}
selectNumber(imageManager, renderer, -1, false);
break;
} else {
//If not, move selection
if (!numbers[realNumber].getLocked()){
for (int i = 0; i < (int)numbers.size(); i++){
numbers[i].selected = (i == realNumber);
}
selectNumber(imageManager, renderer, realNumber, false);
break;
}
}
realNumber += delta;
}
}else if(section==1){
if (x != 0) {
//Loop through levelpacks and update GUI
levelpacks->value += x;
if (levelpacks->value<0){
levelpacks->value = levelpacks->item.size() - 1;
} else if (levelpacks->value>(int)levelpacks->item.size() - 1){
levelpacks->value = 0;
}
GUIEventCallback_OnEvent(imageManager, renderer, "cmdLvlPack", static_cast<GUIObject*>(levelpacks), 0);
}
//If down is pressed, change section
if(y==1){
section=2;
if (!numbers.empty()) {
selectNumber(imageManager, renderer, 0, false);
numbers[0].selected = true;
}
}
}
}
void LevelSelect::handleEvents(ImageManager& imageManager, SDL_Renderer& renderer){
//Check for an SDL_QUIT event.
if(event.type==SDL_QUIT){
setNextState(STATE_EXIT);
}
//Check for a mouse click.
if(event.type==SDL_MOUSEBUTTONUP && event.button.button==SDL_BUTTON_LEFT){
checkMouse(imageManager, renderer);
}
//Check focus movement
if(inputMgr.isKeyDownEvent(INPUTMGR_RIGHT)){
selectNumberKeyboard(imageManager, renderer, 1,0);
}else if(inputMgr.isKeyDownEvent(INPUTMGR_LEFT)){
selectNumberKeyboard(imageManager, renderer, -1,0);
}else if(inputMgr.isKeyDownEvent(INPUTMGR_UP)){
selectNumberKeyboard(imageManager, renderer, 0,-1);
}else if(inputMgr.isKeyDownEvent(INPUTMGR_DOWN)){
selectNumberKeyboard(imageManager, renderer, 0,1);
}
if (inputMgr.isKeyDownEvent(INPUTMGR_TAB)) {
isKeyboardOnly = true;
int mod = SDL_GetModState();
section += (mod & KMOD_SHIFT) ? -1 : 1;
if (section < 1) section = 3;
else if (section > 3) section = 1;
if (section == 2 && (selectedNumber == NULL || selectedNumber->getNumber() < 0) && !numbers.empty()) {
selectNumber(imageManager, renderer, 0, false);
numbers[0].selected = true;
}
}
//Check if enter is pressed
if (isKeyboardOnly && section == 2 && inputMgr.isKeyDownEvent(INPUTMGR_SELECT) && selectedNumber) {
int n = selectedNumber->getNumber();
if (n >= 0) {
selectNumber(imageManager, renderer, n, true);
}
}
//Check if escape is pressed.
if(inputMgr.isKeyDownEvent(INPUTMGR_ESCAPE)){
setNextState(STATE_MENU);
}
//Check for scrolling down and up.
if (event.type == SDL_MOUSEWHEEL && levelScrollBar){
//TODO - tweak the scroll amount
levelScrollBar->value += event.wheel.y > 0 ? -1 : 1;
if (levelScrollBar->value > levelScrollBar->maxValue) {
levelScrollBar->value = levelScrollBar->maxValue;
}
if (levelScrollBar->value < 0) {
levelScrollBar->value = 0;
}
}
}
void LevelSelect::checkMouse(ImageManager &imageManager, SDL_Renderer &renderer){
int x,y,dy=0,m=numbers.size();
//Get the current mouse location.
SDL_GetMouseState(&x,&y);
//Check if there's a scrollbar, if so get the value.
if(levelScrollBar)
dy=levelScrollBar->value;
//Upper bound of levels we'd like to display.
if(m>dy*LEVELS_PER_ROW+LEVELS_DISPLAYED_IN_SCREEN)
m=dy*LEVELS_PER_ROW+LEVELS_DISPLAYED_IN_SCREEN;
y+=dy*64;
SDL_Rect mouse={x,y,0,0};
for(int n=dy*LEVELS_PER_ROW; n<m; n++){
if(!numbers[n].getLocked()){
if(checkCollision(mouse,numbers[n].box)==true){
if(numbers[n].selected){
selectNumber(imageManager, renderer, n,true);
}else{
//Select current level
for(int i=0;i<levels->getLevelCount();i++){
numbers[i].selected=(i==n);
}
selectNumber(imageManager, renderer,n,false);
}
section=2;
break;
}
}
}
}
void LevelSelect::logic(ImageManager&, SDL_Renderer&){}
void LevelSelect::render(ImageManager&, SDL_Renderer& renderer){
int x,y,dy=0,m=numbers.size();
int idx=-1;
//Get the current mouse location.
SDL_GetMouseState(&x,&y);
if(levelScrollBar)
dy=levelScrollBar->value;
//Upper bound of levels we'd like to display.
if(m>dy*LEVELS_PER_ROW+LEVELS_DISPLAYED_IN_SCREEN)
m=dy*LEVELS_PER_ROW+LEVELS_DISPLAYED_IN_SCREEN;
y+=dy*64;
SDL_Rect mouse={x,y,0,0};
//Draw background.
objThemes.getBackground(true)->draw(renderer);
objThemes.getBackground(true)->updateAnimation();
//Draw the title.
drawTitleTexture(SCREEN_WIDTH, *title, renderer);
//Draw highlight and do some calculations in keyboard-only mode.
int realNumber = -1;
if (isKeyboardOnly) {
- if (section == 1) {
- drawGUIBox((SCREEN_WIDTH - 508) / 2, 100, 508, 36, renderer, 0xFFFFFF40);
- }
+ levelpacks->state = (section == 1) ? 0x100 : 0;
if (selectedNumber)
realNumber = selectedNumber->getNumber();
}
//Loop through the level blocks and draw them.
for(int n=dy*LEVELS_PER_ROW;n<m;n++){
numbers[n].show(renderer,dy*64);
if (!numbers[n].getLocked()) {
if (isKeyboardOnly) {
if (realNumber == n) idx = n;
} else {
if (checkCollision(mouse, numbers[n].box)) idx = n;
}
}
}
//Show the tool tip text.
if(idx>=0){
renderTooltip(renderer,idx,dy);
}
}
void LevelSelect::resize(ImageManager& imageManager,SDL_Renderer& renderer){
calcRows();
refresh(imageManager,renderer,false);
//NOTE: We don't need to recreate the listbox and the back button, only resize the list.
levelpacks->left=(SCREEN_WIDTH-500)/2;
levelpackDescription->width = SCREEN_WIDTH;
}
void LevelSelect::GUIEventCallback_OnEvent(ImageManager& imageManager, SDL_Renderer& renderer, std::string name,GUIObject* obj,int eventType){
if(name=="cmdLvlPack"){
getSettings()->setValue("lastlevelpack",static_cast<GUISingleLineListBox*>(obj)->item[obj->value].first);
}else if(name=="cmdBack"){
setNextState(STATE_MENU);
return;
}else{
return;
}
//new: reset the level list scroll bar
if(levelScrollBar)
levelScrollBar->value=0;
levels=getLevelPackManager()->getLevelPack(static_cast<GUISingleLineListBox*>(obj)->item[obj->value].first);
//Load the progress file.
levels->loadProgress();
//And refresh the numbers.
refresh(imageManager, renderer);
//invalidate the tooltip
toolTip.number = -1;
}

File Metadata

Mime Type
text/x-diff
Expires
Fri, May 15, 11:30 PM (3 h, 12 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
63986
Default Alt Text
(44 KB)

Event Timeline