How to use modifier key combinations? - c++

I'm building a basic text editor, with SFML. For this I need to save with the key combination CTRL + S.
My current solution saves when I press CTRL + S, AND produces an 's' in my editor. This extra 's' is not wanted.
This is the code I currently have:
//Main loop:
if (event.type == sf::Event::KeyPressed)
{
if (event.key.code == sf::Keyboard::S
&& event.key.control)
{
cout << "testing" << endl;
}
}
else if (event.type == sf::Event::TextEntered)
{
}
In other words: I want TextEntered to be working normally. But if I press CTRL + S, it will disable TextEntered and perform the save. How do I do this?

Just do a real-time key check inside the handler for TextEntered. e.g.
else if (event.type == sf::Event::TextEntered)
{
if (!sf::Keyboard::isKeyPressed(sf::Keyboard::LControl) &&
!sf::Keyboard::isKeyPressed(sf::Keyboard::RControl))
{
// handle text event
}
else
{
// do something else, or nothing
}
}

Related

Different behavior of events in different projects

In the last question, I had a problem with placing the cursor in the right place when clicking on the LineEdit with the mouse.
The problem was solved, I, with God's help, wrote an algorithm that solves my problem. I wrote it in an EMPTY project, where there is nothing but LineEdit. Everything worked great there. Here is the code:
if(object == ui->lineEdit_newClientPhone && event->type() == QEvent::MouseButtonRelease)
{
QString line = ui->lineEdit_newClientPhone->displayText();
qDebug() << line;
ui->lineEdit_newClientPhone->setFocus();
bool isValid = true;
for(int i = 0; i<=15; i++){
if(line[i] == '_'){
ui->lineEdit_newClientPhone->setCursorPosition(i);
isValid = false;
break;
}
}
if(isValid){
ui->lineEdit_newClientPhone->setCursorPosition(16);
}
}
It worked great. You click on any place in lineEdit, the mouse cursor is placed on the first empty "_" character. I joyfully ran to transfer it to the main project.
But then I encountered a strange behavior - in the main project, exactly copied code does not give such behavior. When you click on lineEdit with the mouse, the cursor is placed in the place where you clicked. I debugged all the events associated with this LineEdit and found strange behavior - the order of the events is different. Excluding unnecessary events, which were the MOST:
if(object == ui->lineEdit_newClientPhone){
if(event->type() != QEvent::Paint && event->type() != QEvent::MouseMove && event->type() != QEvent::HoverMove){
qDebug() << event->type();
}
}
Got this result in "EMPTY" project:
QEvent::MouseButtonPress
QEvent::MouseButtonRelease
QEvent::InputMethodQuery
But in the main project, the sequence turned out to be different:
QEvent::MouseButtonPress
QEvent::InputMethodQuery
QEvent::MouseButtonRelease
I don't know if this is the case, but in this way, my logic does not work and the cursor remains in the place where you clicked. I was able to fix the situation by changing the event to QEvent::MouseButtonRelease, but this way I get mocking cursor travels first to the place where you clicked, then to the place I need. What could be the problem and how can it be fixed? Thanks to all!
I don't think this is the right decision, so I would like to hear more solutions, but QTimer::singleShot helped me
f(object == ui->lineEdit_newClientPhone && event->type() == QEvent::MouseButtonPress){
QTimer::singleShot(0,ui->lineEdit_newClientPhone,[this]
{
QString line = ui->lineEdit_newClientPhone->displayText();
qDebug() << line;
ui->lineEdit_newClientPhone->setFocus();
bool isValid = true;
for(int i = 0; i<=15; i++){
if(line[i] == '_'){
ui->lineEdit_newClientPhone->setCursorPosition(i);
isValid = false;
break;
}
}
if(isValid){
ui->lineEdit_newClientPhone->setCursorPosition(16);
}
});
}

SDL_KEYDOWN triggering twice

