Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: [SOLVED]basic text box  (Read 12809 times)

0 Members and 1 Guest are viewing this topic.

Foz44

  • Newbie
  • *
  • Posts: 6
    • View Profile
    • Email
[SOLVED]basic text box
« on: February 05, 2020, 06:03:40 am »
If I were relatively new to C++ and SFML and were looking for a way to incorporate into my game a text box for user input - something vaguely like a dos shell - would there be a tutorial or guide you might suggest?
« Last Edit: February 17, 2020, 04:35:05 am by Foz44 »

Fx8qkaoy

  • Newbie
  • *
  • Posts: 42
    • View Profile
Re: basic text box
« Reply #1 on: February 06, 2020, 10:12:25 am »
If I were relatively new to C++ and SFML and were looking for a way to incorporate into my game a text box for user input - something vaguely like a dos shell - would there be a tutorial or guide you might suggest?

DOS shell with SFML? U could make an array of text. When receiving input and shell on focus, append the char to last text obj. On new line u could remove from top of array an obj and add at end another one. I recommend having some exp with C++ before diving in cause is not easy for beginners (maybe)

Foz44

  • Newbie
  • *
  • Posts: 6
    • View Profile
    • Email
Re: basic text box
« Reply #2 on: February 07, 2020, 05:08:29 am »
I found most of what I needed in an old post - I can now save user input into a string and 'echo' each char to the screen at the location of a prompt like I want:

 while (window.pollEvent(e)) {
     if (e.type == Event::KeyPressed) {              
         if (e.type == Event::TextEntered) {
              if (e.text.unicode < 128) {
                  input += static_cast<char>(e.text.unicode);
                  output.setFillColor(Color::White);
                  output.setString(input);
              }
          }
      }
.
.
.
window.draw(prompt);
window.draw(output);

What I need now is a way to make the 'backspace' button work like it's supposed to. Will I have to manually pop a char off the end of the string for each "Keyboard::Backspace" detected, or does SFML have something for that?

Fx8qkaoy

  • Newbie
  • *
  • Posts: 42
    • View Profile
Re: basic text box
« Reply #3 on: February 07, 2020, 06:30:55 am »
I found most of what I needed in an old post - I can now save user input into a string and 'echo' each char to the screen at the location of a prompt like I want:

 while (window.pollEvent(e)) {
     if (e.type == Event::KeyPressed) {              
         if (e.type == Event::TextEntered) {
              if (e.text.unicode < 128) {
                  input += static_cast<char>(e.text.unicode);
                  output.setFillColor(Color::White);
                  output.setString(input);
              }
          }
      }
.
.
.
window.draw(prompt);
window.draw(output);

What I need now is a way to make the 'backspace' button work like it's supposed to. Will I have to manually pop a char off the end of the string for each "Keyboard::Backspace" detected, or does SFML have something for that?

If u have a std::string called input which u use to set the Text object, then u need to pop the last char from std::string. There is a fuction substr for std::string which will return a substring (basically u cut the string)

Foz44

  • Newbie
  • *
  • Posts: 6
    • View Profile
    • Email
Re: basic text box
« Reply #4 on: February 08, 2020, 04:03:22 am »
OK, "input = input.supstr(0, input.size() - 2)" was the answer. (I had to make it "input.size() - "2" instead of "1" so that it would also remove the strange character that the BackSpace key itself made.) Thank you, Fx8akaoy, for the help.

For the benefit of anyone else not very familiar with C++ strings who may come across this thread, the internet says that std::string::pop_back() would do exactly what I needed. It did not work at all...
« Last Edit: February 08, 2020, 04:24:47 am by Foz44 »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: [SOLVED] basic text box
« Reply #5 on: February 08, 2020, 10:30:41 am »
You need to filter out non-printable characters in the TextEntered event, so that, for example, backspace doesn't add an unwanted character to your string. And once your string only contains printable characters, you won't have to erase 2 characters, and you can use std::string::pop_back, which is much more efficient than using substr.
Laurent Gomila - SFML developer

Foz44

  • Newbie
  • *
  • Posts: 6
    • View Profile
    • Email
Re:basic text box
« Reply #6 on: February 13, 2020, 09:45:50 pm »
Just when I thought it was safe to go back into the water...<i'm returning this to 'unsolved'>. I originally thought I was doing just that with the line "if(text.unicode < 128)", until I saw that the Unicode for the 'BackSpace' key is '8'. And nothing I've seen yet on the internet is very clear, so just how DO I filter out non-printable characters?
« Last Edit: February 13, 2020, 09:54:20 pm by Foz44 »

Fx8qkaoy

  • Newbie
  • *
  • Posts: 42
    • View Profile
Re:basic text box
« Reply #7 on: February 14, 2020, 07:38:32 am »
Just when I thought it was safe to go back into the water...<i'm returning this to 'unsolved'>. I originally thought I was doing just that with the line "if(text.unicode < 128)", until I saw that the Unicode for the 'BackSpace' key is '8'. And nothing I've seen yet on the internet is very clear, so just how DO I filter out non-printable characters?

Take a look at the ASCII table

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: basic text box
« Reply #8 on: February 14, 2020, 07:49:02 am »
std::isprint works fine for the ASCII range. For the rest of the Unicode world, you'd probably need a 3rd party library.
Laurent Gomila - SFML developer

Mortal

  • Sr. Member
  • ****
  • Posts: 284
    • View Profile
Re: basic text box
« Reply #9 on: February 14, 2020, 08:00:12 am »
it quite easy to retrieve all ASCII characters programmatically, just run a for-loop to print out all characters that range from 0 to 255. unless i misunderstood what you are trying to achieve.

for (int i = 0; i < 255; i++)
    std::cout << i << ' ' << (char)i << '\n';
« Last Edit: February 14, 2020, 08:30:36 am by Mortal »

Foz44

  • Newbie
  • *
  • Posts: 6
    • View Profile
    • Email
Re: basic text box
« Reply #10 on: February 14, 2020, 10:58:14 pm »
Sorry - I'll try to give a better picture of what I'm after. I wanted to simulate the basic construct of the old msdos ui, where the user can type simple commands at the C:\ prompt, for example, and bring up various menus. At first I thought I found exactly what I needed with the example:

while (window.pollEvent(e)) {
     if (e.type == Event::KeyPressed) {             
         if (e.type == Event::TextEntered) {
              if (e.text.unicode < 128) {
                  input += static_cast<char>(e.text.unicode);

from my previous messages: I could use the 'input' variable to echo the characters back to the screen as the user typed them as well as add to input's position.y to bring the prompt down to the next line whenever Unicode 8 (return key) was detected, for example. the problem I'm having is that certain keys (the BackSpace key - Unicode 8, in my case) add unwanted characters to the 'input' string. What I need is to parse the string in such a way that only pure, printable char's are left. That way I could, among other things, use code such as:  if(input == "list") { MyClass.displayList(); }, or - as Laurent pointed out - use the more efficient string::pop_back() when the user presses BackSpace instead of 'substr()' that I have to use now...

Mortal

  • Sr. Member
  • ****
  • Posts: 284
    • View Profile
Re: basic text box
« Reply #11 on: February 15, 2020, 07:51:53 am »
The for-loop will tell you all information you need, the printable ASCII characters and its index position For example:If i run the for-loop in my PC it will print something like this:

30 �
31 �
32 
33 !
34 "
35 #
36 $
37 %
 ........
122 z
123 {
124 |
125 }
126 ~
127 
128 �
129 �


From the output, we can easily now detect all of printable chars and its indices which is clearly the printable chars start from index 32 upto 127. now, we need to add this boundary check to SFML event-loop:

//if (event.text.unicode >= 32 && event.text.unicode <= 127)
if (std::isprint(event.text.unicode)) // thanks to Laurent
    input_text += event.text.unicode;

Don't worry about if user press BackSpace or Return/Enter keys, sf::Text will take care for these inputs internally, just supply the string to sf::Text::setString with escape char in event-loop:

if (event.key.code == sf::Keyboard::BackSpace) {
    if (!input_text.empty())
        input_text.pop_back();// as Laurent pointed out
        //input_text.erase(--input_text.end());
}
if (event.key.code == sf::Keyboard::Return){
        input_text += '\n';
}


That's it, we have accomplished a minimal text editor.

I added a simple blinking effect to “cursor”. It was a part of my early demos when I was working out with the SFML library. ;D


Here the complete source source:
#include <SFML/Graphics.hpp>
#include <locale>

int main()
{
    sf::RenderWindow window({ 640 ,480 }, "test");

    std::string input_text;
    sf::Font font;
    font.loadFromFile("resources/font.ttf");
    sf::Text text("", font);

    sf::Clock clock;

    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
                window.close();
            else if (event.type == sf::Event::TextEntered) {
                if (std::isprint(event.text.unicode))
                    input_text += event.text.unicode;
            }
            else if (event.type == sf::Event::KeyPressed) {
                if (event.key.code == sf::Keyboard::BackSpace) {
                    if (!input_text.empty())
                        input_text.pop_back();
                }
                if (event.key.code == sf::Keyboard::Return) {
                        input_text += '\n';
                }
            }
        }

        static sf::Time text_effect_time;
        static bool show_cursor;

        text_effect_time += clock.restart();

        if (text_effect_time >= sf::seconds(0.5f))
        {
            show_cursor = !show_cursor;
            text_effect_time = sf::Time::Zero;
        }

        text.setString(input_text + (show_cursor ? '_' : ' '));

        window.clear();
        window.draw(text);
        window.display();
    }
}
« Last Edit: February 15, 2020, 10:04:18 pm by Mortal »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: basic text box
« Reply #12 on: February 15, 2020, 09:18:02 pm »
Or use std::isprint and rely on the standard library, instead of trying to reverse-engineer the ASCII table.
Laurent Gomila - SFML developer

Mortal

  • Sr. Member
  • ****
  • Posts: 284
    • View Profile
Re: basic text box
« Reply #13 on: February 15, 2020, 10:01:30 pm »
excellent point Laurent.
yes, indeed, it's quite useful, gotta update my code example.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: basic text box
« Reply #14 on: February 16, 2020, 09:37:58 am »
You may still need the < 128 check, because I don't think std::isprint is defined on the whole Unicode range. It may not even use the current locale, so only the ASCII range would be safe to test. Anyway, its documentation should be read to sort out these details.
Laurent Gomila - SFML developer