1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-06-23 00:28:08 +02:00

Implemented SDL2 unicode input. Hotkeys are sill broken.

This commit is contained in:
AlexVinS
2014-05-23 20:46:54 +04:00
committed by AlexVinS
parent d50976bf4a
commit ac2896da42
13 changed files with 215 additions and 63 deletions

View File

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

View File

@ -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(" "))); 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()) while (text.length())
{ {
ui32 lineWidth = 0; //in characters or given char metric ui32 lineWidth = 0; //in characters or given char metric

View File

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

View File

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

View File

@ -2,29 +2,7 @@
#include "../Global.h" #include "../Global.h"
#include <SDL_version.h> #include "gui/SDL_Compat.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
// This header should be treated as a pre compiled header file(PCH) in the compiler building settings. // This header should be treated as a pre compiled header file(PCH) in the compiler building settings.

View File

@ -60,6 +60,10 @@ void CGuiHandler::processLists(const ui16 activityFlag, std::function<void (std:
processList(CIntObject::TIME,activityFlag,&timeinterested,cb); processList(CIntObject::TIME,activityFlag,&timeinterested,cb);
processList(CIntObject::WHEEL,activityFlag,&wheelInterested,cb); processList(CIntObject::WHEEL,activityFlag,&wheelInterested,cb);
processList(CIntObject::DOUBLECLICK,activityFlag,&doubleClickInterested,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) 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) else if(sEvent->button.button == SDL_BUTTON_WHEELDOWN || sEvent->button.button == SDL_BUTTON_WHEELUP)
{ {
std::list<CIntObject*> hlp = wheelInterested; std::list<CIntObject*> hlp = wheelInterested;
@ -276,7 +280,7 @@ void CGuiHandler::handleEvent(SDL_Event *sEvent)
} }
#endif #endif
} }
#ifndef VCMI_SDL1 #ifndef VCMI_SDL1 //SDL2x only events
else if ((sEvent->type == SDL_MOUSEWHEEL)) else if ((sEvent->type == SDL_MOUSEWHEEL))
{ {
std::list<CIntObject*> hlp = wheelInterested; 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)); (*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 #endif // VCMI_SDL1
else if ((sEvent->type==SDL_MOUSEBUTTONUP) && (sEvent->button.button == SDL_BUTTON_LEFT)) else if ((sEvent->type==SDL_MOUSEBUTTONUP) && (sEvent->button.button == SDL_BUTTON_LEFT))
{ {

View File

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

View File

@ -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 * CIntObject.h, part of VCMI engine
* *
@ -17,6 +7,16 @@ class CGuiHandler;
* Full text of license available in license.txt file, in main folder * 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; using boost::logic::tribool;
@ -123,6 +123,11 @@ public:
bool captureAllKeys; //if true, only this object should get info about pressed keys bool captureAllKeys; //if true, only this object should get info about pressed keys
virtual void keyPressed(const SDL_KeyboardEvent & key){} 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) 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 //mouse movement handling
bool strongInterest; //if true - report all mouse movements, if not - only when hovered bool strongInterest; //if true - report all mouse movements, if not - only when hovered
@ -138,7 +143,7 @@ public:
//double click //double click
virtual void onDoubleClick(){} 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; const ui16 & active;
void addUsedEvents(ui16 newActions); void addUsedEvents(ui16 newActions);
void removeUsedEvents(ui16 newActions); void removeUsedEvents(ui16 newActions);

View File

@ -18,6 +18,7 @@
#include "../GUIClasses.h" #include "../GUIClasses.h"
#include "CGuiHandler.h" #include "CGuiHandler.h"
#include "../CAdvmapInterface.h" #include "../CAdvmapInterface.h"
#include "../../lib/CGeneralTextHandler.h" //for Unicode related stuff
CPicture::CPicture( SDL_Surface *BG, int x, int y, bool Free ) 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; pos.w = Pos.w;
captureAllKeys = true; captureAllKeys = true;
bg = nullptr; bg = nullptr;
addUsedEvents(LCLICK | KEYBOARD); addUsedEvents(LCLICK | KEYBOARD | TEXTINPUT);
giveFocus(); giveFocus();
} }
@ -1557,7 +1558,7 @@ CTextInput::CTextInput( const Rect &Pos, const Point &bgOffset, const std::strin
captureAllKeys = true; captureAllKeys = true;
OBJ_CONSTRUCTION; OBJ_CONSTRUCTION;
bg = new CPicture(bgName, bgOffset.x, bgOffset.y); bg = new CPicture(bgName, bgOffset.x, bgOffset.y);
addUsedEvents(LCLICK | KEYBOARD); addUsedEvents(LCLICK | KEYBOARD | TEXTINPUT);
giveFocus(); giveFocus();
} }
@ -1576,13 +1577,35 @@ CTextInput::CTextInput(const Rect &Pos, SDL_Surface *srf)
pos.w = bg->pos.w; pos.w = bg->pos.w;
pos.h = bg->pos.h; pos.h = bg->pos.h;
bg->pos = pos; bg->pos = pos;
addUsedEvents(LCLICK | KEYBOARD); addUsedEvents(LCLICK | KEYBOARD | TEXTINPUT);
giveFocus(); 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() std::string CTextInput::visibleText()
{ {
return focus ? text + "_" : text; return focus ? text + newText + "_" : text;
} }
void CTextInput::clickLeft( tribool down, bool previousState ) 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 ) 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) if(!focus || key.state != SDL_PRESSED)
return; return;
@ -1603,32 +1650,43 @@ void CTextInput::keyPressed( const SDL_KeyboardEvent & key )
return; return;
} }
std::string oldText = text; bool redrawNeeded = false;
switch(key.keysym.sym) switch(key.keysym.sym)
{ {
case SDLK_DELETE: // have index > ' ' so it won't be filtered out by default section case SDLK_DELETE: // have index > ' ' so it won't be filtered out by default section
return; return;
case SDLK_BACKSPACE: case SDLK_BACKSPACE:
if(!text.empty()) if(!newText.empty())
text.resize(text.size()-1); {
trim(newText);
redrawNeeded = true;
}
else if(!text.empty())
{
trim(text);
redrawNeeded = true;
}
break; break;
default: default:
#ifdef VCMI_SDL1 #ifdef VCMI_SDL1
if (key.keysym.unicode < ' ') if (key.keysym.unicode < ' ')
return; return;
else else
{
text += key.keysym.unicode; //TODO 16-/>8 text += key.keysym.unicode; //TODO 16-/>8
redrawNeeded = true;
}
#endif // 0 #endif // 0
break; break;
} }
#ifdef VCMI_SDL1 #ifdef VCMI_SDL1
filters(text, oldText); filters(text, oldText);
if (text != oldText) #endif // 0
if (redrawNeeded)
{ {
redraw(); redraw();
cb(text); cb(text);
} }
#endif // 0
//todo: handle text input for SDL2 //todo: handle text input for SDL2
} }
@ -1652,10 +1710,41 @@ bool CTextInput::captureThisEvent(const SDL_KeyboardEvent & key)
return true; return true;
#else #else
return false; //todo:CTextInput::captureThisEvent return false;
#endif #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 &) 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 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() CFocusable::~CFocusable()
{ {
if(inputWithFocus == this) if(inputWithFocus == this)
{
focusLost();
inputWithFocus = nullptr; inputWithFocus = nullptr;
}
focusables -= this; focusables -= this;
} }
@ -1717,12 +1809,14 @@ void CFocusable::giveFocus()
if(inputWithFocus) if(inputWithFocus)
{ {
inputWithFocus->focus = false; inputWithFocus->focus = false;
inputWithFocus->focusLost();
inputWithFocus->redraw(); inputWithFocus->redraw();
} }
focus = true; focus = true;
inputWithFocus = this; inputWithFocus = this;
redraw(); focusGot();
redraw();
} }
void CFocusable::moveFocus() void CFocusable::moveFocus()

