• FEATURED
• FEATURED
• FEATURED
• FEATURED
• FEATURED

View more

View more

View more

Image of the Day Submit

IOTD | Top Screenshots

The latest, straight to your Inbox.

Subscribe to GameDev.net Direct to receive the latest updates and exclusive content.

Drop list GUI problem

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

11 replies to this topic

#1/ newbie123   Members

Posted 02 September 2011 - 04:54 PM

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.

#include <SFML/Graphics.hpp>

class Drop_List
{
public:
Drop_List(void);
~Drop_List(void);
void Init(sf::RenderWindow &window);
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;
};



#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;
}

{

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;
}
}


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

int main()
{
Drop_List Drop_List;

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

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;
}



#2/ newbie123   Members

Posted 02 September 2011 - 06:52 PM

#3ApochPiQ  Moderators

Posted 02 September 2011 - 06:54 PM

It's been two hours... be patient :-)
Wielder of the Sacred Wands

#4Dragonsoulj  Members

Posted 02 September 2011 - 07:23 PM

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?

#5/ newbie123   Members

Posted 02 September 2011 - 07:40 PM

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?

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


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);
}

}


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

#6Megahertz  Members

Posted 02 September 2011 - 08:16 PM

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. =)
-=[Megahertz]=-

#7aregee  Members

Posted 02 September 2011 - 09:02 PM

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.


//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;
}
}
}



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.

#8Serapth  Members

Posted 02 September 2011 - 09:05 PM

This should do what you want. It's verbose on purpose.

Drop_List.cpp:

#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;
}

{

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);
}
}

Drop_List.h:

#pragma once
#include <SFML/Graphics.hpp>

class Drop_List
{
public:
Drop_List(void);
~Drop_List(void);
void Init(sf::RenderWindow &window);
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;
};

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.

#9thePyro_13  Members

Posted 02 September 2011 - 10:37 PM

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.

#10Dragonsoulj  Members

Posted 02 September 2011 - 10:39 PM

Simple solution. Use only the true false for it and add "Clicked==false" to the first if check in Handle Events. It was changing it and after then it stayed that way.

#11/ newbie123   Members

Posted 03 September 2011 - 05:04 AM

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.

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

#12aregee  Members

Posted 03 September 2011 - 07:11 AM

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.

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

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.

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.