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

Author Topic: Custom GUI  (Read 3228 times)

0 Members and 1 Guest are viewing this topic.

Strikerklm96

  • Jr. Member
  • **
  • Posts: 74
    • View Profile
    • Email
Custom GUI
« on: August 26, 2013, 05:03:47 am »
I wanted to make my own GUI for learning purposes and for a project i'm doing. I am looking for advice on what I should be doing differently (both in terms of how I used SFML and generic programming practices). I also would like to know the best way to make these buttons interact (in general) with other objects in the world. Here is my code for a Button class that I have made, I didn't write all the functions just to save reading and space. On top of this Button class, I might make a Menu class, to hold and position buttons.

P.S. I looked a lot of my questions up, but it really just came down to a matter of whether I understood it or not, but I can't be certain I did. This code works, (except for the obvious English substitutions) but it's probably not the best method.

class Button
{
public:

    //Construct and Destruct
    Button();
    Button(string text);
    ~Button();

    //Set
    void setText(string text);//text displayed
    void setTexture(string textureFile);//texture used for sprite
    void setFont(string fontFile);//font type
    void setPosition(sf::Vector2f position);//position of button by center
    void setColor(sf::Color color);//color modifyer for sprite
    void setOverHang(int overHang);//overHang is how much larger the sprite is than the text
    void setCharSize(int charSize);

    //Get
    const string getText();
    const sf::Texture getTexture();
    const sf::Font getFont();//returns what type of font is being used
    const sf::Vector2f getPosition();//returns center of button
    const sf::Vector2i getDimensions();//returns size of the sprite, not text size!
    const sf::Color getColor();
    const int getOverHang();
    const int getCharSize();
    const sf::Sprite getSprite();

    //Take Action
    void leftClicked();//these will do something
    void rightClicked();
    void middleClicked();
    void mouseOver();
    void draw(sf::RenderWindow& renderWindow);

private:

    sf::Texture* myTexture;//I need to keep textures until the object is destroyed right?
    sf::Font* myFont;//do i need to keep fonts though?
    sf::Text* myText;
    sf::Sprite* mySprite;
};




///In Button.cpp
Button::Button()
{
    myFont = new sf::Font();
    myText = new sf::Text();
    myTexture = new sf::Texture();
    mySprite = new sf::Sprite();
}
Button::Button(string text)
{
    myFont = new sf::Font();
    myText = new sf::Text();
    myTexture = new sf::Texture();
    mySprite = new sf::Sprite();

    ///inputs
    myText->setString(text);

    ///defaults
    int posX = 100;
    int posY = 100;
    int charSize = 20;
    int overHang = 2;
    myFont->loadFromFile("Georgia.ttf");
    myTexture->loadFromFile("button_grey.png");


    myText->setFont(*myFont);
    myText->setCharacterSize(charSize);
    myText->setStyle(sf::Text::Bold);
    myText->setColor(sf::Color::Black);
    sf::FloatRect textRect = myText->getGlobalBounds();
    myText->setOrigin(textRect.left + (textRect.width/2), textRect.top + (textRect.height/2));
    myText->setPosition(posX, posY);


    sf::IntRect spriteRect(0, 0, textRect.width+2*overHang, textRect.height+2*overHang);
    mySprite->setTexture(*myTexture);
    mySprite->setTextureRect(spriteRect);
    mySprite->setOrigin(spriteRect.width/2, spriteRect.height/2);
    mySprite->setPosition(posX, posY);
    //mySprite->setColor(sf::Color(255, 255, 255, 255));
    mySprite->setPosition(myText->getPosition());
}
Button::~Button()
{
    delete mySprite;
    delete myTexture;
    delete myText;
    delete myFont;
}
void Button::setPosition(sf::Vector2f position)
{
    mySprite->setPosition(position);
    myText->setPosition(position);
}
void Button::setTexture(string textureFile)
{
    myTexture->loadFromFile(textureFile);
}
void Button::setFont(string fontFile)
{
    myFont->loadFromFile(fontFile);
}
void Button::leftClicked()
{
    ///We got left clicked on! do something
}
//same for mouse over, and all other clicked actions
void Button::draw(sf::RenderWindow& renderWindow)
{
    renderWindow.draw(*mySprite);
    renderWindow.draw(*myText);
}


