Opened 10 months ago

Closed 10 months ago

Last modified 10 months ago

#18528 closed defect (fixed)

wxButtons are too large with DPI-Awareness enabled

Reported by: pb101 Owned by: Maarten Bent <MaartenBent@…>
Priority: normal Milestone: 3.1.3
Component: wxMSW Version: dev-latest
Keywords: dpi Cc:
Blocked By: Blocking:
Patch: no

Description

When an MS Windows application is built with DPI Awareness set to "High DPI Aware" and run on a HiDPI system, buttons are too wide and tall. The size difference seems to increase with the value of DPI scaling, see also screenshots here:
https://forums.wxwidgets.org/viewtopic.php?f=1&t=46430

The buttons should be about the same size as in the same DPI-unaware application rescaled by Windows (except not blurry).

The issue can be easily seen in the widgets sample built as "High DPI Aware".

One can also compare the button height in the individual widgets sample pages to the button height displayed by wxMessageBox() in wxWidgets Information dialog (<Ctrl>+<Alt>+<Mouse MiddleClick> in an unoccupied frame space) or the buttons in Common Dialogs (e.g., File Open/Save).

Attachments (3)

buttons3apps.png download (188.9 KB) - added by pb101 10 months ago.
Shows buttons in: Notepad's Save File Dialog; wxWidgets master; wxWidgets maste + patch
patch vs save.png download (11.3 KB) - added by pb101 10 months ago.
Buttons enlarged at pixel level: Left = button from patched widgets sample; right - button from Notepad's File Save Dialog
buttons125_200.png download (11.9 KB) - added by pb101 10 months ago.
Buttons referenced from comment 5

Download all attachments as: .zip

Change History (26)

comment:1 Changed 10 months ago by MaartenB

  • Status changed from new to infoneeded_new

Can you check if the following patch fixes it?

  • src/msw/anybutton.cpp"

    diff --git "a/src/msw/anybutton.cpp" "b/src/msw/anybutton.cpp"
    index 76021aa0ae..e74c535b90 100644
    "a "b wxSize wxMSWButton::IncreaseToStdSizeAndCache(wxControl *btn, const wxSize& size 
    431431        //
    432432        // Note that we intentionally don't use GetDefaultSize() here, because
    433433        // it's inexact -- dialog units depend on this dialog's font.
    434         const wxSize sizeDef = btn->ConvertDialogToPixels(btn->FromDIP(wxSize(50, 14)));
     434        const wxSize sizeDef = btn->ConvertDialogToPixels(wxSize(50, 14));
    435435
    436436        sizeBtn.IncTo(sizeDef);
    437437    }

Changed 10 months ago by pb101

Shows buttons in: Notepad's Save File Dialog; wxWidgets master; wxWidgets maste + patch

Changed 10 months ago by pb101

Buttons enlarged at pixel level: Left = button from patched widgets sample; right - button from Notepad's File Save Dialog

comment:2 Changed 10 months ago by pb101

  • Cc pbfordev@… added
  • Status changed from infoneeded_new to new

At quick glance the patched buttons seem much better, see attachment "buttons3apps.png".

However, the button from widgets sample still appears to be two pixels taller then the button from File Save dialog, see "attachment patch vs save.png".

Sorry, I have no more time for testing ATM.

comment:3 Changed 10 months ago by MaartenB

  • Status changed from new to confirmed

The extra pixels are caused by estimating a height in wxMSWButton::GetFittingSize. I don't really have an idea how to improve this.

I'm still noticing too big sizes when using wxWindowBase::GetDlgUnitBase with PM-DPI Awareness. If I switch to using relative pixel sizes combined with FromDIP() instead of dialog units (seeMSDN), I get the same sizes as the standard open/save/ok buttons. I will create a PR to fix this.

comment:4 Changed 10 months ago by vadz

What bothers me even more than the size discrepancy is the difference in the border sizes: native buttons have 1px vertical (left/right) border and 2px horizontal (top/bottom) borders, while ours have 1px everywhere before the patch and 2px for the right and bottom borders after it. This is surprising both because I don't understand how did the patch change this and because I don't know why is it different in the first place.

FWIW the MSDN guidelines don't seem to be been updated for high DPI, and they still specify 50x14 size in dialog units or 75x23 (this includes the 1px border, so actually it's 73x21, which gives exactly 144x42 size used for the buttons in the standard dialog in the attached screenshot) in pixels. I guess we should indeed just scale the size in pixels appropriately and use it.

Changed 10 months ago by pb101

Buttons referenced from comment 5

comment:5 Changed 10 months ago by pb101

  • Cc pbfordev@… removed

I have tested the button sizes commit in PR 1589.

Now the buttons in wxWidgets seem to be less tall by 3 pixels at 125% and 4 pixels at 200%.

The attachment shows buttons from dialogs sample.

On the left is the Close button from Generic Dialog / Modal dialogs "selector" while on the right is the Cancel button from the common File Open dialog (File Operations / Second Open File).

The top row is at 125% scaling, the bottom row is for 200% scaling.