I am following lazy foo's tutorial, however I realized every time I press press s or p, SDL_KEYDOWNtriggers twice. How can this be fixed?
Here is the code snippet:
while(SDL_PollEvent(&e) != 0) {
if(e.type == SDL_QUIT) {
quit = true;
}
else if(e.type == SDL_KEYDOWN) {
if(e.key.keysym.sym == SDLK_s) {
if(timer.isStarted()) {
timer.stop();
printf("stop\n");
}
else {
timer.start();
printf("start\n");
}
}
else if(e.key.keysym.sym == SDLK_p) {
if(timer.isPaused()) {
timer.unpause();
printf("unpause\n");
}
else {
timer.pause();
printf("pause\n");
}
}
}
}
Pressing s once:
start
stop
TL;DR: Check if e.key.repeat equals to 0 before handling the events.
SDL generates fake repeated keypresses if you hold a key long enough. This is used mostly for text input.
The original key press has .repeat == 0, and fake presses have .repeat == 1.
For convenience reasons probably (I'd argue that it's rather inconvenient), since SDL 2.0.5 the actual key press generates two events instead of one. One has .repeat set to 0, and other (new) one has it set to 1.

NCURSES keyboard input while calling sleep

I'm trying to use ncurses to create a game. I set it so that the character moves by arrow key input, but if I hold the arrow key for a while and then let go the character will keep moving for a while before stopping.
These are my initializations:
initscr();
start_color();
cbreak();
noecho();
nodelay(stdscr,TRUE);
keypad(stdscr, TRUE);
This is my main loop:
while(1) {
...
if (key == (char)27) {
break;
}
else if (key == KEY_DOWN) {
key = 0;
player->advance(0, 1);
}
else if (key == KEY_UP) {
key = 0;
player->advance(0, -1);
}
else if (key == KEY_LEFT) {
key = 0;
player->advance(-1, 0);
}
else if (key == KEY_RIGHT) {
key = 0;
player->advance(1, 0);
}
else {
key = getch();
}
std::this_thread::sleep_for(std::chrono::milliseconds {1000/30});
}
I'm using the sleep_for because I noticed that if I use timeout then the framrate changes if I'm pressing a key. Using sleep_for creates a consistent framerate, but input is somehow "sticky" as I explained. I need to fix this somehow. Either get a consistent framrate using timeout, or "un-stick" the input when using sleep_for.
player->advance(int, int) moves the player one step in the specified direction. Direction is specified as change in x and change in y to be applied to the current position.
You could call flushinp after each call on getch, to ignore type-ahead.

Handle mouse events SFML

I have some trouble with events in SFML. I am making a turnbased game, when the mouse has moved or left mouse button is clicked i check whos turn it is and then spawn a projectile at that objects position, when the projectile has collided with either terrain or opponent it gets destroyed and the turn is changed.
The behaviour is not what i am expecting though. When clicking shoot the projectile sometimes wont spawn at all (and it changes the turn immedeately). I have disabled all collisions so it cant be that. Im 90% sure that the issue is with how i handle events, so id really appretiate input on how i can make it better.
Of what ive learned is that you should not execute the functions in the while poll event, its only for registering what has happend most recently,so i put them outside instead. This does not solve my problem though...
sf::Event event;
sf::Vector2i mousePos;
while (_window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
_window.close();
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Escape)) {
_window.close();
}
if(event.type == sf::Event::MouseMoved) { // <- this is how you check to see if an event
mousePos = sf::Mouse::getPosition();
moved = true;
}
if(event.type == sf::Event::MouseButtonPressed) { // <- this is how you check to see if an event
mousePressed = true;
}
}
if (mousePressed && tank1Turn)
{
sf::Vector2f spawnPos = _t1->getPos() + sf::Vector2f(0, -150);
sf::Vector2f initVel = _t1->getInitVel();
cout << endl;
_p = new Projectile(spawnPos, initVel);
tank1Turn = false;
tank1Firing = true;
mousePressed = false;
}
if (mousePressed && tank2Turn) {
sf::Vector2f spawnPos = _t2->getPos()+sf::Vector2f(0,-150);
sf::Vector2f initVel = _t2->getInitVel();
_p = new Projectile(spawnPos, initVel);
tank2Turn = false;
tank2Firing = true;
mousePressed = false;
}
if (tank1Turn && moved) {
_t1->aim(mousePos);
moved = false;
mousePressed = false;
}
if (tank2Turn && moved) {
_t2->aim(mousePos);
moved = false;
mousePressed = false;
}
}
Consider this a comment. This isn't an answer (I don't have enough reputation to comment so I have to make this an answer) but here are some things I noticed:
You should either replace the ifs in your event loop with if-elses, or use a switch
sf::Keyboard::isKeyPressed is for real time input (put it inside your game update loop). In your event loop it should be sf::Event::KeyPressed and check which key it was with evt.key.code
You aren't checking which mouse button was pressed. Do it with evt.mouseButton.button (sf::Mouse::Button::Left for the left mouse)
Of what ive learned is that you should not execute the functions in the while poll event, its only for registering what has happend most recently,so i put them outside instead.
Where did you read that? I don't think that's true
There shouldn't just be an array of ifs below the event loop. Code should be structured better

