From 80e33849b540ea02284ea6bdd42b411b39f021ca Mon Sep 17 00:00:00 2001 From: Aloshi Date: Mon, 27 May 2013 12:13:38 -0500 Subject: [PATCH 01/26] Fixed a few crashes, better support for higan Fixed AudioManager/InputManager unsigned vs signed comparison warnings. Fixed a FolderData sorting crash (I can't believe nobody's reported this). Fixed a GuiTheme crash for empty paths. Added the %ROM_RAW% tag, for the unescaped ROM name - useful for higan on windows. SystemData will now add folders that end in EXTENSION as GameDatas, and not recurse through them. Also useful for higan. --- README.md | 4 ++- src/AudioManager.cpp | 2 +- src/FolderData.cpp | 4 ++- src/GameData.cpp | 1 + src/InputManager.cpp | 2 +- src/SystemData.cpp | 58 ++++++++++++++++++++----------------- src/components/GuiTheme.cpp | 3 ++ 7 files changed, 44 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 9b9b5868f..47c63a4de 100644 --- a/README.md +++ b/README.md @@ -97,10 +97,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 081670e6c..c76e256b5 100644 --- a/src/AudioManager.cpp +++ b/src/AudioManager.cpp @@ -22,7 +22,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/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/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/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/SystemData.cpp b/src/SystemData.cpp index 9eb4e0866..e673041db 100644 --- a/src/SystemData.cpp +++ b/src/SystemData.cpp @@ -77,6 +77,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 +121,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 +161,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); } } } diff --git a/src/components/GuiTheme.cpp b/src/components/GuiTheme.cpp index 71f9765b5..1c9cb3093 100644 --- a/src/components/GuiTheme.cpp +++ b/src/components/GuiTheme.cpp @@ -332,6 +332,9 @@ 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) { + if(path.empty()) + return ""; + if(path[0] == '~') path = getHomePath() + path.substr(1, path.length() - 1); else if(path[0] == '.') From 582914b4ec17fe9f4425ecb7b2c947f33e090293 Mon Sep 17 00:00:00 2001 From: Aloshi Date: Mon, 27 May 2013 14:26:30 -0500 Subject: [PATCH 02/26] Putting all our eggs in the CMake basket. --- .gitignore | 13 +- .../EmulationStation_vs2010.sln | 20 -- .../EmulationStation_vs2010.vcxproj | 128 ------------ .../EmulationStation_vs2010.vcxproj.filters | 195 ------------------ .../EmulationStation_vs2010/lib_paths.props | 19 -- Makefile | 4 - Makefile.common | 25 --- Makefile.x86 | 4 - 8 files changed, 9 insertions(+), 399 deletions(-) delete mode 100644 EmulationStation_vs2010/EmulationStation_vs2010.sln delete mode 100644 EmulationStation_vs2010/EmulationStation_vs2010/EmulationStation_vs2010.vcxproj delete mode 100644 EmulationStation_vs2010/EmulationStation_vs2010/EmulationStation_vs2010.vcxproj.filters delete mode 100644 EmulationStation_vs2010/EmulationStation_vs2010/lib_paths.props delete mode 100644 Makefile delete mode 100644 Makefile.common delete mode 100644 Makefile.x86 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/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 From 961fccc3f3e38881d9507e4ae1466d4548e78be4 Mon Sep 17 00:00:00 2001 From: Aloshi Date: Mon, 27 May 2013 15:23:25 -0500 Subject: [PATCH 03/26] Updates to README.md Somewhat better build instructions, links to Windows dependencies. --- README.md | 50 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 47c63a4de..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 =========== From 4d31aac85e7d5c911980831cd0aa297973ff88c4 Mon Sep 17 00:00:00 2001 From: Aloshi Date: Sat, 1 Jun 2013 16:47:05 -0500 Subject: [PATCH 04/26] Added Vector2 class. --- CMakeLists.txt | 2 +- src/Vector2.h | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 src/Vector2.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ba96dd160..b28ab3909 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -127,6 +127,7 @@ set(ES_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/Renderer.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 @@ -171,7 +172,6 @@ set(ES_SOURCES ${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/pugiXML/pugixml.cpp diff --git a/src/Vector2.h b/src/Vector2.h new file mode 100644 index 000000000..925115eef --- /dev/null +++ b/src/Vector2.h @@ -0,0 +1,97 @@ +#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 Vector2f; + +#endif From 424fcb032994bb1838f0d5500fca3981abf4e9dc Mon Sep 17 00:00:00 2001 From: Aloshi Date: Sun, 2 Jun 2013 10:08:32 -0500 Subject: [PATCH 05/26] Basic GuiComponent refactor complete. --- CMakeLists.txt | 20 +- src/ComponentContainer.cpp | 34 ++ src/ComponentContainer.h | 25 ++ src/Gui.cpp | 17 - src/Gui.h | 33 -- src/GuiComponent.cpp | 91 +++++ src/GuiComponent.h | 37 ++ src/Window.cpp | 15 +- src/Window.h | 10 +- ...uiAnimation.cpp => AnimationComponent.cpp} | 23 +- .../{GuiAnimation.h => AnimationComponent.h} | 16 +- src/components/GuiBox.cpp | 41 +- src/components/GuiBox.h | 9 +- src/components/GuiDetectDevice.cpp | 12 +- src/components/GuiDetectDevice.h | 6 +- src/components/GuiFastSelect.cpp | 13 +- src/components/GuiFastSelect.h | 13 +- src/components/GuiGameList.cpp | 33 +- src/components/GuiGameList.h | 24 +- src/components/GuiInputConfig.cpp | 10 +- src/components/GuiInputConfig.h | 6 +- src/components/GuiList.cpp | 293 -------------- src/components/GuiList.h | 80 ---- src/components/GuiMenu.cpp | 11 +- src/components/GuiMenu.h | 12 +- .../{GuiImage.cpp => ImageComponent.cpp} | 51 ++- .../{GuiImage.h => ImageComponent.h} | 14 +- src/components/TextListComponent.h | 356 ++++++++++++++++++ .../{GuiTheme.cpp => ThemeComponent.cpp} | 85 ++--- .../{GuiTheme.h => ThemeComponent.h} | 21 +- 30 files changed, 767 insertions(+), 644 deletions(-) create mode 100644 src/ComponentContainer.cpp create mode 100644 src/ComponentContainer.h delete mode 100644 src/Gui.cpp delete mode 100644 src/Gui.h create mode 100644 src/GuiComponent.cpp create mode 100644 src/GuiComponent.h rename src/components/{GuiAnimation.cpp => AnimationComponent.cpp} (65%) rename src/components/{GuiAnimation.h => AnimationComponent.h} (58%) delete mode 100644 src/components/GuiList.cpp delete mode 100644 src/components/GuiList.h rename src/components/{GuiImage.cpp => ImageComponent.cpp} (82%) rename src/components/{GuiImage.h => ImageComponent.h} (87%) create mode 100644 src/components/TextListComponent.h rename src/components/{GuiTheme.cpp => ThemeComponent.cpp} (85%) rename src/components/{GuiTheme.h => ThemeComponent.h} (79%) diff --git a/CMakeLists.txt b/CMakeLists.txt index b28ab3909..3bfba83ca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -112,12 +112,13 @@ endif() #define basic sources and headers set(ES_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/AudioManager.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/ComponentContainer.h ${CMAKE_CURRENT_SOURCE_DIR}/src/EmulationStation.h ${CMAKE_CURRENT_SOURCE_DIR}/src/FileData.h ${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 @@ -131,26 +132,27 @@ set(ES_HEADERS ${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/ImageComponent.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/pugiXML/pugiconfig.hpp ${CMAKE_CURRENT_SOURCE_DIR}/src/pugiXML/pugixml.hpp ${CMAKE_CURRENT_SOURCE_DIR}/data/Resources.h ) set(ES_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/AudioManager.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ComponentContainer.cpp ${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 @@ -165,15 +167,15 @@ set(ES_SOURCES ${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/ImageComponent.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/GuiMenu.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiTheme.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/src/ComponentContainer.cpp b/src/ComponentContainer.cpp new file mode 100644 index 000000000..58dfb7a85 --- /dev/null +++ b/src/ComponentContainer.cpp @@ -0,0 +1,34 @@ +#include "ComponentContainer.h" +#include "GuiComponent.h" + +void ComponentContainer::addChild(GuiComponent* cmp) +{ + mChildren.push_back(cmp); +} + +void ComponentContainer::removeChild(GuiComponent* cmp) +{ + for(auto i = mChildren.begin(); i != mChildren.end(); i++) + { + if(*i == cmp) + { + mChildren.erase(i); + return; + } + } +} + +void ComponentContainer::clearChildren() +{ + mChildren.clear(); +} + +unsigned int ComponentContainer::getChildCount() +{ + return mChildren.size(); +} + +GuiComponent* ComponentContainer::getChild(unsigned int i) +{ + return mChildren.at(i); +} diff --git a/src/ComponentContainer.h b/src/ComponentContainer.h new file mode 100644 index 000000000..053554110 --- /dev/null +++ b/src/ComponentContainer.h @@ -0,0 +1,25 @@ +#ifndef _COMPONENTCONTAINER_H_ +#define _COMPONENTCONTAINER_H_ + +#include + +class GuiComponent; + +/** + Generic container for Components. +**/ + +class ComponentContainer +{ +public: + void addChild(GuiComponent* cmp); + void removeChild(GuiComponent* cmp); + void clearChildren(); + unsigned int getChildCount(); + GuiComponent* getChild(unsigned int i); + +private: + std::vector mChildren; +}; + +#endif \ No newline at end of file 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..938ef3233 --- /dev/null +++ b/src/GuiComponent.cpp @@ -0,0 +1,91 @@ +#include "GuiComponent.h" +#include "Window.h" + +GuiComponent::GuiComponent(Window* window) : mWindow(window), mParent(NULL) +{ +} + +GuiComponent::~GuiComponent() +{ + mWindow->removeGui(this); + + if(mParent) + mParent->removeChild(this); +} + +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() +{ + 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(); + } +} + +void GuiComponent::setParent(GuiComponent* parent) +{ + mParent = parent; +} + +GuiComponent* GuiComponent::getParent() +{ + return mParent; +} + +Vector2i GuiComponent::getGlobalOffset() +{ + if(mParent) + return mParent->getGlobalOffset() + mOffset; + else + return mOffset; +} + +Vector2i GuiComponent::getOffset() +{ + return mOffset; +} + +void GuiComponent::setOffset(Vector2i offset) +{ + mOffset = offset; +} + +void GuiComponent::setOffset(int x, int y) +{ + mOffset.x = x; + mOffset.y = y; +} diff --git a/src/GuiComponent.h b/src/GuiComponent.h new file mode 100644 index 000000000..7349adb65 --- /dev/null +++ b/src/GuiComponent.h @@ -0,0 +1,37 @@ +#ifndef _GUICOMPONENT_H_ +#define _GUICOMPONENT_H_ + +#include "InputConfig.h" +#include "Vector2.h" +#include "ComponentContainer.h" + +class Window; + +class GuiComponent : public ComponentContainer +{ +public: + GuiComponent(Window* window); + virtual ~GuiComponent(); + + //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); + virtual void update(int deltaTime); + virtual void render(); + virtual void init(); + virtual void deinit(); + + Vector2i getGlobalOffset(); + Vector2i getOffset(); + void setOffset(Vector2i offset); + void setOffset(int x, int y); + + void setParent(GuiComponent* parent); + GuiComponent* getParent(); + +protected: + Window* mWindow; + GuiComponent* mParent; + Vector2i mOffset; +}; + +#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/components/GuiAnimation.cpp b/src/components/AnimationComponent.cpp similarity index 65% rename from src/components/GuiAnimation.cpp rename to src/components/AnimationComponent.cpp index 1353fb8bb..3e071f6c1 100644 --- a/src/components/GuiAnimation.cpp +++ b/src/components/AnimationComponent.cpp @@ -1,6 +1,6 @@ -#include "GuiAnimation.h" +#include "AnimationComponent.h" -GuiAnimation::GuiAnimation() +AnimationComponent::AnimationComponent() { mMoveX = 0; mMoveY = 0; @@ -8,14 +8,14 @@ GuiAnimation::GuiAnimation() mFadeRate = 0; } -void GuiAnimation::move(int x, int y, int speed) +void AnimationComponent::move(int x, int y, int speed) { mMoveX = x; mMoveY = y; mMoveSpeed = speed; } -void GuiAnimation::fadeIn(int time) +void AnimationComponent::fadeIn(int time) { mOpacity = 0; setChildrenOpacity(0); @@ -23,7 +23,7 @@ void GuiAnimation::fadeIn(int time) mFadeRate = time; } -void GuiAnimation::fadeOut(int time) +void AnimationComponent::fadeOut(int time) { mOpacity = 255; setChildrenOpacity(255); @@ -31,7 +31,7 @@ void GuiAnimation::fadeOut(int time) mFadeRate = -time; } -void GuiAnimation::update(int deltaTime) +void AnimationComponent::update(int deltaTime) { float mult = deltaTime * 0.05f; @@ -69,21 +69,22 @@ void GuiAnimation::update(int deltaTime) } } -void GuiAnimation::addChild(GuiImage* gui) +void AnimationComponent::addChild(ImageComponent* gui) { mChildren.push_back(gui); } -void GuiAnimation::moveChildren(int offsetx, int offsety) +void AnimationComponent::moveChildren(int offsetx, int offsety) { + Vector2i move(offsetx, offsety); for(unsigned int i = 0; i < mChildren.size(); i++) { - GuiImage* comp = mChildren.at(i); - comp->setOffset(comp->getOffsetX() + offsetx, comp->getOffsetY() + offsety); + ImageComponent* comp = mChildren.at(i); + comp->setOffset(comp->getOffset() + move); } } -void GuiAnimation::setChildrenOpacity(unsigned char opacity) +void AnimationComponent::setChildrenOpacity(unsigned char opacity) { for(unsigned int i = 0; i < mChildren.size(); i++) { diff --git a/src/components/GuiAnimation.h b/src/components/AnimationComponent.h similarity index 58% rename from src/components/GuiAnimation.h rename to src/components/AnimationComponent.h index 767ca2ffc..f25c578e0 100644 --- a/src/components/GuiAnimation.h +++ b/src/components/AnimationComponent.h @@ -1,14 +1,14 @@ -#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 +class AnimationComponent { public: - GuiAnimation(); + AnimationComponent(); void move(int x, int y, int speed); void fadeIn(int time); @@ -16,12 +16,12 @@ 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); diff --git a/src/components/GuiBox.cpp b/src/components/GuiBox.cpp index f7abe8796..84d163b0f 100644 --- a/src/components/GuiBox.cpp +++ b/src/components/GuiBox.cpp @@ -1,11 +1,10 @@ #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); - + setOffset(Vector2i(offsetX, offsetY)); + mWidth = width; mHeight = height; } @@ -41,8 +40,7 @@ void GuiBox::setBackgroundImage(std::string path, bool tiled) mBackgroundImage.setOrigin(0, 0); mBackgroundImage.setResize(mWidth, mHeight, true); mBackgroundImage.setTiling(tiled); - mBackgroundImage.setOffsetX(getOffsetX()); - mBackgroundImage.setOffsetY(getOffsetY()); + mBackgroundImage.setOffset(getOffset()); mBackgroundImage.setImage(path); } @@ -60,53 +58,44 @@ void GuiBox::render() mBackgroundImage.render(); //left border - mHorizontalImage.setOffsetX(getOffsetX() - getHorizontalBorderWidth()); - mHorizontalImage.setOffsetY(getOffsetY()); + mHorizontalImage.setOffset(getOffset().x - getHorizontalBorderWidth(), getOffset().y); mHorizontalImage.setFlipX(false); mHorizontalImage.render(); - //Renderer::drawRect(getOffsetX() - getHorizontalBorderWidth(), getOffsetY(), getHorizontalBorderWidth(), mHeight, 0xFF0000); - + //right border - mHorizontalImage.setOffsetX(getOffsetX() + mWidth); - //same Y + mHorizontalImage.setOffset(getOffset().x + mWidth, getOffset().y); mHorizontalImage.setFlipX(true); mHorizontalImage.render(); - //Renderer::drawRect(getOffsetX() + mWidth, getOffsetY(), getHorizontalBorderWidth(), mHeight, 0xFF0000); - + //top border - mVerticalImage.setOffsetX(getOffsetX()); - mVerticalImage.setOffsetY(getOffsetY() - getVerticalBorderWidth()); + mVerticalImage.setOffset(getOffset().x, getOffset().y - getVerticalBorderWidth()); mVerticalImage.setFlipY(false); mVerticalImage.render(); - //Renderer::drawRect(getOffsetX(), getOffsetY() - getVerticalBorderWidth(), mWidth, getVerticalBorderWidth(), 0x00FF00); - + //bottom border - //same X - mVerticalImage.setOffsetY(getOffsetY() + mHeight); + mVerticalImage.setOffset(getOffset().x, getOffset().y + mHeight); 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(getOffset().x - getHorizontalBorderWidth(), getOffset().y - getVerticalBorderWidth()); mCornerImage.setFlipX(false); mCornerImage.setFlipY(false); mCornerImage.render(); //top right - mCornerImage.setOffsetX(getOffsetX() + mWidth); + mCornerImage.setOffset(getOffset().x + mWidth, mCornerImage.getOffset().y); mCornerImage.setFlipX(true); mCornerImage.render(); //bottom right - mCornerImage.setOffsetY(getOffsetY() + mHeight); + mCornerImage.setOffset(mCornerImage.getOffset().x, getOffset().y + mHeight); mCornerImage.setFlipY(true); mCornerImage.render(); //bottom left - mCornerImage.setOffsetX(getOffsetX() - getHorizontalBorderWidth()); + mCornerImage.setOffset(getOffset().x - getHorizontalBorderWidth(), mCornerImage.getOffset().y); mCornerImage.setFlipX(false); mCornerImage.render(); } diff --git a/src/components/GuiBox.h b/src/components/GuiBox.h index 89be516d5..6308f33da 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); @@ -34,8 +34,9 @@ public: void init(); void deinit(); + private: - GuiImage mBackgroundImage, mHorizontalImage, mVerticalImage, mCornerImage; + ImageComponent mBackgroundImage, mHorizontalImage, mVerticalImage, mCornerImage; int getHorizontalBorderWidth(); int getVerticalBorderWidth(); 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..17f655eeb 100644 --- a/src/components/GuiGameList.cpp +++ b/src/components/GuiGameList.cpp @@ -6,26 +6,26 @@ #include #include "../Log.h" -GuiGameList::GuiGameList(Window* window, bool useDetail) : Gui(window) +GuiGameList::GuiGameList(Window* window, bool useDetail) : GuiComponent(window) { 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 = new ImageComponent(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)); + mList = new TextListComponent(mWindow, 0, Renderer::getDefaultFont(Renderer::LARGE)->getHeight() + 2, Renderer::getDefaultFont(Renderer::MEDIUM)); mScreenshot = NULL; mImageAnimation = NULL; } @@ -94,7 +94,7 @@ void GuiGameList::render() 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()); + Renderer::drawWrappedText(desc, (int)(Renderer::getScreenWidth() * 0.03), mScreenshot->getOffset().y + mScreenshot->getHeight() + 12, (int)(Renderer::getScreenWidth() * (mTheme->getFloat("listOffsetX") - 0.03)), mTheme->getColor("description"), mTheme->getDescriptionFont()); } mScreenshot->render(); @@ -103,7 +103,7 @@ void GuiGameList::render() mList->render(); } -void GuiGameList::input(InputConfig* config, Input input) +bool GuiGameList::input(InputConfig* config, Input input) { mList->input(config, input); @@ -118,6 +118,7 @@ void GuiGameList::input(InputConfig* config, Input input) mFolderStack.push(mFolder); mFolder = (FolderData*)file; updateList(); + return true; }else{ mList->stopScrolling(); @@ -125,7 +126,7 @@ void GuiGameList::input(InputConfig* config, Input input) while(mTheme->getSound("menuSelect")->isPlaying()); mSystem->launchGame(mWindow, (GameData*)file); - return; + return true; } } @@ -139,6 +140,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 +150,12 @@ void GuiGameList::input(InputConfig* config, Input input) if(config->isMappedTo("right", input)) { setSystemId(mSystemId + 1); + return true; } if(config->isMappedTo("left", input)) { setSystemId(mSystemId - 1); + return true; } } @@ -158,12 +163,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 +182,10 @@ void GuiGameList::input(InputConfig* config, Input input) else clearDetailData(); } + return true; } + + return false; } void GuiGameList::updateList() @@ -235,11 +245,10 @@ 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); } diff --git a/src/components/GuiGameList.h b/src/components/GuiGameList.h index 860a8ee23..7a083b607 100644 --- a/src/components/GuiGameList.h +++ b/src/components/GuiGameList.h @@ -1,11 +1,11 @@ #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 #include #include "../SystemData.h" @@ -14,15 +14,15 @@ //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 +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(); @@ -46,10 +46,10 @@ private: int mSystemId; bool mDetailed; - GuiList* mList; - GuiImage* mScreenshot; - GuiAnimation* mImageAnimation; - GuiTheme* mTheme; + TextListComponent* mList; + ImageComponent* mScreenshot; + AnimationComponent* mImageAnimation; + ThemeComponent* mTheme; }; #endif diff --git a/src/components/GuiInputConfig.cpp b/src/components/GuiInputConfig.cpp index 9817cfd5a..4173fd881 100644 --- a/src/components/GuiInputConfig.cpp +++ b/src/components/GuiInputConfig.cpp @@ -9,7 +9,7 @@ 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" }; -GuiInputConfig::GuiInputConfig(Window* window, InputConfig* target) : Gui(window), mTargetConfig(target) +GuiInputConfig::GuiInputConfig(Window* window, InputConfig* target) : GuiComponent(window), mTargetConfig(target) { mCurInputId = 0; LOG(LogInfo) << "Configuring device " << target->getDeviceId(); @@ -20,10 +20,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) { @@ -43,7 +43,7 @@ void GuiInputConfig::input(InputConfig* config, Input input) if(config->getMappedTo(input).size() > 0) { mErrorMsg = "Already mapped!"; - return; + return true; } input.configured = true; @@ -59,6 +59,8 @@ void GuiInputConfig::input(InputConfig* config, Input input) mCurInputId = inputCount; } } + + return true; } void GuiInputConfig::render() diff --git a/src/components/GuiInputConfig.h b/src/components/GuiInputConfig.h index 02e023dc0..ddfec5a36 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(); 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..c3f1708ff 100644 --- a/src/components/GuiMenu.cpp +++ b/src/components/GuiMenu.cpp @@ -8,11 +8,11 @@ //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 +22,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) 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/GuiImage.cpp b/src/components/ImageComponent.cpp similarity index 82% rename from src/components/GuiImage.cpp rename to src/components/ImageComponent.cpp index 97a768735..92a3889da 100644 --- a/src/components/GuiImage.cpp +++ b/src/components/ImageComponent.cpp @@ -1,18 +1,18 @@ -#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; } +unsigned int ImageComponent::getWidth() { return mDrawWidth; } +unsigned int ImageComponent::getHeight() { return mDrawHeight; } -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 resizeExact) : GuiComponent(window) { mTextureID = 0; - setOffset(offsetX, offsetY); + setOffset(Vector2i(offsetX, offsetY)); //default origin is the center of image mOriginX = 0.5; @@ -36,12 +36,12 @@ 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)) @@ -171,7 +171,7 @@ void GuiImage::loadImage(std::string path) resize(); } -void GuiImage::resize() +void ImageComponent::resize() { mDrawWidth = mWidth; mDrawHeight = mHeight; @@ -206,7 +206,7 @@ void GuiImage::resize() } } -void GuiImage::unloadImage() +void ImageComponent::unloadImage() { if(mTextureID) { @@ -216,7 +216,7 @@ void GuiImage::unloadImage() } } -void GuiImage::setImage(std::string path) +void ImageComponent::setImage(std::string path) { if(mPath == path) return; @@ -229,13 +229,13 @@ void GuiImage::setImage(std::string path) } -void GuiImage::setOrigin(float originX, float originY) +void ImageComponent::setOrigin(float originX, float originY) { mOriginX = originX; mOriginY = originY; } -void GuiImage::setTiling(bool tile) +void ImageComponent::setTiling(bool tile) { mTiled = tile; @@ -243,7 +243,7 @@ void GuiImage::setTiling(bool tile) mResizeExact = false; } -void GuiImage::setResize(unsigned int width, unsigned int height, bool resizeExact) +void ImageComponent::setResize(unsigned int width, unsigned int height, bool resizeExact) { mResizeWidth = width; mResizeHeight = height; @@ -251,17 +251,17 @@ void GuiImage::setResize(unsigned int width, unsigned int height, bool resizeExa resize(); } -void GuiImage::setFlipX(bool flip) +void ImageComponent::setFlipX(bool flip) { mFlipX = flip; } -void GuiImage::setFlipY(bool flip) +void ImageComponent::setFlipY(bool flip) { mFlipY = flip; } -void GuiImage::render() +void ImageComponent::render() { if(mTextureID && getOpacity() > 0) { @@ -274,17 +274,17 @@ void GuiImage::render() float yCount = ((float)mResizeHeight/mHeight); Renderer::buildGLColorArray(colors, 0xFFFFFF00 | (getOpacity()), 6); - buildImageArray(getOffsetX(), getOffsetY(), points, texs, xCount, yCount); + buildImageArray(getOffset().x, getOffset().y, points, texs, xCount, yCount); }else{ Renderer::buildGLColorArray(colors, 0xFFFFFF00 | (getOpacity()), 6); - buildImageArray(getOffsetX(), getOffsetY(), points, texs); + buildImageArray(getOffset().x, getOffset().y, points, texs); } drawImageArray(points, texs, colors, 6); } } -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; @@ -322,14 +322,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 +353,21 @@ void GuiImage::drawImageArray(GLfloat* points, GLfloat* texs, GLubyte* colors, u glDisable(GL_BLEND); } -void GuiImage::init() +void ImageComponent::init() { if(!mPath.empty()) loadImage(mPath); } -void GuiImage::deinit() +void ImageComponent::deinit() { unloadImage(); } -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; } diff --git a/src/components/GuiImage.h b/src/components/ImageComponent.h similarity index 87% rename from src/components/GuiImage.h rename to src/components/ImageComponent.h index 9454ce798..1c31a0320 100644 --- a/src/components/GuiImage.h +++ b/src/components/ImageComponent.h @@ -1,22 +1,22 @@ -#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, //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 resizeExact = false); + virtual ~ImageComponent(); 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) @@ -39,12 +39,12 @@ public: unsigned char getOpacity(); void setOpacity(unsigned char opacity); + private: unsigned int mResizeWidth, mResizeHeight; float mOriginX, mOriginY; bool mResizeExact, mTiled, mFlipX, mFlipY; - int mOffsetX, mOffsetY; unsigned char mOpacity; void loadImage(std::string path); diff --git a/src/components/TextListComponent.h b/src/components/TextListComponent.h new file mode 100644 index 000000000..d8ea537ee --- /dev/null +++ b/src/components/TextListComponent.h @@ -0,0 +1,356 @@ +#ifndef _TEXTLISTCOMPONENT_H_ +#define _TEXTLISTCOMPONENT_H_ + +#include "../Renderer.h" +#include "../Font.h" +#include "../GuiComponent.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 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 render(); + + 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); + +private: + static const int SCROLLDELAY = 507; + static const int SCROLLTIME = 200; + + 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; + 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)); + + mTextOffsetX = 0; + + mFont = font; + mSelectorColor = 0x000000FF; + mSelectedTextColorOverride = 0; + mScrollSound = NULL; + mDrawCentered = true; +} + +template +TextListComponent::~TextListComponent() +{ +} + +template +void TextListComponent::render() +{ + const int cutoff = getOffset().y; + 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(getOffset().x, y, Renderer::getScreenWidth(), mFont->getHeight(), mSelectorColor); + } + + ListRow row = mRowVector.at((unsigned int)i); + + if(mDrawCentered) + Renderer::drawCenteredText(row.name, getOffset().x + mTextOffsetX, y, (mSelection == i && mSelectedTextColorOverride != 0) ? mSelectedTextColorOverride : row.color, mFont); + else + Renderer::drawText(row.name, getOffset().x + mTextOffsetX, y, (mSelection == i && mSelectedTextColorOverride != 0) ? mSelectedTextColorOverride : row.color, mFont); + + y += entrySize; + } +} + +template +bool TextListComponent::input(InputConfig* config, Input input) +{ + if(mRowVector.size() > 0) + { + if(input.value != 0) + { + if(config->isMappedTo("down", input)) + { + mScrollDir = 1; + scroll(); + return true; + } + + if(config->isMappedTo("up", input)) + { + mScrollDir = -1; + scroll(); + return true; + } + if(config->isMappedTo("pagedown", input)) + { + mScrollDir = 10; + scroll(); + return true; + } + + if(config->isMappedTo("pageup", input)) + { + mScrollDir = -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 false; +} + +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(); + } + } + } +} + +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; +} + +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 1c9cb3093..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,7 +330,7 @@ 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 ""; @@ -344,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; @@ -360,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; @@ -384,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; @@ -400,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; @@ -412,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; @@ -433,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 79% rename from src/components/GuiTheme.h rename to src/components/ThemeComponent.h index a0e8a8900..d79372586 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 +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; From 628b0b6958f8f305002e9e556d66b754c01f9a59 Mon Sep 17 00:00:00 2001 From: Aloshi Date: Sun, 2 Jun 2013 11:11:29 -0500 Subject: [PATCH 06/26] Put ComponentContainer directly in GuiComponent. Necessary for parenting to allow getOffset(), etc. --- src/ComponentContainer.cpp | 34 ---------------------------- src/ComponentContainer.h | 25 --------------------- src/GuiComponent.cpp | 45 ++++++++++++++++++++++++++++++++++++++ src/GuiComponent.h | 10 +++++++-- 4 files changed, 53 insertions(+), 61 deletions(-) delete mode 100644 src/ComponentContainer.cpp delete mode 100644 src/ComponentContainer.h diff --git a/src/ComponentContainer.cpp b/src/ComponentContainer.cpp deleted file mode 100644 index 58dfb7a85..000000000 --- a/src/ComponentContainer.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "ComponentContainer.h" -#include "GuiComponent.h" - -void ComponentContainer::addChild(GuiComponent* cmp) -{ - mChildren.push_back(cmp); -} - -void ComponentContainer::removeChild(GuiComponent* cmp) -{ - for(auto i = mChildren.begin(); i != mChildren.end(); i++) - { - if(*i == cmp) - { - mChildren.erase(i); - return; - } - } -} - -void ComponentContainer::clearChildren() -{ - mChildren.clear(); -} - -unsigned int ComponentContainer::getChildCount() -{ - return mChildren.size(); -} - -GuiComponent* ComponentContainer::getChild(unsigned int i) -{ - return mChildren.at(i); -} diff --git a/src/ComponentContainer.h b/src/ComponentContainer.h deleted file mode 100644 index 053554110..000000000 --- a/src/ComponentContainer.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef _COMPONENTCONTAINER_H_ -#define _COMPONENTCONTAINER_H_ - -#include - -class GuiComponent; - -/** - Generic container for Components. -**/ - -class ComponentContainer -{ -public: - void addChild(GuiComponent* cmp); - void removeChild(GuiComponent* cmp); - void clearChildren(); - unsigned int getChildCount(); - GuiComponent* getChild(unsigned int i); - -private: - std::vector mChildren; -}; - -#endif \ No newline at end of file diff --git a/src/GuiComponent.cpp b/src/GuiComponent.cpp index 938ef3233..e0ebbce6e 100644 --- a/src/GuiComponent.cpp +++ b/src/GuiComponent.cpp @@ -1,5 +1,6 @@ #include "GuiComponent.h" #include "Window.h" +#include "Log.h" GuiComponent::GuiComponent(Window* window) : mWindow(window), mParent(NULL) { @@ -89,3 +90,47 @@ void GuiComponent::setOffset(int x, int y) mOffset.x = x; mOffset.y = y; } + +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); +} diff --git a/src/GuiComponent.h b/src/GuiComponent.h index 7349adb65..d4bf6f69b 100644 --- a/src/GuiComponent.h +++ b/src/GuiComponent.h @@ -3,11 +3,10 @@ #include "InputConfig.h" #include "Vector2.h" -#include "ComponentContainer.h" class Window; -class GuiComponent : public ComponentContainer +class GuiComponent { public: GuiComponent(Window* window); @@ -28,10 +27,17 @@ public: 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: Window* mWindow; GuiComponent* mParent; Vector2i mOffset; + std::vector mChildren; }; #endif From 1cef2f543333f0dca6a5123103ee4320624b7435 Mon Sep 17 00:00:00 2001 From: Aloshi Date: Sun, 2 Jun 2013 11:17:13 -0500 Subject: [PATCH 07/26] Write PROGRAM_VERSION_STRING at start of log. --- src/main.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index bb1084cd3..e51ec0291 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,6 +11,7 @@ #include "platform.h" #include "Log.h" #include "Window.h" +#include "EmulationStation.h" #ifdef _RPI_ #include @@ -109,6 +110,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); From 24512c0c9f351821c413537fbe02cccd266e24ad Mon Sep 17 00:00:00 2001 From: Aloshi Date: Sun, 2 Jun 2013 14:34:50 -0500 Subject: [PATCH 08/26] Component rendering now uses OpenGL translation. You don't need to take into account offset when rendering anymore. --- src/GuiComponent.cpp | 32 ++++++++++++++++++++---------- src/GuiComponent.h | 13 ++++++++++++ src/Renderer.h | 7 ++++++- src/Renderer_draw_gl.cpp | 10 ++++++++++ src/Renderer_init_sdlgl.cpp | 2 +- src/components/ImageComponent.cpp | 8 +++++--- src/components/ImageComponent.h | 5 +++-- src/components/TextListComponent.h | 20 ++++++++++++------- src/components/ThemeComponent.h | 2 +- 9 files changed, 74 insertions(+), 25 deletions(-) diff --git a/src/GuiComponent.cpp b/src/GuiComponent.cpp index e0ebbce6e..0ad40bbed 100644 --- a/src/GuiComponent.cpp +++ b/src/GuiComponent.cpp @@ -1,6 +1,7 @@ #include "GuiComponent.h" #include "Window.h" #include "Log.h" +#include "Renderer.h" GuiComponent::GuiComponent(Window* window) : mWindow(window), mParent(NULL) { @@ -34,6 +35,15 @@ void GuiComponent::update(int deltaTime) } void GuiComponent::render() +{ + Renderer::translate(mOffset); + + onRender(); + + Renderer::translate(-mOffset); +} + +void GuiComponent::onRender() { for(unsigned int i = 0; i < getChildCount(); i++) { @@ -57,16 +67,7 @@ void GuiComponent::deinit() } } -void GuiComponent::setParent(GuiComponent* parent) -{ - mParent = parent; -} - -GuiComponent* GuiComponent::getParent() -{ - return mParent; -} - +//Offset stuff. Vector2i GuiComponent::getGlobalOffset() { if(mParent) @@ -91,6 +92,7 @@ void GuiComponent::setOffset(int x, int y) mOffset.y = y; } +//Children stuff. void GuiComponent::addChild(GuiComponent* cmp) { mChildren.push_back(cmp); @@ -134,3 +136,13 @@ 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 index d4bf6f69b..5ab217295 100644 --- a/src/GuiComponent.h +++ b/src/GuiComponent.h @@ -12,11 +12,21 @@ 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, but instead want 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(); Vector2i getGlobalOffset(); @@ -34,6 +44,9 @@ public: 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; diff --git a/src/Renderer.h b/src/Renderer.h index 7cbce0b39..c75294256 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,12 @@ 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 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..9b7120e64 100644 --- a/src/Renderer_draw_gl.cpp +++ b/src/Renderer_draw_gl.cpp @@ -26,6 +26,16 @@ namespace Renderer { } } + void translatef(float x, float y) + { + glTranslatef(x, y, 0); + } + + void translate(Vector2i offset) + { + translatef((float)offset.x, (float)offset.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 8480d8eb0..f38b89b1b 100644 --- a/src/Renderer_init_sdlgl.cpp +++ b/src/Renderer_init_sdlgl.cpp @@ -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/components/ImageComponent.cpp b/src/components/ImageComponent.cpp index 92a3889da..19644f95b 100644 --- a/src/components/ImageComponent.cpp +++ b/src/components/ImageComponent.cpp @@ -261,7 +261,7 @@ void ImageComponent::setFlipY(bool flip) mFlipY = flip; } -void ImageComponent::render() +void ImageComponent::onRender() { if(mTextureID && getOpacity() > 0) { @@ -274,14 +274,16 @@ void ImageComponent::render() float yCount = ((float)mResizeHeight/mHeight); Renderer::buildGLColorArray(colors, 0xFFFFFF00 | (getOpacity()), 6); - buildImageArray(getOffset().x, getOffset().y, points, texs, xCount, yCount); + buildImageArray(0, 0, points, texs, xCount, yCount); }else{ Renderer::buildGLColorArray(colors, 0xFFFFFF00 | (getOpacity()), 6); - buildImageArray(getOffset().x, getOffset().y, points, texs); + buildImageArray(0, 0, points, texs); } drawImageArray(points, texs, colors, 6); } + + GuiComponent::onRender(); } void ImageComponent::buildImageArray(int posX, int posY, GLfloat* points, GLfloat* texs, float px, float py) diff --git a/src/components/ImageComponent.h b/src/components/ImageComponent.h index 1c31a0320..476b4bfd3 100644 --- a/src/components/ImageComponent.h +++ b/src/components/ImageComponent.h @@ -31,8 +31,6 @@ public: 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(); @@ -40,6 +38,9 @@ public: unsigned char getOpacity(); void setOpacity(unsigned char opacity); +protected: + void onRender(); + private: unsigned int mResizeWidth, mResizeHeight; float mOriginX, mOriginY; diff --git a/src/components/TextListComponent.h b/src/components/TextListComponent.h index d8ea537ee..9999cfae6 100644 --- a/src/components/TextListComponent.h +++ b/src/components/TextListComponent.h @@ -21,7 +21,6 @@ public: bool input(InputConfig* config, Input input); void update(int deltaTime); - void render(); void addObject(std::string name, T obj, unsigned int color = 0xFF0000); void clear(); @@ -44,6 +43,9 @@ public: void setFont(Font* f); +protected: + void onRender(); + private: static const int SCROLLDELAY = 507; static const int SCROLLTIME = 200; @@ -96,9 +98,9 @@ TextListComponent::~TextListComponent() } template -void TextListComponent::render() +void TextListComponent::onRender() { - const int cutoff = getOffset().y; + const int cutoff = 0; const int entrySize = mFont->getHeight() + 5; int startEntry = 0; @@ -133,18 +135,20 @@ void TextListComponent::render() //draw selector bar if(mSelection == i) { - Renderer::drawRect(getOffset().x, y, Renderer::getScreenWidth(), mFont->getHeight(), mSelectorColor); + Renderer::drawRect(0, y, Renderer::getScreenWidth(), mFont->getHeight(), mSelectorColor); } ListRow row = mRowVector.at((unsigned int)i); if(mDrawCentered) - Renderer::drawCenteredText(row.name, getOffset().x + mTextOffsetX, y, (mSelection == i && mSelectedTextColorOverride != 0) ? mSelectedTextColorOverride : row.color, mFont); + Renderer::drawCenteredText(row.name, mTextOffsetX, y, (mSelection == i && mSelectedTextColorOverride != 0) ? mSelectedTextColorOverride : row.color, mFont); else - Renderer::drawText(row.name, getOffset().x + mTextOffsetX, y, (mSelection == i && mSelectedTextColorOverride != 0) ? mSelectedTextColorOverride : row.color, mFont); + Renderer::drawText(row.name, mTextOffsetX, y, (mSelection == i && mSelectedTextColorOverride != 0) ? mSelectedTextColorOverride : row.color, mFont); y += entrySize; } + + GuiComponent::onRender(); } template @@ -189,7 +193,7 @@ bool TextListComponent::input(InputConfig* config, Input input) } } - return false; + return GuiComponent::input(config, input); } template @@ -228,6 +232,8 @@ void TextListComponent::update(int deltaTime) } } } + + GuiComponent::update(deltaTime); } template diff --git a/src/components/ThemeComponent.h b/src/components/ThemeComponent.h index d79372586..8b711c8ec 100644 --- a/src/components/ThemeComponent.h +++ b/src/components/ThemeComponent.h @@ -9,7 +9,7 @@ #include "../AudioManager.h" #include "../Font.h" -//This class loads an XML-defined list of Guis. +//This class loads an XML-defined list of GuiComponents. class ThemeComponent : public GuiComponent { public: From 826624481af2b54cd1c6447a943e706a8046d5c5 Mon Sep 17 00:00:00 2001 From: Aloshi Date: Sun, 2 Jun 2013 16:05:29 -0500 Subject: [PATCH 09/26] Refactored ImageComponent to use Vector2. --- src/components/ImageComponent.cpp | 127 ++++++++++++------------------ src/components/ImageComponent.h | 18 ++--- src/main.cpp | 3 +- 3 files changed, 62 insertions(+), 86 deletions(-) diff --git a/src/components/ImageComponent.cpp b/src/components/ImageComponent.cpp index 19644f95b..4f8ad075c 100644 --- a/src/components/ImageComponent.cpp +++ b/src/components/ImageComponent.cpp @@ -5,29 +5,27 @@ #include "../Log.h" #include "../Renderer.h" -unsigned int ImageComponent::getWidth() { return mDrawWidth; } -unsigned int ImageComponent::getHeight() { return mDrawHeight; } +unsigned int ImageComponent::getWidth() { return mSize.x; } +unsigned int ImageComponent::getHeight() { return mSize.y; } -ImageComponent::ImageComponent(Window* window, int offsetX, int offsetY, std::string path, unsigned int resizeWidth, unsigned int resizeHeight, bool resizeExact) : GuiComponent(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(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; @@ -46,7 +44,7 @@ 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 +110,6 @@ void ImageComponent::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 +134,8 @@ void ImageComponent::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); @@ -173,36 +148,32 @@ void ImageComponent::loadImage(std::string path) 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); } } @@ -231,8 +202,8 @@ void ImageComponent::setImage(std::string path) void ImageComponent::setOrigin(float originX, float originY) { - mOriginX = originX; - mOriginY = originY; + mOrigin.x = originX; + mOrigin.y = originY; } void ImageComponent::setTiling(bool tile) @@ -240,14 +211,14 @@ void ImageComponent::setTiling(bool tile) mTiled = tile; if(mTiled) - mResizeExact = false; + mAllowUpscale = false; } -void ImageComponent::setResize(unsigned int width, unsigned int height, bool resizeExact) +void ImageComponent::setResize(unsigned int width, unsigned int height, bool allowUpscale) { - mResizeWidth = width; - mResizeHeight = height; - mResizeExact = resizeExact; + mTargetSize.x = width; + mTargetSize.y = height; + mAllowUpscale = allowUpscale; resize(); } @@ -270,9 +241,9 @@ void ImageComponent::onRender() if(mTiled) { - float xCount = ((float)mResizeWidth/mWidth); - float yCount = ((float)mResizeHeight/mHeight); - + float xCount = (float)mTargetSize.x / mTextureSize.x; + float yCount = (float)mTargetSize.y / mTextureSize.y; + Renderer::buildGLColorArray(colors, 0xFFFFFF00 | (getOpacity()), 6); buildImageArray(0, 0, points, texs, xCount, yCount); }else{ @@ -288,13 +259,13 @@ void ImageComponent::onRender() 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) * px; points[1] = posY - (mSize.y * mOrigin.y) * py; + points[2] = posX - (mSize.x * mOrigin.x) * px; points[3] = posY + (mSize.y * (1 - mOrigin.y)) * py; + points[4] = posX + (mSize.x * (1 - mOrigin.x)) * px; points[5] = posY - (mSize.y * mOrigin.y) * py; - 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)) * px; points[7] = posY - (mSize.y * mOrigin.y) * py; + points[8] = posX - (mSize.x * mOrigin.x) * px; points[9] = posY + (mSize.y * (1 - mOrigin.y)) * py; + points[10] = posX + (mSize.x * (1 -mOrigin.x)) * px; points[11] = posY + (mSize.y * (1 - mOrigin.y)) * py; @@ -359,11 +330,15 @@ void ImageComponent::init() { if(!mPath.empty()) loadImage(mPath); + + GuiComponent::init(); } void ImageComponent::deinit() { unloadImage(); + + GuiComponent::deinit(); } bool ImageComponent::hasImage() diff --git a/src/components/ImageComponent.h b/src/components/ImageComponent.h index 476b4bfd3..dab89128d 100644 --- a/src/components/ImageComponent.h +++ b/src/components/ImageComponent.h @@ -13,15 +13,15 @@ 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). - ImageComponent(Window* window, int offsetX = 0, int offsetY = 0, std::string path = "", unsigned int maxWidth = 0, unsigned int maxHeight = 0, bool resizeExact = false); + 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(); 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); @@ -42,9 +42,12 @@ protected: void onRender(); private: - unsigned int mResizeWidth, mResizeHeight; - float mOriginX, mOriginY; - bool mResizeExact, mTiled, mFlipX, mFlipY; + Vector2 mSize; + Vector2 mTargetSize; + Vector2 mTextureSize; + Vector2f mOrigin; + + bool mAllowUpscale, mTiled, mFlipX, mFlipY; unsigned char mOpacity; @@ -56,9 +59,6 @@ private: std::string mPath; - unsigned int mWidth, mHeight; //Our rendered size. - unsigned int mDrawWidth, mDrawHeight; - GLuint mTextureID; }; diff --git a/src/main.cpp b/src/main.cpp index e51ec0291..789f81241 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 @@ -82,7 +83,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 From 7faf9fca535c571861437956338192d10f1ff75f Mon Sep 17 00:00:00 2001 From: Aloshi Date: Sun, 2 Jun 2013 17:33:49 -0500 Subject: [PATCH 10/26] Added Size and getSize() to GuiComponent. Added setClipRect and clearClipRect to Renderer. TextListComponent finally has a marquee. :) --- src/GuiComponent.cpp | 5 +++ src/GuiComponent.h | 5 ++- src/Renderer.h | 4 +++ src/Renderer_draw_gl.cpp | 16 +++++++++ src/Vector2.h | 1 + src/components/GuiBox.cpp | 39 ++++++++++++--------- src/components/GuiBox.h | 7 ++-- src/components/GuiGameList.cpp | 2 +- src/components/ImageComponent.cpp | 26 ++++++++------ src/components/ImageComponent.h | 10 +++--- src/components/TextListComponent.h | 56 ++++++++++++++++++++++++++---- 11 files changed, 126 insertions(+), 45 deletions(-) diff --git a/src/GuiComponent.cpp b/src/GuiComponent.cpp index 0ad40bbed..eef473de7 100644 --- a/src/GuiComponent.cpp +++ b/src/GuiComponent.cpp @@ -92,6 +92,11 @@ void GuiComponent::setOffset(int x, int y) mOffset.y = y; } +Vector2u GuiComponent::getSize() +{ + return mSize; +} + //Children stuff. void GuiComponent::addChild(GuiComponent* cmp) { diff --git a/src/GuiComponent.h b/src/GuiComponent.h index 5ab217295..14ae66868 100644 --- a/src/GuiComponent.h +++ b/src/GuiComponent.h @@ -20,7 +20,7 @@ public: 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, but instead want the protected method onRender. + //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. @@ -34,6 +34,8 @@ public: void setOffset(Vector2i offset); void setOffset(int x, int y); + Vector2u getSize(); + void setParent(GuiComponent* parent); GuiComponent* getParent(); @@ -50,6 +52,7 @@ protected: Window* mWindow; GuiComponent* mParent; Vector2i mOffset; + Vector2u mSize; std::vector mChildren; }; diff --git a/src/Renderer.h b/src/Renderer.h index c75294256..f73f2939a 100644 --- a/src/Renderer.h +++ b/src/Renderer.h @@ -35,6 +35,10 @@ namespace Renderer void translatef(float x, float y); void translate(Vector2i offset); + void setClipRect(int x, int y, unsigned int w, unsigned int h); + void setClipRect(Vector2i offset, Vector2u size); + void clearClipRect(); + 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 9b7120e64..d583bf2e4 100644 --- a/src/Renderer_draw_gl.cpp +++ b/src/Renderer_draw_gl.cpp @@ -36,6 +36,22 @@ namespace Renderer { translatef((float)offset.x, (float)offset.y); } + void setClipRect(int x, int y, unsigned int w, unsigned int h) + { + glScissor(x, y, w, h); + glEnable(GL_SCISSOR_TEST); + } + + void setClipRect(Vector2i offset, Vector2u size) + { + setClipRect(offset.x, offset.y, size.x, size.y); + } + + void clearClipRect() + { + glDisable(GL_SCISSOR_TEST); + } + void drawRect(int x, int y, int w, int h, unsigned int color) { #ifdef USE_OPENGL_ES diff --git a/src/Vector2.h b/src/Vector2.h index 925115eef..08e8b85b6 100644 --- a/src/Vector2.h +++ b/src/Vector2.h @@ -92,6 +92,7 @@ bool operator !=(const Vector2& left, const Vector2& right) } typedef Vector2 Vector2i; +typedef Vector2 Vector2u; typedef Vector2 Vector2f; #endif diff --git a/src/components/GuiBox.cpp b/src/components/GuiBox.cpp index 84d163b0f..3db364d88 100644 --- a/src/components/GuiBox.cpp +++ b/src/components/GuiBox.cpp @@ -5,8 +5,7 @@ GuiBox::GuiBox(Window* window, int offsetX, int offsetY, unsigned int width, uns { setOffset(Vector2i(offsetX, offsetY)); - mWidth = width; - mHeight = height; + mSize = Vector2u(width, height); } void GuiBox::setData(GuiBoxData data) @@ -23,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) @@ -32,15 +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.setOffset(getOffset()); + mBackgroundImage.setOffset(0, 0); mBackgroundImage.setImage(path); } @@ -53,51 +52,53 @@ void GuiBox::setCornerImage(std::string path) mCornerImage.setImage(path); } -void GuiBox::render() +void GuiBox::onRender() { mBackgroundImage.render(); //left border - mHorizontalImage.setOffset(getOffset().x - getHorizontalBorderWidth(), getOffset().y); + mHorizontalImage.setOffset(-getHorizontalBorderWidth(), 0); mHorizontalImage.setFlipX(false); mHorizontalImage.render(); //right border - mHorizontalImage.setOffset(getOffset().x + mWidth, getOffset().y); + mHorizontalImage.setOffset(mSize.x, 0); mHorizontalImage.setFlipX(true); mHorizontalImage.render(); //top border - mVerticalImage.setOffset(getOffset().x, getOffset().y - getVerticalBorderWidth()); + mVerticalImage.setOffset(0, -getVerticalBorderWidth()); mVerticalImage.setFlipY(false); mVerticalImage.render(); //bottom border - mVerticalImage.setOffset(getOffset().x, getOffset().y + mHeight); + mVerticalImage.setOffset(0, mSize.y); mVerticalImage.setFlipY(true); mVerticalImage.render(); //corner top left - mCornerImage.setOffset(getOffset().x - getHorizontalBorderWidth(), getOffset().y - getVerticalBorderWidth()); + mCornerImage.setOffset(-getHorizontalBorderWidth(), -getVerticalBorderWidth()); mCornerImage.setFlipX(false); mCornerImage.setFlipY(false); mCornerImage.render(); //top right - mCornerImage.setOffset(getOffset().x + mWidth, mCornerImage.getOffset().y); + mCornerImage.setOffset(mSize.x, mCornerImage.getOffset().y); mCornerImage.setFlipX(true); mCornerImage.render(); //bottom right - mCornerImage.setOffset(mCornerImage.getOffset().x, getOffset().y + mHeight); + mCornerImage.setOffset(mCornerImage.getOffset().x, mSize.y); mCornerImage.setFlipY(true); mCornerImage.render(); //bottom left - mCornerImage.setOffset(getOffset().x - getHorizontalBorderWidth(), mCornerImage.getOffset().y); + mCornerImage.setOffset(-getHorizontalBorderWidth(), mCornerImage.getOffset().y); mCornerImage.setFlipX(false); mCornerImage.render(); + + GuiComponent::onRender(); } void GuiBox::init() @@ -105,6 +106,8 @@ void GuiBox::init() mVerticalImage.init(); mHorizontalImage.init(); mCornerImage.init(); + + GuiComponent::init(); } void GuiBox::deinit() @@ -112,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 6308f33da..ad1325e5c 100644 --- a/src/components/GuiBox.h +++ b/src/components/GuiBox.h @@ -30,18 +30,17 @@ public: bool hasBackground(); - void render(); - void init(); void deinit(); +protected: + void onRender(); + private: ImageComponent mBackgroundImage, mHorizontalImage, mVerticalImage, mCornerImage; int getHorizontalBorderWidth(); int getVerticalBorderWidth(); - - unsigned int mWidth, mHeight; }; #endif diff --git a/src/components/GuiGameList.cpp b/src/components/GuiGameList.cpp index 17f655eeb..4b605f73e 100644 --- a/src/components/GuiGameList.cpp +++ b/src/components/GuiGameList.cpp @@ -94,7 +94,7 @@ void GuiGameList::render() std::string desc = game->getDescription(); if(!desc.empty()) - Renderer::drawWrappedText(desc, (int)(Renderer::getScreenWidth() * 0.03), mScreenshot->getOffset().y + mScreenshot->getHeight() + 12, (int)(Renderer::getScreenWidth() * (mTheme->getFloat("listOffsetX") - 0.03)), mTheme->getColor("description"), mTheme->getDescriptionFont()); + Renderer::drawWrappedText(desc, (int)(Renderer::getScreenWidth() * 0.03), mScreenshot->getOffset().y + mScreenshot->getSize().y + 12, (int)(Renderer::getScreenWidth() * (mTheme->getFloat("listOffsetX") - 0.03)), mTheme->getColor("description"), mTheme->getDescriptionFont()); } mScreenshot->render(); diff --git a/src/components/ImageComponent.cpp b/src/components/ImageComponent.cpp index 4f8ad075c..6edca1092 100644 --- a/src/components/ImageComponent.cpp +++ b/src/components/ImageComponent.cpp @@ -5,8 +5,7 @@ #include "../Log.h" #include "../Renderer.h" -unsigned int ImageComponent::getWidth() { return mSize.x; } -unsigned int ImageComponent::getHeight() { return mSize.y; } +Vector2u ImageComponent::getTextureSize() { return mTextureSize; } ImageComponent::ImageComponent(Window* window, int offsetX, int offsetY, std::string path, unsigned int resizeWidth, unsigned int resizeHeight, bool allowUpscale) : GuiComponent(window) { @@ -175,6 +174,11 @@ void ImageComponent::resize() if(resizeScale.y) mSize.y = (int)(mSize.y * resizeScale.y); } + + if(mTiled) + { + mSize = mTargetSize; + } } void ImageComponent::unloadImage() @@ -212,6 +216,8 @@ void ImageComponent::setTiling(bool tile) if(mTiled) mAllowUpscale = false; + + resize(); } void ImageComponent::setResize(unsigned int width, unsigned int height, bool allowUpscale) @@ -241,8 +247,8 @@ void ImageComponent::onRender() if(mTiled) { - float xCount = (float)mTargetSize.x / mTextureSize.x; - float yCount = (float)mTargetSize.y / mTextureSize.y; + float xCount = (float)mSize.x / mTextureSize.x; + float yCount = (float)mSize.y / mTextureSize.y; Renderer::buildGLColorArray(colors, 0xFFFFFF00 | (getOpacity()), 6); buildImageArray(0, 0, points, texs, xCount, yCount); @@ -259,13 +265,13 @@ void ImageComponent::onRender() void ImageComponent::buildImageArray(int posX, int posY, GLfloat* points, GLfloat* texs, float px, float py) { - points[0] = posX - (mSize.x * mOrigin.x) * px; points[1] = posY - (mSize.y * mOrigin.y) * py; - points[2] = posX - (mSize.x * mOrigin.x) * px; points[3] = posY + (mSize.y * (1 - mOrigin.y)) * py; - points[4] = posX + (mSize.x * (1 - mOrigin.x)) * px; points[5] = posY - (mSize.y * mOrigin.y) * 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 + (mSize.x * (1 - mOrigin.x)) * px; points[7] = posY - (mSize.y * mOrigin.y) * py; - points[8] = posX - (mSize.x * mOrigin.x) * px; points[9] = posY + (mSize.y * (1 - mOrigin.y)) * py; - points[10] = posX + (mSize.x * (1 -mOrigin.x)) * px; points[11] = posY + (mSize.y * (1 - mOrigin.y)) * 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)); diff --git a/src/components/ImageComponent.h b/src/components/ImageComponent.h index dab89128d..cef220dae 100644 --- a/src/components/ImageComponent.h +++ b/src/components/ImageComponent.h @@ -26,8 +26,9 @@ public: 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(); @@ -42,9 +43,8 @@ protected: void onRender(); private: - Vector2 mSize; - Vector2 mTargetSize; - Vector2 mTextureSize; + Vector2u mTargetSize; + Vector2u mTextureSize; Vector2f mOrigin; bool mAllowUpscale, mTiled, mFlipX, mFlipY; diff --git a/src/components/TextListComponent.h b/src/components/TextListComponent.h index 9999cfae6..a60b008e9 100644 --- a/src/components/TextListComponent.h +++ b/src/components/TextListComponent.h @@ -10,8 +10,12 @@ #include #include "../Sound.h" +#define MARQUEE_DELAY 600 +#define MARQUEE_SPEED 16 +#define MARQUEE_RATE 3 + //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). +//TODO - add truncation to text rendering if name exceeds a maximum width (a trailing elipses, perhaps). Marquee would be nice too. template class TextListComponent : public GuiComponent { @@ -51,10 +55,14 @@ private: 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; @@ -83,6 +91,9 @@ TextListComponent::TextListComponent(Window* window, int offsetX, int offsetY setOffset(Vector2i(offsetX, offsetY)); + mSize = Vector2u(Renderer::getScreenWidth() - getOffset().x, Renderer::getScreenHeight() - getOffset().y); + mMarqueeOffset = 0; + mMarqueeTime = -MARQUEE_DELAY; mTextOffsetX = 0; mFont = font; @@ -140,10 +151,17 @@ void TextListComponent::onRender() ListRow row = mRowVector.at((unsigned int)i); + int x = mTextOffsetX - (mSelection == i ? mMarqueeOffset : 0); + unsigned int color = (mSelection == i && mSelectedTextColorOverride != 0) ? mSelectedTextColorOverride : row.color; + + Renderer::setClipRect(getOffset(), getSize()); + if(mDrawCentered) - Renderer::drawCenteredText(row.name, mTextOffsetX, y, (mSelection == i && mSelectedTextColorOverride != 0) ? mSelectedTextColorOverride : row.color, mFont); + Renderer::drawCenteredText(row.name, x, y, color, mFont); else - Renderer::drawText(row.name, mTextOffsetX, y, (mSelection == i && mSelectedTextColorOverride != 0) ? mSelectedTextColorOverride : row.color, mFont); + Renderer::drawText(row.name, x, y, color, mFont); + + Renderer::clearClipRect(); y += entrySize; } @@ -160,27 +178,27 @@ bool TextListComponent::input(InputConfig* config, Input input) { if(config->isMappedTo("down", input)) { - mScrollDir = 1; + setScrollDir(1); scroll(); return true; } if(config->isMappedTo("up", input)) { - mScrollDir = -1; + setScrollDir(-1); scroll(); return true; } if(config->isMappedTo("pagedown", input)) { - mScrollDir = 10; + setScrollDir(10); scroll(); return true; } if(config->isMappedTo("pageup", input)) { - mScrollDir = -10; + setScrollDir(-10); scroll(); return true; } @@ -196,6 +214,14 @@ bool TextListComponent::input(InputConfig* config, Input input) return GuiComponent::input(config, input); } +template +void TextListComponent::setScrollDir(int val) +{ + mScrollDir = val; + mMarqueeOffset = 0; + mMarqueeTime = -MARQUEE_DELAY; +} + template void TextListComponent::stopScrolling() { @@ -231,6 +257,22 @@ void TextListComponent::update(int deltaTime) 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); From 3a6f2e8e3599d1a08bd17ea1cd95302520a3291f Mon Sep 17 00:00:00 2001 From: Aloshi Date: Sun, 2 Jun 2013 17:44:26 -0500 Subject: [PATCH 11/26] Remove ComponentContainer from CMakeLists. --- CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3bfba83ca..d471265b5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -112,7 +112,6 @@ endif() #define basic sources and headers set(ES_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/AudioManager.h - ${CMAKE_CURRENT_SOURCE_DIR}/src/ComponentContainer.h ${CMAKE_CURRENT_SOURCE_DIR}/src/EmulationStation.h ${CMAKE_CURRENT_SOURCE_DIR}/src/FileData.h ${CMAKE_CURRENT_SOURCE_DIR}/src/FolderData.h @@ -148,7 +147,6 @@ set(ES_HEADERS ) set(ES_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/AudioManager.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/ComponentContainer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/FolderData.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Font.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/GameData.cpp From f577a72c23a2662001375bde07b9ecb03fb52c02 Mon Sep 17 00:00:00 2001 From: Aloshi Date: Sun, 2 Jun 2013 19:18:26 -0500 Subject: [PATCH 12/26] Re-added input config fix that went missing... --- src/components/GuiInputConfig.cpp | 33 ++++++++++++++++++++++++------- src/components/GuiInputConfig.h | 1 + 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/components/GuiInputConfig.cpp b/src/components/GuiInputConfig.cpp index 4173fd881..82c213553 100644 --- a/src/components/GuiInputConfig.cpp +++ b/src/components/GuiInputConfig.cpp @@ -9,7 +9,7 @@ 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" }; -GuiInputConfig::GuiInputConfig(Window* window, InputConfig* target) : GuiComponent(window), mTargetConfig(target) +GuiInputConfig::GuiInputConfig(Window* window, InputConfig* target) : GuiComponent(window), mTargetConfig(target), mCanSkip(false) { mCurInputId = 0; LOG(LogInfo) << "Configuring device " << target->getDeviceId(); @@ -38,8 +38,15 @@ bool 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!"; @@ -53,14 +60,15 @@ bool 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 true; + return false; } void GuiInputConfig::render() @@ -80,10 +88,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 A to skip", textWidth, y, 0x0000AAFF, font); + } } if(!mErrorMsg.empty()) diff --git a/src/components/GuiInputConfig.h b/src/components/GuiInputConfig.h index ddfec5a36..972b50c83 100644 --- a/src/components/GuiInputConfig.h +++ b/src/components/GuiInputConfig.h @@ -17,6 +17,7 @@ private: std::string mErrorMsg; InputConfig* mTargetConfig; int mCurInputId; + bool mCanSkip; }; #endif From 023bc44abd1b584d4e8f8b84dc5bf9cafdad8f21 Mon Sep 17 00:00:00 2001 From: Aloshi Date: Fri, 14 Jun 2013 06:26:04 -0500 Subject: [PATCH 13/26] Fixed a warning. --- src/Font.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Font.cpp b/src/Font.cpp index 02eff9d3b..2a0b34b38 100644 --- a/src/Font.cpp +++ b/src/Font.cpp @@ -341,7 +341,7 @@ void Font::drawText(std::string text, int startx, int starty, int color) 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 +352,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() From aec15ba0a1030a4a2c686425edb0695bb4ee1431 Mon Sep 17 00:00:00 2001 From: Aloshi Date: Fri, 14 Jun 2013 07:34:12 -0500 Subject: [PATCH 14/26] Refactored Font to use a Vertex struct and Vector2. Fixed InputConfig error messages dumping to console and not logging. Fixed skipped inputs being saved. --- src/Font.cpp | 105 ++++++++++-------------------- src/InputConfig.cpp | 8 ++- src/components/GuiInputConfig.cpp | 2 +- 3 files changed, 43 insertions(+), 72 deletions(-) diff --git a/src/Font.cpp b/src/Font.cpp index 2a0b34b38..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,8 +302,7 @@ 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; } 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/components/GuiInputConfig.cpp b/src/components/GuiInputConfig.cpp index 82c213553..61b8ffe3c 100644 --- a/src/components/GuiInputConfig.cpp +++ b/src/components/GuiInputConfig.cpp @@ -101,7 +101,7 @@ void GuiInputConfig::render() if(Renderer::getScreenWidth() / 2.5f > textWidth) textWidth = (int)(Renderer::getScreenWidth() / 2.5f); - Renderer::drawText("press A to skip", textWidth, y, 0x0000AAFF, font); + Renderer::drawText("press Accept to skip", textWidth, y, 0x0000AAFF, font); } } From bf84945010c5011a8324f73316e0fa0655bd10ec Mon Sep 17 00:00:00 2001 From: Aloshi Date: Fri, 14 Jun 2013 10:16:16 -0500 Subject: [PATCH 15/26] Fixed XMLReader system path processing. getHomePath() now uses forward slash as a path separator on all platforms. --- src/XMLReader.cpp | 14 ++++++-------- src/platform.cpp | 4 ++++ 2 files changed, 10 insertions(+), 8 deletions(-) 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/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 From 861297ae25fd2fb496fe0509403ff8e96beef10a Mon Sep 17 00:00:00 2001 From: Aloshi Date: Fri, 14 Jun 2013 10:48:13 -0500 Subject: [PATCH 16/26] Added initial TextComponent. Reworked GuiGameList to use a TextComponent for description. Changed set/clearClipRect to push/popClipRect. Fixed Y coordinate in the clip rect functions. Sorta fixed AnimationComponent being totally out of whack with VSync off. --- CMakeLists.txt | 2 + src/Renderer.h | 6 +-- src/Renderer_draw_gl.cpp | 39 +++++++++++++++--- src/Vector2.h | 11 ++++- src/components/AnimationComponent.cpp | 59 +++++++++++++++------------ src/components/AnimationComponent.h | 4 ++ src/components/GuiGameList.cpp | 49 ++++++++++++++-------- src/components/GuiGameList.h | 6 ++- src/components/TextComponent.cpp | 59 +++++++++++++++++++++++++++ src/components/TextComponent.h | 27 ++++++++++++ src/components/TextListComponent.h | 8 ++-- 11 files changed, 211 insertions(+), 59 deletions(-) create mode 100644 src/components/TextComponent.cpp create mode 100644 src/components/TextComponent.h diff --git a/CMakeLists.txt b/CMakeLists.txt index d471265b5..64a542eb1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -133,6 +133,7 @@ set(ES_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/XMLReader.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/AnimationComponent.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/ImageComponent.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 @@ -167,6 +168,7 @@ set(ES_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/XMLReader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/AnimationComponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/ImageComponent.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 diff --git a/src/Renderer.h b/src/Renderer.h index f73f2939a..f70b8c83b 100644 --- a/src/Renderer.h +++ b/src/Renderer.h @@ -35,9 +35,9 @@ namespace Renderer void translatef(float x, float y); void translate(Vector2i offset); - void setClipRect(int x, int y, unsigned int w, unsigned int h); - void setClipRect(Vector2i offset, Vector2u size); - void clearClipRect(); + 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); diff --git a/src/Renderer_draw_gl.cpp b/src/Renderer_draw_gl.cpp index d583bf2e4..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; @@ -36,20 +39,44 @@ namespace Renderer { translatef((float)offset.x, (float)offset.y); } - void setClipRect(int x, int y, unsigned int w, unsigned int h) + void pushClipRect(int x, int y, unsigned int w, unsigned int h) { - glScissor(x, y, w, 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 setClipRect(Vector2i offset, Vector2u size) + void pushClipRect(Vector2i pos, Vector2u size) { - setClipRect(offset.x, offset.y, size.x, size.y); + pushClipRect(pos.x, pos.y, size.x, size.y); } - void clearClipRect() + void popClipRect() { - glDisable(GL_SCISSOR_TEST); + 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) diff --git a/src/Vector2.h b/src/Vector2.h index 08e8b85b6..672c8ab3e 100644 --- a/src/Vector2.h +++ b/src/Vector2.h @@ -27,7 +27,6 @@ public: T y; }; - template Vector2 operator -(const Vector2& right) { @@ -95,4 +94,14 @@ 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/components/AnimationComponent.cpp b/src/components/AnimationComponent.cpp index 3e071f6c1..31c4c327e 100644 --- a/src/components/AnimationComponent.cpp +++ b/src/components/AnimationComponent.cpp @@ -6,6 +6,8 @@ AnimationComponent::AnimationComponent() mMoveY = 0; mMoveSpeed = 0; mFadeRate = 0; + mOpacity = 0; + mAccumulator = 0; } void AnimationComponent::move(int x, int y, int speed) @@ -31,41 +33,46 @@ void AnimationComponent::fadeOut(int time) mFadeRate = -time; } +//this should really be fixed at the system loop level... void AnimationComponent::update(int deltaTime) { - float mult = deltaTime * 0.05f; - - if(mMoveX != 0 || mMoveY != 0) + mAccumulator += deltaTime; + while(mAccumulator >= ANIMATION_TICK_SPEED) { - int offsetx = (mMoveX > mMoveSpeed) ? mMoveSpeed : mMoveX; - int offsety = (mMoveY > mMoveSpeed) ? mMoveSpeed : mMoveY; + mAccumulator -= ANIMATION_TICK_SPEED; - 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) + if(mMoveX != 0 || mMoveY != 0) { - mFadeRate = 0; - opacity = 255; + 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(opacity < 0) + if(mFadeRate != 0) { - mFadeRate = 0; - opacity = 0; - } + int opacity = (int)mOpacity + mFadeRate; + if(opacity > 255) + { + mFadeRate = 0; + opacity = 255; + } - mOpacity = (unsigned char)opacity; - setChildrenOpacity((unsigned char)opacity); + if(opacity < 0) + { + mFadeRate = 0; + opacity = 0; + } + + mOpacity = (unsigned char)opacity; + setChildrenOpacity((unsigned char)opacity); + } } } diff --git a/src/components/AnimationComponent.h b/src/components/AnimationComponent.h index f25c578e0..99cbd35b7 100644 --- a/src/components/AnimationComponent.h +++ b/src/components/AnimationComponent.h @@ -5,6 +5,8 @@ #include "ImageComponent.h" #include +#define ANIMATION_TICK_SPEED 16 + class AnimationComponent { public: @@ -28,6 +30,8 @@ private: int mFadeRate; int mMoveX, mMoveY, mMoveSpeed; + + int mAccumulator; }; #endif diff --git a/src/components/GuiGameList.cpp b/src/components/GuiGameList.cpp index 4b605f73e..9765a2cde 100644 --- a/src/components/GuiGameList.cpp +++ b/src/components/GuiGameList.cpp @@ -6,7 +6,12 @@ #include #include "../Log.h" -GuiGameList::GuiGameList(Window* window, bool useDetail) : GuiComponent(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) { mDetailed = useDetail; @@ -19,7 +24,7 @@ GuiGameList::GuiGameList(Window* window, bool useDetail) : GuiComponent(window) { mList = new TextListComponent(mWindow, (int)(Renderer::getScreenWidth() * mTheme->getFloat("listOffsetX")), Renderer::getDefaultFont(Renderer::LARGE)->getHeight() + 2, Renderer::getDefaultFont(Renderer::MEDIUM)); - mScreenshot = new ImageComponent(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 = 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")); mImageAnimation = new AnimationComponent(); @@ -30,6 +35,9 @@ GuiGameList::GuiGameList(Window* window, bool useDetail) : GuiComponent(window) mImageAnimation = NULL; } + 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)); + setSystemId(0); } @@ -41,8 +49,9 @@ GuiGameList::~GuiGameList() { delete mImageAnimation; delete mScreenshot; - delete mTheme; } + + delete mTheme; } void GuiGameList::setSystemId(int id) @@ -86,18 +95,14 @@ 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->getOffset().y + mScreenshot->getSize().y + 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(); @@ -118,6 +123,7 @@ bool GuiGameList::input(InputConfig* config, Input input) mFolderStack.push(mFolder); mFolder = (FolderData*)file; updateList(); + updateDetailData(); return true; }else{ mList->stopScrolling(); @@ -251,6 +257,9 @@ void GuiGameList::updateTheme() 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()); } } @@ -261,15 +270,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(""); } diff --git a/src/components/GuiGameList.h b/src/components/GuiGameList.h index 7a083b607..29e16a713 100644 --- a/src/components/GuiGameList.h +++ b/src/components/GuiGameList.h @@ -6,6 +6,7 @@ #include "ImageComponent.h" #include "ThemeComponent.h" #include "AnimationComponent.h" +#include "TextComponent.h" #include #include #include "../SystemData.h" @@ -13,7 +14,7 @@ #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. +//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: @@ -48,8 +49,11 @@ private: TextListComponent* mList; ImageComponent* mScreenshot; + TextComponent mDescription; AnimationComponent* mImageAnimation; ThemeComponent* mTheme; + + Vector2i getImagePos(); }; #endif diff --git a/src/components/TextComponent.cpp b/src/components/TextComponent.cpp new file mode 100644 index 000000000..af707ec21 --- /dev/null +++ b/src/components/TextComponent.cpp @@ -0,0 +1,59 @@ +#include "TextComponent.h" +#include "../Renderer.h" +#include "../Log.h" + +TextComponent::TextComponent(Window* window) : GuiComponent(window), mFont(NULL), mColor(0x000000FF) +{ +} + +TextComponent::TextComponent(Window* window, const std::string& text, Font* font, Vector2i pos, Vector2u size) : GuiComponent(window), + mFont(NULL), mColor(0x000000FF) +{ + setText(text); + setFont(font); + setBox(pos, size); +} + +void TextComponent::setBox(Vector2i pos, Vector2u size) +{ + setOffset(pos); + setExtent(size); +} + +void TextComponent::setExtent(Vector2u size) +{ + mSize = size; +} + +void TextComponent::setFont(Font* font) +{ + mFont = font; +} + +void TextComponent::setColor(unsigned int color) +{ + mColor = color; +} + +void TextComponent::setText(const std::string& text) +{ + mText = text; +} + +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(getOffset(), getSize()); + + Renderer::drawWrappedText(mText, 0, 0, mSize.x, mColor, font); + + Renderer::popClipRect(); + + GuiComponent::onRender(); +} diff --git a/src/components/TextComponent.h b/src/components/TextComponent.h new file mode 100644 index 000000000..c66e59ac0 --- /dev/null +++ b/src/components/TextComponent.h @@ -0,0 +1,27 @@ +#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); + void setText(const std::string& text); + void setColor(unsigned int color); + + void onRender(); + +private: + unsigned int mColor; + Font* mFont; + std::string mText; +}; + +#endif diff --git a/src/components/TextListComponent.h b/src/components/TextListComponent.h index a60b008e9..b3bdbb057 100644 --- a/src/components/TextListComponent.h +++ b/src/components/TextListComponent.h @@ -141,6 +141,8 @@ void TextListComponent::onRender() if(listCutoff > (int)mRowVector.size()) listCutoff = mRowVector.size(); + Renderer::pushClipRect(getOffset(), getSize()); + for(int i = startEntry; i < listCutoff; i++) { //draw selector bar @@ -154,18 +156,16 @@ void TextListComponent::onRender() int x = mTextOffsetX - (mSelection == i ? mMarqueeOffset : 0); unsigned int color = (mSelection == i && mSelectedTextColorOverride != 0) ? mSelectedTextColorOverride : row.color; - Renderer::setClipRect(getOffset(), getSize()); - if(mDrawCentered) Renderer::drawCenteredText(row.name, x, y, color, mFont); else Renderer::drawText(row.name, x, y, color, mFont); - Renderer::clearClipRect(); - y += entrySize; } + Renderer::popClipRect(); + GuiComponent::onRender(); } From 1b71abd44c06f77d07e3587dd5b1e7379c71ffa5 Mon Sep 17 00:00:00 2001 From: Aloshi Date: Sat, 15 Jun 2013 13:06:52 -0500 Subject: [PATCH 17/26] Fixed marquee effect staying on when changing systems. --- src/components/TextListComponent.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/TextListComponent.h b/src/components/TextListComponent.h index b3bdbb057..d1335f861 100644 --- a/src/components/TextListComponent.h +++ b/src/components/TextListComponent.h @@ -10,7 +10,7 @@ #include #include "../Sound.h" -#define MARQUEE_DELAY 600 +#define MARQUEE_DELAY 900 #define MARQUEE_SPEED 16 #define MARQUEE_RATE 3 @@ -315,6 +315,8 @@ void TextListComponent::clear() { mRowVector.clear(); mSelection = 0; + mMarqueeOffset = 0; + mMarqueeTime = -MARQUEE_DELAY; } template From da6ae9ac1027e2cdf0568b70090b9d80427f06ba Mon Sep 17 00:00:00 2001 From: Aloshi Date: Sun, 16 Jun 2013 16:23:04 -0500 Subject: [PATCH 18/26] Initial screen transition when switching systems. --- src/Renderer_init_sdlgl.cpp | 3 ++- src/components/GuiGameList.cpp | 38 +++++++++++++++++++++++++++++- src/components/GuiGameList.h | 5 ++++ src/components/ImageComponent.cpp | 25 ++++++++++++++++++++ src/components/ImageComponent.h | 2 ++ src/components/TextListComponent.h | 4 ++-- 6 files changed, 73 insertions(+), 4 deletions(-) diff --git a/src/Renderer_init_sdlgl.cpp b/src/Renderer_init_sdlgl.cpp index f3500538d..d4d28a061 100644 --- a/src/Renderer_init_sdlgl.cpp +++ b/src/Renderer_init_sdlgl.cpp @@ -71,7 +71,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 | (WINDOWED ? 0 : SDL_FULLSCREEN)); if(sdlScreen == NULL) { diff --git a/src/components/GuiGameList.cpp b/src/components/GuiGameList.cpp index 9765a2cde..aad66bc10 100644 --- a/src/components/GuiGameList.cpp +++ b/src/components/GuiGameList.cpp @@ -11,7 +11,8 @@ 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) +GuiGameList::GuiGameList(Window* window, bool useDetail) : GuiComponent(window), + mDescription(window), mTransitionImage(window, 0, 0, "", Renderer::getScreenWidth(), Renderer::getScreenHeight(), true) { mDetailed = useDetail; @@ -38,11 +39,23 @@ GuiGameList::GuiGameList(Window* window, bool useDetail) : GuiComponent(window), 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) @@ -83,6 +96,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(); @@ -106,6 +126,10 @@ void GuiGameList::render() } mList->render(); + + Renderer::translate(-mOffset); + + mTransitionImage.render(); } bool GuiGameList::input(InputConfig* config, Input input) @@ -156,11 +180,13 @@ bool 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; } } @@ -345,5 +371,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 29e16a713..7f51a0f15 100644 --- a/src/components/GuiGameList.h +++ b/src/components/GuiGameList.h @@ -39,6 +39,8 @@ private: void updateList(); void updateTheme(); void clearDetailData(); + void doTransition(int dir); + std::string getThemeFile(); SystemData* mSystem; @@ -53,6 +55,9 @@ private: AnimationComponent* mImageAnimation; ThemeComponent* mTheme; + ImageComponent mTransitionImage; + AnimationComponent mTransitionAnimation; + Vector2i getImagePos(); }; diff --git a/src/components/ImageComponent.cpp b/src/components/ImageComponent.cpp index 6edca1092..040348cff 100644 --- a/src/components/ImageComponent.cpp +++ b/src/components/ImageComponent.cpp @@ -354,3 +354,28 @@ bool ImageComponent::hasImage() unsigned char ImageComponent::getOpacity() { return mOpacity; } void ImageComponent::setOpacity(unsigned char opacity) { mOpacity = opacity; } + +void ImageComponent::copyScreen() +{ + unloadImage(); + + glReadBuffer(GL_FRONT); + + glGenTextures(1, &mTextureID); + glBindTexture(GL_TEXTURE_2D, mTextureID); + + int width = Renderer::getScreenWidth(); + int height = Renderer::getScreenHeight(); + 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/ImageComponent.h b/src/components/ImageComponent.h index cef220dae..3cf0204b1 100644 --- a/src/components/ImageComponent.h +++ b/src/components/ImageComponent.h @@ -18,6 +18,8 @@ public: 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. diff --git a/src/components/TextListComponent.h b/src/components/TextListComponent.h index b3bdbb057..3490d3577 100644 --- a/src/components/TextListComponent.h +++ b/src/components/TextListComponent.h @@ -141,14 +141,14 @@ void TextListComponent::onRender() if(listCutoff > (int)mRowVector.size()) listCutoff = mRowVector.size(); - Renderer::pushClipRect(getOffset(), getSize()); + Renderer::pushClipRect(getGlobalOffset(), getSize()); for(int i = startEntry; i < listCutoff; i++) { //draw selector bar if(mSelection == i) { - Renderer::drawRect(0, y, Renderer::getScreenWidth(), mFont->getHeight(), mSelectorColor); + Renderer::drawRect(0, y, getSize().x, mFont->getHeight(), mSelectorColor); } ListRow row = mRowVector.at((unsigned int)i); From e8465baaba04afaa554980b47cbab99e0bf37c77 Mon Sep 17 00:00:00 2001 From: Aloshi Date: Mon, 17 Jun 2013 14:01:03 -0500 Subject: [PATCH 19/26] Moved externs for command-line args into a Settings singleton. --- CMakeLists.txt | 2 ++ src/Renderer_init_sdlgl.cpp | 5 ++-- src/Settings.cpp | 48 ++++++++++++++++++++++++++++++++++ src/Settings.h | 35 +++++++++++++++++++++++++ src/SystemData.cpp | 12 ++++----- src/components/GuiGameList.cpp | 4 +-- src/components/GuiMenu.cpp | 6 ++--- src/main.cpp | 28 +++++++------------- 8 files changed, 106 insertions(+), 34 deletions(-) create mode 100644 src/Settings.cpp create mode 100644 src/Settings.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 64a542eb1..08026db09 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -125,6 +125,7 @@ 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 @@ -161,6 +162,7 @@ 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 diff --git a/src/Renderer_init_sdlgl.cpp b/src/Renderer_init_sdlgl.cpp index d4d28a061..b2aa045b7 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 { @@ -72,7 +71,7 @@ namespace Renderer SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); //SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1); //vsync - sdlScreen = SDL_SetVideoMode(display_width, display_height, 16, SDL_OPENGL | (WINDOWED ? 0 : SDL_FULLSCREEN)); + sdlScreen = SDL_SetVideoMode(display_width, display_height, 16, SDL_OPENGL | (Settings::getInstance()->getBool("WINDOWED") ? 0 : SDL_FULLSCREEN)); if(sdlScreen == NULL) { diff --git a/src/Settings.cpp b/src/Settings.cpp new file mode 100644 index 000000000..37cf61c30 --- /dev/null +++ b/src/Settings.cpp @@ -0,0 +1,48 @@ +#include "Settings.h" +#include "Log.h" + +Settings* Settings::sInstance = NULL; + +Settings::Settings() +{ + setDefaults(); +} + +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; +} + +//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_GET(type, mapName, methodName) type Settings::##methodName##(const std::string& name) \ +{ \ + if(mapName.find(name) == mapName.end()) \ + { \ + LOG(LogError) << "Tried to use unset setting " << name << "!"; \ + } \ + return mapName[name]; \ +} + +SETTINGS_GET(bool, mBoolMap, getBool); +SETTINGS_GET(int, mIntMap, getInt); + +void Settings::setBool(const std::string& name, bool value) { mBoolMap[name] = value; } +void Settings::setInt(const std::string& name, int value) { mIntMap[name] = value; } diff --git a/src/Settings.h b/src/Settings.h new file mode 100644 index 000000000..5babf70c7 --- /dev/null +++ b/src/Settings.h @@ -0,0 +1,35 @@ +#ifndef _SETTINGS_H_ +#define _SETTINGS_H_ + +#include +#include + +//This is a singleton for storing settings. +class Settings +{ +public: + static Settings* getInstance(); + + void loadFile(const std::string& path); + void saveFile(const std::string& path); + + //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); + + void setBool(const std::string& name, bool value); + void setInt(const std::string& name, int value); + +private: + static Settings* sInstance; + + Settings(); + + //Clear everything and load default values. + void setDefaults(); + + std::map mBoolMap; + std::map mIntMap; +}; + +#endif diff --git a/src/SystemData.cpp b/src/SystemData.cpp index e673041db..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(); @@ -322,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/components/GuiGameList.cpp b/src/components/GuiGameList.cpp index aad66bc10..6d6986017 100644 --- a/src/components/GuiGameList.cpp +++ b/src/components/GuiGameList.cpp @@ -5,6 +5,7 @@ #include "GuiFastSelect.h" #include #include "../Log.h" +#include "../Settings.h" Vector2i GuiGameList::getImagePos() { @@ -344,12 +345,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++) { diff --git a/src/components/GuiMenu.cpp b/src/components/GuiMenu.cpp index c3f1708ff..a0bd43fa6 100644 --- a/src/components/GuiMenu.cpp +++ b/src/components/GuiMenu.cpp @@ -4,9 +4,7 @@ #include "../Log.h" #include "../SystemData.h" #include "GuiGameList.h" - -//defined in main.cpp -extern bool DONTSHOWEXIT; +#include "../Settings.h" GuiMenu::GuiMenu(Window* window, GuiGameList* parent) : GuiComponent(window) { @@ -76,7 +74,7 @@ void GuiMenu::populateList() 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/main.cpp b/src/main.cpp index 092ce19b9..d633b960b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -14,6 +14,7 @@ #include "Log.h" #include "Window.h" #include "EmulationStation.h" +#include "Settings.h" #ifdef _RPI_ #include @@ -21,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[]) @@ -50,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"; @@ -200,7 +192,7 @@ int main(int argc, char* argv[]) window.update(deltaTime); window.render(); - if(DRAWFRAMERATE) + if(Settings::getInstance()->getBool("DRAWFRAMERATE")) { static int timeElapsed = 0; static int nrOfFrames = 0; @@ -224,7 +216,7 @@ 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; From 1534cec8656c7f4a3de778b219089a4fc1333d6d Mon Sep 17 00:00:00 2001 From: Aloshi Date: Tue, 18 Jun 2013 20:12:30 -0500 Subject: [PATCH 20/26] Added ComponentListComponent for laying out elements in a grid and navigating through them. Added SliderComponent for selecting from a range of values. Added SwitchComponent for selecting an "ON" or "OFF" value. --- CMakeLists.txt | 8 + src/GuiComponent.cpp | 7 +- src/GuiComponent.h | 3 +- src/components/ComponentListComponent.cpp | 312 ++++++++++++++++++++++ src/components/ComponentListComponent.h | 129 +++++++++ src/components/GuiMenu.cpp | 8 + src/components/GuiSettingsMenu.cpp | 70 +++++ src/components/GuiSettingsMenu.h | 29 ++ src/components/SliderComponent.cpp | 77 ++++++ src/components/SliderComponent.h | 25 ++ src/components/SwitchComponent.cpp | 43 +++ src/components/SwitchComponent.h | 18 ++ src/components/TextComponent.cpp | 33 ++- src/components/TextComponent.h | 5 +- src/components/TextListComponent.h | 1 - 15 files changed, 760 insertions(+), 8 deletions(-) create mode 100644 src/components/ComponentListComponent.cpp create mode 100644 src/components/ComponentListComponent.h create mode 100644 src/components/GuiSettingsMenu.cpp create mode 100644 src/components/GuiSettingsMenu.h create mode 100644 src/components/SliderComponent.cpp create mode 100644 src/components/SliderComponent.h create mode 100644 src/components/SwitchComponent.cpp create mode 100644 src/components/SwitchComponent.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 08026db09..ab88f5c13 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -133,7 +133,10 @@ set(ES_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/Window.h ${CMAKE_CURRENT_SOURCE_DIR}/src/XMLReader.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 @@ -143,6 +146,7 @@ set(ES_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiGameList.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiInputConfig.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMenu.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 @@ -169,7 +173,10 @@ set(ES_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/Window.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/XMLReader.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 @@ -178,6 +185,7 @@ set(ES_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiGameList.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiInputConfig.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMenu.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/src/GuiComponent.cpp b/src/GuiComponent.cpp index eef473de7..4a5ee0195 100644 --- a/src/GuiComponent.cpp +++ b/src/GuiComponent.cpp @@ -13,6 +13,9 @@ GuiComponent::~GuiComponent() if(mParent) mParent->removeChild(this); + + for(unsigned int i = 0; i < getChildCount(); i++) + getChild(i)->setParent(NULL); } bool GuiComponent::input(InputConfig* config, Input input) @@ -83,13 +86,15 @@ Vector2i GuiComponent::getOffset() void GuiComponent::setOffset(Vector2i offset) { - mOffset = offset; + setOffset(offset.x, offset.y); + onOffsetChanged(); } void GuiComponent::setOffset(int x, int y) { mOffset.x = x; mOffset.y = y; + onOffsetChanged(); } Vector2u GuiComponent::getSize() diff --git a/src/GuiComponent.h b/src/GuiComponent.h index 14ae66868..b7f36af64 100644 --- a/src/GuiComponent.h +++ b/src/GuiComponent.h @@ -29,10 +29,11 @@ public: //Called when the Renderer deinitializes. Passes to children. virtual void deinit(); - Vector2i getGlobalOffset(); + virtual Vector2i getGlobalOffset(); Vector2i getOffset(); void setOffset(Vector2i offset); void setOffset(int x, int y); + virtual void onOffsetChanged() {}; Vector2u getSize(); diff --git a/src/components/ComponentListComponent.cpp b/src/components/ComponentListComponent.cpp new file mode 100644 index 000000000..acc97632a --- /dev/null +++ b/src/components/ComponentListComponent.cpp @@ -0,0 +1,312 @@ +#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(); +} diff --git a/src/components/ComponentListComponent.h b/src/components/ComponentListComponent.h new file mode 100644 index 000000000..95faf527b --- /dev/null +++ b/src/components/ComponentListComponent.h @@ -0,0 +1,129 @@ +#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(); + +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/GuiMenu.cpp b/src/components/GuiMenu.cpp index a0bd43fa6..4e678d527 100644 --- a/src/components/GuiMenu.cpp +++ b/src/components/GuiMenu.cpp @@ -5,6 +5,7 @@ #include "../SystemData.h" #include "GuiGameList.h" #include "../Settings.h" +#include "GuiSettingsMenu.h" GuiMenu::GuiMenu(Window* window, GuiGameList* parent) : GuiComponent(window) { @@ -52,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) { @@ -69,6 +74,9 @@ 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); diff --git a/src/components/GuiSettingsMenu.cpp b/src/components/GuiSettingsMenu.cpp new file mode 100644 index 000000000..3e31d7fc0 --- /dev/null +++ b/src/components/GuiSettingsMenu.cpp @@ -0,0 +1,70 @@ +#include "GuiSettingsMenu.h" +#include "../Renderer.h" +#include "../Settings.h" +#include "SliderComponent.h" + +GuiSettingsMenu::GuiSettingsMenu(Window* window) : GuiComponent(window), + mList(window, Vector2u(2, 3)), + mDrawFramerateSwitch(window) +{ + 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("Volume: "); + label->setColor(0x0000FFFF); + mList.setEntry(Vector2u(0, 1), Vector2u(1, 1), label, false, ComponentListComponent::AlignRight, Vector2(true, true)); + + //volume slider + SliderComponent* slider = new SliderComponent(mWindow, 0, 1); + mList.setEntry(Vector2u(1, 1), Vector2u(1, 1), slider, true, ComponentListComponent::AlignCenter, Vector2(true, true)); + + label = new TextComponent(mWindow); + label->setText("B TO CLOSE"); + label->setColor(0x00FF00FF); + mList.setEntry(Vector2u(0, 2), Vector2u(2, 1), label, true, ComponentListComponent::AlignCenter, Vector2(false, true)); + mLabels.push_back(label); + + mList.setOffset(Renderer::getScreenWidth() / 2 - mList.getSize().x / 2, 0); + + loadStates(); +} + +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; + } + + return false; +} + +void GuiSettingsMenu::loadStates() +{ + Settings* s = Settings::getInstance(); + mDrawFramerateSwitch.setState(s->getBool("DRAWFRAMERATE")); +} diff --git a/src/components/GuiSettingsMenu.h b/src/components/GuiSettingsMenu.h new file mode 100644 index 000000000..8f123a335 --- /dev/null +++ b/src/components/GuiSettingsMenu.h @@ -0,0 +1,29 @@ +#ifndef _SETTINGSMENU_H_ +#define _SETTINGSMENU_H_ + +#include "../GuiComponent.h" +#include "ComponentListComponent.h" +#include +#include "SwitchComponent.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; + + std::vector mLabels; +}; + +#endif diff --git a/src/components/SliderComponent.cpp b/src/components/SliderComponent.cpp new file mode 100644 index 000000000..117d84600 --- /dev/null +++ b/src/components/SliderComponent.cpp @@ -0,0 +1,77 @@ +#include "SliderComponent.h" +#include +#include "../Renderer.h" + +SliderComponent::SliderComponent(Window* window, float min, float max) : GuiComponent(window), + mMin(min), mMax(max), mMoveRate(0) +{ + assert((min - max) != 0); + + mValue = (max + min) / 2; + + //calculate move scale + mMoveScale = (max - min) * 0.0007f; + + setSize(Vector2u(128, 32)); +} + +bool SliderComponent::input(InputConfig* config, Input input) +{ + if(config->isMappedTo("left", input)) + { + if(input.value) + mMoveRate = -1; + else + mMoveRate = 0; + + return true; + } + if(config->isMappedTo("right", input)) + { + if(input.value) + mMoveRate = 1; + else + mMoveRate = 0; + + return true; + } + + return GuiComponent::input(config, input); +} + +void SliderComponent::update(int deltaTime) +{ + mValue += mMoveRate * deltaTime * mMoveScale; + + if(mValue < mMin) + mValue = mMin; + if(mValue > mMax) + mValue = mMax; + + 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; +} diff --git a/src/components/SliderComponent.h b/src/components/SliderComponent.h new file mode 100644 index 000000000..4550cf652 --- /dev/null +++ b/src/components/SliderComponent.h @@ -0,0 +1,25 @@ +#pragma once + +#include "../GuiComponent.h" + +class SliderComponent : public GuiComponent +{ +public: + SliderComponent(Window* window, float min, float max); + + 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 mMoveScale; + + 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 index af707ec21..76312e80c 100644 --- a/src/components/TextComponent.cpp +++ b/src/components/TextComponent.cpp @@ -2,12 +2,12 @@ #include "../Renderer.h" #include "../Log.h" -TextComponent::TextComponent(Window* window) : GuiComponent(window), mFont(NULL), mColor(0x000000FF) +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) + mFont(NULL), mColor(0x000000FF), mAutoCalcExtent(true) { setText(text); setFont(font); @@ -22,12 +22,22 @@ void TextComponent::setBox(Vector2i pos, Vector2u size) void TextComponent::setExtent(Vector2u size) { - mSize = 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) @@ -38,6 +48,9 @@ void TextComponent::setColor(unsigned int color) void TextComponent::setText(const std::string& text) { mText = text; + + if(mAutoCalcExtent) + calculateExtent(); } void TextComponent::onRender() @@ -49,7 +62,7 @@ void TextComponent::onRender() return; } - Renderer::pushClipRect(getOffset(), getSize()); + Renderer::pushClipRect(getGlobalOffset(), getSize()); Renderer::drawWrappedText(mText, 0, 0, mSize.x, mColor, font); @@ -57,3 +70,15 @@ void TextComponent::onRender() 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 index c66e59ac0..67a6f9b5d 100644 --- a/src/components/TextComponent.h +++ b/src/components/TextComponent.h @@ -12,15 +12,18 @@ public: void setFont(Font* font); void setBox(Vector2i pos, Vector2u size); - void setExtent(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; }; diff --git a/src/components/TextListComponent.h b/src/components/TextListComponent.h index c73edea64..0aea6cea4 100644 --- a/src/components/TextListComponent.h +++ b/src/components/TextListComponent.h @@ -15,7 +15,6 @@ #define MARQUEE_RATE 3 //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). Marquee would be nice too. template class TextListComponent : public GuiComponent { From 62529029d7e409befdeff44c04952097043c78a4 Mon Sep 17 00:00:00 2001 From: Aloshi Date: Wed, 19 Jun 2013 16:02:42 -0500 Subject: [PATCH 21/26] Hooked up GuiSettingsMenu. Settings now save/load from ~/.emulationstation/es_settings.cfg. --- src/Settings.cpp | 65 ++++++++++++++++++++--- src/Settings.h | 7 ++- src/components/ComponentListComponent.cpp | 7 +++ src/components/ComponentListComponent.h | 2 + src/components/GuiSettingsMenu.cpp | 43 ++++++++++----- src/components/GuiSettingsMenu.h | 3 ++ src/components/SliderComponent.cpp | 44 +++++++++++---- src/components/SliderComponent.h | 5 +- 8 files changed, 145 insertions(+), 31 deletions(-) diff --git a/src/Settings.cpp b/src/Settings.cpp index 37cf61c30..3ce442708 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -1,11 +1,15 @@ #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() @@ -31,18 +35,67 @@ void Settings::setDefaults() 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_GET(type, mapName, methodName) type Settings::##methodName##(const std::string& name) \ +#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_GET(bool, mBoolMap, getBool); -SETTINGS_GET(int, mIntMap, getInt); - -void Settings::setBool(const std::string& name, bool value) { mBoolMap[name] = value; } -void Settings::setInt(const std::string& name, int value) { mIntMap[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 index 5babf70c7..099a0afa7 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -10,15 +10,17 @@ class Settings public: static Settings* getInstance(); - void loadFile(const std::string& path); - void saveFile(const std::string& path); + 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; @@ -30,6 +32,7 @@ private: std::map mBoolMap; std::map mIntMap; + std::map mFloatMap; }; #endif diff --git a/src/components/ComponentListComponent.cpp b/src/components/ComponentListComponent.cpp index acc97632a..0e6af1060 100644 --- a/src/components/ComponentListComponent.cpp +++ b/src/components/ComponentListComponent.cpp @@ -310,3 +310,10 @@ 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 index 95faf527b..2045ec53a 100644 --- a/src/components/ComponentListComponent.h +++ b/src/components/ComponentListComponent.h @@ -31,6 +31,8 @@ public: void resetCursor(); bool cursorValid(); + GuiComponent* getSelectedComponent(); + private: class ComponentEntry { diff --git a/src/components/GuiSettingsMenu.cpp b/src/components/GuiSettingsMenu.cpp index 3e31d7fc0..981f11db5 100644 --- a/src/components/GuiSettingsMenu.cpp +++ b/src/components/GuiSettingsMenu.cpp @@ -1,12 +1,16 @@ #include "GuiSettingsMenu.h" #include "../Renderer.h" #include "../Settings.h" -#include "SliderComponent.h" +#include "../VolumeControl.h" GuiSettingsMenu::GuiSettingsMenu(Window* window) : GuiComponent(window), mList(window, Vector2u(2, 3)), - mDrawFramerateSwitch(window) + mDrawFramerateSwitch(window), + mVolumeSlider(window, 0, 100, 1), + mSaveLabel(window) { + loadStates(); + addChild(&mList); mList.setOffset(Renderer::getScreenWidth() / 4, 0); @@ -21,23 +25,19 @@ GuiSettingsMenu::GuiSettingsMenu(Window* window) : GuiComponent(window), mList.setEntry(Vector2u(1, 0), Vector2u(1, 1), &mDrawFramerateSwitch, true, ComponentListComponent::AlignCenter, Vector2(true, true)); label = new TextComponent(mWindow); - label->setText("Volume: "); + label->setText("System volume: "); label->setColor(0x0000FFFF); mList.setEntry(Vector2u(0, 1), Vector2u(1, 1), label, false, ComponentListComponent::AlignRight, Vector2(true, true)); //volume slider - SliderComponent* slider = new SliderComponent(mWindow, 0, 1); - mList.setEntry(Vector2u(1, 1), Vector2u(1, 1), slider, true, ComponentListComponent::AlignCenter, Vector2(true, true)); + mList.setEntry(Vector2u(1, 1), Vector2u(1, 1), &mVolumeSlider, true, ComponentListComponent::AlignCenter, Vector2(true, true)); - label = new TextComponent(mWindow); - label->setText("B TO CLOSE"); - label->setColor(0x00FF00FF); - mList.setEntry(Vector2u(0, 2), Vector2u(2, 1), label, true, ComponentListComponent::AlignCenter, Vector2(false, true)); - mLabels.push_back(label); + //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); - - loadStates(); } GuiSettingsMenu::~GuiSettingsMenu() @@ -60,6 +60,13 @@ bool GuiSettingsMenu::input(InputConfig* config, Input input) return true; } + if(config->isMappedTo("a", input) && mList.getSelectedComponent() == &mSaveLabel && input.value) + { + applyStates(); + delete this; + return true; + } + return false; } @@ -67,4 +74,16 @@ 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 index 8f123a335..0b9af8dc3 100644 --- a/src/components/GuiSettingsMenu.h +++ b/src/components/GuiSettingsMenu.h @@ -5,6 +5,7 @@ #include "ComponentListComponent.h" #include #include "SwitchComponent.h" +#include "SliderComponent.h" #include "TextComponent.h" class GuiSettingsMenu : public GuiComponent @@ -22,6 +23,8 @@ private: ComponentListComponent mList; SwitchComponent mDrawFramerateSwitch; + SliderComponent mVolumeSlider; + TextComponent mSaveLabel; std::vector mLabels; }; diff --git a/src/components/SliderComponent.cpp b/src/components/SliderComponent.cpp index 117d84600..e9e92f49a 100644 --- a/src/components/SliderComponent.cpp +++ b/src/components/SliderComponent.cpp @@ -2,15 +2,15 @@ #include #include "../Renderer.h" -SliderComponent::SliderComponent(Window* window, float min, float max) : GuiComponent(window), - mMin(min), mMax(max), mMoveRate(0) +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; + mMoveScale = ((max - min) * 0.0007f) / increment; setSize(Vector2u(128, 32)); } @@ -20,19 +20,24 @@ bool SliderComponent::input(InputConfig* config, Input input) if(config->isMappedTo("left", input)) { if(input.value) - mMoveRate = -1; + 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 = 1; + mMoveRate = mIncrement; else mMoveRate = 0; + mRepeatWaitTimer = 0; + return true; } @@ -41,13 +46,22 @@ bool SliderComponent::input(InputConfig* config, Input input) void SliderComponent::update(int deltaTime) { - mValue += mMoveRate * deltaTime * mMoveScale; + 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(mValue < mMin) + mValue = mMin; + if(mValue > mMax) + mValue = mMax; + if(mRepeatWaitTimer < 450) + mRepeatWaitTimer += deltaTime; + } + GuiComponent::update(deltaTime); } @@ -75,3 +89,13 @@ 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 index 4550cf652..c6c49ad0d 100644 --- a/src/components/SliderComponent.h +++ b/src/components/SliderComponent.h @@ -5,7 +5,8 @@ class SliderComponent : public GuiComponent { public: - SliderComponent(Window* window, float min, float max); + //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(); @@ -19,7 +20,9 @@ public: private: float mMin, mMax; float mValue; + float mIncrement; float mMoveScale; + int mRepeatWaitTimer; float mMoveRate; }; From f651ea401ca36e2b27c7d90e4c88ddb0a469bc7f Mon Sep 17 00:00:00 2001 From: Aloshi Date: Wed, 19 Jun 2013 16:07:12 -0500 Subject: [PATCH 22/26] Removed volume from GuiInputConfig since it's in GuiSettingsMenu now. --- src/components/GuiInputConfig.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/GuiInputConfig.cpp b/src/components/GuiInputConfig.cpp index 61b8ffe3c..6f217fcec 100644 --- a/src/components/GuiInputConfig.cpp +++ b/src/components/GuiInputConfig.cpp @@ -5,9 +5,12 @@ #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"}; + +//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) { From 56569e542521ab1d52c4ebdc5d41c9da7dc4507a Mon Sep 17 00:00:00 2001 From: Aloshi Date: Wed, 19 Jun 2013 18:31:59 -0500 Subject: [PATCH 23/26] Made Settings Getter/Setter macro more g++-friendly. --- src/Settings.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Settings.cpp b/src/Settings.cpp index 3ce442708..52111668d 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -83,7 +83,7 @@ void Settings::loadFile() } //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) \ +#define SETTINGS_GETSET(type, mapName, getMethodName, setMethodName) type Settings::getMethodName(const std::string& name) \ { \ if(mapName.find(name) == mapName.end()) \ { \ @@ -91,7 +91,7 @@ void Settings::loadFile() } \ return mapName[name]; \ } \ -void Settings::##setMethodName##(const std::string& name, type value) \ +void Settings::setMethodName(const std::string& name, type value) \ { \ mapName[name] = value; \ } From 5619674c551b6e1adbe91ce4ef02f6eacbe2f382 Mon Sep 17 00:00:00 2001 From: Aloshi Date: Wed, 19 Jun 2013 19:56:45 -0500 Subject: [PATCH 24/26] Work-around for no front buffer access on GLES. Buffer swap now occurs after update but before render. --- src/components/ImageComponent.cpp | 13 ++++++++++--- src/main.cpp | 4 +++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/components/ImageComponent.cpp b/src/components/ImageComponent.cpp index 040348cff..238c9019e 100644 --- a/src/components/ImageComponent.cpp +++ b/src/components/ImageComponent.cpp @@ -359,13 +359,20 @@ void ImageComponent::copyScreen() { unloadImage(); - glReadBuffer(GL_FRONT); + 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); - int width = Renderer::getScreenWidth(); - int height = Renderer::getScreenHeight(); + //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); diff --git a/src/main.cpp b/src/main.cpp index d633b960b..64367bb05 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -190,6 +190,7 @@ 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(Settings::getInstance()->getBool("DRAWFRAMERATE")) @@ -221,9 +222,10 @@ int main(int argc, char* argv[]) sleeping = true; timeSinceLastEvent = 0; Renderer::drawRect(0, 0, Renderer::getScreenWidth(), Renderer::getScreenHeight(), 0x000000A0); + Renderer::swapBuffers(); } - Renderer::swapBuffers(); + Log::flush(); } From ae50cc82adc931f37a6f7c4de98ce86bf3050bdb Mon Sep 17 00:00:00 2001 From: Aloshi Date: Thu, 20 Jun 2013 14:08:33 -0500 Subject: [PATCH 25/26] Updated version strings. --- src/EmulationStation.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 From 85f3ebf1525b10820293156b05e95099794961b3 Mon Sep 17 00:00:00 2001 From: Aloshi Date: Fri, 21 Jun 2013 16:54:41 -0500 Subject: [PATCH 26/26] Fixed GuiGameList crashing on non-detailed view. --- src/components/GuiGameList.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/components/GuiGameList.cpp b/src/components/GuiGameList.cpp index 6d6986017..a65c94668 100644 --- a/src/components/GuiGameList.cpp +++ b/src/components/GuiGameList.cpp @@ -26,17 +26,15 @@ GuiGameList::GuiGameList(Window* window, bool useDetail) : GuiComponent(window), { mList = new TextListComponent(mWindow, (int)(Renderer::getScreenWidth() * mTheme->getFloat("listOffsetX")), 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")); - mImageAnimation = new AnimationComponent(); mImageAnimation->addChild(mScreenshot); }else{ mList = new TextListComponent(mWindow, 0, Renderer::getDefaultFont(Renderer::LARGE)->getHeight() + 2, Renderer::getDefaultFont(Renderer::MEDIUM)); - mScreenshot = NULL; - mImageAnimation = NULL; } + 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));