Sign in to follow this  
newbie123

Drop list GUI problem

Recommended Posts

newbie123    100
Hello everybody
i have been working for the past two days on a GUI for SFML that i want to put in my Tile based map editor, and maybe make a tutorial on how to make your own GUI in SFML, (You know share the love xD).

anyways i'm having a problem with the implementation of the drop list. now what i have is the following:-
2 images one show Up arrow and one show Down arrow, both are in the same x,y position.

now there is a boolean called Clicked which is set to false, and when its set to false the Up arrow image is drawn.

when you click the mouse on the drop list the variable Clicked = true and the down arrow image get drawn.

finally when you click again and variable Clicked==true, another boolean called Clicked_Again = true, and when its true the up image get drawn again.

but the problem is when you click the first time the down arrow image get drawn but when you try to click again the up arrow image doesn't get drawn.

[code]
#include <SFML/Graphics.hpp>

class Drop_List
{
public:
Drop_List(void);
~Drop_List(void);
void Init(sf::RenderWindow &window);
void LoadFile();
void Handle_Events(sf::Event &Event);
void Draw(sf::RenderWindow &window);

private:
int MouseX, MouseY,
ImageX, ImageY,
ImageSizeX, ImageSizeY;

bool Clicked, Clicked_Again, Hover, NotClicked;
sf::Image Image_Up, Image_Down;
sf::Sprite Sprite_Up, Sprite_Down;
};

[/code]

[code]
#include "Drop_List.h"

Drop_List::Drop_List(void)
{
Clicked=false;
Clicked_Again=false;
}


Drop_List::~Drop_List(void)
{
}

void Drop_List::Init(sf::RenderWindow &window)
{
MouseX = window.GetInput().GetMouseX();
MouseY = window.GetInput().GetMouseY();

ImageX = Sprite_Up.GetPosition().x;
ImageY = Sprite_Up.GetPosition().y;

ImageSizeX = Sprite_Up.GetSize().x;
ImageSizeY = Sprite_Up.GetSize().y;
}

void Drop_List::LoadFile()
{
Image_Up.LoadFromFile("DropList_UP.png");
Image_Down.LoadFromFile("DropList_Down.png");

Sprite_Up.SetPosition(200,200);
Sprite_Down.SetPosition(200,200);

Sprite_Up.SetImage(Image_Up);
Sprite_Down.SetImage(Image_Down);
}

void Drop_List::Handle_Events(sf::Event &Event)
{
for(int ButtonX=ImageX; ButtonX<=ImageX + ImageSizeX; ButtonX++)
{
for(int ButtonY=ImageY; ButtonY<=ImageY + ImageSizeY; ButtonY++)
{

if(MouseX==ButtonX && MouseY==ButtonY && Event.Type == sf::Event::MouseButtonPressed && Event.MouseButton.Button == sf::Mouse::Left)
{
Clicked=true;
}

if(MouseX==ButtonX && MouseY==ButtonY && Event.Type == sf::Event::MouseButtonPressed && Event.MouseButton.Button == sf::Mouse::Left && Clicked==true)
{
Clicked_Again=true;
}
}
}
}

void Drop_List::Draw(sf::RenderWindow &window)
{
if(Clicked==false)
window.Draw(Sprite_Up);

if(Clicked==true)
{
window.Draw(Sprite_Down);
}

else if(Clicked_Again==true)
{
window.Draw(Sprite_Up);
Clicked_Again=false;
}
}
[/code]

[code]
#include <SFML/Graphics.hpp>
#include "Drop_List.h"

int main()
{
Drop_List Drop_List;

sf::RenderWindow window(sf::VideoMode(800,600,32),"test");

Drop_List.LoadFile();


while(window.IsOpened())
{
sf::Event Event;
while(window.GetEvent(Event))
{
Drop_List.Handle_Events(Event);
}

Drop_List.Init(window);

window.Clear();
Drop_List.Draw(window);
window.Display();

}
return 0;
}

[/code]

Share this post


Link to post
Share on other sites
Dragonsoulj    3212
So if I understood you correctly, you want to be able to click an arrow and have it change to another arrow, then you click it again and it changes back. If that is correct, you shouldn't need the clicked_again option. For the if statement in handle events, instead of setting clicked_again to true, just set clicked to false. Then the draw command will flip back the arrow. Make sense?

