Add emoji keyboard

This commit is contained in:
Benau 2019-06-20 01:24:55 +08:00
parent 1389d55e35
commit 7165449da8
12 changed files with 320 additions and 70 deletions

40
data/emoji_used.txt Normal file
View File

@ -0,0 +1,40 @@
# List of emojis separated with newline used in on-screen keyboard
# Only top 38 will be used
🎉
👀
✌️
👉
👌
👍
👎
👏
❤️
💔
💯
🔥
😁
😂
😄
😅
😉
☺️
😋
😌
😍
😎
😏
😑
😒
😔
😕
😘
😜
😞
😢
😩
😪
😭
😳
😴
🙌
🙏

View File

@ -19,8 +19,10 @@
<box proportion="2" height="100%" layout="vertical-row">
<div x="1%" width="98%" height="fit" layout="horizontal-row">
<textbox id="chat" proportion="1"/>
<spacer width="3%" height="10"/>
<button id="send" width="20%" height="fit" align="center" I18N="In the network lobby" text="Send"/>
<spacer width="2%" height="10"/>
<button id="send" width="10%" height="fit" align="center" text=""/>
<spacer width="1%" height="10"/>
<button id="emoji" width="10%" height="fit" align="center" text=""/>
</div>
<spacer height="5%" width="10"/>
<label id="timeout-message" x="1%" width="98%" proportion="1" text="" word_wrap="true" align="center"/>

View File

