1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +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

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

View File

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

View File

@ -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();

View File

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

View File

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

View File

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

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
*
@ -18,6 +8,16 @@ class CGuiHandler;
*
*/
#pragma once
#include <SDL_events.h>
#include "Geometries.h"
#include "../Graphics.h"
struct SDL_Surface;
class CPicture;
class CGuiHandler;
using boost::logic::tribool;
// Defines a activate/deactive method
@ -124,6 +124,11 @@ public:
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
virtual void mouseMoved (const SDL_MouseMotionEvent & sEvent){}
@ -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);

View File

@ -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,11 +1809,13 @@ void CFocusable::giveFocus()
if(inputWithFocus)
{
inputWithFocus->focus = false;
inputWithFocus->focusLost();
inputWithFocus->redraw();
}
focus = true;
inputWithFocus = this;
focusGot();
redraw();
}

View File

@ -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;
@ -467,6 +473,13 @@ public:
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);
//Filter that will allow only input of numbers in range min-max (min-max are allowed)

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:
// 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;

View File

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