I've just started looking into window regions, and I'm trying to create an elliptical window that I can move by dragging the client area. Unfortunately, when I drag the window, the window flashes back and forth from the ellipse to the normal window (as if I never called SetWindowRgn), and back, repeatedly and rapidly.
I read on MSDN that I have to call SetWindowRgn(nullptr);, then move the window, then reset the region, which I have already done in my code. I move the window by calling SetWindowPos with SWP_NOZORDER, SWP_NOSIZE, and SWP_NOREDRAW, and I've tried adding on all of SWP_NOSENDCHANGING, SWP_DEFERERASE, and SWP_NOCOPYBITS as well, to no avail.
Here's my window procedure, with emphasis on WM_MOUSEMOVE. I know it won't work if I release the button outside of the window; I was planning to deal with that after this worked. I also left out error checking. It's pretty obvious that the calls do work, though, because the window does move as I drag it.
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
static bool moving{};
switch (msg) {
case WM_DESTROY: {
PostQuitMessage(0);
return 0;
}
case WM_LBUTTONDOWN: {
moving = true;
return 0;
}
case WM_LBUTTONUP: {
moving = false;
return 0;
}
case WM_MOUSEMOVE: {
static POINT old{0, 0};
if (moving) {
RECT r;
GetWindowRect(hwnd, &r);
int x = GET_X_LPARAM(lParam);
int y = GET_Y_LPARAM(lParam);
RECT newPos;
newPos.left = r.left + x - old.x;
newPos.top = r.top + y - old.y;
SetWindowRgn(hwnd, nullptr, FALSE);
SetWindowPos(hwnd, nullptr, newPos.left, newPos.top, 0, 0,
SWP_NOZORDER | SWP_NOSIZE | SWP_NOREDRAW
);
SetWindowRgn(hwnd, CreateEllipticRgn(200, 200, 600, 400), FALSE);
}
old.x = GET_X_LPARAM(lParam);
old.y = GET_Y_LPARAM(lParam);
return 0;
}
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
I've also tried calling ValidateRgn(hwnd, nullptr); at the end of WM_MOUSEMOVE, which doesn't change anything. As well, I've tried wrapping the DefWindowProc call in a condition that just returns 0 if moving is set in order to see whether it was just some other message being sent that was messing with it, but that resulted in the window doing nothing when dragged. I then applied that condition to WM_PAINT and WM_ERASEBKGND handlers, but that resulted in the same flickering problem when dragged.
In order to test it more easily, here's full code (just rudimentary window creation etc). Am I going about moving the window the right way? Is there some message or something that I should handle, but don't? This is happening on Window 7 Ultimate N.
A much easier way to handle the move logic is to handle the WM_NCHITTEST message, returning HTCAPTION. This makes Windows handle all the move logic for you.
Related
I'm having trouble detecting a hover over a static Win32 control.
This is not a duplicate issue because this looks for multiple static controls instead of just looking for a single known handle to a static control at compile time.
While this can be done in seconds in another language, I'm growing a little frustrated after trying things out for some hours. I hope to get an answer here.
First, I created a class called Label. I create a static control window within it. I'll refer to static as label for now on.
// Create the label's handle.
m_handle = CreateWindowEx(NULL, "static", m_text.c_str(),
WS_CHILD | SS_LEFT | SS_NOTIFY, m_x, m_y, m_width, m_height,
m_parentWindow, (HMENU)(UINT_PTR)m_id, m_hInstance, NULL);
if (m_handle == NULL)
return false;
When the mouse hovers over this label, the following method should be called:
void Label::invokeOnMouseHover()
{
if (m_onMouseOver)
m_onMouseOver();
}
This will call my method:
void lblName_onMouseOver()
{
MessageBox::show("Hovering!", "My Console",
MessageBoxButtons::Ok, MessageBoxIcon::Information);
}
This is how I create it from the top level:
Label lblName("This is a label.", 0, 0);
lblName.setVisible(true);
lblName.OnMouseOver(lblName_onMouseOver);
frm.add(lblName);
Admit it, this thin layer is beautiful.
While my callbacks are working fine for my Button and Checkbox controls, I noticed that statics are a little different.
So, let's go down a few levels:
This is in the main window's procedure:
case WM_MOUSEMOVE:
{
int xPos = LOWORD(lParam);
int yPos = HIWORD(lParam);
frm.setMousePos(xPos, yPos);
// Get the static's id
int id = // ?? Which static control id is it out of several?
// Obtain the control associated with the id.
X3D::Windows::Control *ctrl = frm.getControls().find(id)->second;
if (ctrl == NULL)
return 0;
// Check if this is a X3D Label control.
if (typeid(*ctrl) == typeid(X3D::Windows::Label))
{
Label *lbl = dynamic_cast<X3D::Windows::Label*>(ctrl);
int xPos = GET_X_LPARAM(lParam);
int yPos = GET_Y_LPARAM(lParam);
if (xPos >= lbl->getX() &&
yPos >= lbl->getY() &&
(xPos < (lbl->getX() + lbl->getWidth())) &&
(yPos < (lbl->getY() + lbl->getHeight())))
{
if (lbl != NULL)
lbl->invokeOnMouseHover();
}
}
}
break;
What I'm trying to do here is detect what label id was detected, and then call Label::invokeOnMouseOver().
Even though I Understand I need to use TRACKMOUSEEVENT at some point, its field member 'HWND' requires the label's handle. But I can't easily say which handle it will be because the collection may contain one or several labels.
Overall, I'm looking for suggestions on how to either restructure this or see if there's an easy solution here. My guess is I'm overthinking this.
Thanks.
Update:
Here is a code update after reading the first answer with the first solution. While this solves the hover issue, I don't see the label's text on execution.
case WM_MOUSEMOVE:
{
int xPos = LOWORD(lParam);
int yPos = HIWORD(lParam);
// Get the mouse position
frm.setMousePos(xPos, yPos);
// Check for labels
X3D::Windows::Control *ctrl = (X3D::Windows::Control*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
if (ctrl)
{
// Check if this is a X3D Label control.
Label *lbl = dynamic_cast<X3D::Windows::Label*>(ctrl);
if (lbl)
lbl->invokeOnMouseHover();
return CallWindowProc(lbl->getOldProc(), hWnd, msg, wParam, lParam);
}
}
break;
And the control creation:
// Create the label's handle.
m_handle = CreateWindowEx(NULL, TEXT("static"), m_text.c_str(),
WS_CHILD | SS_LEFT | SS_NOTIFY, m_x, m_y, m_width, m_height,
m_parentWindow, (HMENU)(UINT_PTR)m_id, m_hInstance, NULL);
if (!m_handle)
return false;
SetWindowLongPtr(m_handle, GWLP_USERDATA, (LONG_PTR)(X3D::Windows::Control*)this);
m_oldProc = (WNDPROC)SetWindowLongPtr(m_handle, GWLP_WNDPROC, (LONG_PTR)&wndProc);
If it's a paint issue, this is what I have in WndProc.
case WM_PAINT:
{
hDC = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
return 0;
}
break;
Update #2:
The alternative solution fixed the issue.
If anyone has trouble with unresolved external symbols with SetWindowSubclass() in the future, remember to add the following to your project or just #pragma it:
#pragma comment(lib, "comctl32.lib")
The only identifying info that WM_MOUSEMOVE gives you is the HWND that the mouse is moving over (or the HWND that has captured the mouse). The reported X/Y coordinates are relative to that HWND. That is the HWND that you are looking for, so you don't need to hunt for it, the message gives it to you.
If you need access to an HWND's control ID, you can use GetDlgCtrlID() for that. But note that every HWND has its own window procedure, so control IDs are typically only useful for notification messages, like WM_COMMAND and WM_NOTIFY, which are sent to a control's parent window (and even then, such notifications also carry the child's HWND as well).
When the mouse moves over a particular HWND, WM_MOUSEMOVE is posted to the message procedure of that HWND only (or to the HWND that has captured the mouse). It sounds like you are expecting it to be posted to the control's parent window instead, and that is simply not the case. That is why your WM_MOUSEMOVE handler is not being called. You are handling the message at the wrong level. You need to be prepared to handle messages on a per-control basis instead, using the control's own message procedure.
It would be more efficient to store your Control object's this pointer inside its associated HWND itself, via SetWindowLongPtr(GWLP_USERDATA), SetWindowSubClass(), or SetProp(), and then your message handlers can access the Control* pointer of the message's reported HWND when needed, for example:
// Create the label's handle.
m_handle = CreateWindowEx(NULL, TEXT("static"), m_text.c_str(),
WS_CHILD | SS_LEFT | SS_NOTIFY, m_x, m_y, m_width, m_height,
m_parentWindow, (HMENU)(UINT_PTR)m_id, m_hInstance, NULL);
if (!m_handle)
return false;
SetWindowLongPtr(m_handle, GWLP_USERDATA, (LONG_PTR)(X3D::Windows::Control*)this);
m_oldproc = (WNDPROC) SetWindowLongPtr(m_handle, GWL_WNDPROC, (LONG_PTR)&MyWndProc);
...
LRESULT CALLBACK MyWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_MOUSEMOVE:
{
...
X3D::Windows::Control *ctrl = (X3D::Windows::Control*) GetWindowLongPtr(hWnd, GWLP_USERDATA);
if (ctrl)
{
// Check if this is a X3D Label control.
Label *lbl = dynamic_cast<X3D::Windows::Label*>(ctrl);
if (lbl)
lbl->invokeOnMouseHover();
}
break;
}
...
}
return CallWindowProc(m_oldproc, hWnd, uMsg, wParam, lParam);
}
Alternatively:
// Create the label's handle.
m_handle = CreateWindowEx(NULL, TEXT("static"), m_text.c_str(),
WS_CHILD | SS_LEFT | SS_NOTIFY, m_x, m_y, m_width, m_height,
m_parentWindow, (HMENU)(UINT_PTR)m_id, m_hInstance, NULL);
if (!m_handle)
return false;
SetWindowSubclass(m_handle, &MySubclassProc, 1, (DWORD_PTR)(X3D::Windows::Control*)this);
...
LRESULT CALLBACK MySubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (uMsg)
{
case WM_NCDESTROY:
RemoveWindowSubclass(hWnd, &MySubclassProc, uIdSubclass);
break;
case WM_MOUSEMOVE:
{
X3D::Windows::Control *ctrl = (X3D::Windows::Control*) dwRefData;
// Check if this is a X3D Label control.
Label *lbl = dynamic_cast<X3D::Windows::Label*>(ctrl);
if (lbl)
lbl->invokeOnMouseHover();
break;
}
...
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
This post doesn't answer the question you asked but you should read it. It is important, and it is posted in a spirit of helpfulness after my rather acid comment above. I hope you find it so.
This class library is going to run into trouble. Code like this (using a dynamic_cast):
case WM_MOUSEMOVE:
{
X3D::Windows::Control *ctrl = (X3D::Windows::Control*) dwRefData;
// Check if this is a X3D Label control.
Label *lbl = dynamic_cast<X3D::Windows::Label*>(ctrl);
if (lbl)
lbl->invokeOnMouseHover();
break;
}
Is almost always wrong.
Why? Well, suppose you want to hover over some other kind of control? What are you going to do now? And these guys are just going to multiply like rabbits, so don't do that.
Instead, for a message that the library understands (such as this one), declare a corresponding virtual method in the base class that derived classes can override if they're interested in processing that message. Then you have the basis of a solid design (and this is pretty basic stuff).
So, in this case you would have:
class Control // Base class
{
...
virtual void onMouseHover (...) { }
...
};
And then:
class Label : public Control // Derived class
{
...
virtual void onMouseHover (...) override { ... }
...
};
Now for my second point: you are going to find that, particularly for dialogs, your application is going to want to handle a lot of messages that the base class doesn't understand (or care about).
How are you going to do this? Are you going to add code to the base class for each new message that your application (or for that matter specific types of control implemented in the class library) becomes interested in? That is not a very attractive prospect.
Now MFC handles this with something it calls a message map, which is essentially a table of message ID's and their corresponding command handlers which you can associate with (in your case) any object ultimately derived from Control and I recommend you do something similar.
But thanks to the magic of the STL, you can do better. I have something like this in my class library (my base class is actually called Window, as I would suggest yours should be):
typedef INT_PTR (Window::*MESSAGE_HANDLER)
(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
// Register a message handler
void Window::RegisterMessageHandler (UINT uMsg, MESSAGE_HANDLER handler);
And what RegisterMessageHandler() actually does is to add handler to a std::unordered_map associated with the Window object using uMsg as a key. Then, when that message subsequently comes in, it can be dispatched to the right handler without the base class knowing anything about the meaning of the message whatsoever, and that's what you're going to need.
So, you might declare the following in class Control (code untested, written in Notepad):
class Control // Base class
{
...
std::unordered_map <UINT, MESSAGE_HANDLER> m_message_map;
...
};
And then RegisterMessageHandler() might look like this:
void Window::RegisterMessageHandler (UINT uMsg, MESSAGE_HANDLER handler)
{
m_message_map.emplace (uMsg, handler);
}
And MySubclassProc() might look like this:
LRESULT CALLBACK MySubclassProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
X3D::Windows::Control *ctrl = (X3D::Windows::Control*) dwRefData;
auto handler ctrl->m_message_map.find (uMsg);
if (handler != ctrl->m_message_map.end ())
return handler.second (hWnd, uMsg, wParam, lParam);
...
}
My own class library is actually more sophisticated than this (I started with something simple but then embellished it over time) but that's the basic idea. You might have to master a few C++ skills to pull this off but trust me, if you implement something like this you'll be very glad you did, down the line.
I have a little problem.
I have a window with OpenGL child window,
All the Opengl window does so far is renders a texture on the bottom right of the window.
if i resize the window really fast from the left to the right the texture sometimes moves left, its like its not repainting fast enough even know im drawing direct to the opengl context.
The only way i have removed the problem is to InvalidateRect of the opengl window when main window resized, But im not sure why that fixes the issue because im drawing to the opengl context so i shouldnt need to make it paint its self after i have painted to the context.
if bHandled is false the defWndProc is called else it returns the function
Main window WM_SIZE
int ControllerMainWnd::OnSize(WPARAM wParam, LPARAM lParam, bool & bHandled)
{
bHandled = true;
int width = LOWORD(lParam);
int height = HIWORD(lParam);
//Set OpenGL Window Size
::SetWindowPos(glHandle, 0, 0, 0, width, height, SWP_NOZORDER);
//Invalidate OpenGL Window
::InvalidateRect(glHandle, 0, FALSE); // if i comment this out my texture sometime moves
return 0;
}
OpenGL window
int ControllerGL::OnPaint(WPARAM wParam, LPARAM lParam, bool & bHandled)
{
//bHandled = true;
//LRESULT lRes = defWinProc(WM_PAINT, wParam, lParam);
Paint();
//return lRes;
return 0;
}
int ControllerGL::OnSize(WPARAM wParam, LPARAM lParam, bool & bHandled)
{
bHandled = true;
int width = LOWORD(lParam);
int height = HIWORD(lParam);
modelGL->setWindowSize(width, height);
Paint();
return 0;
}
int ControllerGL::OnEraseBkgnd(WPARAM wParam, LPARAM lParam, bool & bHandled)
{
bHandled = true;
return 1L;
}
auto ControllerGL::Paint() -> void
{
openGLcontext->activateContext();
modelGL->draw();
openGLcontext->swapBuffers();
}
Your problem would probably be associated with the fact that you are causing the window to repaint 2 times every time you move the mouse during a resize.
First, Windows automatically invalidates the windows area when you resize it (you can control this with with the class styles CS_HREDRAW and CS_VREDRAW that should always be set for OpenGL windows as a resize typically needs a full window redraw).
Then you (redundantly) manually invalidate the window rect. Then you force it to paint - with a future paint now queued.
The OnPaint handler for a OpenGL window should really look like:
int ControllerGL::OnPaint(WPARAM wParam, LPARAM lParam, bool & bHandled)
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd,&ps);
wglMakeCurrent(hdc,hglrc);
scene->Display();
SwapBuffers(hdc);
wglMakeCurrent(hdc,NULL);
EndPaint(hwnd,&ps);
bHandled = true;
return 0;
}
I leave it as an exercise to properly encapsulate the hwnd and hglrc, writing it in this way shows you explicitly that SwapBuffers works on a DC, not on the implicit OpenGL context, and that wglMakeCurrent should be scoped with a dynamically obtained window DC.
Most OpenGL samples are very VERY lazy and use a class DC and simply leave the openGL content "active" all the time but that's going to fail as soon as two windows on the thread are trying to do OpenGL. Far better to omit the CS_OWNDC (if you have it) and get (and release) the DC whenever you actually need it.
i.e. If you wanted to handle your WM_SIZE more traditionally (similar to the glut Reshape callback) you would do this:
int ControllerGL::OnSize(WPARAM wParam, LPARAM lParam, bool & bHandled)
{
bHandled = true;
int width = LOWORD(lParam);
int height = HIWORD(lParam);
HDC hdc = GetDC(hwnd);
wglMakeCurrent(hdc,hglrc);
scene->Reshape(width,height);
wglMakeCurrent(hdc,NULL);
ReleaseDC(hwnd,hdc);
return 0;
}
Every other call from ControllerGL into what I call scene and you call modelGL would be similarly wrapped.
I have a main window in a process that is not owned by the program I'm creating. I'm using a Windows Hook to inject a DLL into this process for the purpose of adding a child window to this main window.
My end goal was to create a WS_EX_LAYERED window that allows me to create an internal colored border but allow the center portion to be transparent and allow mouse clicks through. This part works perfectly.
WNDCLASS wndClass = {};
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = OverlayProc;
wndClass.hInstance = g_TargetInstance;
wndClass.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(0, 255, 255));
wndClass.lpszClassName = "OVERLAY";
RegisterClass(&wndClass);
g_Window = CreateWindowEx(WS_EX_LAYERED | WS_EX_TRANSPARENT, "OVERLAY", nullptr,
WS_CHILDWINDOW, rect.left, rect.top, rect.right+1, rect.bottom+1, data->hwnd, nullptr, g_TargetInstance, nullptr);
SetLayeredWindowAttributes(g_Window, RGB(0, 255, 255), 0, LWA_COLORKEY);
ShowWindow(g_Window, SW_SHOW);
UpdateWindow(g_Window);
The 2nd part to this is a I wanted to conditionally block all mouse input to the parent window. I couldn't do this with the transparent portion of the WS_EX_LAYERED window so I tried creating a 2nd transparent STATIC control as a child of the main window but this doesn't block mouse input either.
I'm also sending simulated mouse clicks to the parent window through calls to PostMessage, passing WM_LBUTTONDOWN and WM_LBUTTONUP. How could I block all mouse input to the parent window via a transparent window?
It appears this is not possible to do with a simple transparent window drawn over sibling controls. What I ended up doing was using SetWindowHookEx to add a WH_GETMESSAGE hook into the process from which I use to replace the main window's WndProc function and intercept mouse messages. I tag my simulated mouse messages with a specific value in the wParam argument so the proc will now it was simulated and removes that value, passing it along to the parent window.
If it does not detect my "tag" value in the click message, it will swallow the mouse message and not pass it along to the original WndProc function.
Injected WndProc replacement
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (uMsg)
{
case WM_LBUTTONDOWN:
wParam -= 11141008;
if (wParam != MK_LBUTTON && !g_Paused)
return 0;
break;
case WM_LBUTTONUP:
wParam -= 11141008;
if (wParam != 0 && !g_Paused)
return 0;
break;
case WM_MOUSEHOVER:
case WM_MOUSEMOVE:
if (!g_Paused)
return 0;
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
Snippet from Windows Hook function
//...
switch (data->message)
{
case (WM_USER + 1):
{
g_Paused = FALSE;
//...
SetWindowSubclass(data->hwnd, WndProc, 1, 0);
break;
}
case (WM_USER + 2):
{
RemoveWindowSubclass(data->hwnd, WndProc, 1);
//...
break;
}
}
//...
The code inside the window hook function is used to subclass the main process window and inject my own WndProc function which in turn processes mouse input the way I want.
This is the code used to "simulate" mouse clicks without physically clicking in the window. Note the added value to wParam to identify this click as simulated and not generated by the user.
void Window::LeftClick(DWORD x, DWORD y, DWORD delayMillis)
{
LPARAM lparam = MAKELPARAM(x, y);
lock_guard<mutex> lock(this->m_ClickMutex);
PostMessage(this->m_Window, WM_LBUTTONDOWN, 11141008 + MK_LBUTTON, lparam);
this_thread::sleep_for(std::chrono::milliseconds(delayMillis));
PostMessage(this->m_Window, WM_LBUTTONUP, 11141008, lparam);
}
Also, just for the person in the comments who was ridiculing my choice of the word simulated and the added criticism for using PostMessage to simulate keyboard input, here is my keyboard input test method which (for my purposes) works flawlessly and very reliably
void GameWindow::KeyPress(UINT vkCode) const
{
UINT scanCode = MapVirtualKey(vkCode, MAPVK_VK_TO_VSC);
LPARAM lparam1 = MAKELPARAM(1, scanCode);
LPARAM lparam2 = MAKELPARAM(1, 0xC000 | scanCode);
PostMessage(this->m_Window, WM_KEYDOWN, vkCode, lparam1);
this_thread::sleep_for(chrono::milliseconds(25));
PostMessage(this->m_Window, WM_KEYUP, vkCode, lparam2);
}
I'm asked to add new feature to an existing program. The program consists of a dialog without title/border. I need couple of things:
When the user simply clicks inside the dialog area, just close it;
Move the dialog when the user mouse-down inside its area and drag
Here's what I found so far:
void MyDialog::onMessageReceived(UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_LBUTTONDOWN:
lastX=LOWORD(lParam);
lastY=HIWORD(lParam);
SendMessage(DlgHandle, WM_NCLBUTTONDOWN, HTCAPTION, NULL);
break;
case WM_LBUTTONUP:
if (LOWORD(lParam)==lastX && HIWORD(lParam)==lastY)
onKillButtonClick();
break;
}}
EDIT:
This function is called in this way:
INT_PTR CALLBACK MyDialog::dialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
_this->onMessageReceived(uMsg, wParam, lParam);
}
Moving the window works very well, but looks like the WM_LBUTTONUP event is lost. I had to click twice to get it fired.
Hope someone can help me...
EDIT:
Using Spy++ I saw that WM_LBTTONUP is fired, but immediately after a new WM_NCLBUTTONDOWN is emitted.
First, I agree with Michael Walz - this is a very confusing behavior: the processing of the mouse up event depends on whether or not it has moved... What if it moved just a little bit? I would much rather dismiss this dialog with a different action - click on the icon, right-click, etc.
However, the correct way to let the user move your captionless window is to process WM_NCHITTEST message and return HTCAPTION:
case WM_NCHITTEST:
SetWindowLong(hDlg, DWL_MSGRESULT, HTCAPTION);
return HTCAPTION;
Unfortunately, Windows then will take over all mouse events, so, as you observed, you would never get WM_LBUTTONUP. You have an option to set a short timer and see if the user started to move your window; cancel it when you get WM_ENTERSIZEMOVE message. If that timer fires - close your window. Yes, it's also awkward, but no more thatn your proposal.
Another way is to handle the move yourself:
static bool bDragging(false), bMoved(false);
static POINT pt1 = {}, pt2 = {};
static RECT r;
switch (message)
{
case WM_LBUTTONDOWN:
GetWindowRect(hDlg, &r);
pt1 = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
ClientToScreen(hDlg, &pt1);
bDragging = true;
bMoved = false;
break;
case WM_MOUSEMOVE:
if (bDragging)
{
pt2 = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
ClientToScreen(hDlg, &pt2);
if (pt2.x != pt1.x || pt2.y != pt1.y)
{
OffsetRect(&r, pt2.x - pt1.x, pt2.y - pt1.y);
SetWindowPos(hDlg, 0, r.left, r.top, 0, 0, SWP_NOSIZE);
pt1 = pt2;
bMoved = true;
}
}
break;
case WM_LBUTTONUP:
bDragging = false;
if (!bMoved)
PostMessage(hDlg, WM_COMMAND, IDCANCEL, 0);
bMoved = false;
break;
}
return (INT_PTR)FALSE;
I have a Win32 HWND and I'd like to allow the user to hold control and the left mouse button to drag the window around the screen. Given (1) that I can detect when the user holds control, the left mouse button, and moves the mouse, and (2) I have the new and the old mouse position, how do I use the Win32 API and my HWND to change the position of the window?
Implement a message handler for WM_NCHITTEST. Call DefWindowProc() and check if the return value is HTCLIENT. Return HTCAPTION if it is, otherwise return the DefWindowProc return value. You can now click the client area and drag the window, just like you'd drag a window by clicking on the caption.
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_NCHITTEST: {
LRESULT hit = DefWindowProc(hWnd, message, wParam, lParam);
if (hit == HTCLIENT) hit = HTCAPTION;
return hit;
}
// etc..
}
Sorry for being a little late to answer but here is the code you desire
First you declare these global variables:
bool mousedown = false;
POINT lastLocation;
bool mousedown tells us if user is holding left button on their mouse or not
Then in callback funtion you write these lines of code
case WM_LBUTTONDOWN: {
mousedown = true;
GetCursorPos(&lastLocation);
RECT rect;
GetWindowRect(hwnd, &rect);
lastLocation.x = lastLocation.x - rect.left;
lastLocation.y = lastLocation.y - rect.top;
break;
}
case WM_LBUTTONUP: {
mousedown = false;
break;
}
case WM_MOUSEMOVE: {
if (mousedown) {
POINT currentpos;
GetCursorPos(¤tpos);
int x = currentpos.x - lastLocation.x;
int y = currentpos.y - lastLocation.y;
MoveWindow(hwnd, x, y, window_lenght, window_height, false);
}
break;
}