Mouse wheel up and down scroll - c++

I want to get mouse wheel up / down scroll events.
I got this so far:
switch (uMsg)
{
case WM_MOUSEWHEEL:
if (wParam < 0)
{
g_pMenu->SelectedOption--;
}
else
{
g_pMenu->SelectedOption++;
}
break;
}
but this does the same thing when I scroll up and down. What am I doing wrong?

Solved it. Working code:
switch (uMsg)
{
case WM_MOUSEWHEEL:
if ((short)HIWORD(wParam) < 0)
{
g_pMenu->SelectedOption--;
}
else
{
g_pMenu->SelectedOption++;
}
break;
}

Related

How to get left click notification on an edit control?

I want to track event of single left-click on edit control.
I override PretranslateMessage function as below:
BOOL CMyClass::PreTranslateMessage(Msg* pMsg)
{
switch(pMsg->message)
case WM_LBUTTONDOWN:
{
CWnd* pWnd = GetFocus();
if (pWnd->GetDlgCtrlID == MY_EDIT_CTRL_ID)
{
//Do some thing
}
break;
}
}
The problem is that when I click on the edit control, all other control become disabled (for example buttons don't respond to clicks etc.)
How can I fix this problem? Or how can I track click notificationN on edit box?
You need this:
BOOL CMyClass::PreTranslateMessage(MSG* pMsg)
{
switch(pMsg->message)
{
case WM_LBUTTONDOWN:
{
CWnd* pWnd = GetFocus();
if (pWnd->GetDlgCtrlID() == MY_EDIT_CTRL_ID) // << typo corrected here
{
//Do some thing
}
break;
}
}
return __super::PreTranslateMessage(pMsg); //<< added
}
BTW its a bit awkword to use a switch statement here. Following code is cleaner IMO, unless you want to add morecases than only WM_LBUTTONDOWN:
BOOL CMyClass::PreTranslateMessage(MSG* pMsg)
{
if (pMsg->message == WM_LBUTTONDOWN)
{
CWnd* pWnd = GetFocus();
if (pWnd->GetDlgCtrlID() == MY_EDIT_CTRL_ID)
{
//Do some thing
}
}
return __super::PreTranslateMessage(pMsg); //<< added
}

SDL 2.0 Key repeat and delay

I'm having a problem with SDL 2.0 keyboard input in pong-like game. When I order to move to the left by pressing left arrow, it is processed by SDL_PollEvents() and responds correctly if the key was pressed once. However, if I keep the key pressed, I get a short delay (as long as Windows key repeat delay) before moving continuously.
Here is function processing events:
void Event::PlayerEvent (Player &player)
{
while (SDL_PollEvent (&mainEvent))
{
switch (mainEvent.type)
{
case SDL_KEYDOWN :
switch (mainEvent.key.keysym.sym)
{
case SDLK_ESCAPE :
gameRunning = false;
break;
case SDLK_LEFT :
player.moving = player.left;
break;
case SDLK_RIGHT :
player.moving = player.right;
}
break;
case SDL_QUIT :
gameRunning = false;
}
}
}
EDIT: After all, I managed to fix this issue by calling
SystemParametersInfo (SPI_SETKEYBOARDDELAY, 0, 0, 0) at the start of the program and SystemParametersInfo (SPI_SETKEYBOARDDELAY, 1, 0, 0) at the end, to return to standard key repeat delay.
For game movement, you would typically not use events, but rather use states.
Try using SDL_GetKeyboardState() outside of the event loop:
const Uint8* keystates = SDL_GetKeyboardState(NULL);
...
if(keystates[SDL_SCANCODE_LEFT])
player.moving = player.left;
else if(keystates[SDL_SCANCODE_RIGHT])
player.moving = player.right;
using SPI_SETKEYBOARDDELAY is not a good approach. This way your game will not be protable anymore since its only available on Windows.
Instead you should use like menthiond in an answer before SDL_GetKeyboardState.
Howeve be aware that you still have to collect the SDL_PollEvent. Otherwise SDL_GetKeyboardState will be always empty.
So it should be like this:
//...
SDL_Event sdlEvent;
while (SDL_PollEvent(&sdlEvent)) {
if (sdlEvent.type == SDL_QUIT) {
//..
}
}
const Uint8* keystates = SDL_GetKeyboardState(NULL);
if(keystates[SDL_SCANCODE_LEFT]) {
//...
}
if(keystates[SDL_SCANCODE_RIGHT]) {
/...
}
//...
simple as that
int vertical = 0;
int horizontal = 0;
float x = 500;
float y = 500;
float speed = 5.0;
in your sdl loop:
if (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_KEYDOWN:
{
switch (event.key.keysym.sym)
{
case SDLK_LEFT: horizontal=-1; break;
case SDLK_RIGHT: horizontal = 1; break;
case SDLK_UP: vertical=-1; break;
case SDLK_DOWN: vertical=+1; break;
}
break;
}
case SDL_KEYUP:
{
switch (event.key.keysym.sym)
{
case SDLK_LEFT: horizontal = 0; break;
case SDLK_RIGHT: horizontal = 0; break;
case SDLK_UP: vertical = 0; break;
case SDLK_DOWN: vertical = 0; break;
}
break;
}
}
}
x += horizontal * speed;
y += vertical * speed;
Use the SDL_GetKeyboardState capturing outside of - while (SDL_PollEvent (&mainEvent)), that works fine.

