Began refactoring away the GuiComponent system in favor of ES-config's Gui system.

Man, I'm nuts.
This commit is contained in:
Aloshi 2013-04-08 09:41:25 -05:00
parent 3e6adf7be4
commit 4a35c34dc0
10 changed files with 291 additions and 546 deletions

View file

@ -2,7 +2,7 @@ CXX=g++
CXXFLAGS=-Wall -g -O2 CXXFLAGS=-Wall -g -O2
LDFLAGS= LDFLAGS=
SRC_SOURCES=AudioManager.cpp InputConfig.cpp Log.cpp FolderData.cpp Font.cpp GameData.cpp GuiComponent.cpp InputManager.cpp main.cpp MathExp.cpp Renderer.cpp Renderer_draw_gl.cpp Renderer_init.cpp Sound.cpp SystemData.cpp XMLReader.cpp components/GuiAnimation.cpp components/GuiBox.cpp components/GuiFastSelect.cpp components/GuiGameList.cpp components/GuiImage.cpp components/GuiInputConfig.cpp components/GuiMenu.cpp components/GuiTheme.cpp pugiXML/pugixml.cpp SRC_SOURCES=AudioManager.cpp InputConfig.cpp Log.cpp FolderData.cpp Font.cpp GameData.cpp Gui.cpp InputManager.cpp main.cpp MathExp.cpp Renderer_draw_gl.cpp Renderer_init.cpp Sound.cpp SystemData.cpp XMLReader.cpp components/GuiAnimation.cpp components/GuiBox.cpp components/GuiFastSelect.cpp components/GuiGameList.cpp components/GuiImage.cpp components/GuiInputConfig.cpp components/GuiMenu.cpp components/GuiTheme.cpp pugiXML/pugixml.cpp
SOURCES=$(addprefix src/,$(SRC_SOURCES)) SOURCES=$(addprefix src/,$(SRC_SOURCES))
OBJECTS=$(SOURCES:.cpp=.o) OBJECTS=$(SOURCES:.cpp=.o)
DEPS=$(SOURCES:.cpp=.d) DEPS=$(SOURCES:.cpp=.d)

11
src/Gui.cpp Normal file
View file

@ -0,0 +1,11 @@
#include "Gui.h"
#include "Window.h"
Gui::Gui(Window* window) : mWindow(window)
{
}
Gui::~Gui()
{
mWindow->removeGui(this);
}

21
src/Gui.h Normal file
View file

@ -0,0 +1,21 @@
#ifndef _GUI_H_
#define _GUI_H_
#include "InputConfig.h"
class Window;
class Gui
{
public:
Gui(Window* window);
virtual ~Gui();
virtual void input(InputConfig* config, Input input) = 0;
virtual void update(int deltaTime) = 0;
virtual void render() = 0;
protected:
Window* mWindow;
};
#endif

View file