Share this post


Link to post
Share on other sites
newbie123    100
[quote name='Dragonsoulj' timestamp='1315012988' post='4857001']
So if I understood you correctly, you want to be able to click an arrow and have it change to another arrow, then you click it again and it changes back. If that is correct, you shouldn't need the clicked_again option. For the if statement in handle events, instead of setting clicked_again to true, just set clicked to false. Then the draw command will flip back the arrow. Make sense?
[/quote]

i tried that but it didnt work. i have spent the past 2 hours trying to fix it but it didnt work.

[code]

void Drop_List::Handle_Events(sf::Event &Event)
{
for(int ButtonX=ImageX; ButtonX<=ImageX + ImageSizeX; ButtonX++)
{
for(int ButtonY=ImageY; ButtonY<=ImageY + ImageSizeY; ButtonY++)
{

if(MouseX==ButtonX && MouseY==ButtonY && Event.Type == sf::Event::MouseButtonPressed && Event.MouseButton.Button == sf::Mouse::Left)
{
Clicked=true;
}

else if(MouseX==ButtonX && MouseY==ButtonY && Event.Type == sf::Event::MouseButtonPressed && Event.MouseButton.Button == sf::Mouse::Left && Clicked==true)
{
Clicked=false;
}
}
}
}

void Drop_List::Draw(sf::RenderWindow &window)
{
if(Clicked==false)
window.Draw(Sprite_Up);

if(Clicked==true)
{
window.Draw(Sprite_Down);
}

}
[/code]

when i do that, i click on the drop list and the arrow will change from Up to Down but then when i click it again it doesnt change from down to up, it stays down

Share this post


Link to post
Share on other sites
Megahertz    286
Have you tried setting a break point and stepping through the code?

If you can understand where the logic is going bad, you can then figure out how to fix it. =)

Share this post


Link to post
Share on other sites
aregee    1072
It seems you are scanning each row and column of your image to test if the mouse button was clicked there. A better way of finding out whether your mouse pointer was clicked inside the image or not is to test against the borders of your image. It is easily done doing a test like this:

(Given screen coordinates starting with (0, 0) at the top left corner.)

Be warned that I am basing this on some assumptions on your code. I don't know anything about the parameters in the first 'if' test for instance. I am just assuming it is correct. I hope at least it will give you some ideas.

[code]

//To test if the left button was clicked at all

if (!(Event.Type == sf::Event::MouseButtonPressed && Event.MouseButton.Button == sf::Mouse::Left))
{
// Left button was not clicked, so we do not need any more testing
return;
}

// The left mouse button was clicked,
// Now test if the click was inside the image

int LeftEdge = ImageX;
int RightEdge = ImageX + ImageSizeX;
int TopEdge = ImageY;
int BottomEdge = ImageY + ImageSizeY;

if ((MouseX >= LeftEdge) && (MouseX <= RightEdge))
{
if ((MouseY >= TopEdge) && (MouseY <= BottomEdge))
{
// Here you know that the mouse click was positioned within the image

// To toggle the state of the image:
if (Clicked)
{
Clicked = false;
}
else
{
Clicked = true;
}
}
}

[/code]

I believe that the code above should work, if I understood your code correctly. Hope it is of some help as I am making quite a bit of guessing myself. :)

Edit: I changed ButtonX and Y to MouseX and Y as the latter is what I assume is giving you the mouse coordinates.

Share this post


Link to post
Share on other sites
Serapth    6671
This should do what you want. It's verbose on purpose.

Drop_List.cpp:

[code]#include "stdafx.h"
#include "Drop_List.h"

Drop_List::Drop_List(void)
{
_open = false;
_clickCount = 0;
}


Drop_List::~Drop_List(void)
{
}

void Drop_List::Init(sf::RenderWindow &window)
{
ImageX = Sprite_Up.GetPosition().x;
ImageY = Sprite_Up.GetPosition().y;

ImageSizeX = Sprite_Up.GetSize().x;
ImageSizeY = Sprite_Up.GetSize().y;
}