SDL_event list getting long and messy?

What is a good way of doing SDL event handling?
You usually have the:
while (SDL_PollEvent(&event)) {
//Handles all the events when in the menu screen...
eventsMenu(event);
}
Problem is, when you get going with a game, there's usually a mess of a list of controls that you can do, like up and down detetion for many different keys. And I'm wondering weither the method I'm using is efficient and clean. Or if I should approach it diffrently...
I have a function pointer in the mainLoop, that can be assigned to quickly change how the events will be handled. (Say I swap to another function, and it will use those events)
However, I think the list of events get messy anyways, so I tried adding regions to break it up. Is that a good idea? And yeah, just want some input if I'm on the right path to a readable code.
void Window::eventsMenu(SDL_Event event) {
switch (event.type) {
#pragma region "Button Down"
case SDL_MOUSEBUTTONDOWN: {
//printf("Mouse button down!\n");
glClearColor(0.1, 0.1, 0.1, 1);
if (event.button.button == SDL_BUTTON_LEFT) {
mouseButtonLeft = true;
}
break;
}
#pragma endregion;
#pragma region "Button Up"
case SDL_MOUSEBUTTONUP: {
//printf("Mouse button up!\n");
glClearColor(0, 0, 0, 1);
if (event.button.button == SDL_BUTTON_LEFT) {
mouseButtonLeft = false;
}
break;
}
#pragma endregion;
#pragma region "Mouse Motion"
case SDL_MOUSEMOTION: {
//printf("Mouse moved!\n");
if (mouseButtonLeft) {
rotX += event.motion.xrel;
rotY += event.motion.yrel;
}
break;
}
#pragma endregion;
#pragma region "Mouse Wheel"
case SDL_MOUSEWHEEL: {
if (event.wheel.y != 0) {
musicVolume += ((event.wheel.y > 0) ? 1 : -1);
if (musicVolume > 100) {
musicVolume = 100;
}
else if (musicVolume < 0) {
musicVolume = 0;
}
Mix_VolumeMusic(musicVolume);
printf("Volume: %i%c\n", musicVolume, '%');
}
if (event.wheel.y > 0) {
//printf("Scroll forward!\n");
}
else {
//printf("Scroll backward!\n");
}
break;
}
#pragma endregion;
#pragma region "Key Down"
case SDL_KEYDOWN: {
printf("Button [%s] pressed\n", SDL_GetKeyName(event.key.keysym.sym));
switch (event.key.keysym.sym) {
case SDLK_1: {
Mix_PlayChannel(-1, sound1, 0);
break;
}
case SDLK_2: {
Mix_PlayChannel(-1, sound2, 0);
break;
}
}
break;
}
#pragma endregion;
case SDL_QUIT: {
running = false;
}
}
}
Two suggestions :
Remove the braces ( { and } ) around the case labels. You don't need them unless you need a new stack.
My second suggestion is to split things into function. Even if it will only be called from within switch. Putting things into several functions helps makes the code easier to read and understand.
So for instance :
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
HandeMouseButton( event );
break;
void HandeMouseButton( const SDL_Event &event )
{
if ( event.type == MOUSEBUTTONDOWN )
{
glClearColor(0.1, 0.1, 0.1, 1);
if (event.button.button == SDL_BUTTON_LEFT) {
mouseButtonLeft = true;
}
else if ( event.type == MOUSEBUTTONUP )
glClearColor(0, 0, 0, 1);
if (event.button.button == SDL_BUTTON_LEFT) {
mouseButtonLeft = false;
}
}
}
And generally ( slightly opinion based ) ; if you need to use #pragma once to make the code readable, it can ( and should ) be split into more function

C++ Registering A Single Mouse Click

