Ввод текста на экране в SFML - PullRequest
0 голосов
/ 12 декабря 2018

Итак, я создаю графический калькулятор.У меня есть входная строка с.Из строки я могу построить график с использованием SFML.Я начинаю с x-координаты MIN до x-координаты MAX, получаю соответствующий y из метода EvaluateString () и все координаты в VertexArray v. Я уже написал свой метод и метод построения графиков, и все это работало хорошо.

Однако у меня небольшая проблема.Я хочу ввести свою строку на экране, например, "sin (cos (tan (x)))", как это.Я изо всех сил пытаюсь найти способ сделать это.Я вроде понял, что это связано с событием TextEntered, но все же я не могу ничего найти полностью.

Пожалуйста, предложите мне способ.

Sample graph

class Calculator{
public:
    void main();
private:
    WindowSize DefaultWindow;
    sf::RenderWindow window;
    Cartesian vertexX[2],vertexY[2];
    sf::Vertex axis[4];
    const double MAX = 10;
    const double MIN = -10;
    const double INCREMENT = 0.001;

};

int main(){ 
    DefaultWindow.Max = Cartesian(10,10);
    DefaultWindow.Min = Cartesian(-10,-10);
    DefaultWindow.plane.width=1500;
    DefaultWindow.plane.height=1500;

    // Set up x and y-axis
    vertexX[0] = Cartesian(-100,0);
    vertexX[1] = Cartesian(100, 0);
    vertexY[0] = Cartesian(0,-100);
    vertexY[1] = Cartesian(0,100);

    axis[0] = sf::Vertex(convertCartesiantoWindow(vertexX[0],DefaultWindow));
    axis[1] = sf::Vertex(convertCartesiantoWindow(vertexX[1],DefaultWindow));
    axis[2] = sf::Vertex(convertCartesiantoWindow(vertexY[0],DefaultWindow));
    axis[3] = sf::Vertex(convertCartesiantoWindow(vertexY[1],DefaultWindow));

    // Set up the window
    window.create(sf::VideoMode(1500, 1500), "Graphing calculator");

    // Input string
    string s = "sin(cos(tan(x)))";

    // Stack c contains all the Cartesian coordinate vertices
    // Cartesian is a struct which contains x and y coordinates
    Stack<Cartesian> c;

    sf::VertexArray v;

    // For a certain function in string s, I evaluate it 
    // and return the y_coordinate from the function EvaluateString (s, i)
    // Push each (x,y) evaluated in the Stack c
    for (double i = MIN; i <= MAX; i+= INCREMENT)
        c.Push(Cartesian(i,EvaluateString(s,i)));

    // v is VertexArray which contains all the vertices (x,y)
    v = plot(DefaultWindow, c);


    while (window.isOpen()) {
        sf::Event event;
        while (window.pollEvent(event)) {

            switch (event.type) {
                case sf::Event::Closed:
                    window.close();
                    break;
            }
        }
    }

    // Draw the graph
    window.clear(sf::Color::Black);
    window.draw(axis,4,sf::Lines);
    window.draw(v);
    window.display();
}

Ответы [ 2 ]

0 голосов
/ 13 декабря 2018

Поскольку @ super предлагает , использование библиотеки было бы хорошим решением, и, безусловно, лучше, чем мое, но на случай, если это удовлетворит ваши потребности, я реализовал super basic TextField class.

Может быть много ошибок, но это может дать вам представление о том, как достичь этой функциональности.

A TextField - это не более чем прямоугольник, который содержит текст .Поскольку он будет иметь sf::Text, он должен иметь sf::Font.Кроме того, я ограничиваю количество символов, которое оно будет содержать.Для того, чтобы мы могли писать внутри TextField, мы должны знать, выбран ли он, то есть имеет ли он focus .Итак, первый подход может быть следующим:

class TextField : public sf::Transformable, public sf::Drawable{
    private:
        unsigned int m_size;
        sf::Font m_font;
        std::string m_text;
        sf::RectangleShape m_rect;
        bool m_hasfocus;
};

