Port usersettings to qml

This commit is contained in:
Nicolas Werner 2022-01-09 00:28:03 +01:00
parent 8d52c17f29
commit f1a23355bd
No known key found for this signature in database
GPG key ID: C8D75E610773F2D9
16 changed files with 1330 additions and 859 deletions

View file

@ -16,6 +16,7 @@ AbstractButton {
property color highlightColor: Nheko.colors.highlight
property color buttonTextColor: Nheko.colors.buttonText
property bool changeColorOnHover: true
property bool ripple: true
focusPolicy: Qt.NoFocus
width: 16
@ -38,6 +39,7 @@ AbstractButton {
}
Ripple {
enabled: button.ripple
color: Qt.rgba(buttonTextColor.r, buttonTextColor.g, buttonTextColor.b, 0.5)
}

View file

@ -194,7 +194,7 @@ Rectangle {
}
room.input.send();
event.accepted = true;
} else if (event.key == Qt.Key_Tab) {
} else if (event.key == Qt.Key_Tab && (event.modifiers == Qt.NoModifier || event.modifiers == Qt.ShiftModifier)) {
event.accepted = true;
if (popup.opened) {
if (event.modifiers & Qt.ShiftModifier)

View file

@ -678,6 +678,7 @@ Page {
visible: !collapsed
Layout.fillWidth: true
hoverEnabled: true
ripple: false
width: 22
height: 22
image: ":/icons/icons/ui/settings.svg"
@ -685,7 +686,7 @@ Page {
ToolTip.delay: Nheko.tooltipDelay
ToolTip.text: qsTr("User settings")
Layout.margins: Nheko.paddingMedium
onClicked: Nheko.showUserSettingsPage()
onClicked: mainWindow.push(userSettingsPage);
}
}

View file

@ -144,6 +144,14 @@ Page {
}
Component {
id: userSettingsPage
UserSettingsPage {
}
}
Shortcut {
sequence: "Ctrl+K"
onActivated: {
@ -353,8 +361,13 @@ Page {
target: UIA
}
ChatPage {
StackView {
id: mainWindow
anchors.fill: parent
initialItem: ChatPage {
//anchors.fill: parent
}
}
}

View file

@ -36,7 +36,7 @@ Switch {
width: parent.height
height: width
radius: width / 2
color: toggleButton.down ? "whitesmoke" : "whitesmoke"
color: toggleButton.enabled ? "whitesmoke" : "#cccccc"
border.color: "#ebebeb"
}

View file

@ -0,0 +1,218 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
// SPDX-FileCopyrightText: 2022 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import "ui"
import Qt.labs.platform 1.1 as Platform
import QtQuick 2.15
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2
import QtQuick.Window 2.13
import im.nheko 1.0
Rectangle {
id: userSettingsDialog
property bool collapsed: width < 800
color: Nheko.colors.window
Shortcut {
sequence: StandardKey.Cancel
onActivated: userSettingsDialog.close()
}
ScrollView {
id: scroll
palette: Nheko.colors
ScrollBar.horizontal.visible: false
anchors.fill: parent
anchors.margins: Nheko.paddingLarge
contentWidth: availableWidth
Timer {
id: deadTimer
interval: 500
}
Connections {
target: scroll.contentItem
function onContentYChanged() { deadTimer.restart(); }
}
GridLayout {
id: grid
columns: userSettingsDialog.collapsed ? 1 : 2
rowSpacing: Nheko.paddingMedium
columnSpacing: Nheko.paddingMedium
anchors.fill: parent
anchors.leftMargin: userSettingsDialog.collapsed ? Nheko.paddingLarge : (userSettingsDialog.width-600) * 0.4
anchors.rightMargin: userSettingsDialog.collapsed ? Nheko.paddingLarge : (userSettingsDialog.width-600) * 0.4
Repeater {
model: UserSettingsModel
delegate: Item {
required property var model
id: r
Component.onCompleted: {
while (children.length) {
console.log("Reparenting: " + children[0]);
children[0].parent = grid;
}
}
Label {
Layout.alignment: Qt.AlignLeft
Layout.fillWidth: true
color: Nheko.colors.text
text: model.name
//Layout.column: 0
Layout.columnSpan: (model.type == UserSettingsModel.SectionTitle && !userSettingsDialog.collapsed) ? 2 : 1
//Layout.row: model.index
Layout.minimumWidth: implicitWidth
Layout.leftMargin: model.type == UserSettingsModel.SectionTitle ? 0 : Nheko.paddingMedium
Layout.topMargin: model.type == UserSettingsModel.SectionTitle ? Nheko.paddingLarge : 0
font.pointSize: 1.1 * fontInfo.pointSize
HoverHandler {
id: hovered
enabled: model.description ?? false
}
ToolTip.visible: hovered.hovered && model.description
ToolTip.text: model.description ?? ""
ToolTip.delay: Nheko.tooltipDelay
}
DelegateChooser {
id: chooser
roleValue: model.type
Layout.alignment: Qt.AlignRight
//Layout.column: model.type == UserSettingsModel.SectionTitle ? 0 : 1
Layout.columnSpan: (model.type == UserSettingsModel.SectionTitle && !userSettingsDialog.collapsed) ? 2 : 1
//Layout.row: model.index
Layout.preferredHeight: child.height
Layout.preferredWidth: Math.min(child.implicitWidth, child.width || 1000)
Layout.fillWidth: model.type == UserSettingsModel.SectionTitle
Layout.rightMargin: model.type == UserSettingsModel.SectionTitle ? 0 : Nheko.paddingMedium
DelegateChoice {
roleValue: UserSettingsModel.Toggle
ToggleButton {
checked: model.value
onCheckedChanged: model.value = checked
enabled: model.enabled
}
}
DelegateChoice {
roleValue: UserSettingsModel.Options
ComboBox {
Layout.preferredWidth: Math.min(200, implicitWidth)
width: Math.min(200, implicitWidth)
model: r.model.values
currentIndex: r.model.value
enabled: !deadTimer.running
onCurrentIndexChanged: r.model.value = currentIndex
}
}
DelegateChoice {
roleValue: UserSettingsModel.Number
SpinBox {
//implicitWidth: 100
enabled: !deadTimer.running && model.enabled
from: model.valueLowerBound
to: model.valueUpperBound
stepSize: model.valueStep
value: model.value
onValueChanged: model.value = value
}
}
DelegateChoice {
roleValue: UserSettingsModel.ReadOnlyText
Text {
color: Nheko.colors.text
text: model.value
}
}
DelegateChoice {
roleValue: UserSettingsModel.SectionTitle
Item {
width: grid.width
height: fontMetrics.lineSpacing
Rectangle {
anchors.topMargin: Nheko.paddingSmall
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
color: Nheko.colors.buttonText
height: 1
}
}
}
DelegateChoice {
roleValue: UserSettingsModel.KeyStatus
Text {
color: model.good ? "green" : Nheko.theme.error
text: model.value ? qsTr("CACHED") : qsTr("NOT CACHED")
}
}
DelegateChoice {
roleValue: UserSettingsModel.SessionKeyImportExport
RowLayout {
Button {
text: qsTr("IMPORT")
onClicked: UserSettingsModel.importSessionKeys()
}
Button {
text: qsTr("EXPORT")
onClicked: UserSettingsModel.exportSessionKeys()
}
}
}
DelegateChoice {
roleValue: UserSettingsModel.XSignKeysRequestDownload
RowLayout {
Button {
text: qsTr("DOWNLOAD")
onClicked: UserSettingsModel.downloadCrossSigningSecrets()
}
Button {
text: qsTr("REQUEST")
onClicked: UserSettingsModel.requestCrossSigningSecrets()
}
}
}
DelegateChoice {
Text {
text: model.value
}
}
}
}
}
}
}
ImageButton {
anchors.top: parent.top
anchors.left: parent.left
anchors.margins: Nheko.paddingMedium
width: Nheko.avatarSize
height: Nheko.avatarSize
image: ":/icons/icons/ui/angle-arrow-left.svg"
ToolTip.visible: hovered
ToolTip.text: qsTr("Back")
onClicked: mainWindow.pop()
}
}

View file

@ -213,7 +213,7 @@ ApplicationWindow {
ToggleButton {
checked: imagePack.isEmotePack
onClicked: imagePack.isEmotePack = checked
onCheckedChanged: imagePack.isEmotePack = checked
Layout.alignment: Qt.AlignRight
}
@ -223,7 +223,7 @@ ApplicationWindow {
ToggleButton {
checked: imagePack.isStickerPack
onClicked: imagePack.isStickerPack = checked
onCheckedChanged: imagePack.isStickerPack = checked
Layout.alignment: Qt.AlignRight
}
@ -279,7 +279,7 @@ ApplicationWindow {
ToggleButton {
checked: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.IsEmote)
onClicked: imagePack.setData(imagePack.index(currentImageIndex, 0), checked, SingleImagePackModel.IsEmote)
onCheckedChanged: imagePack.setData(imagePack.index(currentImageIndex, 0), checked, SingleImagePackModel.IsEmote)
Layout.alignment: Qt.AlignRight
}
@ -289,7 +289,7 @@ ApplicationWindow {
ToggleButton {
checked: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.IsSticker)
onClicked: imagePack.setData(imagePack.index(currentImageIndex, 0), checked, SingleImagePackModel.IsSticker)
onCheckedChanged: imagePack.setData(imagePack.index(currentImageIndex, 0), checked, SingleImagePackModel.IsSticker)
Layout.alignment: Qt.AlignRight
}

View file

@ -185,7 +185,7 @@ ApplicationWindow {
ToggleButton {
ToolTip.text: qsTr("Enables this pack to be used in all rooms")
checked: currentPack ? currentPack.isGloballyEnabled : false
onClicked: currentPack.isGloballyEnabled = !currentPack.isGloballyEnabled
onCheckedChanged: currentPack.isGloballyEnabled = checked
Layout.alignment: Qt.AlignRight
}

View file

@ -214,7 +214,7 @@ ApplicationWindow {
id: encryptionToggle
checked: roomSettings.isEncryptionEnabled
onClicked: {
onCheckedChanged: {
if (roomSettings.isEncryptionEnabled) {
checked = true;
return ;

View file

@ -86,6 +86,7 @@
<file>qml/CommunitiesList.qml</file>
<file>qml/RoomList.qml</file>
<file>qml/TimelineView.qml</file>
<file>qml/UserSettingsPage.qml</file>
<file>qml/Avatar.qml</file>
<file>qml/Completer.qml</file>
<file>qml/EncryptionIndicator.qml</file>

View file

@ -60,7 +60,6 @@ MainWindow::MainWindow(QWidget *parent)
login_page_ = new LoginPage(this);
register_page_ = new RegisterPage(this);
chat_page_ = new ChatPage(userSettings_, this);
userSettingsPage_ = new UserSettingsPage(userSettings_, this);
// Initialize sliding widget manager.
pageStack_ = new QStackedWidget(this);
@ -68,7 +67,6 @@ MainWindow::MainWindow(QWidget *parent)
pageStack_->addWidget(login_page_);
pageStack_->addWidget(register_page_);
pageStack_->addWidget(chat_page_);
pageStack_->addWidget(userSettingsPage_);
setCentralWidget(pageStack_);
@ -93,13 +91,7 @@ MainWindow::MainWindow(QWidget *parent)
showLoginPage();
});
connect(userSettingsPage_, &UserSettingsPage::moveBack, this, [this]() {
pageStack_->setCurrentWidget(chat_page_);
});
connect(userSettingsPage_, SIGNAL(trayOptionChanged(bool)), trayIcon_, SLOT(setVisible(bool)));
connect(
userSettingsPage_, &UserSettingsPage::themeChanged, chat_page_, &ChatPage::themeChanged);
connect(userSettings_.get(), &UserSettings::trayChanged, trayIcon_, &TrayIcon::setVisible);
connect(trayIcon_,
SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
this,
@ -109,8 +101,6 @@ MainWindow::MainWindow(QWidget *parent)
connect(this, &MainWindow::focusChanged, chat_page_, &ChatPage::chatFocusChanged);
connect(chat_page_, &ChatPage::showUserSettingsPage, this, &MainWindow::showUserSettingsPage);
connect(login_page_, &LoginPage::loginOk, this, [this](const mtx::responses::Login &res) {
http::client()->set_user(res.user_id);
showChatPage();
@ -247,14 +237,8 @@ MainWindow::showChatPage()
login_page_->reset();
chat_page_->bootstrap(userid, homeserver, token);
connect(cache::client(),
&Cache::databaseReady,
userSettingsPage_,
&UserSettingsPage::updateSecretStatus);
connect(cache::client(),
&Cache::secretChanged,
userSettingsPage_,
&UserSettingsPage::updateSecretStatus);
connect(cache::client(), &Cache::databaseReady, this, &MainWindow::secretsChanged);
connect(cache::client(), &Cache::secretChanged, this, &MainWindow::secretsChanged);
emit reload();
}
@ -403,9 +387,3 @@ MainWindow::showRegisterPage()
pageStack_->addWidget(register_page_);
pageStack_->setCurrentWidget(register_page_);
}
void
MainWindow::showUserSettingsPage()
{
pageStack_->setCurrentWidget(userSettingsPage_);
}

View file

@ -84,9 +84,6 @@ private slots:
//! Show the register page in the main window.
void showRegisterPage();
//! Show user settings page.
void showUserSettingsPage();
//! Show the chat page and start communicating with the given access token.
void showChatPage();
@ -98,6 +95,7 @@ private slots:
signals:
void focusChanged(const bool focused);
void reload();
void secretsChanged();
private:
void showDialog(QWidget *dialog);
@ -120,7 +118,6 @@ private:
QStackedWidget *pageStack_;
//! The main chat area.
ChatPage *chat_page_;
UserSettingsPage *userSettingsPage_;
QSharedPointer<UserSettings> userSettings_;
//! Tray icon that shows the unread message count.
TrayIcon *trayIcon_;

File diff suppressed because it is too large Load diff

View file

@ -6,6 +6,7 @@
#pragma once
#include <QAbstractListModel>
#include <QFontDatabase>
#include <QFrame>
#include <QProcessEnvironment>
@ -353,89 +354,125 @@ private:
static QSharedPointer<UserSettings> instance_;
};
class HorizontalLine : public QFrame
class UserSettingsModel : public QAbstractListModel
{
Q_OBJECT
public:
HorizontalLine(QWidget *parent = nullptr);
};
class UserSettingsPage : public QWidget
enum Indices
{
Q_OBJECT
GeneralSection,
Theme,
MobileMode,
#ifndef Q_OS_MAC
ScaleFactor,
#endif
Font,
FontSize,
EmojiFont,
AvatarCircles,
UseIdenticon,
PrivacyScreen,
PrivacyScreenTimeout,
TimelineSection,
TimelineMaxWidth,
MessageHoverHighlight,
EnlargeEmojiOnlyMessages,
AnimateImagesOnHover,
TypingNotifications,
ReadReceipts,
ButtonsInTimeline,
Markdown,
SidebarSection,
GroupView,
SortByImportance,
DecryptSidebar,
TraySection,
Tray,
StartInTray,
NotificationsSection,
DesktopNotifications,
AlertOnNotification,
VoipSection,
UseStunServer,
Microphone,
Camera,
CameraResolution,
CameraFrameRate,
Ringtone,
EncryptionSection,
OnlyShareKeysWithVerifiedUsers,
ShareKeysWithTrustedUsers,
SessionKeys,
UseOnlineKeyBackup,
OnlineBackupKey,
SelfSigningKey,
UserSigningKey,
MasterKey,
CrossSigningSecrets,
DeviceId,
DeviceFingerprint,
LoginInfoSection,
UserId,
Homeserver,
Profile,
Version,
Platform,
COUNT,
// hidden for now
AccessToken,
#ifdef Q_OS_MAC
ScaleFactor,
#endif
};
public:
UserSettingsPage(QSharedPointer<UserSettings> settings, QWidget *parent = nullptr);
protected:
void showEvent(QShowEvent *event) override;
void paintEvent(QPaintEvent *event) override;
signals:
void moveBack();
void trayOptionChanged(bool value);
void themeChanged();
void decryptSidebarChanged();
public slots:
void updateSecretStatus();
private slots:
void importSessionKeys();
void exportSessionKeys();
private:
// Layouts
QVBoxLayout *topLayout_;
QHBoxLayout *topBarLayout_;
QFormLayout *formLayout_;
// Shared settings object.
QSharedPointer<UserSettings> settings_;
Toggle *trayToggle_;
Toggle *startInTrayToggle_;
Toggle *groupViewToggle_;
Toggle *timelineButtonsToggle_;
Toggle *typingNotifications_;
Toggle *messageHoverHighlight_;
Toggle *enlargeEmojiOnlyMessages_;
Toggle *sortByImportance_;
Toggle *readReceipts_;
Toggle *markdown_;
Toggle *animateImagesOnHover_;
Toggle *desktopNotifications_;
Toggle *alertOnNotification_;
Toggle *avatarCircles_;
Toggle *useIdenticon_;
Toggle *useStunServer_;
Toggle *decryptSidebar_;
Toggle *privacyScreen_;
QSpinBox *privacyScreenTimeout_;
Toggle *shareKeysWithTrustedUsers_;
Toggle *onlyShareKeysWithVerifiedUsers_;
Toggle *useOnlineKeyBackup_;
Toggle *mobileMode_;
QLabel *deviceFingerprintValue_;
QLabel *deviceIdValue_;
QLabel *backupSecretCached;
QLabel *masterSecretCached;
QLabel *selfSigningSecretCached;
QLabel *userSigningSecretCached;
QComboBox *themeCombo_;
QComboBox *scaleFactorCombo_;
QComboBox *fontSizeCombo_;
QFontComboBox *fontSelectionCombo_;
QComboBox *emojiFontSelectionCombo_;
QComboBox *ringtoneCombo_;
QComboBox *microphoneCombo_;
QComboBox *cameraCombo_;
QComboBox *cameraResolutionCombo_;
QComboBox *cameraFrameRateCombo_;
QSpinBox *timelineMaxWidthSpin_;
int sideMargin_ = 0;
enum Types
{
Toggle,
ReadOnlyText,
Options,
Number,
SectionTitle,
SectionBar,
KeyStatus,
SessionKeyImportExport,
XSignKeysRequestDownload,
};
Q_ENUM(Types);
enum Roles
{
Name,
Description,
Value,
Type,
ValueLowerBound,
ValueUpperBound,
ValueStep,
Values,
Good,
Enabled,
};
UserSettingsModel(QObject *parent = nullptr);
QHash<int, QByteArray> roleNames() const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override
{
(void)parent;
return (int)COUNT;
}
QVariant data(const QModelIndex &index, int role) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
Q_INVOKABLE void importSessionKeys();
Q_INVOKABLE void exportSessionKeys();
Q_INVOKABLE void requestCrossSigningSecrets();
Q_INVOKABLE void downloadCrossSigningSecrets();
};

View file

@ -394,7 +394,7 @@ utils::humanReadableFingerprint(const QString &ed25519)
QString fingerprint;
for (int i = 0; i < ed25519.length(); i = i + 4) {
fingerprint.append(QStringView(ed25519).mid(i, 4));
if (i > 0 && i % 16 == 12)
if (i > 0 && i == 20)
fingerprint.append('\n');
else if (i < ed25519.length())
fingerprint.append(' ');

View file

@ -259,6 +259,10 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par
"im.nheko", 1, 0, "Nheko", [](QQmlEngine *, QJSEngine *) -> QObject * {
return new Nheko();
});
qmlRegisterSingletonType<UserSettingsModel>(
"im.nheko", 1, 0, "UserSettingsModel", [](QQmlEngine *, QJSEngine *) -> QObject * {
return new UserSettingsModel();
});
qmlRegisterSingletonInstance("im.nheko", 1, 0, "VerificationManager", verificationManager_);
qmlRegisterSingletonInstance("im.nheko", 1, 0, "Presence", presenceEmitter);
qmlRegisterSingletonType<SelfVerificationStatus>(