SFML - Rendering a character when the key is pressed?

So I've got this program that is supposed to imitate a console (with a little coding help from this user):
#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>
#include <SFML/Window.hpp>
sf::Color fontColor;
sf::Font mainFont;
sf::Clock myClock;
bool showCursor = true;
void LoadFont() {
mainFont.loadFromFile("dos.ttf");
fontColor.r = 0;
fontColor.g = 203;
fontColor.b = 0;
}
int main() {
sf::RenderWindow wnd(sf::VideoMode(1366, 768), "SFML Console");
wnd.setSize(sf::Vector2u(1366, 768));
LoadFont();
sf::Text myTxt;
myTxt.setColor(fontColor);
myTxt.setString("System Module:");
myTxt.setFont(mainFont);
myTxt.setCharacterSize(18);
myTxt.setStyle(sf::Text::Regular);
myTxt.setPosition(0, 0);
while(wnd.isOpen()) {
sf::Event myEvent;
while (wnd.pollEvent(myEvent)) {
if (myEvent.type == sf::Event::Closed) {
wnd.close();
}
if (myEvent.type == sf::Event::KeyPressed) {
if (myEvent.key.code == sf::Keyboard::Escape) {
wnd.close();
}
}
}
wnd.clear();
if (myClock.getElapsedTime() >= sf::milliseconds(500)) {
myClock.restart();
showCursor = !showCursor;
if(showCursor == true) {
myTxt.setString("System Module:_");
} else {
myTxt.setString("System Module:");
}
}
wnd.draw(myTxt);
wnd.display();
}
}
I need to be able to let the user type a key on the keyboard, and then render that key on the screen. I'm thinking about using an std::vector of sf::Keyboard::Key, and use a while loop to check what the key is (looping through the std::vector<sf::Keyboard::Key>) without using a whole bunch of if statements, but I don't exactly know how to handle that yet, so I'd like to know if there is an easier way to accomplish my main goal. Suggestions? Comments?
Thank you for your time,
~Mike
SFML has a nice feature for this, sf::Event::TextEntered (tutorial). That is typically what you want and it avoids you to do crazy things to interpret the text entered by the user.
Stock your text entered by adding every character into a sf::String (rather than std::string, it may deal better with sfml's unicode types ~ not sure, but that would need a little check) which is then the perfect type for sf::Text::setString !
Don't hesitate to look at the docs, it has further documentation in every classes' page.
Example:
sf::String userInput;
// ...
while( wnd.pollEvent(event))
{
if(event.type == sf::Event::TextEntered)
{
/* Choose one of the 2 following, and note that the insert method
may be more efficient, as it avoids creating a new string by
concatenating and then copying into userInput.
*/
// userInput += event.text.unicode;
userInput.insert(userInput.getSize(), event.text.unicode);
}
else if(event.type == sf::Event::KeyPressed)
{
if(event.key.code == sf::Keyboard::BackSpace) // delete the last character
{
userInput.erase(userInput.getSize() - 1);
}
}
}