@ -1,113 +0,0 @@
#include "GuiComponent.h"
#include "Renderer.h"
#include <iostream>
#include "Log.h"
std::vector<GuiComponent*> GuiComponent::sComponentVector;
GuiComponent::GuiComponent()
{
sComponentVector.push_back(this);
mOffsetX = 0;
mOffsetY = 0;
mOpacity = 255;
}
GuiComponent::~GuiComponent()
{
for(unsigned int i = 0; i < sComponentVector.size(); i++)
{
if(sComponentVector.at(i) == this)
{
sComponentVector.erase(sComponentVector.begin() + i);
}
}
}
void GuiComponent::addChild(GuiComponent* comp)
{
mChildren.push_back(comp);
}
void GuiComponent::removeChild(GuiComponent* comp)
{
for(unsigned int i = 0; i < mChildren.size(); i++)
{
if(mChildren.at(i) == comp)
{
mChildren.erase(mChildren.begin() + i);
return;
}
}
LOG(LogError) << "Error - tried to remove GuiComponent child, but couldn't find it!";
}
void GuiComponent::clearChildren()
{
mChildren.clear();
}
void GuiComponent::processTicks(int deltaTime)
{
for(unsigned int i = 0; i < sComponentVector.size(); i++)
{
sComponentVector.at(i)->onTick(deltaTime);
}
}
void GuiComponent::render()
{
onRender();
for(unsigned int i = 0; i < mChildren.size(); i++)
{
mChildren.at(i)->render();
}
}
void GuiComponent::pause()
{
onPause();
for(unsigned int i = 0; i < mChildren.size(); i++)
mChildren.at(i)->pause();
}
void GuiComponent::resume()
{
onResume();
for(unsigned int i = 0; i < mChildren.size(); i++)
mChildren.at(i)->resume();
}
void GuiComponent::init()
{
onInit();
for(unsigned int i = 0; i < mChildren.size(); i++)
{
mChildren.at(i)->init();
}
}
void GuiComponent::deinit()
{
onDeinit();
for(unsigned int i = 0; i < mChildren.size(); i++)
{
mChildren.at(i)->deinit();
}
}
void GuiComponent::setOffsetX(int val) { mOffsetX = val; }
void GuiComponent::setOffsetY(int val) { mOffsetY = val; }
void GuiComponent::setOffset(int x, int y) { mOffsetX = x; mOffsetY = y; }
int GuiComponent::getOffsetX() { return mOffsetX; }
int GuiComponent::getOffsetY() { return mOffsetY; }
void GuiComponent::setOpacity(unsigned char opacity) { mOpacity = opacity; }
unsigned char GuiComponent::getOpacity() { return mOpacity; }

View file

@ -1,68 +0,0 @@
#ifndef _GUICOMPONENT_H_
#define _GUICOMPONENT_H_
#include <vector>
#include "Renderer.h"
#include "InputManager.h"
/*
The GuiComponent class is what everything that is rendered, updated by time (ticking), or takes input is subclassed from.
GuiComponents have a list of child GuiComponents. Rendering, ticking, pausing/resuming, init/deinit, are all automatically sent to children.
You can rely on the parent getting called first - this way, you can control what order components are rendered in.
You can also manually call the render/pause/resume/init/deinit methods if you so desire (e.g. want a child to render *before* its parent).
To make a GuiComponent render/take input, you must register with the Renderer or InputManager respectively (Renderer::registerComponent(comp) or InputManager::registerComponent(comp)).
All components are automatically ticked every frame, just add an onTick(int deltaTime) method.
onInput calls arrive before onRender calls.
*/
class GuiComponent
{
public:
GuiComponent();
virtual ~GuiComponent();
void render();
virtual void onRender() { };
virtual void onTick(int deltaTime) { };
void pause();
void resume();
virtual void onPause() { };
virtual void onResume() { };
void init();
void deinit();
virtual void onInit() { };
virtual void onDeinit() { };
virtual void onInput(InputManager::InputButton button, bool keyDown) { };
void addChild(GuiComponent* comp);
void removeChild(GuiComponent* comp);
void clearChildren();
unsigned int getChildCount() { return mChildren.size(); }
GuiComponent* getChild(unsigned int i) { return mChildren.at(i); }
int getOffsetX();
int getOffsetY();
void setOffsetX(int val);
void setOffsetY(int val);
void setOffset(int x, int y);
unsigned char getOpacity();
void setOpacity(unsigned char opacity);
static void processTicks(int deltaTime);
private:
int mOffsetX, mOffsetY;
unsigned char mOpacity;
static std::vector<GuiComponent*> sComponentVector;
std::vector<GuiComponent*> mChildren;
};
#endif

View file

