Improvements for wxTextCtrl, wxSearchCtrl, wxButton when using
non-default DPI.

See https://github.com/wxWidgets/wxWidgets/pull/1634
This commit is contained in:
Vadim Zeitlin
2019-11-13 15:47:59 +01:00
7 changed files with 105 additions and 71 deletions

View File

@@ -91,12 +91,13 @@ WXDLLIMPEXP_BASE void wxSetInstance(HINSTANCE hInst);
// Return the height of a native text control corresponding to the given
// character height (as returned by GetCharHeight() or wxGetCharSize()).
//
// The wxWindow parameter must be valid and used for getting the DPI.
inline int wxGetEditHeightFromCharHeight(int cy, const wxWindow* w)
// The wxWindow parameter is currently not used but should still be valid.
inline int wxGetEditHeightFromCharHeight(int cy, const wxWindow* WXUNUSED(w))
{
// The value 8 here is empiric, i.e. it's not necessarily correct, but
// seems to work relatively well.
return cy + w->FromDIP(8);
// Don't use FromDIP(8), this seems not needed.
return cy + 8;
}
// Compatibility macro used in the existing code. It assumes that it's called

View File

@@ -244,6 +244,9 @@ protected:
#if wxUSE_RICHEDIT
virtual void MSWUpdateFontOnDPIChange(const wxSize& newDPI) wxOVERRIDE;
// Apply m_richDPIscale zoom to rich control.
void MSWSetRichZoom();
// Apply the character-related parts of wxTextAttr to the given selection
// or the entire control if start == end == -1.
//
@@ -265,6 +268,10 @@ protected:
// (although not directly: 1 is for 1.0, 2 is for either 2.0 or 3.0 as we
// can't nor really need to distinguish between them and 4 is for 4.1)
int m_verRichEdit;
// Rich text controls need temporary scaling when they are created on a
// display with non-system DPI.
float m_richDPIscale;
#endif // wxUSE_RICHEDIT
// number of EN_UPDATE events sent by Windows when we change the controls

View File

@@ -111,9 +111,6 @@ protected:
// (re)create the wxButton
void CreateButton();
// add m_button to m_sizerButton using current value of m_chkFit
void AddButtonToSizer();
// helper function: create a bitmap for wxBitmapButton
wxBitmap CreateBitmap(const wxString& label, const wxArtID& type);
@@ -315,7 +312,7 @@ void ButtonWidgetsPage::CreateContent()
sizerLeft->AddSpacer(5);
wxButton *btn = new wxButton(this, ButtonPage_Reset, "&Reset");
sizerLeft->Add(btn, wxSizerFlags().CentreHorizontal().Border(wxALL, 15));
sizerLeft->Add(btn, wxSizerFlags().CentreHorizontal().TripleBorder(wxALL));
// middle pane
wxStaticBox *box2 = new wxStaticBox(this, wxID_ANY, "&Operations");
@@ -340,15 +337,15 @@ void ButtonWidgetsPage::CreateContent()
// right pane
m_sizerButton = new wxBoxSizer(wxHORIZONTAL);
m_sizerButton->SetMinSize(150, 0);
m_sizerButton->SetMinSize(FromDIP(150), 0);
// the 3 panes panes compose the window
sizerTop->Add(sizerLeft,
wxSizerFlags(0).Expand().Border((wxALL & ~wxLEFT), 10));
wxSizerFlags(0).Expand().DoubleBorder(wxALL & ~wxLEFT));
sizerTop->Add(sizerMiddle,
wxSizerFlags(1).Expand().Border(wxALL, 10));
wxSizerFlags(1).Expand().DoubleBorder(wxALL));
sizerTop->Add(m_sizerButton,
wxSizerFlags(1).Expand().Border((wxALL & ~wxRIGHT), 10));
wxSizerFlags(1).Expand().DoubleBorder((wxALL & ~wxRIGHT)));
// do create the main control
Reset();
@@ -364,7 +361,7 @@ void ButtonWidgetsPage::CreateContent()
void ButtonWidgetsPage::Reset()
{
m_chkBitmapOnly->SetValue(false);
m_chkFit->SetValue(true);
m_chkFit->SetValue(false);
m_chkAuthNeeded->SetValue(false);
m_chkTextAndBitmap->SetValue(false);
m_chkDefault->SetValue(false);
@@ -453,6 +450,11 @@ void ButtonWidgetsPage::CreateButton()
break;
}
if ( m_chkFit->GetValue() )
{
flags |= wxBU_EXACTFIT;
}
bool showsBitmap = false;
if ( m_chkBitmapOnly->GetValue() )
{
@@ -557,25 +559,13 @@ void ButtonWidgetsPage::CreateButton()
m_button->Enable(!m_chkDisable->IsChecked());
AddButtonToSizer();
m_sizerButton->AddStretchSpacer();
m_sizerButton->Add(m_button, wxSizerFlags().Centre().Border());
m_sizerButton->AddStretchSpacer();
m_sizerButton->Layout();
}
void ButtonWidgetsPage::AddButtonToSizer()
{
if ( m_chkFit->GetValue() )
{
m_sizerButton->AddStretchSpacer(1);
m_sizerButton->Add(m_button, wxSizerFlags(0).Centre().Border());
m_sizerButton->AddStretchSpacer(1);
}
else // take up the entire space
{
m_sizerButton->Add(m_button, wxSizerFlags(1).Expand().Border());
}
}
// ----------------------------------------------------------------------------
// event handlers
// ----------------------------------------------------------------------------