Нам нужен конструктор для этого класса:

class TextField : public sf::Transformable, public sf::Drawable{
    public:
        TextField(unsigned int maxChars) :
            m_size(maxChars),
            m_rect(sf::Vector2f(15 * m_size, 20)), // 15 pixels per char, 20 pixels height, you can tweak
            m_hasfocus(false)
        {
            m_font.loadFromFile("C:/Windows/Fonts/Arial.ttf"); // I'm working on Windows, you can put your own font instead
            m_rect.setOutlineThickness(2);
            m_rect.setFillColor(sf::Color::White);
            m_rect.setOutlineColor(sf::Color(127,127,127));
            m_rect.setPosition(this->getPosition());
        }

    private:
        unsigned int m_size;
        sf::Font m_font;
        std::string m_text;
        sf::RectangleShape m_rect;
        bool m_hasfocus;
};

Нам также нужны некоторые базовые методы, мы хотим получить текст внутри:

const std::string sf::TextField::getText() const{
    return m_text;
}

и переместите его, поместив его где-нибудь внутри нашего окна:

void sf::TextField::setPosition(float x, float y){
    sf::Transformable::setPosition(x, y);
    m_rect.setPosition(x, y);
}

это сложный вопрос.Мы перезаписываем setPosition метод из sf::Transformable, потому что нам нужно обновить наш m_rect.

Кроме того, нам нужно знать, находится ли точка внутри поля:

bool sf::TextField::contains(sf::Vector2f point) const{
    return m_rect.getGlobalBounds().contains(point);
}

довольно просто, мы используем cointains метод sf::RectangleShape, уже в .

Установите (или отключите) фокус на TextField:

void sf::TextField::setFocus(bool focus){
    m_hasfocus = focus;
    if (focus){
        m_rect.setOutlineColor(sf::Color::Blue);
    }
    else{
        m_rect.setOutlineColor(sf::Color(127, 127, 127)); // Gray color
    }
}

легкий.Для эстетики мы также меняем цвет контура рамки, когда фокусируем.

И последнее, но не менее важное, наш TextField должен вести себя как-то так, когда ввод (или sf::Event) получен:

void sf::TextField::handleInput(sf::Event e){
    if (!m_hasfocus || e.type != sf::Event::TextEntered)
        return;

    if (e.text.unicode == 8){   // Delete key
        m_text = m_text.substr(0, m_text.size() - 1);
    }
    else if (m_text.size() < m_size){
        m_text += e.text.unicode;
    }
}

Эта проверка удаления ключа немного грязная, я знаю.Может быть, вы можете найти лучшее решение.

Вот и все!Теперь main выглядит так:

int main()
{
    RenderWindow window({ 500, 500 }, "SFML", Style::Close);

    sf::TextField tf(20);
    tf.setPosition(30, 30);

    while (window.isOpen())
    {
        for (Event event; window.pollEvent(event);)
            if (event.type == Event::Closed)
                window.close();
            else if (event.type == Event::MouseButtonReleased){
                auto pos = sf::Mouse::getPosition(window);
                tf.setFocus(false);
                if (tf.contains(sf::Vector2f(pos))){
                    tf.setFocus(true);
                }
            }
            else{
                tf.handleInput(event);
            }

            window.clear();
            window.draw(tf);
            window.display();
    }
    return 0;
}

Подтверждение концепции:

enter image description here

0 голосов
/ 12 декабря 2018
std::string str;
sf::String text;

// In event loop...
if (event.Type == sf::Event::TextEntered)
{
    // Handle ASCII characters only
    if (event.Text.Unicode < 128)
    {
        str += static_cast<char>(event.Text.Unicode);
        text.SetText(str);
    }
}

// In main loop...
window.Draw(text);

Это должно создать sf :: Event :: TextEntered для ввода и sf :: String для вывода

...