I have a question about registering a single mouse click on a hotspot - it's been giving me all sorts of problems, and I cannot seem to find the answer here, in any of my books, or on Google. I've only been at programming for a couple months, so bear with me.
I have managed to create an (albeit primitive and not very elegant) hotbox where someone rolls over a button and their icon changes. If they click on the button, it populates a vector on another screen and plays a sound. That's all fine. However, the code I'm using counts the MouseLButton down action - which means, because of the loop, the MouseLButton down keeps being registered. How would I make this code count the action as a single click and do nothing afterward until a user clicks again:
OfficeManager.cpp:
void Hire::hireScreenNavigation()
{
if ((input->getMouseLButton()) && (input->getMouseX() > 900 && input->getMouseX() < 940) && (input->getMouseY() > 154 && input->getMouseY() < 174))
{
audio->playCue (CLICK_EXPAND);
hire = true;
Office.push_back (HireRecruitmentPool[0]);
--officeCapacity;
}
if ((hireScreenSwitch==true) && (input->getMouseX() > 900 && input->getMouseX() < 940) && (input->getMouseY() > 154 && input->getMouseY() < 174))
{
SetCursor(LoadCursor(NULL,IDC_HAND));
}
}
This is the code in the Game class above (Game.cpp):
if(initialized)
{
switch( msg )
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_KEYDOWN: case WM_SYSKEYDOWN:
input->keyDown(wParam);
return 0;
case WM_KEYUP: case WM_SYSKEYUP:
input->keyUp(wParam);
return 0;
case WM_CHAR:
input->keyIn(wParam);
return 0;
case WM_MOUSEMOVE:
input->mouseIn(lParam);
return 0;
case WM_INPUT:
input->mouseRawIn(lParam);
return 0;
case WM_LBUTTONDOWN:
input->setMouseLButton(true);
input->mouseIn(lParam);
clicked = true;
return 0;
case WM_LBUTTONUP:
input->setMouseLButton(false);
input->mouseIn(lParam);
return 0;
case WM_MBUTTONDOWN:
input->setMouseMButton(true);
input->mouseIn(lParam);
return 0;
case WM_MBUTTONUP:
input->setMouseMButton(false);
input->mouseIn(lParam);
return 0;
case WM_RBUTTONDOWN:
input->setMouseRButton(true);
input->mouseIn(lParam);
return 0;
case WM_RBUTTONUP:
input->setMouseRButton(false);
input->mouseIn(lParam);
return 0;
}
}
return DefWindowProc( hwnd, msg, wParam, lParam );
If anyone has any examples on how to code it so that the hire functionality above counts a single click within those coordinates (instead of mouse down), I would be extremely appreciative. Keep in mind the coordinates for the hotspots will be different for each button in the game.
Thanks!
Generally a "click" involves both a down action and an up action, and in fact some people only consider the up event when determining a click. What you might try:
1. On button down, record the position so you know where on the screen the user pressed down. More specifically, record the item that was "hit".
2. Do nothing until the up event. At that time, check that the mouse position is still over the item that was originally hit. If so, consider that a successful click on the item and do whatever processing you need.

hide mouse cursor over specific client area in windows

I have directx embedded into a child window of my application and would like to hide the windows cursor only when it's over that client area. I know how to hide the cursor in general and did manage to find a make-shift example if only showing the cursor while it's not over any client areas, but it wasn't helpful for this.
How can I hide the cursor only while it's over a specific client area (/child window)?
edit:
this is as close as I've gotten but the cursor flickers unpredictably (as the mouse moves) while over the dx area
case WM_SETCURSOR:
{
static bool bCursorVisible = TRUE;
if( hWnd!=hwD3DArea && !bCursorVisible )
{
ShowCursor((bCursorVisible=TRUE));
}
else if( hWnd==hwD3DArea && bCursorVisible )
{
ShowCursor((bCursorVisible=FALSE));
return TRUE;
}
}
break;
edit2:
AHAH!
you have to use wParam instead of hWnd in this message
Here's the working code:
case WM_SETCURSOR:
{
static bool bCursorVisible = TRUE;
if( ((HWND)wParam)!=hwD3DArea && !bCursorVisible )
{
ShowCursor((bCursorVisible=TRUE));
}
else if( ((HWND)wParam)==hwD3DArea && bCursorVisible )
{
ShowCursor((bCursorVisible=FALSE));
return TRUE;
}
}
break;
case WM_SETCURSOR:
{
if (LOWORD(lParam) == HTCLIENT)
{
SetCursor(NULL);
return TRUE;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
I think it would be simpler if you just set the cursor for that specific client window to a null cursor.
the fix:
case WM_SETCURSOR:
{
static bool bCursorVisible = TRUE;
if( ((HWND)wParam)!=hwD3DArea && !bCursorVisible )
{
ShowCursor((bCursorVisible=TRUE));
}
else if( ((HWND)wParam)==hwD3DArea && bCursorVisible )
{
ShowCursor((bCursorVisible=FALSE));
return TRUE;
}
}
break;
I was on the right track but was using hWnd when I should have been using wParam (which holds the REAL handle of the window the cursor is in)