Are the coordinates in WINDOWPOS on WM_WINDOWPOSCHANGED in parent coordinates or screen coordinates? - winapi

Quick and simple one this time. I have a subclassed tab control that handles WM_WINDOWPOSCHANGED to, when not SWP_NOSIZE, resize its content. It passes the WINDOWPOS lParam's cx and cy fields to TCM_ADJUSTRECT to get the content rect size.
I need to do this on command as well (after changing tabs, say). However, I can't just do a dummy resize to the same size; even with an explicit SetWindowPos(), real Windows seems to add SWP_NOSIZE itself if the size doesn't change. So I want to write the analogous code to my WM_WINDOWPOSCHANGED handler.
My question is: what coordinate system are the coordinates given to WM_WINDOWPOSCHANGED given in, parent coordinates or screen coordinates? GetWindowRect() returns screen coordinates, so I'd like to know if I need to convert the coordinates to get the same values that I would get in WM_WINDOWPOSCHANGED. The documentation for WM_WINDOWPOSCHANGED doesn't say; neither does the documentation for WINDOWPOS.
Thanks.

WINDOWPOS, GetWindowRect, GetCursorPos, etc. give screen coordinates. When you use SetWindowPos you have to supply coordinates in relation to parent. This is straight forward for main window and popup windows which use screen coordinates. For moving child windows, you can use ScreenToClient and ClientToScreen for conversion.
For example, this will find the coordinates of OK button in relation to top-left corner of dialog box:
RECT rcOK;
HWND hitem = ::GetDlgItem(m_hWnd, IDOK);
GetWndRect(rcOK, hitem, m_hWnd);
void GetWndRect(RECT &rect, HWND item, HWND parent)
{
::GetWindowRect(item, &rect);//screen coordinates of OK button
POINT offset{ 0 };
ClientToScreen(parent, &offset); //Top-left (0,0) of client area of dialog -> screen coordinates
rect.left -= offset.x;
rect.right -= offset.x;
rect.top -= offset.y;
rect.bottom -= offset.y;
//client coordinates of OK button in relation to Dialog's Top-Right
}
Now we can move up the OK button by 10px:
rc.top -= 10;
rc.bottom -= 10;
::SetWindowPos(hitem, 0, rc.left, rc.top, 0, 0, SWP_NOSIZE);

Related

Why is DrawFocusRect affected by the Text color?

