wxUiEditor icon indicating copy to clipboard operation
wxUiEditor copied to clipboard

Improve passing absolute positions/sizes to controls

Open PBfordev opened this issue 4 years ago • 15 comments

When adding a control do not pre-check "using dialog units", IMO using dialog units in wxWidgets code is quite unusual.

Additionally, add an option to use device independent pixels, which when checked results in absolute position/sizes wrapped in FromDIP() call.

PBfordev avatar Jan 09 '22 10:01 PBfordev

The problem I see is that FromDIP changes the value based on the current display resolution by calling GetPPI(), whereas dialog units call GetAverageASCIILetterSize(). If the system font changes either due to a different OS, different language, or simply changed by the user, GetPPI() will return the same value (as long as the screen resolution is the same), whereas GetAverageASCIILetterSize() will correctly change. I.e., the layout of the dialog will not change if the system font is changed and FromDIP() is used, but will change if dialog units are used.

I know you've been involved on the wxWidgets team dealing with high dpi changes -- am I missing something in my understanding of how and when to use FromDIP versus ConvertPixelsToDialog? FromDIP seems extremely valuable for changing bitmaps or any other non-text dimensions, but it seems to me that using it for text dimensions will not get the desired result if the system font is changed without the resolution being changed.

Code change note

For any code change related to this issue: there needs to be support for some form of optional dialog units since XRC, wxSmith, and Windows RC dialogs use them, so support is required for importing to maintain the same UI appearance.

KeyWorksRW avatar Jan 09 '22 14:01 KeyWorksRW

Firstly, my involvement in implementing wxWidget HiDPI was purely in reporting the issues, I had written no code nor do I have any knowledge how it works.

Disclaimer: I am familiar only with Windows.

I do not have any recent recollection of using text-based sizes, as i wrote I believe this is not a common idiom with wxWidgets. I only have some vague recollection of using dialog units decades ago in Win32 resources, which used hard-coded positions and sizes. AFAICT, wxWindow::ConvertPixelsToDialog() is not used anywhere in the wxWidgets source or samples.

As we all know, one is not supposed to use hard-coded coordinates and sizes at all as they are bound to break. wxWidgets does not support changing translations during runtime, so that cannot happen. I do not think people change the OS text size much. In the end, the only common runtime change may be when the DPI changes usually because the window was moved to a screen with a different DPI.

AFAICT, wxWidgets depends on the control (e.g., a checkbox) reporting the best size it should have, based on its content (see also wxControl::GetSizeFromText()). When DPI changes, the control changes its best size as well.

I think one is supposed to use FromDIP() for hinting sizes, such as the initial control size. The bitmaps are supposed to be using wxBitmapBundle, which of course is easier said than done (unless you have your graphics in a vector format already).

I have not used XRC so I know nothing about that.

IMO, the project should have some default settings which would affect preset choices, among those using ConvertPixelsToDialog() or FromDIP() for hard-coded sizes.

Either way, the issue was just my suggestion, feel free to close it.

PBfordev avatar Jan 09 '22 19:01 PBfordev

I'm leaving the issue open because I do need to separate coordinates for text from coordinates for graphics (including sizes of UI elements like tabs). I want to think more about whether or not to provide the option to generate text settings without using dialog units or text extents. I realize it's a common practice, but it's also one that could easily cause problems later on. I suspect someone creating their program on a Unix system is going to be unpleasantly surprised to see their text clipped on a Windows 10 system -- which can happen with absolute pixel sizes.

KeyWorksRW avatar Jan 09 '22 22:01 KeyWorksRW

Kind @KeyWorksRW

Please, I beg your attention regarding this topic. I've chosen wxUiEditor as IDE for my developing system, but now I'm stuck designing my applications.

Or perhaps I'm missing something regarding how to configure wxUI ...

I've been implementing portable applications for many years using wxWidgets, and I've never used "Dialog units", I think the issues you described above among OSs are nowadays largely solved. What @PBfordev suggested in its last comment is reasonable and, in my opinion, it should be implemented.

UI development using wxWidgets library is going towards FromDIP() it should be clear now. Here they describe it without any doubts High DPI Support in wxWidgets

Below the issue I'm facing. I'd like to get an UI like the 4th point, but I can't. Is there a kind of work-around? Please help me to understand how could I proceed by using wxUI

1) Display scale 100% without dialog units In this case the UI is correct image

2) Display scale 150% without dialog units This UI is wrong because only the icon is aligned with de screen DIP and not the button size. image

3) Display scale 150% using dialog units I think this UI is unacceptable, why the uge button size? image

4) Display scale 150% using FromDIP() In this case the UI is correct, but unfortunately it can't be achieved by using wxUI image

rossanoparis avatar May 30 '24 09:05 rossanoparis

Can you paste in the C++ code that you used to create 4) above? Or, even better, select the dialog in wxUiEditor, choose Copy and paste the XML file into text here. That way I can better see the context for what you are trying to do. The reason I'm asking is because I did a quick test with a bitmap button and got exactly what you wanted, with the exception of the text being on one line, so I'm unclear how you achieved 3) above.

