fix: 150% and 200% zoom levels prevent the user from logging in

- TLDR: we were scaling twice, resulting in ginourmous pixel values

The long story:
- since Qt treats the various scale factors in a multiplicative way (see
https://www.qt.io/blog/2016/01/26/high-dpi-support-in-qt-5-6 for
explanation) and there's no way to get the screen's baseline scale
factor programatically, we also have to export `QT_SCREEN_SCALE_FACTORS`
to something that's not equal to `0` or `1` to force the monitor scale
factor to `100%` and then compensate for it when exporting our own scale
value using `QT_SCALE_FACTOR`
- make the UI slider values go in `25%` steps, allowing for more fine
grained control; with `100%` we fallback to the Qt's native handling of
highdpi
- raised the maximum to `300%` since on highres displays, one wouldn't
be able to go over the implicit maximum of `200%` (due to the internal
scaling being 2x)
- scale our main window's minimum width/height so that we don't overflow
the monitor's available space
- modernize the `ConfirmAppRestartModal` to use `StatusDialog`
- use the new `Utils.restartApplication()` when changing the UI language
as well
- remove some dead code

In the (very) long term, we should take a different approach of scaling
our app independently of Qt, just taking the monitor
`Screen.devicePixelRatio` into account, similar to what other apps like
Telegram do

Fixes #13484
This commit is contained in:
Lukáš Tinkl 2024-02-22 16:16:47 +01:00 committed by Lukáš Tinkl
parent 0497ecd82e
commit 630da7caaa
5 changed files with 75 additions and 81 deletions

View File

@ -1,44 +1,26 @@
import QtQuick 2.13 import QtQuick 2.15
import QtQuick.Controls 2.13 import QtQml.Models 2.15
import QtQuick.Layouts 1.13
import utils 1.0
import StatusQ.Controls 0.1 import StatusQ.Controls 0.1
import StatusQ.Core 0.1
import StatusQ.Popups.Dialog 0.1
import shared 1.0 StatusDialog {
import shared.panels 1.0 id: root
import shared.popups 1.0
// TODO: replace with StatusModal
ModalPopup {
height: 237
width: 400
property Popup parentPopup
title: qsTr("Application Restart") title: qsTr("Application Restart")
StyledText { contentItem: StatusBaseText {
text: qsTr("Please restart the application to apply the changes.") text: qsTr("Please restart the application to apply the changes.")
font.pixelSize: 15
anchors.left: parent.left
anchors.right: parent.right
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
} }
footer: Item { footer: StatusDialogFooter {
id: footerContainer rightButtons: ObjectModel {
width: parent.width
height: children[0].height
StatusButton { StatusButton {
anchors.right: parent.right
anchors.rightMargin: Style.current.smallPadding
type: StatusBaseButton.Type.Danger type: StatusBaseButton.Type.Danger
text: qsTr("Restart") text: qsTr("Restart")
anchors.bottom: parent.bottom onClicked: root.accepted()
onClicked: Utils.restartApplication(); }
} }
} }
} }

View File