View File

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

37
client/gui/SDL_Compat.h Normal file
View File

@ -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

View File

@ -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: // length of utf-8 character can be determined from 1st byte by counting number of highest bits set to 1:
// 0xxxxxxx -> 1 - ASCII chars // 0xxxxxxx -> 1 - ASCII chars
@ -27,14 +27,14 @@ size_t Unicode::getCharacterSize(ui8 firstByte)
// 11110xxx -> 4 - last allowed in current standard // 11110xxx -> 4 - last allowed in current standard
// 1111110x -> 6 - last allowed in original standard // 1111110x -> 6 - last allowed in original standard
if (firstByte < 0x80) if ((ui8)firstByte < 0x80)
return 1; // ASCII return 1; // ASCII
size_t ret = 0; size_t ret = 0;
for (size_t i=0; i<8; i++) for (size_t i=0; i<8; i++)
{ {
if ((firstByte & (0x80 >> i)) != 0) if (((ui8)firstByte & (0x80 >> i)) != 0)
ret++; ret++;
else else
break; break;
@ -42,12 +42,15 @@ size_t Unicode::getCharacterSize(ui8 firstByte)
return ret; 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 // 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) return false; // above maximum allowed in standard (UTF codepoints are capped at 0x0010FFFF)
if (size > maxSize) 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 // remaining characters must have highest bit set to 1
for (size_t i = 1; i < size; i++) for (size_t i = 1; i < size; i++)
{ {
if ((character[i] & 0x80) == 0) if (((ui8)character[i] & 0x80) == 0)
return false; return false;
} }
return true; 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])) 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 false;
} }
return true; 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])) 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 false;
} }
return true; return true;

View File

@ -16,11 +16,11 @@
namespace Unicode namespace Unicode
{ {
/// evaluates size of UTF-8 character /// 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 /// test if character is a valid UTF-8 symbol
/// maxSize - maximum number of bytes this symbol may consist from ( = remainer of string) /// 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) /// test if text contains ASCII-string (no need for unicode conversion)
bool DLL_LINKAGE isValidASCII(const std::string & text); bool DLL_LINKAGE isValidASCII(const std::string & text);