iber999
iber999

Reputation: 89

C++ wxWidgets GetSizeFromText() returns bigger size on high dpi

I have been using wxWidgets 3.1.6 to create a C++ Windows 11 application, which I haven't been having a good experience with so far.

My application has been using absolute positioning for a long time, so I decided to switch to using wxSizers instead.

With the help of wxSizers, my widgets are sized and laid out for me automatically.


I managed to use wxSizers with my application without having to hardcode any sizes.

However when checking to see what my program looks like on high dpi, I see that my wxTextCtrl isn't sized properly.

This is because even though I didn't hardcode any widgets, I did do something similar with my wxTextCtrl.


Consider this code:


/* I am trying to set a wxTextCtrl's size based on the number of characters in it
/* In this case the number of characters is 162.
/* */

wxFont bigfont;           // Creating font
bigfont.SetPointSize(11);

auto textctrl = new wxTextCtrl(panel, wxID_ANY, "");   // Creating textctrls
text->SetFont(bigfont);

std::string number_of_characters = std::string(162, ' ');            
wxSize size = textctrl->GetSizeFromText(number_of_characters);    // When checking the variable "size" in the debugger, it shows a different value for high and low dpi
textctrl->SetMinSize(size);

I am trying to set the wxTextCtrl's size by making it display a given amount of characters, in this case, it's 162.

When checking the variable size in the debugger :


On low dpi:

x = 499 , y = -1

On high dpi:

x = 644 , y = -1

Note: I don't care about the y axis since it will be calculated automatically by the sizers.


How Do I use wxControl::GetSizeFromText() in such a way that I can make my wxTextCtrl have the same size for high and low dpi ??

If I can't do that ( I have realistic expectations ) then I need some other way to size the wxTextCtrl.

Without sizing it myself, The widget would be sized differently which would be bad for UX reasons.

I've tried using FromDIP() and ToDIP() and dividing the size with wxWindow::GetDPIScaleFactor().


They didn't work though:

FromDIP() returned a bigger size, when I needed to decrease the size:

x = 830 y = -1

ToDIP() returned a smaller size :

x = 531 y = -1

Dividing my size with wxWindow::GetDPIScaleFactor() returns the same size as ToDIP().


I checked the value of wxWindow::GetDPIScaleFactor() :


Here is what the problem looks like :

enter image description here

As you can see the wxTextCtrl on high dpi is much longer then the wxTextCtrl on low dpi.

This is what is looks like without FromDIP() and ToDIP().


Thank you.

Edit :

Here is my reproducible example , please test this on high and low dpi to see the difference:

Sample Project

This is only valid 7 days from now...

Edit 2 :

Here is the source code:

wxFrame.cpp

#include "wxFrame.h"

MyFrame::MyFrame() : wxFrame(nullptr,
    wxID_ANY,
    "Sample",
    wxDefaultPosition,
    wxDefaultSize,
    wxDEFAULT_FRAME_STYLE)
{


    wxFont bigfont;
    bigfont.SetPointSize(11);

    wxFont smallfont;
    smallfont.SetPointSize(10);


    wxPanel* panel = new wxPanel(this, wxID_ANY);

    wxBoxSizer* outerSizer = new wxBoxSizer(wxVERTICAL);
    wxFlexGridSizer* fgs = new wxFlexGridSizer(3, 0, 3);
    // row 1
    int hello = 4;
    auto text = new wxStaticText(panel, wxID_ANY, "Input File:");
    auto textctrl = new wxTextCtrl(panel, wxID_ANY, "");
    auto button = new wxButton(panel, wxID_ANY, "   ...   ", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
    text->SetFont(bigfont);

    // Getting textctrl size from text size
    std::string spaces = std::string(162, ' ');
    double dpi = wxWindow::GetDPIScaleFactor();
    wxSize size__ = ToDIP(textctrl->GetSizeFromTextSize(
        textctrl->GetTextExtent(spaces)));
    //  size__ /= dpi;
    textctrl->SetMinSize(size__);

    fgs->Add(text, 0, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL | wxALL, 4);
    fgs->Add(textctrl, 0, wxEXPAND | wxALL, 4);
    fgs->Add(button, 0, wxALL, 4);

    // row 2

    auto text_ = new wxStaticText(panel, wxID_ANY, "Output Folder:");
    auto textctrl_ = new wxTextCtrl(panel, wxID_ANY, "");
    auto button_ = new wxButton(panel, wxID_ANY, "   ...   ", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
    text_->SetFont(bigfont);


    fgs->Add(text_, 0, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL | wxALL, 4);
    fgs->Add(textctrl_, 0, wxEXPAND | wxALL, 4);
    fgs->Add(button_, 0, wxALL, 4);

    fgs->AddGrowableCol(1);

    outerSizer->Add(fgs, 0, wxEXPAND | wxALL, 16);   // <- border at all sides

    panel->SetSizer(outerSizer);
    outerSizer->SetSizeHints(this);
}

MyFrame::~MyFrame()
{
}

wxFrame.h

#include "wxIncludes.h"



///// Our Main Frame
/**/

class MyFrame : public wxFrame {

public :

    // Sample Button
    wxButton* sample = nullptr;

    // Constructor
    MyFrame();


    // Destructor
    ~MyFrame();


};

wxApp.h

///// wxWidgets includes header
/**/

#include "wxIncludes.h"
#include "wxFrame.h"


class myApp : public wxApp {
public :
    
    ///// Startup function
    /**/
    myApp();
    ~myApp();

    virtual bool OnInit();
};

wxApp.cpp

#include "wxApp.h"

///// Initalize the app
/**/

IMPLEMENT_APP(myApp);


// Constructor
myApp::myApp()
{}

// Destructor
myApp::~myApp()
{}


///// Startup function
/**/

bool myApp::OnInit()
{
    // Creating our frame
    MyFrame* myFrame = new MyFrame();
    myFrame->Show();

    // Continue to show window
    return true;

}

wxIncludes.h

///// Winsock header
/**/

#include <winsock2.h>


///// Winsock header
/**/

#include <wx/wx.h>
#include <wx/zipstrm.h>
#include <wx/dir.h>
#include <wx/wfstream.h>
#include <wx/progdlg.h>
#include <wx/dirdlg.h>
#include <wx/statline.h>

Upvotes: 2

Views: 547

Answers (1)

VZ.
VZ.

Reputation: 22753

The fact that GetSizeFromText() doesn't return the same size in high DPI is not a problem at all -- actually it would be a problem if it did return the same size, as then the control would be twice smaller when using 200% scaling, for example.

What you seem to be complaining about it is that its result doesn't scale perfectly with the DPI scaling factor, i.e., if I understand you correctly, you'd expect it to return 1.25*500 == 625 instead of 644 you actually get when using 125% scaling. This is not totally unexpected neither because:

  1. Fonts metrics don't scale exactly with DPI scale.
  2. Text control size is computed using DPI-dependent font and mostly DPI-independent margins.

The difference of 19 pixels is a bit more than I'd expect, but, again, you can't count on it being exactly the same anyhow.

If you really must have the same size in any DPI (why?), you need to scale the desired size in DPI-Independent Pixels (called DIPs in wx API) using FromDIP(), e.g. you could use FromDIP(wxSize(500, -1)) instead. It is not recommended to this, GetSizeFromText() should be more precise, but if you want to have exactly the same size in all resolutions, you could do it like this.

Upvotes: 4

Related Questions