@ -408,7 +408,7 @@ struct SEvent
struct SKeyInput
{
//! Character corresponding to the key (0, if not a character, value undefined in key releases)
wchar_t Char;
char32_t Char;
//! Key which has been pressed or released
EKEY_CODE Key;

View File

@ -0,0 +1,110 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2019 SuperTuxKart-Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License: or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "guiengine/emoji_keyboard.hpp"
#include "io/file_manager.hpp"
#include "utils/string_utils.hpp"
#include "utils/utf8.h"
#include <fstream>
#include <memory>
#include <string>
using namespace GUIEngine;
// ============================================================================
ScreenKeyboard::KeyboardLayoutProportions
EmojiKeyboard::getKeyboardLayoutProportions() const
{
return
{
{2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
{2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
{2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
{2, 2, 2, 2, 2, 2, 2, 2, 2, 2}
};
} // getKeyboardLayoutProportions
// ============================================================================
ScreenKeyboard::KeyboardLayout*
EmojiKeyboard::getKeyboardLayout(ButtonsType bt) const
{
static KeyboardLayout emojis =
{
{"?", "?", "?", "?", "?", "?", "?", "?", "?", "?"},
{"?", "?", "?", "?", "?", "?", "?", "?", "?", "?"},
{"?", "?", "?", "?", "?", "?", "?", "?", "?", "Back"},
{"?", "?", "?", "?", "?", "?", "?", "?", "?", "Enter"}
};
static bool loaded_emojis = false;
if (!loaded_emojis)
{
const std::string file_name = file_manager->getAsset("emoji_used.txt");
std::vector<std::string> emoji_chars;
try
{
std::unique_ptr<std::istream> in(new std::ifstream(file_name.c_str()));
if (!in.get())
{
Log::error("EmojiKeyboard", "Error: failure opening: '%s'.",
file_name.c_str());
}
else
{
std::string line;
while (!StringUtils::safeGetline(*in, line).eof())
{
// Check for possible bom mark
if (line[0] == '#' ||
utf8::starts_with_bom(line.begin(), line.end()))
continue;
emoji_chars.push_back(line);
}
}
}
catch (std::exception& e)
{
Log::error("EmojiKeyboard", "Error: failure emoji used file.");
Log::error("EmojiKeyboard", "%s", e.what());
}
// Default if no such emoji in case errors
emoji_chars.resize(38, "?");
unsigned emoji_copied = 0;
for (unsigned i = 0; i < emojis.size(); i++)
{
for (unsigned j = 0; j < emojis[i].size(); j++)
{
std::string& emoji = emojis[i][j];
if (emoji == "Back" || emoji == "Enter")
continue;
emoji = emoji_chars[emoji_copied++];
}
}
loaded_emojis = true;
}
KeyboardLayout* keys = NULL;
switch (bt)
{
case BUTTONS_EMOJI:
keys = &emojis;
break;
default:
break;
};
return keys;
} // getKeyboardLayout

View File

@ -0,0 +1,42 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2019 SuperTuxKart-Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License: or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef HEADER_EMOJI_KEYBOARD_HPP
#define HEADER_EMOJI_KEYBOARD_HPP
#include "guiengine/screen_keyboard.hpp"
#include "utils/cpp2011.hpp"
/**
* \ingroup guiengine
*/
namespace GUIEngine
{
class EmojiKeyboard : public ScreenKeyboard
{
public:
LEAK_CHECK()
EmojiKeyboard(float percent_width, float percent_height, CGUIEditBox* edit_box)
: ScreenKeyboard(percent_width, percent_height, edit_box) {}
virtual KeyboardLayoutProportions getKeyboardLayoutProportions() const OVERRIDE;
virtual KeyboardLayout* getKeyboardLayout(ButtonsType bt) const OVERRIDE;
virtual ButtonsType getDefaultButtonsType() const OVERRIDE { return BUTTONS_EMOJI; }
};
}
#endif

View File

@ -26,45 +26,75 @@
#include "guiengine/widgets/CGUIEditBox.hpp"
#include "states_screens/state_manager.hpp"
#include "utils/log.hpp"
#include "utils/string_utils.hpp"
#include <algorithm>
#include <string>
using namespace GUIEngine;
// ============================================================================
ScreenKeyboard::KeyboardLayoutProportions
ScreenKeyboard::getKeyboardLayoutProportions() const
{
return
{
{2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
{1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1},
{2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
{3, 2, 10, 2, 3}
};
} // getKeyboardLayoutProportions
typedef std::vector<std::vector<std::string> > KeyboardLayout;
typedef std::vector<std::vector<int> > KeyboardLayoutProportions;
KeyboardLayout layout_lower =
// ============================================================================
ScreenKeyboard::KeyboardLayout*
ScreenKeyboard::getKeyboardLayout(ButtonsType bt) const
{
static KeyboardLayout layout_lower =
{{"q", "w", "e", "r", "t", "y", "u", "i", "o", "p"},
{"Separator", "a", "s", "d", "f", "g", "h", "j", "k", "l", "Separator"},
{"Shift", "z", "x", "c", "v", "b", "n", "m", "?", "Back"},
{"123", ",", "Space", ".", "Enter"}};
KeyboardLayout layout_upper =
static KeyboardLayout layout_upper =
{{"Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"},
{"Separator", "A", "S", "D", "F", "G", "H", "J", "K", "L", "Separator"},
{"Shift", "Z", "X", "C", "V", "B", "N", "M", "!", "Back"},
{"123", ",", "Space", ".", "Enter"}};
KeyboardLayout layout_digits =
static KeyboardLayout layout_digits =
{{"1", "2", "3", "4", "5", "6", "7", "8", "9", "0"},
{"Separator", "@", "#", "$", "%", "^", "&", "*", "(", ")", "Separator"},
{"Shift", "-", "+", ":", ";", "\"", "\'", "/", "?", "Back"},
{"Text", ",", "Space", ".", "Enter"}};
KeyboardLayout layout_digits2 =
static KeyboardLayout layout_digits2 =
{{"1", "2", "3", "4", "5", "6", "7", "8", "9", "0"},
{"Separator", "@", "[", "]", "{", "}", "~", "`", "\\", "|", "Separator"},
{"Shift", "_", "=", ":", ";", "<", ">", "/", "!", "Back"},
{"Text", ",", "Space", ".", "Enter"}};
KeyboardLayoutProportions layout_proportions =
{{2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
{1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1},
{2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
{3, 2, 10, 2, 3}};
KeyboardLayout* keys = NULL;
switch (bt)
{
case BUTTONS_LOWER:
keys = &layout_lower;
break;
case BUTTONS_UPPER:
keys = &layout_upper;
break;
case BUTTONS_DIGITS:
keys = &layout_digits;
break;
case BUTTONS_DIGITS2:
keys = &layout_digits2;
break;
default:
break;
};
return keys;
} // getKeyboardLayout
// ============================================================================
ScreenKeyboard* ScreenKeyboard::m_screen_keyboard = NULL;
// ----------------------------------------------------------------------------
@ -95,8 +125,6 @@ ScreenKeyboard::ScreenKeyboard(float percent_width, float percent_height,
m_repeat_time = 0;
m_back_button_pressed = false;
m_schedule_close = false;
init();
} // ScreenKeyboard
// ----------------------------------------------------------------------------
@ -115,7 +143,8 @@ ScreenKeyboard::~ScreenKeyboard()
} // ~ScreenKeyboard
// ----------------------------------------------------------------------------
/** Internal function that makes whole screen keyboard initialization
/** Screen keyboard initialization, needs to be called after new to take into
* account for runtime polymorphism
*/
void ScreenKeyboard::init()
{
@ -148,7 +177,7 @@ void ScreenKeyboard::init()
input_manager->setMode(InputManager::MENU);
createButtons();
assignButtons(BUTTONS_LOWER);
assignButtons(getDefaultButtonsType());
Widget* button_widget = getWidget<ButtonWidget>("Back");
assert(button_widget != NULL);
@ -160,6 +189,7 @@ void ScreenKeyboard::init()
*/
void ScreenKeyboard::createButtons()
{
const auto& layout_proportions = getKeyboardLayoutProportions();
int rows_num = layout_proportions.size();
int pos_y = 3;
@ -219,11 +249,9 @@ void ScreenKeyboard::createButtons()
} // createButtons
// ----------------------------------------------------------------------------
std::wstring ScreenKeyboard::getKeyName(std::string key_id)
core::stringw ScreenKeyboard::getKeyName(std::string key_id)
{
std::wstring key_name;
core::stringw key_name;
if (key_id == "Enter")
{
key_name = L"\u21B2";
@ -242,10 +270,8 @@ std::wstring ScreenKeyboard::getKeyName(std::string key_id)
}
else
{
std::wstring tmp(key_id.begin(), key_id.end());
key_name = tmp;
key_name = StringUtils::utf8ToWide(key_id);
}
return key_name;
}
@ -255,30 +281,11 @@ std::wstring ScreenKeyboard::getKeyName(std::string key_id)
*/
void ScreenKeyboard::assignButtons(ButtonsType buttons_type)
{
const auto& layout_proportions = getKeyboardLayoutProportions();
int rows_num = layout_proportions.size();
m_buttons_type = buttons_type;
KeyboardLayout* keys = NULL;
switch (buttons_type)
{
case BUTTONS_LOWER:
keys = &layout_lower;
break;
case BUTTONS_UPPER:
keys = &layout_upper;
break;
case BUTTONS_DIGITS:
keys = &layout_digits;
break;
case BUTTONS_DIGITS2:
keys = &layout_digits2;
break;
default:
keys = NULL;
break;
};
KeyboardLayout* keys = getKeyboardLayout(buttons_type);
unsigned int current_button_id = 0;
@ -289,7 +296,7 @@ void ScreenKeyboard::assignButtons(ButtonsType buttons_type)
for (int j = 0; j < cols_num; j++)
{
std::string key = keys != NULL ? (*keys)[i][j] : "?";
std::wstring key_name = getKeyName(key);
const core::stringw& key_name = getKeyName(key);
assert(current_button_id < m_buttons.size());
ButtonWidget* button = m_buttons[current_button_id];
@ -301,7 +308,7 @@ void ScreenKeyboard::assignButtons(ButtonsType buttons_type)
else
{
button->setVisible(true);
button->setText(key_name.c_str());
button->setText(key_name);
button->m_properties[PROP_ID] = key;
}
@ -408,6 +415,7 @@ EventPropagation ScreenKeyboard::processEvent(const std::string& eventSource)
SEvent event;
bool send_event = false;
bool close_keyboard = false;
std::vector<char32_t> chars;
if (eventSource == "Shift")
{
@ -440,7 +448,7 @@ EventPropagation ScreenKeyboard::processEvent(const std::string& eventSource)
else if (eventSource == "Enter")
{
event.KeyInput.Key = IRR_KEY_RETURN;
event.KeyInput.Char = 0;
chars.push_back(0);
send_event = true;
close_keyboard = true;
}
@ -452,13 +460,16 @@ EventPropagation ScreenKeyboard::processEvent(const std::string& eventSource)
else if (eventSource == "Space")
{
event.KeyInput.Key = IRR_KEY_UNKNOWN;
event.KeyInput.Char = ' ';
chars.push_back(U' ');
send_event = true;
}
else if (eventSource.size() > 0)
{
event.KeyInput.Key = IRR_KEY_UNKNOWN;
event.KeyInput.Char = eventSource.at(0);
// For possible emoji ligatures
const std::u32string& s = StringUtils::utf8ToUtf32(eventSource);
for (char32_t c : s)
chars.push_back(c);
send_event = true;
}
@ -468,8 +479,11 @@ EventPropagation ScreenKeyboard::processEvent(const std::string& eventSource)
event.KeyInput.PressedDown = true;
event.KeyInput.Control = false;
event.KeyInput.Shift = false;
m_edit_box->OnEvent(event);
for (char32_t c : chars)
{
event.KeyInput.Char = c;
m_edit_box->OnEvent(event);
}
}
if (close_keyboard)

View File

@ -47,16 +47,19 @@ namespace GUIEngine
class ScreenKeyboard : public SkinWidgetContainer,
public AbstractTopLevelContainer
{
private:
protected:
typedef std::vector<std::vector<std::string> > KeyboardLayout;
typedef std::vector<std::vector<int> > KeyboardLayoutProportions;
enum ButtonsType
{
BUTTONS_NONE,
BUTTONS_LOWER,
BUTTONS_UPPER,
BUTTONS_DIGITS,
BUTTONS_DIGITS2
BUTTONS_DIGITS2,
BUTTONS_EMOJI
};
private:
/** Global instance of the current screen keyboard */
static ScreenKeyboard* m_screen_keyboard;
@ -98,10 +101,9 @@ namespace GUIEngine
/** Remembered input mode that was used before keyboard creation */
InputManager::InputDriverMode m_previous_mode;
void init();
void createButtons();
void assignButtons(ButtonsType buttons_type);
std::wstring getKeyName(std::string key_id);
core::stringw getKeyName(std::string key_id);
public:
LEAK_CHECK()
@ -110,6 +112,8 @@ namespace GUIEngine
CGUIEditBox* edit_box);
~ScreenKeyboard();
void init();
virtual EventPropagation processEvent(const std::string& eventSource);
static void dismiss();
@ -146,6 +150,15 @@ namespace GUIEngine
/** Returns assigned edit box */
CGUIEditBox* getEditBox() {return m_edit_box;}
virtual KeyboardLayoutProportions getKeyboardLayoutProportions() const;
virtual KeyboardLayout* getKeyboardLayout(ButtonsType bt) const;
virtual ButtonsType getDefaultButtonsType() const
{
return BUTTONS_LOWER;
}
};
}

View File

@ -1268,7 +1268,7 @@ void CGUIEditBox::setTextRect(s32 line)
}
void CGUIEditBox::inputChar(wchar_t c)
void CGUIEditBox::inputChar(char32_t c)
{
if (!isEnabled())
return;
@ -1476,7 +1476,8 @@ void CGUIEditBox::openScreenKeyboard()
if (GUIEngine::ScreenKeyboard::getCurrent() != NULL)
return;
new GUIEngine::ScreenKeyboard(1.0f, 0.40f, this);
GUIEngine::ScreenKeyboard* k = new GUIEngine::ScreenKeyboard(1.0f, 0.40f, this);
k->init();
}
// Real copying is happening in text_box_widget.cpp with static function

View File

@ -137,7 +137,7 @@ namespace GUIEngine
//! sets the area of the given line
void setTextRect(s32 line);
//! adds a letter to the edit box
void inputChar(wchar_t c);
void inputChar(char32_t c);
//! calculates the current scroll position
void calculateScrollPos();
//! send some gui event to parent

View File

@ -26,6 +26,7 @@
#include "font/font_manager.hpp"
#include "graphics/irr_driver.hpp"
#include "guiengine/CGUISpriteBank.hpp"
#include "guiengine/emoji_keyboard.hpp"
#include "guiengine/scalable_font.hpp"
#include "guiengine/widgets/CGUIEditBox.hpp"
#include "guiengine/widgets/button_widget.hpp"
@ -114,6 +115,9 @@ void NetworkingLobby::loadedFromFile()
m_send_button = getWidget<ButtonWidget>("send");
assert(m_send_button != NULL);
m_emoji_button = getWidget<ButtonWidget>("emoji");
assert(m_emoji_button != NULL);
m_icon_bank = new irr::gui::STKModifiedSpriteBank(GUIEngine::getGUIEnv());
video::ITexture* icon_1 = irr_driver->getTexture
(file_manager->getAsset(FileManager::GUI_ICON, "crown.png"));
@ -191,6 +195,12 @@ void NetworkingLobby::init()
m_chat_box->setTextBoxType(TBT_CAP_SENTENCES);
m_send_button->setVisible(false);
m_send_button->setActive(false);
// Unicode enter arrow
m_send_button->setText(L"\u21B2");
m_emoji_button->setVisible(false);
m_emoji_button->setActive(false);
// Unicode smile emoji
m_emoji_button->setText(L"\u263A");
// Connect to server now if we have saved players and not disconnected
if (!LobbyProtocol::get<LobbyProtocol>() &&
@ -212,6 +222,8 @@ void NetworkingLobby::init()
m_chat_box->setActive(true);
m_send_button->setVisible(true);
m_send_button->setActive(true);
m_emoji_button->setVisible(true);
m_emoji_button->setActive(true);
}
else
{
@ -221,6 +233,8 @@ void NetworkingLobby::init()
m_chat_box->setActive(false);
m_send_button->setVisible(true);
m_send_button->setActive(false);
m_emoji_button->setVisible(true);
m_emoji_button->setActive(false);
}
if (auto cl = LobbyProtocol::get<ClientLobby>())
{
@ -315,11 +329,13 @@ void NetworkingLobby::onUpdate(float delta)
{
m_chat_box->setActive(true);
m_send_button->setActive(true);
m_emoji_button->setActive(true);
}
else if (!cl->serverEnabledChat() && m_send_button->isActivated())
{
m_chat_box->setActive(false);
m_send_button->setActive(false);
m_emoji_button->setActive(false);
}
}
if (cl && cl->isWaitingForGame())
@ -586,6 +602,13 @@ void NetworkingLobby::eventCallback(Widget* widget, const std::string& name,
cl->sendChat(m_chat_box->getText());
m_chat_box->setText("");
} // send chat message
else if (name == m_emoji_button->m_properties[PROP_ID] &&
!ScreenKeyboard::isActive())
{
EmojiKeyboard* ek = new EmojiKeyboard(1.0f, 0.40f,
m_chat_box->getIrrlichtElement<CGUIEditBox>());
ek->init();
}
else if (name == m_start_button->m_properties[PROP_ID])
{
if (m_client_live_joinable)
@ -747,6 +770,8 @@ void NetworkingLobby::finishAddingPlayers()
m_chat_box->setActive(true);
m_send_button->setVisible(true);
m_send_button->setActive(true);
m_emoji_button->setVisible(true);
m_emoji_button->setActive(true);
}
else
{
@ -755,6 +780,8 @@ void NetworkingLobby::finishAddingPlayers()
m_chat_box->setActive(false);
m_send_button->setVisible(true);
m_send_button->setActive(false);
m_emoji_button->setVisible(true);
m_emoji_button->setActive(false);
}
} // finishAddingPlayers

View File

@ -96,6 +96,7 @@ private:
GUIEngine::ListWidget* m_player_list;
GUIEngine::TextBoxWidget* m_chat_box;
GUIEngine::ButtonWidget* m_send_button;
GUIEngine::ButtonWidget* m_emoji_button;
irr::gui::STKModifiedSpriteBank* m_icon_bank;