@ -1,311 +1,151 @@
#include "InputManager.h" #include "InputManager.h"
#include "GuiComponent.h" #include "InputConfig.h"
#include "Window.h"
#include <iostream> #include <iostream>
#include <fstream>
#include <sstream>
#include "Log.h"
std::vector<GuiComponent*> InputManager::inputVector; InputManager::InputManager(Window* window) : mWindow(window)
SDL_Event* InputManager::lastEvent = NULL;
std::map<int, InputManager::InputButton> InputManager::joystickButtonMap, InputManager::joystickAxisPosMap, InputManager::joystickAxisNegMap;
std::map<int, int> InputManager::axisState;
InputManager::InputButton InputManager::hatState = InputManager::UNKNOWN;
std::string InputManager::joystickName = "";
int InputManager::deadzone = 28000;
void InputManager::registerComponent(GuiComponent* comp)
{ {
inputVector.push_back(comp); mJoysticks = NULL;
mKeyboardInputConfig = NULL;
mNumJoysticks = 0;
mNumPlayers = 0;
} }
void InputManager::unregisterComponent(GuiComponent* comp) InputManager::~InputManager()
{ {
for(unsigned int i = 0; i < inputVector.size(); i++) deinit();
{
if(inputVector.at(i) == comp)
{
inputVector.erase(inputVector.begin() + i);
break;
}
}
} }
InputManager::InputButton InputManager::processEvent(SDL_Event* event) void InputManager::init()
{ {
bool keyDown = false; if(mJoysticks != NULL)
InputButton button = UNKNOWN; deinit();
lastEvent = event; SDL_InitSubSystem(SDL_INIT_JOYSTICK);
//keyboard events mNumJoysticks = SDL_NumJoysticks();
if(event->type == SDL_KEYDOWN || event->type == SDL_KEYUP) mJoysticks = new SDL_Joystick*[mNumJoysticks];
mInputConfigs = new InputConfig*[mNumJoysticks];
mPrevAxisValues = new std::map<int, int>[mNumJoysticks];
for(int i = 0; i < mNumJoysticks; i++)
{ {
if(event->key.state == SDL_PRESSED) mJoysticks[i] = SDL_JoystickOpen(i);
keyDown = true; mInputConfigs[i] = new InputConfig(i);
//get InputButton from the event for(int k = 0; k < SDL_JoystickNumAxes(mJoysticks[i]); k++)
switch(event->key.keysym.sym)
{ {
case SDLK_LEFT: mPrevAxisValues[i][k] = 0;
button = LEFT;
break;
case SDLK_RIGHT:
button = RIGHT;
break;
case SDLK_UP:
button = UP;
break;
case SDLK_DOWN:
button = DOWN;
break;
case SDLK_PAGEUP:
button = PAGEUP;
break;
case SDLK_RIGHTBRACKET:
button = PAGEUP;
break;
case SDLK_PAGEDOWN:
button = PAGEDOWN;
break;
case SDLK_LEFTBRACKET:
button = PAGEDOWN;
break;
case SDLK_RETURN:
button = BUTTON1;
break;
case SDLK_ESCAPE:
button = BUTTON2;
break;
case SDLK_F1:
button = MENU;
break;
case SDLK_F2:
button = SELECT;
break;
default:
button = UNKNOWN;
break;
}
//catch emergency quit event
if(event->key.keysym.sym == SDLK_F4 && keyDown)
{
//I have no idea if SDL will delete this event, but we're quitting, so I don't think it really matters
SDL_Event* quit = new SDL_Event();
quit->type = SDL_QUIT;
SDL_PushEvent(quit);
LOG(LogInfo) << "Pushing quit event.";
}
}else{
if(event->type == SDL_JOYBUTTONDOWN || event->type == SDL_JOYBUTTONUP) //joystick button events
{
if(event->type == SDL_JOYBUTTONDOWN) //defaults to false, so no else
keyDown = true;
button = joystickButtonMap[event->jbutton.button];
}else{
if(event->type == SDL_JOYHATMOTION)
{
int hat = event->jhat.value;
if(hat == 0) //centered
{
//we need to send a keyUp event for the last hat
//keyDown is already false
button = hatState;
}else{
keyDown = true;
}
if(hat & SDL_HAT_LEFT)
button = LEFT;
if(hat & SDL_HAT_RIGHT)
button = RIGHT;
if(hat & SDL_HAT_UP)
button = UP;
if(hat & SDL_HAT_DOWN)
button = DOWN;
if(button == hatState && keyDown)
{
//ignore this hat event since the user most likely just made it a diagonal (but it still is using the old direction)
button = UNKNOWN;
}else{
if(hatState != UNKNOWN && keyDown)
{
//this will occur if the user went down -> downLeft -> Left or similar
button = hatState;
keyDown = false;
hatState = UNKNOWN;
processEvent(event);
}else{
if(!keyDown)
hatState = UNKNOWN;
else
hatState = button;
}
}
}else{
if(event->type == SDL_JOYAXISMOTION)
{
int axis = event->jaxis.axis;
int value = event->jaxis.value;
//if this axis was previously not centered, it can only keyUp
if(axisState[axis] != 0)
{
if(abs(value) < deadzone) //if it has indeed centered
{
if(axisState[axis] > 0)
button = joystickAxisPosMap[axis];
else
button = joystickAxisNegMap[axis];
axisState[axis] = 0;
}
}else{
if(value > deadzone)
{
//axisPos keyDown
axisState[axis] = 1;
keyDown = true;
button = joystickAxisPosMap[axis];
}else if(value < -deadzone)
{
axisState[axis] = -1;
keyDown = true;
button = joystickAxisNegMap[axis];
}
}
}
}
} }
} }
for(unsigned int i = 0; i < inputVector.size(); i++) mKeyboardInputConfig = new InputConfig(DEVICE_KEYBOARD);
{
inputVector.at(i)->onInput(button, keyDown);
}
return button;
}
void InputManager::loadConfig()
{
LOG(LogDebug) << "Loading input config...";
//clear any old config
joystickButtonMap.clear();
joystickAxisPosMap.clear();
joystickAxisNegMap.clear();
std::string path = getConfigPath();
std::ifstream file(path.c_str());
joystickName = "";
while(file.good())
{
std::string line;
std::getline(file, line);
//skip blank lines and comments
if(line.empty() || line[0] == *"#")
continue;
//I know I could probably just read from the file stream directly, but I feel it would be harder to catch errors in a readable way
std::istringstream stream(line);
std::string token[3];
int tokNum = 0;
while(std::getline(stream, token[tokNum], ' '))
{
tokNum++;
//JOYNAME can have spaces
if(tokNum == 1 && token[0] == "JOYNAME")
{
std::getline(stream, token[1]);
break;
}
if(tokNum >= 3)
break;
}
if(token[0] == "BUTTON")
{
joystickButtonMap[atoi(token[1].c_str())] = (InputButton)atoi(token[2].c_str());
}else if(token[0] == "AXISPOS")
{
joystickAxisPosMap[atoi(token[1].c_str())] = (InputButton)atoi(token[2].c_str());
}else if(token[0] == "AXISNEG")
{
joystickAxisNegMap[atoi(token[1].c_str())] = (InputButton)atoi(token[2].c_str());
}else if(token[0] == "JOYNAME")
{
joystickName = token[1];
}else{
LOG(LogWarning) << "Invalid input type - " << token[0];
return;
}
}
LOG(LogDebug) << "Finished loading input config";
openJoystick();
}
void InputManager::openJoystick()
{
//if any joystick is plugged in
if(SDL_NumJoysticks() > 0)
{
if(!joystickName.empty())
{
LOG(LogDebug) << " attempting to open joystick of name \"" << joystickName << "\"";
bool found = false;
for(int i = 0; i < SDL_NumJoysticks(); i++)
{
if(strcmp(SDL_JoystickName(i), joystickName.c_str()) == 0)
{
LOG(LogDebug) << " found, opening joystick " << joystickName << " at " << i;
SDL_JoystickOpen(i);
found = true;
break;
}
}
if(!found)
{
LOG(LogWarning) << " could not find named joystick! You could try manually removing the joystick name entry in the input config file.";
}
}else{
LOG(LogDebug) << " opening first joystick (no name saved)";
SDL_JoystickOpen(0); //if we don't have a specific joystick in mind, take the first
}
}else{
LOG(LogDebug) << " no joysticks detected by SDL_NumJoysticks(), so no joystick opened";
}
SDL_JoystickEventState(SDL_ENABLE); SDL_JoystickEventState(SDL_ENABLE);
} }
std::string InputManager::getConfigPath() void InputManager::deinit()
{ {
std::string home = getenv("HOME"); SDL_JoystickEventState(SDL_DISABLE);
home += "/.emulationstation/es_input.cfg";
return home; if(!SDL_WasInit(SDL_INIT_JOYSTICK))
return;
if(mJoysticks != NULL)
{
for(int i = 0; i < mNumJoysticks; i++)
{
SDL_JoystickClose(mJoysticks[i]);
delete mInputConfigs[i];
}
delete mKeyboardInputConfig;
delete[] mJoysticks;
delete[] mInputConfigs;
delete[] mPrevAxisValues;
mJoysticks = NULL;
mKeyboardInputConfig = NULL;
mInputConfigs = NULL;
}
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
}
int InputManager::getNumJoysticks() { return mNumJoysticks; }
int InputManager::getNumPlayers() { return mNumPlayers; }
void InputManager::setNumPlayers(int num) { mNumPlayers = num; }
InputConfig* InputManager::getInputConfigByDevice(int device)
{
if(device == DEVICE_KEYBOARD)
return mKeyboardInputConfig;
else
return mInputConfigs[device];
}
InputConfig* InputManager::getInputConfigByPlayer(int player)
{
if(mKeyboardInputConfig->getPlayerNum() == player)
return mKeyboardInputConfig;
for(int i = 0; i < mNumJoysticks; i++)
{
if(mInputConfigs[i]->getPlayerNum() == player)
return mInputConfigs[i];
}
std::cout << "Could not find input config for player number " << player << "!\n";
return NULL;
}
void InputManager::parseEvent(const SDL_Event& ev)
{
switch(ev.type)
{
case SDL_JOYAXISMOTION:
//if it switched boundaries
if((abs(ev.jaxis.value) > DEADZONE) != (abs(mPrevAxisValues[ev.jaxis.which][ev.jaxis.axis]) > DEADZONE))
{
int normValue;
if(abs(ev.jaxis.value) <= DEADZONE)
normValue = 0;
else
if(ev.jaxis.value > 0)
normValue = 1;
else
normValue = -1;
mWindow->input(getInputConfigByDevice(ev.jaxis.which), Input(ev.jaxis.which, TYPE_AXIS, ev.jaxis.axis, normValue, false));
}
mPrevAxisValues[ev.jaxis.which][ev.jaxis.axis] = ev.jaxis.value;
break;
case SDL_JOYBUTTONDOWN:
case SDL_JOYBUTTONUP:
mWindow->input(getInputConfigByDevice(ev.jbutton.which), Input(ev.jbutton.which, TYPE_BUTTON, ev.jbutton.button, ev.jbutton.state == SDL_PRESSED, false));
break;
case SDL_JOYHATMOTION:
mWindow->input(getInputConfigByDevice(ev.jhat.which), Input(ev.jhat.which, TYPE_HAT, ev.jhat.hat, ev.jhat.value, false));
break;
case SDL_KEYDOWN:
if(ev.key.keysym.sym == SDLK_F4)
{
SDL_Event* quit = new SDL_Event();
quit->type = SDL_QUIT;
SDL_PushEvent(quit);
return;
}
mWindow->input(getInputConfigByDevice(DEVICE_KEYBOARD), Input(DEVICE_KEYBOARD, TYPE_KEY, ev.key.keysym.sym, 1, false));
break;
case SDL_KEYUP:
mWindow->input(getInputConfigByDevice(DEVICE_KEYBOARD), Input(DEVICE_KEYBOARD, TYPE_KEY, ev.key.keysym.sym, 0, false));
break;
}
} }

