mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-13 19:49:51 +01:00
Use Qt's High-DPI scaling on Windows (#4868)
This commit is contained in:
parent
8202cd0d99
commit
febcf464fe
|
@ -3,8 +3,10 @@
|
|||
## Unversioned
|
||||
|
||||
- Major: Release plugins alpha. (#5288)
|
||||
- Major: Improve high-DPI support on Windows. (#4868)
|
||||
- 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)
|
||||
- Dev: Use Qt's high DPI scaling. (#4868)
|
||||
- Dev: Add doxygen build target. (#5377)
|
||||
- Dev: Make printing of strings in tests easier. (#5379)
|
||||
- Dev: Refactor and document `Scrollbar`. (#5334)
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
* {
|
||||
font-size: <font-size>px;
|
||||
font-size: 14px;
|
||||
font-family: "Segoe UI";
|
||||
}
|
||||
|
||||
QCheckBox::indicator {
|
||||
width: <checkbox-size>px;
|
||||
height: <checkbox-size>px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
chatterino--ComboBox {
|
||||
|
|
|
@ -86,10 +86,6 @@ namespace {
|
|||
QApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
|
||||
#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"));
|
||||
|
||||
#ifndef Q_OS_MAC
|
||||
|
|
|
@ -26,11 +26,6 @@ using namespace chatterino;
|
|||
|
||||
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);
|
||||
|
||||
QCoreApplication::setApplicationName("chatterino");
|
||||
|
|
|
@ -155,8 +155,8 @@ void EmoteElement::addToContainer(MessageLayoutContainer &container,
|
|||
{
|
||||
if (flags.has(MessageElementFlag::EmoteImages))
|
||||
{
|
||||
auto image =
|
||||
this->emote_->images.getImageOrLoaded(container.getScale());
|
||||
auto image = this->emote_->images.getImageOrLoaded(
|
||||
container.getImageScale());
|
||||
if (image->isEmpty())
|
||||
{
|
||||
return;
|
||||
|
@ -210,7 +210,7 @@ void LayeredEmoteElement::addToContainer(MessageLayoutContainer &container,
|
|||
{
|
||||
if (flags.has(MessageElementFlag::EmoteImages))
|
||||
{
|
||||
auto images = this->getLoadedImages(container.getScale());
|
||||
auto images = this->getLoadedImages(container.getImageScale());
|
||||
if (images.empty())
|
||||
{
|
||||
return;
|
||||
|
@ -364,7 +364,7 @@ void BadgeElement::addToContainer(MessageLayoutContainer &container,
|
|||
if (flags.hasAny(this->getFlags()))
|
||||
{
|
||||
auto image =
|
||||
this->emote_->images.getImageOrLoaded(container.getScale());
|
||||
this->emote_->images.getImageOrLoaded(container.getImageScale());
|
||||
if (image->isEmpty())
|
||||
{
|
||||
return;
|
||||
|
@ -798,7 +798,7 @@ void ScalingImageElement::addToContainer(MessageLayoutContainer &container,
|
|||
if (flags.hasAny(this->getFlags()))
|
||||
{
|
||||
const auto &image =
|
||||
this->images_.getImageOrLoaded(container.getScale());
|
||||
this->images_.getImageOrLoaded(container.getImageScale());
|
||||
if (image->isEmpty())
|
||||
{
|
||||
return;
|
||||
|
|
|
@ -74,7 +74,8 @@ int MessageLayout::getWidth() const
|
|||
|
||||
// Layout
|
||||
// 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)
|
||||
{
|
||||
// BenchmarkGuard benchmark("MessageLayout::layout()");
|
||||
|
@ -106,6 +107,8 @@ bool MessageLayout::layout(int width, float scale, MessageElementFlags flags,
|
|||
// check if dpi changed
|
||||
layoutRequired |= this->scale_ != scale;
|
||||
this->scale_ = scale;
|
||||
layoutRequired |= this->imageScale_ != imageScale;
|
||||
this->imageScale_ = imageScale;
|
||||
|
||||
if (!layoutRequired)
|
||||
{
|
||||
|
@ -148,7 +151,8 @@ void MessageLayout::actuallyLayout(int width, MessageElementFlags flags)
|
|||
bool hideSimilar = getSettings()->hideSimilar;
|
||||
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)
|
||||
{
|
||||
|
@ -288,16 +292,11 @@ QPixmap *MessageLayout::ensureBuffer(QPainter &painter, int width)
|
|||
}
|
||||
|
||||
// Create new buffer
|
||||
#if defined(Q_OS_MACOS) || defined(Q_OS_LINUX)
|
||||
this->buffer_ = std::make_unique<QPixmap>(
|
||||
int(width * painter.device()->devicePixelRatioF()),
|
||||
int(this->container_.getHeight() *
|
||||
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;
|
||||
DebugCount::increase("message drawing buffers");
|
||||
|
|
|
@ -56,8 +56,8 @@ public:
|
|||
|
||||
MessageLayoutFlags flags;
|
||||
|
||||
bool layout(int width, float scale_, MessageElementFlags flags,
|
||||
bool shouldInvalidateBuffer);
|
||||
bool layout(int width, float scale_, float imageScale,
|
||||
MessageElementFlags flags, bool shouldInvalidateBuffer);
|
||||
|
||||
// Painting
|
||||
MessagePaintResult paint(const MessagePaintContext &ctx);
|
||||
|
@ -128,6 +128,7 @@ private:
|
|||
int currentLayoutWidth_ = -1;
|
||||
int layoutState_ = -1;
|
||||
float scale_ = -1;
|
||||
float imageScale_ = -1.F;
|
||||
MessageElementFlags currentWordFlags_;
|
||||
|
||||
#ifdef FOURTF
|
||||
|
|
|
@ -30,7 +30,7 @@ constexpr const QMargins MARGIN{8, 4, 8, 4};
|
|||
namespace chatterino {
|
||||
|
||||
void MessageLayoutContainer::beginLayout(int width, float scale,
|
||||
MessageFlags flags)
|
||||
float imageScale, MessageFlags flags)
|
||||
{
|
||||
this->elements_.clear();
|
||||
this->lines_.clear();
|
||||
|
@ -45,6 +45,7 @@ void MessageLayoutContainer::beginLayout(int width, float scale,
|
|||
this->width_ = width;
|
||||
this->height_ = 0;
|
||||
this->scale_ = scale;
|
||||
this->imageScale_ = imageScale;
|
||||
this->flags_ = flags;
|
||||
auto mediumFontMetrics =
|
||||
getIApp()->getFonts()->getFontMetrics(FontStyle::ChatMedium, scale);
|
||||
|
@ -526,6 +527,11 @@ float MessageLayoutContainer::getScale() const
|
|||
return this->scale_;
|
||||
}
|
||||
|
||||
float MessageLayoutContainer::getImageScale() const
|
||||
{
|
||||
return this->imageScale_;
|
||||
}
|
||||
|
||||
bool MessageLayoutContainer::isCollapsed() const
|
||||
{
|
||||
return this->isCollapsed_;
|
||||
|
|
|
@ -32,7 +32,8 @@ struct MessageLayoutContainer {
|
|||
* This will reset all line calculations, and will be considered incomplete
|
||||
* 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
|
||||
|
@ -146,6 +147,11 @@ struct MessageLayoutContainer {
|
|||
*/
|
||||
float getScale() const;
|
||||
|
||||
/**
|
||||
* Returns the image scale
|
||||
*/
|
||||
float getImageScale() const;
|
||||
|
||||
/**
|
||||
* Returns true if this message is collapsed
|
||||
*/
|
||||
|
@ -270,6 +276,10 @@ private:
|
|||
|
||||
// variables
|
||||
float scale_ = 1.F;
|
||||
/**
|
||||
* Scale factor for images
|
||||
*/
|
||||
float imageScale_ = 1.F;
|
||||
int width_ = 0;
|
||||
MessageFlags flags_{};
|
||||
/**
|
||||
|
|
|
@ -270,20 +270,22 @@ void AttachedWindow::updateWindowRect(void *_attachedPtr)
|
|||
}
|
||||
|
||||
float scale = 1.f;
|
||||
float ourScale = 1.F;
|
||||
if (auto dpi = getWindowDpi(attached))
|
||||
{
|
||||
scale = *dpi / 96.f;
|
||||
ourScale = scale / this->devicePixelRatio();
|
||||
|
||||
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)
|
||||
{
|
||||
this->ui_.split->setFixedWidth(int(this->width_ * scale));
|
||||
this->ui_.split->setFixedWidth(int(this->width_ * ourScale));
|
||||
|
||||
// offset
|
||||
int o = this->fullscreen_ ? 0 : 8;
|
||||
|
|
|
@ -120,19 +120,6 @@ void BaseWidget::setScaleIndependantHeight(int 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)
|
||||
{
|
||||
if (event->added())
|
||||
|
|
|
@ -34,8 +34,6 @@ public:
|
|||
void setScaleIndependantWidth(int value);
|
||||
void setScaleIndependantHeight(int value);
|
||||
|
||||
float qtFontScale() const;
|
||||
|
||||
protected:
|
||||
void childEvent(QChildEvent *) override;
|
||||
void showEvent(QShowEvent *) override;
|
||||
|
|
|
@ -29,12 +29,163 @@
|
|||
# pragma comment(lib, "Dwmapi.lib")
|
||||
|
||||
# include <QHBoxLayout>
|
||||
|
||||
# define WM_DPICHANGED 0x02E0
|
||||
# include <QOperatingSystemVersion>
|
||||
#endif
|
||||
|
||||
#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 {
|
||||
|
||||
BaseWindow::BaseWindow(FlagsEnum<Flags> _flags, QWidget *parent)
|
||||
|
@ -117,23 +268,17 @@ float BaseWindow::scale() const
|
|||
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()
|
||||
{
|
||||
#ifdef USEWINSDK
|
||||
if (this->hasCustomWindowFrame())
|
||||
{
|
||||
// CUSTOM WINDOW FRAME
|
||||
QVBoxLayout *layout = new QVBoxLayout();
|
||||
auto *layout = new QVBoxLayout(this);
|
||||
this->ui_.windowLayout = layout;
|
||||
layout->setContentsMargins(1, 1, 1, 1);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->setSpacing(0);
|
||||
this->setLayout(layout);
|
||||
{
|
||||
|
||||
if (!this->frameless_)
|
||||
{
|
||||
QHBoxLayout *buttonLayout = this->ui_.titlebarBox =
|
||||
|
@ -148,64 +293,55 @@ void BaseWindow::init()
|
|||
title->setText(text);
|
||||
});
|
||||
|
||||
QSizePolicy policy(QSizePolicy::Ignored,
|
||||
QSizePolicy::Preferred);
|
||||
QSizePolicy policy(QSizePolicy::Ignored, QSizePolicy::Preferred);
|
||||
policy.setHorizontalStretch(1);
|
||||
title->setSizePolicy(policy);
|
||||
buttonLayout->addWidget(title);
|
||||
this->ui_.titleLabel = title;
|
||||
|
||||
// buttons
|
||||
TitleBarButton *_minButton = new TitleBarButton;
|
||||
_minButton->setButtonStyle(TitleBarButtonStyle::Minimize);
|
||||
TitleBarButton *_maxButton = new TitleBarButton;
|
||||
_maxButton->setButtonStyle(TitleBarButtonStyle::Maximize);
|
||||
TitleBarButton *_exitButton = new TitleBarButton;
|
||||
_exitButton->setButtonStyle(TitleBarButtonStyle::Close);
|
||||
auto *minButton = new TitleBarButton;
|
||||
minButton->setButtonStyle(TitleBarButtonStyle::Minimize);
|
||||
auto *maxButton = new TitleBarButton;
|
||||
maxButton->setButtonStyle(TitleBarButtonStyle::Maximize);
|
||||
auto *exitButton = new TitleBarButton;
|
||||
exitButton->setButtonStyle(TitleBarButtonStyle::Close);
|
||||
|
||||
QObject::connect(_minButton, &TitleBarButton::leftClicked, this,
|
||||
QObject::connect(minButton, &TitleBarButton::leftClicked, this,
|
||||
[this] {
|
||||
this->setWindowState(Qt::WindowMinimized |
|
||||
this->windowState());
|
||||
});
|
||||
QObject::connect(_maxButton, &TitleBarButton::leftClicked, this,
|
||||
[this, _maxButton] {
|
||||
this->setWindowState(
|
||||
_maxButton->getButtonStyle() !=
|
||||
QObject::connect(
|
||||
maxButton, &TitleBarButton::leftClicked, this,
|
||||
[this, maxButton] {
|
||||
this->setWindowState(maxButton->getButtonStyle() !=
|
||||
TitleBarButtonStyle::Maximize
|
||||
? Qt::WindowActive
|
||||
: Qt::WindowMaximized);
|
||||
});
|
||||
QObject::connect(_exitButton, &TitleBarButton::leftClicked,
|
||||
this, [this] {
|
||||
QObject::connect(exitButton, &TitleBarButton::leftClicked, this,
|
||||
[this] {
|
||||
this->close();
|
||||
});
|
||||
|
||||
this->ui_.titlebarButtons = new TitleBarButtons(
|
||||
this, _minButton, _maxButton, _exitButton);
|
||||
this->ui_.titlebarButtons =
|
||||
new TitleBarButtons(this, minButton, maxButton, exitButton);
|
||||
|
||||
this->ui_.buttons.push_back(_minButton);
|
||||
this->ui_.buttons.push_back(_maxButton);
|
||||
this->ui_.buttons.push_back(_exitButton);
|
||||
this->ui_.buttons.push_back(minButton);
|
||||
this->ui_.buttons.push_back(maxButton);
|
||||
this->ui_.buttons.push_back(exitButton);
|
||||
|
||||
// buttonLayout->addStretch(1);
|
||||
buttonLayout->addWidget(_minButton);
|
||||
buttonLayout->addWidget(_maxButton);
|
||||
buttonLayout->addWidget(_exitButton);
|
||||
buttonLayout->addWidget(minButton);
|
||||
buttonLayout->addWidget(maxButton);
|
||||
buttonLayout->addWidget(exitButton);
|
||||
buttonLayout->setSpacing(0);
|
||||
}
|
||||
}
|
||||
|
||||
this->ui_.layoutBase = new BaseWidget(this);
|
||||
this->ui_.layoutBase->setContentsMargins(1, 0, 1, 1);
|
||||
layout->addWidget(this->ui_.layoutBase);
|
||||
}
|
||||
|
||||
// DPI
|
||||
// auto dpi = getWindowDpi(this->safeHWND());
|
||||
|
||||
// if (dpi) {
|
||||
// this->scale = dpi.value() / 96.f;
|
||||
// }
|
||||
#endif
|
||||
|
||||
// TopMost flag overrides setting
|
||||
|
@ -571,29 +707,8 @@ void BaseWindow::resizeEvent(QResizeEvent *)
|
|||
}
|
||||
|
||||
#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->updateRealSize();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -655,10 +770,6 @@ bool BaseWindow::nativeEvent(const QByteArray &eventType, void *message,
|
|||
|
||||
switch (msg->message)
|
||||
{
|
||||
case WM_DPICHANGED:
|
||||
returnValue = this->handleDPICHANGED(msg);
|
||||
break;
|
||||
|
||||
case WM_SHOWWINDOW:
|
||||
returnValue = this->handleSHOWWINDOW(msg);
|
||||
break;
|
||||
|
@ -697,12 +808,15 @@ bool BaseWindow::nativeEvent(const QByteArray &eventType, void *message,
|
|||
{
|
||||
*result = 0;
|
||||
returnValue = true;
|
||||
long x = GET_X_LPARAM(msg->lParam);
|
||||
long y = GET_Y_LPARAM(msg->lParam);
|
||||
|
||||
RECT winrect;
|
||||
GetWindowRect(msg->hwnd, &winrect);
|
||||
QPoint globalPos(x, y);
|
||||
POINT p{GET_X_LPARAM(msg->lParam), GET_Y_LPARAM(msg->lParam)};
|
||||
ScreenToClient(msg->hwnd, &p);
|
||||
|
||||
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->lastEventWasNcMouseMove_ = true;
|
||||
}
|
||||
|
@ -748,12 +862,14 @@ bool BaseWindow::nativeEvent(const QByteArray &eventType, void *message,
|
|||
*result = 0;
|
||||
|
||||
auto ht = msg->wParam;
|
||||
long x = GET_X_LPARAM(msg->lParam);
|
||||
long y = GET_Y_LPARAM(msg->lParam);
|
||||
|
||||
RECT winrect;
|
||||
GetWindowRect(msg->hwnd, &winrect);
|
||||
QPoint globalPos(x, y);
|
||||
POINT p{GET_X_LPARAM(msg->lParam), GET_Y_LPARAM(msg->lParam)};
|
||||
ScreenToClient(msg->hwnd, &p);
|
||||
|
||||
QPoint globalPos(p.x, p.y);
|
||||
globalPos /= this->devicePixelRatio();
|
||||
globalPos = this->mapToGlobal(globalPos);
|
||||
|
||||
if (msg->message == WM_NCLBUTTONDOWN)
|
||||
{
|
||||
this->ui_.titlebarButtons->mousePress(ht, globalPos);
|
||||
|
@ -784,7 +900,7 @@ void BaseWindow::scaleChangedEvent(float scale)
|
|||
#endif
|
||||
|
||||
this->setFont(
|
||||
getIApp()->getFonts()->getFont(FontStyle::UiTabs, this->qtFontScale()));
|
||||
getIApp()->getFonts()->getFont(FontStyle::UiTabs, this->scale()));
|
||||
}
|
||||
|
||||
void BaseWindow::paintEvent(QPaintEvent *)
|
||||
|
@ -802,10 +918,9 @@ void BaseWindow::paintEvent(QPaintEvent *)
|
|||
|
||||
void BaseWindow::updateScale()
|
||||
{
|
||||
auto scale =
|
||||
this->nativeScale_ * (this->flags_.has(DisableCustomScaling)
|
||||
auto scale = this->flags_.has(DisableCustomScaling)
|
||||
? 1
|
||||
: getSettings()->getClampedUiScale());
|
||||
: getSettings()->getClampedUiScale();
|
||||
|
||||
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()
|
||||
{
|
||||
if (!this->shown_)
|
||||
|
@ -846,31 +977,25 @@ void BaseWindow::drawCustomWindowFrame(QPainter &painter)
|
|||
{
|
||||
QColor bg = this->overrideBackgroundColor_.value_or(
|
||||
this->theme->window.background);
|
||||
painter.fillRect(QRect(1, 2, this->width() - 2, this->height() - 3),
|
||||
bg);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool BaseWindow::handleDPICHANGED(MSG *msg)
|
||||
if (this->isMaximized_)
|
||||
{
|
||||
#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;
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -883,16 +1008,6 @@ bool BaseWindow::handleSHOWWINDOW(MSG *msg)
|
|||
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_)
|
||||
{
|
||||
this->shown_ = true;
|
||||
|
@ -906,14 +1021,12 @@ bool BaseWindow::handleSHOWWINDOW(MSG *msg)
|
|||
|
||||
if (!this->initalBounds_.isNull())
|
||||
{
|
||||
::SetWindowPos(msg->hwnd, nullptr, this->initalBounds_.x(),
|
||||
this->initalBounds_.y(), this->initalBounds_.width(),
|
||||
this->initalBounds_.height(),
|
||||
SWP_NOZORDER | SWP_NOACTIVATE);
|
||||
this->setGeometry(this->initalBounds_);
|
||||
this->currentBounds_ = this->initalBounds_;
|
||||
}
|
||||
|
||||
this->calcButtonsSizes();
|
||||
this->updateRealSize();
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -929,23 +1042,54 @@ bool BaseWindow::handleNCCALCSIZE(MSG *msg, long *result)
|
|||
#endif
|
||||
{
|
||||
#ifdef USEWINSDK
|
||||
if (this->hasCustomWindowFrame())
|
||||
if (!this->hasCustomWindowFrame())
|
||||
{
|
||||
if (msg->wParam == TRUE)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (msg->wParam != TRUE)
|
||||
{
|
||||
*result = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
auto *params = reinterpret_cast<NCCALCSIZE_PARAMS *>(msg->lParam);
|
||||
auto *r = ¶ms->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
|
||||
return false;
|
||||
#endif
|
||||
|
@ -962,28 +1106,11 @@ bool BaseWindow::handleSIZE(MSG *msg)
|
|||
}
|
||||
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;
|
||||
|
||||
if (this->isNotMinimizedOrMaximized_)
|
||||
{
|
||||
RECT rect;
|
||||
::GetWindowRect(msg->hwnd, &rect);
|
||||
this->currentBounds_ =
|
||||
QRect(QPoint(rect.left, rect.top),
|
||||
QPoint(rect.right - 1, rect.bottom - 1));
|
||||
this->currentBounds_ = this->geometry();
|
||||
}
|
||||
this->useNextBounds_.stop();
|
||||
|
||||
|
@ -993,6 +1120,12 @@ bool BaseWindow::handleSIZE(MSG *msg)
|
|||
// the minimize button, so we have to emulate it.
|
||||
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;
|
||||
|
@ -1006,11 +1139,7 @@ bool BaseWindow::handleMOVE(MSG *msg)
|
|||
#ifdef USEWINSDK
|
||||
if (this->isNotMinimizedOrMaximized_)
|
||||
{
|
||||
RECT rect;
|
||||
::GetWindowRect(msg->hwnd, &rect);
|
||||
this->nextBounds_ = QRect(QPoint(rect.left, rect.top),
|
||||
QPoint(rect.right - 1, rect.bottom - 1));
|
||||
|
||||
this->nextBounds_ = this->geometry();
|
||||
this->useNextBounds_.start(10);
|
||||
}
|
||||
#endif
|
||||
|
@ -1024,31 +1153,37 @@ bool BaseWindow::handleNCHITTEST(MSG *msg, long *result)
|
|||
#endif
|
||||
{
|
||||
#ifdef USEWINSDK
|
||||
const LONG border_width = 8; // in pixels
|
||||
RECT winrect;
|
||||
GetWindowRect(msg->hwnd, &winrect);
|
||||
const LONG borderWidth = 8; // in device independent pixels
|
||||
|
||||
long x = GET_X_LPARAM(msg->lParam);
|
||||
long y = GET_Y_LPARAM(msg->lParam);
|
||||
auto rect = this->rect();
|
||||
|
||||
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())
|
||||
{
|
||||
*result = 0;
|
||||
|
||||
bool resizeWidth = minimumWidth() != maximumWidth();
|
||||
bool resizeHeight = minimumHeight() != maximumHeight();
|
||||
bool resizeWidth =
|
||||
minimumWidth() != maximumWidth() && !this->isMaximized();
|
||||
bool resizeHeight =
|
||||
minimumHeight() != maximumHeight() && !this->isMaximized();
|
||||
|
||||
if (resizeWidth)
|
||||
{
|
||||
// left border
|
||||
if (x < winrect.left + border_width)
|
||||
if (x < rect.left() + borderWidth)
|
||||
{
|
||||
*result = HTLEFT;
|
||||
}
|
||||
// right border
|
||||
if (x >= winrect.right - border_width)
|
||||
if (x >= rect.right() - borderWidth)
|
||||
{
|
||||
*result = HTRIGHT;
|
||||
}
|
||||
|
@ -1056,12 +1191,12 @@ bool BaseWindow::handleNCHITTEST(MSG *msg, long *result)
|
|||
if (resizeHeight)
|
||||
{
|
||||
// bottom border
|
||||
if (y >= winrect.bottom - border_width)
|
||||
if (y >= rect.bottom() - borderWidth)
|
||||
{
|
||||
*result = HTBOTTOM;
|
||||
}
|
||||
// top border
|
||||
if (y < winrect.top + border_width)
|
||||
if (y < rect.top() + borderWidth)
|
||||
{
|
||||
*result = HTTOP;
|
||||
}
|
||||
|
@ -1069,26 +1204,26 @@ bool BaseWindow::handleNCHITTEST(MSG *msg, long *result)
|
|||
if (resizeWidth && resizeHeight)
|
||||
{
|
||||
// bottom left corner
|
||||
if (x >= winrect.left && x < winrect.left + border_width &&
|
||||
y < winrect.bottom && y >= winrect.bottom - border_width)
|
||||
if (x >= rect.left() && x < rect.left() + borderWidth &&
|
||||
y < rect.bottom() && y >= rect.bottom() - borderWidth)
|
||||
{
|
||||
*result = HTBOTTOMLEFT;
|
||||
}
|
||||
// bottom right corner
|
||||
if (x < winrect.right && x >= winrect.right - border_width &&
|
||||
y < winrect.bottom && y >= winrect.bottom - border_width)
|
||||
if (x < rect.right() && x >= rect.right() - borderWidth &&
|
||||
y < rect.bottom() && y >= rect.bottom() - borderWidth)
|
||||
{
|
||||
*result = HTBOTTOMRIGHT;
|
||||
}
|
||||
// top left corner
|
||||
if (x >= winrect.left && x < winrect.left + border_width &&
|
||||
y >= winrect.top && y < winrect.top + border_width)
|
||||
if (x >= rect.left() && x < rect.left() + borderWidth &&
|
||||
y >= rect.top() && y < rect.top() + borderWidth)
|
||||
{
|
||||
*result = HTTOPLEFT;
|
||||
}
|
||||
// top right corner
|
||||
if (x < winrect.right && x >= winrect.right - border_width &&
|
||||
y >= winrect.top && y < winrect.top + border_width)
|
||||
if (x < rect.right() && x >= rect.right() - borderWidth &&
|
||||
y >= rect.top() && y < rect.top() + borderWidth)
|
||||
{
|
||||
*result = HTTOPRIGHT;
|
||||
}
|
||||
|
|
|
@ -75,7 +75,6 @@ public:
|
|||
bool applyLastBoundsCheck();
|
||||
|
||||
float scale() const override;
|
||||
float qtFontScale() const;
|
||||
|
||||
/// @returns true if the window is the top-most window.
|
||||
/// Either #setTopMost was called or the `TopMost` flag is set which overrides this
|
||||
|
@ -132,7 +131,6 @@ private:
|
|||
void drawCustomWindowFrame(QPainter &painter);
|
||||
void onFocusLost();
|
||||
|
||||
bool handleDPICHANGED(MSG *msg);
|
||||
bool handleSHOWWINDOW(MSG *msg);
|
||||
bool handleSIZE(MSG *msg);
|
||||
bool handleMOVE(MSG *msg);
|
||||
|
@ -149,8 +147,6 @@ private:
|
|||
bool frameless_;
|
||||
bool shown_ = false;
|
||||
FlagsEnum<Flags> flags_;
|
||||
float nativeScale_ = 1;
|
||||
bool isResizeFixing_ = false;
|
||||
bool isTopMost_ = false;
|
||||
|
||||
struct {
|
||||
|
@ -168,6 +164,7 @@ private:
|
|||
widgets::BoundsChecking lastBoundsCheckMode_ = widgets::BoundsChecking::Off;
|
||||
|
||||
#ifdef USEWINSDK
|
||||
void updateRealSize();
|
||||
/// @brief Returns the HWND of this window if it has one
|
||||
///
|
||||
/// A QWidget only has an HWND if it has been created. Before that,
|
||||
|
@ -193,6 +190,10 @@ private:
|
|||
QTimer useNextBounds_;
|
||||
bool isNotMinimizedOrMaximized_{};
|
||||
bool lastEventWasNcMouseMove_ = false;
|
||||
/// The real bounds of the window as returned by
|
||||
/// GetWindowRect. Used for drawing.
|
||||
QRect realBounds_;
|
||||
bool isMaximized_ = false;
|
||||
#endif
|
||||
|
||||
pajlada::Signals::SignalHolder connections_;
|
||||
|
|
|
@ -88,23 +88,10 @@ void Label::paintEvent(QPaintEvent *)
|
|||
{
|
||||
QPainter painter(this);
|
||||
|
||||
qreal deviceDpi =
|
||||
#ifdef Q_OS_WIN
|
||||
this->devicePixelRatioF();
|
||||
#else
|
||||
1.0;
|
||||
#endif
|
||||
|
||||
QFontMetrics metrics = getIApp()->getFonts()->getFontMetrics(
|
||||
this->getFontStyle(),
|
||||
this->scale() * 96.f /
|
||||
std::max<float>(
|
||||
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))));
|
||||
this->getFontStyle(), this->scale());
|
||||
painter.setFont(
|
||||
getIApp()->getFonts()->getFont(this->getFontStyle(), this->scale()));
|
||||
|
||||
int offset = this->getOffset();
|
||||
|
||||
|
|
|
@ -86,6 +86,7 @@ bool TooltipEntryWidget::refreshPixmap()
|
|||
this->attemptRefresh_ = true;
|
||||
return false;
|
||||
}
|
||||
pixmap->setDevicePixelRatio(this->devicePixelRatio());
|
||||
|
||||
if (this->customImgWidth_ > 0 || this->customImgHeight_ > 0)
|
||||
{
|
||||
|
|
|
@ -47,7 +47,10 @@ SettingsDialog::SettingsDialog(QWidget *parent)
|
|||
|
||||
this->resize(915, 600);
|
||||
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->addTabs();
|
||||
|
@ -396,25 +399,19 @@ void SettingsDialog::refresh()
|
|||
|
||||
void SettingsDialog::scaleChangedEvent(float newDpi)
|
||||
{
|
||||
QFile file(":/qss/settings.qss");
|
||||
file.open(QFile::ReadOnly);
|
||||
QString styleSheet = QLatin1String(file.readAll());
|
||||
styleSheet.replace("<font-size>", QString::number(int(14 * newDpi)));
|
||||
styleSheet.replace("<checkbox-size>", QString::number(int(14 * newDpi)));
|
||||
assert(newDpi == 1.F &&
|
||||
"Scaling is disabled for the settings dialog - its scale should "
|
||||
"always be 1");
|
||||
|
||||
for (SettingsDialogTab *tab : this->tabs_)
|
||||
{
|
||||
tab->setFixedHeight(int(30 * newDpi));
|
||||
tab->setFixedHeight(30);
|
||||
}
|
||||
|
||||
this->setStyleSheet(styleSheet);
|
||||
|
||||
if (this->ui_.tabContainerContainer)
|
||||
{
|
||||
this->ui_.tabContainerContainer->setFixedWidth(int(150 * newDpi));
|
||||
this->ui_.tabContainerContainer->setFixedWidth(150);
|
||||
}
|
||||
|
||||
this->dpi_ = newDpi;
|
||||
}
|
||||
|
||||
void SettingsDialog::themeChangedEvent()
|
||||
|
|
|
@ -8,26 +8,44 @@
|
|||
#include <QPainter>
|
||||
#include <QScreen>
|
||||
|
||||
namespace chatterino {
|
||||
namespace {
|
||||
|
||||
// returns a new resized image or the old one if the size didn't change
|
||||
auto resizePixmap(const QPixmap ¤t, const QPixmap resized,
|
||||
const QSize &size) -> QPixmap
|
||||
QSizeF deviceIndependentSize(const QPixmap &pixmap)
|
||||
{
|
||||
if (resized.size() == size)
|
||||
{
|
||||
return resized;
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 2, 0)
|
||||
return QSizeF(pixmap.width(), pixmap.height()) / pixmap.devicePixelRatio();
|
||||
#else
|
||||
return pixmap.deviceIndependentSize();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
return current.scaled(size, Qt::IgnoreAspectRatio,
|
||||
if (deviceIndependentSize(target) == size)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QPixmap resized = source;
|
||||
resized.setDevicePixelRatio(dpr);
|
||||
target = resized.scaled(size * dpr, Qt::IgnoreAspectRatio,
|
||||
Qt::SmoothTransformation);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
Button::Button(BaseWidget *parent)
|
||||
: BaseWidget(parent)
|
||||
{
|
||||
|
@ -47,6 +65,12 @@ void Button::setMouseEffectColor(std::optional<QColor> color)
|
|||
|
||||
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->resizedPixmap_ = {};
|
||||
this->update();
|
||||
|
@ -158,8 +182,8 @@ void Button::paintButton(QPainter &painter)
|
|||
|
||||
QRect rect = this->rect();
|
||||
|
||||
this->resizedPixmap_ =
|
||||
resizePixmap(this->pixmap_, this->resizedPixmap_, rect.size());
|
||||
resizePixmap(this->resizedPixmap_, this->pixmap_, rect.size(),
|
||||
this->devicePixelRatio());
|
||||
|
||||
int margin = this->height() < 22 * this->scale() ? 3 : 6;
|
||||
|
||||
|
|
|
@ -615,7 +615,7 @@ void ChannelView::scaleChangedEvent(float scale)
|
|||
|
||||
if (this->goToBottom_)
|
||||
{
|
||||
auto factor = this->qtFontScale();
|
||||
auto factor = this->scale();
|
||||
#ifdef Q_OS_MACOS
|
||||
factor = scale * 80.F /
|
||||
std::max<float>(
|
||||
|
@ -703,8 +703,10 @@ void ChannelView::layoutVisibleMessages(
|
|||
{
|
||||
const auto &message = messages[i];
|
||||
|
||||
redrawRequired |= message->layout(layoutWidth, this->scale(), flags,
|
||||
this->bufferInvalidationQueued_);
|
||||
redrawRequired |= message->layout(
|
||||
layoutWidth, this->scale(),
|
||||
this->scale() * static_cast<float>(this->devicePixelRatio()),
|
||||
flags, this->bufferInvalidationQueued_);
|
||||
|
||||
y += message->getHeight();
|
||||
}
|
||||
|
@ -738,7 +740,10 @@ void ChannelView::updateScrollbar(
|
|||
{
|
||||
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();
|
||||
|
||||
|
@ -1720,9 +1725,11 @@ void ChannelView::wheelEvent(QWheelEvent *event)
|
|||
}
|
||||
else
|
||||
{
|
||||
snapshot[i - 1]->layout(this->getLayoutWidth(),
|
||||
this->scale(), this->getFlags(),
|
||||
false);
|
||||
snapshot[i - 1]->layout(
|
||||
this->getLayoutWidth(), this->scale(),
|
||||
this->scale() *
|
||||
static_cast<float>(this->devicePixelRatio()),
|
||||
this->getFlags(), false);
|
||||
scrollFactor = 1;
|
||||
currentScrollLeft = snapshot[i - 1]->getHeight();
|
||||
}
|
||||
|
@ -1755,9 +1762,11 @@ void ChannelView::wheelEvent(QWheelEvent *event)
|
|||
}
|
||||
else
|
||||
{
|
||||
snapshot[i + 1]->layout(this->getLayoutWidth(),
|
||||
this->scale(), this->getFlags(),
|
||||
false);
|
||||
snapshot[i + 1]->layout(
|
||||
this->getLayoutWidth(), this->scale(),
|
||||
this->scale() *
|
||||
static_cast<float>(this->devicePixelRatio()),
|
||||
this->getFlags(), false);
|
||||
|
||||
scrollFactor = 1;
|
||||
currentScrollLeft = snapshot[i + 1]->getHeight();
|
||||
|
|
|
@ -27,15 +27,6 @@
|
|||
|
||||
namespace chatterino {
|
||||
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.
|
||||
// For example, if location is Top, the rectangle will be translated in the negative Y direction,
|
||||
// or "up" on the screen, by amount.
|
||||
|
@ -196,8 +187,8 @@ int NotebookTab::normalTabWidth()
|
|||
float scale = this->scale();
|
||||
int width;
|
||||
|
||||
auto metrics = getIApp()->getFonts()->getFontMetrics(
|
||||
FontStyle::UiTabs, float(qreal(this->scale()) * deviceDpi(this)));
|
||||
QFontMetrics metrics =
|
||||
getIApp()->getFonts()->getFontMetrics(FontStyle::UiTabs, scale);
|
||||
|
||||
if (this->hasXButton())
|
||||
{
|
||||
|
@ -439,11 +430,9 @@ void NotebookTab::paintEvent(QPaintEvent *)
|
|||
QPainter painter(this);
|
||||
float scale = this->scale();
|
||||
|
||||
auto div = std::max<float>(0.01f, this->logicalDpiX() * deviceDpi(this));
|
||||
painter.setFont(
|
||||
getIApp()->getFonts()->getFont(FontStyle::UiTabs, scale * 96.f / div));
|
||||
painter.setFont(app->getFonts()->getFont(FontStyle::UiTabs, scale));
|
||||
QFontMetrics metrics =
|
||||
app->getFonts()->getFontMetrics(FontStyle::UiTabs, scale * 96.f / div);
|
||||
app->getFonts()->getFontMetrics(FontStyle::UiTabs, scale);
|
||||
|
||||
int height = int(scale * NOTEBOOK_TAB_HEIGHT);
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ public:
|
|||
builder.append(
|
||||
std::make_unique<TextElement>(text, MessageElementFlag::Text));
|
||||
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;
|
||||
|
|
Loading…
Reference in a new issue