View File

@@ -156,13 +156,13 @@ void SearchCtrlWidgetsPage::CreateContent()
m_searchBtnCheck->SetValue(true);
box->Add(m_searchBtnCheck, 0, wxALL, 5);
box->Add(m_cancelBtnCheck, 0, wxALL, 5);
box->Add(m_menuBtnCheck, 0, wxALL, 5);
box->Add(m_searchBtnCheck, wxSizerFlags().Border());
box->Add(m_cancelBtnCheck, wxSizerFlags().Border());
box->Add(m_menuBtnCheck, wxSizerFlags().Border());
wxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(box, 0, wxALL|wxEXPAND, 15);
sizer->Add(m_srchCtrl, 0, wxALL|wxALIGN_CENTER, 15);
sizer->Add(box, wxSizerFlags().Expand().TripleBorder());
sizer->Add(m_srchCtrl, wxSizerFlags().Centre().TripleBorder());
SetSizer(sizer);
}

View File

@@ -132,7 +132,8 @@ protected:
// can't use wxBORDER_NONE to calculate a good height, in which case we just have to
// assume a border in the code above and then subtract the space that would be taken up
// by a themed border (the thin blue border and the white internal border).
size.y -= FromDIP(4);
// Don't use FromDIP(4), this seems not needed.
size.y -= 4;
self->SetWindowStyleFlag(flags);
@@ -451,28 +452,21 @@ wxString wxSearchCtrl::GetDescriptiveText() const
wxSize wxSearchCtrl::DoGetBestClientSize() const
{
wxSize sizeText = m_text->GetBestSize();
wxSize sizeSearch(0,0);
wxSize sizeCancel(0,0);
int searchMargin = 0;
int cancelMargin = 0;
wxSize size = m_text->GetBestSize();
if ( IsSearchButtonVisible() )
{
sizeSearch = m_searchButton->GetBestSize();
searchMargin = FromDIP(MARGIN);
size.x += m_searchButton->GetBestSize().x + FromDIP(MARGIN);
}
if ( IsCancelButtonVisible() )
{
sizeCancel = m_cancelButton->GetBestSize();
cancelMargin = FromDIP(MARGIN);
size.x += m_cancelButton->GetBestSize().x + FromDIP(MARGIN);
}
int horizontalBorder = FromDIP(1) + ( sizeText.y - sizeText.y * 14 / 21 ) / 2;
int horizontalBorder = FromDIP(1) + (size.y - size.y * 14 / 21 ) / 2;
size.x += 2 * horizontalBorder;
// buttons are square and equal to the height of the text control
int height = sizeText.y;
return wxSize(sizeSearch.x + searchMargin + sizeText.x + cancelMargin + sizeCancel.x + 2*horizontalBorder,
height);
return size;
}
void wxSearchCtrl::LayoutControls()
@@ -533,7 +527,7 @@ void wxSearchCtrl::LayoutControls()
// of the white border that's part of the theme border. We can also remove a pixel from
// the height to fit the text control in, because the padding in EDIT_HEIGHT_FROM_CHAR_HEIGHT
// is already generous.
int textY = FromDIP(2);
int textY = FromDIP(1);
#else
int textY = 0;
#endif