https://trac.wxwidgets.org/raw-attachment/ticket/18528/buttons125_200.png

Windows 10 v1903 (Build 18362.418)
Display resolution 2560x1440

comment:6 Changed 10 months ago by MaartenB

Thanks for testing, even though I forgot to reference this ticket or mention it here.

My math says we should when use FromDIP(25) for the height. At 125% this results in the desired 31px (in the screenshot), at 200% in 50px.

ConvertDialogToPixels(wxSize(50, 14) returns 35 and 62 in these cases, which is definitely to high.

comment:7 Changed 10 months ago by pb101

  • Cc pbfordev@… added

Sorry for stupid question, but isn't the 4 px discrepancy(31 vs 34) somehow related to the visible vs. invisible size difference (invisible border), see 21 vs 23 pixels here
https://docs.microsoft.com/en-us/windows/win32/uxguide/ctrl-command-buttons#recommended-sizing-and-spacing

comment:8 Changed 10 months ago by MaartenB

Maybe, but in that example the invisible size is 23, and the visible size is 21. We want a visible size of 25† (31@125, 50@200) to match the default buttons. The example has Windows 7 style buttons, maybe it has changed?

† we actually use 26@100 returned by btn->ConvertDialogToPixels(wxSize(50, 14)

comment:9 Changed 10 months ago by pb101

  • Cc pbfordev@… removed

It may not be of any use but when running this code built with master as DPI Unaware on Win10, I got this

wxCheckBox height = 15 px
wxComboBox height = 23 px
wxButton height = 26 px
wxCommandLinkButton height = 64 px
wxGauge height = 15 px
wxRadioButton height = 15 px
wxSlider height = 30 px
wxStaticText height = 16 px
wxTextCtrl height = 23 px

Comparing the above values against the MS Sizing Guidelines, some seem to be off, some are smaller some are larger (obtained vs Guideline):

check box: 15 vs 17
combo box: same (23)
command button: 26 vs 23
command link button: 64 vs 58
progress bar: same (15)
radio button: 15 vs 17
slider: 30 vs 24 (may be different style)
static text: 16 vs 13
single line text edit: same (23)

So it looks as if the values in guidelines are not correct anymore (controls look as from Win7) OR the best sizes are not correct in wxWidgets even at standard DPI OR the difference can be explained somehow (borders) OR my code is wrong?

comment:10 Changed 10 months ago by pb101

  • Cc pbfordev@… added

Sorry, I forgot to write that I played with Spy++ a bit as well (switched my scaling to 100% on both monitors). I looked at the Cancel button height in the common dialogs in WordPad: it was 26 in File Open as well as Page Setup but 23 in Print.

I have also noticed that even the height of the check boxes, static texts, and radio buttons was not uniform across various WordPad dialogs, so...

comment:11 Changed 10 months ago by pb101

  • Cc pbfordev@… removed

I am not trying to hurry anyone, just asking:

AFAIK, wxWidgets 3.1.3 is going to be released in less than a week and it would be nice if this issue was fixed there.

Unfortunately, doing that is above my ability, so I can only help with testing.

comment:12 Changed 10 months ago by MaartenB

I agree. I don't know if PR1589 will be merged in time, but at least the fix for the regression with the button sizes (removing FromDIP like here) should be included.

comment:13 Changed 10 months ago by pb101

  • Cc pbfordev@… added

Thanks for the quick response, MaartenB.

I have tried wxWidgets with PR1589 and I think the button height is basically OK, perhaps slightly taller then most which may not be a bad thing.

Button heights in pixels as obtained with Spy++ :
125%
wxWidgets: 31
Notepad File Open: 33
Some built-in Windows dialogs (various settings in Control Panel, Page Setup dialog, messagebox): mostly 28

200%:
wxWidgets: 50
Notepad File Open: 52
Some built-in Windows dialogs (various settings in Control Panel, Page Setup dialog, messagebox): mostly 44

comment:14 Changed 10 months ago by vadz

  • Milestone set to 3.1.3

Thanks a lot for testing, this is much appreciated!

It's annoying that we still can't match the height of the native buttons precisely, but if they differ in sizes, I think following the official MSDN guidelines is the best we can do.

And I'll definitely merge PR 1589 before 3.1.3, I simply want to at least minimally test it myself first and just couldn't do it yet.

comment:15 Changed 10 months ago by pb101

  • Cc pbfordev@… removed

I have checked button heights at 100% with:

  • 3.0.4,
  • 3.1.2,
  • master with PR1589.

The buttons seem to be a bit taller even at 100%:
wxWidgets: 26
Notepad File Open: 26
Sizing Guidelines, built-in Windows dialogs (various settings in Control Panel, Page Setup, Print), message box: 23

23 is also the recommended height for single-line text controls and combo boxes, with which wxWidgets complies.

I have tested also 2.8.12 where the button height is 23; however, heights of some other controls there are different from the Guidelines. E.g., text control and combo box (21 vs 23), check box and radio button (13 and 19 vs 17).

I have tested this only on one computer with latest Windows 10, so in theory this may be specific to my setup. However, I do not think I have modified any relevant settings.

comment:16 Changed 10 months ago by vadz

I've looked at it too and I can confirm that things are not really consistent in Windows itself, unfortunately. I think the height of the buttons in the notepad "File open" dialog could be affected by the presence of the combobox in it in the same row because the buttons in the "Print", "About" (stock) or "Go to" (custom) dialogs in the same Notepad do have the standard 73*21 size at 100%. But in its "Font" dialog the size is (slightly) different. And most interestingly, in 200% it's 148*42 and not 146 as one might expect and I really have no idea why.

If you look at the "Properties" shell dialog in the Explorer itself, it's 73*21 at 100% and 146*42 at 200%, just as you'd expect. But it also uses 4px border on the default button instead of 2px border in Notepad. Unfortunately, I think this means that this shell dialog doesn't use the standard control at all but rather the .NET one, so we can't get exactly the same appearance as it.

Unfortunately, these inconsistencies aside, our buttons have very different sizes: they're 86*24 and 162*48 in 100% and 200%, respectively. This seems to be just due to the fact that dialog units are not precise enough due to rounding errors: the base is 7*15 in 100%, but 13*32 in 200% and, obviously, everything based on it can't be exactly twice bigger because of this. So I think we have no choice but to stop using them and use "relative pixels" (what we call DIPs, basically) instead.

Also, the problem with the button being too tall comes from our own wxMSWButton::GetFittingSize() and the solution is simple: just stop increasing the height there, as it's unnecessary anyhow. However this doesn't solve the problem completely because it looks like the extra 1px invisible borders around the visible part of the button are not scaled correctly, so we need to add them without scaling.

With all these fixes applied, our buttons look the same as (most of the) ones in Notepad and I think this is the best we can do with Win32 API, so I'm going to push this out.

Of course, more testing would still be welcome, especially in 125% and 150%. TIA!

comment:17 Changed 10 months ago by Maarten Bent <MaartenBent@…>

  • Owner set to Maarten Bent <MaartenBent@…>
  • Resolution set to fixed
  • Status changed from confirmed to closed

In 591136c7/git-wxWidgets:

Improve button heights at high DPI on wxMSW

Don't use FromDIP together with ConvertDialogToPixels, because the font height
is already adjusted to the DPI.
Also limit the button height at higher DPI. Because the height determined by
ConvertDialogToPixels is higher than standard buttons use on Windows.

Closes #18528

comment:18 Changed 10 months ago by Vadim Zeitlin <vadim@…>

In 20a8b0465/git-wxWidgets:

Don't increase the button height in wxMSWButton::GetFittingSize()

This results in buttons being forced to be too tall when using high DPI
and is useless anyhow, as all callers of this function deal with the
height by increasing it to the minimum acceptable value already (or
discard it entirely in wxMessageDialog::AdjustButtonLabels()).

See #18528.

comment:19 Changed 10 months ago by Vadim Zeitlin <vadim@…>

In c6d2f6d9f/git-wxWidgets:

Really fix the standard button size in high DPI under MSW

Use "relative pixels" (known as DIPs in wx) instead of dialog units, as
the latter ones don't scale correctly due to rounding errors when using
high DPI.

Also take into account the fact that the 1px invisible border around the
visible part of the buttons is not scaled by the standard control, so
don't apply scaling to this part when determining the best size neither.

Closes #18528.

comment:20 Changed 10 months ago by vadz

BTW, sorry for potentially misleading information: my previous comments were based on my tests under old 1709 version of the OS. After updating to v1803, the standard buttons do have 2px "active" border on 200% display, so this was just a bug that got fixed in later versions.

comment:21 Changed 10 months ago by Maarten Bent <MaartenBent@…>

In 591136c7/git-wxWidgets:

Improve button heights at high DPI on wxMSW

Don't use FromDIP together with ConvertDialogToPixels, because the font height
is already adjusted to the DPI.
Also limit the button height at higher DPI. Because the height determined by
ConvertDialogToPixels is higher than standard buttons use on Windows.

Closes #18528

comment:22 Changed 10 months ago by Vadim Zeitlin <vadim@…>

In 20a8b0465/git-wxWidgets:

Don't increase the button height in wxMSWButton::GetFittingSize()

This results in buttons being forced to be too tall when using high DPI
and is useless anyhow, as all callers of this function deal with the
height by increasing it to the minimum acceptable value already (or
discard it entirely in wxMessageDialog::AdjustButtonLabels()).

See #18528.

comment:23 Changed 10 months ago by Vadim Zeitlin <vadim@…>

In c6d2f6d9f/git-wxWidgets:

Really fix the standard button size in high DPI under MSW

Use "relative pixels" (known as DIPs in wx) instead of dialog units, as
the latter ones don't scale correctly due to rounding errors when using
high DPI.

Also take into account the fact that the 1px invisible border around the
visible part of the buttons is not scaled by the standard control, so
don't apply scaling to this part when determining the best size neither.

Closes #18528.

Note: See TracTickets for help on using tickets.