KeyWorksRW avatar May 30 '24 19:05 KeyWorksRW

Thank you @KeyWorksRW I posted screenshots regarding a custom control, but even using a normal wxButton the results are same as points above

UI Project wxTestUI.zip

The button in "Mock up" panel is already uge, even with a scale of 100% With the compiled application and a scale of 150% dimension seams 4 times as expected. Generated code: m_pButton = new wxButton(this, wxID_ANY, _("MyButton"), wxDefaultPosition, ConvertDialogToPixels(wxSize(150, 100))); image

Here the generated code without "using dialog unit" options I don't see FromDIP() function, as I would have expected to see, so I don't understand how could I achive the point 4 above without it ... I had to add the function FromDIP(wxSize(150, 100)) by hand in order to get the UI as point 4 above. image

Form XML code <node class="wxFrame" class_name="CMainFrameDesigner" style="wxCAPTION|wxRESIZE_BORDER|wxSYSTEM_MENU|wxCLOSE_BOX" title="CMainFrameDesigner" base_file="CMainFrameDesigner" minimum_size="400,300" size="400,300" wxEVT_CONTEXT_MENU="OnContextMenu" wxEVT_SIZE="OnSize" wxEVT_PAINT="OnPaint"><node class="wxMenuBar" class_access="none" id="ID_MENUBAR"><node class="wxMenu" class_access="none" label="File" var_name="m_menu3"><node class="wxMenuItem" help="Close" id="ID_MNU_EXIT" label="&amp;Exit" var_name="m_menuitem1" wxEVT_MENU="OnExit"/></node><node class="wxMenu" class_access="none" label="&amp;LunaSVG" var_name="m_menu1"><node class="wxMenuItem" id="ID_MNU_CONVERT" label="Convert SVG file ..." var_name="m_menuItem2" wxEVT_MENU="OnConvert"/><node class="wxMenuItem" id="ID_MNU_LOCTRA" label="Transformations" var_name="m_menuItem3" wxEVT_MENU="OnTransformations"/><node class="wxMenuItem" id="ID_MNU_ATTRIBUTES" label="Attributes" var_name="m_menuItem4" wxEVT_MENU="OnAttributes"/><node class="wxMenuItem" id="ID_MNU_DRAWBMP" label="Draw bitmap" var_name="m_menuItem5" wxEVT_MENU="OnDrawBitmap"/></node><node class="wxMenu" class_access="none" label="wxhImage" var_name="m_menu2"><node class="wxMenuItem" id="ID_MNU_IMAGETEST" label="Test ..." var_name="m_menuItem6" wxEVT_MENU="OnImageTest"/></node><node class="wxMenu" class_access="none" label="wxhSvgDocument" var_name="m_menu4"><node class="wxMenuItem" id="ID_MNU_SVGDOC" label="Test ..." var_name="m_menuItem7" wxEVT_MENU="OnSvgDocTest"/></node></node><node class="wxBoxSizer" orientation="wxVERTICAL" flags="wxEXPAND"><node class="wxButton" var_name="m_pButton" size="150,100"/></node></node>

This is the application just started, with a scale of 150% and ConvertDialogToPixels(wxSize(150, 100))); ... This doesn't happen using FromDIP(wxSize(150, 100))); image

Then I dynamically load an image to set the bitmap button ... image

rossanoparis avatar May 30 '24 21:05 rossanoparis

With an actual button, you could set the bitmap position to the top, increase the margins around it, and get what you want without setting the button size at all. Text, bitmap and button size will scale automatically depending on the resolution of the display. However, that's not going to work for a custom control unless it derives from a wxButton.

Note that the buton, text and bitmap are all scaled automatically -- effectively calling the equivalent of FromDIP, so calling FromDIP yourself just means it will be called twice.

The reason for ConvertDialogToPixels() is to take account of a different font size between OS and even version of OS. E.g., Windows 11 uses a different font and font size then it did in Windows 7, so something sized correctly for Windows 7 is going to be off on Windows 11 because of the smaller font size. Ideally, you don't set the dimensions at all, next best is taking into account possible font changes, and only then if you still can't get what you want do you force the dimensions. That's with using the system font for your text. If you are using your own font, then setting the dimensions your self should work fine, as long as you keep in mind that both of them will be automatically scaled on Windows.

In any event, I'm going to look into this further using your project file as the test case to resolve.

KeyWorksRW avatar May 31 '24 15:05 KeyWorksRW

Here's my thoughts/plans for this issue:

Starting with version 3.1.7, FromDIP can be used to scale values based on the screen resolution. It has an advantage over ConvertDialogToPixels in that it is cross platform, and because the values are in pixels, it makes it easier to read the values and have a clearer idea of the size or position being set. The downside is that it requires wxWidgets 3.2 or later, and it might not work in XRC files (xrc does have FromDIP support, but right off hand I'm not sure how to specify using it in the xrc file itself).

I cannot remove the "using dialog units" since that is required for importing other projects that support dialog units, as well as importing Windows Resource Files. My inclination is to add a "use FromDIP" before the "using dialog units". If the user sets 3.2 or higher for the wxWidgets version, then the default would be to use FromDIP unless importing or loading a project that had dialog units checked. I would also add a global option in Preferences that would allow the user to choose a default scaling option (none, Dialog Units, FromDIP).

KeyWorksRW avatar Jul 07 '24 13:07 KeyWorksRW

it would be very welcome and useful thank you so much

rossanoparis avatar Jul 07 '24 15:07 rossanoparis

I would not pay much attention to pre-3.2 wxWidgets: 3.0 is very outdated and unsupported, 3.1 was unstable branch superseded by 3.2.

As for wxXRC, it is odd that it still does not support using scaled pixels (nor bitmap bundles?). I have never used it but AFAICT, it would be probably impossible (due to backward compatibility) to add a new unit suffix. But perhaps you could propose to devs a new property (at XML or top-level window level), setting which would make the wxXRC system to use FromDIP() on coordinates and sizes...

PBfordev avatar Jul 08 '24 06:07 PBfordev

The following is just reference information:

  • This link is to the current wxWidgets documentation of High DPI support
  • From the documentation:

Actually, using any pixel values is not recommended and replacing them with the values based on the text metrics, i.e. obtained using wxWindow::GetTextExtent(), or expressing them in dialog units (see wxWindow::ConvertDialogToPixels()) is preferable.

This does not change my plans for supporting FromDIP(), I just wanted to point out the current documentation recommending ConvertDialogToPixels(). I have no plans for generating calls to GetTextExtent() at this point.

XRC does support BitmapBundles and ConvertDialogToPixels. When generating XRC when the user has specified use DIP I'll simply convert the pixels to dialog units and add the 'd' to the value generated in the XRC.

KeyWorksRW avatar Jul 11 '24 14:07 KeyWorksRW

Thank you @KeyWorksRW I've read such references. Despite them, I'm not able to explain the behaviour I tried to post with this issue, wich has nothing to do nither with custom controls nor double resising action. Simply usign ConvertDialogToPixels(wxSize(150, 100))); creates a control with uge size wich is not proportional with its font. Of Course it is not related to wxUI, but it is something due to wx, which I can't explain. The only way I found to create a button able to respect its size along different DPI, is the function FromDIP(). That's why I tried to ask for such kind of option in wxUI.

rossanoparis avatar Jul 16 '24 06:07 rossanoparis

I've mostly got this implemented now, though a bit differently than originally planned. Now when an older project is loaded, I automatically convert wxSize and wxPoint that used dialog units into physical pixels. For C++ with wxWidgets 3.1, I automatically convert the physical pixel values back into dialog units and generate ConvertDialogToPixels(). For C++ with wxWidgets 3.2 or higher along with wxPython and wxRuby, I generate FromDIP() around the wxSize/wxPoint. The internal project version number is changed, so if the project file is saved, older versions of wxUiEditor will complain about loading the project (and they will no longer scale wxSize/wxPoint at all).

Internally, I do support turning off scaling, but I'm not currently exposing that as a user option. Right off hand, I can't think of a scenario where that would be useful. Technically, calling FromDIP() isn't necessary on some/most Linux systems, but wxWidgets correctly handles any scenario where the OS already does automatic scaling. It's critical on Windows for any code that will ever be run on a high-DPI display, which is why I think it should always be on. I can always expose the UI for turning off scaling at a later date, but I'm reluctant to do so at this point unless someone can give me a reasonable usage scenario where it would be valuable.

Probably worth mentioning that wxWidgets XRC code already does call FromDIP() automatically unless dialog units are specified.

KeyWorksRW avatar Jul 21 '24 17:07 KeyWorksRW

PR #1487 now has initial support for the revised method of scaling and should show up in daily builds starting on 8/9/24. In particular, older projects with dialog units will be converted to physical pixels and imported and new projects will always use physical pixels. Following the way XRC generation handles this, these values will always be scaled using FromDIP() (C++ with wxWidgets 3.2, and all supported versions of wxPython and wxRuby3). Work on this is not 100% complete, particularly for wxPython and wxRuby, but I wanted it in the daily builds for anyone wanting to check out what's being done currently. More changes/fixes for scaling will be coming a bit later this month.

KeyWorksRW avatar Aug 08 '24 15:08 KeyWorksRW

@KeyWorksRW. thank you very much for your effort, it is highly appreciated.

rossanoparis avatar Aug 09 '24 06:08 rossanoparis

At this point I think I've covered all of the scaling issues for forms involving C++ code generation. I've got a separate issue in a different database for verifying the code generation for wxPython and wxRuby. As such I'm closing this issue as completed.

Randalphwa avatar Sep 18 '24 21:09 Randalphwa