View File

@@ -437,16 +437,16 @@ wxSize wxMSWButton::IncreaseToStdSizeAndCache(wxControl *btn, const wxSize& size
sizeBtn.IncTo(sizeDef);
}
else // wxBU_EXACTFIT case
{
// Such buttons are typically used alongside a text control or similar,
// so make them as high as it.
int yText;
wxGetCharSize(GetHwndOf(btn), NULL, &yText, btn->GetFont());
yText = wxGetEditHeightFromCharHeight(yText, btn);
sizeBtn.IncTo(wxSize(-1, yText));
}
// wxBU_EXACTFIT is typically used alongside a text control or similar,
// so make them as high as it.
// The standard height is generally higher than this, but if not (e.g. when
// using a larger font) increase the button height as well.
int yText;
wxGetCharSize(GetHwndOf(btn), NULL, &yText, btn->GetFont());
yText = wxGetEditHeightFromCharHeight(yText, btn);
sizeBtn.IncTo(wxSize(-1, yText));
btn->CacheBestSize(sizeBtn);

View File

@@ -339,6 +339,7 @@ void wxTextCtrl::Init()
{
#if wxUSE_RICHEDIT
m_verRichEdit = 0;
m_richDPIscale = 1;
#endif // wxUSE_RICHEDIT
#if wxUSE_INKEDIT && wxUSE_RICHEDIT
@@ -608,6 +609,13 @@ bool wxTextCtrl::MSWCreateText(const wxString& value,
#endif
if ( !contextMenuConnected )
Bind(wxEVT_CONTEXT_MENU, &wxTextCtrl::OnContextMenu, this);
// Determine the system DPI and the DPI of the display the rich control
// is shown on, and calculate and apply the scaling factor.
// When this control is created in a (wxFrame) constructor the zoom is
// not correctly applied, use CallAfter to delay setting the zoom.
m_richDPIscale = GetDPI().y / (float)::GetDeviceCaps(ScreenHDC(), LOGPIXELSY);
CallAfter(&wxTextCtrl::MSWSetRichZoom);
}
else
#endif // wxUSE_RICHEDIT
@@ -2538,8 +2546,10 @@ wxSize wxTextCtrl::DoGetBestSize() const
wxSize wxTextCtrl::DoGetSizeFromTextSize(int xlen, int ylen) const
{
int cx, cy;
wxGetCharSize(GetHWND(), &cx, &cy, GetFont());
int cy;
wxFont font = GetFont();
font.WXAdjustToPPI(GetDPI());
wxGetCharSize(GetHWND(), NULL, &cy, font);
DWORD wText = FromDIP(1);
::SystemParametersInfo(SPI_GETCARETWIDTH, 0, &wText, 0);
@@ -2718,14 +2728,49 @@ wxMenu *wxTextCtrl::MSWCreateContextMenu()
return m;
}
void wxTextCtrl::MSWSetRichZoom()
{
// nothing to scale
if ( m_richDPIscale == 1 )
return;
// get the current zoom ratio
UINT num = 1;
UINT denom = 1;
::SendMessage(GetHWND(), EM_GETZOOM, (WPARAM)&num, (LPARAM)&denom);
// combine the zoom ratio with the DPI scale factor
float ratio = m_richDPIscale;
if ( denom > 0 )
ratio = ratio * (num / (float)denom);
// apply the new zoom ratio, Windows uses a default denominator of 100, so
// do it here as well
num = 100 * ratio;
denom = 100;
::SendMessage(GetHWND(), EM_SETZOOM, (WPARAM)num, (LPARAM)denom);
}
void wxTextCtrl::MSWUpdateFontOnDPIChange(const wxSize& newDPI)
{
// Don't do anything for the rich edit controls, they (somehow?) update
// their appearance on their own and changing their HFONT, as the base
// class version does, would reset all the styles used by them when the DPI
// changes, which is unwanted.
// Don't use MSWUpdateFontOnDPIChange for the rich edit controls, they
// (somehow?) update their appearance on their own and changing their
// HFONT, as the base class version does, would reset all the styles used
// by them when the DPI changes, which is unwanted.
if ( !IsRich() )
{
wxTextCtrlBase::MSWUpdateFontOnDPIChange(newDPI);
}
// If the rich control is created on a screen with non-system DPI, an
// initial zoom factor was applied. This needs to be reset after the first
// DPI change. First invert the scale, then set it to 1 so it is not
// applied again.
else if ( m_richDPIscale != 1 )
{
m_richDPIscale = 1 / m_richDPIscale;
MSWSetRichZoom();
m_richDPIscale = 1;
}
}
// ----------------------------------------------------------------------------
@@ -2890,7 +2935,7 @@ bool wxTextCtrl::SetFont(const wxFont& font)
{
// Native text control sends EN_CHANGE when the font changes, producing
// a wxEVT_TEXT event as if the user changed the value. This is not
// the case, so supress the event.
// the case, so suppress the event.
wxEventBlocker block(this, wxEVT_TEXT);
if ( !wxTextCtrlBase::SetFont(font) )
@@ -2980,7 +3025,7 @@ bool wxTextCtrl::MSWSetCharFormat(const wxTextAttr& style, long start, long end)
wxFont font(style.GetFont());
LOGFONT lf = font.GetNativeFontInfo()->lf;
cf.yHeight = 20*font.GetPointSize(); // 1 pt = 20 twips
cf.yHeight = 20 * font.GetFractionalPointSize(); // 1 pt = 20 twips
cf.bCharSet = lf.lfCharSet;
cf.bPitchAndFamily = lf.lfPitchAndFamily;
wxStrlcpy(cf.szFaceName, lf.lfFaceName, WXSIZEOF(cf.szFaceName));
@@ -3303,12 +3348,6 @@ bool wxTextCtrl::GetStyle(long position, wxTextAttr& style)
LOGFONT lf;
// Convert the height from the units of 1/20th of the point in which
// CHARFORMAT stores it to pixel-based units used by LOGFONT.
// Note that RichEdit seems to always use standard DPI of 96, even when the
// window is a monitor using a higher DPI.
lf.lfHeight = wxNativeFontInfo::GetLogFontHeightAtPPI(cf.yHeight/20.0f,
GetDPI().y);
lf.lfWidth = 0;
lf.lfCharSet = ANSI_CHARSET; // FIXME: how to get correct charset?
lf.lfClipPrecision = 0;
@@ -3341,7 +3380,10 @@ bool wxTextCtrl::GetStyle(long position, wxTextAttr& style)
else
lf.lfWeight = FW_NORMAL;
// Determine the pointSize that was used in SetStyle. Don't worry about
// lfHeight or PPI, style.SetFont() will lose this information anyway.
wxFont font(wxNativeFontInfo(lf, this));
font.SetFractionalPointSize(cf.yHeight / 20.0f); // 1 pt = 20 twips
if (font.IsOk())
{
style.SetFont(font);