Reputation: 95
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
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
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();
}
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 :
sf::Vector2i mouse;
object& aim;
, the field aim is a reference
at a object
type, where object
is your template parameterso, 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
Reputation: 790
You have two different objects of type sf::Text
:
stuff
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