I have a simple test script, which it meant to change a boolean when the user clicks within the dimensions of the button but it is not working.
I approached it like so:
while( SDL_PollEvent( &event ) ) {
switch( event.type ){
case SDL_QUIT: quit = true; break;
case SDL_MOUSEMOTION: mouseX = event.motion.x; mouseY = event.motion.y; break;
case SDL_MOUSEBUTTONDOWN: click = true;
}
}
Button btn_settings(btn_x,btn_y);
if(btn_settings.IsIn(mouseX,mouseY)){
btn_settings.RenderImg(menu,screen,"button_on.png","Settings");
if(click){
quit = true;
}
} else {
btn_settings.RenderImg(menu,screen,"button.png","Settings");
}
The problem is if i click any where then click equals true, then if the mouse is over the quit button it exits even if the button wasn't pressed when over the button.
I'm confused how i can make it work properly.
Try handling your "click" event on mouse button up instead. I've never used SDL but I suspect there is a SDL_MOUSEBUTTONUP defined. Otherwise you don't know if they want to perform a drag operation or if they moved the mouse someplace else before letting go of the mouse button.
Related
I have a custom class, CustomButton that extends Fl_Button. On my screen there are a bunch of Fl_Input and CustomButton widgets that I want to be able to navigate between using a tab keypress. Tabbing between input fields works fine, but once a CustomButton gets focus, I can't seem to tab away from it.
Here's my handle function
int CustomButton::handle ( int event )
{
int is_event_handled = 0;
switch (event)
{
case FL_KEYBOARD:
// If the keypress was enter, toggle the button on/off
if (Fl::event_key() == FL_Enter || Fl::event_key() == FL_KP_Enter)
{
// Do stuff...
}
is_event_handled = 1;
break;
case FL_FOCUS:
case FL_UNFOCUS:
// The default Fl_Button handling does not allow Focus/Unfocus
// for the button so mark the even as handled to skip the Fl_Button processing
is_event_handled = 1;
break;
default:
is_event_handled = 0;
break;
}
if ( is_event_handled == 1 ) return 1;
return Fl_Round_Button::handle ( event );
}
I am using fltk 1.1.10.
For a very simple, minimal example which demonstrates how to control the focus, please check the navigation.cxx test file.
Maybe your widget did get the focus (check this with Fl::focus()), but it does not show that up (you need to handle the FL_FOCUS and/or FL_UNFOCUS events)?
My problem was my CustomButton::handle() returning 1 after a FL_KEYBOARD event without actually using the tab key press.
Moving is_event_handled = 1 into the if statement lets me consume the FL_Enter keypress only and lets other widgets (i.e. the Fl_Group that controls navigation) take any other keypresses.
Alternatively get rid of the if and replace with something like
switch(Fl::event_key())
{
case FL_Enter:
case FL_KP_Enter:
// Do stuff
is_event_handled = 1;
break;
default:
is_event_handled = 0;
break;
}
So I'm working on a tower defense game. And I need to select a tile where the user clicks the mouse (right now i'm just drawing a border around the tile). Now the code I have works sometimes and I'm not sure how to get it to work better. Right now with the code I have when I click on an area, the tile gets selected and sometimes it deselects right away and other times it doesn't. Almost like I'm holding the mouse down and it just keeps "clicking" the mouse. What I need is for it to select the tile without deselecting it no matter how long the mouse button is being pressed. My mouse input class is set up like this:
MouseInput.h
#pragma once
#include <SDL.h>
class MouseInput
{
public:
MouseInput();
~MouseInput();
void Update();
bool GetMouseButton(int index);
SDL_Event GetEvent();
bool GetPressed();
bool GetReleased();
private:
void GetButtonStates();
private:
bool mouseArray[3];
int x, y;
bool justReleased;
bool justPressed;
SDL_Event e;
};
MouseInput.cpp
#include "Input.h"
MouseInput::MouseInput()
:
x(0),
y(0),
justReleased(false),
justPressed(false)
{
SDL_PumpEvents();
for (int i = 0; i < 3; i++)
{
mouseArray[i] = false;
}
}
MouseInput::~MouseInput()
{
}
void MouseInput::Update()
{
SDL_PollEvent(&e);
justReleased = false;
justPressed = false;
if (e.type == SDL_MOUSEBUTTONDOWN && e.button.button == SDL_BUTTON_LEFT)
justPressed = true;
else if (e.type == SDL_MOUSEBUTTONUP && e.button.button == SDL_BUTTON_LEFT)
justReleased = true;
}
bool MouseInput::GetPressed()
{
return justPressed;
}
bool MouseInput::GetReleased()
{
return justReleased;
}
bool MouseInput::GetMouseButton(int index)
{
if (index <= 3 && index >= 1)
{
return mouseArray[index - 1];
}
return false;
}
SDL_Event MouseInput::GetEvent()
{
return e;
}
void MouseInput::GetButtonStates()
{
SDL_PollEvent(&e);
if (e.type == SDL_MOUSEBUTTONDOWN)
{
// 1 = Left; 2 = Center; 3 = Right
mouseArray[e.button.button - 1] = true;
}
if (e.type == SDL_MOUSEBUTTONUP)
{
mouseArray[e.button.button - 1] = false;
}
}
In my Game class I have setup the mouseinput as well as a variable to store the coordinates of the tile that was clicked. I also Have a variable pressed to check to see if the mouse was pressed. In my updating function I check if the mouse was not pressed. If the mouse was not pressed, then I check if the mouse is pressed that way the game can get the coordinates of the mouse position. I know that sounds crazy so here is some of my code:
std::pair<int, int> coords; // Will store the coordinates of the tile position that the mouse rests in
bool pressed; // If the mouse was pressed (i should call it toggle because it's true if the mouse was pressed once, then false if the mouse was pressed again, then true...ect.)
Updating function
Mouse Class
bool clicked; // In my custom Mouse.h file and set to false in constructor
MouseInput input; // In my custom Mouse.h file
Mouse class Update()
input.Update(); // Calls the MouseInput updating
if (input.GetPressed() && !clicked)
{
clicked = !clicked;
printf("OK");
}
//else if (clicked && input.GetReleased())
else if (clicked && input.GetPressed()) // Somewhat works. It works almost like before. If I click the mouse it shows up then sometimes when I click again it works, and other times I have to click a couple times
{
clicked = !clicked;
}
In my game class I just check if mouse clicked is true, then draw what i need
if (mouse.GetClicked())
{
// Draw what I need here.
// Currently is only drawn when I hold down the mouse
// And not drawn when I release the mouse.
}
Then when I'm drawing the square around the tile, I only draw it if pressed = true. Otherwise it is not drawn. This code works sometimes, and other times it doesn't. In order to get the effect I want, I have to real quickly click the mouse. If I don't, sometimes it appears as if I'm holding the mouse button down and the square just flashes.
The issue is that the "mouse-down" event is exactly that - an event. It should happen once, be handled, and then not occur until the button is physically pressed again. With your code, once pressed it'll set the internal state to "pressed" and then not change it until it's released. Which is okay, except your code polling this internal state treats it as if it's an event, not as state, so every time the update function runs while the mouse button is pressed, it'll see a "new button press".
This (simplified version of your code that uses one button and an idea of showing an image only when a button's pressed) might help you visualise it:
bool buttonPressed = false;
bool imageShown = false;
while (true)
{
SDL_Event e;
SDL_PollEvent(&e);
if (e.type == SDL_MOUSEBUTTONDOWN)
buttonPressed = true;
else if (e.type == SDL_MOUSEBUTTONUP)
buttonPressed = false;
if (!imageShown && buttonPressed)
imageShown = true;
else if (buttonPressed)
imageShown = false;
}
Do you see how each time the loop runs, even if no events have been fired this iteration, the state remains? This would cause the imageShown variable to keep toggling on and off each iteration, until a "button up" event is fired to reset the state.
You should probably rework a bit of your input controller - you need to expose a way to differentiate from "button being pressed right now" and "button that's been pressed for a while".
An idea would be to provide eg. ButtonsPressed and ButtonsJustPressed members (although I'm sure you can think of more appropriate names). The ButtonsJustPressed members should be reset to false each time you call GetButtonStates, so they're only true if on that specific iteration of the loop, they've been pressed, while the ButtonsPressed members are exactly what you're doing right now.
A tweaked version of the above to take this into account:
bool buttonJustPressed = false;
bool buttonJustReleased = false;
bool imageShown = false;
while (true)
{
// As far as we know, this iteration no events have been fired
buttonJustPressed = false;
buttonJustReleased = false
SDL_Event e;
SDL_PollEvent(&e);
if (e.type == SDL_MOUSEBUTTONDOWN)
buttonJustPressed = true;
else if (e.type == SDL_MOUSEBUTTONUP)
buttonJustReleased = true;
if (!imageShown && buttonJustPressed)
imageShown = true;
else if (imageShown && buttonJustReleased)
imageShown = false;
}
See how this version will only change the visibility of the image if a button has just been pressed/released - if the button's being held down, the events aren't fired, so the state remains false, and the image's state remains the same.
I am currently working with SDL2 and am fairly new to it. I am trying to use case statments to get the mouse motion coordinates only while the left mouse button is pressed down.
In the end, I need to be able to click on a object and find how much the mouse is dragged from that selected object.
So far I have been able to get the mouse press and the mouse motion working separately, but not at the same time.
Here is my code for the mouse press event:
void SDL::OnEvent(SDL_Event *_event)
{
Mallet mallet;
switch (_event->type)
{
case SDL_QUIT:
m_running = false;
break;
default:
break;
case SDL_KEYUP:
switch (_event->key.keysym.sym)
{
case SDLK_SPACE:
if(m_playerTurn == 1)
m_playerTurn = 2;
else
m_playerTurn = 1;
std::cout<<"player turn = "<<m_playerTurn<<std::endl;
break;
}
case SDL_MOUSEBUTTONDOWN:
switch(_event->button.button)
{
case SDL_BUTTON_LEFT:
int x = _event->button.x;
int y = _event->button.y;
if(m_playerTurn == 1)
{
bool collision = checkCollision(x, y, m_player1->getTeamMallets(), mallet);
if(collision)
std::cout<<"collision with P1"<<std::endl;
}
if(m_playerTurn == 2)
{
bool collision = checkCollision(x, y, m_player2->getTeamMallets(), mallet);
if(collision)
std::cout<<"collision with P2"<<std::endl;
}
break;
}
}
}
Can anyone help.
Many thanks in advance.
Will
on SDL_MOUSEBUTTONDOWN set variable click = true ans save x,y coordinates,
on SDL_MOUSEMOTION check if click == true and update x,y coordinates,
on SDL_MOUSEBUTTONUP set click = false and calculate distance.
http://lazyfoo.net/tutorials/SDL/17_mouse_events/index.php
I am trying to detect when a key is pressed down using the SDL_Event system.
I am using this code to detect the key presses:
void GameLoop::update(SDL_Event eventHandler, Camera &camera){
Vector2 move = Vector2();
if (eventHandler.type == SDL_KEYDOWN){
switch (eventHandler.key.keysym.sym){
case SDLK_w:
move = Vector2(0, -1);
break;
case SDLK_s:
move = Vector2(0, 1);
break;
case SDLK_a:
move = Vector2(-1, 0);
break;
case SDLK_d:
move = Vector2(1, 0);
break;
}
}
camera.translateCamera(move);
}
If I switch out 'WASD' inputs to 'UP DOWN LEFT RIGHT' The system works and if I remove the line:
if (eventHandler.type == SDL_KEYDOWN){
It works due to the SDL_KEYUP type, using 'WASD' or 'UP DOWN LEFT RIGHT'
What is causing this behaviour and how to fix it?
SDL literally checks IF the keyboard is pressed, it just so happens that the program reads faster than the actual press which will result in numerous presses, I'm not sure about your up, down, left, right buttons, maybe they are meant to be read by SDL but their physical connection might not be the same with the letters in the keyboard.
Now if you change KEYDOWN to KEYUP, all it would read is IF it is up, this isn't really good.
What you would want to do is use booleans and ticks
if (eventHandler.type == SDL_KEYDOWN && !clicked){
ticking2 = SDL_GetTicks();
switch (eventHandler.key.keysym.sym){
case SDLK_w:
clicked = true;
break;
case SDLK_s:
clicked = true;
break;
}
}
if (eventHandler.type == SDL_KEYUP && clicked){
ticking = SDL_GetTicks();
switch (eventHandler.key.keysym.sym){
case SDLK_w:
if(ticking-ticking2 >= 3000)
{
move = Vector2(0, -1);
clicked = false;
}
break;
case SDLK_s:
if(ticking-ticking2 >= 3000)
{
move = Vector2(0, 1);
clicked = false;
}
break;
}
}
Upon pressing a button, it gets the time and pushes it into ticking2 but then after upon a click, it turns the bool clicked into true which then makes it stop reading the loop, thus, freezing the value of ticking 2.
Upon releasing a button, it checks if clicked is true so that the actions will only be made IF the button was actually pressed.
It gets the time and pushes it into ticking, ticking-ticking2 should equal to something around 0, I made those to ensure that effects won't be instant since it might cause some glitches.
How to handle TreeView double or right mouse click in WinProc?
i have tried this:
if(LOWORD(wParam) == GetWindowID(g_hWndTV &&
HIWORD(wParam) == WM_RBUTTONUP)
......
but this does not work.
Thanks for answers
Both these events will come via a WM_NOTIFY message sent to the tree control's parent window. You'll get NM_RCLICK for a right-click, and NM_DBLCLK for a double-click.
case WM_NOTIFY:
if (reinterpret_cast<LPNMHDR>(lParam)->hwndFrom == g_hWndTV)
{
if (reinterpret_cast<LPNMHDR>(lParam)->code == NM_RCLICK)
{
// right-click
}
else
if (reinterpret_cast<LPNMHDR>(lParam)->code == NM_DBLCLK)
{
// double-click
}
}
break;