///EXAMPLE CODE
//similar for Mouse Moved ect.
//If i had a menu class, rather than checking against all buttons on screen, i could check whether the
//mouse was over a given menu, one at a time. If yes, go into that menu, do that button, and exit out
//of the loop. Thus not having to check every button on screen.
if (event.type == sf::Event::MouseButtonPressed)
{
    if (event.mouseButton.button == sf::Mouse::Left)
        for(all buttons in the list)
        {
            if (button.getSprite()->getGlobalBounds().contains(window.mapPixelToCoords(mouseCoordinates)))
                button.leftClicked();
        }
}

for(all buttons in the list)
    button.draw(window);

Ixrec

  • Hero Member
  • *****
  • Posts: 1241
    • View Profile
    • Email
Re: Custom GUI
« Reply #1 on: August 26, 2013, 06:02:15 am »
Regarding good programming practices for C++ or SFML, the only thing I see is that you should unique_ptr instead of raw pointers if you have access to C++11 features.  Everything else honestly seems fine to me.  Though I'm sure one of the smarter people on this forum will notice something I didn't.

Since the question's pretty vague and I can't test the code there isn't a lot else I can say, so I'll throw out some random things to consider when adding to your code, since I *think* that's part of what you're asking for.

- At the moment, it looks like your button has a hardcoded size, font and texture.  Obviously, a GUI library should provide a little more customization than that, but you probably thought of this one already.
- When you add other controls, plan your class hierarchy carefully.  Should checkboxes, pull down menus, etc be derived from Button? Or should they share a base class like Control?
- Obviously leftClick() will end up being polymorphic at some point, but does anything else need to be?  You should probably decide on a core set of polymorphic functions that main() can call for any type of control.
- The criterion for clicking on a control won't always be whether the mouse cursor is inside a sprite's bounds, so I'd recommend a polymorphic clickCheck() function that takes the result of mapPixelToCoords().
- Decide whether you want to plan ahead for the complications of a multi-window GUI.

Strikerklm96

  • Jr. Member
  • **
  • Posts: 74
    • View Profile
    • Email
Re: Custom GUI
« Reply #2 on: August 26, 2013, 04:09:00 pm »
Thanks for your response, and sorry for not being more clear with the question.
My main question is if this:

if (event.type == sf::Event::MouseButtonPressed)
{
    if (event.mouseButton.button == sf::Mouse::Left)
        for(all buttons in the list)
        {
            if (button.getSprite()->getGlobalBounds().contains(window.mapPixelToCoords(mouseCoordinates)))
                button.leftClicked();
//or as you have now suggested button.leftClicked(window.mapPixelToCoords(mouseCoordinates));
        }
}

for(all buttons in the list)
    button.draw(window);

is the correct way to check buttons and draw.

zsbzsb

  • Hero Member
  • *****
  • Posts: 1409
  • Active Maintainer of CSFML/SFML.NET
    • View Profile
    • My little corner...
    • Email
Re: Custom GUI
« Reply #3 on: August 26, 2013, 04:56:50 pm »
Thanks for your response, and sorry for not being more clear with the question.
My main question is if this:

if (event.type == sf::Event::MouseButtonPressed)
{
    if (event.mouseButton.button == sf::Mouse::Left)
        for(all buttons in the list)
        {
            if (button.getSprite()->getGlobalBounds().contains(window.mapPixelToCoords(mouseCoordinates)))
                button.leftClicked();
//or as you have now suggested button.leftClicked(window.mapPixelToCoords(mouseCoordinates));
        }
}