void Drop_List::LoadFile()
{
Image_Up.LoadFromFile("DropList_UP.png");
Image_Down.LoadFromFile("DropList_Down.png");

Sprite_Up.SetPosition(200,200);
Sprite_Down.SetPosition(200,200);

Sprite_Up.SetImage(Image_Up);
Sprite_Down.SetImage(Image_Down);
}

void Drop_List::Handle_Events(sf::Event &eventToHandle)
{
if(eventToHandle.Type == sf::Event::MouseButtonPressed)
{
if(eventToHandle.MouseButton.Button == sf::Mouse::Button::Left)
{
int clickX = eventToHandle.MouseButton.X;
int clickY = eventToHandle.MouseButton.Y;

sf::Vector2f rectSize = Sprite_Up.GetSize();
float width = rectSize.x;
float height = rectSize.y;

sf::Vector2f topLeft = Sprite_Up.GetPosition();

sf::Vector2f bottomRight;
bottomRight.x = Sprite_Up.GetPosition().x + width;
bottomRight.y = Sprite_Up.GetPosition().y + height;



if( ( ( clickX > topLeft.x) && (clickX < bottomRight.x) )
&& ( (clickY > topLeft.y ) && (clickY < bottomRight.y ) ))

{
if(_open)
{
//close
_clickCount = 0;
_open = false;
}
else
{
if(_clickCount == 0)
_clickCount ++;
else
_open = true;
}
}
}
}
}


void Drop_List::Draw(sf::RenderWindow &window)
{
if(_open)
window.Draw(Sprite_Up);
else
{
window.Draw(Sprite_Down);
}
}[/code]


Drop_List.h:

[code]#pragma once
#include <SFML/Graphics.hpp>

class Drop_List
{
public:
Drop_List(void);
~Drop_List(void);
void Init(sf::RenderWindow &window);
void LoadFile();
void Handle_Events(sf::Event &Event);
void Draw(sf::RenderWindow &window);

private:
int MouseX, MouseY,
ImageX, ImageY,
ImageSizeX, ImageSizeY;

bool _open;
int _clickCount;

sf::Image Image_Up, Image_Down;
sf::Sprite Sprite_Up, Sprite_Down;
};[/code]




I've made parts of your code redundant ( for example Init() and all the variables contained within ) that I haven't bothered removing, but the effect should be what you want.

Share this post


Link to post
Share on other sites
thePyro_13    688
You can use sf::rect<T>.contains(x,y); to simplify the above code and hide all the ugly point comparisons.

In fact using rects and vectors(the points not the containers) will make your code a lot simpler and easier to understand/maintain. SFML implements them both for you, so all you have to do is use them.

Share this post


Link to post
Share on other sites
newbie123    100
[quote name='aregee' timestamp='1315018970' post='4857019']
It seems you are scanning each row and column of your image to test if the mouse button was clicked there. A better way of finding out whether your mouse pointer was clicked inside the image or not is to test against the borders of your image.
[/quote]

you are correct, i'm scanning for every pixel of the image to see if that pixel was clicked or not. why do you think scanning every pixel is not a good idea ? do you think the software will get slower and the CPU will have more calculations to do ?

and thank you for the code its work perfectly :D

Share this post


Link to post
Share on other sites
aregee    1072
[quote name='newbie123' timestamp='1315047868' post='4857104']
[quote name='aregee' timestamp='1315018970' post='4857019']
It seems you are scanning each row and column of your image to test if the mouse button was clicked there. A better way of finding out whether your mouse pointer was clicked inside the image or not is to test against the borders of your image.
[/quote]

you are correct, i'm scanning for every pixel of the image to see if that pixel was clicked or not. why do you think scanning every pixel is not a good idea ? do you think the software will get slower and the CPU will have more calculations to do ?

and thank you for the code its work perfectly :D
[/quote]

Scanning each pixel in the image is bad because it is slow. Maybe not that slow on a small image, but what if your button was like 100x100 in size, or even bigger, or if you have many such small images to check for a button down event. It would take "ages". (Even a couple of hundred millisecond feels like ages in a user interface.) What I did takes the same amount of time regardless of the size of the image.

I am glad my example worked. It was made to give you some ideas of alternative ways to do things. It is not by way the only or best solution at all. I would actually have a look at what Serapth posted just after me. It seems to be a much more elegant solution than mine. :)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this