@ -1,7 +1,8 @@
import QtQuick 2.13 import QtQuick 2.15
import QtQuick.Controls 2.13 import QtQuick.Controls 2.15
import QtQuick.Layouts 1.13 import QtQuick.Layouts 1.15
import QtQuick.Controls.Universal 2.12 import QtQuick.Window 2.15
import QtQuick.Controls.Universal 2.15
import utils 1.0 import utils 1.0
import shared 1.0 import shared 1.0
@ -37,20 +38,29 @@ SettingsContentBase {
appearanceView.updateFontSize(localAccountSensitiveSettings.fontSize) appearanceView.updateFontSize(localAccountSensitiveSettings.fontSize)
} }
readonly property var priv: QtObject {
id: priv
readonly property real savedDpr: {
const scaleFactorStr = appearanceView.appearanceStore.readTextFile(uiScaleFilePath)
if (scaleFactorStr === "") {
return 1
}
const scaleFactor = parseFloat(scaleFactorStr)
if (isNaN(scaleFactor)) {
return 1
}
return scaleFactor
}
}
Item { Item {
id: appearanceContainer id: appearanceContainer
anchors.left: !!parent ? parent.left : undefined anchors.left: !!parent ? parent.left : undefined
anchors.leftMargin: Style.current.padding
width: appearanceView.contentWidth - 2 * Style.current.padding width: appearanceView.contentWidth - 2 * Style.current.padding
height: childrenRect.height height: childrenRect.height
ButtonGroup {
id: chatModeSetting
}
ButtonGroup {
id: appearanceSetting
}
Rectangle { Rectangle {
id: preview id: preview
anchors.top: parent.top anchors.top: parent.top
@ -128,41 +138,34 @@ SettingsContentBase {
StatusQ.StatusLabeledSlider { StatusQ.StatusLabeledSlider {
id: zoomSlider id: zoomSlider
readonly property int initialValue: {
let scaleFactorStr = appearanceView.appearanceStore.readTextFile(uiScaleFilePath) readonly property int initialValue: priv.savedDpr * 100
if (scaleFactorStr === "") { readonly property bool dirty: value !== initialValue
return 100
}
let scaleFactor = parseFloat(scaleFactorStr)
if (isNaN(scaleFactor)) {
return 100
}
return scaleFactor * 100
}
anchors.top: labelZoom.bottom anchors.top: labelZoom.bottom
anchors.topMargin: Style.current.padding anchors.topMargin: Style.current.padding
width: parent.width width: parent.width
from: 50 from: 50
to: 200 to: 300
stepSize: 50 stepSize: 25
model: [ qsTr("50%"), qsTr("100%"), qsTr("150%"), qsTr("200%") ] model: [ qsTr("50%"), qsTr("75%"), qsTr("100%"), qsTr("125%"), qsTr("150%"), qsTr("175%"), qsTr("200%"),
qsTr("225%"), qsTr("250%"), qsTr("275%"), qsTr("300%")]
value: initialValue value: initialValue
onValueChanged: { onMoved: {
if (value !== initialValue) { const uiScale = zoomSlider.value === 100 ? "" // reset to native highdpi
appearanceView.appearanceStore.writeTextFile(uiScaleFilePath, value / 100.0) : zoomSlider.value / 100.0
} appearanceView.appearanceStore.writeTextFile(uiScaleFilePath, uiScale)
} }
onPressedChanged: { onPressedChanged: {
if (!pressed && value !== initialValue) { if (!pressed && dirty) {
confirmAppRestartModal.open() confirmAppRestartModal.open()
} }
} }
ConfirmAppRestartModal { ConfirmAppRestartModal {
id: confirmAppRestartModal id: confirmAppRestartModal
onClosed: { onAccepted: Utils.restartApplication();
zoomSlider.value = zoomSlider.initialValue onClosed: zoomSlider.value = zoomSlider.initialValue
}
} }
} }

View File

@ -189,13 +189,12 @@ SettingsContentBase {
sourceComponent: ConfirmationDialog { sourceComponent: ConfirmationDialog {
headerSettings.title: qsTr("Change language") headerSettings.title: qsTr("Change language")
confirmationText: qsTr("Display language has been changed. You must restart the application for changes to take effect.") confirmationText: qsTr("Display language has been changed. You must restart the application for changes to take effect.")
confirmButtonLabel: qsTr("Close the app now") confirmButtonLabel: qsTr("Restart")
onConfirmButtonClicked: { onConfirmButtonClicked: {
languageConfirmationDialog.active = false languageConfirmationDialog.active = false
Qt.quit() Utils.restartApplication()
} }
} }
} }
} }
} }

View File

@ -1,11 +1,11 @@
import QtQuick 2.13 import QtQuick 2.15
import QtQuick.Controls 2.13 import QtQuick.Controls 2.15
import QtQuick.Layouts 1.13 import QtQuick.Layouts 1.15
import Qt.labs.platform 1.1 import Qt.labs.platform 1.1
import Qt.labs.settings 1.0 import Qt.labs.settings 1.1
import QtQuick.Window 2.12 import QtQuick.Window 2.15
import QtQml 2.13 import QtQml 2.15
import QtQuick.Controls.Universal 2.12 import QtQuick.Controls.Universal 2.15
import utils 1.0 import utils 1.0
import shared 1.0 import shared 1.0
@ -25,8 +25,8 @@ StatusWindow {
id: applicationWindow id: applicationWindow
objectName: "mainWindow" objectName: "mainWindow"
minimumWidth: 1200 minimumWidth: 1200 / Screen.devicePixelRatio
minimumHeight: 680 minimumHeight: 680 / Screen.devicePixelRatio
color: Style.current.background color: Style.current.background
title: { title: {
// Set application settings // Set application settings

View File

@ -166,8 +166,18 @@ void dos_qguiapplication_enable_hdpi(const char *uiScaleFilePath)
QFile scaleFile(QString::fromUtf8(uiScaleFilePath)); QFile scaleFile(QString::fromUtf8(uiScaleFilePath));
if (scaleFile.open(QIODevice::ReadOnly)) { if (scaleFile.open(QIODevice::ReadOnly)) {
const auto scale = scaleFile.readAll(); const auto scaleStr = scaleFile.readAll();
qputenv("QT_SCALE_FACTOR", scale); bool ok = false;
const auto scale = scaleStr.toDouble(&ok);
if (ok) {
// we want to scale the app on our own
qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "0");
// workaround for bug/feature when Qt would bail out if the scale is "1" and revert to DPI based scaling
constexpr auto unaryScale = 1.1;
qputenv("QT_SCREEN_SCALE_FACTORS", QByteArray::number(unaryScale));
// compensate for the workaround above so that we get the desired scale factor
qputenv("QT_SCALE_FACTOR", QByteArray::number(scale/unaryScale, 'f', 2));
}
} }
} }
@ -373,7 +383,7 @@ void dos_qguiapplication_quit()
void dos_qguiapplication_restart() void dos_qguiapplication_restart()
{ {
QProcess::startDetached(QCoreApplication::applicationFilePath()); QProcess::startDetached(QCoreApplication::applicationFilePath(), {});
dos_qguiapplication_quit(); dos_qguiapplication_quit();
} }