Various wxQt fixes including improved high DPI support.

See #24573.
This commit is contained in:
Vadim Zeitlin
2024-06-09 01:02:02 +02:00
16 changed files with 167 additions and 53 deletions
+1 -1
View File
@@ -112,7 +112,7 @@
physical pixels and a window must be 200px wide to have the same apparent
size in high DPI as in normal DPI.
*/
#if defined(__WXGTK3__) || defined(__WXMAC__)
#if defined(__WXGTK3__) || defined(__WXMAC__) || defined(__WXQT__)
#define wxHAS_DPI_INDEPENDENT_PIXELS
// This is an older synonym kept only for compatibility
+5
View File
@@ -38,6 +38,9 @@ public:
virtual bool Create(const wxSize& sz, int depth = wxBITMAP_SCREEN_DEPTH) override;
virtual bool Create(int width, int height, const wxDC& dc);
virtual void SetScaleFactor(double scale);
virtual double GetScaleFactor() const;
virtual int GetHeight() const override;
virtual int GetWidth() const override;
virtual int GetDepth() const override;
@@ -83,6 +86,8 @@ protected:
virtual wxGDIRefData *CreateGDIRefData() const override;
virtual wxGDIRefData *CloneGDIRefData(const wxGDIRefData *data) const override;
virtual bool DoCreate(const wxSize& sz, double scale, int depth) override;
private:
#if wxUSE_IMAGE
void InitFromImage(const wxImage& image, int depth, double WXUNUSED(scale));
+4
View File
@@ -101,6 +101,10 @@ public:
// get the (average) character size for the current font
virtual int GetCharHeight() const override;
virtual int GetCharWidth() const override;
virtual double GetContentScaleFactor() const override;
virtual wxSize GetDPI() const;
virtual double GetDPIScaleFactor() const override;
virtual void SetScrollbar( int orient,
int pos,
+1 -1
View File
@@ -528,7 +528,7 @@ public:
The physical size of the bitmap created by this function depends on the
platform and will be the same as @a size on the platforms for which
`wxHAS_DPI_INDEPENDENT_PIXELS` is not defined (e.g. wxMSW) or @a size
multiplied by @a scale for those where it is (e.g. wxGTK3 and wxOSX).
multiplied by @a scale for those where it is (e.g. wxGTK3, wxOSX and wxQt).
In other words, this function is the same as CreateWithDIPSize() if
`wxHAS_DPI_INDEPENDENT_PIXELS` is defined, but not otherwise.
+1 -1
View File
@@ -104,7 +104,7 @@ void wxAnyButton::QtSetBitmap( const wxBitmapBundle &bitmapBundle )
if ( pixmap != nullptr )
{
m_qtPushButton->setIcon(QIcon(*pixmap));
m_qtPushButton->setIconSize(pixmap->rect().size());
m_qtPushButton->setIconSize(pixmap->rect().size() / pixmap->devicePixelRatio());
InvalidateBestSize();
}
+50 -5
View File
@@ -230,13 +230,15 @@ wxBitmap::wxBitmap(const wxString &filename, wxBitmapType type )
}
#if wxUSE_IMAGE
void wxBitmap::InitFromImage(const wxImage& image, int depth, double WXUNUSED(scale) )
void wxBitmap::InitFromImage(const wxImage& image, int depth, double scale )
{
wxMask* mask = nullptr;
auto qtImage = depth == 1
? QBitmap::fromImage(ConvertImage(image), Qt::ThresholdDither)
: QPixmap::fromImage(ConvertImage(image, &mask));
qtImage.setDevicePixelRatio(scale);
m_refData = new wxBitmapRefData(qtImage, mask);
}
@@ -271,9 +273,38 @@ bool wxBitmap::Create(const wxSize& sz, int depth )
return Create(sz.GetWidth(), sz.GetHeight(), depth);
}
bool wxBitmap::Create(int width, int height, const wxDC& WXUNUSED(dc))
bool wxBitmap::Create(int width, int height, const wxDC& dc)
{
return Create(width, height);
return DoCreate(wxSize(width, height),
dc.GetContentScaleFactor(),
wxBITMAP_SCREEN_DEPTH);
}
bool wxBitmap::DoCreate(const wxSize& size, double scale, int depth)
{
Create(size*scale, depth);
M_PIXDATA.setDevicePixelRatio( scale );
return true;
}
void wxBitmap::SetScaleFactor(double scale)
{
wxCHECK_RET( IsOk(), "invalid bitmap" );
if ( M_PIXDATA.devicePixelRatio() != scale )
{
AllocExclusive();
M_PIXDATA.setDevicePixelRatio( scale );
}
}
double wxBitmap::GetScaleFactor() const
{
wxCHECK_MSG( IsOk(), -1, "invalid bitmap" );
return M_PIXDATA.devicePixelRatio();
}
int wxBitmap::GetHeight() const
@@ -378,9 +409,23 @@ void wxBitmap::SetMask(wxMask *mask)
M_MASK = mask;
}
wxBitmap wxBitmap::GetSubBitmap(const wxRect& rect) const
wxBitmap wxBitmap::GetSubBitmap(const wxRect& r) const
{
wxBitmap bmp = wxBitmap(M_PIXDATA.copy(wxQtConvertRect(rect)));
wxBitmap bmp;
const double s = M_PIXDATA.devicePixelRatio();
const wxRect rect(wxRound(r.x * s), wxRound(r.y * s), wxRound(r.width * s), wxRound(r.height * s));
const int w = rect.width;
const int h = rect.height;
wxCHECK_MSG(rect.x >= 0 && rect.y >= 0 &&
rect.x + w <= M_PIXDATA.width() &&
rect.y + h <= M_PIXDATA.height(),
bmp, wxT("invalid bitmap region"));
bmp = wxBitmap(M_PIXDATA.copy(wxQtConvertRect(rect)));
bmp.SetScaleFactor(s);
if ( M_MASK && M_MASK->GetHandle() )
{
+2 -2
View File
@@ -165,9 +165,9 @@ void wxCursor::InitFromImage( const wxImage & image )
GetHandle() = QCursor(*bmp.GetHandle(),
image.HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_X) ?
image.GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_X) : -1,
image.GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_X) : 0,
image.HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y) ?
image.GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_Y) : -1);
image.GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_Y) : 0);
}
#endif // wxUSE_IMAGE
+2
View File
@@ -57,6 +57,8 @@ void wxMemoryDCImpl::DoSelect( const wxBitmap& bitmap )
m_qtPixmap = bitmap.GetHandle();
if ( bitmap.IsOk() && !m_qtPixmap->isNull() )
{
m_contentScaleFactor = bitmap.GetScaleFactor();
// apply mask before drawing
wxMask *mask = bitmap.GetMask();
if ( mask && mask->GetHandle() )
+25 -9
View File
@@ -11,9 +11,13 @@
#include "wx/display.h"
#include "wx/private/display.h"
#include <QtWidgets/QApplication>
#include <QtWidgets/QDesktopWidget>
#include <QtGui/QScreen>
#include "wx/qt/private/converter.h"
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
#include <QtWidgets/QDesktopWidget>
#endif
class wxDisplayImplQt : public wxDisplayImpl
{
public:
@@ -22,6 +26,7 @@ public:
virtual wxRect GetGeometry() const override;
virtual wxRect GetClientArea() const override;
virtual int GetDepth() const override;
virtual double GetScaleFactor() const override;
#if wxUSE_DISPLAY
virtual wxArrayVideoModes GetModes(const wxVideoMode& mode) const override;
@@ -37,17 +42,22 @@ wxDisplayImplQt::wxDisplayImplQt( unsigned n )
wxRect wxDisplayImplQt::GetGeometry() const
{
return wxQtConvertRect( QApplication::desktop()->screenGeometry( GetIndex() ));
return wxQtConvertRect(QApplication::screens().value(GetIndex())->geometry());
}
wxRect wxDisplayImplQt::GetClientArea() const
{
return wxQtConvertRect( QApplication::desktop()->availableGeometry( GetIndex() ));
return wxQtConvertRect(QApplication::screens().value(GetIndex())->availableGeometry());
}
int wxDisplayImplQt::GetDepth() const
{
return IsPrimary() ? QApplication::desktop()->depth() : 0;
return QApplication::screens().value(GetIndex())->depth();
}
double wxDisplayImplQt::GetScaleFactor() const
{
return QApplication::screens().value(GetIndex())->devicePixelRatio();
}
#if wxUSE_DISPLAY
@@ -58,9 +68,11 @@ wxArrayVideoModes wxDisplayImplQt::GetModes(const wxVideoMode& WXUNUSED(mode)) c
wxVideoMode wxDisplayImplQt::GetCurrentMode() const
{
int width = QApplication::desktop()->width();
int height = QApplication::desktop()->height();
int depth = QApplication::desktop()->depth();
QScreen *screen = QApplication::screens().value(GetIndex());
int width = screen->size().width();
int height = screen->size().height();
int depth = screen->depth();
return wxVideoMode( width, height, depth );
}
@@ -91,12 +103,16 @@ wxDisplayImpl *wxDisplayFactoryQt::CreateDisplay(unsigned n)
unsigned wxDisplayFactoryQt::GetCount()
{
return QApplication::desktop()->screenCount();
return QApplication::screens().size();
}
int wxDisplayFactoryQt::GetFromPoint(const wxPoint& pt)
{
return QApplication::desktop()->screenNumber( wxQtConvertPoint( pt ));
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
return QApplication::screens().indexOf(QApplication::screenAt(wxQtConvertPoint(pt)));
#else
return QApplication::desktop()->screenNumber(wxQtConvertPoint(pt));
#endif
}
//##############################################################################
+33 -16
View File
@@ -31,29 +31,29 @@
namespace
{
Qt::AlignmentFlag wxQtConvertTextAlign(wxListColumnFormat align)
Qt::Alignment wxQtConvertTextAlign(wxListColumnFormat align)
{
switch (align)
{
case wxLIST_FORMAT_LEFT:
return Qt::AlignLeft;
return Qt::AlignLeft | Qt::AlignVCenter;
case wxLIST_FORMAT_RIGHT:
return Qt::AlignRight;
return Qt::AlignRight | Qt::AlignVCenter;
case wxLIST_FORMAT_CENTRE:
return Qt::AlignCenter;
return Qt::AlignHCenter | Qt::AlignVCenter;
}
return Qt::AlignLeft;
return Qt::AlignLeft | Qt::AlignVCenter;
}
wxListColumnFormat wxQtConvertAlignFlag(int align)
{
switch (align)
switch (align & Qt::AlignHorizontal_Mask)
{
case Qt::AlignLeft:
return wxLIST_FORMAT_LEFT;
case Qt::AlignRight:
return wxLIST_FORMAT_RIGHT;
case Qt::AlignCenter:
case Qt::AlignHCenter:
return wxLIST_FORMAT_CENTRE;
}
return wxLIST_FORMAT_LEFT;
@@ -230,7 +230,7 @@ public:
: QVariant();
case Qt::TextAlignmentRole:
return columnItem.m_align;
return static_cast<int>(columnItem.m_align);
case Qt::CheckStateRole:
return col == 0 && m_listCtrl->HasCheckBoxes()
@@ -306,7 +306,7 @@ public:
return header.m_label;
case Qt::TextAlignmentRole:
return header.m_align;
return static_cast<int>(header.m_align);
case Qt::DecorationRole:
{
@@ -647,14 +647,27 @@ public:
if ( row == -1 || static_cast<size_t>(row) >= m_rows.size() )
{
m_rows.push_back(RowItem(m_headers.size()));
size_t colCount = m_headers.size();
RowItem rowItem(colCount);
for (size_t i = 0; i < colCount; i++)
rowItem.m_columns[i].m_align = m_headers[i].m_align;
m_rows.push_back(rowItem);
newRowIndex = m_rows.size() - 1;
}
else
{
std::vector<RowItem>::iterator i = m_rows.begin();
std::advance(i, row);
m_rows.insert(i, RowItem(m_headers.size()));
size_t colCount = m_headers.size();
RowItem rowItem(colCount);
for (size_t col = 0; col < colCount; col++)
rowItem.m_columns[col].m_align = m_headers[col].m_align;
m_rows.insert(i, rowItem);
newRowIndex = row;
}
@@ -805,7 +818,7 @@ private:
struct ColumnItem
{
ColumnItem() :
m_align(Qt::AlignLeft),
m_align(Qt::AlignLeft | Qt::AlignVCenter),
m_image(-1),
m_selectedImage(-1)
{
@@ -815,7 +828,7 @@ private:
QColor m_backgroundColour;
QColor m_textColour;
QFont m_font;
Qt::AlignmentFlag m_align;
Qt::Alignment m_align;
int m_image;
int m_selectedImage;
};
@@ -993,6 +1006,11 @@ public:
if (!current_index.isValid())
return;
// closeEditor can be called through wxQtLineEdit destructor,
// after m_qtEdit in wxTextCtrl has been deleted.
if (!m_itemDelegate.GetEditControl() || m_itemDelegate.GetEditControl()->IsBeingDeleted())
return;
const wxString editedText = m_itemDelegate.GetEditControl()->GetLineText(0);
wxListEvent event;
@@ -1898,9 +1916,8 @@ void wxListCtrl::DoUpdateImages(int which)
if ( imageList )
{
int width, height;
imageList->GetSize(0, width, height);
m_qtTreeWidget->setIconSize(QSize(width, height));
const wxBitmap bitmap = imageList->GetBitmap(0);
m_qtTreeWidget->setIconSize(wxQtConvertSize(bitmap.GetLogicalSize()));
m_qtTreeWidget->update();
}
}
+2 -3
View File
@@ -145,9 +145,8 @@ void wxNotebook::OnImagesChanged()
{
wxImageList* const imageList = GetUpdatedImageListFor(this);
int width, height;
imageList->GetSize(0, width, height);
m_qtTabWidget->setIconSize(QSize(width, height));
const wxBitmap bitmap = imageList->GetBitmap(0);
m_qtTabWidget->setIconSize(wxQtConvertSize(bitmap.GetLogicalSize()));
m_qtTabWidget->update();
}
}
+5 -5
View File
@@ -36,7 +36,7 @@ public:
wxQtSignalHandler(handler)
{
connect(this,
static_cast<void (QButtonGroup::*)(int index)>(&QButtonGroup::buttonClicked),
static_cast<void (QButtonGroup::*)(QAbstractButton *)>(&QButtonGroup::buttonClicked),
this, &wxQtButtonGroup::buttonClicked);
}
@@ -46,17 +46,17 @@ public:
}
private:
void buttonClicked(int index);
void buttonClicked(QAbstractButton *qbutton);
};
void wxQtButtonGroup::buttonClicked(int index)
void wxQtButtonGroup::buttonClicked(QAbstractButton *qbutton)
{
wxRadioBox *handler = GetRadioBox();
if ( handler )
{
wxCommandEvent event( wxEVT_RADIOBOX, handler->GetId() );
event.SetInt(index);
event.SetString(wxQtConvertString(button(index)->text()));
event.SetInt(buttons().indexOf(qbutton));
event.SetString(wxQtConvertString(qbutton->text()));
EmitEvent( event );
}
}
+3 -2
View File
@@ -11,6 +11,7 @@
#include "wx/settings.h"
#include "wx/qt/private/converter.h"
#include <QtGui/QPalette>
#include <QtGui/QScreen>
#include <QtWidgets/QApplication>
#include <QtWidgets/QDesktopWidget>
#include <QtWidgets/QStyle>
@@ -196,10 +197,10 @@ int wxSystemSettingsNative::GetMetric(wxSystemMetric index, const wxWindow* WXUN
return QApplication::style()->pixelMetric(QStyle::PM_IconViewIconSize);
case wxSYS_SCREEN_X:
return QApplication::desktop()->screenGeometry().width();
return QApplication::primaryScreen()->size().width();
case wxSYS_SCREEN_Y:
return QApplication::desktop()->screenGeometry().height();
return QApplication::primaryScreen()->size().height();
case wxSYS_HSCROLL_Y:
case wxSYS_VSCROLL_X:
+6 -1
View File
@@ -697,11 +697,16 @@ bool wxTextCtrl::Create(wxWindow *parent,
wxTextCtrl::~wxTextCtrl()
{
delete m_qtEdit;
m_qtEdit = nullptr;
}
wxSize wxTextCtrl::DoGetBestSize() const
{
return wxTextCtrlBase::DoGetBestSize();
if (IsSingleLine())
return wxQtConvertSize(m_qtEdit->GetHandle()->sizeHint());
return wxSize(80,
1 + GetCharHeight() * wxMax(wxMin(GetNumberOfLines(), 10), 2));
}
int wxTextCtrl::GetLineLength(long lineNo) const
+27
View File
@@ -11,6 +11,7 @@
#include <QtGui/QPicture>
#include <QtGui/QPainter>
#include <QtGui/QWindow>
#include <QtWidgets/QScrollBar>
#include <QtWidgets/QGridLayout>
#include <QtWidgets/QApplication>
@@ -679,6 +680,32 @@ int wxWindowQt::GetCharWidth() const
return ( GetHandle()->fontMetrics().averageCharWidth() );
}
double wxWindowQt::GetContentScaleFactor() const
{
if (GetHandle())
{
QWidget* npw = GetHandle()->nativeParentWidget();
if (npw)
{
QWindow *win = npw->windowHandle();
return win->devicePixelRatio();
}
}
return qApp->devicePixelRatio();
}
double wxWindowQt::GetDPIScaleFactor() const
{
return GetContentScaleFactor();
}
wxSize wxWindowQt::GetDPI() const
{
return MakeDPIFromScaleFactor(GetDPIScaleFactor());
}
void wxWindowQt::DoGetTextExtent(const wxString& string, int *x, int *y, int *descent,
int *externalLeading, const wxFont *font ) const
{
-7
View File
@@ -1364,18 +1364,11 @@ TEST_CASE("wxTextCtrl::GetBestSize", "[wxTextCtrl][best-size]")
s += s;
const wxSize sizeVeryLong = getBestSizeFor(s);
#ifndef __WXQT__
// Control with a few lines of text in it should be taller.
CHECK( sizeMedium.y > sizeEmpty.y );
// And a control with many lines in it should be even more so.
CHECK( sizeLong.y > sizeMedium.y );
#else
// Under wxQt, the multiline textctrl has a fixed calculated best size
// regardless of its content.
CHECK( sizeMedium.y == sizeEmpty.y );
CHECK( sizeLong.y == sizeMedium.y );
#endif
// However there is a cutoff at 10 lines currently, so anything longer than
// that should still have the same best size.