View file

@ -1,37 +1,46 @@
#ifndef _INPUTMANAGER_H_ #ifndef _INPUTMANAGER_H_
#define _INPUTMANAGER_H_ #define _INPUTMANAGER_H_
#include <SDL.h>
#include <vector> #include <vector>
#include <SDL/SDL.h>
#include <map> #include <map>
#include <string>
class GuiComponent; class InputConfig;
class Window;
//The InputManager takes native system input and abstracts it into InputButtons. //you should only ever instantiate one of these, by the way
//GuiComponents can be registered to receive onInput() events. class InputManager
namespace InputManager { {
void registerComponent(GuiComponent* comp); public:
void unregisterComponent(GuiComponent* comp); InputManager(Window* window);
~InputManager();
void loadConfig(); void init();
void openJoystick(); void deinit();
//enum for identifying input, regardless of configuration void setNumPlayers(int num);
enum InputButton { UNKNOWN, UP, DOWN, LEFT, RIGHT, BUTTON1, BUTTON2, MENU, SELECT, PAGEUP, PAGEDOWN}; int getNumPlayers();
InputButton processEvent(SDL_Event* event); int getNumJoysticks();
extern std::vector<GuiComponent*> inputVector; void parseEvent(const SDL_Event& ev);
extern SDL_Event* lastEvent; //mostly for GuiInputConfig
extern int deadzone;
std::string getConfigPath();
extern std::map<int, InputButton> joystickButtonMap; InputConfig* getInputConfigByPlayer(int player);
extern std::map<int, InputButton> joystickAxisPosMap, joystickAxisNegMap;
extern std::map<int, int> axisState; private:
extern InputButton hatState; static const int DEADZONE = 23000;
extern std::string joystickName;
} Window* mWindow;
//non-InputManager classes shouldn't use this, as you can easily miss the keyboard
InputConfig* getInputConfigByDevice(int device);
int mNumJoysticks;
int mNumPlayers;
SDL_Joystick** mJoysticks;
InputConfig** mInputConfigs;
InputConfig* mKeyboardInputConfig;
std::map<int, int>* mPrevAxisValues;
};
#endif #endif