I was getting weird results with DrawFocusRect() in a TreeView custom draw routine. The outline was somewhat different, some were almost a solid line and others were dashed. I found out that it's the HDC SetTextColor() value that is affecting it. Even though the selection bars fill color is exactly the same, as I changed various text colors, indeed the drawn outline was different.
I ended up with setting the text color to match the fill color of the highlight bar which gives the same outline the default tree drawing routine gives.
Is this not documented anywhere? Is there even more to it?
Thanks?
DrawFocusRect draws the focus rectangle with a bitwise XOR operation. This is hinted at in the SDK documentation, under the "Remarks" section, where it says:
Because DrawFocusRect is an XOR function, calling it a second time with the same rectangle removes the rectangle from the screen.
This is the whole advantage of DrawFocusRect. It means that you (or the window manager) can draw the focus rectangle and erase it whenever needed without redrawing the entire underlying contents. (This was a big performance win in the era before double-buffering, gobs of memory, fast graphic cards, etc.) The focus rectangle is drawn wherever it needs to be drawn, then, when it needs to be erased, it is just drawn again on top of where it was drawn the first time. The only thing that you (or the window manager) need to keep track of is the coordinates of the rectangle.
You can achieve exactly the same effect as DrawFocusRect by using code like the following to draw an XOR rectangle manually:
void DrawXorRect(HDC hDC, const RECT* prc)
{
// (1)
static const WORD pattern[] = { 0x5555, 0xAAAA,
0x5555, 0xAAAA,
0x5555, 0xAAAA,
0x5555, 0xAAAA,
};
HBITMAP hbmpPattern = CreateBitmap(8, 8, 1, 1, pattern);
// (2)
HBRUSH hbrPattern = CreatePatternBrush(hbmpPattern);
// (3)
HBRUSH hbrOriginal = (HBRUSH)SelectObject(hDC, hbrPattern);
// (4)
UINT cx;
UINT cy;
SystemParametersInfo(SPI_GETFOCUSBORDERWIDTH , 0, &cx, 0);
SystemParametersInfo(SPI_GETFOCUSBORDERHEIGHT, 0, &cy, 0);
// (5)
PatBlt(hDC, prc->left , prc->top , prc->right - prc->left, cy , PATINVERT); // top
PatBlt(hDC, prc->left , prc->bottom - cy, prc->right - prc->left, cy , PATINVERT); // bottom
PatBlt(hDC, prc->left , prc->top + cy, cx , prc->bottom - prc->top - (cy * 2), PATINVERT); // left
PatBlt(hDC, prc->right - cx, prc->top + cy, cx , prc->bottom - prc->top - (cy * 2), PATINVERT); // right
// (6)
SelectObject(hDC, hbrOriginal);
DeleteObject(hbrPattern);
DeleteObject(hbmpPattern);
}
Error-checking has been elided for brevity, but this code does work, and it does mimic DrawFocusRect exactly. (Yes, I even tested it.)
Let's go through the code, step by step, to see what it does, how it works, and what that means:
First, it creates a monochrome 8×8 bitmap (CreateBitmap()) consisting of an alternating pattern of bits (pattern[]): on, off, on, off, etc. (If you don't know why, open up the Windows calculator, switch it to hex mode, and paste in the hex values that comprise the pattern. Look at the bits: see how they alternate between 0s and 1s? Now, imagine creating a bitmap from that.)
Then, it uses that bitmap to create a brush (CreatePatternBrush()).
Next, it selects the newly-created brush into the DC (SelectObject()). It saves the return value, which is the brush that was originally selected in the DC because it needs to restore that later.
With all the drawing objects created, it calls SystemParametersInfo() to retrieve the width (SPI_GETFOCUSBORDER_WIDTH) and height (SPI_GETFOCUSBORDERHEIGHT) of the focus rectangle.
Armed with all the objects and information needed to do the drawing, it blits each of the 4 sides of the rectangle using the PATINVERT raster operation. What is PATINVERT?
Combines the colors of the specified pattern with the colors of the destination rectangle by using the Boolean XOR operator.
Finally, it cleans up after itself by restoring the originally-selected brush and deleting the drawing objects that it created.
So, basically, the focus rectangle is drawn by XORing an alternating pattern of on-and-off pixels (a checkerboard pattern) with the contents of the device context (i.e., whatever is on the screen). That's what PATINVERT does. But wait—we know what the colors of the device context are, but what are the colors of the brush? Well, remember that the brush was created from a monochrome bitmap. The SDK documentation for CreateBitmap says:
If the bitmap is monochrome, zeros represent the foreground color and ones represent the background color for the destination device context.
Aha! So when the brush's bitmap pattern is 0, the brush's color is your device context's foreground color (SetTextColor); when the brush's bitmap pattern is 1, the brush's color is your device context's background color (SetBackColor). This is how the foreground and background color of the device context come into play. These colors are merged together, via an XOR operation, with the color of the original pixel in the device context.
When using DrawFocusRect, your DC's foreground and background colors should be black and white, respectively. This is the default foreground and background colors for a device context, so if you haven't called the SetTextColor or SetBackColor functions, these will be its colors. If you have called either of those functions, you either need to:
Save the value returned by each and restore it later, once you are finished drawing, but before you call DrawFocusRect (just as the code above did with the return value of the first call to SelectObject), or
Call the SaveDC function at the beginning of your drawing code to save its state (by pushing it onto a stack internally), and then call the RestoreDC function at the end of your drawing code to restore it to its original state.
When the DC's foreground and background colors are black and white, respectively, then the DrawFocusRect function works as it is expected to. This is what you need to do if you want to match how the window manager draws focus rectangles around controls. (And you do, obviously, want to do this!)
If, for some reason, you don't want to reset the DC's colors, then you won't be able to use DrawFocusRect. You'll need to take another tack where you control the colors yourself. For example, you could create a color (non-monochrome) bitmap, with its colors fixed as white and black, and then create a brush from that. Alternatively, you could consider drawing with a pen and the R2_NOT ROP code; e.g.:
void DrawXorRect2(HDC hDC, const RECT* prc)
{
LOGBRUSH lb;
lb.lbStyle = BS_SOLID;
lb.lbColor = RGB(0, 0, 0); // black
HPEN hpenNew = ExtCreatePen(PS_COSMETIC | PS_ALTERNATE, 1, &lb, 0, NULL);
HPEN hpenOld = (HPEN)SelectObject(hDC, hpenNew);
int ropOld = SetROP2(hDC, R2_NOT);
int modeOld = SetBkMode(hDC, TRANSPARENT);
HBRUSH hbrOriginal = (HBRUSH)SelectObject(hDC, GetStockObject(NULL_BRUSH));
Rectangle(hDC, prc->left, prc->top, prc->right, prc->bottom);
SelectObject(hDC, hbrOriginal);
SetBkMode(hDC, modeOld);
SetROP2(hDC, ropOld);
SelectObject(hDC, hpenOld);
DeleteObject(hpenNew);
}
I'm not sure this is actually much better, as it still requires that you set attributes of the DC—namely, the ROP and the background mode. If you're going to set and restore attributes of the DC, why not just set and restore the foreground and background colors? Plus, this pen-based implementation has the additional disadvantage of not respecting the user's preference for the thickness of the focus rectangle, which is an accessibility bug. Also, the pixel grid is slightly different here compared to the built-in DrawFocusRect, but that is, admittedly, an extremely minor drawback.
Another approach would be to create a PS_GEOMETRIC pen with a custom pattern (PS_USERSTYLE, to make it alternating), and setting the ROP style to R2_XORPEN/R2_NOTXORPEN:
void DrawXorRect3(HDC hDC, const RECT* prc)
{
UINT cx;
UINT cy;
SystemParametersInfo(SPI_GETFOCUSBORDERWIDTH , 0, &cx, 0);
SystemParametersInfo(SPI_GETFOCUSBORDERHEIGHT, 0, &cy, 0);
LOGBRUSH lb;
lb.lbStyle = BS_SOLID;
lb.lbColor = RGB(0, 0, 0);
static const DWORD pattern[] = { 0, 2 };
HPEN hpenWidth = ExtCreatePen(PS_GEOMETRIC | PS_USERSTYLE, cx, &lb, 2, pattern);
HPEN hpenHeight = ExtCreatePen(PS_GEOMETRIC | PS_USERSTYLE, cy, &lb, 2, pattern);
int ropOriginal = SetROP2(hDC, R2_NOTXORPEN);
HPEN hpenOriginal = (HPEN)SelectObject(hDC, hpenHeight);
MoveToEx(hDC, prc->left , prc->top , NULL); // \ top
LineTo (hDC, prc->right , prc->top ); // / edge
MoveToEx(hDC, prc->left + cx, prc->bottom - cy , NULL); // \ bottom
LineTo (hDC, prc->right - cx, prc->bottom - cy ); // / edge
SelectObject(hDC, hpenWidth);
MoveToEx(hDC, prc->left , prc->top + (cy * 2), NULL); // \ left
LineTo (hDC, prc->left , prc->bottom - cy ); // / edge
MoveToEx(hDC, prc->right - cx, prc->top + cy , NULL); // \ right
LineTo (hDC, prc->right - cx, prc->bottom ); // / edge
SelectObject(hDC, hpenOriginal);
SetROP2(hDC, ropOriginal);
DeleteObject(hpenHeight);
DeleteObject(hpenWidth);
}
This addresses the accessibility bug and makes the pixel pattern match that produced by DrawFocusRect, at the expense of creating an additional pen. It also, like the second attempt, works as expected regardless of what the DC's foreground or background colors are set to.
Note that all of these implementations of DrawXorRect become more efficient if you create the required drawing objects once and cache them, instead of creating and destroying them each time. This is surely a big part of the reason (along with convenience—who wants to write all this ugly code?) why the window manager provides the DrawFocusRect function for applications to use.

Map mouse position from hwnd to HWND_DESKTOP

Hello how i can map mouse position from local area window to screen window?
in code :
//Get x & y mouse coord...
Point.x = GET_X_LPARAM(lParam);
Point.y = GET_Y_LPARAM(lParam);
//Translate to Screen
MapWindowPoints(hWnd, HWND_DESKTOP, &Point, 1);
//not work!
SetCursorPos(Point.x, Point.y);
ok try next..
POINT Point;
Point.x = GET_X_LPARAM(lParam);
Point.y = GET_Y_LPARAM(lParam);
ClientToScreen(hWnd, &Point);
SetCursorPos(Point.x, Point.y); // not work!
Just saying something "not work!" is not helpful. Are the x and y coordinates what you expect?
Converting LPARAM client coordinates to screen coordinates and calling SetCursorPos is not going to move the mouse because the mouse pointer is already there!
If your application is not DPI-aware and you are running on a high DPI system then the coordinates of your application might not be the real coordinates but in your case I'm guessing that you are just not setting x and y to the "correct" values.

Maximise left and maximise right for a WIN32 application

I am creating a WIN32 application. Is there a way I can change the window so that it can maximise to the left or right, as if you pressed win + right arrow or win + left arrow?
I've tried using the ShowWindow() method, but none of the parameters accept left or right maximisation. I've also tried using AdjustWindowRect() using the following code:
AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, TRUE); // adjust the window
where wr is of type RECT, however this does not seem to change the window size or position.
Thanks!
'AdjustWindowRect' only "Calculates the required size of the window rectangle".
Use the MoveWindow function.
For example, to move the left border of the window to the left edge of the screen:
RECT rc;
GetWindowRect(hWnd, &rc);
MoveWindow(hWnd, 0, rc.top, rc.right, rc.bottom - rc.top, TRUE);

