Use Qt's High-DPI scaling on Windows (#4868)

This commit is contained in:
nerix 2024-05-12 13:59:14 +02:00 committed by GitHub
parent 8202cd0d99
commit febcf464fe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 459 additions and 320 deletions

View file

@ -3,8 +3,10 @@
## Unversioned ## Unversioned
- Major: Release plugins alpha. (#5288) - Major: Release plugins alpha. (#5288)
- Major: Improve high-DPI support on Windows. (#4868)
- Minor: Add option to customise Moderation buttons with images. (#5369) - Minor: Add option to customise Moderation buttons with images. (#5369)
- Bugfix: If a network request errors with 200 OK, Qt's error code is now reported instead of the HTTP status. (#5378) - Bugfix: If a network request errors with 200 OK, Qt's error code is now reported instead of the HTTP status. (#5378)
- Dev: Use Qt's high DPI scaling. (#4868)
- Dev: Add doxygen build target. (#5377) - Dev: Add doxygen build target. (#5377)
- Dev: Make printing of strings in tests easier. (#5379) - Dev: Make printing of strings in tests easier. (#5379)
- Dev: Refactor and document `Scrollbar`. (#5334) - Dev: Refactor and document `Scrollbar`. (#5334)

View file

@ -1,11 +1,11 @@
* { * {
font-size: <font-size>px; font-size: 14px;
font-family: "Segoe UI"; font-family: "Segoe UI";
} }
QCheckBox::indicator { QCheckBox::indicator {
width: <checkbox-size>px; width: 14px;
height: <checkbox-size>px; height: 14px;
} }
chatterino--ComboBox { chatterino--ComboBox {

View file

@ -86,10 +86,6 @@ namespace {
QApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); QApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
#endif #endif
#if defined(Q_OS_WIN32) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QApplication::setAttribute(Qt::AA_DisableHighDpiScaling, true);
#endif
QApplication::setStyle(QStyleFactory::create("Fusion")); QApplication::setStyle(QStyleFactory::create("Fusion"));
#ifndef Q_OS_MAC #ifndef Q_OS_MAC

View file

@ -26,11 +26,6 @@ using namespace chatterino;
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
// TODO: This is a temporary fix (see #4552).
#if defined(Q_OS_WINDOWS) && QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
qputenv("QT_ENABLE_HIGHDPI_SCALING", "0");
#endif
QApplication a(argc, argv); QApplication a(argc, argv);
QCoreApplication::setApplicationName("chatterino"); QCoreApplication::setApplicationName("chatterino");

View file

@ -155,8 +155,8 @@ void EmoteElement::addToContainer(MessageLayoutContainer &container,
{ {
if (flags.has(MessageElementFlag::EmoteImages)) if (flags.has(MessageElementFlag::EmoteImages))
{ {
auto image = auto image = this->emote_->images.getImageOrLoaded(
this->emote_->images.getImageOrLoaded(container.getScale()); container.getImageScale());
if (image->isEmpty()) if (image->isEmpty())
{ {
return; return;
@ -210,7 +210,7 @@ void LayeredEmoteElement::addToContainer(MessageLayoutContainer &container,
{ {
if (flags.has(MessageElementFlag::EmoteImages)) if (flags.has(MessageElementFlag::EmoteImages))
{ {
auto images = this->getLoadedImages(container.getScale()); auto images = this->getLoadedImages(container.getImageScale());
if (images.empty()) if (images.empty())
{ {
return; return;
@ -364,7 +364,7 @@ void BadgeElement::addToContainer(MessageLayoutContainer &container,
if (flags.hasAny(this->getFlags())) if (flags.hasAny(this->getFlags()))
{ {
auto image = auto image =
this->emote_->images.getImageOrLoaded(container.getScale()); this->emote_->images.getImageOrLoaded(container.getImageScale());
if (image->isEmpty()) if (image->isEmpty())
{ {
return; return;
@ -798,7 +798,7 @@ void ScalingImageElement::addToContainer(MessageLayoutContainer &container,
if (flags.hasAny(this->getFlags())) if (flags.hasAny(this->getFlags()))
{ {
const auto &image = const auto &image =
this->images_.getImageOrLoaded(container.getScale()); this->images_.getImageOrLoaded(container.getImageScale());
if (image->isEmpty()) if (image->isEmpty())
{ {
return; return;

View file

@ -74,7 +74,8 @@ int MessageLayout::getWidth() const
// Layout // Layout
// return true if redraw is required // return true if redraw is required
bool MessageLayout::layout(int width, float scale, MessageElementFlags flags, bool MessageLayout::layout(int width, float scale, float imageScale,
MessageElementFlags flags,
bool shouldInvalidateBuffer) bool shouldInvalidateBuffer)
{ {
// BenchmarkGuard benchmark("MessageLayout::layout()"); // BenchmarkGuard benchmark("MessageLayout::layout()");
@ -106,6 +107,8 @@ bool MessageLayout::layout(int width, float scale, MessageElementFlags flags,
// check if dpi changed // check if dpi changed
layoutRequired |= this->scale_ != scale; layoutRequired |= this->scale_ != scale;
this->scale_ = scale; this->scale_ = scale;
layoutRequired |= this->imageScale_ != imageScale;
this->imageScale_ = imageScale;
if (!layoutRequired) if (!layoutRequired)
{ {
@ -148,7 +151,8 @@ void MessageLayout::actuallyLayout(int width, MessageElementFlags flags)
bool hideSimilar = getSettings()->hideSimilar; bool hideSimilar = getSettings()->hideSimilar;
bool hideReplies = !flags.has(MessageElementFlag::RepliedMessage); bool hideReplies = !flags.has(MessageElementFlag::RepliedMessage);
this->container_.beginLayout(width, this->scale_, messageFlags); this->container_.beginLayout(width, this->scale_, this->imageScale_,
messageFlags);
for (const auto &element : this->message_->elements) for (const auto &element : this->message_->elements)
{ {
@ -288,16 +292,11 @@ QPixmap *MessageLayout::ensureBuffer(QPainter &painter, int width)
} }
// Create new buffer // Create new buffer
#if defined(Q_OS_MACOS) || defined(Q_OS_LINUX)
this->buffer_ = std::make_unique<QPixmap>( this->buffer_ = std::make_unique<QPixmap>(
int(width * painter.device()->devicePixelRatioF()), int(width * painter.device()->devicePixelRatioF()),
int(this->container_.getHeight() * int(this->container_.getHeight() *
painter.device()->devicePixelRatioF())); painter.device()->devicePixelRatioF()));
this->buffer_->setDevicePixelRatio(painter.device()->devicePixelRatioF()); this->buffer_->setDevicePixelRatio(painter.device()->devicePixelRatioF());
#else
this->buffer_ = std::make_unique<QPixmap>(
width, std::max(16, this->container_.getHeight()));
#endif
this->bufferValid_ = false; this->bufferValid_ = false;
DebugCount::increase("message drawing buffers"); DebugCount::increase("message drawing buffers");

View file

@ -56,8 +56,8 @@ public:
MessageLayoutFlags flags; MessageLayoutFlags flags;
bool layout(int width, float scale_, MessageElementFlags flags, bool layout(int width, float scale_, float imageScale,
bool shouldInvalidateBuffer); MessageElementFlags flags, bool shouldInvalidateBuffer);
// Painting // Painting
MessagePaintResult paint(const MessagePaintContext &ctx); MessagePaintResult paint(const MessagePaintContext &ctx);
@ -128,6 +128,7 @@ private:
int currentLayoutWidth_ = -1; int currentLayoutWidth_ = -1;
int layoutState_ = -1; int layoutState_ = -1;
float scale_ = -1; float scale_ = -1;
float imageScale_ = -1.F;
MessageElementFlags currentWordFlags_; MessageElementFlags currentWordFlags_;
#ifdef FOURTF #ifdef FOURTF

View file

@ -30,7 +30,7 @@ constexpr const QMargins MARGIN{8, 4, 8, 4};
namespace chatterino { namespace chatterino {
void MessageLayoutContainer::beginLayout(int width, float scale, void MessageLayoutContainer::beginLayout(int width, float scale,
MessageFlags flags) float imageScale, MessageFlags flags)
{ {
this->elements_.clear(); this->elements_.clear();
this->lines_.clear(); this->lines_.clear();
@ -45,6 +45,7 @@ void MessageLayoutContainer::beginLayout(int width, float scale,
this->width_ = width; this->width_ = width;
this->height_ = 0; this->height_ = 0;
this->scale_ = scale; this->scale_ = scale;
this->imageScale_ = imageScale;
this->flags_ = flags; this->flags_ = flags;
auto mediumFontMetrics = auto mediumFontMetrics =
getIApp()->getFonts()->getFontMetrics(FontStyle::ChatMedium, scale); getIApp()->getFonts()->getFontMetrics(FontStyle::ChatMedium, scale);
@ -526,6 +527,11 @@ float MessageLayoutContainer::getScale() const
return this->scale_; return this->scale_;
} }
float MessageLayoutContainer::getImageScale() const
{
return this->imageScale_;
}
bool MessageLayoutContainer::isCollapsed() const bool MessageLayoutContainer::isCollapsed() const
{ {
return this->isCollapsed_; return this->isCollapsed_;

View file

@ -32,7 +32,8 @@ struct MessageLayoutContainer {
* This will reset all line calculations, and will be considered incomplete * This will reset all line calculations, and will be considered incomplete
* until the accompanying end function has been called * until the accompanying end function has been called
*/ */
void beginLayout(int width_, float scale_, MessageFlags flags_); void beginLayout(int width, float scale, float imageScale,
MessageFlags flags);
/** /**
* Finish the layout process of this message * Finish the layout process of this message
@ -146,6 +147,11 @@ struct MessageLayoutContainer {
*/ */
float getScale() const; float getScale() const;
/**
* Returns the image scale
*/
float getImageScale() const;
/** /**
* Returns true if this message is collapsed * Returns true if this message is collapsed
*/ */
@ -270,6 +276,10 @@ private:
// variables // variables
float scale_ = 1.F; float scale_ = 1.F;
/**
* Scale factor for images
*/
float imageScale_ = 1.F;
int width_ = 0; int width_ = 0;
MessageFlags flags_{}; MessageFlags flags_{};
/** /**

View file

@ -270,20 +270,22 @@ void AttachedWindow::updateWindowRect(void *_attachedPtr)
} }
float scale = 1.f; float scale = 1.f;
float ourScale = 1.F;
if (auto dpi = getWindowDpi(attached)) if (auto dpi = getWindowDpi(attached))
{ {
scale = *dpi / 96.f; scale = *dpi / 96.f;
ourScale = scale / this->devicePixelRatio();
for (auto w : this->ui_.split->findChildren<BaseWidget *>()) for (auto w : this->ui_.split->findChildren<BaseWidget *>())
{ {
w->setOverrideScale(scale); w->setOverrideScale(ourScale);
} }
this->ui_.split->setOverrideScale(scale); this->ui_.split->setOverrideScale(ourScale);
} }
if (this->height_ != -1) if (this->height_ != -1)
{ {
this->ui_.split->setFixedWidth(int(this->width_ * scale)); this->ui_.split->setFixedWidth(int(this->width_ * ourScale));
// offset // offset
int o = this->fullscreen_ ? 0 : 8; int o = this->fullscreen_ ? 0 : 8;

View file

@ -120,19 +120,6 @@ void BaseWidget::setScaleIndependantHeight(int value)
QSize(this->scaleIndependantSize_.width(), value)); QSize(this->scaleIndependantSize_.width(), value));
} }
float BaseWidget::qtFontScale() const
{
if (auto *window = dynamic_cast<BaseWindow *>(this->window()))
{
// ensure no div by 0
return this->scale() / std::max<float>(0.01f, window->nativeScale_);
}
else
{
return this->scale();
}
}
void BaseWidget::childEvent(QChildEvent *event) void BaseWidget::childEvent(QChildEvent *event)
{ {
if (event->added()) if (event->added())

View file

@ -34,8 +34,6 @@ public:
void setScaleIndependantWidth(int value); void setScaleIndependantWidth(int value);
void setScaleIndependantHeight(int value); void setScaleIndependantHeight(int value);
float qtFontScale() const;
protected: protected:
void childEvent(QChildEvent *) override; void childEvent(QChildEvent *) override;
void showEvent(QShowEvent *) override; void showEvent(QShowEvent *) override;

View file

@ -29,12 +29,163 @@
# pragma comment(lib, "Dwmapi.lib") # pragma comment(lib, "Dwmapi.lib")
# include <QHBoxLayout> # include <QHBoxLayout>
# include <QOperatingSystemVersion>
# define WM_DPICHANGED 0x02E0
#endif #endif
#include "widgets/helper/TitlebarButton.hpp" #include "widgets/helper/TitlebarButton.hpp"
namespace {
#ifdef USEWINSDK
// From kHiddenTaskbarSize in Firefox
constexpr UINT HIDDEN_TASKBAR_SIZE = 2;
bool isWindows11OrGreater()
{
static const bool result = [] {
// This calls RtlGetVersion under the hood so we don't have to.
// The micro version corresponds to dwBuildNumber.
auto version = QOperatingSystemVersion::current();
return (version.majorVersion() > 10) ||
(version.microVersion() >= 22000);
}();
return result;
}
/// Finds the taskbar HWND on a specific monitor (or any)
HWND findTaskbarWindow(LPRECT rcMon = nullptr)
{
HWND taskbar = nullptr;
RECT taskbarRect;
// return value of IntersectRect, unused
RECT intersectionRect;
while ((taskbar = FindWindowEx(nullptr, taskbar, L"Shell_TrayWnd",
nullptr)) != nullptr)
{
if (!rcMon)
{
// no monitor was specified, return the first encountered window
break;
}
if (GetWindowRect(taskbar, &taskbarRect) != 0 &&
IntersectRect(&intersectionRect, &taskbarRect, rcMon) != 0)
{
// taskbar intersects with the monitor - this is the one
break;
}
}
return taskbar;
}
/// Gets the edge of the taskbar if it's automatically hidden
std::optional<UINT> hiddenTaskbarEdge(LPRECT rcMon = nullptr)
{
HWND taskbar = findTaskbarWindow(rcMon);
if (!taskbar)
{
return std::nullopt;
}
APPBARDATA state = {sizeof(state), taskbar};
APPBARDATA pos = {sizeof(pos), taskbar};
auto appBarState =
static_cast<LRESULT>(SHAppBarMessage(ABM_GETSTATE, &state));
if ((appBarState & ABS_AUTOHIDE) == 0)
{
return std::nullopt;
}
if (SHAppBarMessage(ABM_GETTASKBARPOS, &pos) == 0)
{
qCDebug(chatterinoApp) << "Failed to get taskbar pos";
return ABE_BOTTOM;
}
return pos.uEdge;
}
/// @brief Gets the window borders for @a hwnd
///
/// Each side of the returned RECT has the correct sign, so they can be added
/// to a window rect.
/// Shrinking by 1px would return {left: 1, top: 1, right: -1, left: -1}.
RECT windowBordersFor(HWND hwnd, bool isMaximized)
{
RECT margins{0, 0, 0, 0};
auto addBorders = isMaximized || isWindows11OrGreater();
if (addBorders)
{
auto dpi = GetDpiForWindow(hwnd);
auto systemMetric = [&](auto index) {
if (dpi != 0)
{
return GetSystemMetricsForDpi(index, dpi);
}
return GetSystemMetrics(index);
};
auto paddedBorder = systemMetric(SM_CXPADDEDBORDER);
auto borderWidth = systemMetric(SM_CXSIZEFRAME) + paddedBorder;
auto borderHeight = systemMetric(SM_CYSIZEFRAME) + paddedBorder;
margins.left += borderWidth;
margins.right -= borderWidth;
if (isMaximized)
{
margins.top += borderHeight;
}
margins.bottom -= borderHeight;
}
if (isMaximized)
{
auto *hMonitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
MONITORINFO mi;
mi.cbSize = sizeof(mi);
auto *monitor = [&]() -> LPRECT {
if (GetMonitorInfo(hMonitor, &mi))
{
return &mi.rcMonitor;
}
return nullptr;
}();
auto edge = hiddenTaskbarEdge(monitor);
if (edge)
{
switch (*edge)
{
case ABE_LEFT:
margins.left += HIDDEN_TASKBAR_SIZE;
break;
case ABE_RIGHT:
margins.right -= HIDDEN_TASKBAR_SIZE;
break;
case ABE_TOP:
margins.top += HIDDEN_TASKBAR_SIZE;
break;
case ABE_BOTTOM:
margins.bottom -= HIDDEN_TASKBAR_SIZE;
break;
default:
break;
}
}
}
return margins;
}
#endif
} // namespace
namespace chatterino { namespace chatterino {
BaseWindow::BaseWindow(FlagsEnum<Flags> _flags, QWidget *parent) BaseWindow::BaseWindow(FlagsEnum<Flags> _flags, QWidget *parent)
@ -117,95 +268,80 @@ float BaseWindow::scale() const
return std::max<float>(0.01f, this->overrideScale().value_or(this->scale_)); return std::max<float>(0.01f, this->overrideScale().value_or(this->scale_));
} }
float BaseWindow::qtFontScale() const
{
return this->scale() / std::max<float>(0.01F, this->nativeScale_);
}
void BaseWindow::init() void BaseWindow::init()
{ {
#ifdef USEWINSDK #ifdef USEWINSDK
if (this->hasCustomWindowFrame()) if (this->hasCustomWindowFrame())
{ {
// CUSTOM WINDOW FRAME // CUSTOM WINDOW FRAME
QVBoxLayout *layout = new QVBoxLayout(); auto *layout = new QVBoxLayout(this);
this->ui_.windowLayout = layout; this->ui_.windowLayout = layout;
layout->setContentsMargins(1, 1, 1, 1); layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(0); layout->setSpacing(0);
this->setLayout(layout);
if (!this->frameless_)
{ {
if (!this->frameless_) QHBoxLayout *buttonLayout = this->ui_.titlebarBox =
{ new QHBoxLayout();
QHBoxLayout *buttonLayout = this->ui_.titlebarBox = buttonLayout->setContentsMargins(0, 0, 0, 0);
new QHBoxLayout(); layout->addLayout(buttonLayout);
buttonLayout->setContentsMargins(0, 0, 0, 0);
layout->addLayout(buttonLayout);
// title // title
Label *title = new Label; Label *title = new Label;
QObject::connect(this, &QWidget::windowTitleChanged, QObject::connect(this, &QWidget::windowTitleChanged,
[title](const QString &text) { [title](const QString &text) {
title->setText(text); title->setText(text);
}); });
QSizePolicy policy(QSizePolicy::Ignored, QSizePolicy policy(QSizePolicy::Ignored, QSizePolicy::Preferred);
QSizePolicy::Preferred); policy.setHorizontalStretch(1);
policy.setHorizontalStretch(1); title->setSizePolicy(policy);
title->setSizePolicy(policy); buttonLayout->addWidget(title);
buttonLayout->addWidget(title); this->ui_.titleLabel = title;
this->ui_.titleLabel = title;
// buttons // buttons
TitleBarButton *_minButton = new TitleBarButton; auto *minButton = new TitleBarButton;
_minButton->setButtonStyle(TitleBarButtonStyle::Minimize); minButton->setButtonStyle(TitleBarButtonStyle::Minimize);
TitleBarButton *_maxButton = new TitleBarButton; auto *maxButton = new TitleBarButton;
_maxButton->setButtonStyle(TitleBarButtonStyle::Maximize); maxButton->setButtonStyle(TitleBarButtonStyle::Maximize);
TitleBarButton *_exitButton = new TitleBarButton; auto *exitButton = new TitleBarButton;
_exitButton->setButtonStyle(TitleBarButtonStyle::Close); exitButton->setButtonStyle(TitleBarButtonStyle::Close);
QObject::connect(_minButton, &TitleBarButton::leftClicked, this, QObject::connect(minButton, &TitleBarButton::leftClicked, this,
[this] { [this] {
this->setWindowState(Qt::WindowMinimized | this->setWindowState(Qt::WindowMinimized |
this->windowState()); this->windowState());
}); });
QObject::connect(_maxButton, &TitleBarButton::leftClicked, this, QObject::connect(
[this, _maxButton] { maxButton, &TitleBarButton::leftClicked, this,
this->setWindowState( [this, maxButton] {
_maxButton->getButtonStyle() != this->setWindowState(maxButton->getButtonStyle() !=
TitleBarButtonStyle::Maximize TitleBarButtonStyle::Maximize
? Qt::WindowActive ? Qt::WindowActive
: Qt::WindowMaximized); : Qt::WindowMaximized);
}); });
QObject::connect(_exitButton, &TitleBarButton::leftClicked, QObject::connect(exitButton, &TitleBarButton::leftClicked, this,
this, [this] { [this] {
this->close(); this->close();
}); });
this->ui_.titlebarButtons = new TitleBarButtons( this->ui_.titlebarButtons =
this, _minButton, _maxButton, _exitButton); new TitleBarButtons(this, minButton, maxButton, exitButton);
this->ui_.buttons.push_back(_minButton); this->ui_.buttons.push_back(minButton);
this->ui_.buttons.push_back(_maxButton); this->ui_.buttons.push_back(maxButton);
this->ui_.buttons.push_back(_exitButton); this->ui_.buttons.push_back(exitButton);
// buttonLayout->addStretch(1); buttonLayout->addWidget(minButton);
buttonLayout->addWidget(_minButton); buttonLayout->addWidget(maxButton);
buttonLayout->addWidget(_maxButton); buttonLayout->addWidget(exitButton);
buttonLayout->addWidget(_exitButton); buttonLayout->setSpacing(0);
buttonLayout->setSpacing(0);
}
} }
this->ui_.layoutBase = new BaseWidget(this); this->ui_.layoutBase = new BaseWidget(this);
this->ui_.layoutBase->setContentsMargins(1, 0, 1, 1); this->ui_.layoutBase->setContentsMargins(1, 0, 1, 1);
layout->addWidget(this->ui_.layoutBase); layout->addWidget(this->ui_.layoutBase);
} }
// DPI
// auto dpi = getWindowDpi(this->safeHWND());
// if (dpi) {
// this->scale = dpi.value() / 96.f;
// }
#endif #endif
// TopMost flag overrides setting // TopMost flag overrides setting
@ -571,29 +707,8 @@ void BaseWindow::resizeEvent(QResizeEvent *)
} }
#ifdef USEWINSDK #ifdef USEWINSDK
if (this->hasCustomWindowFrame() && !this->isResizeFixing_)
{
this->isResizeFixing_ = true;
QTimer::singleShot(50, this, [this] {
auto hwnd = this->safeHWND();
if (!hwnd)
{
this->isResizeFixing_ = false;
return;
}
RECT rect;
::GetWindowRect(*hwnd, &rect);
::SetWindowPos(*hwnd, nullptr, 0, 0, rect.right - rect.left + 1,
rect.bottom - rect.top, SWP_NOMOVE | SWP_NOZORDER);
::SetWindowPos(*hwnd, nullptr, 0, 0, rect.right - rect.left,
rect.bottom - rect.top, SWP_NOMOVE | SWP_NOZORDER);
QTimer::singleShot(10, this, [this] {
this->isResizeFixing_ = false;
});
});
}
this->calcButtonsSizes(); this->calcButtonsSizes();
this->updateRealSize();
#endif #endif
} }
@ -655,10 +770,6 @@ bool BaseWindow::nativeEvent(const QByteArray &eventType, void *message,
switch (msg->message) switch (msg->message)
{ {
case WM_DPICHANGED:
returnValue = this->handleDPICHANGED(msg);
break;
case WM_SHOWWINDOW: case WM_SHOWWINDOW:
returnValue = this->handleSHOWWINDOW(msg); returnValue = this->handleSHOWWINDOW(msg);
break; break;
@ -697,12 +808,15 @@ bool BaseWindow::nativeEvent(const QByteArray &eventType, void *message,
{ {
*result = 0; *result = 0;
returnValue = true; returnValue = true;
long x = GET_X_LPARAM(msg->lParam);
long y = GET_Y_LPARAM(msg->lParam);
RECT winrect; POINT p{GET_X_LPARAM(msg->lParam), GET_Y_LPARAM(msg->lParam)};
GetWindowRect(msg->hwnd, &winrect); ScreenToClient(msg->hwnd, &p);
QPoint globalPos(x, y);
QPoint globalPos(p.x, p.y);
globalPos /= this->devicePixelRatio();
globalPos = this->mapToGlobal(globalPos);
// TODO(nerix): use TrackMouseEvent here
this->ui_.titlebarButtons->hover(msg->wParam, globalPos); this->ui_.titlebarButtons->hover(msg->wParam, globalPos);
this->lastEventWasNcMouseMove_ = true; this->lastEventWasNcMouseMove_ = true;
} }
@ -748,12 +862,14 @@ bool BaseWindow::nativeEvent(const QByteArray &eventType, void *message,
*result = 0; *result = 0;
auto ht = msg->wParam; auto ht = msg->wParam;
long x = GET_X_LPARAM(msg->lParam);
long y = GET_Y_LPARAM(msg->lParam);
RECT winrect; POINT p{GET_X_LPARAM(msg->lParam), GET_Y_LPARAM(msg->lParam)};
GetWindowRect(msg->hwnd, &winrect); ScreenToClient(msg->hwnd, &p);
QPoint globalPos(x, y);
QPoint globalPos(p.x, p.y);
globalPos /= this->devicePixelRatio();
globalPos = this->mapToGlobal(globalPos);
if (msg->message == WM_NCLBUTTONDOWN) if (msg->message == WM_NCLBUTTONDOWN)
{ {
this->ui_.titlebarButtons->mousePress(ht, globalPos); this->ui_.titlebarButtons->mousePress(ht, globalPos);
@ -784,7 +900,7 @@ void BaseWindow::scaleChangedEvent(float scale)
#endif #endif
this->setFont( this->setFont(
getIApp()->getFonts()->getFont(FontStyle::UiTabs, this->qtFontScale())); getIApp()->getFonts()->getFont(FontStyle::UiTabs, this->scale()));
} }
void BaseWindow::paintEvent(QPaintEvent *) void BaseWindow::paintEvent(QPaintEvent *)
@ -802,10 +918,9 @@ void BaseWindow::paintEvent(QPaintEvent *)
void BaseWindow::updateScale() void BaseWindow::updateScale()
{ {
auto scale = auto scale = this->flags_.has(DisableCustomScaling)
this->nativeScale_ * (this->flags_.has(DisableCustomScaling) ? 1
? 1 : getSettings()->getClampedUiScale();
: getSettings()->getClampedUiScale());
this->setScale(scale); this->setScale(scale);
@ -815,6 +930,22 @@ void BaseWindow::updateScale()
} }
} }
#ifdef USEWINSDK
void BaseWindow::updateRealSize()
{
auto hwnd = this->safeHWND();
if (!hwnd)
{
return;
}
RECT real;
::GetWindowRect(*hwnd, &real);
this->realBounds_ = QRect(real.left, real.top, real.right - real.left,
real.bottom - real.top);
}
#endif
void BaseWindow::calcButtonsSizes() void BaseWindow::calcButtonsSizes()
{ {
if (!this->shown_) if (!this->shown_)
@ -846,34 +977,28 @@ void BaseWindow::drawCustomWindowFrame(QPainter &painter)
{ {
QColor bg = this->overrideBackgroundColor_.value_or( QColor bg = this->overrideBackgroundColor_.value_or(
this->theme->window.background); this->theme->window.background);
painter.fillRect(QRect(1, 2, this->width() - 2, this->height() - 3), if (this->isMaximized_)
bg); {
painter.fillRect(this->rect(), bg);
}
else
{
// Draw a border that's exactly 1px wide
//
// There is a bug where the border can get <dpr>px wide while dragging.
// this "fixes" itself when deselecting the window.
auto dpr = this->devicePixelRatio();
if (dpr != 1)
{
painter.setTransform(QTransform::fromScale(1 / dpr, 1 / dpr));
}
painter.fillRect(1, 1, this->realBounds_.width() - 2,
this->realBounds_.height() - 2, bg);
}
} }
#endif #endif
} }
bool BaseWindow::handleDPICHANGED(MSG *msg)
{
#ifdef USEWINSDK
int dpi = HIWORD(msg->wParam);
float _scale = dpi / 96.f;
auto *prcNewWindow = reinterpret_cast<RECT *>(msg->lParam);
SetWindowPos(msg->hwnd, nullptr, prcNewWindow->left, prcNewWindow->top,
prcNewWindow->right - prcNewWindow->left,
prcNewWindow->bottom - prcNewWindow->top,
SWP_NOZORDER | SWP_NOACTIVATE);
this->nativeScale_ = _scale;
this->updateScale();
return true;
#else
return false;
#endif
}
bool BaseWindow::handleSHOWWINDOW(MSG *msg) bool BaseWindow::handleSHOWWINDOW(MSG *msg)
{ {
#ifdef USEWINSDK #ifdef USEWINSDK
@ -883,16 +1008,6 @@ bool BaseWindow::handleSHOWWINDOW(MSG *msg)
return true; return true;
} }
if (auto dpi = getWindowDpi(msg->hwnd))
{
float currentScale = (float)dpi.value() / 96.F;
if (currentScale != this->nativeScale_)
{
this->nativeScale_ = currentScale;
this->updateScale();
}
}
if (!this->shown_) if (!this->shown_)
{ {
this->shown_ = true; this->shown_ = true;
@ -906,14 +1021,12 @@ bool BaseWindow::handleSHOWWINDOW(MSG *msg)
if (!this->initalBounds_.isNull()) if (!this->initalBounds_.isNull())
{ {
::SetWindowPos(msg->hwnd, nullptr, this->initalBounds_.x(), this->setGeometry(this->initalBounds_);
this->initalBounds_.y(), this->initalBounds_.width(),
this->initalBounds_.height(),
SWP_NOZORDER | SWP_NOACTIVATE);
this->currentBounds_ = this->initalBounds_; this->currentBounds_ = this->initalBounds_;
} }
this->calcButtonsSizes(); this->calcButtonsSizes();
this->updateRealSize();
} }
return true; return true;
@ -929,23 +1042,54 @@ bool BaseWindow::handleNCCALCSIZE(MSG *msg, long *result)
#endif #endif
{ {
#ifdef USEWINSDK #ifdef USEWINSDK
if (this->hasCustomWindowFrame()) if (!this->hasCustomWindowFrame())
{ {
if (msg->wParam == TRUE) return false;
{ }
// remove 1 extra pixel on top of custom frame
auto *ncp = reinterpret_cast<NCCALCSIZE_PARAMS *>(msg->lParam);
if (ncp)
{
ncp->lppos->flags |= SWP_NOREDRAW;
ncp->rgrc[0].top -= 1;
}
}
if (msg->wParam != TRUE)
{
*result = 0; *result = 0;
return true; return true;
} }
return false;
auto *params = reinterpret_cast<NCCALCSIZE_PARAMS *>(msg->lParam);
auto *r = &params->rgrc[0];
WINDOWPLACEMENT wp;
wp.length = sizeof(WINDOWPLACEMENT);
this->isMaximized_ = GetWindowPlacement(msg->hwnd, &wp) != 0 &&
(wp.showCmd == SW_SHOWMAXIMIZED);
auto borders = windowBordersFor(msg->hwnd, this->isMaximized_);
r->left += borders.left;
r->top += borders.top;
r->right += borders.right;
r->bottom += borders.bottom;
if (borders.left != 0 || borders.top != 0 || borders.right != 0 ||
borders.bottom != 0)
{
// We added borders -> we changed the rect, so we can't return
// WVR_VALIDRECTS
*result = 0;
return true;
}
// This is an attempt at telling Windows to not redraw (or at least to do a
// better job at redrawing) the window. There is a long list of tricks
// people tried to prevent this at
// https://stackoverflow.com/q/53000291/16300717
//
// We set the source and destination rectangles to a 1x1 rectangle at the
// top left. Windows is instructed by WVR_VALIDRECTS to copy and preserve
// some parts of the window image.
QPoint fixed = {r->left, r->top};
params->rgrc[1] = {fixed.x(), fixed.y(), fixed.x() + 1, fixed.y() + 1};
params->rgrc[2] = {fixed.x(), fixed.y(), fixed.x() + 1, fixed.y() + 1};
*result = WVR_VALIDRECTS;
return true;
#else #else
return false; return false;
#endif #endif
@ -962,28 +1106,11 @@ bool BaseWindow::handleSIZE(MSG *msg)
} }
else if (this->hasCustomWindowFrame()) else if (this->hasCustomWindowFrame())
{ {
if (msg->wParam == SIZE_MAXIMIZED)
{
auto offset =
int(getWindowDpi(msg->hwnd).value_or(96) * 8 / 96);
this->ui_.windowLayout->setContentsMargins(offset, offset,
offset, offset);
}
else
{
this->ui_.windowLayout->setContentsMargins(0, 1, 0, 0);
}
this->isNotMinimizedOrMaximized_ = msg->wParam == SIZE_RESTORED; this->isNotMinimizedOrMaximized_ = msg->wParam == SIZE_RESTORED;
if (this->isNotMinimizedOrMaximized_) if (this->isNotMinimizedOrMaximized_)
{ {
RECT rect; this->currentBounds_ = this->geometry();
::GetWindowRect(msg->hwnd, &rect);
this->currentBounds_ =
QRect(QPoint(rect.left, rect.top),
QPoint(rect.right - 1, rect.bottom - 1));
} }
this->useNextBounds_.stop(); this->useNextBounds_.stop();
@ -993,6 +1120,12 @@ bool BaseWindow::handleSIZE(MSG *msg)
// the minimize button, so we have to emulate it. // the minimize button, so we have to emulate it.
this->ui_.titlebarButtons->leave(); this->ui_.titlebarButtons->leave();
} }
RECT real;
::GetWindowRect(msg->hwnd, &real);
this->realBounds_ =
QRect(real.left, real.top, real.right - real.left,
real.bottom - real.top);
} }
} }
return false; return false;
@ -1006,11 +1139,7 @@ bool BaseWindow::handleMOVE(MSG *msg)
#ifdef USEWINSDK #ifdef USEWINSDK
if (this->isNotMinimizedOrMaximized_) if (this->isNotMinimizedOrMaximized_)
{ {
RECT rect; this->nextBounds_ = this->geometry();
::GetWindowRect(msg->hwnd, &rect);
this->nextBounds_ = QRect(QPoint(rect.left, rect.top),
QPoint(rect.right - 1, rect.bottom - 1));
this->useNextBounds_.start(10); this->useNextBounds_.start(10);
} }
#endif #endif
@ -1024,31 +1153,37 @@ bool BaseWindow::handleNCHITTEST(MSG *msg, long *result)
#endif #endif
{ {
#ifdef USEWINSDK #ifdef USEWINSDK
const LONG border_width = 8; // in pixels const LONG borderWidth = 8; // in device independent pixels
RECT winrect;
GetWindowRect(msg->hwnd, &winrect);
long x = GET_X_LPARAM(msg->lParam); auto rect = this->rect();
long y = GET_Y_LPARAM(msg->lParam);
QPoint point(x - winrect.left, y - winrect.top); POINT p{GET_X_LPARAM(msg->lParam), GET_Y_LPARAM(msg->lParam)};
ScreenToClient(msg->hwnd, &p);
QPoint point(p.x, p.y);
point /= this->devicePixelRatio();
auto x = point.x();
auto y = point.y();
if (this->hasCustomWindowFrame()) if (this->hasCustomWindowFrame())
{ {
*result = 0; *result = 0;
bool resizeWidth = minimumWidth() != maximumWidth(); bool resizeWidth =
bool resizeHeight = minimumHeight() != maximumHeight(); minimumWidth() != maximumWidth() && !this->isMaximized();
bool resizeHeight =
minimumHeight() != maximumHeight() && !this->isMaximized();
if (resizeWidth) if (resizeWidth)
{ {
// left border // left border
if (x < winrect.left + border_width) if (x < rect.left() + borderWidth)
{ {
*result = HTLEFT; *result = HTLEFT;
} }
// right border // right border
if (x >= winrect.right - border_width) if (x >= rect.right() - borderWidth)
{ {
*result = HTRIGHT; *result = HTRIGHT;
} }
@ -1056,12 +1191,12 @@ bool BaseWindow::handleNCHITTEST(MSG *msg, long *result)
if (resizeHeight) if (resizeHeight)
{ {
// bottom border // bottom border
if (y >= winrect.bottom - border_width) if (y >= rect.bottom() - borderWidth)
{ {
*result = HTBOTTOM; *result = HTBOTTOM;
} }
// top border // top border
if (y < winrect.top + border_width) if (y < rect.top() + borderWidth)
{ {
*result = HTTOP; *result = HTTOP;
} }
@ -1069,26 +1204,26 @@ bool BaseWindow::handleNCHITTEST(MSG *msg, long *result)
if (resizeWidth && resizeHeight) if (resizeWidth && resizeHeight)
{ {
// bottom left corner // bottom left corner
if (x >= winrect.left && x < winrect.left + border_width && if (x >= rect.left() && x < rect.left() + borderWidth &&
y < winrect.bottom && y >= winrect.bottom - border_width) y < rect.bottom() && y >= rect.bottom() - borderWidth)
{ {
*result = HTBOTTOMLEFT; *result = HTBOTTOMLEFT;
} }
// bottom right corner // bottom right corner
if (x < winrect.right && x >= winrect.right - border_width && if (x < rect.right() && x >= rect.right() - borderWidth &&
y < winrect.bottom && y >= winrect.bottom - border_width) y < rect.bottom() && y >= rect.bottom() - borderWidth)
{ {
*result = HTBOTTOMRIGHT; *result = HTBOTTOMRIGHT;
} }
// top left corner // top left corner
if (x >= winrect.left && x < winrect.left + border_width && if (x >= rect.left() && x < rect.left() + borderWidth &&
y >= winrect.top && y < winrect.top + border_width) y >= rect.top() && y < rect.top() + borderWidth)
{ {
*result = HTTOPLEFT; *result = HTTOPLEFT;
} }
// top right corner // top right corner
if (x < winrect.right && x >= winrect.right - border_width && if (x < rect.right() && x >= rect.right() - borderWidth &&
y >= winrect.top && y < winrect.top + border_width) y >= rect.top() && y < rect.top() + borderWidth)
{ {
*result = HTTOPRIGHT; *result = HTTOPRIGHT;
} }

View file

@ -75,7 +75,6 @@ public:
bool applyLastBoundsCheck(); bool applyLastBoundsCheck();
float scale() const override; float scale() const override;
float qtFontScale() const;
/// @returns true if the window is the top-most window. /// @returns true if the window is the top-most window.
/// Either #setTopMost was called or the `TopMost` flag is set which overrides this /// Either #setTopMost was called or the `TopMost` flag is set which overrides this
@ -132,7 +131,6 @@ private:
void drawCustomWindowFrame(QPainter &painter); void drawCustomWindowFrame(QPainter &painter);
void onFocusLost(); void onFocusLost();
bool handleDPICHANGED(MSG *msg);
bool handleSHOWWINDOW(MSG *msg); bool handleSHOWWINDOW(MSG *msg);
bool handleSIZE(MSG *msg); bool handleSIZE(MSG *msg);
bool handleMOVE(MSG *msg); bool handleMOVE(MSG *msg);
@ -149,8 +147,6 @@ private:
bool frameless_; bool frameless_;
bool shown_ = false; bool shown_ = false;
FlagsEnum<Flags> flags_; FlagsEnum<Flags> flags_;
float nativeScale_ = 1;
bool isResizeFixing_ = false;
bool isTopMost_ = false; bool isTopMost_ = false;
struct { struct {
@ -168,6 +164,7 @@ private:
widgets::BoundsChecking lastBoundsCheckMode_ = widgets::BoundsChecking::Off; widgets::BoundsChecking lastBoundsCheckMode_ = widgets::BoundsChecking::Off;
#ifdef USEWINSDK #ifdef USEWINSDK
void updateRealSize();
/// @brief Returns the HWND of this window if it has one /// @brief Returns the HWND of this window if it has one
/// ///
/// A QWidget only has an HWND if it has been created. Before that, /// A QWidget only has an HWND if it has been created. Before that,
@ -193,6 +190,10 @@ private:
QTimer useNextBounds_; QTimer useNextBounds_;
bool isNotMinimizedOrMaximized_{}; bool isNotMinimizedOrMaximized_{};
bool lastEventWasNcMouseMove_ = false; bool lastEventWasNcMouseMove_ = false;
/// The real bounds of the window as returned by
/// GetWindowRect. Used for drawing.
QRect realBounds_;
bool isMaximized_ = false;
#endif #endif
pajlada::Signals::SignalHolder connections_; pajlada::Signals::SignalHolder connections_;

View file

@ -88,23 +88,10 @@ void Label::paintEvent(QPaintEvent *)
{ {
QPainter painter(this); QPainter painter(this);
qreal deviceDpi =
#ifdef Q_OS_WIN
this->devicePixelRatioF();
#else
1.0;
#endif
QFontMetrics metrics = getIApp()->getFonts()->getFontMetrics( QFontMetrics metrics = getIApp()->getFonts()->getFontMetrics(
this->getFontStyle(), this->getFontStyle(), this->scale());
this->scale() * 96.f / painter.setFont(
std::max<float>( getIApp()->getFonts()->getFont(this->getFontStyle(), this->scale()));
0.01F, static_cast<float>(this->logicalDpiX() * deviceDpi)));
painter.setFont(getIApp()->getFonts()->getFont(
this->getFontStyle(),
this->scale() * 96.f /
std::max<float>(
0.02F, static_cast<float>(this->logicalDpiX() * deviceDpi))));
int offset = this->getOffset(); int offset = this->getOffset();

View file

@ -86,6 +86,7 @@ bool TooltipEntryWidget::refreshPixmap()
this->attemptRefresh_ = true; this->attemptRefresh_ = true;
return false; return false;
} }
pixmap->setDevicePixelRatio(this->devicePixelRatio());
if (this->customImgWidth_ > 0 || this->customImgHeight_ > 0) if (this->customImgWidth_ > 0 || this->customImgHeight_ > 0)
{ {

View file

@ -47,7 +47,10 @@ SettingsDialog::SettingsDialog(QWidget *parent)
this->resize(915, 600); this->resize(915, 600);
this->themeChangedEvent(); this->themeChangedEvent();
this->scaleChangedEvent(this->scale()); QFile styleFile(":/qss/settings.qss");
styleFile.open(QFile::ReadOnly);
QString stylesheet = QString::fromUtf8(styleFile.readAll());
this->setStyleSheet(stylesheet);
this->initUi(); this->initUi();
this->addTabs(); this->addTabs();
@ -396,25 +399,19 @@ void SettingsDialog::refresh()
void SettingsDialog::scaleChangedEvent(float newDpi) void SettingsDialog::scaleChangedEvent(float newDpi)
{ {
QFile file(":/qss/settings.qss"); assert(newDpi == 1.F &&
file.open(QFile::ReadOnly); "Scaling is disabled for the settings dialog - its scale should "
QString styleSheet = QLatin1String(file.readAll()); "always be 1");
styleSheet.replace("<font-size>", QString::number(int(14 * newDpi)));
styleSheet.replace("<checkbox-size>", QString::number(int(14 * newDpi)));
for (SettingsDialogTab *tab : this->tabs_) for (SettingsDialogTab *tab : this->tabs_)
{ {
tab->setFixedHeight(int(30 * newDpi)); tab->setFixedHeight(30);
} }
this->setStyleSheet(styleSheet);
if (this->ui_.tabContainerContainer) if (this->ui_.tabContainerContainer)
{ {
this->ui_.tabContainerContainer->setFixedWidth(int(150 * newDpi)); this->ui_.tabContainerContainer->setFixedWidth(150);
} }
this->dpi_ = newDpi;
} }
void SettingsDialog::themeChangedEvent() void SettingsDialog::themeChangedEvent()

View file

@ -8,26 +8,44 @@
#include <QPainter> #include <QPainter>
#include <QScreen> #include <QScreen>
namespace chatterino {
namespace { namespace {
// returns a new resized image or the old one if the size didn't change QSizeF deviceIndependentSize(const QPixmap &pixmap)
auto resizePixmap(const QPixmap &current, const QPixmap resized, {
const QSize &size) -> QPixmap #if QT_VERSION < QT_VERSION_CHECK(6, 2, 0)
return QSizeF(pixmap.width(), pixmap.height()) / pixmap.devicePixelRatio();
#else
return pixmap.deviceIndependentSize();
#endif
}
/**
* Resizes a pixmap to a desired size.
* Does nothing if the target pixmap is already sized correctly.
*
* @param target The target pixmap.
* @param source The unscaled pixmap.
* @param size The desired device independent size.
* @param dpr The device pixel ratio of the target area. The size of the target in pixels will be `size * dpr`.
*/
void resizePixmap(QPixmap &target, const QPixmap &source, const QSize &size,
qreal dpr)
{
if (deviceIndependentSize(target) == size)
{ {
if (resized.size() == size) return;
{
return resized;
}
else
{
return current.scaled(size, Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
}
} }
QPixmap resized = source;
resized.setDevicePixelRatio(dpr);
target = resized.scaled(size * dpr, Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
}
} // namespace } // namespace
namespace chatterino {
Button::Button(BaseWidget *parent) Button::Button(BaseWidget *parent)
: BaseWidget(parent) : BaseWidget(parent)
{ {
@ -47,6 +65,12 @@ void Button::setMouseEffectColor(std::optional<QColor> color)
void Button::setPixmap(const QPixmap &_pixmap) void Button::setPixmap(const QPixmap &_pixmap)
{ {
// Avoid updates if the pixmap didn't change
if (_pixmap.cacheKey() == this->pixmap_.cacheKey())
{
return;
}
this->pixmap_ = _pixmap; this->pixmap_ = _pixmap;
this->resizedPixmap_ = {}; this->resizedPixmap_ = {};
this->update(); this->update();
@ -158,8 +182,8 @@ void Button::paintButton(QPainter &painter)
QRect rect = this->rect(); QRect rect = this->rect();
this->resizedPixmap_ = resizePixmap(this->resizedPixmap_, this->pixmap_, rect.size(),
resizePixmap(this->pixmap_, this->resizedPixmap_, rect.size()); this->devicePixelRatio());
int margin = this->height() < 22 * this->scale() ? 3 : 6; int margin = this->height() < 22 * this->scale() ? 3 : 6;

View file

@ -615,7 +615,7 @@ void ChannelView::scaleChangedEvent(float scale)
if (this->goToBottom_) if (this->goToBottom_)
{ {
auto factor = this->qtFontScale(); auto factor = this->scale();
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
factor = scale * 80.F / factor = scale * 80.F /
std::max<float>( std::max<float>(
@ -703,8 +703,10 @@ void ChannelView::layoutVisibleMessages(
{ {
const auto &message = messages[i]; const auto &message = messages[i];
redrawRequired |= message->layout(layoutWidth, this->scale(), flags, redrawRequired |= message->layout(
this->bufferInvalidationQueued_); layoutWidth, this->scale(),
this->scale() * static_cast<float>(this->devicePixelRatio()),
flags, this->bufferInvalidationQueued_);
y += message->getHeight(); y += message->getHeight();
} }
@ -738,7 +740,10 @@ void ChannelView::updateScrollbar(
{ {
auto *message = messages[i].get(); auto *message = messages[i].get();
message->layout(layoutWidth, this->scale(), flags, false); message->layout(
layoutWidth, this->scale(),
this->scale() * static_cast<float>(this->devicePixelRatio()), flags,
false);
h -= message->getHeight(); h -= message->getHeight();
@ -1720,9 +1725,11 @@ void ChannelView::wheelEvent(QWheelEvent *event)
} }
else else
{ {
snapshot[i - 1]->layout(this->getLayoutWidth(), snapshot[i - 1]->layout(
this->scale(), this->getFlags(), this->getLayoutWidth(), this->scale(),
false); this->scale() *
static_cast<float>(this->devicePixelRatio()),
this->getFlags(), false);
scrollFactor = 1; scrollFactor = 1;
currentScrollLeft = snapshot[i - 1]->getHeight(); currentScrollLeft = snapshot[i - 1]->getHeight();
} }
@ -1755,9 +1762,11 @@ void ChannelView::wheelEvent(QWheelEvent *event)
} }
else else
{ {
snapshot[i + 1]->layout(this->getLayoutWidth(), snapshot[i + 1]->layout(
this->scale(), this->getFlags(), this->getLayoutWidth(), this->scale(),
false); this->scale() *
static_cast<float>(this->devicePixelRatio()),
this->getFlags(), false);
scrollFactor = 1; scrollFactor = 1;
currentScrollLeft = snapshot[i + 1]->getHeight(); currentScrollLeft = snapshot[i + 1]->getHeight();

View file

@ -27,15 +27,6 @@
namespace chatterino { namespace chatterino {
namespace { namespace {
qreal deviceDpi(QWidget *widget)
{
#ifdef Q_OS_WIN
return widget->devicePixelRatioF();
#else
return 1.0;
#endif
}
// Translates the given rectangle by an amount in the direction to appear like the tab is selected. // Translates the given rectangle by an amount in the direction to appear like the tab is selected.
// For example, if location is Top, the rectangle will be translated in the negative Y direction, // For example, if location is Top, the rectangle will be translated in the negative Y direction,
// or "up" on the screen, by amount. // or "up" on the screen, by amount.
@ -196,8 +187,8 @@ int NotebookTab::normalTabWidth()
float scale = this->scale(); float scale = this->scale();
int width; int width;
auto metrics = getIApp()->getFonts()->getFontMetrics( QFontMetrics metrics =
FontStyle::UiTabs, float(qreal(this->scale()) * deviceDpi(this))); getIApp()->getFonts()->getFontMetrics(FontStyle::UiTabs, scale);
if (this->hasXButton()) if (this->hasXButton())
{ {
@ -439,11 +430,9 @@ void NotebookTab::paintEvent(QPaintEvent *)
QPainter painter(this); QPainter painter(this);
float scale = this->scale(); float scale = this->scale();
auto div = std::max<float>(0.01f, this->logicalDpiX() * deviceDpi(this)); painter.setFont(app->getFonts()->getFont(FontStyle::UiTabs, scale));
painter.setFont(
getIApp()->getFonts()->getFont(FontStyle::UiTabs, scale * 96.f / div));
QFontMetrics metrics = QFontMetrics metrics =
app->getFonts()->getFontMetrics(FontStyle::UiTabs, scale * 96.f / div); app->getFonts()->getFontMetrics(FontStyle::UiTabs, scale);
int height = int(scale * NOTEBOOK_TAB_HEIGHT); int height = int(scale * NOTEBOOK_TAB_HEIGHT);

View file

@ -63,7 +63,7 @@ public:
builder.append( builder.append(
std::make_unique<TextElement>(text, MessageElementFlag::Text)); std::make_unique<TextElement>(text, MessageElementFlag::Text));
this->layout = std::make_unique<MessageLayout>(builder.release()); this->layout = std::make_unique<MessageLayout>(builder.release());
this->layout->layout(WIDTH, 1, MessageElementFlag::Text, false); this->layout->layout(WIDTH, 1, 1, MessageElementFlag::Text, false);
} }
MockApplication mockApplication; MockApplication mockApplication;