Browse Source

Implemented SDL2 unicode input. Hotkeys are sill broken.

AlexVinS 11 years ago
parent
commit
ac2896da42

+ 1 - 1
client/CMT.cpp

@@ -1031,7 +1031,7 @@ static void listenForEvents()
 	while(1) //main SDL events loop
 	{
 		SDL_Event ev;
-
+		
 		int ret = SDL_WaitEvent(&ev);
 		if (ret == 0 || (ev.type==SDL_QUIT) ||
 			(ev.type == SDL_KEYDOWN && ev.key.keysym.sym==SDLK_F4 && (ev.key.keysym.mod & KMOD_ALT)))

+ 1 - 1
client/CMessage.cpp

@@ -134,7 +134,7 @@ std::vector<std::string> CMessage::breakText( std::string text, size_t maxLineWi
 
 	boost::algorithm::trim_right_if(text,boost::algorithm::is_any_of(std::string(" ")));
 
-	// each interation generates one output line
+	// each iteration generates one output line
 	while (text.length())
 	{
 		ui32 lineWidth = 0;    //in characters or given char metric

+ 2 - 1
client/CMusicHandler.cpp

@@ -481,7 +481,8 @@ void MusicEntry::load(std::string musicURI)
 
 	data = CResourceHandler::get()->load(ResourceID(musicURI, EResType::MUSIC))->readAll();
 	musicFile = SDL_RWFromConstMem(data.first.get(), data.second);
-	#if 0
+	
+	#ifdef VCMI_SDL1
 	music = Mix_LoadMUS_RW(musicFile);
 
 	if(!music)

+ 1 - 1
client/CPreGame.cpp

@@ -1896,7 +1896,7 @@ CChatBox::CChatBox(const Rect &rect)
 {
 	OBJ_CONSTRUCTION;
 	pos += rect;
-	addUsedEvents(KEYBOARD);
+	addUsedEvents(KEYBOARD | TEXTINPUT);
 	captureAllKeys = true;
 
 	const int height = graphics->fonts[FONT_SMALL]->getLineHeight();

+ 1 - 23
client/StdInc.h

@@ -2,29 +2,7 @@
 
 #include "../Global.h"
 
-#include <SDL_version.h>
-
-#if (SDL_MAJOR_VERSION == 2)
-#define VCMI_SDL2
-
-#include <SDL_keycode.h>
-typedef int SDLX_Coord;
-typedef int SDLX_Size;
-
-typedef SDL_Keycode SDLKey;
-
-#define SDL_SRCCOLORKEY SDL_TRUE
-
-#define SDL_FULLSCREEN SDL_WINDOW_FULLSCREEN
-
-#elif (SDL_MAJOR_VERSION == 1) 
-#define VCMI_SDL1
-//SDL 1.x
-typedef Sint16 SDLX_Coord;
-typedef Uint16 SDLX_Size;
-#else
-#error "unkown or unsupported SDL version"
-#endif
+#include "gui/SDL_Compat.h"
 
 // This header should be treated as a pre compiled header file(PCH) in the compiler building settings.
 

+ 20 - 2
client/gui/CGuiHandler.cpp

@@ -60,6 +60,10 @@ void CGuiHandler::processLists(const ui16 activityFlag, std::function<void (std:
 	processList(CIntObject::TIME,activityFlag,&timeinterested,cb);
 	processList(CIntObject::WHEEL,activityFlag,&wheelInterested,cb);
 	processList(CIntObject::DOUBLECLICK,activityFlag,&doubleClickInterested,cb);
+	
+	#ifndef VCMI_SDL1
+	processList(CIntObject::TEXTINPUT,activityFlag,&textInterested,cb);
+	#endif // VCMI_SDL1
 }
 
 void CGuiHandler::handleElementActivate(CIntObject * elem, ui16 activityFlag)
@@ -264,7 +268,7 @@ void CGuiHandler::handleEvent(SDL_Event *sEvent)
 				}
 			}
 		}
-		#ifdef VCMI_SDL1
+		#ifdef VCMI_SDL1 //SDL1x only events
 		else if(sEvent->button.button == SDL_BUTTON_WHEELDOWN || sEvent->button.button == SDL_BUTTON_WHEELUP)
 		{
 			std::list<CIntObject*> hlp = wheelInterested;
@@ -276,7 +280,7 @@ void CGuiHandler::handleEvent(SDL_Event *sEvent)
 		}
 		#endif
 	}
-	#ifndef VCMI_SDL1	
+	#ifndef VCMI_SDL1 //SDL2x only events	
 	else if ((sEvent->type == SDL_MOUSEWHEEL))
 	{
 		std::list<CIntObject*> hlp = wheelInterested;
@@ -286,6 +290,20 @@ void CGuiHandler::handleEvent(SDL_Event *sEvent)
 			(*i)->wheelScrolled(sEvent->wheel.y < 0, isItIn(&(*i)->pos,sEvent->motion.x,sEvent->motion.y));
 		}		
 	}
