Why doesn’t the "Automatically move pointer to the default button in a dialog box" work for nonstandard dialog boxes, and how do I add it to my own nonstandard dialog boxes?


The Mouse control panel has a setting called Automatically move pointer to the default button in a dialog box, known informally as Snap to default button or simply Snap To. You may have discovered that it doesn't work for all dialog boxes. Why not?

The Snap To feature is implemented by the dialog manager. When the window is shown and the setting is enabled, it will center the pointer on the default button. If your application does not use the dialog manager but instead creates its own custom dialog-like windows, then naturally the code in the standard dialog manager will not run.

If you want your nonstandard dialog box to support the Snap To feature, you get to implement it yourself.

Here's a Little Program that creates a window with a default pushbutton inside it, and which centers the mouse on the button when the window is shown. Start with our scratch program and make these changes:

POINT GetRectCenter(LPCRECT prc)
{
  POINT pt = {
    prc->left + (prc->right - prc->left) / 2,
    prc->top + (prc->bottom - prc->top) / 2
  };
  return pt;
}

The Get­Rect­Center helper function calculates center of a rectangle.

BOOL OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
{
  CreateWindow(TEXT("button"),
               TEXT("Button 1"),
               WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
               0, 0, 200, 50,
               hwnd,
               (HMENU)1,
               g_hinst,
               0);
  return TRUE;
}

When our main window is created, we put a default button inside it.

#define WM_CHECKSNAPDEFBUTTON WM_APP

void OnShowWindow(HWND hwnd, BOOL fShow, UINT status)
{
  if (fShow && status == 0)
  {
    PostMessage(hwnd, WM_CHECKSNAPDEFBUTTON, 0, 0);
  }
}

void OnCheckSnapDefButton(HWND hwnd)
{
  BOOL fSnapToDefButton;
  if (SystemParametersInfo(SPI_GETSNAPTODEFBUTTON, 0,
                           &fSnapToDefButton, 0) &&
                           fSnapToDefButton &&
                           hwnd == GetForegroundWindow())
  {
    RECT rcButton;
    GetWindowRect(GetDlgItem(hwnd, 1), &rcButton);
    POINT ptCenter = GetRectCenter(&rcButton);
    SetCursorPos(ptCenter.x, ptCenter.y);
  }
}

When the window is shown, we post a message to check the Snap To setting after things have settled down. Once things settle down, we check the Snap To setting, and if it's enabled, and if our window is still the foreground window, then we center the cursor on our button.

It's important to check that our window is still the foreground window, because it would be rude to move the cursor to our button even if we opened in the background.

That's why we need to post a message to perform the check later. The WM_SHOW­WINDOW message is sent early in the Show­Window calculations, before the activation actually changes. If we performed the check then, the answer would always be, "No, you're not the foreground window," and we would always back off.

Naturally, we need to hook up our new messages.

  HANDLE_MSG(hwnd, WM_SHOWWINDOW, OnShowWindow);
  case WM_CHECKSNAPDEFBUTTON: OnCheckSnapDefButton(hwnd); break;

And there you have it, a program that honors the Automatically move pointer to the default button in a dialog box setting in its custom nonstandard dialog.

Exercise: What assumptions are made about the rectangle by the Get­Rect­Center function? How do they differ from the assumptions made by this alternate version:

POINT GetRectCenter(LPCRECT prc)
{
  POINT pt = {
    (prc->left + prc->right) / 2,
    (prc->top + prc->bottom) / 2
  };
  return pt;
}
Comments (30)
  1. Clipboarder Gadget says:

    Ha, I noticed the unusual lengthy version of GetRectCenter before I read the Exercise. Its point surely is to prevent an overflow in the case the window happens to be on the far, far right or bottom.

  2. Adam Rosenfield says:

    You should have waited 22 more days for today's article, then you could have posted the same exercise you posted 10 years prior: blogs.msdn.com/…/54945.aspx

  3. If your LONG coordinates in POINT structure are causing integer overflow, you must have some billions of monitors.

    [If the function were called GetScreenRectCenter, maybe. But this is a generic center-finding function. A better exercise would be to make a generic center-finding function that didn't suffer from either limitation. -Raymond]
  4. Bob says:

    The first version of GetRectCenter will always round toward the top left corner, not so for the second.

  5. ErikF says:

    If you know the right way of doing something and the wrong way of doing something, why would you choose the wrong way? It's the principle of the thing! (At least, that's what my teachers beat into me!)

  6. If you want to beat the integer overflow, your second variant will not work either for windows larger than LONG_MAX.

    The version that works:

    POINT GetRectCenter(LPCRECT prc)

    {

     POINT pt = {

       prc->left + (unsigned)(prc->right – prc->left) / 2,

       prc->top + (unsigned)(prc->bottom – prc->top) / 2

     };

     return pt;

    }

  7. John Muller says:

    Whenever I see Left/Right in UI code, I think "test in a bi-di system"

  8. Joker_vD says:

    "You may have discovered that it doesn't work for all dialog boxes. Why not?"

    The universal answer on questions of such sort is "And how would it work?"

  9. Another consideration for this kind of functions:

    If there is a possibility it can be implemented wrong, it might be mplemented wrong in the OS libraries. A prudent programmer would instead make sure they never use such large coordinates in the program, to avoid possibility of the OS code mishandling them.

  10. As Windows versions "improve", it works for less and less dialogs. e.g the new copy progress and conflict dialogs in Windows 8 don't support 'snap to'. The number of dialogs it doesn't work with keeps increasing.

  11. Pseudo-Anonymous says:

    Eventually, as the number of dialogs which support "Snap To" decrease, Microsoft will remove the feature. If questioned, they will state the reason why they removed the feature is because nobody uses it.

  12. DWalker59 says:

    I would have guessed that things like "Snap to" would not work for things that are called "non-standard" just as a matter of course.  Hence the name "non-standard"!  The other half of the question, "how do I do it", is of course more interesting.

  13. Joshua says:

    Ah good xpclient's back. You know, he's got a point. MS sure lacks the attention to detail they once had.

  14. It's not just Microsoft. Almost nobody writes programs with accessibility in mind (even me, sometimes). I still get strange looks when I check in a fix that is nothing but correcting the tab-order of a form to something sane, instead of the semi-random order it ends up in after using the UI designer. Apparently, I'm the only one at work who tabs between fields in programs.

  15. "What assumptions are made about the rectangle by the Get­Rect­Center function? How do they differ from the assumptions made by this alternate version"

    The first version assumes the rect express an absolute coordinates. the second version assumes the rect be expresse as coorindate point plus offsets.

    [Not sure what you're getting at here. Both versions expect the rectangle to be expressed in terms of this structure. -Raymond]
  16. [QUOTE]Not sure what you're getting at here. Both versions expect the rectangle to be expressed in terms of this structure. -Raymond[/QUOTE]

    I try to repeat it with different words.

    I know at least two ways to store coordinates about a window rectangle with the RECT structure.

    1-way) left & top fields store the x and y axis coordinates of the left-top point of your rectangle (in your case screen coordinate) while right and bottom fields store the x and y axis coordinates of the right-bottom point of your rectangle (again in your case screen coordinates). Those are absolute position in Windows screen coordinate.

    2-way) left & top fields store the x and y axis coordinates of the left-top point of your rectangle (in your case screen coordinate) while the right and bottom fields this time store the offset values of the sides of you rectangle (expressed in same coordinate system obviously).

    Am I right?

    [Windows uses method 1, as documented in MSDN. If you want to use method 2, then don't call your structure RECT. That'll just confuse people. -Raymond]
  17. Marcel says:

    @CodeVisio: I don't think the RECT structure should ever be used this way (your 2nd way) and it certainly isn't in example no. 2, because the result would be wrong. Both examples work on the same input values.

  18. AC says:

    Raymond: "A better exercise would be to make a generic center-finding function that didn't suffer from either limitation."

    Now you've got me confused. "Either" limitation? I thought your first version doesn't suffer from overflow and is correct. What limitation is there left in it?

  19. Neil says:

    AC: Did you not see alegra1's comment? The first version fails if the window is too wide to fit in a signed type.

    The second version fails if the sum overflows. There is another approach I found through a quick search. It's based on the identity (a + b) = (a & b) * 2 + (a ^ b), so the midpoint between a and b could be calculated as (a & b) + (a ^ b) / 2.

    Of course this still assumes that the rectangle lies wholly within the range of the signed type, so that if the right is to the left of the left then there is some reflection going on, while alegra1's version allows for a toroidal coordinate system.

  20. Neil says:

    @DAVe3283Martin: That annoys me for web pages too. I think there was a popular blogging software (I forget which one) where the submit button did not follow the comment field in the tab order.

  21. Danny says:

    1st version makes the assumption the origin (0, 0) is in top-left corner. The second makes the assumption the origin is like we are taught in math class at school (bottom-left corner).

  22. Ah, xpclient. Thou dost despair greatly at thine dialogs that contain no button of default status.

    Hey, if I'm gonna feed the trolls, may as well have a little fun :)

  23. Rick C says:

    @DAVe3283Martin "Apparently, I'm the only one at work who tabs between fields in programs."

    Yup, that's very frustrating.  Most people will use the mouse to change fields and then move their hands to the keyboard to enter text, and then mouse to the next field, and so on.  Worse, they're so used to doing it that way, you can't really get them to switch to using Tab (and even if you did, so many programs no longer have meaningful ordering.)

  24. JD says:

    Also the first version will divide by zero if passed an empty rect. Whether a zero-area construct has a "center" and therefore what the desired behavior would be is left as an exercise for the reader.

    [Not sure what you're getting at. 0/2 is not division by zero. -Raymond]
  25. Nick says:

    [Not sure what you're getting at. 0/2 is not division by zero. -Raymond]

    But it's so close, it's scary!  02  Bam, there it is.  And people get those back/forward/left/right/one-next-to-backspace slashes mixed up all the time!

  26. @Marcel,

    you can use the structure RECT as you wish as long as in your code you are coherent with your assumptions. It is obvious that if you need to pass it to a WIN32 function that expects the RECT structure as described in the MSDN (as Raymond pointed out) you should honor the meaning/semantic it is supposed to be.

    @Raymond,

    Yes, you are right, I was to fast answering you. Is the assumption about GetClientRect()? ( left = top = 0 ).

    [Um, the program never calls GetClientRect. -Raymond]
  27. Random User 82094828 says:

    @CodeVisio

    Yes. YMMV, but when I search for "GetClientRect", the first MSDN hit has this as the third sentence: "Because client coordinates are relative to the upper-left corner of a window's client area, the coordinates of the upper-left corner are (0,0)."

  28. You can totally divide by zero. Or so I've seen on certain educational flash charts (Math in a flash) for division.

  29. 640k says:

    Division by 0 is defined as undefined. Performance to handle the exceptions, and the additional work for maintain bloated code, is of course a turn off.

  30. Mike Dimmick says:

    On the subject of what RECT means, I uncovered a horror recently where some code I was modifying passed a .NET Rectangle to a Win32 API expecting a RECT. RECT is left, top, right, bottom. Rectangle is left, top, *width*, *height*. If the top-left co-ordinate is not (0,0) it does not behave as you expect – but all the code *looks* right.

Comments are closed.