Drop list GUI problem

Started by
10 comments, last by aregee 12 years, 7 months ago
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 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;
};




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



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

Advertisement
guys any help please ? :unsure:
It's been two hours... be patient :-)

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

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?

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
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]=-
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.
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;
}

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



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





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.
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.
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.

This topic is closed to new replies.

Advertisement