+	else if(sEvent->type == SDL_TEXTINPUT)
+	{
+		for(auto it : textInterested)
+		{
+			it->textInputed(sEvent->text);
+		}
+	}	
+	else if(sEvent->type == SDL_TEXTEDITING)
+	{
+		for(auto it : textInterested)
+		{
+			it->textEdited(sEvent->edit);
+		}
+	}	
 	#endif // VCMI_SDL1
 	else if ((sEvent->type==SDL_MOUSEBUTTONUP) && (sEvent->button.button == SDL_BUTTON_LEFT))
 	{

+ 3 - 0
client/gui/CGuiHandler.h

@@ -58,6 +58,9 @@ private:
 	               timeinterested,
 	               wheelInterested,
 	               doubleClickInterested;
+	#ifndef VCMI_SDL1
+	CIntObjectList textInterested;
+	#endif // VCMI_SDL1
 	               
 	void processLists(const ui16 activityFlag, std::function<void (std::list<CIntObject*> *)> cb);               
 public:

+ 16 - 11
client/gui/CIntObject.h

@@ -1,13 +1,3 @@
-#pragma once
-
-#include <SDL_events.h>
-#include "Geometries.h"
-#include "../Graphics.h"
-
-struct SDL_Surface;
-class CPicture;
-class CGuiHandler;
-
 /*
  * CIntObject.h, part of VCMI engine
  *
@@ -17,6 +7,16 @@ class CGuiHandler;
  * Full text of license available in license.txt file, in main folder
  *
  */
+ 
+#pragma once
+
+#include <SDL_events.h>
+#include "Geometries.h"
+#include "../Graphics.h"
+
+struct SDL_Surface;
+class CPicture;
+class CGuiHandler;
 
 using boost::logic::tribool;
 
@@ -123,6 +123,11 @@ public:
 	bool captureAllKeys; //if true, only this object should get info about pressed keys
 	virtual void keyPressed(const SDL_KeyboardEvent & key){}
 	virtual bool captureThisEvent(const SDL_KeyboardEvent & key); //allows refining captureAllKeys against specific events (eg. don't capture ENTER)
+	
+#ifndef VCMI_SDL1
+	virtual void textInputed(const SDL_TextInputEvent & event){};
+	virtual void textEdited(const SDL_TextEditingEvent & event){};
+#endif // VCMI_SDL1
 
 	//mouse movement handling
 	bool strongInterest; //if true - report all mouse movements, if not - only when hovered
@@ -138,7 +143,7 @@ public:
 	//double click
 	virtual void onDoubleClick(){}
 
-	enum {LCLICK=1, RCLICK=2, HOVER=4, MOVE=8, KEYBOARD=16, TIME=32, GENERAL=64, WHEEL=128, DOUBLECLICK=256, ALL=0xffff};
+	enum {LCLICK=1, RCLICK=2, HOVER=4, MOVE=8, KEYBOARD=16, TIME=32, GENERAL=64, WHEEL=128, DOUBLECLICK=256, TEXTINPUT=512, ALL=0xffff};
 	const ui16 & active;
 	void addUsedEvents(ui16 newActions);
 	void removeUsedEvents(ui16 newActions);

+ 106 - 12
client/gui/CIntObjectClasses.cpp

@@ -18,6 +18,7 @@
 #include "../GUIClasses.h"
 #include "CGuiHandler.h"
 #include "../CAdvmapInterface.h"
+#include "../../lib/CGeneralTextHandler.h" //for Unicode related stuff
 
 CPicture::CPicture( SDL_Surface *BG, int x, int y, bool Free )
 {
@@ -1545,7 +1546,7 @@ CTextInput::CTextInput(const Rect &Pos, EFonts font, const CFunctionList<void(co
 	pos.w = Pos.w;
 	captureAllKeys = true;
 	bg = nullptr;
-	addUsedEvents(LCLICK | KEYBOARD);
+	addUsedEvents(LCLICK | KEYBOARD | TEXTINPUT);
 	giveFocus();
 }
 
@@ -1557,7 +1558,7 @@ CTextInput::CTextInput( const Rect &Pos, const Point &bgOffset, const std::strin
 	captureAllKeys = true;
 	OBJ_CONSTRUCTION;
 	bg = new CPicture(bgName, bgOffset.x, bgOffset.y);
-	addUsedEvents(LCLICK | KEYBOARD);
+	addUsedEvents(LCLICK | KEYBOARD | TEXTINPUT);
 	giveFocus();
 }
 
@@ -1576,13 +1577,35 @@ CTextInput::CTextInput(const Rect &Pos, SDL_Surface *srf)
 	pos.w = bg->pos.w;
 	pos.h = bg->pos.h;
 	bg->pos = pos;
-	addUsedEvents(LCLICK | KEYBOARD);
+	addUsedEvents(LCLICK | KEYBOARD | TEXTINPUT);
 	giveFocus();
 }
 
+void CTextInput::focusGot()
+{
+	#ifndef VCMI_SDL1
+	if (SDL_IsTextInputActive() == SDL_FALSE)		
+	{		
+		SDL_StartTextInput();		
+	}		
+	SDL_SetTextInputRect(&pos);
+	#endif	
+}
+
+void CTextInput::focusLost()
+{
+	#ifndef VCMI_SDL1
+	if (SDL_IsTextInputActive() == SDL_TRUE)
+	{		
+		SDL_StopTextInput();			
+	}		
+	#endif	
+}
+
+
 std::string CTextInput::visibleText()
 {
-	return focus ? text + "_" : text;
+	return focus ? text + newText + "_" : text;
 }
 
 void CTextInput::clickLeft( tribool down, bool previousState )
@@ -1593,6 +1616,30 @@ void CTextInput::clickLeft( tribool down, bool previousState )
 
 void CTextInput::keyPressed( const SDL_KeyboardEvent & key )
 {
+	auto trim = [](std::string & s){
+		if(s.empty())
+			return;
+		auto b = s.begin(); 
+		auto e = s.end();
+		size_t lastLen = 0;
+		size_t len = 0;
+		while (b != e) {
+			lastLen = len;
+			size_t n = Unicode::getCharacterSize(*b);
+			
+			if(!Unicode::isValidCharacter(&(*b),e-b))
+			{				
+				logGlobal->errorStream() << "Invalid UTF8 sequence";
+				break;//invalid sequence will be trimmed
+			}
+		
+			len += n;
+			b += n;
+		}		
+		
+		s.resize(lastLen);
+	};
+	
 	if(!focus || key.state != SDL_PRESSED)
 		return;
 
@@ -1603,32 +1650,43 @@ void CTextInput::keyPressed( const SDL_KeyboardEvent & key )
 		return;
 	}
 
-	std::string oldText = text;
+	bool redrawNeeded = false;
 	switch(key.keysym.sym)
 	{
 	case SDLK_DELETE: // have index > ' ' so it won't be filtered out by default section
 		return;
 	case SDLK_BACKSPACE:
-		if(!text.empty())
-			text.resize(text.size()-1);
+		if(!newText.empty())
+		{
+			trim(newText);
+			redrawNeeded = true;
+		}
+		else if(!text.empty())
+		{
+			trim(text);
+			redrawNeeded = true;
+		}			
 		break;
 	default:
 		#ifdef VCMI_SDL1
 		if (key.keysym.unicode < ' ')
 			return;
 		else
+		{
 			text += key.keysym.unicode; //TODO 16-/>8
+			redrawNeeded = true;
+		}			
 		#endif // 0
 		break;
 	}
 	#ifdef VCMI_SDL1
 	filters(text, oldText);
-	if (text != oldText)
+	#endif // 0
+	if (redrawNeeded)
 	{
 		redraw();
 		cb(text);
-	}
-	#endif // 0
+	}	
 	
 	//todo: handle text input for SDL2
 }
