user3290356
user3290356

Reputation: 95

SFML calling parent's function from template derived class

I'm working on a custom project to learn SFML and I have a problem. I want to do a 'clickable' class where I could add some fancy function, my first goal to write an 'onMouseOver' function which could be nearly the same as in Javascript. This is the class:

template <class object>
class clickable: public object{
private:
    sf::Vector2i mouse;
    object& aim;
    virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const{ target.draw(aim, states); }
public:
    clickable(sf::Vector2i mouse,object& aim):mouse(mouse),aim(aim){}
    bool onMouseOver(sf::Vector2i mouse) {  
        float width = aim.getLocalBounds().width;
        float height = aim.getLocalBounds().height;
        if ((mouse.x >= aim.getPosition().x) && (mouse.x <= aim.getPosition().x + width) && (mouse.y >= aim.getPosition().y) && (mouse.y <= aim.getPosition().y + height))
            return true;
        else return false;
    }
};

And here is my main:

    int main()
{
    sf::Font font;
    if (!font.loadFromFile("arial.ttf")) {
        std::cout << "Error!";
    }
    sf::RenderWindow window(sf::VideoMode(800, 600), "Title", sf::Style::Default);
    sf::Vector2i mouse = sf::Mouse::getPosition(window);
    vector<sf::Text> objects;
    objects.push_back(sf::Text("Test text.", font, 14));
    objects[0].setPosition(20, 20);
    clickable<sf::Text> stuff(mouse,objects[0]);
    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
            {
                window.close();
            }
            if (event.type == sf::Event::MouseMoved) {
                eger = sf::Mouse::getPosition(window);
            }
        }
        window.clear();
        window.draw(stuff);
        if (stuff.onMouseOver(mouse)) objects[0].setFillColor(sf::Color::Red);
        else objects[0].setFillColor(sf::Color::White);
        window.display();
    }
    return 0;}

I tested this code (with different variable names, because I'm from Hungary) and it worked. My problem is that if I want to change the ' objects[0].setFillColor( sf::Color::Red ) ' to 'stuff.setFillColor(sf::Color::Red)' nothing happens on the screen. I assume that the problem is that I want to call an inherited parent-function from the child class, but I am not sure what could I do to solve the problem. I hope it was understandable and sorry for the possible bad grammar, but I'm not a native speaker, as I mention above. Any help would be very appreciated!

Thanks in advance!

Upvotes: 0

Views: 190

Answers (3)

user3290356
user3290356

Reputation: 95

Thanks you both of you, now I got it I understand! Both of explanations were good. I redesigned the class, for the following stucked programmers here is my solution:

template <class object>
class clickable: public object{
public:
    clickable(const string& s, const sf::Font& f, unsigned int size) : object::Text(s, f, size) {}

    void cmd_out() {
        cout << (string) (*this).getString();
    }
    void graph_out(float x = 0, float y = 0) {
    }
    bool onMouseOver(const sf::Vector2i& eger) {    
        float width = (*this).getLocalBounds().width;
        float height = (*this).getLocalBounds().height;
        if ((eger.x >= (*this).getPosition().x) && (eger.x <= (*this).getPosition().x + width) && (eger.y >= (*this).getPosition().y) && (eger.y <= (*this).getPosition().y + height))
            return true;
        else return false;
    }
};

Now it's working, but I can only make with this sf::Text clickable objects, but in case if I would need any other, it would be easy to add another contructor based on which is required.

Upvotes: 0

HatsuPointerKun
HatsuPointerKun

Reputation: 637

I have a very fast and easy quick fix for your problem. Simply add this line of code in your draw member function of your clickable class, before calling target.draw(aim, states); :

aim.setFillColor(this->getFillColor());

like this :

virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const {
    aim.setFillColor(this->getFillColor());
    target.draw(aim, states);
}

or you can overload both setFillColor and getFillColor member functions :

void setFillColor(const sf::Color& c) {
    aim.setFillColor(c);
}

const sf::Color& getFillColor() {
    return aim.getFillColor();
}

Explanations :

You declared a variable stuff like this : clickable<sf::Text> stuff(mouse, objects[0]);

According to your template class definition, that means stuff is a clickable that inherits from sf::Text, so it's a clickable sf::Text, it's also a sf::Text.

Looking at the fields of your clickable class :

  • a sf::Vector2i mouse;
  • a object& aim;, the field aim is a reference at a object type, where object is your template parameter

so, your variable stuff holds all the data that a sf::Text object would hold, since stuff is a sf::Text, a sf::Vector2i, and a reference to a sf::Text object, which is not himself. So here, your stuff object can access 2 different sf::Text objects. Himself and the one referenced by aim.

You draw member function code shows that it draw the field aim, so the sf::Text referenced, and not the data held by himself

When, in your code, you do

objects[0].setFillColor( sf::Color::Red );

You call setFillColor method on the object that is referenced by the aim field of the stuff object, whereas that :

stuff.setFillColor(sf::Color::Red);

Changes the stuff object itself. And your draw function will draw the object referenced by the stuff object, but don't know at all modifications applied to the stuff object

So the solution is simple : all modifications applied to your stuff object need to be applied on the referenced object

You can do that when you want to draw your object ( in the draw method ) or when you ask to apply modifications ( when you call setFillColor method for example ).

By the way, Alex is right on the fact that your class is not really well designed. You can also not hold reference on sf::Text object and call the sf::Text draw method for (*this) ( or just don't overload the draw method in the clickable object and do proper copy of the sf::Text in your object )

Upvotes: 1

Alex
Alex

Reputation: 790

You have two different objects of type sf::Text:

  1. stuff
  2. stuff's data member called aim

Calling the setFillColor method of stuff will set stuff's fill color, not aim's. But the draw method of stuff defers completely to the draw method of aim:

virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const{ target.draw(aim, states); }

aim is ignorant of stuff's fill color, so window.draw(stuff) will do exactly the same thing as before.

The solution is to redesign your clickable class, and that depends on your intentions. Why do you want to both inherit from the template class and have a data member of that class type?

The simplest thing to do right now is probably to reimplement the setFillColor method of sf::Text as a new method of clickable:

void clickable::setFillColor( const Color & color ) {
    aim.setFillColor( color );
}

Upvotes: 1

Related Questions