Questions About Drawing without WM_PAINT Message

In MSDN, I read this
When drawing without using a WM_PAINT message, the application usually
does not invalidate the window. Instead, it draws in such a fashion
that it can easily restore the window and remove the drawing.
What does it means ? Becuase I wrote a GIS program, I want to implement the "Zoom In" feature,
Mouse left button down and move the mouse to draw a rectangle, it's drawing a rectangle but the rectangle cover the map, first I think it is becuase the rectangle is not transparent, then I use AlphaBlend() to draw a transparent rectangle, it is the same.
What should I do ?
LRESULT OnMouseMove(UINT nflags, CPoint point)
{
if (m_bZoomInMode)
{
m_curScreenPoint.x = point.x;
m_curScreenPoint.y = point.y;
HDC hdc = GetDC();
SelectObject(hdc, (HBRUSH)NULL_BRUSH);
Rectangle(hdc, m_startScreenPoint.x, m_startScreenPoint.y, point.x, point.y);
ReleaseDC(hdc);
return 0;
}
}
To draw the rectangle, you can draw 4 lines. Or use a clear brush – that is use brush style BS_HOLLOW.
The other issue to solve is that as the user moves the mouse and the zoom rectangle will move and you will need to repaint what lies underneath. Do this as follows:
Call InvalidateRect passing a rect for the previous location of the zoom rectangle.
Call UpdateRect for force a paint cycle and paint what was under the previous zoom rectangle.
Draw the new zoom rectangle.

GetPixel always returns CLR_INVALID

I'm trying to read the pixel color of a given window. The window is not mine. The window may not have focus or be at foreground. I don't know if it matters, does it? I have the window handle, so I do:
HDC hdc = GetDC(m_window);
if (hdc)
{
COLORREF color = GetPixel(hdc,x,y);
if(color == CLR_INVALID)
{
wxLogDebug("COLOR DATA INVALID");
}
else
{
wxLogDebug("COLOR DATA 0x%x", color);
}
x and y are screen coordinates that lie inside the window. Don't know why this is not working. Any ideas?
You should convert x, y into client coordinates with ScreenToClient Function.

Resources