@@ -1652,10 +1710,41 @@ bool CTextInput::captureThisEvent(const SDL_KeyboardEvent & key)
 
 	return true;
 	#else
-	return false; //todo:CTextInput::captureThisEvent
+	return false;
 	#endif
 }
 
+#ifndef VCMI_SDL1
+void CTextInput::textInputed(const SDL_TextInputEvent & event)
+{
+	if(!focus)
+		return;
+	std::string oldText = text;
+	
+	text += event.text;	
+	
+	filters(text,oldText);
+	if (text != oldText)
+	{
+		redraw();
+		cb(text);
+	}	
+	newText = "";
+}
+
+void CTextInput::textEdited(const SDL_TextEditingEvent & event)
+{
+	if(!focus)
+		return;
+		
+	newText = event.text;
+	redraw();
+	cb(text+newText);	
+}
+
+#endif
+
+
 void CTextInput::filenameFilter(std::string & text, const std::string &)
 {
 	static const std::string forbiddenChars = "<>:\"/\\|?*\r\n"; //if we are entering a filename, some special characters won't be allowed
@@ -1708,7 +1797,10 @@ CFocusable::CFocusable()
 CFocusable::~CFocusable()
 {
 	if(inputWithFocus == this)
+	{
+		focusLost();
 		inputWithFocus = nullptr;
+	}	
 
 	focusables -= this;
 }
@@ -1717,12 +1809,14 @@ void CFocusable::giveFocus()
 	if(inputWithFocus)
 	{
 		inputWithFocus->focus = false;
+		inputWithFocus->focusLost();
 		inputWithFocus->redraw();
 	}
 
 	focus = true;
 	inputWithFocus = this;
-	redraw();
+	focusGot();
+	redraw();	
 }
 
 void CFocusable::moveFocus()

