diff --git a/.gitignore b/.gitignore index 4d087d107..063da9dcf 100644 --- a/.gitignore +++ b/.gitignore @@ -14,13 +14,18 @@ # Dependency makefiles *.d -#Compiled executable +# Compiled executable emulationstation -#build directory -EmulationStation_vs2010 +# build directory build Debug Release MinSizeRel -RelWithDebInfo \ No newline at end of file +RelWithDebInfo + +# CMake +CMakeCache.txt +CMakeFiles +cmake_install.cmake +Makefile diff --git a/CMakeLists.txt b/CMakeLists.txt index ba96dd160..ab88f5c13 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -117,7 +117,7 @@ set(ES_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/FolderData.h ${CMAKE_CURRENT_SOURCE_DIR}/src/Font.h ${CMAKE_CURRENT_SOURCE_DIR}/src/GameData.h - ${CMAKE_CURRENT_SOURCE_DIR}/src/Gui.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/GuiComponent.h ${CMAKE_CURRENT_SOURCE_DIR}/src/ImageIO.h ${CMAKE_CURRENT_SOURCE_DIR}/src/InputConfig.h ${CMAKE_CURRENT_SOURCE_DIR}/src/InputManager.h @@ -125,21 +125,28 @@ set(ES_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/MathExp.h ${CMAKE_CURRENT_SOURCE_DIR}/src/platform.h ${CMAKE_CURRENT_SOURCE_DIR}/src/Renderer.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/Settings.h ${CMAKE_CURRENT_SOURCE_DIR}/src/Sound.h ${CMAKE_CURRENT_SOURCE_DIR}/src/SystemData.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/Vector2.h ${CMAKE_CURRENT_SOURCE_DIR}/src/VolumeControl.h ${CMAKE_CURRENT_SOURCE_DIR}/src/Window.h ${CMAKE_CURRENT_SOURCE_DIR}/src/XMLReader.h - ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiAnimation.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/AnimationComponent.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/ComponentListComponent.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/ImageComponent.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/SliderComponent.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/SwitchComponent.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/TextComponent.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/TextListComponent.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/ThemeComponent.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiBox.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiDetectDevice.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiFastSelect.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiGameList.h - ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiImage.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiInputConfig.h - ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiList.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMenu.h - ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiTheme.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiSettingsMenu.h ${CMAKE_CURRENT_SOURCE_DIR}/src/pugiXML/pugiconfig.hpp ${CMAKE_CURRENT_SOURCE_DIR}/src/pugiXML/pugixml.hpp ${CMAKE_CURRENT_SOURCE_DIR}/data/Resources.h @@ -149,7 +156,7 @@ set(ES_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/FolderData.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Font.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/GameData.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/Gui.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/GuiComponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/ImageIO.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/InputConfig.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/InputManager.cpp @@ -159,21 +166,26 @@ set(ES_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/platform.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Renderer_draw_gl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Renderer_init.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/Settings.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Sound.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/SystemData.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/VolumeControl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Window.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/XMLReader.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiAnimation.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/AnimationComponent.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/ComponentListComponent.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/ImageComponent.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/SliderComponent.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/SwitchComponent.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/TextComponent.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/ThemeComponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiBox.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiDetectDevice.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiFastSelect.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiGameList.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiImage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiInputConfig.cpp -# ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiList.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMenu.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiTheme.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiSettingsMenu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/pugiXML/pugixml.cpp ${CMAKE_CURRENT_SOURCE_DIR}/data/logo/ES_logo_16.cpp ${CMAKE_CURRENT_SOURCE_DIR}/data/logo/ES_logo_32.cpp diff --git a/EmulationStation_vs2010/EmulationStation_vs2010.sln b/EmulationStation_vs2010/EmulationStation_vs2010.sln deleted file mode 100644 index 2e2783b31..000000000 --- a/EmulationStation_vs2010/EmulationStation_vs2010.sln +++ /dev/null @@ -1,20 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual C++ Express 2010 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EmulationStation_vs2010", "EmulationStation_vs2010\EmulationStation_vs2010.vcxproj", "{0AB5C397-7B64-4AAD-92AD-E6B72FEDE8C3}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0AB5C397-7B64-4AAD-92AD-E6B72FEDE8C3}.Debug|Win32.ActiveCfg = Debug|Win32 - {0AB5C397-7B64-4AAD-92AD-E6B72FEDE8C3}.Debug|Win32.Build.0 = Debug|Win32 - {0AB5C397-7B64-4AAD-92AD-E6B72FEDE8C3}.Release|Win32.ActiveCfg = Release|Win32 - {0AB5C397-7B64-4AAD-92AD-E6B72FEDE8C3}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/EmulationStation_vs2010/EmulationStation_vs2010/EmulationStation_vs2010.vcxproj b/EmulationStation_vs2010/EmulationStation_vs2010/EmulationStation_vs2010.vcxproj deleted file mode 100644 index d29e27a3d..000000000 --- a/EmulationStation_vs2010/EmulationStation_vs2010/EmulationStation_vs2010.vcxproj +++ /dev/null @@ -1,128 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {0AB5C397-7B64-4AAD-92AD-E6B72FEDE8C3} - EmulationStation_vs2010 - - - - Application - true - MultiByte - - - Application - false - true - MultiByte - - - - - - - - - - - - - - - - - Level3 - Disabled - - - true - - - - - Level3 - MaxSpeed - true - true - USE_OPENGL_DESKTOP;%(PreprocessorDefinitions) - - - true - true - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/EmulationStation_vs2010/EmulationStation_vs2010/EmulationStation_vs2010.vcxproj.filters b/EmulationStation_vs2010/EmulationStation_vs2010/EmulationStation_vs2010.vcxproj.filters deleted file mode 100644 index 4be1df7e5..000000000 --- a/EmulationStation_vs2010/EmulationStation_vs2010/EmulationStation_vs2010.vcxproj.filters +++ /dev/null @@ -1,195 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - {31a8e8d1-9795-42bf-99fd-500e57ac87d4} - - - {bec1ca52-69f2-42eb-b134-57016057661a} - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - components - - - components - - - components - - - components - - - components - - - components - - - components - - - components - - - components - - - PugiXML - - - Source Files - - - Source Files - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - components - - - components - - - components - - - components - - - components - - - components - - - components - - - components - - - components - - - components - - - PugiXML - - - PugiXML - - - \ No newline at end of file diff --git a/EmulationStation_vs2010/EmulationStation_vs2010/lib_paths.props b/EmulationStation_vs2010/EmulationStation_vs2010/lib_paths.props deleted file mode 100644 index 4b9b5bf1d..000000000 --- a/EmulationStation_vs2010/EmulationStation_vs2010/lib_paths.props +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - $(IncludePath) - $(LibraryPath) - - - - _DESKTOP_;%(PreprocessorDefinitions) - - - SDLmain.lib;SDL.lib;FreeImage.lib;freetype.lib;opengl32.lib;%(AdditionalDependencies) - Windows - - - - \ No newline at end of file diff --git a/Makefile b/Makefile deleted file mode 100644 index f64e41ea6..000000000 --- a/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -CPPFLAGS=-I/opt/vc/include -I/opt/vc/include/interface/vcos/pthreads -I/opt/vc/include/interface/vmcs_host/linux -I/usr/include/freetype2 -I/usr/include/SDL -D_RPI_ -LIBS=-L/opt/vc/lib -lbcm_host -lEGL -lGLESv2 -lfreetype -lSDL -lboost_system -lboost_filesystem -lfreeimage -lSDL_mixer - -include Makefile.common diff --git a/Makefile.common b/Makefile.common deleted file mode 100644 index 15ec7a98c..000000000 --- a/Makefile.common +++ /dev/null @@ -1,25 +0,0 @@ -CXX=g++ -CXXFLAGS=-Wall -g -O2 -LDFLAGS= - -SRC_SOURCES=platform.cpp AudioManager.cpp Window.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/GuiMenu.cpp components/GuiTheme.cpp components/GuiInputConfig.cpp components/GuiDetectDevice.cpp pugiXML/pugixml.cpp -SOURCES=$(addprefix src/,$(SRC_SOURCES)) -OBJECTS=$(SOURCES:.cpp=.o) -DEPS=$(SOURCES:.cpp=.d) -EXECUTABLE=emulationstation - -all: $(EXECUTABLE) - -$(EXECUTABLE): $(DEPS) $(OBJECTS) - $(CXX) -o $@ $(LDFLAGS) $(OBJECTS) $(LIBS) - -include $(wildcard src/*.d src/components/*.d src/pugiXML/*.d) - -%.d: %.cpp - $(SHELL) -ec '$(CXX) -M $(CPPFLAGS) $< | tr -d "\\\\\n" | sed -e "s|$(*F).o|$(*D)/& $@|" -e "s|: \(.*\)|: \$$\(wildcard \1\)|" > $@' - -.PHONY: clean - -clean: - rm -f src/*.[do] src/components/*.[do] src/pugiXML/*.[do] $(EXECUTABLE) - diff --git a/Makefile.x86 b/Makefile.x86 deleted file mode 100644 index 1217a1f00..000000000 --- a/Makefile.x86 +++ /dev/null @@ -1,4 +0,0 @@ -CPPFLAGS=-I/usr/include/freetype2 -I/usr/include/SDL -D_DESKTOP_ -LIBS=-lGL -lfreetype -lSDL -lboost_system -lboost_filesystem -lfreeimage -lSDL_mixer - -include Makefile.common diff --git a/README.md b/README.md index 9b9b5868f..5a1700639 100644 --- a/README.md +++ b/README.md @@ -1,44 +1,54 @@ EmulationStation ================ -A graphical front-end for emulators with controller navigation. Developed for the Raspbery Pi, but runs on most Linux systems. +A cross-platform graphical front-end for emulators with controller navigation. -A cool guy named petrockblog made a script which automatically installs RetroArch, its cores, and ES. It also includes options for configuring your RPi and setting it up to boot directly into ES. You can find it here: https://github.com/petrockblog/RetroPie-Setup +**Raspberry Pi users:** +A cool guy named petrockblog made a script which automatically installs many emulators and ES. It also includes options for configuring your RPi and setting it up to boot directly into ES. You can find it here: https://github.com/petrockblog/RetroPie-Setup Building ======== -**On the Raspberry Pi:** +EmulationStation uses some C++11 code, which means you'll need to install at least g++-4.7 on Linux, or VS2010 on Windows. -EmulationStation has a few dependencies. For building, you'll need SDL 1.2, SDL_mixer, FreeImage, FreeType, and Boost.Filesystem, which can easily be obtained with apt-get: +EmulationStation has a few dependencies. For building, you'll need SDL 1.2, Boost.System, Boost.Filesystem, FreeImage, FreeType, and the DejaVu TrueType font. + +**On Linux:** +All of this be easily installed with apt-get: ``` sudo apt-get install libsdl1.2-dev libboost-system-dev libboost-filesystem-dev libfreeimage-dev libfreetype6-dev ttf-dejavu ``` -There are also a few libraries already on the RPi (located in /opt/vc/, like the Broadcom libraries, EGL, and GLES). You can build EmulationStation by simply running `make`. - - -**On something else (desktop):** - -EmulationStation can also be built on a "normal" Linux system. You'll need the same libraries listed above: -``` -sudo apt-get install libsdl1.2-dev libboost-system-dev libboost-filesystem-dev libfreeimage-dev libfreetype6-dev ttf-dejavu -``` - -You'll also need OpenGL. I you don't have `/usr/include/GL/gl.h` and `libGL` try installing the MESA development package with: +On "desktop" Linux (that is, *not* the Raspberry Pi), you'll also need OpenGL. Try installing the MESA development package with: ``` sudo apt-get libgl1-mesa-dev ``` -You can build with `make -f Makefile.x86` (badly named Makefile, I know). For some reason it doesn't seem to run with X closed on desktop. +On the Raspberry Pi, there are also a few special libraries, located in /opt/vc/: the Broadcom libraries, libEGL, and GLES. You shouldn't need to install them. - -**Via CMake:** -
+**Generate and Build Makefile with CMake:**
+```
 cd EmulationStation
 cmake .
 make
-
+``` + +**On Windows:** + +[Boost](http://www.boost.org/users/download/) (you'll need to compile for Boost.Filesystem) + +[FreeImage](http://downloads.sourceforge.net/freeimage/FreeImage3154Win32.zip) + +[FreeType2](http://download.savannah.gnu.org/releases/freetype/freetype-2.4.9.tar.bz2) (you'll need to compile) + +[SDL-1.2](http://www.libsdl.org/release/SDL-devel-1.2.15-VC.zip) + +(remember to copy necessary .DLLs into the same folder as the executable: FreeImage.dll, freetype6.dll, SDL.dll, and zlib1.dll) + +[CMake](http://www.cmake.org/cmake/resources/software.html) (this is used for generating the Visual Studio project) + +(If you don't know how to use CMake, here are some hints: run CMake-gui and point it at your EmulationStation folder. Point the "build" directory somewhere - I use EmulationStation/build. Click configure, choose "Visual Studio 2010 Project", fill in red fields as they appear, then click Generate.) + Configuring =========== @@ -97,10 +107,12 @@ The COMMAND is the shell command ES will execute to start your emulator. As it i The following "tags" are replaced by ES in COMMANDs: -`%ROM%` - Replaced with absolute path to the selected ROM. +`%ROM%` - Replaced with absolute path to the selected ROM, with most Bash special characters escaped with a backslash. `%BASENAME%` - Replaced with the "base" name of the path to the selected ROM. For example, a path of "/foo/bar.rom", this tag would be "bar". This tag is useful for setting up AdvanceMAME. +`%ROM_RAW%` - Replaced with the unescaped absolute path to the selected ROM. If your emulator is picky about paths, you might want to use this instead of %ROM%, but enclosed in quotes. + gamelist.xml ============ diff --git a/src/AudioManager.cpp b/src/AudioManager.cpp index 9f68dde8d..47887ede7 100644 --- a/src/AudioManager.cpp +++ b/src/AudioManager.cpp @@ -23,7 +23,7 @@ void AudioManager::mixAudio(void *unused, Uint8 *stream, int len) { //calculate rest length of current sample Uint32 restLength = (sound->getLength() - sound->getPosition()); - if (restLength > len) { + if (restLength > (Uint32)len) { //if stream length is smaller than smaple lenght, clip it restLength = len; } diff --git a/src/EmulationStation.h b/src/EmulationStation.h index 9420daa8c..5c5f8640b 100644 --- a/src/EmulationStation.h +++ b/src/EmulationStation.h @@ -4,9 +4,9 @@ // Do this version number update as the very last commit for the new release version. #define PROGRAM_VERSION_MAJOR 1 #define PROGRAM_VERSION_MINOR 0 -#define PROGRAM_VERSION_MAINTENANCE 0 +#define PROGRAM_VERSION_MAINTENANCE 1 #define PROGRAM_VERSION_REVISION 0 -#define PROGRAM_VERSION_STRING "1.0.0.0 - built " __DATE__ " - " __TIME__ -#define RESOURCE_VERSION_STRING "1,0,0,0\0" +#define PROGRAM_VERSION_STRING "1.0.1.0 - built " __DATE__ " - " __TIME__ +#define RESOURCE_VERSION_STRING "1,0,1,0\0" #define RESOURCE_VERSION PROGRAM_VERSION_MAJOR,PROGRAM_VERSION_MINOR,PROGRAM_VERSION_MAINTENANCE,PROGRAM_VERSION_REVISION diff --git a/src/FolderData.cpp b/src/FolderData.cpp index 8d0d43596..2bc937cca 100644 --- a/src/FolderData.cpp +++ b/src/FolderData.cpp @@ -37,7 +37,9 @@ bool filesort(FileData* file1, FileData* file2) std::string name1 = file1->getName(); std::string name2 = file2->getName(); - for(unsigned int i = 0; i < name1.length(); i++) + //min of name1/name2 .length()s + unsigned int count = name1.length() > name2.length() ? name2.length() : name1.length(); + for(unsigned int i = 0; i < count; i++) { if(toupper(name1[i]) != toupper(name2[i])) { diff --git a/src/Font.cpp b/src/Font.cpp index 02eff9d3b..f085c7586 100644 --- a/src/Font.cpp +++ b/src/Font.cpp @@ -5,6 +5,7 @@ #include "Renderer.h" #include #include "Log.h" +#include "Vector2.h" FT_Library Font::sLibrary; bool Font::libraryInitialized = false; @@ -199,7 +200,7 @@ void Font::buildAtlas() if((y + maxHeight) >= textureHeight) { //failed to create a proper font texture - LOG(LogError) << "Error - font with size " << mSize << " exceeded texture size! Trying again..."; + LOG(LogWarning) << "Font with size " << mSize << " exceeded max texture size! Trying again..."; //try a 3/4th smaller size and redo initialization fontScale *= 1.25f; mSize = (int)(mSize * (1.0f / fontScale)); @@ -207,10 +208,8 @@ void Font::buildAtlas() init(); } else { - LOG(LogInfo) << "Created font with size " << mSize << std::endl; + LOG(LogInfo) << "Created font with size " << mSize << "."; } - - //std::cout << "generated texture \"" << textureID << "\" (w: " << w << " h: " << h << ")" << std::endl; } Font::~Font() @@ -220,33 +219,10 @@ Font::~Font() } - -//why these aren't in an array: -//openGL reads these in the order they are in memory -//if I use an array, it will be 4 x values then 4 y values -//it'll read XX, XX, YY instead of XY, XY, XY -//... -//that was the thinking at the time and honestly an array would have been smarter wow I'm dumb -struct point { - GLfloat pos0x; - GLfloat pos0y; - - GLfloat pos1x; - GLfloat pos1y; - - GLfloat pos2x; - GLfloat pos2y; -}; - -struct tex { - GLfloat tex0x; - GLfloat tex0y; - - GLfloat tex1x; - GLfloat tex1y; - - GLfloat tex2x; - GLfloat tex2y; +struct Vertex +{ + Vector2 pos; + Vector2 tex; }; void Font::drawText(std::string text, int startx, int starty, int color) @@ -257,10 +233,9 @@ void Font::drawText(std::string text, int startx, int starty, int color) return; } - int pointCount = text.length() * 2; - point* points = new point[pointCount]; - tex* texs = new tex[pointCount]; - GLubyte* colors = new GLubyte[pointCount * 3 * 4]; + const int triCount = text.length() * 2; + Vertex* vert = new Vertex[triCount * 3]; + GLubyte* colors = new GLubyte[triCount * 3 * 4]; glBindTexture(GL_TEXTURE_2D, textureID); glEnable(GL_TEXTURE_2D); @@ -274,58 +249,51 @@ void Font::drawText(std::string text, int startx, int starty, int color) float x = (float)startx; float y = starty + mMaxGlyphHeight * 1.1f * fontScale; //padding (another 0.5% is added to the bottom through the sizeText function) - int p = 0; - for(int i = 0; p < pointCount; i++, p++) + int charNum = 0; + for(int i = 0; i < triCount * 3; i += 6, charNum++) { - unsigned char letter = text[i]; + unsigned char letter = text[charNum]; if(letter < 32 || letter >= 128) letter = 127; //print [X] if character is not standard ASCII - points[p].pos0x = x; - points[p].pos0y = y + (charData[letter].texH - charData[letter].bearingY) * fontScale; - points[p].pos1x = x + charData[letter].texW * fontScale; - points[p].pos1y = y - charData[letter].bearingY * fontScale; - points[p].pos2x = x; - points[p].pos2y = points[p].pos1y; + //order is bottom left, top right, top left + vert[i + 0].pos = Vector2(x, y + (charData[letter].texH - charData[letter].bearingY) * fontScale); + vert[i + 1].pos = Vector2(x + charData[letter].texW * fontScale, y - charData[letter].bearingY * fontScale); + vert[i + 2].pos = Vector2(x, vert[i + 1].pos.y); - texs[p].tex0x = charData[letter].texX / tw; - texs[p].tex0y = (charData[letter].texY + charData[letter].texH) / th; - texs[p].tex1x = (charData[letter].texX + charData[letter].texW) / tw; - texs[p].tex1y = charData[letter].texY / th; - texs[p].tex2x = texs[p].tex0x; - texs[p].tex2y = texs[p].tex1y; + Vector2 charTexCoord(charData[letter].texX, charData[letter].texY); + Vector2 charTexSize(charData[letter].texW, charData[letter].texH); - p++; + vert[i + 0].tex = Vector2(charTexCoord.x / tw, (charTexCoord.y + charTexSize.y) / th); + vert[i + 1].tex = Vector2((charTexCoord.x + charTexSize.x) / tw, charTexCoord.y / th); + vert[i + 2].tex = Vector2(vert[i + 0].tex.x, vert[i + 1].tex.y); - points[p].pos0x = points[p-1].pos0x; - points[p].pos0y = points[p-1].pos0y; - points[p].pos1x = points[p-1].pos1x; - points[p].pos1y = points[p-1].pos1y; - points[p].pos2x = points[p-1].pos1x; - points[p].pos2y = points[p-1].pos0y; + //next triangle (second half of the quad) + vert[i + 3].pos = vert[i + 0].pos; + vert[i + 4].pos = vert[i + 1].pos; + vert[i + 5].pos.x = vert[i + 1].pos.x; + vert[i + 5].pos.y = vert[i + 0].pos.y; - texs[p].tex0x = texs[p-1].tex0x; - texs[p].tex0y = texs[p-1].tex0y; - texs[p].tex1x = texs[p-1].tex1x; - texs[p].tex1y = texs[p-1].tex1y; - texs[p].tex2x = texs[p-1].tex1x; - texs[p].tex2y = texs[p-1].tex0y; + vert[i + 3].tex = vert[i + 0].tex; + vert[i + 4].tex = vert[i + 1].tex; + vert[i + 5].tex.x = vert[i + 1].tex.x; + vert[i + 5].tex.y = vert[i + 0].tex.y; x += charData[letter].advX * fontScale; } - Renderer::buildGLColorArray(colors, color, pointCount * 3); + Renderer::buildGLColorArray(colors, color, triCount * 3); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnableClientState(GL_COLOR_ARRAY); - glVertexPointer(2, GL_FLOAT, 0, points); - glTexCoordPointer(2, GL_FLOAT, 0, texs); + glVertexPointer(2, GL_FLOAT, sizeof(Vertex), &vert[0].pos); + glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &vert[0].tex); glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors); - glDrawArrays(GL_TRIANGLES, 0, pointCount * 3); + glDrawArrays(GL_TRIANGLES, 0, triCount * 3); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); @@ -334,14 +302,13 @@ void Font::drawText(std::string text, int startx, int starty, int color) glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); - delete[] points; - delete[] texs; + delete[] vert; delete[] colors; } void Font::sizeText(std::string text, int* w, int* h) { - int cwidth = 0; + float cwidth = 0.0f; for(unsigned int i = 0; i < text.length(); i++) { unsigned char letter = text[i]; @@ -352,10 +319,10 @@ void Font::sizeText(std::string text, int* w, int* h) } if(w != NULL) - *w = cwidth; + *w = (int)cwidth; if(h != NULL) - *h = (int)(mMaxGlyphHeight * 1.5f * fontScale); + *h = getHeight(); } int Font::getHeight() diff --git a/src/GameData.cpp b/src/GameData.cpp index b6d981fbd..bb656be01 100644 --- a/src/GameData.cpp +++ b/src/GameData.cpp @@ -22,6 +22,7 @@ std::string GameData::getBashPath() { //a quick and dirty way to insert a backslash before most characters that would mess up a bash path std::string path = mPath; + const char* invalidChars = " '\"\\!$^&*(){}[]?;<>"; for(unsigned int i = 0; i < path.length(); i++) { diff --git a/src/Gui.cpp b/src/Gui.cpp deleted file mode 100644 index b36053d3a..000000000 --- a/src/Gui.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "Gui.h" -#include "Window.h" - -Gui::Gui(Window* window) : mWindow(window) -{ -} - -Gui::~Gui() -{ - mWindow->removeGui(this); -} - -void Gui::setOffset(int x, int y) { mOffsetX = x; mOffsetY = y; } -void Gui::setOffsetX(int x) { mOffsetX = x; } -void Gui::setOffsetY(int y) { mOffsetY = y; } -int Gui::getOffsetX() { return mOffsetX; } -int Gui::getOffsetY() { return mOffsetY; } diff --git a/src/Gui.h b/src/Gui.h deleted file mode 100644 index d8ba72dd8..000000000 --- a/src/Gui.h +++ /dev/null @@ -1,33 +0,0 @@ -#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) { }; - virtual void update(int deltaTime) { }; - virtual void render() { }; - - virtual void init() { }; - virtual void deinit() { }; - - void setOffsetX(int x); - void setOffsetY(int y); - void setOffset(int x, int y); - int getOffsetX(); - int getOffsetY(); -protected: - int mOffsetX; - int mOffsetY; - - Window* mWindow; -}; - -#endif diff --git a/src/GuiComponent.cpp b/src/GuiComponent.cpp new file mode 100644 index 000000000..4a5ee0195 --- /dev/null +++ b/src/GuiComponent.cpp @@ -0,0 +1,158 @@ +#include "GuiComponent.h" +#include "Window.h" +#include "Log.h" +#include "Renderer.h" + +GuiComponent::GuiComponent(Window* window) : mWindow(window), mParent(NULL) +{ +} + +GuiComponent::~GuiComponent() +{ + mWindow->removeGui(this); + + if(mParent) + mParent->removeChild(this); + + for(unsigned int i = 0; i < getChildCount(); i++) + getChild(i)->setParent(NULL); +} + +bool GuiComponent::input(InputConfig* config, Input input) +{ + for(unsigned int i = 0; i < getChildCount(); i++) + { + if(getChild(i)->input(config, input)) + return true; + } + + return false; +} + +void GuiComponent::update(int deltaTime) +{ + for(unsigned int i = 0; i < getChildCount(); i++) + { + getChild(i)->update(deltaTime); + } +} + +void GuiComponent::render() +{ + Renderer::translate(mOffset); + + onRender(); + + Renderer::translate(-mOffset); +} + +void GuiComponent::onRender() +{ + for(unsigned int i = 0; i < getChildCount(); i++) + { + getChild(i)->render(); + } +} + +void GuiComponent::init() +{ + for(unsigned int i = 0; i < getChildCount(); i++) + { + getChild(i)->init(); + } +} + +void GuiComponent::deinit() +{ + for(unsigned int i = 0; i < getChildCount(); i++) + { + getChild(i)->deinit(); + } +} + +//Offset stuff. +Vector2i GuiComponent::getGlobalOffset() +{ + if(mParent) + return mParent->getGlobalOffset() + mOffset; + else + return mOffset; +} + +Vector2i GuiComponent::getOffset() +{ + return mOffset; +} + +void GuiComponent::setOffset(Vector2i offset) +{ + setOffset(offset.x, offset.y); + onOffsetChanged(); +} + +void GuiComponent::setOffset(int x, int y) +{ + mOffset.x = x; + mOffset.y = y; + onOffsetChanged(); +} + +Vector2u GuiComponent::getSize() +{ + return mSize; +} + +//Children stuff. +void GuiComponent::addChild(GuiComponent* cmp) +{ + mChildren.push_back(cmp); + + if(cmp->getParent()) + cmp->getParent()->removeChild(cmp); + + cmp->setParent(this); +} + +void GuiComponent::removeChild(GuiComponent* cmp) +{ + if(cmp->getParent() != this) + { + LOG(LogError) << "Tried to remove child from incorrect parent!"; + } + + cmp->setParent(NULL); + + for(auto i = mChildren.begin(); i != mChildren.end(); i++) + { + if(*i == cmp) + { + mChildren.erase(i); + return; + } + } +} + +void GuiComponent::clearChildren() +{ + mChildren.clear(); +} + +unsigned int GuiComponent::getChildCount() +{ + return mChildren.size(); +} + +GuiComponent* GuiComponent::getChild(unsigned int i) +{ + return mChildren.at(i); +} + +void GuiComponent::setParent(GuiComponent* parent) +{ + mParent = parent; +} + +GuiComponent* GuiComponent::getParent() +{ + return mParent; +} diff --git a/src/GuiComponent.h b/src/GuiComponent.h new file mode 100644 index 000000000..b7f36af64 --- /dev/null +++ b/src/GuiComponent.h @@ -0,0 +1,60 @@ +#ifndef _GUICOMPONENT_H_ +#define _GUICOMPONENT_H_ + +#include "InputConfig.h" +#include "Vector2.h" + +class Window; + +class GuiComponent +{ +public: + GuiComponent(Window* window); + virtual ~GuiComponent(); + + //Called when input is received. + //Return true if the input is consumed, false if it should continue to be passed to other children. + virtual bool input(InputConfig* config, Input input); + + //Called when time passes. Default implementation also calls update(deltaTime) on children - so you should probably call GuiComponent::update(deltaTime) at some point. + virtual void update(int deltaTime); + + //Called when it's time to render. Translates the OpenGL matrix, calls onRender() (which renders children), then un-translates the OpenGL matrix. + //You probably don't need to override this, and should use the protected method onRender. + virtual void render(); + + //Called when the Renderer initializes. Passes to children. + virtual void init(); + + //Called when the Renderer deinitializes. Passes to children. + virtual void deinit(); + + virtual Vector2i getGlobalOffset(); + Vector2i getOffset(); + void setOffset(Vector2i offset); + void setOffset(int x, int y); + virtual void onOffsetChanged() {}; + + Vector2u getSize(); + + void setParent(GuiComponent* parent); + GuiComponent* getParent(); + + void addChild(GuiComponent* cmp); + void removeChild(GuiComponent* cmp); + void clearChildren(); + unsigned int getChildCount(); + GuiComponent* getChild(unsigned int i); + +protected: + //Default implementation just renders children - you should probably always call GuiComponent::onRender at some point in your custom onRender. + virtual void onRender(); + + Window* mWindow; + GuiComponent* mParent; + Vector2i mOffset; + Vector2u mSize; + std::vector mChildren; +}; + +#endif diff --git a/src/InputConfig.cpp b/src/InputConfig.cpp index 2e6d8319b..0036e3d11 100644 --- a/src/InputConfig.cpp +++ b/src/InputConfig.cpp @@ -3,6 +3,7 @@ #include #include #include +#include "Log.h" //some util functions std::string inputTypeToString(InputType type) @@ -138,7 +139,7 @@ void InputConfig::loadFromXML(pugi::xml_node node, int playerNum) if(typeEnum == TYPE_COUNT) { - std::cout << "ERROR - input type \"" << type << "\" is invalid! Skipping input \"" << name << "\".\n"; + LOG(LogError) << "InputConfig load error - input of type \"" << type << "\" is invalid! Skipping input \"" << name << "\".\n"; continue; } @@ -146,7 +147,7 @@ void InputConfig::loadFromXML(pugi::xml_node node, int playerNum) int value = input.attribute("value").as_int(); if(value == 0) - std::cout << "WARNING: InputConfig value is 0 for " << type << " " << id << "!\n"; + LOG(LogWarning) << "WARNING: InputConfig value is 0 for " << type << " " << id << "!\n"; mNameMap[toLower(name)] = Input(mDeviceId, typeEnum, id, value, true); } @@ -167,6 +168,9 @@ void InputConfig::writeToXML(pugi::xml_node parent) typedef std::map::iterator it_type; for(it_type iterator = mNameMap.begin(); iterator != mNameMap.end(); iterator++) { + if(!iterator->second.configured) + continue; + pugi::xml_node input = cfg.append_child("input"); input.append_attribute("name") = iterator->first.c_str(); input.append_attribute("type") = inputTypeToString(iterator->second.type).c_str(); diff --git a/src/InputManager.cpp b/src/InputManager.cpp index 4f8f15394..5df74a951 100644 --- a/src/InputManager.cpp +++ b/src/InputManager.cpp @@ -72,7 +72,7 @@ std::vector InputManager::getInputDevices() const if (GetRawInputDeviceList(deviceList, &nrOfDevices, sizeof(RAWINPUTDEVICELIST)) != -1) { //loop through input devices - for (int i = 0; i < nrOfDevices; i++) + for (unsigned int i = 0; i < nrOfDevices; i++) { //get device name char * rawName = new char[2048]; diff --git a/src/Renderer.h b/src/Renderer.h index 7cbce0b39..f70b8c83b 100644 --- a/src/Renderer.h +++ b/src/Renderer.h @@ -3,6 +3,7 @@ #include #include +#include "Vector2.h" #include "platform.h" #include GLHEADER //#include "Font.h" @@ -28,8 +29,16 @@ namespace Renderer Font* getDefaultFont(FontSize size); void buildGLColorArray(GLubyte* ptr, unsigned int color, unsigned int vertCount); - //drawing commands + //graphics commands void swapBuffers(); + + void translatef(float x, float y); + void translate(Vector2i offset); + + void pushClipRect(int x, int y, unsigned int w, unsigned int h); + void pushClipRect(Vector2i offset, Vector2u size); + void popClipRect(); + void drawRect(int x, int y, int w, int h, unsigned int color); void drawText(std::string text, int x, int y, unsigned int color, Font* font); void drawCenteredText(std::string text, int xOffset, int y, unsigned int color, Font* font); diff --git a/src/Renderer_draw_gl.cpp b/src/Renderer_draw_gl.cpp index ee2058d30..22dcf4c1e 100644 --- a/src/Renderer_draw_gl.cpp +++ b/src/Renderer_draw_gl.cpp @@ -5,10 +5,13 @@ #include "Font.h" #include #include "Log.h" +#include namespace Renderer { bool loadedFonts = false; + std::stack clipStack; + void setColor4bArray(GLubyte* array, unsigned int color) { array[0] = (color & 0xff000000) / 0x1000000; @@ -26,6 +29,56 @@ namespace Renderer { } } + void translatef(float x, float y) + { + glTranslatef(x, y, 0); + } + + void translate(Vector2i offset) + { + translatef((float)offset.x, (float)offset.y); + } + + void pushClipRect(int x, int y, unsigned int w, unsigned int h) + { + Rect rect(x, y, w, h); + if(rect.size.x == 0) + rect.size.x = Renderer::getScreenWidth() - rect.pos.x; + if(rect.size.y == 0) + rect.size.y = Renderer::getScreenHeight() - rect.pos.y; + + //glScissor starts at the bottom left of the window + //so (0, 0, 1, 1) is the bottom left pixel + //everything else uses y+ = down, so flip it to be consistent + rect.pos.y = Renderer::getScreenHeight() - rect.pos.y - rect.size.y; + + clipStack.push(rect); + glScissor(rect.pos.x, rect.pos.y, rect.size.x, rect.size.y); + glEnable(GL_SCISSOR_TEST); + } + + void pushClipRect(Vector2i pos, Vector2u size) + { + pushClipRect(pos.x, pos.y, size.x, size.y); + } + + void popClipRect() + { + if(clipStack.empty()) + { + LOG(LogError) << "Tried to popClipRect while the stack was empty!"; + return; + } + clipStack.pop(); + if(clipStack.empty()) + { + glDisable(GL_SCISSOR_TEST); + }else{ + Rect top = clipStack.top(); + glScissor(top.pos.x, top.pos.y, top.size.x, top.size.y); + } + } + void drawRect(int x, int y, int w, int h, unsigned int color) { #ifdef USE_OPENGL_ES diff --git a/src/Renderer_init_sdlgl.cpp b/src/Renderer_init_sdlgl.cpp index 3a464fd08..9659e3b0a 100644 --- a/src/Renderer_init_sdlgl.cpp +++ b/src/Renderer_init_sdlgl.cpp @@ -15,8 +15,7 @@ #include "ImageIO.h" #include "../data/Resources.h" #include "EmulationStation.h" - -extern bool WINDOWED; +#include "Settings.h" namespace Renderer { @@ -71,7 +70,8 @@ namespace Renderer SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - sdlScreen = SDL_SetVideoMode(display_width, display_height, 16, SDL_OPENGL | (WINDOWED ? 0 : SDL_FULLSCREEN) | SDL_DOUBLEBUF); + //SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1); //vsync + sdlScreen = SDL_SetVideoMode(display_width, display_height, 16, SDL_OPENGL | (Settings::getInstance()->getBool("WINDOWED") ? 0 : SDL_FULLSCREEN)); if(sdlScreen == NULL) { @@ -88,7 +88,7 @@ namespace Renderer LOG(LogInfo) << "Created surface successfully."; //hide mouse cursor - initialCursorState = SDL_ShowCursor(0); + initialCursorState = SDL_ShowCursor(0) == 1; return true; } diff --git a/src/Settings.cpp b/src/Settings.cpp new file mode 100644 index 000000000..52111668d --- /dev/null +++ b/src/Settings.cpp @@ -0,0 +1,101 @@ +#include "Settings.h" +#include "Log.h" +#include "pugiXML/pugixml.hpp" +#include "platform.h" +#include + +Settings* Settings::sInstance = NULL; + +Settings::Settings() +{ + setDefaults(); + loadFile(); +} + +Settings* Settings::getInstance() +{ + if(sInstance == NULL) + sInstance = new Settings(); + + return sInstance; +} + +void Settings::setDefaults() +{ + mBoolMap.clear(); + mIntMap.clear(); + + mBoolMap["PARSEGAMELISTONLY"] = false; + mBoolMap["IGNOREGAMELIST"] = false; + mBoolMap["DRAWFRAMERATE"] = false; + mBoolMap["DONTSHOWEXIT"] = false; + mBoolMap["DEBUG"] = false; + mBoolMap["WINDOWED"] = false; + + mIntMap["DIMTIME"] = 30*1000; +} + +template +void saveMap(pugi::xml_document& doc, std::map& map, const char* type) +{ + for(auto iter = map.begin(); iter != map.end(); iter++) + { + pugi::xml_node node = doc.append_child(type); + node.append_attribute("name").set_value(iter->first.c_str()); + node.append_attribute("value").set_value(iter->second); + } +} + +void Settings::saveFile() +{ + const std::string path = getHomePath() + "/.emulationstation/es_settings.cfg"; + + pugi::xml_document doc; + + saveMap(doc, mBoolMap, "bool"); + saveMap(doc, mIntMap, "int"); + saveMap(doc, mFloatMap, "float"); + + doc.save_file(path.c_str()); +} + +void Settings::loadFile() +{ + const std::string path = getHomePath() + "/.emulationstation/es_settings.cfg"; + + if(!boost::filesystem::exists(path)) + return; + + pugi::xml_document doc; + pugi::xml_parse_result result = doc.load_file(path.c_str()); + if(!result) + { + LOG(LogError) << "Could not parse Settings file!\n " << result.description(); + return; + } + + for(pugi::xml_node node = doc.child("bool"); node; node = node.next_sibling()) + setBool(node.attribute("name").as_string(), node.attribute("value").as_bool()); + for(pugi::xml_node node = doc.child("int"); node; node = node.next_sibling()) + setInt(node.attribute("name").as_string(), node.attribute("value").as_int()); + for(pugi::xml_node node = doc.child("float"); node; node = node.next_sibling()) + setFloat(node.attribute("name").as_string(), node.attribute("value").as_float()); +} + +//Print a warning message if the setting we're trying to get doesn't already exist in the map, then return the value in the map. +#define SETTINGS_GETSET(type, mapName, getMethodName, setMethodName) type Settings::getMethodName(const std::string& name) \ +{ \ + if(mapName.find(name) == mapName.end()) \ + { \ + LOG(LogError) << "Tried to use unset setting " << name << "!"; \ + } \ + return mapName[name]; \ +} \ +void Settings::setMethodName(const std::string& name, type value) \ +{ \ + mapName[name] = value; \ +} + +SETTINGS_GETSET(bool, mBoolMap, getBool, setBool); +SETTINGS_GETSET(int, mIntMap, getInt, setInt); +SETTINGS_GETSET(float, mFloatMap, getFloat, setFloat); diff --git a/src/Settings.h b/src/Settings.h new file mode 100644 index 000000000..099a0afa7 --- /dev/null +++ b/src/Settings.h @@ -0,0 +1,38 @@ +#ifndef _SETTINGS_H_ +#define _SETTINGS_H_ + +#include +#include + +//This is a singleton for storing settings. +class Settings +{ +public: + static Settings* getInstance(); + + void loadFile(); + void saveFile(); + + //You will get a warning if you try a get on a key that is not already present. + bool getBool(const std::string& name); + int getInt(const std::string& name); + float getFloat(const std::string& name); + + void setBool(const std::string& name, bool value); + void setInt(const std::string& name, int value); + void setFloat(const std::string& name, float value); + +private: + static Settings* sInstance; + + Settings(); + + //Clear everything and load default values. + void setDefaults(); + + std::map mBoolMap; + std::map mIntMap; + std::map mFloatMap; +}; + +#endif diff --git a/src/SystemData.cpp b/src/SystemData.cpp index 9eb4e0866..2e983e5e4 100644 --- a/src/SystemData.cpp +++ b/src/SystemData.cpp @@ -11,14 +11,12 @@ #include "Log.h" #include "InputManager.h" #include +#include "Settings.h" std::vector SystemData::sSystemVector; namespace fs = boost::filesystem; -extern bool PARSEGAMELISTONLY; -extern bool IGNOREGAMELIST; - std::string SystemData::getStartPath() { return mStartPath; } std::string SystemData::getExtension() { return mSearchExtension; } @@ -41,10 +39,10 @@ SystemData::SystemData(std::string name, std::string descName, std::string start mRootFolder = new FolderData(this, mStartPath, "Search Root"); - if(!PARSEGAMELISTONLY) + if(!Settings::getInstance()->getBool("PARSEGAMELISTONLY")) populateFolder(mRootFolder); - if(!IGNOREGAMELIST) + if(!Settings::getInstance()->getBool("IGNOREGAMELIST")) parseGamelist(this); mRootFolder->sort(); @@ -77,6 +75,7 @@ void SystemData::launchGame(Window* window, GameData* game) command = strreplace(command, "%ROM%", game->getBashPath()); command = strreplace(command, "%BASENAME%", game->getBaseName()); + command = strreplace(command, "%ROM_RAW%", game->getPath()); LOG(LogInfo) << " " << command; std::cout << "==============================================\n"; @@ -120,7 +119,37 @@ void SystemData::populateFolder(FolderData* folder) if(filePath.stem().string().empty()) continue; - if(fs::is_directory(filePath)) + //this is a little complicated because we allow a list of extensions to be defined (delimited with a space) + //we first get the extension of the file itself: + std::string extension = filePath.extension().string(); + std::string chkExt; + size_t extPos = 0; + + //folders *can* also match the extension and be added as games - this is mostly just to support higan + //see issue #75: https://github.com/Aloshi/EmulationStation/issues/75 + bool isGame = false; + do { + //now we loop through every extension in the list + size_t cpos = extPos; + extPos = mSearchExtension.find(" ", extPos); + chkExt = mSearchExtension.substr(cpos, ((extPos == std::string::npos) ? mSearchExtension.length() - cpos: extPos - cpos)); + + //if it matches, add it + if(chkExt == extension) + { + GameData* newGame = new GameData(this, filePath.string(), filePath.stem().string()); + folder->pushFileData(newGame); + isGame = true; + break; + }else if(extPos != std::string::npos) //if not, add one to the "next position" marker to skip the space when reading the next extension + { + extPos++; + } + + } while(extPos != std::string::npos && chkExt != "" && chkExt.find(".") != std::string::npos); + + //add directories that also do not match an extension as folders + if(!isGame && fs::is_directory(filePath)) { FolderData* newFolder = new FolderData(this, filePath.string(), filePath.stem().string()); populateFolder(newFolder); @@ -130,31 +159,6 @@ void SystemData::populateFolder(FolderData* folder) delete newFolder; else folder->pushFileData(newFolder); - }else{ - //this is a little complicated because we allow a list of extensions to be defined (delimited with a space) - //we first get the extension of the file itself: - std::string extension = filePath.extension().string(); - std::string chkExt; - size_t extPos = 0; - - do { - //now we loop through every extension in the list - size_t cpos = extPos; - extPos = mSearchExtension.find(" ", extPos); - chkExt = mSearchExtension.substr(cpos, ((extPos == std::string::npos) ? mSearchExtension.length() - cpos: extPos - cpos)); - - //if it matches, add it - if(chkExt == extension) - { - GameData* newGame = new GameData(this, filePath.string(), filePath.stem().string()); - folder->pushFileData(newGame); - break; - }else if(extPos != std::string::npos) //if not, add one to the "next position" marker to skip the space when reading the next extension - { - extPos++; - } - - } while(extPos != std::string::npos && chkExt != "" && chkExt.find(".") != std::string::npos); } } } @@ -316,8 +320,8 @@ FolderData* SystemData::getRootFolder() return mRootFolder; } -std::string SystemData::getGamelistPath(){ - +std::string SystemData::getGamelistPath() +{ std::string filePath; filePath = mRootFolder->getPath() + "/gamelist.xml"; diff --git a/src/Vector2.h b/src/Vector2.h new file mode 100644 index 000000000..672c8ab3e --- /dev/null +++ b/src/Vector2.h @@ -0,0 +1,107 @@ +#ifndef _VECTOR2_H_ +#define _VECTOR2_H_ + +//Taken from the SFML Vector2 class: +//https://github.com/LaurentGomila/SFML/blob/master/include/SFML/System/Vector2.hpp +//https://github.com/LaurentGomila/SFML/blob/master/include/SFML/System/Vector2.inl + +template +class Vector2 +{ +public: + Vector2() : x(0), y(0) + { + } + + Vector2(T X, T Y) : x(X), y(Y) + { + } + + //convert between vector types + template + explicit Vector2(const Vector2& vector) : x(static_cast(vector.x)), y(static_cast(vector.y)) + { + } + + T x; + T y; +}; + +template +Vector2 operator -(const Vector2& right) +{ + return Vector2(-right.x, -right.y); +} + +template +Vector2& operator +=(Vector2& left, const Vector2& right) +{ + left.x += right.x; + left.y += right.y; + + return left; +} + +template +Vector2& operator -=(Vector2& left, const Vector2& right) +{ + left.x -= right.x; + left.y -= right.y; + + return left; +} + +template +Vector2 operator +(const Vector2& left, const Vector2& right) +{ + return Vector2(left.x + right.x, left.y + right.y); +} + +template +Vector2 operator -(const Vector2& left, const Vector2& right) +{ + return Vector2(left.x - right.x, left.y - right.y); +} + +template +Vector2 operator *(const Vector2& left, T right) +{ + return Vector2(left.x * right, left.y * right); +} + +template +Vector2& operator *=(Vector2& left, T right) +{ + left.x *= right; + left.y *= right; + + return left; +} + +template +bool operator ==(const Vector2& left, const Vector2& right) +{ + return (left.x == right.x && left.y == right.y); +} + +template +bool operator !=(const Vector2& left, const Vector2& right) +{ + return (left.x != right.x) || (left.y != right.y); +} + +typedef Vector2 Vector2i; +typedef Vector2 Vector2u; +typedef Vector2 Vector2f; + +class Rect +{ +public: + Vector2i pos; + Vector2u size; + + Rect() {}; + Rect(int x, int y, unsigned int w, unsigned int h) : pos(x, y), size(w, h) {}; +}; + +#endif diff --git a/src/Window.cpp b/src/Window.cpp index 81ce134de..340cd09a3 100644 --- a/src/Window.cpp +++ b/src/Window.cpp @@ -14,24 +14,24 @@ Window::~Window() delete mInputManager; } -void Window::pushGui(Gui* gui) +void Window::pushGui(GuiComponent* gui) { mGuiStack.push_back(gui); } -void Window::removeGui(Gui* gui) +void Window::removeGui(GuiComponent* gui) { - for(unsigned int i = 0; i < mGuiStack.size(); i++) + for(auto i = mGuiStack.begin(); i != mGuiStack.end(); i++) { - if(mGuiStack.at(i) == gui) + if(*i == gui) { - mGuiStack.erase(mGuiStack.begin() + i); - break; + mGuiStack.erase(i); + return; } } } -Gui* Window::peekGui() +GuiComponent* Window::peekGui() { if(mGuiStack.size() == 0) return NULL; @@ -41,6 +41,7 @@ Gui* Window::peekGui() void Window::render() { + //there's nothing to render, which should pretty much never happen if(mGuiStack.size() == 0) std::cout << "guistack empty\n"; diff --git a/src/Window.h b/src/Window.h index cbabce33f..6319bb68c 100644 --- a/src/Window.h +++ b/src/Window.h @@ -1,7 +1,7 @@ #ifndef _WINDOW_H_ #define _WINDOW_H_ -#include "Gui.h" +#include "GuiComponent.h" #include "InputManager.h" #include @@ -11,9 +11,9 @@ public: Window(); ~Window(); - void pushGui(Gui* gui); - void removeGui(Gui* gui); - Gui* peekGui(); + void pushGui(GuiComponent* gui); + void removeGui(GuiComponent* gui); + GuiComponent* peekGui(); void input(InputConfig* config, Input input); void update(int deltaTime); @@ -26,7 +26,7 @@ public: private: InputManager* mInputManager; - std::vector mGuiStack; + std::vector mGuiStack; }; #endif diff --git a/src/XMLReader.cpp b/src/XMLReader.cpp index 3ccea64f0..d1e19b8ae 100644 --- a/src/XMLReader.cpp +++ b/src/XMLReader.cpp @@ -33,14 +33,12 @@ GameData* createGameFromPath(std::string gameAbsPath, SystemData* system) std::string sysPath = system->getStartPath(); //strip out the system path stuff so it's relative to the system root folder - for(unsigned int i = 0; i < gamePath.length(); i++) - { - if(gamePath[i] != sysPath[i]) - { - gamePath = gamePath.substr(i, gamePath.length() - i); - break; - } - } + unsigned int i = 0; + while(i < gamePath.length() && i < sysPath.length() && gamePath[i] == sysPath[i]) + i++; + + gamePath = gamePath.substr(i, gamePath.length() - i); + if(gamePath[0] != '/') gamePath.insert(0, "/"); diff --git a/src/components/AnimationComponent.cpp b/src/components/AnimationComponent.cpp new file mode 100644 index 000000000..31c4c327e --- /dev/null +++ b/src/components/AnimationComponent.cpp @@ -0,0 +1,100 @@ +#include "AnimationComponent.h" + +AnimationComponent::AnimationComponent() +{ + mMoveX = 0; + mMoveY = 0; + mMoveSpeed = 0; + mFadeRate = 0; + mOpacity = 0; + mAccumulator = 0; +} + +void AnimationComponent::move(int x, int y, int speed) +{ + mMoveX = x; + mMoveY = y; + mMoveSpeed = speed; +} + +void AnimationComponent::fadeIn(int time) +{ + mOpacity = 0; + setChildrenOpacity(0); + + mFadeRate = time; +} + +void AnimationComponent::fadeOut(int time) +{ + mOpacity = 255; + setChildrenOpacity(255); + + mFadeRate = -time; +} + +//this should really be fixed at the system loop level... +void AnimationComponent::update(int deltaTime) +{ + mAccumulator += deltaTime; + while(mAccumulator >= ANIMATION_TICK_SPEED) + { + mAccumulator -= ANIMATION_TICK_SPEED; + + if(mMoveX != 0 || mMoveY != 0) + { + Vector2i offset(mMoveX, mMoveY); + if(abs(offset.x) > mMoveSpeed) + offset.x = mMoveSpeed * (offset.x > 0 ? 1 : -1); + if(abs(offset.y) > mMoveSpeed) + offset.y = mMoveSpeed * (offset.y > 0 ? 1 : -1); + + moveChildren(offset.x, offset.y); + + mMoveX -= offset.x; + mMoveY -= offset.y; + } + + if(mFadeRate != 0) + { + int opacity = (int)mOpacity + mFadeRate; + if(opacity > 255) + { + mFadeRate = 0; + opacity = 255; + } + + if(opacity < 0) + { + mFadeRate = 0; + opacity = 0; + } + + mOpacity = (unsigned char)opacity; + setChildrenOpacity((unsigned char)opacity); + } + } +} + +void AnimationComponent::addChild(ImageComponent* gui) +{ + mChildren.push_back(gui); +} + +void AnimationComponent::moveChildren(int offsetx, int offsety) +{ + Vector2i move(offsetx, offsety); + for(unsigned int i = 0; i < mChildren.size(); i++) + { + ImageComponent* comp = mChildren.at(i); + comp->setOffset(comp->getOffset() + move); + } +} + +void AnimationComponent::setChildrenOpacity(unsigned char opacity) +{ + for(unsigned int i = 0; i < mChildren.size(); i++) + { + mChildren.at(i)->setOpacity(opacity); + } +} diff --git a/src/components/GuiAnimation.h b/src/components/AnimationComponent.h similarity index 53% rename from src/components/GuiAnimation.h rename to src/components/AnimationComponent.h index 767ca2ffc..99cbd35b7 100644 --- a/src/components/GuiAnimation.h +++ b/src/components/AnimationComponent.h @@ -1,14 +1,16 @@ -#ifndef _GUIANIMATION_H_ -#define _GUIANIMATION_H_ +#ifndef _ANIMATIONCOMPONENT_H_ +#define _ANIMATIONCOMPONENT_H_ -#include "../Gui.h" -#include "GuiImage.h" +#include "../GuiComponent.h" +#include "ImageComponent.h" #include -class GuiAnimation +#define ANIMATION_TICK_SPEED 16 + +class AnimationComponent { public: - GuiAnimation(); + AnimationComponent(); void move(int x, int y, int speed); void fadeIn(int time); @@ -16,18 +18,20 @@ public: void update(int deltaTime); - void addChild(GuiImage* gui); + void addChild(ImageComponent* gui); private: unsigned char mOpacity; - std::vector mChildren; + std::vector mChildren; void moveChildren(int offsetx, int offsety); void setChildrenOpacity(unsigned char opacity); int mFadeRate; int mMoveX, mMoveY, mMoveSpeed; + + int mAccumulator; }; #endif diff --git a/src/components/ComponentListComponent.cpp b/src/components/ComponentListComponent.cpp new file mode 100644 index 000000000..0e6af1060 --- /dev/null +++ b/src/components/ComponentListComponent.cpp @@ -0,0 +1,319 @@ +#include "ComponentListComponent.h" +#include "../Log.h" +#include "../Renderer.h" + +#define INITIAL_CELL_SIZE 12 + +ComponentListComponent::ComponentListComponent(Window* window, Vector2u gridDimensions) : GuiComponent(window), mGrid(NULL), mColumnWidths(NULL), mRowHeights(NULL) +{ + mEntries.reserve(gridDimensions.x*gridDimensions.y); + makeCells(gridDimensions); +} + +void ComponentListComponent::makeCells(Vector2u size) +{ + if(mGrid) + delete[] mGrid; + if(mColumnWidths) + delete[] mColumnWidths; + if(mRowHeights) + delete[] mRowHeights; + + mGridSize = size; + mGrid = new ComponentEntry*[size.x * size.y]; + std::fill(mGrid, mGrid + (size.x * size.y), (ComponentEntry*)NULL); + + mColumnWidths = new unsigned int[size.x]; + std::fill(mColumnWidths, mColumnWidths + size.x, INITIAL_CELL_SIZE); + + mRowHeights = new unsigned int[size.y]; + std::fill(mRowHeights, mRowHeights + size.y, INITIAL_CELL_SIZE); + + updateSize(); + resetCursor(); +} + +void ComponentListComponent::setEntry(Vector2u pos, Vector2u size, GuiComponent* component, bool canFocus, AlignmentType align, + Vector2 autoFit, UpdateBehavior updateType) +{ + if(pos.x > mGridSize.x || pos.y > mGridSize.y) + { + LOG(LogError) << "Tried to set entry beyond grid size!"; + return; + } + + if(component == NULL) + { + LOG(LogError) << "Tried to add NULL component to ComponentList!"; + return; + } + + ComponentEntry entry(Rect(pos.x, pos.y, size.x, size.y), component, updateType, canFocus, align); + + mEntries.push_back(entry); + + for(unsigned int y = pos.y; y < pos.y + size.y; y++) + { + for(unsigned int x = pos.x; x < pos.x + size.x; x++) + { + setCell(x, y, &mEntries.back()); + } + } + + if(component->getParent() != NULL) + LOG(LogError) << "ComponentListComponent ruining an existing parent-child relationship! Call a social worker!"; + component->setParent(this); + + if(!cursorValid() && canFocus) + mCursor = (Vector2i)pos; + + //update the column width and row height + if(autoFit.x && getColumnWidth(pos.x) < component->getSize().x) + setColumnWidth(pos.x, component->getSize().x); + if(autoFit.y && getRowHeight(pos.y) < component->getSize().y) + setRowHeight(pos.y, component->getSize().y); + + component->setOffset(getCellOffset(pos)); +} + +void ComponentListComponent::setRowHeight(int row, unsigned int size) +{ + mRowHeights[row] = size; + updateSize(); +} + +void ComponentListComponent::setColumnWidth(int col, unsigned int size) +{ + mColumnWidths[col] = size; + updateSize(); +} + +unsigned int ComponentListComponent::getRowHeight(int row) { return mRowHeights[row]; } +unsigned int ComponentListComponent::getColumnWidth(int col) { return mColumnWidths[col]; } + +Vector2i ComponentListComponent::getCellOffset(Vector2u pos) +{ + Vector2i offset; + + for(unsigned int y = 0; y < pos.y; y++) + offset.y += getRowHeight(y); + + for(unsigned int x = 0; x < pos.x; x++) + offset.x += getColumnWidth(x); + + ComponentEntry* entry = getCell(pos.x, pos.y); + + Vector2u gridSize; + for(unsigned int x = pos.x; x < pos.x + entry->box.size.x; x++) + gridSize.x += getColumnWidth(x); + for(unsigned int y = pos.y; y < pos.y + entry->box.size.y; y++) + gridSize.y += getRowHeight(y); + + //if AlignCenter, add half of cell width - half of control width + if(entry->alignment == AlignCenter) + offset.x += gridSize.x / 2 - entry->component->getSize().x / 2; + + //if AlignRight, add cell width - control width + if(entry->alignment == AlignRight) + offset.x += gridSize.x - entry->component->getSize().x; + + //always center on the Y axis + offset.y += gridSize.y / 2 - entry->component->getSize().y / 2; + + return offset; +} + +void ComponentListComponent::setCell(unsigned int x, unsigned int y, ComponentEntry* entry) +{ + if(x < 0 || y < 0 || x >= mGridSize.x || y >= mGridSize.y) + { + LOG(LogError) << "Invalid setCell - position " << x << ", " << y << " out of bounds!"; + return; + } + + mGrid[y * mGridSize.x + x] = entry; +} + +ComponentListComponent::ComponentEntry* ComponentListComponent::getCell(unsigned int x, unsigned int y) +{ + if(x < 0 || y < 0 || x >= mGridSize.x || y >= mGridSize.y) + { + LOG(LogError) << "Invalid getCell - position " << x << ", " << y << " out of bounds!"; + return NULL; + } + + return mGrid[y * mGridSize.x + x]; +} + +void ComponentListComponent::updateSize() +{ + mSize = Vector2u(0, 0); + for(unsigned int x = 0; x < mGridSize.x; x++) + mSize.x += getColumnWidth(x); + for(unsigned int y = 0; y < mGridSize.y; y++) + mSize.y += getRowHeight(y); +} + +void ComponentListComponent::updateComponentOffsets() +{ + for(auto iter = mEntries.begin(); iter != mEntries.end(); iter++) + { + iter->component->setOffset(getCellOffset((Vector2u)iter->box.pos)); + } +} + +bool ComponentListComponent::input(InputConfig* config, Input input) +{ + if(cursorValid() && getCell(mCursor.x, mCursor.y)->component->input(config, input)) + return true; + + if(!input.value) + return false; + + if(config->isMappedTo("down", input)) + { + moveCursor(Vector2i(0, 1)); + return true; + } + if(config->isMappedTo("up", input)) + { + moveCursor(Vector2i(0, -1)); + return true; + } + + return false; +} + +void ComponentListComponent::resetCursor() +{ + if(mEntries.size() == 0) + { + mCursor = Vector2i(-1, -1); + return; + } + + mCursor = mEntries.at(0).box.pos; +} + +void ComponentListComponent::moveCursor(Vector2i dir) +{ + if(dir.x != 0 && dir.y != 0) + { + LOG(LogError) << "Invalid cursor move dir!"; + return; + } + + if(!cursorValid()) + { + resetCursor(); + if(!cursorValid()) + return; + } + + Vector2i origCursor = mCursor; + + Vector2i searchAxis(dir.x == 0, dir.y == 0); + + while(mCursor.x >= 0 && mCursor.y >= 0 && mCursor.x < (int)mGridSize.x && mCursor.y < (int)mGridSize.y) + { + mCursor = mCursor + dir; + + Vector2i curDirPos = mCursor; + + //spread out on search axis+ + while(mCursor.x < (int)mGridSize.x && mCursor.y < (int)mGridSize.y) + { + if(cursorValid() && getCell(mCursor.x, mCursor.y)->canFocus) + return; + + mCursor += searchAxis; + } + + //now again on search axis- + mCursor = curDirPos; + while(mCursor.x >= 0 && mCursor.y >= 0) + { + if(cursorValid() && getCell(mCursor.x, mCursor.y)->canFocus) + return; + + mCursor -= searchAxis; + } + } + + //failed to find another focusable element in this direction + mCursor = origCursor; +} + +bool ComponentListComponent::cursorValid() +{ + if(mCursor.x < 0 || mCursor.y < 0 || mCursor.x >= (int)mGridSize.x || mCursor.y >= (int)mGridSize.y) + return false; + + return getCell(mCursor.x, mCursor.y) != NULL; +} + +void ComponentListComponent::update(int deltaTime) +{ + for(auto iter = mEntries.begin(); iter != mEntries.end(); iter++) + { + if(iter->updateType == UpdateAlways) + { + iter->component->update(deltaTime); + continue; + } + + if(iter->updateType == UpdateFocused && cursorValid() && getCell(mCursor.x, mCursor.y)->component == iter->component) + { + iter->component->update(deltaTime); + continue; + } + } +} + +void ComponentListComponent::onRender() +{ + Renderer::drawRect(0, 0, getSize().x, getSize().y, 0xFFFFFFAA); + + for(auto iter = mEntries.begin(); iter != mEntries.end(); iter++) + { + iter->component->render(); + } + + //draw cell outlines + /*Vector2i pos; + for(unsigned int x = 0; x < mGridSize.x; x++) + { + for(unsigned int y = 0; y < mGridSize.y; y++) + { + Renderer::drawRect(pos.x, pos.y, getColumnWidth(x), 2, 0x000000AA); + Renderer::drawRect(pos.x, pos.y, 2, getRowHeight(y), 0x000000AA); + Renderer::drawRect(pos.x + getColumnWidth(x), pos.y, 2, getRowHeight(y), 0x000000AA); + Renderer::drawRect(pos.x, pos.y + getRowHeight(y) - 2, getColumnWidth(x), 2, 0x000000AA); + + pos.y += getRowHeight(y); + } + + pos.y = 0; + pos.x += getColumnWidth(x); + }*/ + + //draw cursor + if(cursorValid()) + { + ComponentEntry* entry = getCell(mCursor.x, mCursor.y); + Renderer::drawRect(entry->component->getOffset().x, entry->component->getOffset().y, 4, 4, 0xFF0000FF); + Renderer::drawRect(entry->component->getOffset().x, entry->component->getOffset().y, entry->component->getSize().x, entry->component->getSize().y, 0x0000AA88); + } +} + +void ComponentListComponent::onOffsetChanged() +{ + updateComponentOffsets(); +} + +GuiComponent* ComponentListComponent::getSelectedComponent() +{ + if(!cursorValid()) + return NULL; + return getCell(mCursor.x, mCursor.y)->component; +} diff --git a/src/components/ComponentListComponent.h b/src/components/ComponentListComponent.h new file mode 100644 index 000000000..2045ec53a --- /dev/null +++ b/src/components/ComponentListComponent.h @@ -0,0 +1,131 @@ +#pragma once + +#include "../GuiComponent.h" + +class ComponentListComponent : public GuiComponent +{ +public: + ComponentListComponent(Window* window, Vector2u gridDimensions); + + enum UpdateBehavior + { + UpdateAlways, UpdateFocused + }; + + enum AlignmentType + { + AlignLeft, AlignRight, AlignCenter + }; + + void setEntry(Vector2u pos, Vector2u size, GuiComponent* component, bool canFocus, AlignmentType align, Vector2 autoFit, UpdateBehavior updateType = UpdateAlways); + + void onOffsetChanged() override; + + bool input(InputConfig* config, Input input) override; + void update(int deltaTime) override; + void onRender() override; + + void setColumnWidth(int col, unsigned int size); + void setRowHeight(int row, unsigned int size); + + void resetCursor(); + bool cursorValid(); + + GuiComponent* getSelectedComponent(); + +private: + class ComponentEntry + { + public: + Rect box; + GuiComponent* component; + UpdateBehavior updateType; + AlignmentType alignment; + bool canFocus; + + ComponentEntry() : component(NULL), updateType(UpdateAlways), canFocus(true), alignment(AlignCenter) {}; + ComponentEntry(Rect b, GuiComponent* comp, UpdateBehavior update, bool focus, AlignmentType align) : box(b), component(comp), updateType(update), canFocus(focus), alignment(align) {}; + + operator bool() const + { + return component != NULL; + } + }; + + //Offset we render components by (for scrolling). + Vector2i mComponentOffset; + + Vector2u mGridSize; + ComponentEntry** mGrid; + std::vector mEntries; + void makeCells(Vector2u size); + void setCell(unsigned int x, unsigned int y, ComponentEntry* entry); + ComponentEntry* getCell(unsigned int x, unsigned int y); + + Vector2u mSelectedCellIndex; + + unsigned int getColumnWidth(int col); + unsigned int getRowHeight(int row); + + unsigned int* mColumnWidths; + unsigned int* mRowHeights; + + Vector2i getCellOffset(Vector2u gridPos); + void updateSize(); + + void moveCursor(Vector2i dir); + Vector2i mCursor; + + void updateComponentOffsets(); +}; + +//ability to define a list of components in terms of a grid + +//input +//pass to selected component +// if returns true, stop +// else, process: +// if input == up/down +// scroll to prev/next selectable component in grid Y +// if input == left/right +// scroll to prev/next selectable component in grid X +// if input == accept +// call registered function? + +//entry struct/class +// GuiComponent* component - component to work with +// bool canFocus - can we pass input to this? (necessary for labels to not be selectable) +// Function* selectFunc? +// UpdateBehavior update - how to handle updates (all the time or only when focused) + +//update +//animate component offset to display selected component within the bounds +//pass update to all entries with appropriate update behavior + +//render +//clip rect to our size +//render a "selected" effect behind component with focus somehow +// an edge filter would be cool, but we can't really do that without shader support +// a transparent rect will work for now, but it's kind of ugly...maybe a GuiBox +//glTranslatef by our render offset +// doesn't handle getGlobalOffset for our components...would need parenting for that + +//methods +//List::setEntry(Vector2i gridPos, GuiComponent* component, bool canFocus, AlignmentType align, +// Function* selectFunc = NULL, UpdateBehavior updateType = UpdateAlways); + +//example of setting up the SettingsMenu list: +//ComponentListComponent list; +//int row = 0; +//TextComponent* label = new TextComponent(Vector2i(0, 0), "Debug:", font, lblColor, etc); +// +//list.setEntry(Vector2i(-1, row), label, false, AlignRight); +//list.setEntry(Vector2i(0, row++), &mDebugSwitch, true, AlignLeft); +//... +//list.setEntry(Rect(-1, row, 2, 1), &mSaveButton, true, AlignCenter); + +//example of setting up GameGrid list: +//ComponentListComponent list; +//for(int y = 0; y < yMax; y++) +// for(int x = 0; x < xMax; x++) +// list.setEntry(Vector2i(x, y), getGameImage(x, y), true, AlignCenter, &this->onSelectGame); diff --git a/src/components/GuiAnimation.cpp b/src/components/GuiAnimation.cpp deleted file mode 100644 index 1353fb8bb..000000000 --- a/src/components/GuiAnimation.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#include "GuiAnimation.h" - -GuiAnimation::GuiAnimation() -{ - mMoveX = 0; - mMoveY = 0; - mMoveSpeed = 0; - mFadeRate = 0; -} - -void GuiAnimation::move(int x, int y, int speed) -{ - mMoveX = x; - mMoveY = y; - mMoveSpeed = speed; -} - -void GuiAnimation::fadeIn(int time) -{ - mOpacity = 0; - setChildrenOpacity(0); - - mFadeRate = time; -} - -void GuiAnimation::fadeOut(int time) -{ - mOpacity = 255; - setChildrenOpacity(255); - - mFadeRate = -time; -} - -void GuiAnimation::update(int deltaTime) -{ - float mult = deltaTime * 0.05f; - - if(mMoveX != 0 || mMoveY != 0) - { - int offsetx = (mMoveX > mMoveSpeed) ? mMoveSpeed : mMoveX; - int offsety = (mMoveY > mMoveSpeed) ? mMoveSpeed : mMoveY; - - offsetx = (int)(offsetx * mult); - offsety = (int)(offsety * mult); - - moveChildren(offsetx, offsety); - - mMoveX -= offsetx; - mMoveY -= offsety; - } - - if(mFadeRate != 0) - { - int opacity = (int)mOpacity + mFadeRate; - if(opacity > 255) - { - mFadeRate = 0; - opacity = 255; - } - - if(opacity < 0) - { - mFadeRate = 0; - opacity = 0; - } - - mOpacity = (unsigned char)opacity; - setChildrenOpacity((unsigned char)opacity); - } -} - -void GuiAnimation::addChild(GuiImage* gui) -{ - mChildren.push_back(gui); -} - -void GuiAnimation::moveChildren(int offsetx, int offsety) -{ - for(unsigned int i = 0; i < mChildren.size(); i++) - { - GuiImage* comp = mChildren.at(i); - comp->setOffset(comp->getOffsetX() + offsetx, comp->getOffsetY() + offsety); - } -} - -void GuiAnimation::setChildrenOpacity(unsigned char opacity) -{ - for(unsigned int i = 0; i < mChildren.size(); i++) - { - mChildren.at(i)->setOpacity(opacity); - } -} diff --git a/src/components/GuiBox.cpp b/src/components/GuiBox.cpp index f7abe8796..3db364d88 100644 --- a/src/components/GuiBox.cpp +++ b/src/components/GuiBox.cpp @@ -1,13 +1,11 @@ #include "GuiBox.h" -GuiBox::GuiBox(Window* window, int offsetX, int offsetY, unsigned int width, unsigned int height) : Gui(window), mBackgroundImage(window), +GuiBox::GuiBox(Window* window, int offsetX, int offsetY, unsigned int width, unsigned int height) : GuiComponent(window), mBackgroundImage(window), mHorizontalImage(window), mVerticalImage(window), mCornerImage(window) { - setOffsetX(offsetX); - setOffsetY(offsetY); - - mWidth = width; - mHeight = height; + setOffset(Vector2i(offsetX, offsetY)); + + mSize = Vector2u(width, height); } void GuiBox::setData(GuiBoxData data) @@ -24,7 +22,7 @@ void GuiBox::setHorizontalImage(std::string path, bool tiled) mHorizontalImage.setOrigin(0, 0); mHorizontalImage.setImage(path); - mHorizontalImage.setResize(mHorizontalImage.getHeight(), mHeight, true); + mHorizontalImage.setResize(getHorizontalBorderWidth(), mSize.y, true); } void GuiBox::setVerticalImage(std::string path, bool tiled) @@ -33,16 +31,15 @@ void GuiBox::setVerticalImage(std::string path, bool tiled) mVerticalImage.setOrigin(0, 0); mVerticalImage.setImage(path); - mVerticalImage.setResize(mWidth, mVerticalImage.getHeight(), true); + mVerticalImage.setResize(mSize.x, getVerticalBorderWidth(), true); } void GuiBox::setBackgroundImage(std::string path, bool tiled) { mBackgroundImage.setOrigin(0, 0); - mBackgroundImage.setResize(mWidth, mHeight, true); + mBackgroundImage.setResize(mSize.x, mSize.y, true); mBackgroundImage.setTiling(tiled); - mBackgroundImage.setOffsetX(getOffsetX()); - mBackgroundImage.setOffsetY(getOffsetY()); + mBackgroundImage.setOffset(0, 0); mBackgroundImage.setImage(path); } @@ -55,60 +52,53 @@ void GuiBox::setCornerImage(std::string path) mCornerImage.setImage(path); } -void GuiBox::render() +void GuiBox::onRender() { mBackgroundImage.render(); //left border - mHorizontalImage.setOffsetX(getOffsetX() - getHorizontalBorderWidth()); - mHorizontalImage.setOffsetY(getOffsetY()); + mHorizontalImage.setOffset(-getHorizontalBorderWidth(), 0); mHorizontalImage.setFlipX(false); mHorizontalImage.render(); - //Renderer::drawRect(getOffsetX() - getHorizontalBorderWidth(), getOffsetY(), getHorizontalBorderWidth(), mHeight, 0xFF0000); - + //right border - mHorizontalImage.setOffsetX(getOffsetX() + mWidth); - //same Y + mHorizontalImage.setOffset(mSize.x, 0); mHorizontalImage.setFlipX(true); mHorizontalImage.render(); - //Renderer::drawRect(getOffsetX() + mWidth, getOffsetY(), getHorizontalBorderWidth(), mHeight, 0xFF0000); - + //top border - mVerticalImage.setOffsetX(getOffsetX()); - mVerticalImage.setOffsetY(getOffsetY() - getVerticalBorderWidth()); + mVerticalImage.setOffset(0, -getVerticalBorderWidth()); mVerticalImage.setFlipY(false); mVerticalImage.render(); - //Renderer::drawRect(getOffsetX(), getOffsetY() - getVerticalBorderWidth(), mWidth, getVerticalBorderWidth(), 0x00FF00); - + //bottom border - //same X - mVerticalImage.setOffsetY(getOffsetY() + mHeight); + mVerticalImage.setOffset(0, mSize.y); mVerticalImage.setFlipY(true); mVerticalImage.render(); - //Renderer::drawRect(getOffsetX(), getOffsetY() + mHeight, mWidth, getVerticalBorderWidth(), 0x00FF00); //corner top left - mCornerImage.setOffsetX(getOffsetX() - getHorizontalBorderWidth()); - mCornerImage.setOffsetY(getOffsetY() - getVerticalBorderWidth()); + mCornerImage.setOffset(-getHorizontalBorderWidth(), -getVerticalBorderWidth()); mCornerImage.setFlipX(false); mCornerImage.setFlipY(false); mCornerImage.render(); //top right - mCornerImage.setOffsetX(getOffsetX() + mWidth); + mCornerImage.setOffset(mSize.x, mCornerImage.getOffset().y); mCornerImage.setFlipX(true); mCornerImage.render(); //bottom right - mCornerImage.setOffsetY(getOffsetY() + mHeight); + mCornerImage.setOffset(mCornerImage.getOffset().x, mSize.y); mCornerImage.setFlipY(true); mCornerImage.render(); //bottom left - mCornerImage.setOffsetX(getOffsetX() - getHorizontalBorderWidth()); + mCornerImage.setOffset(-getHorizontalBorderWidth(), mCornerImage.getOffset().y); mCornerImage.setFlipX(false); mCornerImage.render(); + + GuiComponent::onRender(); } void GuiBox::init() @@ -116,6 +106,8 @@ void GuiBox::init() mVerticalImage.init(); mHorizontalImage.init(); mCornerImage.init(); + + GuiComponent::init(); } void GuiBox::deinit() @@ -123,16 +115,18 @@ void GuiBox::deinit() mVerticalImage.deinit(); mHorizontalImage.deinit(); mCornerImage.deinit(); + + GuiComponent::deinit(); } int GuiBox::getHorizontalBorderWidth() { - return mHorizontalImage.getWidth(); + return mHorizontalImage.getTextureSize().x; } int GuiBox::getVerticalBorderWidth() { - return mVerticalImage.getHeight(); + return mVerticalImage.getTextureSize().y; } bool GuiBox::hasBackground() diff --git a/src/components/GuiBox.h b/src/components/GuiBox.h index 89be516d5..ad1325e5c 100644 --- a/src/components/GuiBox.h +++ b/src/components/GuiBox.h @@ -1,8 +1,8 @@ #ifndef _GUIBOX_H_ #define _GUIBOX_H_ -#include "../Gui.h" -#include "GuiImage.h" +#include "../GuiComponent.h" +#include "ImageComponent.h" #include struct GuiBoxData @@ -16,7 +16,7 @@ struct GuiBoxData std::string cornerPath; }; -class GuiBox : public Gui +class GuiBox : public GuiComponent { public: GuiBox(Window* window, int offsetX, int offsetY, unsigned int width, unsigned int height); @@ -30,17 +30,17 @@ public: bool hasBackground(); - void render(); - void init(); void deinit(); + +protected: + void onRender(); + private: - GuiImage mBackgroundImage, mHorizontalImage, mVerticalImage, mCornerImage; + ImageComponent mBackgroundImage, mHorizontalImage, mVerticalImage, mCornerImage; int getHorizontalBorderWidth(); int getVerticalBorderWidth(); - - unsigned int mWidth, mHeight; }; #endif diff --git a/src/components/GuiDetectDevice.cpp b/src/components/GuiDetectDevice.cpp index a46ccf0f4..8c05f1d91 100644 --- a/src/components/GuiDetectDevice.cpp +++ b/src/components/GuiDetectDevice.cpp @@ -7,7 +7,7 @@ #include #include -GuiDetectDevice::GuiDetectDevice(Window* window) : Gui(window) +GuiDetectDevice::GuiDetectDevice(Window* window) : GuiComponent(window) { //clear any player information from the InputManager for(int i = 0; i < mWindow->getInputManager()->getNumPlayers(); i++) @@ -22,7 +22,7 @@ GuiDetectDevice::GuiDetectDevice(Window* window) : Gui(window) mHoldingFinish = false; } -void GuiDetectDevice::input(InputConfig* config, Input input) +bool GuiDetectDevice::input(InputConfig* config, Input input) { if((input.type == TYPE_BUTTON || input.type == TYPE_KEY)) { @@ -38,11 +38,11 @@ void GuiDetectDevice::input(InputConfig* config, Input input) mHoldingFinish = false; } } - return; + return true; } if(!input.value) - return; + return false; config->setPlayerNum(mCurrentPlayer); mWindow->getInputManager()->setNumPlayers(mWindow->getInputManager()->getNumPlayers() + 1); //inc total number of players @@ -53,7 +53,11 @@ void GuiDetectDevice::input(InputConfig* config, Input input) { done(); } + + return true; } + + return false; } void GuiDetectDevice::done() diff --git a/src/components/GuiDetectDevice.h b/src/components/GuiDetectDevice.h index 18f1f8047..9dee87b52 100644 --- a/src/components/GuiDetectDevice.h +++ b/src/components/GuiDetectDevice.h @@ -1,14 +1,14 @@ #ifndef _GUIDETECTDEVICE_H_ #define _GUIDETECTDEVICE_H_ -#include "../Gui.h" +#include "../GuiComponent.h" -class GuiDetectDevice : public Gui +class GuiDetectDevice : public GuiComponent { public: GuiDetectDevice(Window* window); - void input(InputConfig* config, Input input); + bool input(InputConfig* config, Input input); void update(int deltaTime); void render(); diff --git a/src/components/GuiFastSelect.cpp b/src/components/GuiFastSelect.cpp index 488125a0c..008f4b972 100644 --- a/src/components/GuiFastSelect.cpp +++ b/src/components/GuiFastSelect.cpp @@ -7,8 +7,8 @@ const std::string GuiFastSelect::LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; const int GuiFastSelect::SCROLLSPEED = 100; const int GuiFastSelect::SCROLLDELAY = 507; -GuiFastSelect::GuiFastSelect(Window* window, GuiGameList* parent, GuiList* list, char startLetter, GuiBoxData data, - int textcolor, std::shared_ptr & scrollsound, Font* font) : Gui(window) +GuiFastSelect::GuiFastSelect(Window* window, GuiGameList* parent, TextListComponent* list, char startLetter, GuiBoxData data, + int textcolor, std::shared_ptr & scrollsound, Font* font) : GuiComponent(window) { mLetterID = LETTERS.find(toupper(startLetter)); if(mLetterID == std::string::npos) @@ -48,18 +48,20 @@ void GuiFastSelect::render() Renderer::drawCenteredText(LETTERS.substr(mLetterID, 1), 0, (int)(sh * 0.5f - (mFont->getHeight() * 0.5f)), mTextColor, mFont); } -void GuiFastSelect::input(InputConfig* config, Input input) +bool GuiFastSelect::input(InputConfig* config, Input input) { if(config->isMappedTo("up", input) && input.value != 0) { mScrollOffset = -1; scroll(); + return true; } if(config->isMappedTo("down", input) && input.value != 0) { mScrollOffset = 1; scroll(); + return true; } if((config->isMappedTo("up", input) || config->isMappedTo("down", input)) && input.value == 0) @@ -67,14 +69,17 @@ void GuiFastSelect::input(InputConfig* config, Input input) mScrolling = false; mScrollTimer = 0; mScrollOffset = 0; + return true; } if(config->isMappedTo("select", input) && input.value == 0) { setListPos(); delete this; - return; + return true; } + + return false; } void GuiFastSelect::update(int deltaTime) diff --git a/src/components/GuiFastSelect.h b/src/components/GuiFastSelect.h index 350e78282..67032e1f1 100644 --- a/src/components/GuiFastSelect.h +++ b/src/components/GuiFastSelect.h @@ -1,22 +1,23 @@ #ifndef _GUIFASTSELECT_H_ #define _GUIFASTSELECT_H_ -#include "../Gui.h" +#include "../GuiComponent.h" #include "../SystemData.h" #include "../FolderData.h" #include "../Sound.h" -#include "GuiList.h" +#include "TextListComponent.h" #include "GuiBox.h" class GuiGameList; -class GuiFastSelect : public Gui +class GuiFastSelect : public GuiComponent { public: - GuiFastSelect(Window* window, GuiGameList* parent, GuiList* list, char startLetter, GuiBoxData data, int textcolor, std::shared_ptr & scrollsound, Font* font); + GuiFastSelect(Window* window, GuiGameList* parent, TextListComponent* list, char startLetter, GuiBoxData data, + int textcolor, std::shared_ptr & scrollsound, Font* font); ~GuiFastSelect(); - void input(InputConfig* config, Input input); + bool input(InputConfig* config, Input input); void update(int deltaTime); void render(); private: @@ -28,7 +29,7 @@ private: void scroll(); void setLetterID(int id); - GuiList* mList; + TextListComponent* mList; size_t mLetterID; GuiGameList* mParent; diff --git a/src/components/GuiGameList.cpp b/src/components/GuiGameList.cpp index ffdebc921..a65c94668 100644 --- a/src/components/GuiGameList.cpp +++ b/src/components/GuiGameList.cpp @@ -5,44 +5,65 @@ #include "GuiFastSelect.h" #include #include "../Log.h" +#include "../Settings.h" -GuiGameList::GuiGameList(Window* window, bool useDetail) : Gui(window) +Vector2i GuiGameList::getImagePos() +{ + return Vector2i((int)(Renderer::getScreenWidth() * mTheme->getFloat("gameImageOffsetX")), (int)(Renderer::getScreenHeight() * mTheme->getFloat("gameImageOffsetY"))); +} + +GuiGameList::GuiGameList(Window* window, bool useDetail) : GuiComponent(window), + mDescription(window), mTransitionImage(window, 0, 0, "", Renderer::getScreenWidth(), Renderer::getScreenHeight(), true) { mDetailed = useDetail; - mTheme = new GuiTheme(mWindow, mDetailed); + mTheme = new ThemeComponent(mWindow, mDetailed); //The GuiGameList can use the older, simple game list if so desired. //The old view only shows a list in the center of the screen; the new view can display an image and description. //Those with smaller displays may prefer the older view. if(mDetailed) { - mList = new GuiList(mWindow, (int)(Renderer::getScreenWidth() * mTheme->getFloat("listOffsetX")), Renderer::getDefaultFont(Renderer::LARGE)->getHeight() + 2, Renderer::getDefaultFont(Renderer::MEDIUM)); + mList = new TextListComponent(mWindow, (int)(Renderer::getScreenWidth() * mTheme->getFloat("listOffsetX")), Renderer::getDefaultFont(Renderer::LARGE)->getHeight() + 2, Renderer::getDefaultFont(Renderer::MEDIUM)); - mScreenshot = new GuiImage(mWindow, (int)(Renderer::getScreenWidth() * mTheme->getFloat("gameImageOffsetX")), (int)(Renderer::getScreenHeight() * mTheme->getFloat("gameImageOffsetY")), "", (unsigned int)mTheme->getFloat("gameImageWidth"), (unsigned int)mTheme->getFloat("gameImageHeight"), false); - mScreenshot->setOrigin(mTheme->getFloat("gameImageOriginX"), mTheme->getFloat("gameImageOriginY")); - - mImageAnimation = new GuiAnimation(); + mImageAnimation = new AnimationComponent(); mImageAnimation->addChild(mScreenshot); }else{ - mList = new GuiList(mWindow, 0, Renderer::getDefaultFont(Renderer::LARGE)->getHeight() + 2, Renderer::getDefaultFont(Renderer::MEDIUM)); - mScreenshot = NULL; - mImageAnimation = NULL; + mList = new TextListComponent(mWindow, 0, Renderer::getDefaultFont(Renderer::LARGE)->getHeight() + 2, Renderer::getDefaultFont(Renderer::MEDIUM)); } + mScreenshot = new ImageComponent(mWindow, getImagePos().x, getImagePos().y, "", (unsigned int)mTheme->getFloat("gameImageWidth"), (unsigned int)mTheme->getFloat("gameImageHeight"), false); + mScreenshot->setOrigin(mTheme->getFloat("gameImageOriginX"), mTheme->getFloat("gameImageOriginY")); + + mDescription.setOffset(Vector2i((int)(Renderer::getScreenWidth() * 0.03), mScreenshot->getOffset().y + mScreenshot->getSize().y + 12)); + mDescription.setExtent(Vector2u((int)(Renderer::getScreenWidth() * (mTheme->getFloat("listOffsetX") - 0.03)), 0)); + + mTransitionImage.setOffset(Renderer::getScreenWidth(), 0); + mTransitionImage.setOrigin(0, 0); + mTransitionAnimation.addChild(&mTransitionImage); + + //a hack! the GuiGameList doesn't use the children system right now because I haven't redone it to do so yet. + //the list depends on knowing it's final window coordinates (getGlobalOffset), which requires knowing the where the GuiGameList is. + //the GuiGameList now moves during screen transitions, so we have to let it know somehow. + //this should be removed in favor of using real children soon. + mList->setParent(this); + setSystemId(0); } GuiGameList::~GuiGameList() { + //undo the parenting hack because otherwise it's not really a child and will try to remove itself on delete + mList->setParent(NULL); delete mList; if(mDetailed) { delete mImageAnimation; delete mScreenshot; - delete mTheme; } + + delete mTheme; } void GuiGameList::setSystemId(int id) @@ -74,6 +95,13 @@ void GuiGameList::setSystemId(int id) void GuiGameList::render() { + if(mTransitionImage.getOffset().x > 0) //transitioning in from the left + mOffset.x = mTransitionImage.getOffset().x - Renderer::getScreenWidth(); + else //transitioning in from the right + mOffset.x = mTransitionImage.getOffset().x + Renderer::getScreenWidth(); + + Renderer::translate(mOffset); + if(mTheme) mTheme->render(); @@ -86,24 +114,24 @@ void GuiGameList::render() //divider if(!mTheme->getBool("hideDividers")) Renderer::drawRect((int)(Renderer::getScreenWidth() * mTheme->getFloat("listOffsetX")) - 4, Renderer::getDefaultFont(Renderer::LARGE)->getHeight() + 2, 8, Renderer::getScreenHeight(), 0x0000FFFF); - - //if we're not scrolling and we have selected a non-folder - if(!mList->isScrolling() && mList->getSelectedObject() && !mList->getSelectedObject()->isFolder()) - { - GameData* game = (GameData*)mList->getSelectedObject(); - - std::string desc = game->getDescription(); - if(!desc.empty()) - Renderer::drawWrappedText(desc, (int)(Renderer::getScreenWidth() * 0.03), mScreenshot->getOffsetY() + mScreenshot->getHeight() + 12, (int)(Renderer::getScreenWidth() * (mTheme->getFloat("listOffsetX") - 0.03)), mTheme->getColor("description"), mTheme->getDescriptionFont()); - } - + mScreenshot->render(); + + //if we're not scrolling and we have selected a non-folder + if(!mList->isScrolling() && !mList->getSelectedObject()->isFolder()) + { + mDescription.render(); + } } mList->render(); + + Renderer::translate(-mOffset); + + mTransitionImage.render(); } -void GuiGameList::input(InputConfig* config, Input input) +bool GuiGameList::input(InputConfig* config, Input input) { mList->input(config, input); @@ -118,6 +146,8 @@ void GuiGameList::input(InputConfig* config, Input input) mFolderStack.push(mFolder); mFolder = (FolderData*)file; updateList(); + updateDetailData(); + return true; }else{ mList->stopScrolling(); @@ -125,7 +155,7 @@ void GuiGameList::input(InputConfig* config, Input input) while(mTheme->getSound("menuSelect")->isPlaying()); mSystem->launchGame(mWindow, (GameData*)file); - return; + return true; } } @@ -139,6 +169,8 @@ void GuiGameList::input(InputConfig* config, Input input) //play the back sound mTheme->getSound("menuBack")->play(); + + return true; } //only allow switching systems if more than one exists (otherwise it'll reset your position when you switch and it's annoying) @@ -147,10 +179,14 @@ void GuiGameList::input(InputConfig* config, Input input) if(config->isMappedTo("right", input)) { setSystemId(mSystemId + 1); + doTransition(-1); + return true; } if(config->isMappedTo("left", input)) { setSystemId(mSystemId - 1); + doTransition(1); + return true; } } @@ -158,12 +194,14 @@ void GuiGameList::input(InputConfig* config, Input input) if(config->isMappedTo("menu", input) && input.value != 0) { mWindow->pushGui(new GuiMenu(mWindow, this)); + return true; } //open the fast select menu if(config->isMappedTo("select", input) && input.value != 0) { mWindow->pushGui(new GuiFastSelect(mWindow, this, mList, mList->getSelectedObject()->getName()[0], mTheme->getBoxData(), mTheme->getColor("fastSelect"), mTheme->getSound("menuScroll"), mTheme->getFastSelectFont())); + return true; } if(mDetailed) @@ -175,7 +213,10 @@ void GuiGameList::input(InputConfig* config, Input input) else clearDetailData(); } + return true; } + + return false; } void GuiGameList::updateList() @@ -235,13 +276,15 @@ void GuiGameList::updateTheme() { mList->setCentered(mTheme->getBool("listCentered")); - mList->setOffsetX((int)(mTheme->getFloat("listOffsetX") * Renderer::getScreenWidth())); + mList->setOffset((int)(mTheme->getFloat("listOffsetX") * Renderer::getScreenWidth()), mList->getOffset().y); mList->setTextOffsetX((int)(mTheme->getFloat("listTextOffsetX") * Renderer::getScreenWidth())); - mScreenshot->setOffsetX((int)(mTheme->getFloat("gameImageOffsetX") * Renderer::getScreenWidth())); - mScreenshot->setOffsetY((int)(mTheme->getFloat("gameImageOffsetY") * Renderer::getScreenHeight())); + mScreenshot->setOffset((int)(mTheme->getFloat("gameImageOffsetX") * Renderer::getScreenWidth()), (int)(mTheme->getFloat("gameImageOffsetY") * Renderer::getScreenHeight())); mScreenshot->setOrigin(mTheme->getFloat("gameImageOriginX"), mTheme->getFloat("gameImageOriginY")); mScreenshot->setResize((int)mTheme->getFloat("gameImageWidth"), (int)mTheme->getFloat("gameImageHeight"), false); + + mDescription.setColor(mTheme->getColor("description")); + mDescription.setFont(mTheme->getDescriptionFont()); } } @@ -252,15 +295,19 @@ void GuiGameList::updateDetailData() if(mList->getSelectedObject() && !mList->getSelectedObject()->isFolder()) { - mScreenshot->setOffset((int)((mTheme->getFloat("gameImageOffsetX") - 0.05) * Renderer::getScreenWidth()), (int)(mTheme->getFloat("gameImageOffsetY") * Renderer::getScreenHeight())); - if(((GameData*)mList->getSelectedObject())->getImagePath().empty()) mScreenshot->setImage(mTheme->getString("imageNotFoundPath")); else mScreenshot->setImage(((GameData*)mList->getSelectedObject())->getImagePath()); - mImageAnimation->fadeIn(15); - mImageAnimation->move((int)(0.05 * Renderer::getScreenWidth()), 0, 5); + Vector2i imgOffset = Vector2i((int)(Renderer::getScreenWidth() * 0.10f), 0); + mScreenshot->setOffset(getImagePos() - imgOffset); + + mImageAnimation->fadeIn(35); + mImageAnimation->move(imgOffset.x, imgOffset.y, 20); + + mDescription.setOffset(Vector2i((int)(Renderer::getScreenWidth() * 0.03), mScreenshot->getOffset().y + mScreenshot->getSize().y + 12)); + mDescription.setText(((GameData*)mList->getSelectedObject())->getDescription()); }else{ mScreenshot->setImage(""); } @@ -296,12 +343,11 @@ void GuiGameList::init() } } -extern bool IGNOREGAMELIST; //defined in main.cpp (as a command line argument) GuiGameList* GuiGameList::create(Window* window) { bool detailed = false; - if(!IGNOREGAMELIST) + if(!Settings::getInstance()->getBool("IGNOREGAMELIST")) { for(unsigned int i = 0; i < SystemData::sSystemVector.size(); i++) { @@ -323,5 +369,15 @@ void GuiGameList::update(int deltaTime) if(mDetailed) mImageAnimation->update(deltaTime); + mTransitionAnimation.update(deltaTime); + mList->update(deltaTime); } + +void GuiGameList::doTransition(int dir) +{ + mTransitionImage.copyScreen(); + mTransitionImage.setOpacity(255); + mTransitionImage.setOffset(0, 0); + mTransitionAnimation.move(Renderer::getScreenWidth() * dir, 0, 50); +} diff --git a/src/components/GuiGameList.h b/src/components/GuiGameList.h index 860a8ee23..7f51a0f15 100644 --- a/src/components/GuiGameList.h +++ b/src/components/GuiGameList.h @@ -1,11 +1,12 @@ #ifndef _GUIGAMELIST_H_ #define _GUIGAMELIST_H_ -#include "../Gui.h" -#include "GuiList.h" -#include "GuiImage.h" -#include "GuiTheme.h" -#include "GuiAnimation.h" +#include "../GuiComponent.h" +#include "TextListComponent.h" +#include "ImageComponent.h" +#include "ThemeComponent.h" +#include "AnimationComponent.h" +#include "TextComponent.h" #include #include #include "../SystemData.h" @@ -13,16 +14,16 @@ #include "../FolderData.h" //This is where the magic happens - GuiGameList is the parent of almost every graphical element in ES at the moment. -//It has a GuiList child that handles the game list, a GuiTheme that handles the theming system, and a GuiImage for game images. -class GuiGameList : public Gui +//It has a TextListComponent child that handles the game list, a ThemeComponent that handles the theming system, and an ImageComponent for game images. +class GuiGameList : public GuiComponent { public: GuiGameList(Window* window, bool useDetail = false); - ~GuiGameList(); + virtual ~GuiGameList(); void setSystemId(int id); - void input(InputConfig* config, Input input); + bool input(InputConfig* config, Input input); void update(int deltaTime); void render(); @@ -38,6 +39,8 @@ private: void updateList(); void updateTheme(); void clearDetailData(); + void doTransition(int dir); + std::string getThemeFile(); SystemData* mSystem; @@ -46,10 +49,16 @@ private: int mSystemId; bool mDetailed; - GuiList* mList; - GuiImage* mScreenshot; - GuiAnimation* mImageAnimation; - GuiTheme* mTheme; + TextListComponent* mList; + ImageComponent* mScreenshot; + TextComponent mDescription; + AnimationComponent* mImageAnimation; + ThemeComponent* mTheme; + + ImageComponent mTransitionImage; + AnimationComponent mTransitionAnimation; + + Vector2i getImagePos(); }; #endif diff --git a/src/components/GuiInputConfig.cpp b/src/components/GuiInputConfig.cpp index 9817cfd5a..6f217fcec 100644 --- a/src/components/GuiInputConfig.cpp +++ b/src/components/GuiInputConfig.cpp @@ -5,11 +5,14 @@ #include "GuiGameList.h" #include "../Log.h" -static int inputCount = 12; -static std::string inputName[12] = { "Up", "Down", "Left", "Right", "A", "B", "Menu", "Select", "PageUp", "PageDown", "MasterVolUp", "MasterVolDown" }; -static std::string inputDispName[12] = { "Up", "Down", "Left", "Right", "Accept", "Back", "Menu", "Jump to Letter", "Page Up", "Page Down", "Master volume up", "Master volume down" }; +static const int inputCount = 10; +static std::string inputName[inputCount] = { "Up", "Down", "Left", "Right", "A", "B", "Menu", "Select", "PageUp", "PageDown"}; +static std::string inputDispName[inputCount] = { "Up", "Down", "Left", "Right", "Accept", "Back", "Menu", "Jump to Letter", "Page Up", "Page Down"}; -GuiInputConfig::GuiInputConfig(Window* window, InputConfig* target) : Gui(window), mTargetConfig(target) +//MasterVolUp and MasterVolDown are also hooked up, but do not appear on this screen. +//If you want, you can manually add them to es_input.cfg. + +GuiInputConfig::GuiInputConfig(Window* window, InputConfig* target) : GuiComponent(window), mTargetConfig(target), mCanSkip(false) { mCurInputId = 0; LOG(LogInfo) << "Configuring device " << target->getDeviceId(); @@ -20,10 +23,10 @@ void GuiInputConfig::update(int deltaTime) } -void GuiInputConfig::input(InputConfig* config, Input input) +bool GuiInputConfig::input(InputConfig* config, Input input) { if(config != mTargetConfig || input.value == 0) - return; + return false; if(mCurInputId >= inputCount) { @@ -38,12 +41,19 @@ void GuiInputConfig::input(InputConfig* config, Input input) GuiGameList::create(mWindow); } delete this; + return true; } }else{ + if(mCanSkip && config->isMappedTo("a", input)) + { + mCurInputId++; + return true; + } + if(config->getMappedTo(input).size() > 0) { mErrorMsg = "Already mapped!"; - return; + return true; } input.configured = true; @@ -53,12 +63,15 @@ void GuiInputConfig::input(InputConfig* config, Input input) mCurInputId++; mErrorMsg = ""; - //if the controller doesn't have enough buttons for Page Up/Page Down, skip to done - if(mWindow->getInputManager()->getButtonCountByDevice(config->getDeviceId()) <= 4 && mCurInputId >= 8) + //for buttons with not enough buttons, press A to skip + if(mCurInputId >= 7) { - mCurInputId = inputCount; + mCanSkip = true; } + return true; } + + return false; } void GuiInputConfig::render() @@ -78,10 +91,21 @@ void GuiInputConfig::render() if(mCurInputId >= inputCount) { - Renderer::drawCenteredText("Basic config done!", 0, (int)(Renderer::getScreenHeight() * 0.6), 0x00CC00FF, font); - Renderer::drawCenteredText("Press any button to continue.", 0, (int)(Renderer::getScreenHeight() * 0.6) + font->getHeight() + 4, 0x000000FF, font); + Renderer::drawCenteredText("Basic config done!", 0, (int)(Renderer::getScreenHeight() * 0.4), 0x00CC00FF, font); + Renderer::drawCenteredText("Press any button to continue.", 0, (int)(Renderer::getScreenHeight() * 0.4) + font->getHeight() + 4, 0x000000FF, font); }else{ Renderer::drawText(inputDispName[mCurInputId], 10, y, 0x000000FF, font); + if(mCanSkip) + { + int textWidth = 0; + font->sizeText(inputDispName[mCurInputId], &textWidth, NULL); + textWidth += 14; + + if(Renderer::getScreenWidth() / 2.5f > textWidth) + textWidth = (int)(Renderer::getScreenWidth() / 2.5f); + + Renderer::drawText("press Accept to skip", textWidth, y, 0x0000AAFF, font); + } } if(!mErrorMsg.empty()) diff --git a/src/components/GuiInputConfig.h b/src/components/GuiInputConfig.h index 02e023dc0..972b50c83 100644 --- a/src/components/GuiInputConfig.h +++ b/src/components/GuiInputConfig.h @@ -1,15 +1,15 @@ #ifndef _GUIINPUTCONFIG_H_ #define _GUIINPUTCONFIG_H_ -#include "../Gui.h" +#include "../GuiComponent.h" #include -class GuiInputConfig : public Gui +class GuiInputConfig : public GuiComponent { public: GuiInputConfig(Window* window, InputConfig* target); - void input(InputConfig* config, Input input); + bool input(InputConfig* config, Input input); void update(int deltaTime); void render(); @@ -17,6 +17,7 @@ private: std::string mErrorMsg; InputConfig* mTargetConfig; int mCurInputId; + bool mCanSkip; }; #endif diff --git a/src/components/GuiList.cpp b/src/components/GuiList.cpp deleted file mode 100644 index 89217fee8..000000000 --- a/src/components/GuiList.cpp +++ /dev/null @@ -1,293 +0,0 @@ -//This is *actually* part of the GuiList header and not meant to be compiled. - -#include "GuiList.h" -#include - -template -GuiList::GuiList(Window* window, int offsetX, int offsetY, Font* font) : Gui(window) -{ - mSelection = 0; - mScrollDir = 0; - mScrolling = 0; - mScrollAccumulator = 0; - - mOffsetX = offsetX; - mOffsetY = offsetY; - - mTextOffsetX = 0; - - mFont = font; - mSelectorColor = 0x000000FF; - mSelectedTextColorOverride = 0; - mScrollSound = NULL; - mDrawCentered = true; -} - -template -GuiList::~GuiList() -{ -} - -template -void GuiList::render() -{ - const int cutoff = getOffsetY(); - const int entrySize = mFont->getHeight() + 5; - - int startEntry = 0; - - //number of entries that can fit on the screen simultaniously - int screenCount = (Renderer::getScreenHeight() - cutoff) / entrySize; - //screenCount -= 1; - - if((int)mRowVector.size() >= screenCount) - { - startEntry = mSelection - (int)(screenCount * 0.5); - if(startEntry < 0) - startEntry = 0; - if(startEntry >= (int)mRowVector.size() - screenCount) - startEntry = mRowVector.size() - screenCount; - } - - int y = cutoff; - - if(mRowVector.size() == 0) - { - Renderer::drawCenteredText("The list is empty.", 0, y, 0xFF0000FF, mFont); - return; - } - - int listCutoff = startEntry + screenCount; - if(listCutoff > (int)mRowVector.size()) - listCutoff = mRowVector.size(); - - for(int i = startEntry; i < listCutoff; i++) - { - //draw selector bar - if(mSelection == i) - { - Renderer::drawRect(getOffsetX(), y, Renderer::getScreenWidth(), mFont->getHeight(), mSelectorColor); - } - - ListRow row = mRowVector.at((unsigned int)i); - - if(mDrawCentered) - Renderer::drawCenteredText(row.name, getOffsetX(), y, (mSelection == i && mSelectedTextColorOverride != 0) ? mSelectedTextColorOverride : row.color, mFont); - else - Renderer::drawText(row.name, getOffsetX() + mTextOffsetX, y, (mSelection == i && mSelectedTextColorOverride != 0) ? mSelectedTextColorOverride : row.color, mFont); - - y += entrySize; - } -} - -template -void GuiList::input(InputConfig* config, Input input) -{ - if(mRowVector.size() > 0) - { - if(input.value != 0) - { - if(config->isMappedTo("down", input)) - { - mScrollDir = 1; - scroll(); - } - - if(config->isMappedTo("up", input)) - { - mScrollDir = -1; - scroll(); - } - if(config->isMappedTo("pagedown", input)) - { - mScrollDir = 10; - scroll(); - } - - if(config->isMappedTo("pageup", input)) - { - mScrollDir = -10; - scroll(); - } - }else{ - //if((button == InputManager::DOWN && mScrollDir > 0) || (button == InputManager::PAGEDOWN && mScrollDir > 0) || (button == InputManager::UP && mScrollDir < 0) || (button == InputManager::PAGEUP && mScrollDir < 0)) - if(config->isMappedTo("down", input) || config->isMappedTo("up", input) || config->isMappedTo("pagedown", input) || config->isMappedTo("pageup", input)) - { - stopScrolling(); - } - } - } -} - -template -void GuiList::stopScrolling() -{ - mScrollAccumulator = 0; - mScrolling = false; - mScrollDir = 0; -} - -template -void GuiList::update(int deltaTime) -{ - if(mScrollDir != 0) - { - mScrollAccumulator += deltaTime; - - if(!mScrolling) - { - if(mScrollAccumulator >= SCROLLDELAY) - { - mScrollAccumulator = SCROLLTIME; - mScrolling = true; - } - } - - if(mScrolling) - { - mScrollAccumulator += deltaTime; - - while(mScrollAccumulator >= SCROLLTIME) - { - mScrollAccumulator -= SCROLLTIME; - - scroll(); - } - } - } -} - -template -void GuiList::scroll() -{ - mSelection += mScrollDir; - - if(mSelection < 0) - { - if(mScrollDir < -1) - mSelection = 0; - else - mSelection += mRowVector.size(); - } - if(mSelection >= (int)mRowVector.size()) - { - if(mScrollDir > 1) - mSelection = (int)mRowVector.size() - 1; - else - mSelection -= mRowVector.size(); - } - - if(mScrollSound) - mScrollSound->play(); -} - -//list management stuff -template -void GuiList::addObject(std::string name, listType obj, unsigned int color) -{ - ListRow row = {name, obj, color}; - mRowVector.push_back(row); -} - -template -void GuiList::clear() -{ - mRowVector.clear(); - mSelection = 0; -} - -template -std::string GuiList::getSelectedName() -{ - if((int)mRowVector.size() > mSelection) - return mRowVector.at(mSelection).name; - else - return ""; -} - -template -listType GuiList::getSelectedObject() -{ - if((int)mRowVector.size() > mSelection) - return mRowVector.at(mSelection).object; - else - return NULL; -} - -template -int GuiList::getSelection() -{ - return mSelection; -} - -template -bool GuiList::isScrolling() -{ - return mScrollDir != 0; -} - -template -void GuiList::setSelectorColor(unsigned int selectorColor) -{ - mSelectorColor = selectorColor; -} - -template -void GuiList::setSelectedTextColor(unsigned int selectedColor) -{ - mSelectedTextColorOverride = selectedColor; -} - -template -void GuiList::setCentered(bool centered) -{ - mDrawCentered = centered; -} - -template -void GuiList::setTextOffsetX(int textoffsetx) -{ - mTextOffsetX = textoffsetx; -} - -template -int GuiList::getObjectCount() -{ - return mRowVector.size(); -} - -template -listType GuiList::getObject(int i) -{ - return mRowVector.at(i).object; -} - -template -void GuiList::setSelection(int i) -{ - mSelection = i; -} - -template -void GuiList::setScrollSound(std::shared_ptr & sound) -{ - mScrollSound = sound; -} - -template -void GuiList::setFont(Font* font) -{ - mFont = font; -} - -template -int GuiList::getOffsetX() -{ - return mOffsetX; -} - -template -int GuiList::getOffsetY() -{ - return mOffsetY; -} diff --git a/src/components/GuiList.h b/src/components/GuiList.h deleted file mode 100644 index cb1110d11..000000000 --- a/src/components/GuiList.h +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef _GUILIST_H_ -#define _GUILIST_H_ - -#include "../Renderer.h" -#include "../Font.h" -#include "../Gui.h" -#include "../InputManager.h" -#include -#include -#include -#include "../Sound.h" - -//A graphical list. Supports multiple colors for rows and scrolling. -//TODO - add truncation to text rendering if name exceeds a maximum width (a trailing elipses, perhaps). -template -class GuiList : public Gui -{ -public: - GuiList(Window* window, int offsetX, int offsetY, Font* font); - ~GuiList(); - - void input(InputConfig* config, Input input); - void update(int deltaTime); - void render(); - - void addObject(std::string name, listType obj, unsigned int color = 0xFF0000); - void clear(); - - std::string getSelectedName(); - listType getSelectedObject(); - int getSelection(); - void stopScrolling(); - bool isScrolling(); - - void setSelectorColor(unsigned int selectorColor); - void setSelectedTextColor(unsigned int selectedColor); - void setCentered(bool centered); - void setScrollSound(std::shared_ptr & sound); - void setTextOffsetX(int textoffsetx); - - int getObjectCount(); - listType getObject(int i); - void setSelection(int i); - - void setFont(Font* f); - - int getOffsetX(); - int getOffsetY(); -private: - static const int SCROLLDELAY = 507; - static const int SCROLLTIME = 200; - - int mOffsetX, mOffsetY; - - void scroll(); //helper method, scrolls in whatever direction scrollDir is - - int mScrollDir, mScrollAccumulator; - bool mScrolling; - - Font* mFont; - unsigned int mSelectorColor, mSelectedTextColorOverride; - bool mDrawCentered; - - int mTextOffsetX; - - struct ListRow - { - std::string name; - listType object; - unsigned int color; - }; - - std::vector mRowVector; - int mSelection; - std::shared_ptr mScrollSound; -}; - -#include "GuiList.cpp" - -#endif diff --git a/src/components/GuiMenu.cpp b/src/components/GuiMenu.cpp index 6bf58319c..4e678d527 100644 --- a/src/components/GuiMenu.cpp +++ b/src/components/GuiMenu.cpp @@ -4,15 +4,14 @@ #include "../Log.h" #include "../SystemData.h" #include "GuiGameList.h" +#include "../Settings.h" +#include "GuiSettingsMenu.h" -//defined in main.cpp -extern bool DONTSHOWEXIT; - -GuiMenu::GuiMenu(Window* window, GuiGameList* parent) : Gui(window) +GuiMenu::GuiMenu(Window* window, GuiGameList* parent) : GuiComponent(window) { mParent = parent; - mList = new GuiList(mWindow, 0, Renderer::getDefaultFont(Renderer::LARGE)->getHeight() + 2, Renderer::getDefaultFont(Renderer::LARGE)); + mList = new TextListComponent(mWindow, 0, Renderer::getDefaultFont(Renderer::LARGE)->getHeight() + 2, Renderer::getDefaultFont(Renderer::LARGE)); mList->setSelectedTextColor(0x0000FFFF); populateList(); } @@ -22,20 +21,23 @@ GuiMenu::~GuiMenu() delete mList; } -void GuiMenu::input(InputConfig* config, Input input) +bool GuiMenu::input(InputConfig* config, Input input) { mList->input(config, input); if(config->isMappedTo("menu", input) && input.value != 0) { delete this; - return; + return true; } if(config->isMappedTo("a", input) && input.value != 0) { executeCommand(mList->getSelectedObject()); + return true; } + + return false; } void GuiMenu::executeCommand(std::string command) @@ -51,6 +53,10 @@ void GuiMenu::executeCommand(std::string command) //reload the game list SystemData::loadConfig(); mParent->setSystemId(0); + }else if(command == "es_settings") + { + mWindow->pushGui(new GuiSettingsMenu(mWindow)); + delete this; }else{ if(system(command.c_str()) != 0) { @@ -68,12 +74,15 @@ void GuiMenu::populateList() //the method is GuiList::addObject(std::string displayString, std::string commandString, unsigned int displayHexColor); //the list will automatically adjust as items are added to it, this should be the only area you need to change //if you want to do something special within ES, override your command in the executeComand() method + + mList->addObject("Settings", "es_settings", 0x0000FFFF); + mList->addObject("Restart", "sudo shutdown -r now", 0x0000FFFF); mList->addObject("Shutdown", "sudo shutdown -h now", 0x0000FFFF); mList->addObject("Reload", "es_reload", 0x0000FFFF); - if(!DONTSHOWEXIT) + if(!Settings::getInstance()->getBool("DONTSHOWEXIT")) mList->addObject("Exit", "exit", 0xFF0000FF); //a special case; pushes an SDL quit event to the event stack instead of being called by system() } diff --git a/src/components/GuiMenu.h b/src/components/GuiMenu.h index 58fc2395b..4f40a0161 100644 --- a/src/components/GuiMenu.h +++ b/src/components/GuiMenu.h @@ -1,24 +1,24 @@ #ifndef _GUIMENU_H_ #define _GUIMENU_H_ -#include "../Gui.h" -#include "GuiList.h" +#include "../GuiComponent.h" +#include "TextListComponent.h" class GuiGameList; -class GuiMenu : public Gui +class GuiMenu : public GuiComponent { public: GuiMenu(Window* window, GuiGameList* parent); - ~GuiMenu(); + virtual ~GuiMenu(); - void input(InputConfig* config, Input input); + bool input(InputConfig* config, Input input); void update(int deltaTime); void render(); private: GuiGameList* mParent; - GuiList* mList; + TextListComponent* mList; void populateList(); void executeCommand(std::string command); diff --git a/src/components/GuiSettingsMenu.cpp b/src/components/GuiSettingsMenu.cpp new file mode 100644 index 000000000..981f11db5 --- /dev/null +++ b/src/components/GuiSettingsMenu.cpp @@ -0,0 +1,89 @@ +#include "GuiSettingsMenu.h" +#include "../Renderer.h" +#include "../Settings.h" +#include "../VolumeControl.h" + +GuiSettingsMenu::GuiSettingsMenu(Window* window) : GuiComponent(window), + mList(window, Vector2u(2, 3)), + mDrawFramerateSwitch(window), + mVolumeSlider(window, 0, 100, 1), + mSaveLabel(window) +{ + loadStates(); + + addChild(&mList); + + mList.setOffset(Renderer::getScreenWidth() / 4, 0); + + TextComponent* label = new TextComponent(mWindow); + label->setText("Draw Framerate: "); + label->setColor(0x0000FFFF); + mList.setEntry(Vector2u(0, 0), Vector2u(1, 1), label, false, ComponentListComponent::AlignRight, Vector2(true, true)); + mLabels.push_back(label); + + //drawFramerate switch + mList.setEntry(Vector2u(1, 0), Vector2u(1, 1), &mDrawFramerateSwitch, true, ComponentListComponent::AlignCenter, Vector2(true, true)); + + label = new TextComponent(mWindow); + label->setText("System volume: "); + label->setColor(0x0000FFFF); + mList.setEntry(Vector2u(0, 1), Vector2u(1, 1), label, false, ComponentListComponent::AlignRight, Vector2(true, true)); + + //volume slider + mList.setEntry(Vector2u(1, 1), Vector2u(1, 1), &mVolumeSlider, true, ComponentListComponent::AlignCenter, Vector2(true, true)); + + //save label + mSaveLabel.setText("SAVE"); + mSaveLabel.setColor(0x000000FF); + mList.setEntry(Vector2u(0, 2), Vector2u(2, 1), &mSaveLabel, true, ComponentListComponent::AlignCenter, Vector2(false, true)); + + mList.setOffset(Renderer::getScreenWidth() / 2 - mList.getSize().x / 2, 0); +} + +GuiSettingsMenu::~GuiSettingsMenu() +{ + for(auto iter = mLabels.begin(); iter != mLabels.end(); iter++) + { + delete *iter; + } +} + +bool GuiSettingsMenu::input(InputConfig* config, Input input) +{ + //let our children (read: list) go first + if(GuiComponent::input(config, input)) + return true; + + if(config->isMappedTo("b", input) && input.value) + { + delete this; + return true; + } + + if(config->isMappedTo("a", input) && mList.getSelectedComponent() == &mSaveLabel && input.value) + { + applyStates(); + delete this; + return true; + } + + return false; +} + +void GuiSettingsMenu::loadStates() +{ + Settings* s = Settings::getInstance(); + mDrawFramerateSwitch.setState(s->getBool("DRAWFRAMERATE")); + + mVolumeSlider.setValue((float)VolumeControl::getInstance()->getVolume()); +} + +void GuiSettingsMenu::applyStates() +{ + Settings* s = Settings::getInstance(); + s->setBool("DRAWFRAMERATE", mDrawFramerateSwitch.getState()); + + VolumeControl::getInstance()->setVolume((int)mVolumeSlider.getValue()); + + s->saveFile(); +} diff --git a/src/components/GuiSettingsMenu.h b/src/components/GuiSettingsMenu.h new file mode 100644 index 000000000..0b9af8dc3 --- /dev/null +++ b/src/components/GuiSettingsMenu.h @@ -0,0 +1,32 @@ +#ifndef _SETTINGSMENU_H_ +#define _SETTINGSMENU_H_ + +#include "../GuiComponent.h" +#include "ComponentListComponent.h" +#include +#include "SwitchComponent.h" +#include "SliderComponent.h" +#include "TextComponent.h" + +class GuiSettingsMenu : public GuiComponent +{ +public: + GuiSettingsMenu(Window* window); + ~GuiSettingsMenu(); + + bool input(InputConfig* config, Input input) override; + +private: + void loadStates(); + void applyStates(); + + ComponentListComponent mList; + + SwitchComponent mDrawFramerateSwitch; + SliderComponent mVolumeSlider; + TextComponent mSaveLabel; + + std::vector mLabels; +}; + +#endif diff --git a/src/components/GuiImage.cpp b/src/components/ImageComponent.cpp similarity index 53% rename from src/components/GuiImage.cpp rename to src/components/ImageComponent.cpp index 97a768735..238c9019e 100644 --- a/src/components/GuiImage.cpp +++ b/src/components/ImageComponent.cpp @@ -1,33 +1,30 @@ -#include "GuiImage.h" +#include "ImageComponent.h" #include #include #include #include "../Log.h" #include "../Renderer.h" -unsigned int GuiImage::getWidth() { return mDrawWidth; } -unsigned int GuiImage::getHeight() { return mDrawHeight; } +Vector2u ImageComponent::getTextureSize() { return mTextureSize; } -GuiImage::GuiImage(Window* window, int offsetX, int offsetY, std::string path, unsigned int resizeWidth, unsigned int resizeHeight, bool resizeExact) : Gui(window) +ImageComponent::ImageComponent(Window* window, int offsetX, int offsetY, std::string path, unsigned int resizeWidth, unsigned int resizeHeight, bool allowUpscale) : GuiComponent(window) { mTextureID = 0; - setOffset(offsetX, offsetY); + setOffset(Vector2i(offsetX, offsetY)); //default origin is the center of image - mOriginX = 0.5; - mOriginY = 0.5; - mOpacity = 255; + mOrigin.x = 0.5; + mOrigin.y = 0.5; - mWidth = mDrawWidth = 0; - mHeight = mDrawHeight = 0; + mOpacity = 255; mTiled = false; - mResizeWidth = resizeWidth; - mResizeHeight = resizeHeight; + mTargetSize.x = resizeWidth; + mTargetSize.y = resizeHeight; - mResizeExact = resizeExact; + mAllowUpscale = allowUpscale; mFlipX = false; mFlipY = false; @@ -36,17 +33,17 @@ GuiImage::GuiImage(Window* window, int offsetX, int offsetY, std::string path, u setImage(path); } -GuiImage::~GuiImage() +ImageComponent::~ImageComponent() { unloadImage(); } -void GuiImage::loadImage(std::string path) +void ImageComponent::loadImage(std::string path) { //make sure the file *exists* if(!boost::filesystem::exists(path)) { - LOG(LogError) << "File \"" << path << "\" not found!"; + LOG(LogError) << "Image \"" << path << "\" not found!"; return; } @@ -112,29 +109,6 @@ void GuiImage::loadImage(std::string path) return; } - - /* - //set width/height to powers of 2 for OpenGL - for(unsigned int i = 0; i < 22; i++) - { - unsigned int pwrOf2 = pow(2, i); - if(!widthPwrOf2 && pwrOf2 >= width) - widthPwrOf2 = pwrOf2; - if(!heightPwrOf2 && pwrOf2 >= height) - heightPwrOf2 = pwrOf2; - - if(widthPwrOf2 && heightPwrOf2) - break; - } - - if(!widthPwrOf2 || !heightPwrOf2) - { - LOG(LogError) << "Error assigning power of two for width or height of image!"; - FreeImage_Unload(image); - return; - }*/ - - //convert from BGRA to RGBA GLubyte* imageRGBA = new GLubyte[4*width*height]; for(unsigned int i = 0; i < width*height; i++) @@ -159,8 +133,8 @@ void GuiImage::loadImage(std::string path) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - mWidth = width; - mHeight = height; + mTextureSize.x = width; + mTextureSize.y = height; //free the image data FreeImage_Unload(image); @@ -171,42 +145,43 @@ void GuiImage::loadImage(std::string path) resize(); } -void GuiImage::resize() +void ImageComponent::resize() { - mDrawWidth = mWidth; - mDrawHeight = mHeight; + mSize.x = mTextureSize.x; + mSize.y = mTextureSize.y; //(we don't resize tiled images) - if(!mTiled) + if(!mTiled && (mTargetSize.x || mTargetSize.y)) { - float resizeScaleX = 0, resizeScaleY = 0; - if(mResizeExact) - { - if(mResizeWidth) - resizeScaleX = (float)mResizeWidth / mWidth; - if(mResizeHeight) - resizeScaleY = (float)mResizeHeight / mHeight; - }else{ - if(mResizeWidth && mWidth > mResizeWidth) - resizeScaleX = (float)mResizeWidth / mWidth; + Vector2f resizeScale; - if(mResizeHeight && mHeight > mResizeHeight) - resizeScaleY = (float)mResizeHeight / mHeight; + if(mTargetSize.x && (mAllowUpscale || mSize.x > mTargetSize.x)) + { + resizeScale.x = (float)mTargetSize.x / mSize.x; + } + if(mTargetSize.y && (mAllowUpscale || mSize.y > mTargetSize.y)) + { + resizeScale.y = (float)mTargetSize.y / mSize.y; } - if(resizeScaleX && !resizeScaleY) - resizeScaleY = resizeScaleX; - if(resizeScaleY && !resizeScaleX) - resizeScaleX = resizeScaleY; + if(resizeScale.x && !resizeScale.y) + resizeScale.y = resizeScale.x; + if(resizeScale.y && !resizeScale.x) + resizeScale.x = resizeScale.y; - if(resizeScaleX) - mDrawWidth = (int)(mDrawWidth * resizeScaleX); - if(resizeScaleY) - mDrawHeight = (int)(mDrawHeight * resizeScaleY); + if(resizeScale.x) + mSize.x = (int)(mSize.x * resizeScale.x); + if(resizeScale.y) + mSize.y = (int)(mSize.y * resizeScale.y); + } + + if(mTiled) + { + mSize = mTargetSize; } } -void GuiImage::unloadImage() +void ImageComponent::unloadImage() { if(mTextureID) { @@ -216,7 +191,7 @@ void GuiImage::unloadImage() } } -void GuiImage::setImage(std::string path) +void ImageComponent::setImage(std::string path) { if(mPath == path) return; @@ -229,39 +204,41 @@ void GuiImage::setImage(std::string path) } -void GuiImage::setOrigin(float originX, float originY) +void ImageComponent::setOrigin(float originX, float originY) { - mOriginX = originX; - mOriginY = originY; + mOrigin.x = originX; + mOrigin.y = originY; } -void GuiImage::setTiling(bool tile) +void ImageComponent::setTiling(bool tile) { mTiled = tile; if(mTiled) - mResizeExact = false; -} + mAllowUpscale = false; -void GuiImage::setResize(unsigned int width, unsigned int height, bool resizeExact) -{ - mResizeWidth = width; - mResizeHeight = height; - mResizeExact = resizeExact; resize(); } -void GuiImage::setFlipX(bool flip) +void ImageComponent::setResize(unsigned int width, unsigned int height, bool allowUpscale) +{ + mTargetSize.x = width; + mTargetSize.y = height; + mAllowUpscale = allowUpscale; + resize(); +} + +void ImageComponent::setFlipX(bool flip) { mFlipX = flip; } -void GuiImage::setFlipY(bool flip) +void ImageComponent::setFlipY(bool flip) { mFlipY = flip; } -void GuiImage::render() +void ImageComponent::onRender() { if(mTextureID && getOpacity() > 0) { @@ -270,29 +247,31 @@ void GuiImage::render() if(mTiled) { - float xCount = ((float)mResizeWidth/mWidth); - float yCount = ((float)mResizeHeight/mHeight); - + float xCount = (float)mSize.x / mTextureSize.x; + float yCount = (float)mSize.y / mTextureSize.y; + Renderer::buildGLColorArray(colors, 0xFFFFFF00 | (getOpacity()), 6); - buildImageArray(getOffsetX(), getOffsetY(), points, texs, xCount, yCount); + buildImageArray(0, 0, points, texs, xCount, yCount); }else{ Renderer::buildGLColorArray(colors, 0xFFFFFF00 | (getOpacity()), 6); - buildImageArray(getOffsetX(), getOffsetY(), points, texs); + buildImageArray(0, 0, points, texs); } drawImageArray(points, texs, colors, 6); } + + GuiComponent::onRender(); } -void GuiImage::buildImageArray(int posX, int posY, GLfloat* points, GLfloat* texs, float px, float py) +void ImageComponent::buildImageArray(int posX, int posY, GLfloat* points, GLfloat* texs, float px, float py) { - points[0] = posX - (mDrawWidth * mOriginX) * px; points[1] = posY - (mDrawHeight * mOriginY) * py; - points[2] = posX - (mDrawWidth * mOriginX) * px; points[3] = posY + (mDrawHeight * (1 - mOriginY)) * py; - points[4] = posX + (mDrawWidth * (1 - mOriginX)) * px; points[5] = posY - (mDrawHeight * mOriginY) * py; + points[0] = posX - (mSize.x * mOrigin.x); points[1] = posY - (mSize.y * mOrigin.y); + points[2] = posX - (mSize.x * mOrigin.x); points[3] = posY + (mSize.y * (1 - mOrigin.y)); + points[4] = posX + (mSize.x * (1 - mOrigin.x)); points[5] = posY - (mSize.y * mOrigin.y); - points[6] = posX + (mDrawWidth * (1 - mOriginX)) * px; points[7] = posY - (mDrawHeight * mOriginY) * py; - points[8] = posX - (mDrawWidth * mOriginX) * px; points[9] = posY + (mDrawHeight * (1 - mOriginY)) * py; - points[10] = posX + (mDrawWidth * (1 -mOriginX)) * px; points[11] = posY + (mDrawHeight * (1 - mOriginY)) * py; + points[6] = posX + (mSize.x * (1 - mOrigin.x)); points[7] = posY - (mSize.y * mOrigin.y); + points[8] = posX - (mSize.x * mOrigin.x); points[9] = posY + (mSize.y * (1 - mOrigin.y)); + points[10] = posX + (mSize.x * (1 -mOrigin.x)); points[11] = posY + (mSize.y * (1 - mOrigin.y)); @@ -322,14 +301,13 @@ void GuiImage::buildImageArray(int posX, int posY, GLfloat* points, GLfloat* tex } } -void GuiImage::drawImageArray(GLfloat* points, GLfloat* texs, GLubyte* colors, unsigned int numArrays) +void ImageComponent::drawImageArray(GLfloat* points, GLfloat* texs, GLubyte* colors, unsigned int numArrays) { glBindTexture(GL_TEXTURE_2D, mTextureID); glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); @@ -354,21 +332,57 @@ void GuiImage::drawImageArray(GLfloat* points, GLfloat* texs, GLubyte* colors, u glDisable(GL_BLEND); } -void GuiImage::init() +void ImageComponent::init() { if(!mPath.empty()) loadImage(mPath); + + GuiComponent::init(); } -void GuiImage::deinit() +void ImageComponent::deinit() { unloadImage(); + + GuiComponent::deinit(); } -bool GuiImage::hasImage() +bool ImageComponent::hasImage() { return !mPath.empty(); } -unsigned char GuiImage::getOpacity() { return mOpacity; } -void GuiImage::setOpacity(unsigned char opacity) { mOpacity = opacity; } +unsigned char ImageComponent::getOpacity() { return mOpacity; } +void ImageComponent::setOpacity(unsigned char opacity) { mOpacity = opacity; } + +void ImageComponent::copyScreen() +{ + unloadImage(); + + int width = Renderer::getScreenWidth(); + int height = Renderer::getScreenHeight(); + + //glReadBuffer(GL_FRONT); + + /*char* data = new char[width*height*3]; + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, data);*/ + + glGenTextures(1, &mTextureID); + glBindTexture(GL_TEXTURE_2D, mTextureID); + + //glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); + //delete[] data; + glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, width, height, 0); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + mTextureSize.x = height; + mTextureSize.y = height; + + resize(); +} diff --git a/src/components/GuiImage.h b/src/components/ImageComponent.h similarity index 62% rename from src/components/GuiImage.h rename to src/components/ImageComponent.h index 9454ce798..3cf0204b1 100644 --- a/src/components/GuiImage.h +++ b/src/components/ImageComponent.h @@ -1,50 +1,56 @@ -#ifndef _GUIIMAGE_H_ -#define _GUIIMAGE_H_ +#ifndef _IMAGECOMPONENT_H_ +#define _IMAGECOMPONENT_H_ #include "../platform.h" #include GLHEADER -#include "../Gui.h" +#include "../GuiComponent.h" #include #include -class GuiImage : public Gui +class ImageComponent : public GuiComponent { public: //Creates a new GuiImage at the given location. If given an image, it will be loaded. If maxWidth and/or maxHeight are nonzero, the image will be - //resized to fix. If only one axis is specified, the other will be resized in accordance with the image's aspect ratio. If resizeExact is false, + //resized to fit. If only one axis is specified, the other will be set in accordance with the image's aspect ratio. If allowUpscale is false, //the image will only be downscaled, never upscaled (the image's size must surpass at least one nonzero bound). - GuiImage(Window* window, int offsetX = 0, int offsetY = 0, std::string path = "", unsigned int maxWidth = 0, unsigned int maxHeight = 0, bool resizeExact = false); - ~GuiImage(); + ImageComponent(Window* window, int offsetX = 0, int offsetY = 0, std::string path = "", unsigned int maxWidth = 0, unsigned int maxHeight = 0, bool allowUpscale = false); + virtual ~ImageComponent(); + //Copy the entire screen into a texture for us to use. + void copyScreen(); void setImage(std::string path); //Loads the image at the given filepath. void setOrigin(float originX, float originY); //Sets the origin as a percentage of this image (e.g. (0, 0) is top left, (0.5, 0.5) is the center) void setTiling(bool tile); //Enables or disables tiling. Must be called before loading an image or resizing will be weird. - void setResize(unsigned int width, unsigned int height, bool resizeExact); + void setResize(unsigned int width, unsigned int height, bool allowUpscale); void setFlipX(bool flip); void setFlipY(bool flip); - unsigned int getWidth(); //Returns render width in pixels. May be different than actual texture width. - unsigned int getHeight(); //Returns render height in pixels. May be different than actual texture height. + //You can get the rendered size of the ImageComponent with getSize(). + Vector2u getTextureSize(); + bool hasImage(); - void render(); - //Image textures will be deleted on renderer deinitialization, and recreated on reinitialization (if mPath is not empty). void init(); void deinit(); unsigned char getOpacity(); void setOpacity(unsigned char opacity); -private: - unsigned int mResizeWidth, mResizeHeight; - float mOriginX, mOriginY; - bool mResizeExact, mTiled, mFlipX, mFlipY; - int mOffsetX, mOffsetY; +protected: + void onRender(); + +private: + Vector2u mTargetSize; + Vector2u mTextureSize; + Vector2f mOrigin; + + bool mAllowUpscale, mTiled, mFlipX, mFlipY; + unsigned char mOpacity; void loadImage(std::string path); @@ -55,9 +61,6 @@ private: std::string mPath; - unsigned int mWidth, mHeight; //Our rendered size. - unsigned int mDrawWidth, mDrawHeight; - GLuint mTextureID; }; diff --git a/src/components/SliderComponent.cpp b/src/components/SliderComponent.cpp new file mode 100644 index 000000000..e9e92f49a --- /dev/null +++ b/src/components/SliderComponent.cpp @@ -0,0 +1,101 @@ +#include "SliderComponent.h" +#include +#include "../Renderer.h" + +SliderComponent::SliderComponent(Window* window, float min, float max, float increment) : GuiComponent(window), + mMin(min), mMax(max), mIncrement(increment), mMoveRate(0), mRepeatWaitTimer(0) +{ + assert((min - max) != 0); + + mValue = (max + min) / 2; + + //calculate move scale + mMoveScale = ((max - min) * 0.0007f) / increment; + + setSize(Vector2u(128, 32)); +} + +bool SliderComponent::input(InputConfig* config, Input input) +{ + if(config->isMappedTo("left", input)) + { + if(input.value) + mMoveRate = -mIncrement; + else + mMoveRate = 0; + + //setting mRepeatWaitTimer to 0 will trigger an initial move in our update method + mRepeatWaitTimer = 0; + + return true; + } + if(config->isMappedTo("right", input)) + { + if(input.value) + mMoveRate = mIncrement; + else + mMoveRate = 0; + + mRepeatWaitTimer = 0; + + return true; + } + + return GuiComponent::input(config, input); +} + +void SliderComponent::update(int deltaTime) +{ + if(mMoveRate != 0) + { + if(mRepeatWaitTimer == 0) + mValue += mMoveRate; + else if(mRepeatWaitTimer >= 450) + mValue += mMoveRate * deltaTime * mMoveScale; + + if(mValue < mMin) + mValue = mMin; + if(mValue > mMax) + mValue = mMax; + + if(mRepeatWaitTimer < 450) + mRepeatWaitTimer += deltaTime; + } + + GuiComponent::update(deltaTime); +} + +void SliderComponent::onRender() +{ + //render line + const int lineWidth = 2; + Renderer::drawRect(0, mSize.y / 2 - lineWidth / 2, mSize.x, lineWidth, 0x000000CC); + + //render left end + const int capWidth = (int)(mSize.x * 0.03f); + Renderer::drawRect(0, 0, capWidth, mSize.y, 0x000000CC); + + //render right end + Renderer::drawRect(mSize.x - capWidth, 0, capWidth, mSize.y, 0x000000CC); + + //render our value + const int lineLength = mSize.x - capWidth; + Renderer::drawRect((int)(((mValue + mMin) / mMax) * lineLength), 0, capWidth, mSize.y, 0x0000FFFF); + + GuiComponent::onRender(); +} + +void SliderComponent::setSize(Vector2u size) +{ + mSize = size; +} + +void SliderComponent::setValue(float value) +{ + mValue = value; +} + +float SliderComponent::getValue() +{ + return mValue; +} diff --git a/src/components/SliderComponent.h b/src/components/SliderComponent.h new file mode 100644 index 000000000..c6c49ad0d --- /dev/null +++ b/src/components/SliderComponent.h @@ -0,0 +1,28 @@ +#pragma once + +#include "../GuiComponent.h" + +class SliderComponent : public GuiComponent +{ +public: + //Minimum value (far left of the slider), maximum value (far right of the slider), increment size (how much just pressing L/R moves by). + SliderComponent(Window* window, float min, float max, float increment); + + void setValue(float val); + float getValue(); + + bool input(InputConfig* config, Input input) override; + void update(int deltaTime) override; + void onRender() override; + + void setSize(Vector2u size); + +private: + float mMin, mMax; + float mValue; + float mIncrement; + float mMoveScale; + int mRepeatWaitTimer; + + float mMoveRate; +}; diff --git a/src/components/SwitchComponent.cpp b/src/components/SwitchComponent.cpp new file mode 100644 index 000000000..ac78c810e --- /dev/null +++ b/src/components/SwitchComponent.cpp @@ -0,0 +1,43 @@ +#include "SwitchComponent.h" +#include "../Renderer.h" +#include "../Font.h" + +SwitchComponent::SwitchComponent(Window* window, bool state) : GuiComponent(window), mState(state) +{ + //mSize = Vector2u((unsigned int)(Renderer::getScreenWidth() * 0.05), + // (unsigned int)(Renderer::getScreenHeight() * 0.05)); + + Renderer::getDefaultFont(Renderer::MEDIUM)->sizeText("OFF", (int*)&mSize.x, (int*)&mSize.y); +} + +bool SwitchComponent::input(InputConfig* config, Input input) +{ + if(config->isMappedTo("a", input) && input.value) + { + mState = !mState; + return true; + } + + return false; +} + +void SwitchComponent::onRender() +{ + Renderer::pushClipRect(getGlobalOffset(), getSize()); + + Renderer::drawText(mState ? "ON" : "OFF", 0, 0, mState ? 0x00FF00FF : 0xFF0000FF, Renderer::getDefaultFont(Renderer::MEDIUM)); + + Renderer::popClipRect(); + + GuiComponent::onRender(); +} + +bool SwitchComponent::getState() +{ + return mState; +} + +void SwitchComponent::setState(bool state) +{ + mState = state; +} diff --git a/src/components/SwitchComponent.h b/src/components/SwitchComponent.h new file mode 100644 index 000000000..485c4d7e9 --- /dev/null +++ b/src/components/SwitchComponent.h @@ -0,0 +1,18 @@ +#pragma once + +#include "../GuiComponent.h" + +class SwitchComponent : public GuiComponent +{ +public: + SwitchComponent(Window* window, bool state = false); + + bool input(InputConfig* config, Input input) override; + void onRender() override; + + bool getState(); + void setState(bool state); + +private: + bool mState; +}; diff --git a/src/components/TextComponent.cpp b/src/components/TextComponent.cpp new file mode 100644 index 000000000..76312e80c --- /dev/null +++ b/src/components/TextComponent.cpp @@ -0,0 +1,84 @@ +#include "TextComponent.h" +#include "../Renderer.h" +#include "../Log.h" + +TextComponent::TextComponent(Window* window) : GuiComponent(window), mFont(NULL), mColor(0x000000FF), mAutoCalcExtent(true) +{ +} + +TextComponent::TextComponent(Window* window, const std::string& text, Font* font, Vector2i pos, Vector2u size) : GuiComponent(window), + mFont(NULL), mColor(0x000000FF), mAutoCalcExtent(true) +{ + setText(text); + setFont(font); + setBox(pos, size); +} + +void TextComponent::setBox(Vector2i pos, Vector2u size) +{ + setOffset(pos); + setExtent(size); +} + +void TextComponent::setExtent(Vector2u size) +{ + if(size == Vector2u(0, 0)) + { + mAutoCalcExtent = true; + calculateExtent(); + }else{ + mAutoCalcExtent = false; + mSize = size; + } +} + +void TextComponent::setFont(Font* font) +{ + mFont = font; + + if(mAutoCalcExtent) + calculateExtent(); +} + +void TextComponent::setColor(unsigned int color) +{ + mColor = color; +} + +void TextComponent::setText(const std::string& text) +{ + mText = text; + + if(mAutoCalcExtent) + calculateExtent(); +} + +void TextComponent::onRender() +{ + Font* font = (mFont ? mFont : Renderer::getDefaultFont(Renderer::MEDIUM)); + if(font == NULL) + { + LOG(LogError) << "TextComponent can't get a valid font!"; + return; + } + + Renderer::pushClipRect(getGlobalOffset(), getSize()); + + Renderer::drawWrappedText(mText, 0, 0, mSize.x, mColor, font); + + Renderer::popClipRect(); + + GuiComponent::onRender(); +} + +void TextComponent::calculateExtent() +{ + Font* font = (mFont ? mFont : Renderer::getDefaultFont(Renderer::MEDIUM)); + if(font == NULL) + { + LOG(LogError) << "TextComponent can't get a valid font!"; + return; + } + + font->sizeText(mText, (int*)&mSize.x, (int*)&mSize.y); +} diff --git a/src/components/TextComponent.h b/src/components/TextComponent.h new file mode 100644 index 000000000..67a6f9b5d --- /dev/null +++ b/src/components/TextComponent.h @@ -0,0 +1,30 @@ +#ifndef _TEXTCOMPONENT_H_ +#define _TEXTCOMPONENT_H_ + +#include "../GuiComponent.h" +#include "../Font.h" + +class TextComponent : public GuiComponent +{ +public: + TextComponent(Window* window); + TextComponent(Window* window, const std::string& text, Font* font, Vector2i pos, Vector2u size); + + void setFont(Font* font); + void setBox(Vector2i pos, Vector2u size); + void setExtent(Vector2u size); //Use Vector2u(0, 0) to automatically generate extent. + void setText(const std::string& text); + void setColor(unsigned int color); + + void onRender(); + +private: + void calculateExtent(); + + unsigned int mColor; + Font* mFont; + bool mAutoCalcExtent; + std::string mText; +}; + +#endif diff --git a/src/components/TextListComponent.h b/src/components/TextListComponent.h new file mode 100644 index 000000000..0aea6cea4 --- /dev/null +++ b/src/components/TextListComponent.h @@ -0,0 +1,405 @@ +#ifndef _TEXTLISTCOMPONENT_H_ +#define _TEXTLISTCOMPONENT_H_ + +#include "../Renderer.h" +#include "../Font.h" +#include "../GuiComponent.h" +#include "../InputManager.h" +#include +#include +#include +#include "../Sound.h" + +#define MARQUEE_DELAY 900 +#define MARQUEE_SPEED 16 +#define MARQUEE_RATE 3 + +//A graphical list. Supports multiple colors for rows and scrolling. +template +class TextListComponent : public GuiComponent +{ +public: + TextListComponent(Window* window, int offsetX, int offsetY, Font* font); + virtual ~TextListComponent(); + + bool input(InputConfig* config, Input input); + void update(int deltaTime); + + void addObject(std::string name, T obj, unsigned int color = 0xFF0000); + void clear(); + + std::string getSelectedName(); + T getSelectedObject(); + int getSelection(); + void stopScrolling(); + bool isScrolling(); + + void setSelectorColor(unsigned int selectorColor); + void setSelectedTextColor(unsigned int selectedColor); + void setCentered(bool centered); + void setScrollSound(std::shared_ptr & sound); + void setTextOffsetX(int textoffsetx); + + int getObjectCount(); + T getObject(int i); + void setSelection(int i); + + void setFont(Font* f); + +protected: + void onRender(); + +private: + static const int SCROLLDELAY = 507; + static const int SCROLLTIME = 200; + + void scroll(); //helper method, scrolls in whatever direction scrollDir is + void setScrollDir(int val); //helper method, set mScrollDir as well as reset marquee stuff + + int mScrollDir, mScrollAccumulator; + bool mScrolling; + + int mMarqueeOffset; + int mMarqueeTime; + + Font* mFont; + unsigned int mSelectorColor, mSelectedTextColorOverride; + bool mDrawCentered; + + int mTextOffsetX; + + struct ListRow + { + std::string name; + T object; + unsigned int color; + }; + + std::vector mRowVector; + int mSelection; + std::shared_ptr mScrollSound; +}; + +template +TextListComponent::TextListComponent(Window* window, int offsetX, int offsetY, Font* font) : GuiComponent(window) +{ + mSelection = 0; + mScrollDir = 0; + mScrolling = 0; + mScrollAccumulator = 0; + + setOffset(Vector2i(offsetX, offsetY)); + + mSize = Vector2u(Renderer::getScreenWidth() - getOffset().x, Renderer::getScreenHeight() - getOffset().y); + mMarqueeOffset = 0; + mMarqueeTime = -MARQUEE_DELAY; + mTextOffsetX = 0; + + mFont = font; + mSelectorColor = 0x000000FF; + mSelectedTextColorOverride = 0; + mScrollSound = NULL; + mDrawCentered = true; +} + +template +TextListComponent::~TextListComponent() +{ +} + +template +void TextListComponent::onRender() +{ + const int cutoff = 0; + const int entrySize = mFont->getHeight() + 5; + + int startEntry = 0; + + //number of entries that can fit on the screen simultaniously + int screenCount = (Renderer::getScreenHeight() - cutoff) / entrySize; + //screenCount -= 1; + + if((int)mRowVector.size() >= screenCount) + { + startEntry = mSelection - (int)(screenCount * 0.5); + if(startEntry < 0) + startEntry = 0; + if(startEntry >= (int)mRowVector.size() - screenCount) + startEntry = mRowVector.size() - screenCount; + } + + int y = cutoff; + + if(mRowVector.size() == 0) + { + Renderer::drawCenteredText("The list is empty.", 0, y, 0xFF0000FF, mFont); + return; + } + + int listCutoff = startEntry + screenCount; + if(listCutoff > (int)mRowVector.size()) + listCutoff = mRowVector.size(); + + Renderer::pushClipRect(getGlobalOffset(), getSize()); + + for(int i = startEntry; i < listCutoff; i++) + { + //draw selector bar + if(mSelection == i) + { + Renderer::drawRect(0, y, getSize().x, mFont->getHeight(), mSelectorColor); + } + + ListRow row = mRowVector.at((unsigned int)i); + + int x = mTextOffsetX - (mSelection == i ? mMarqueeOffset : 0); + unsigned int color = (mSelection == i && mSelectedTextColorOverride != 0) ? mSelectedTextColorOverride : row.color; + + if(mDrawCentered) + Renderer::drawCenteredText(row.name, x, y, color, mFont); + else + Renderer::drawText(row.name, x, y, color, mFont); + + y += entrySize; + } + + Renderer::popClipRect(); + + GuiComponent::onRender(); +} + +template +bool TextListComponent::input(InputConfig* config, Input input) +{ + if(mRowVector.size() > 0) + { + if(input.value != 0) + { + if(config->isMappedTo("down", input)) + { + setScrollDir(1); + scroll(); + return true; + } + + if(config->isMappedTo("up", input)) + { + setScrollDir(-1); + scroll(); + return true; + } + if(config->isMappedTo("pagedown", input)) + { + setScrollDir(10); + scroll(); + return true; + } + + if(config->isMappedTo("pageup", input)) + { + setScrollDir(-10); + scroll(); + return true; + } + }else{ + //if((button == InputManager::DOWN && mScrollDir > 0) || (button == InputManager::PAGEDOWN && mScrollDir > 0) || (button == InputManager::UP && mScrollDir < 0) || (button == InputManager::PAGEUP && mScrollDir < 0)) + if(config->isMappedTo("down", input) || config->isMappedTo("up", input) || config->isMappedTo("pagedown", input) || config->isMappedTo("pageup", input)) + { + stopScrolling(); + } + } + } + + return GuiComponent::input(config, input); +} + +template +void TextListComponent::setScrollDir(int val) +{ + mScrollDir = val; + mMarqueeOffset = 0; + mMarqueeTime = -MARQUEE_DELAY; +} + +template +void TextListComponent::stopScrolling() +{ + mScrollAccumulator = 0; + mScrolling = false; + mScrollDir = 0; +} + +template +void TextListComponent::update(int deltaTime) +{ + if(mScrollDir != 0) + { + mScrollAccumulator += deltaTime; + + if(!mScrolling) + { + if(mScrollAccumulator >= SCROLLDELAY) + { + mScrollAccumulator = SCROLLTIME; + mScrolling = true; + } + } + + if(mScrolling) + { + mScrollAccumulator += deltaTime; + + while(mScrollAccumulator >= SCROLLTIME) + { + mScrollAccumulator -= SCROLLTIME; + + scroll(); + } + } + }else{ + //if we're not scrolling and this object's text goes outside our size, marquee it! + std::string text = getSelectedName(); + int w; + mFont->sizeText(text, &w, NULL); + + //it's long enough to marquee + if(w - mMarqueeOffset > (int)getSize().x - 12) + { + mMarqueeTime += deltaTime; + while(mMarqueeTime > MARQUEE_SPEED) + { + mMarqueeOffset += MARQUEE_RATE; + mMarqueeTime -= MARQUEE_SPEED; + } + } + } + + GuiComponent::update(deltaTime); +} + +template +void TextListComponent::scroll() +{ + mSelection += mScrollDir; + + if(mSelection < 0) + { + if(mScrollDir < -1) + mSelection = 0; + else + mSelection += mRowVector.size(); + } + if(mSelection >= (int)mRowVector.size()) + { + if(mScrollDir > 1) + mSelection = (int)mRowVector.size() - 1; + else + mSelection -= mRowVector.size(); + } + + if(mScrollSound) + mScrollSound->play(); +} + +//list management stuff +template +void TextListComponent::addObject(std::string name, T obj, unsigned int color) +{ + ListRow row = {name, obj, color}; + mRowVector.push_back(row); +} + +template +void TextListComponent::clear() +{ + mRowVector.clear(); + mSelection = 0; + mMarqueeOffset = 0; + mMarqueeTime = -MARQUEE_DELAY; +} + +template +std::string TextListComponent::getSelectedName() +{ + if((int)mRowVector.size() > mSelection) + return mRowVector.at(mSelection).name; + else + return ""; +} + +template +T TextListComponent::getSelectedObject() +{ + if((int)mRowVector.size() > mSelection) + return mRowVector.at(mSelection).object; + else + return NULL; +} + +template +int TextListComponent::getSelection() +{ + return mSelection; +} + +template +bool TextListComponent::isScrolling() +{ + return mScrollDir != 0; +} + +template +void TextListComponent::setSelectorColor(unsigned int selectorColor) +{ + mSelectorColor = selectorColor; +} + +template +void TextListComponent::setSelectedTextColor(unsigned int selectedColor) +{ + mSelectedTextColorOverride = selectedColor; +} + +template +void TextListComponent::setCentered(bool centered) +{ + mDrawCentered = centered; +} + +template +void TextListComponent::setTextOffsetX(int textoffsetx) +{ + mTextOffsetX = textoffsetx; +} + +template +int TextListComponent::getObjectCount() +{ + return mRowVector.size(); +} + +template +T TextListComponent::getObject(int i) +{ + return mRowVector.at(i).object; +} + +template +void TextListComponent::setSelection(int i) +{ + mSelection = i; +} + +template +void TextListComponent::setScrollSound(std::shared_ptr & sound) +{ + mScrollSound = sound; +} + +template +void TextListComponent::setFont(Font* font) +{ + mFont = font; +} + +#endif diff --git a/src/components/GuiTheme.cpp b/src/components/ThemeComponent.cpp similarity index 85% rename from src/components/GuiTheme.cpp rename to src/components/ThemeComponent.cpp index 71f9765b5..56a5d42e1 100644 --- a/src/components/GuiTheme.cpp +++ b/src/components/ThemeComponent.cpp @@ -1,41 +1,41 @@ -#include "GuiTheme.h" +#include "ThemeComponent.h" #include "../MathExp.h" #include #include "GuiGameList.h" -#include "GuiImage.h" +#include "ImageComponent.h" #include #include #include "../Renderer.h" #include "../Log.h" -unsigned int GuiTheme::getColor(std::string name) +unsigned int ThemeComponent::getColor(std::string name) { return mColorMap[name]; } -bool GuiTheme::getBool(std::string name) +bool ThemeComponent::getBool(std::string name) { return mBoolMap[name]; } -float GuiTheme::getFloat(std::string name) +float ThemeComponent::getFloat(std::string name) { return mFloatMap[name]; } -std::shared_ptr & GuiTheme::getSound(std::string name) +std::shared_ptr & ThemeComponent::getSound(std::string name) { return mSoundMap[name]; } -std::string GuiTheme::getString(std::string name) +std::string ThemeComponent::getString(std::string name) { return mStringMap[name]; } -GuiBoxData GuiTheme::getBoxData() { return mBoxData; } +GuiBoxData ThemeComponent::getBoxData() { return mBoxData; } -Font* GuiTheme::getListFont() +Font* ThemeComponent::getListFont() { if(mListFont == NULL) return Renderer::getDefaultFont(Renderer::MEDIUM); @@ -43,7 +43,7 @@ Font* GuiTheme::getListFont() return mListFont; } -Font* GuiTheme::getDescriptionFont() +Font* ThemeComponent::getDescriptionFont() { if(mDescFont == NULL) return Renderer::getDefaultFont(Renderer::SMALL); @@ -51,7 +51,7 @@ Font* GuiTheme::getDescriptionFont() return mDescFont; } -Font* GuiTheme::getFastSelectFont() +Font* ThemeComponent::getFastSelectFont() { if(mFastSelectFont == NULL) return Renderer::getDefaultFont(Renderer::LARGE); @@ -59,7 +59,7 @@ Font* GuiTheme::getFastSelectFont() return mFastSelectFont; } -GuiTheme::GuiTheme(Window* window, bool detailed, std::string path) : Gui(window) +ThemeComponent::ThemeComponent(Window* window, bool detailed, std::string path) : GuiComponent(window) { mDetailed = detailed; @@ -84,12 +84,12 @@ GuiTheme::GuiTheme(Window* window, bool detailed, std::string path) : Gui(window readXML(path); } -GuiTheme::~GuiTheme() +ThemeComponent::~ThemeComponent() { deleteComponents(); } -void GuiTheme::setDefaults() +void ThemeComponent::setDefaults() { mColorMap["primary"] = 0x0000FFFF; mColorMap["secondary"] = 0x00FF00FF; @@ -144,14 +144,14 @@ void GuiTheme::setDefaults() } } -void GuiTheme::deleteComponents() +void ThemeComponent::deleteComponents() { - for(unsigned int i = 0; i < mComponentVector.size(); i++) + for(unsigned int i = 0; i < getChildCount(); i++) { - delete mComponentVector.at(i); + delete getChild(i); } - mComponentVector.clear(); + clearChildren(); //deletes fonts if any were created setDefaults(); @@ -159,7 +159,7 @@ void GuiTheme::deleteComponents() -void GuiTheme::readXML(std::string path) +void ThemeComponent::readXML(std::string path) { if(mPath == path) return; @@ -264,11 +264,11 @@ void GuiTheme::readXML(std::string path) } //recursively creates components -void GuiTheme::createComponentChildren(pugi::xml_node node, Gui* parent) +void ThemeComponent::createComponentChildren(pugi::xml_node node, GuiComponent* parent) { for(pugi::xml_node data = node.child("component"); data; data = data.next_sibling("component")) { - Gui* nextComp = createElement(data, parent); + GuiComponent* nextComp = createElement(data, parent); if(nextComp) createComponentChildren(data, nextComp); @@ -276,7 +276,7 @@ void GuiTheme::createComponentChildren(pugi::xml_node node, Gui* parent) } //takes an XML element definition and creates an object from it -Gui* GuiTheme::createElement(pugi::xml_node data, Gui* parent) +GuiComponent* ThemeComponent::createElement(pugi::xml_node data, GuiComponent* parent) { std::string type = data.child("type").text().get(); @@ -315,12 +315,12 @@ Gui* GuiTheme::createElement(pugi::xml_node data, Gui* parent) float ox = strToFloat(originX); float oy = strToFloat(originY); - GuiImage* comp = new GuiImage(mWindow, x, y, "", w, h, true); + ImageComponent* comp = new ImageComponent(mWindow, x, y, "", w, h, true); comp->setOrigin(ox, oy); comp->setTiling(tiled); comp->setImage(path); - mComponentVector.push_back(comp); + addChild(comp); return comp; } @@ -330,8 +330,11 @@ Gui* GuiTheme::createElement(pugi::xml_node data, Gui* parent) } //expands a file path (./ becomes the directory of this theme file, ~/ becomes $HOME/) -std::string GuiTheme::expandPath(std::string path) +std::string ThemeComponent::expandPath(std::string path) { + if(path.empty()) + return ""; + if(path[0] == '~') path = getHomePath() + path.substr(1, path.length() - 1); else if(path[0] == '.') @@ -341,7 +344,7 @@ std::string GuiTheme::expandPath(std::string path) } //takes a string containing a mathematical expression (possibly including variables) and resolves it to a float value -float GuiTheme::resolveExp(std::string str, float defaultVal) +float ThemeComponent::resolveExp(std::string str, float defaultVal) { if(str.empty()) return defaultVal; @@ -357,7 +360,7 @@ float GuiTheme::resolveExp(std::string str, float defaultVal) } //takes a string of hex and resolves it to an integer -unsigned int GuiTheme::resolveColor(std::string str, unsigned int defaultColor) +unsigned int ThemeComponent::resolveColor(std::string str, unsigned int defaultColor) { if(str.empty()) return defaultColor; @@ -381,7 +384,7 @@ unsigned int GuiTheme::resolveColor(std::string str, unsigned int defaultColor) } //splits a string in two at the first instance of the delimiter -void GuiTheme::splitString(std::string str, char delim, std::string* before, std::string* after) +void ThemeComponent::splitString(std::string str, char delim, std::string* before, std::string* after) { if(str.empty()) return; @@ -397,7 +400,7 @@ void GuiTheme::splitString(std::string str, char delim, std::string* before, std } //converts a string to a float -float GuiTheme::strToFloat(std::string str, float defaultVal) +float ThemeComponent::strToFloat(std::string str, float defaultVal) { if(str.empty()) return defaultVal; @@ -409,7 +412,7 @@ float GuiTheme::strToFloat(std::string str, float defaultVal) return ret; } -Font* GuiTheme::resolveFont(pugi::xml_node node, std::string defaultPath, unsigned int defaultSize) +Font* ThemeComponent::resolveFont(pugi::xml_node node, std::string defaultPath, unsigned int defaultSize) { if(!node) return NULL; @@ -430,34 +433,21 @@ Font* GuiTheme::resolveFont(pugi::xml_node node, std::string defaultPath, unsign return new Font(path, size); } -void GuiTheme::render() +void ThemeComponent::init() { - for(unsigned int i = 0; i < mComponentVector.size(); i++) - { - mComponentVector.at(i)->render(); - } -} - -void GuiTheme::init() -{ - for(unsigned int i = 0; i < mComponentVector.size(); i++) - { - mComponentVector.at(i)->init(); - } - //fonts are special if(mListFont) mListFont->init(); if(mDescFont) mDescFont->init(); if(mFastSelectFont) mFastSelectFont->init(); + + GuiComponent::init(); } -void GuiTheme::deinit() +void ThemeComponent::deinit() { - for(unsigned int i = 0; i < mComponentVector.size(); i++) - { - mComponentVector.at(i)->deinit(); - } + GuiComponent::deinit(); + //fonts are special if(mListFont) mListFont->deinit(); if(mDescFont) mDescFont->deinit(); if(mFastSelectFont) mFastSelectFont->deinit(); diff --git a/src/components/GuiTheme.h b/src/components/ThemeComponent.h similarity index 76% rename from src/components/GuiTheme.h rename to src/components/ThemeComponent.h index a0e8a8900..8b711c8ec 100644 --- a/src/components/GuiTheme.h +++ b/src/components/ThemeComponent.h @@ -1,27 +1,25 @@ -#ifndef _GUITHEME_H_ -#define _GUITHEME_H_ +#ifndef _THEMECOMPONENT_H_ +#define _THEMECOMPONENT_H_ #include -#include "../Gui.h" +#include "../GuiComponent.h" #include "../pugiXML/pugixml.hpp" #include "GuiBox.h" #include "../AudioManager.h" #include "../Font.h" -//This class loads an XML-defined list of Guis. -class GuiTheme : public Gui +//This class loads an XML-defined list of GuiComponents. +class ThemeComponent : public GuiComponent { public: - GuiTheme(Window* window, bool detailed, std::string path = ""); - ~GuiTheme(); + ThemeComponent(Window* window, bool detailed, std::string path = ""); + virtual ~ThemeComponent(); void readXML(std::string path); GuiBoxData getBoxData(); - void render(); - void init(); void deinit(); @@ -34,11 +32,12 @@ public: Font* getListFont(); Font* getDescriptionFont(); Font* getFastSelectFont(); + private: void setDefaults(); void deleteComponents(); - void createComponentChildren(pugi::xml_node node, Gui* parent); - Gui* createElement(pugi::xml_node data, Gui* parent); + void createComponentChildren(pugi::xml_node node, GuiComponent* parent); + GuiComponent* createElement(pugi::xml_node data, GuiComponent* parent); //utility functions std::string expandPath(std::string path); @@ -48,7 +47,6 @@ private: float strToFloat(std::string str, float defaultVal = 0.0f); Font* resolveFont(pugi::xml_node node, std::string defaultPath, unsigned int defaultSize); - std::vector mComponentVector; std::string mPath; bool mDetailed; @@ -59,6 +57,7 @@ private: std::map mStringMap; GuiBoxData mBoxData; + Font* mListFont; Font* mDescFont; Font* mFastSelectFont; diff --git a/src/main.cpp b/src/main.cpp index ccda7b387..64367bb05 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,4 +1,5 @@ //EmulationStation, a graphical front-end for ROM browsing. Created by Alec "Aloshi" Lofquist. +//http://www.aloshi.com #include #include @@ -12,6 +13,8 @@ #include "platform.h" #include "Log.h" #include "Window.h" +#include "EmulationStation.h" +#include "Settings.h" #ifdef _RPI_ #include @@ -19,15 +22,6 @@ #include -//these can be set by command-line arguments -bool PARSEGAMELISTONLY = false; -bool IGNOREGAMELIST = false; -bool DRAWFRAMERATE = false; -bool DONTSHOWEXIT = false; -bool DEBUG = false; -bool WINDOWED = false; -unsigned int DIMTIME = 30*1000; - namespace fs = boost::filesystem; int main(int argc, char* argv[]) @@ -48,27 +42,27 @@ int main(int argc, char* argv[]) i++; //skip the argument value }else if(strcmp(argv[i], "--gamelist-only") == 0) { - PARSEGAMELISTONLY = true; + Settings::getInstance()->setBool("PARSEGAMELISTONLY", true); }else if(strcmp(argv[i], "--ignore-gamelist") == 0) { - IGNOREGAMELIST = true; + Settings::getInstance()->setBool("IGNOREGAMELIST", true); }else if(strcmp(argv[i], "--draw-framerate") == 0) { - DRAWFRAMERATE = true; + Settings::getInstance()->setBool("DRAWFRAMERATE", true); }else if(strcmp(argv[i], "--no-exit") == 0) { - DONTSHOWEXIT = true; + Settings::getInstance()->setBool("DONTSHOWEXIT", true); }else if(strcmp(argv[i], "--debug") == 0) { - DEBUG = true; + Settings::getInstance()->setBool("DEBUG", true); Log::setReportingLevel(LogDebug); }else if(strcmp(argv[i], "--dimtime") == 0) { - DIMTIME = atoi(argv[i + 1]) * 1000; + Settings::getInstance()->setInt("DIMTIME", atoi(argv[i + 1]) * 1000); i++; //skip the argument value }else if(strcmp(argv[i], "--windowed") == 0) { - WINDOWED = true; + Settings::getInstance()->setBool("WINDOWED", true); }else if(strcmp(argv[i], "--help") == 0) { std::cout << "EmulationStation, a graphical front-end for ROM browsing.\n"; @@ -82,7 +76,7 @@ int main(int argc, char* argv[]) std::cout << "--debug even more logging\n"; std::cout << "--dimtime [seconds] time to wait before dimming the screen (default 30, use 0 for never)\n"; - #ifdef _DESKTOP_ + #ifdef USE_OPENGL_DESKTOP std::cout << "--windowed not fullscreen\n"; #endif @@ -110,6 +104,7 @@ int main(int argc, char* argv[]) //start the logger Log::open(); + LOG(LogInfo) << "EmulationStation - " << PROGRAM_VERSION_STRING; //the renderer also takes care of setting up SDL for input and sound bool renderInit = Renderer::init(width, height); @@ -195,9 +190,10 @@ int main(int argc, char* argv[]) lastTime = curTime; window.update(deltaTime); + Renderer::swapBuffers(); //swap here so we can read the last screen state during updates (see ImageComponent::copyScreen()) window.render(); - if(DRAWFRAMERATE) + if(Settings::getInstance()->getBool("DRAWFRAMERATE")) { static int timeElapsed = 0; static int nrOfFrames = 0; @@ -221,14 +217,15 @@ int main(int argc, char* argv[]) //sleeping entails setting a flag to start skipping frames //and initially drawing a black semi-transparent rect to dim the screen timeSinceLastEvent += deltaTime; - if(timeSinceLastEvent >= DIMTIME && DIMTIME != 0) + if(timeSinceLastEvent >= (unsigned int)Settings::getInstance()->getInt("DIMTIME") && Settings::getInstance()->getInt("DIMTIME") != 0) { sleeping = true; timeSinceLastEvent = 0; Renderer::drawRect(0, 0, Renderer::getScreenWidth(), Renderer::getScreenHeight(), 0x000000A0); + Renderer::swapBuffers(); } - Renderer::swapBuffers(); + Log::flush(); } diff --git a/src/platform.cpp b/src/platform.cpp index 33e7620e6..5690e5b9d 100644 --- a/src/platform.cpp +++ b/src/platform.cpp @@ -19,6 +19,10 @@ std::string getHomePath() if (envDir != nullptr && envPath != nullptr) { homePath = envDir; homePath += envPath; + + for(unsigned int i = 0; i < homePath.length(); i++) + if(homePath[i] == '\\') + homePath[i] = '/'; } } #else