View file

@ -1,54 +0,0 @@
#include "Renderer.h"
#include "GuiComponent.h"
std::vector<GuiComponent*> renderVector;
void Renderer::registerComponent(GuiComponent* comp)
{
renderVector.push_back(comp);
}
void Renderer::unregisterComponent(GuiComponent* comp)
{
for(unsigned int i = 0; i < renderVector.size(); i++)
{
if(renderVector.at(i) == comp)
{
renderVector.erase(renderVector.begin() + i);
break;
}
}
}
void Renderer::deleteAll()
{
for(unsigned int i = 0; i < renderVector.size(); i++)
{
delete renderVector.at(i);
}
renderVector.clear();
}
void Renderer::render()
{
for(unsigned int i = 0; i < renderVector.size(); i++)
{
renderVector.at(i)->render();
}
}
void Renderer::onInit()
{
for(unsigned int i = 0; i < renderVector.size(); i++)
{
renderVector.at(i)->init();
}
}
void Renderer::onDeinit()
{
for(unsigned int i = 0; i < renderVector.size(); i++)
{
renderVector.at(i)->deinit();
}
}

70
src/Window.cpp Normal file
View file

@ -0,0 +1,70 @@
#include "Window.h"
#include <iostream>
Window::Window()
{
mInputManager = new InputManager(this);
}
Window::~Window()
{
delete mInputManager;
}
void Window::pushGui(Gui* gui)
{
mGuiStack.push_back(gui);
}
void Window::removeGui(Gui* gui)
{
for(unsigned int i = 0; i < mGuiStack.size(); i++)
{
if(mGuiStack.at(i) == gui)
{
mGuiStack.erase(mGuiStack.begin() + i);
break;
}
}
}
Gui* Window::peekGui()
{
if(mGuiStack.size() == 0)
return NULL;
return mGuiStack.at(mGuiStack.size() - 1);
}
void Window::render()
{
if(mGuiStack.size() == 0)
std::cout << "guistack empty\n";
for(unsigned int i = 0; i < mGuiStack.size(); i++)
{
mGuiStack.at(i)->render();
}
}
void Window::input(InputConfig* config, Input input)
{
if(peekGui())
this->peekGui()->input(config, input);
}
void Window::update(int deltaTime)
{
if(peekGui())
peekGui()->update(deltaTime);
}
InputManager* Window::getInputManager()
{
return mInputManager;
}
asIScriptEngine* Window::getScriptEngine()
{
return mScriptEngine;
}

29
src/Window.h Normal file
View file

@ -0,0 +1,29 @@
#ifndef _WINDOW_H_
#define _WINDOW_H_
#include "Gui.h"
#include "InputManager.h"
#include <vector>
class Window
{
public:
Window();
~Window();
void pushGui(Gui* gui);
void removeGui(Gui* gui);
Gui* peekGui();
void input(InputConfig* config, Input input);
void update(int deltaTime);
void render();
InputManager* getInputManager();
private:
InputManager* mInputManager;
std::vector<Gui*> mGuiStack;
};
#endif