+ 13 - 0
client/gui/CIntObjectClasses.h

@@ -436,6 +436,9 @@ public:
 /// UIElement which can get input focus
 class CFocusable : public virtual CIntObject
 {
+protected:
+	virtual void focusGot(){};
+	virtual void focusLost(){};
 public:
 	bool focus; //only one focusable control can have focus at one moment
 
@@ -451,9 +454,12 @@ public:
 /// Text input box where players can enter text
 class CTextInput : public CLabel, public CFocusable
 {
+	std::string newText;
 protected:
 	std::string visibleText() override;
 
+	void focusGot() override;
+	void focusLost() override;
 public:
 	CFunctionList<void(const std::string &)> cb;
 	CFunctionList<void(std::string &, const std::string &)> filters;
@@ -466,6 +472,13 @@ public:
 	void clickLeft(tribool down, bool previousState) override;
 	void keyPressed(const SDL_KeyboardEvent & key) override;
 	bool captureThisEvent(const SDL_KeyboardEvent & key) override;
+	
+#ifndef VCMI_SDL1
+	void textInputed(const SDL_TextInputEvent & event) override;
+	void textEdited(const SDL_TextEditingEvent & event) override;
+	
+	
+#endif // VCMI_SDL1	
 
 	//Filter that will block all characters not allowed in filenames
 	static void filenameFilter(std::string &text, const std::string & oldText);

+ 37 - 0
client/gui/SDL_Compat.h

@@ -0,0 +1,37 @@
+#pragma once
+
+/*
+ * SDL_Compat.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+ 
+#include <SDL_version.h>
+
+#if (SDL_MAJOR_VERSION == 2)
+#define VCMI_SDL2
+
+#include <SDL_keycode.h>
+typedef int SDLX_Coord;
+typedef int SDLX_Size;
+
+typedef SDL_Keycode SDLKey;
+
+#define SDL_SRCCOLORKEY SDL_TRUE
+
+#define SDL_FULLSCREEN SDL_WINDOW_FULLSCREEN
+
+#elif (SDL_MAJOR_VERSION == 1) 
+#define VCMI_SDL1
+//SDL 1.x
+typedef Sint16 SDLX_Coord;
+typedef Uint16 SDLX_Size;
+#else
+#error "unknown or unsupported SDL version"
+#endif
+ 
+

+ 12 - 9
lib/CGeneralTextHandler.cpp

@@ -19,7 +19,7 @@
  *
  */
 
-size_t Unicode::getCharacterSize(ui8 firstByte)
+size_t Unicode::getCharacterSize(char firstByte)
 {
 	// length of utf-8 character can be determined from 1st byte by counting number of highest bits set to 1:
 	// 0xxxxxxx -> 1 -  ASCII chars
@@ -27,14 +27,14 @@ size_t Unicode::getCharacterSize(ui8 firstByte)
 	// 11110xxx -> 4 - last allowed in current standard
 	// 1111110x -> 6 - last allowed in original standard
 
-	if (firstByte < 0x80)
+	if ((ui8)firstByte < 0x80)
 		return 1; // ASCII
 
 	size_t ret = 0;
 
 	for (size_t i=0; i<8; i++)
 	{
-		if ((firstByte & (0x80 >> i)) != 0)
+		if (((ui8)firstByte & (0x80 >> i)) != 0)
 			ret++;
 		else
 			break;
@@ -42,12 +42,15 @@ size_t Unicode::getCharacterSize(ui8 firstByte)
 	return ret;
 }
 
-bool Unicode::isValidCharacter(const ui8 *character, size_t maxSize)
+bool Unicode::isValidCharacter(const char * character, size_t maxSize)
 {
+	// can't be first byte in UTF8
+	if ((ui8)character[0] >= 0x80 && (ui8)character[0] < 0xC0)
+		return false;
 	// first character must follow rules checked in getCharacterSize
-	size_t size = getCharacterSize(character[0]);
+	size_t size = getCharacterSize((ui8)character[0]);
 
-	if (character[0] > 0xF4)
+	if ((ui8)character[0] > 0xF4)
 		return false; // above maximum allowed in standard (UTF codepoints are capped at 0x0010FFFF)
 
 	if (size > maxSize)
@@ -56,7 +59,7 @@ bool Unicode::isValidCharacter(const ui8 *character, size_t maxSize)
 	// remaining characters must have highest bit set to 1
 	for (size_t i = 1; i < size; i++)
 	{
-		if ((character[i] & 0x80) == 0)
+		if (((ui8)character[i] & 0x80) == 0)
 			return false;
 	}
 	return true;
@@ -82,7 +85,7 @@ bool Unicode::isValidString(const std::string & text)
 {
 	for (size_t i=0; i<text.size(); i += getCharacterSize(text[i]))
 	{
-		if (!isValidCharacter(reinterpret_cast<const ui8*>(text.data() + i), text.size() - i))
+		if (!isValidCharacter(text.data() + i, text.size() - i))
 			return false;
 	}
 	return true;
@@ -92,7 +95,7 @@ bool Unicode::isValidString(const char * data, size_t size)
 {
 	for (size_t i=0; i<size; i += getCharacterSize(data[i]))
 	{
-		if (!isValidCharacter(reinterpret_cast<const ui8*>(data + i), size - i))
+		if (!isValidCharacter(data + i, size - i))
 			return false;
 	}
 	return true;

+ 2 - 2
lib/CGeneralTextHandler.h

@@ -16,11 +16,11 @@
 namespace Unicode
 {
 	/// evaluates size of UTF-8 character
-	size_t DLL_LINKAGE getCharacterSize(ui8 firstByte);
+	size_t DLL_LINKAGE getCharacterSize(char firstByte);
 
 	/// test if character is a valid UTF-8 symbol
 	/// maxSize - maximum number of bytes this symbol may consist from ( = remainer of string)
-	bool DLL_LINKAGE isValidCharacter(const ui8 *character, size_t maxSize);
+	bool DLL_LINKAGE isValidCharacter(const char * character, size_t maxSize);
 
 	/// test if text contains ASCII-string (no need for unicode conversion)
 	bool DLL_LINKAGE isValidASCII(const std::string & text);