Hi!
I'm implementing a small interface for a game, with multiple focus handing. Keyboard can be used by several players playing at the same time, each player having its own mapping to navigate in menus. When a player activates a text entry control, he gains full access to the keyboard until he finished typing.
Since keys pressed when typing text in entry must not be handled for menu navigation, there are two input modes: a "key mode" in which KeyPressed are handled for menu navigation and TextEntered are ignored, and a "text mode" in which TextEntered and non-text KeyPressed are handled for typing and editing (backspacing, etc.).
I'm having issues handling the transition from one mode to another.
After entering text mode, I wait for the next KeyPressed until actually handling TextEntered to avoid handling pending the TextEntered triggered by the previous KeyPressed. But this trick is not sufficient for characters typed using multiple keys (typically dead keys). For instance, on a French keyboard,
ê is typed with
^ +
e.
Here is text code implementing this:
#include <string>
#include <iostream>
#include <SFML/Window.hpp>
int main()
{
sf::Window window(sf::VideoMode(100, 100), "");
window.setFramerateLimit(60);
bool text_mode_enabled = false;
bool text_mode_active = false;
while(window.isOpen()) {
sf::Event event;
while(window.pollEvent(event)) {
if(event.type == sf::Event::Closed) {
window.close();
} else if(event.type == sf::Event::MouseButtonPressed) {
// toggle text mode on click
text_mode_enabled = !text_mode_enabled;
text_mode_active = false;
std::cout << "text mode: " << (text_mode_enabled ? "enabled" : "disabled") << std::endl;
} else if(event.type == sf::Event::KeyPressed) {
if(text_mode_enabled) {
// actually active text mode once a key has been pressed
text_mode_active = true;
} else {
// key mode: handle KeyPressed normally
std::cout << "press: " << event.key.code << std::endl;
}
} else if(event.type == sf::Event::TextEntered) {
if(text_mode_active) {
// handle TextEntered only if text mode is active
auto s = sf::String{event.text.unicode}.toWideString();
std::wcout << "text: " << event.text.unicode << " (" << s << ")" << std::endl;
}
}
}
window.setActive();
window.display();
}
}
With the following sequence:
-
^ : navigate in menus
-
click : enable text mode
-
e : first input text
The result is:
press: 47
text mode: enabled
text: 234 (ê)
The user typed
e, but input text is
ê because of the previous
^ used to navigate (before text mode is enabled).
Is there a workaround to this problem?
A clean solution would be to be able to flush pending input and partially input text in SFML, but I doubt it is actually possible on all supported platforms.