for(all buttons in the list)
    button.draw(window);

is the correct way to check buttons and draw.

Yes, that is basically the idea  ;)
Motion / MotionNET - Complete video / audio playback for SFML / SFML.NET

NetEXT - An SFML.NET Extension Library based on Thor

Strikerklm96

  • Jr. Member
  • **
  • Posts: 74
    • View Profile
    • Email
Re: Custom GUI
« Reply #4 on: August 26, 2013, 11:43:34 pm »
Nice, and one final thing, I asked how the buttons should interact with other things, but didn't really get a response. So, I made the button a template that can store a pointer to some object that it wants to affect. Should I do this in a different way?

///In Button.h
template <typename T>
class Button
{
public:

    //Construct and Destruct
    Button();
    Button(string text);
    Button(T& link);
    Button(T& link, string text);
    Button(T& link, string text, int overHangX, int overHangY);
    Button(T& link, string text, string texture, string font, sf::Vector2f position, int overHangX, int overHangY, int charSize, sf::Color color);
    ~Button();

    //Set
    void setText(string text);//text displayed
    void setFont(string fontFile);//font type
    void setCharSize(int charSize);
    void setTexture(string textureFile);//texture used for sprite
    void setColor(sf::Color color);//color modifyer for sprite
    void setOverHangX(int overHangX);//overHang is how much larger the sprite is than the text
    void setOverHangY(int overHangY);//overHang is how much larger the sprite is than the text
    void setPosition(sf::Vector2f position);//position of button by center

    T setLink(T link);///this allows button to hold a pointer to something!

    //Get
    const string getText();
    const string getFontFile();//returns what type of font is being used
    const string getTextureFile();//returns texture files used for sprite
    const sf::Color getColor();
    const int getOverHangX();//overHang is how much larger the sprite is than the text
    const int getOverHangY();//overHang is how much larger the sprite is than the text
    const int getCharSize();
    const sf::Vector2f getPosition();//returns center of button

    const T getLink();

    //Get but not Set
    const sf::Vector2i getDimensions();//returns size of the sprite, not text size!

    //Take Action
    void leftClicked();//these will do something
    void rightClicked();
    void middleClicked();
    void sideOneClicked();
    void sideTwoClicked();
    void mouseOver();
    void draw(sf::RenderWindow& renderWindow);

private:

    sf::Texture* myTexture;//I need to keep textures until the object is destroyed right?
    sf::Font* myFont;//do i need to keep fonts though?
    sf::Text* myText;
    sf::Sprite* mySprite;
    T* myLink;                      ///==========here it is
};




///In Button.cpp
template <typename T> Button<T>::Button(T& link)
{
    myFont = new sf::Font();
    myText = new sf::Text();
    myTexture = new sf::Texture();
    mySprite = new sf::Sprite();
    myLink = &link;
}

Ixrec

  • Hero Member
  • *****
  • Posts: 1241
    • View Profile
    • Email
Re: Custom GUI
« Reply #5 on: August 27, 2013, 12:35:19 am »
Oh right, that was another one I wasn't sure about since it was so vague.

My gut feeling there is you should try using a function pointer instead of an object pointer, because that seems like it should be even more general (I'm not quite certain since I've never tried using function pointers like this before).  Function pointer syntax is esoteric as hell but stackoverflow linked me to http://www.newty.de/fpt/fpt.html#chapter2 which looks like it might be all you need.

If that doesn't work out for whatever reason then object pointers are probably the next best thing.  To keep it simple yet general, just define a polymorphic function that any button can call on whatever object its tied to (so abstract base class Clickable defines the onClick function, etc etc).

Edit: Oh, in your new snippet I notice you're starting to get constructor creep, so try to minimize that by assigning default values to some of the arguments (eg, Button(T& link, string text = ""); ).
« Last Edit: August 27, 2013, 12:38:30 am by Ixrec »

 

anything