Create a QML module for settings
This commit is contained in:
13
src/settings/About.qml
Normal file
13
src/settings/About.qml
Normal file
@@ -0,0 +1,13 @@
|
||||
// SPDX-FileCopyrightText: 2020 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-FileCopyrightText: 2021 Carl Schwan <carl@carlschwan.eu>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
import QtQuick.Layouts
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
import org.kde.neochat
|
||||
import org.kde.coreaddons
|
||||
|
||||
FormCard.AboutPage {
|
||||
title: i18nc("@title:window", "About NeoChat")
|
||||
aboutData: AboutData
|
||||
}
|
||||
8
src/settings/AboutKDE.qml
Normal file
8
src/settings/AboutKDE.qml
Normal file
@@ -0,0 +1,8 @@
|
||||
// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
|
||||
FormCard.AboutKDE {
|
||||
title: i18nc("@title:window", "About KDE")
|
||||
}
|
||||
236
src/settings/AccountEditorPage.qml
Normal file
236
src/settings/AccountEditorPage.qml
Normal file
@@ -0,0 +1,236 @@
|
||||
// SPDX-FileCopyrightText: 2020 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
import Qt.labs.platform
|
||||
import QtQuick.Window
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
import org.kde.kirigamiaddons.components as KirigamiComponents
|
||||
import org.kde.neochat
|
||||
|
||||
FormCard.FormCardPage {
|
||||
id: root
|
||||
|
||||
title: i18n("Edit Account")
|
||||
property NeoChatConnection connection
|
||||
|
||||
KirigamiComponents.AvatarButton {
|
||||
id: avatar
|
||||
|
||||
property OpenFileDialog fileDialog: null
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||
Layout.topMargin: Kirigami.Units.largeSpacing
|
||||
|
||||
// Square button
|
||||
implicitWidth: Kirigami.Units.gridUnit * 5
|
||||
implicitHeight: implicitWidth
|
||||
|
||||
padding: 0
|
||||
|
||||
source: root.connection && root.connection.localUser.avatarMediaId ? ("image://mxc/" + root.connection.localUser.avatarMediaId) : ""
|
||||
name: root.connection.localUser.displayName
|
||||
|
||||
onClicked: {
|
||||
if (fileDialog) {
|
||||
return;
|
||||
}
|
||||
fileDialog = openFileDialog.createObject(this);
|
||||
fileDialog.chosen.connect(receivedSource => {
|
||||
if (!receivedSource) {
|
||||
return;
|
||||
}
|
||||
source = receivedSource;
|
||||
});
|
||||
fileDialog.open();
|
||||
}
|
||||
|
||||
QQC2.Button {
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
right: parent.right
|
||||
}
|
||||
visible: avatar.source.toString().length === 0
|
||||
icon.name: "cloud-upload"
|
||||
text: i18n("Upload new avatar")
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
|
||||
onClicked: parent.clicked()
|
||||
|
||||
QQC2.ToolTip.text: text
|
||||
QQC2.ToolTip.visible: hovered
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
|
||||
QQC2.Button {
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
right: parent.right
|
||||
}
|
||||
visible: avatar.source.toString().length !== 0
|
||||
icon.name: "edit-clear"
|
||||
text: i18n("Remove current avatar")
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
|
||||
onClicked: avatar.source = ""
|
||||
|
||||
QQC2.ToolTip.text: text
|
||||
QQC2.ToolTip.visible: hovered
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
Component {
|
||||
id: openFileDialog
|
||||
|
||||
OpenFileDialog {
|
||||
currentFolder: StandardPaths.standardLocations(StandardPaths.PicturesLocation)[0]
|
||||
parentWindow: root.Window.window
|
||||
|
||||
onAccepted: destroy()
|
||||
onRejected: destroy()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18n("User information")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
FormCard.FormTextFieldDelegate {
|
||||
id: name
|
||||
label: i18n("Name:")
|
||||
text: root.connection ? root.connection.localUser.displayName : ""
|
||||
}
|
||||
FormCard.FormDelegateSeparator {}
|
||||
FormCard.FormTextFieldDelegate {
|
||||
id: accountLabel
|
||||
label: i18n("Label:")
|
||||
text: root.connection ? root.connection.label : ""
|
||||
}
|
||||
FormCard.FormDelegateSeparator {}
|
||||
FormCard.FormButtonDelegate {
|
||||
text: i18n("Save")
|
||||
onClicked: {
|
||||
if (!root.connection.setAvatar(avatar.source)) {
|
||||
showPassiveNotification("The Avatar could not be set");
|
||||
}
|
||||
if (root.connection.localUser.displayName !== name.text) {
|
||||
root.connection.localUser.rename(name.text);
|
||||
}
|
||||
if (root.connection.label !== accountLabel.text) {
|
||||
root.connection.label = accountLabel.text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18n("Password")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
FormCard.FormTextDelegate {
|
||||
visible: root.connection !== undefined && root.connection.canChangePassword === false
|
||||
text: i18n("Your server doesn't support changing your password")
|
||||
}
|
||||
FormCard.FormDelegateSeparator {
|
||||
visible: root.connection !== undefined && root.connection.canChangePassword === false
|
||||
}
|
||||
FormCard.FormTextFieldDelegate {
|
||||
id: currentPassword
|
||||
label: i18n("Current Password:")
|
||||
enabled: root.connection !== undefined && root.connection.canChangePassword !== false
|
||||
echoMode: TextInput.Password
|
||||
}
|
||||
FormCard.FormDelegateSeparator {}
|
||||
FormCard.FormTextFieldDelegate {
|
||||
id: newPassword
|
||||
label: i18n("New Password:")
|
||||
enabled: root.connection !== undefined && root.connection.canChangePassword !== false
|
||||
echoMode: TextInput.Password
|
||||
}
|
||||
FormCard.FormDelegateSeparator {}
|
||||
FormCard.FormTextFieldDelegate {
|
||||
id: confirmPassword
|
||||
label: i18n("Confirm new Password:")
|
||||
enabled: root.connection !== undefined && root.connection.canChangePassword !== false
|
||||
echoMode: TextInput.Password
|
||||
onTextChanged: if (newPassword.text !== confirmPassword.text && confirmPassword.text.length > 0) {
|
||||
confirmPassword.status = FormCard.AbstractFormDelegate.Status.Error;
|
||||
confirmPassword.statusMessage = i18n("Passwords don't match");
|
||||
} else {
|
||||
confirmPassword.status = FormCard.AbstractFormDelegate.Status.Default;
|
||||
confirmPassword.statusMessage = '';
|
||||
}
|
||||
}
|
||||
FormCard.FormDelegateSeparator {}
|
||||
FormCard.FormButtonDelegate {
|
||||
text: i18n("Save")
|
||||
enabled: currentPassword.text.length > 0 && newPassword.text.length > 0 && confirmPassword.text.length > 0
|
||||
onClicked: {
|
||||
if (newPassword.text === confirmPassword.text) {
|
||||
root.connection.changePassword(currentPassword.text, newPassword.text);
|
||||
} else {
|
||||
showPassiveNotification(i18n("Passwords do not match"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormHeader {
|
||||
Layout.fillWidth: true
|
||||
title: i18n("Server Information")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
FormCard.FormTextDelegate {
|
||||
text: i18n("Homeserver url")
|
||||
description: root.connection.homeserver
|
||||
}
|
||||
|
||||
/* TODO but needs first some api in Quotient
|
||||
FormCard.FormTextDelegate {
|
||||
text: i18n("Server file upload limit")
|
||||
description: root.connection.homeserver
|
||||
}
|
||||
|
||||
FormCard.FormTextDelegate {
|
||||
text: i18n("Server name")
|
||||
description: root.connection.homeserver
|
||||
}
|
||||
|
||||
FormCard.FormTextDelegate {
|
||||
text: i18n("Server version")
|
||||
description: root.connection.homeserver
|
||||
}*/
|
||||
}
|
||||
FormCard.FormHeader {
|
||||
title: i18nc("@title", "Account Management")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
FormCard.FormButtonDelegate {
|
||||
id: deactivateAccountButton
|
||||
text: i18n("Deactivate Account")
|
||||
onClicked: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ConfirmDeactivateAccountDialog.qml'), {
|
||||
connection: root.connection
|
||||
}, {
|
||||
title: i18nc("@title", "Confirm Deactivating Account")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
data: Connections {
|
||||
target: root.connection
|
||||
function onPasswordStatus(status) {
|
||||
if (status === NeoChatConnection.Success) {
|
||||
showPassiveNotification(i18n("Password changed successfully"));
|
||||
} else if (status === NeoChatConnection.Wrong) {
|
||||
showPassiveNotification(i18n("Wrong password entered"));
|
||||
} else {
|
||||
showPassiveNotification(i18n("Unknown problem while trying to change password"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
116
src/settings/AccountsPage.qml
Normal file
116
src/settings/AccountsPage.qml
Normal file
@@ -0,0 +1,116 @@
|
||||
// SPDX-FileCopyrightText: 2020 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Window
|
||||
import Qt.labs.platform
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
|
||||
|
||||
import org.kde.neochat
|
||||
import org.kde.neochat.accounts
|
||||
|
||||
FormCard.FormCardPage {
|
||||
id: root
|
||||
|
||||
title: i18n("Accounts")
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18n("Accounts")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
Repeater {
|
||||
model: AccountRegistry
|
||||
delegate: FormCard.AbstractFormDelegate {
|
||||
id: accountDelegate
|
||||
required property NeoChatConnection connection
|
||||
Layout.fillWidth: true
|
||||
onClicked: applicationWindow().pageStack.layers.push('AccountEditorPage.qml', {
|
||||
connection: accountDelegate.connection
|
||||
}, {
|
||||
title: i18n("Account editor")
|
||||
})
|
||||
|
||||
contentItem: RowLayout {
|
||||
KirigamiComponents.Avatar {
|
||||
name: accountDelegate.connection.localUser.displayName
|
||||
source: accountDelegate.connection.localUser.avatarMediaId ? ("image://mxc/" + accountDelegate.connection.localUser.avatarMediaId) : ""
|
||||
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
implicitWidth: Kirigami.Units.iconSizes.medium
|
||||
implicitHeight: Kirigami.Units.iconSizes.medium
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
QQC2.Label {
|
||||
Layout.fillWidth: true
|
||||
text: accountDelegate.connection.localUser.displayName
|
||||
textFormat: Text.PlainText
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.Wrap
|
||||
maximumLineCount: 2
|
||||
color: Kirigami.Theme.textColor
|
||||
}
|
||||
|
||||
QQC2.Label {
|
||||
Layout.fillWidth: true
|
||||
text: accountDelegate.connection.localUserId
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
font: Kirigami.Theme.smallFont
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.ToolButton {
|
||||
text: i18n("Logout")
|
||||
icon.name: "im-kick-user"
|
||||
onClicked: confirmLogoutDialogComponent.createObject(applicationWindow().overlay).open()
|
||||
}
|
||||
|
||||
Component {
|
||||
id: confirmLogoutDialogComponent
|
||||
ConfirmLogoutDialog {
|
||||
connection: accountDelegate.connection
|
||||
onAccepted: {
|
||||
if (AccountRegistry.accountCount === 1) {
|
||||
root.Window.window.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormArrow {
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
direction: Qt.RightArrow
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
FormCard.FormDelegateSeparator {
|
||||
below: addAccountDelegate
|
||||
}
|
||||
|
||||
FormCard.FormButtonDelegate {
|
||||
id: addAccountDelegate
|
||||
text: i18n("Add Account")
|
||||
icon.name: "list-add"
|
||||
onClicked: applicationWindow().pageStack.layers.push(Qt.createComponent('org.kde.neochat', 'WelcomePage.qml'))
|
||||
}
|
||||
}
|
||||
|
||||
property Connections connections: Connections {
|
||||
target: Controller
|
||||
function onConnectionAdded() {
|
||||
if (pageStack.layers.depth > 2) {
|
||||
pageStack.layers.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
356
src/settings/AppearanceSettingsPage.qml
Normal file
356
src/settings/AppearanceSettingsPage.qml
Normal file
@@ -0,0 +1,356 @@
|
||||
// SPDX-FileCopyrightText: 2020 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-FileCopyrightText: 2021 Carl Schwan <carl@carlschwan.eu>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
|
||||
|
||||
import org.kde.neochat
|
||||
import org.kde.neochat.config
|
||||
|
||||
FormCard.FormCardPage {
|
||||
id: root
|
||||
|
||||
title: i18nc("@title:window", "Appearance")
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18n("General theme")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
FormCard.AbstractFormDelegate {
|
||||
id: timelineModeSetting
|
||||
background: Item {}
|
||||
contentItem: RowLayout {
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
QQC2.ButtonGroup {
|
||||
id: themeGroup
|
||||
}
|
||||
ThemeRadioButton {
|
||||
thin: timelineModeSetting.width < Kirigami.Units.gridUnit * 22
|
||||
innerObject: [
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
KirigamiComponents.Avatar {
|
||||
color: "#4a5bcc"
|
||||
Layout.alignment: Qt.AlignTop
|
||||
visible: Config.showAvatarInTimeline
|
||||
Layout.preferredWidth: Config.showAvatarInTimeline ? Kirigami.Units.largeSpacing * 2 : 0
|
||||
Layout.preferredHeight: Kirigami.Units.largeSpacing * 2
|
||||
}
|
||||
QQC2.Control {
|
||||
Layout.fillWidth: true
|
||||
contentItem: ColumnLayout {
|
||||
QQC2.Label {
|
||||
Layout.fillWidth: true
|
||||
font.weight: Font.Bold
|
||||
font.pixelSize: 7
|
||||
text: "Paul Müller"
|
||||
color: "#4a5bcc"
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
QQC2.Label {
|
||||
Layout.fillWidth: true
|
||||
text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus facilisis porta mauris, quis finibus sem suscipit tincidunt."
|
||||
wrapMode: Text.Wrap
|
||||
font.pixelSize: 7
|
||||
}
|
||||
}
|
||||
background: Kirigami.ShadowedRectangle {
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
radius: Kirigami.Units.smallSpacing
|
||||
shadow.size: Kirigami.Units.smallSpacing
|
||||
shadow.color: Qt.rgba(0.0, 0.0, 0.0, 0.10)
|
||||
border.color: Kirigami.ColorUtils.tintWithAlpha(color, Kirigami.Theme.textColor, 0.15)
|
||||
border.width: 1
|
||||
}
|
||||
}
|
||||
},
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
KirigamiComponents.Avatar {
|
||||
color: "#9f244b"
|
||||
Layout.alignment: Qt.AlignTop
|
||||
visible: Config.showAvatarInTimeline
|
||||
Layout.preferredWidth: Config.showAvatarInTimeline ? Kirigami.Units.largeSpacing * 2 : 0
|
||||
Layout.preferredHeight: Kirigami.Units.largeSpacing * 2
|
||||
}
|
||||
QQC2.Control {
|
||||
Layout.fillWidth: true
|
||||
contentItem: ColumnLayout {
|
||||
QQC2.Label {
|
||||
Layout.fillWidth: true
|
||||
font.weight: Font.Bold
|
||||
font.pixelSize: 7
|
||||
text: "Jean Paul"
|
||||
color: "#9f244b"
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
QQC2.Label {
|
||||
Layout.fillWidth: true
|
||||
text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus facilisis porta , quis sem suscipit tincidunt."
|
||||
wrapMode: Text.Wrap
|
||||
font.pixelSize: 7
|
||||
}
|
||||
}
|
||||
background: Kirigami.ShadowedRectangle {
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
radius: Kirigami.Units.smallSpacing
|
||||
shadow.size: Kirigami.Units.smallSpacing
|
||||
shadow.color: Qt.rgba(0.0, 0.0, 0.0, 0.10)
|
||||
border.color: Kirigami.ColorUtils.tintWithAlpha(color, Kirigami.Theme.textColor, 0.15)
|
||||
border.width: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
text: i18n("Bubbles")
|
||||
checked: !Config.compactLayout
|
||||
QQC2.ButtonGroup.group: themeGroup
|
||||
enabled: !Config.isCompactLayoutImmutable
|
||||
|
||||
onToggled: {
|
||||
Config.compactLayout = !checked;
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
ThemeRadioButton {
|
||||
// Layout.alignment: Qt.AlignRight
|
||||
thin: timelineModeSetting.width < Kirigami.Units.gridUnit * 22
|
||||
innerObject: [
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
KirigamiComponents.Avatar {
|
||||
color: "#4a5bcc"
|
||||
Layout.alignment: Qt.AlignTop
|
||||
visible: Config.showAvatarInTimeline
|
||||
Layout.preferredWidth: Config.showAvatarInTimeline ? Kirigami.Units.largeSpacing * 2 : 0
|
||||
Layout.preferredHeight: Kirigami.Units.largeSpacing * 2
|
||||
}
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
QQC2.Label {
|
||||
Layout.fillWidth: true
|
||||
font.weight: Font.Bold
|
||||
font.pixelSize: 7
|
||||
text: "Paul Müller"
|
||||
color: "#4a5bcc"
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
QQC2.Label {
|
||||
Layout.fillWidth: true
|
||||
text: "Lorem ipsum dolor sit amet, consectetur elit. Vivamus facilisis porta mauris, finibus sem suscipit tincidunt."
|
||||
wrapMode: Text.Wrap
|
||||
font.pixelSize: 7
|
||||
}
|
||||
}
|
||||
},
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
KirigamiComponents.Avatar {
|
||||
color: "#9f244b"
|
||||
Layout.alignment: Qt.AlignTop
|
||||
visible: Config.showAvatarInTimeline
|
||||
Layout.preferredWidth: Config.showAvatarInTimeline ? Kirigami.Units.largeSpacing * 2 : 0
|
||||
Layout.preferredHeight: Kirigami.Units.largeSpacing * 2
|
||||
}
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
QQC2.Label {
|
||||
Layout.fillWidth: true
|
||||
font.weight: Font.Bold
|
||||
font.pixelSize: 7
|
||||
text: "Jean Paul"
|
||||
color: "#9f244b"
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
QQC2.Label {
|
||||
Layout.fillWidth: true
|
||||
text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus facilisis porta mauris, quis finibus sem suscipit tincidunt."
|
||||
wrapMode: Text.Wrap
|
||||
font.pixelSize: 7
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
text: i18n("Compact")
|
||||
checked: Config.compactLayout
|
||||
QQC2.ButtonGroup.group: themeGroup
|
||||
enabled: !Config.isCompactLayoutImmutable
|
||||
|
||||
onToggled: {
|
||||
Config.compactLayout = checked;
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormDelegateSeparator {
|
||||
below: compactRoomListDelegate
|
||||
}
|
||||
|
||||
FormCard.FormCheckDelegate {
|
||||
id: compactRoomListDelegate
|
||||
text: i18n("Use compact room list")
|
||||
checked: Config.compactRoomList
|
||||
onToggled: {
|
||||
Config.compactRoomList = checked;
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormDelegateSeparator {
|
||||
above: compactRoomListDelegate
|
||||
below: colorSchemeDelegate.item
|
||||
visible: colorSchemeDelegate.visible
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: colorSchemeDelegate
|
||||
visible: item !== null
|
||||
sourceComponent: Qt.createComponent('org.kde.neochat.settings', 'ColorScheme.qml')
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormCard {
|
||||
Layout.topMargin: Kirigami.Units.largeSpacing
|
||||
FormCard.FormCheckDelegate {
|
||||
id: showFancyEffectsDelegate
|
||||
text: i18n("Show fancy effects in chat")
|
||||
checked: Config.showFancyEffects
|
||||
enabled: !Config.isShowFancyEffectsImmutable
|
||||
onToggled: {
|
||||
Config.showFancyEffects = checked;
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormDelegateSeparator {
|
||||
above: showFancyEffectsDelegate
|
||||
below: hasWindowSystemDelegate
|
||||
}
|
||||
|
||||
FormCard.FormCheckDelegate {
|
||||
id: hasWindowSystemDelegate
|
||||
visible: WindowController.hasWindowSystem
|
||||
text: i18n("Use transparent chat page")
|
||||
enabled: !Config.compactLayout && !Config.isBlurImmutable
|
||||
checked: Config.blur
|
||||
onToggled: {
|
||||
Config.blur = checked;
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormDelegateSeparator {
|
||||
above: hasWindowSystemDelegate
|
||||
below: transparencyDelegate
|
||||
}
|
||||
|
||||
FormCard.AbstractFormDelegate {
|
||||
id: transparencyDelegate
|
||||
visible: WindowController.hasWindowSystem && Config.blur
|
||||
enabled: !Config.isTransparancyImmutable
|
||||
background: Item {}
|
||||
contentItem: ColumnLayout {
|
||||
QQC2.Label {
|
||||
text: i18n("Transparency")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
QQC2.Slider {
|
||||
enabled: !Config.compactLayout && Config.blur
|
||||
from: 0
|
||||
to: 1
|
||||
stepSize: 0.05
|
||||
value: Config.transparency
|
||||
onMoved: {
|
||||
Config.transparency = value;
|
||||
Config.save();
|
||||
}
|
||||
Layout.fillWidth: true
|
||||
|
||||
HoverHandler {
|
||||
id: sliderHover
|
||||
}
|
||||
QQC2.ToolTip.visible: sliderHover.hovered && !enabled
|
||||
QQC2.ToolTip.text: i18n("Only enabled if the transparent chat page is enabled.")
|
||||
}
|
||||
QQC2.Label {
|
||||
text: Math.round(Config.transparency * 100) + "%"
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormDelegateSeparator {
|
||||
above: transparencyDelegate
|
||||
below: showLocalMessagesOnRightDelegate
|
||||
visible: transparencyDelegate.visible
|
||||
}
|
||||
|
||||
FormCard.FormCheckDelegate {
|
||||
id: showLocalMessagesOnRightDelegate
|
||||
text: i18n("Show your messages on the right")
|
||||
checked: Config.showLocalMessagesOnRight
|
||||
enabled: !Config.isShowLocalMessagesOnRightImmutable && !Config.compactLayout
|
||||
onToggled: {
|
||||
Config.showLocalMessagesOnRight = checked;
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormDelegateSeparator {
|
||||
above: showLocalMessagesOnRightDelegate
|
||||
below: showLinkPreviewDelegate
|
||||
}
|
||||
|
||||
FormCard.FormCheckDelegate {
|
||||
id: showLinkPreviewDelegate
|
||||
text: i18n("Show links preview in the chat messages")
|
||||
checked: Config.showLinkPreview
|
||||
onToggled: {
|
||||
Config.showLinkPreview = checked;
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18n("Show Avatar")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
FormCard.FormCheckDelegate {
|
||||
text: i18n("In chat")
|
||||
checked: Config.showAvatarInTimeline
|
||||
onToggled: {
|
||||
Config.showAvatarInTimeline = checked;
|
||||
Config.save();
|
||||
}
|
||||
enabled: !Config.isShowAvatarInTimelineImmutable
|
||||
}
|
||||
|
||||
FormCard.FormCheckDelegate {
|
||||
text: i18n("In sidebar")
|
||||
checked: Config.showAvatarInRoomDrawer
|
||||
enabled: !Config.isShowAvatarInRoomDrawerImmutable
|
||||
onToggled: {
|
||||
Config.showAvatarInRoomDrawer = checked;
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
34
src/settings/CMakeLists.txt
Normal file
34
src/settings/CMakeLists.txt
Normal file
@@ -0,0 +1,34 @@
|
||||
# SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
qt_add_library(settings STATIC)
|
||||
qt_add_qml_module(settings
|
||||
URI org.kde.neochat.settings
|
||||
QML_FILES
|
||||
NeoChatSettings.qml
|
||||
RoomSettings.qml
|
||||
About.qml
|
||||
AboutKDE.qml
|
||||
AccountsPage.qml
|
||||
AccountEditorPage.qml
|
||||
AppearanceSettingsPage.qml
|
||||
DevicesPage.qml
|
||||
EmoticonsPage.qml
|
||||
EmoticonEditorPage.qml
|
||||
GlobalNotificationsPage.qml
|
||||
NeoChatGeneralPage.qml
|
||||
NeoChatSecurityPage.qml
|
||||
NetworkProxyPage.qml
|
||||
Permissions.qml
|
||||
PushNotification.qml
|
||||
RoomGeneralPage.qml
|
||||
RoomSecurityPage.qml
|
||||
SonnetConfigPage.qml
|
||||
ColorScheme.qml
|
||||
DevicesCard.qml
|
||||
DeviceDelegate.qml
|
||||
EmoticonFormCard.qml
|
||||
IgnoredUsersDialog.qml
|
||||
NotificationRuleItem.qml
|
||||
ThemeRadioButton.qml
|
||||
)
|
||||
25
src/settings/ColorScheme.qml
Normal file
25
src/settings/ColorScheme.qml
Normal file
@@ -0,0 +1,25 @@
|
||||
// SPDX-FileCopyrightText: 2020 Carl Schwan <carlschwan@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
|
||||
import org.kde.neochat
|
||||
import org.kde.neochat.config
|
||||
|
||||
FormCard.FormComboBoxDelegate {
|
||||
id: root
|
||||
|
||||
text: i18n("Color theme")
|
||||
textRole: "display"
|
||||
valueRole: "display"
|
||||
model: ColorSchemer.model
|
||||
Component.onCompleted: currentIndex = ColorSchemer.indexForScheme(Config.colorScheme)
|
||||
onCurrentValueChanged: {
|
||||
ColorSchemer.apply(currentIndex);
|
||||
Config.colorScheme = ColorSchemer.nameForIndex(currentIndex);
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
132
src/settings/DeviceDelegate.qml
Normal file
132
src/settings/DeviceDelegate.qml
Normal file
@@ -0,0 +1,132 @@
|
||||
// SPDX-FileCopyrightText: 2020 - 2023 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-FileCopyrightText: 2022 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
FormCard.AbstractFormDelegate {
|
||||
id: root
|
||||
|
||||
required property string id
|
||||
required property string timestamp
|
||||
required property string displayName
|
||||
|
||||
property bool editDeviceName: false
|
||||
property bool showVerifyButton
|
||||
|
||||
onClicked: root.editDeviceName = true
|
||||
|
||||
contentItem: RowLayout {
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
|
||||
Kirigami.Icon {
|
||||
source: "network-connect"
|
||||
implicitWidth: Kirigami.Units.iconSizes.medium
|
||||
implicitHeight: Kirigami.Units.iconSizes.medium
|
||||
}
|
||||
ColumnLayout {
|
||||
id: deviceLabel
|
||||
Layout.fillWidth: true
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
visible: !root.editDeviceName
|
||||
|
||||
QQC2.Label {
|
||||
Layout.fillWidth: true
|
||||
text: root.displayName
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.Wrap
|
||||
maximumLineCount: 2
|
||||
}
|
||||
|
||||
QQC2.Label {
|
||||
Layout.fillWidth: true
|
||||
text: i18nc("@label", "%1, Last activity: %2", root.id, root.timestamp)
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
font: Kirigami.Theme.smallFont
|
||||
elide: Text.ElideRight
|
||||
visible: text.length > 0
|
||||
}
|
||||
}
|
||||
Kirigami.ActionTextField {
|
||||
id: nameField
|
||||
Accessible.description: i18n("New device name")
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: deviceLabel.implicitHeight
|
||||
visible: root.editDeviceName
|
||||
|
||||
text: root.displayName
|
||||
|
||||
rightActions: [
|
||||
Kirigami.Action {
|
||||
text: i18n("Cancel editing display name")
|
||||
icon.name: "edit-delete-remove"
|
||||
onTriggered: {
|
||||
root.editDeviceName = false;
|
||||
}
|
||||
},
|
||||
Kirigami.Action {
|
||||
text: i18n("Confirm new display name")
|
||||
icon.name: "checkmark"
|
||||
visible: nameField.text !== root.displayName
|
||||
onTriggered: {
|
||||
devicesModel.setName(root.id, nameField.text);
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
onAccepted: devicesModel.setName(root.id, nameField.text)
|
||||
}
|
||||
QQC2.ToolButton {
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
action: Kirigami.Action {
|
||||
id: editDeviceAction
|
||||
text: i18n("Edit device name")
|
||||
icon.name: "document-edit"
|
||||
onTriggered: root.editDeviceName = true
|
||||
}
|
||||
QQC2.ToolTip {
|
||||
text: editDeviceAction.text
|
||||
delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
}
|
||||
QQC2.ToolButton {
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
visible: root.showVerifyButton
|
||||
action: Kirigami.Action {
|
||||
id: verifyDeviceAction
|
||||
text: i18n("Verify device")
|
||||
icon.name: "security-low-symbolic"
|
||||
onTriggered: {
|
||||
devicesModel.connection.startKeyVerificationSession(devicesModel.connection.localUserId, root.id);
|
||||
}
|
||||
}
|
||||
QQC2.ToolTip {
|
||||
text: verifyDeviceAction.text
|
||||
delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
}
|
||||
QQC2.ToolButton {
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
action: Kirigami.Action {
|
||||
id: logoutDeviceAction
|
||||
text: i18n("Logout device")
|
||||
icon.name: "edit-delete-remove"
|
||||
onTriggered: {
|
||||
passwordSheet.deviceId = root.id;
|
||||
passwordSheet.open();
|
||||
}
|
||||
}
|
||||
QQC2.ToolTip {
|
||||
text: logoutDeviceAction.text
|
||||
delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
46
src/settings/DevicesCard.qml
Normal file
46
src/settings/DevicesCard.qml
Normal file
@@ -0,0 +1,46 @@
|
||||
// SPDX-FileCopyrightText: 2020 - 2023 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-FileCopyrightText: 2022 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
required property string title
|
||||
required property var type
|
||||
required property bool showVerifyButton
|
||||
|
||||
visible: deviceRepeater.count > 0
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: root.title
|
||||
}
|
||||
|
||||
FormCard.FormCard {
|
||||
id: devicesCard
|
||||
|
||||
Repeater {
|
||||
id: deviceRepeater
|
||||
model: DevicesProxyModel {
|
||||
sourceModel: devicesModel
|
||||
type: root.type
|
||||
}
|
||||
|
||||
Kirigami.LoadingPlaceholder {
|
||||
visible: deviceModel.count === 0 // We can assume 0 means loading since there is at least one device
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
delegate: DeviceDelegate {
|
||||
showVerifyButton: root.showVerifyButton
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
97
src/settings/DevicesPage.qml
Normal file
97
src/settings/DevicesPage.qml
Normal file
@@ -0,0 +1,97 @@
|
||||
// SPDX-FileCopyrightText: 2020 - 2023 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-FileCopyrightText: 2022 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
FormCard.FormCardPage {
|
||||
id: root
|
||||
|
||||
title: i18n("Devices")
|
||||
|
||||
background: Kirigami.PlaceholderMessage {
|
||||
text: i18n("Loading…")
|
||||
visible: !thisDeviceCard.visible
|
||||
}
|
||||
|
||||
required property NeoChatConnection connection
|
||||
|
||||
property DevicesModel devicesModel: DevicesModel {
|
||||
id: devicesModel
|
||||
connection: root.connection
|
||||
}
|
||||
|
||||
DevicesCard {
|
||||
id: thisDeviceCard
|
||||
title: i18n("This Device")
|
||||
type: DevicesModel.This
|
||||
showVerifyButton: false
|
||||
}
|
||||
DevicesCard {
|
||||
title: i18n("Verified Devices")
|
||||
type: DevicesModel.Verified
|
||||
showVerifyButton: true
|
||||
}
|
||||
DevicesCard {
|
||||
title: i18n("Unverified Devices")
|
||||
type: DevicesModel.Unverified
|
||||
showVerifyButton: true
|
||||
}
|
||||
DevicesCard {
|
||||
title: i18n("Devices without Encryption Support")
|
||||
type: DevicesModel.Unencrypted
|
||||
showVerifyButton: false
|
||||
}
|
||||
|
||||
FormCard.AbstractFormDelegate {
|
||||
Layout.fillWidth: true
|
||||
visible: root.connection && devicesModel.count === 0 // We can assume 0 means loading since there is at least one device
|
||||
contentItem: Kirigami.LoadingPlaceholder {}
|
||||
}
|
||||
|
||||
Kirigami.InlineMessage {
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: Kirigami.Units.gridUnit * 30
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: i18n("Please login to view the signed-in devices for your account.")
|
||||
type: Kirigami.MessageType.Information
|
||||
visible: !root.connection
|
||||
}
|
||||
|
||||
property Kirigami.Dialog passwordSheet: Kirigami.Dialog {
|
||||
id: passwordSheet
|
||||
|
||||
property string deviceId
|
||||
|
||||
preferredWidth: Kirigami.Units.gridUnit * 24
|
||||
|
||||
title: i18n("Remove device")
|
||||
|
||||
standardButtons: QQC2.Dialog.Cancel
|
||||
FormCard.FormCard {
|
||||
FormCard.FormTextFieldDelegate {
|
||||
id: passwordField
|
||||
label: i18n("Password:")
|
||||
echoMode: TextInput.Password
|
||||
}
|
||||
}
|
||||
customFooterActions: [
|
||||
Kirigami.Action {
|
||||
text: i18nc("As in 'Remove this device'", "Remove")
|
||||
icon.name: "delete"
|
||||
onTriggered: {
|
||||
devicesModel.logout(passwordSheet.deviceId, passwordField.text);
|
||||
passwordField.text = "";
|
||||
passwordSheet.close();
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
137
src/settings/EmoticonEditorPage.qml
Normal file
137
src/settings/EmoticonEditorPage.qml
Normal file
@@ -0,0 +1,137 @@
|
||||
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
import Qt.labs.platform
|
||||
import QtQuick.Window
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
FormCard.FormCardPage {
|
||||
id: root
|
||||
|
||||
required property string description
|
||||
required property string index
|
||||
required property string url
|
||||
required property string shortcode
|
||||
required property var model
|
||||
required property var proxyModel
|
||||
property bool newEmoticon: false
|
||||
required property var emoticonType
|
||||
|
||||
title: emoticonType === EmoticonFormCard.Stickers ? (newEmoticon ? i18nc("@title", "Add Sticker") : i18nc("@title", "Edit Sticker")) : (newEmoticon ? i18nc("@title", "Add Emoji") : i18nc("@title", "Edit Emoji"))
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: emoticonType === EmoticonFormCard.Stickers ? i18n("Sticker") : i18n("Emoji")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
FormCard.AbstractFormDelegate {
|
||||
background: Item {}
|
||||
contentItem: RowLayout {
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
Image {
|
||||
id: image
|
||||
Layout.alignment: Qt.AlignRight
|
||||
source: root.url
|
||||
sourceSize.width: Kirigami.Units.gridUnit * 4
|
||||
sourceSize.height: Kirigami.Units.gridUnit * 4
|
||||
width: Kirigami.Units.gridUnit * 4
|
||||
height: Kirigami.Units.gridUnit * 4
|
||||
|
||||
Kirigami.Icon {
|
||||
source: emoticonType === EmoticonFormCard.Stickers ? "stickers" : "preferences-desktop-emoticons"
|
||||
anchors.fill: parent
|
||||
visible: parent.status !== Image.Ready
|
||||
}
|
||||
|
||||
QQC2.Button {
|
||||
icon.name: "edit-entry"
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
onClicked: mouseArea.clicked()
|
||||
text: image.source != "" ? i18n("Change Image") : i18n("Set Image")
|
||||
QQC2.ToolTip.text: text
|
||||
QQC2.ToolTip.visible: hovered
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
display: QQC2.Button.IconOnly
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
property var fileDialog: null
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onClicked: {
|
||||
if (fileDialog != null) {
|
||||
return;
|
||||
}
|
||||
fileDialog = openFileDialog.createObject(QQC2.ApplicationWindow.Overlay);
|
||||
fileDialog.chosen.connect(function (receivedSource) {
|
||||
mouseArea.fileDialog = null;
|
||||
if (!receivedSource) {
|
||||
return;
|
||||
}
|
||||
parent.source = receivedSource;
|
||||
});
|
||||
fileDialog.onRejected.connect(function () {
|
||||
mouseArea.fileDialog = null;
|
||||
});
|
||||
fileDialog.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
FormCard.FormTextFieldDelegate {
|
||||
id: shortcode
|
||||
label: i18n("Shortcode:")
|
||||
text: root.shortcode
|
||||
}
|
||||
FormCard.FormTextFieldDelegate {
|
||||
id: description
|
||||
label: i18n("Description:")
|
||||
text: root.description
|
||||
}
|
||||
FormCard.FormButtonDelegate {
|
||||
id: save
|
||||
text: i18n("Save")
|
||||
icon.name: "document-save"
|
||||
enabled: !root.newEmoticon || (image.source.toString().length > 0 && shortcode.text && description.text)
|
||||
onClicked: {
|
||||
if (root.newEmoticon) {
|
||||
model.addEmoticon(image.source, shortcode.text, description.text, emoticonType === EmoticonFormCard.Stickers ? "sticker" : "emoticon");
|
||||
} else {
|
||||
if (description.text !== root.description) {
|
||||
root.model.setEmoticonBody(proxyModel.mapToSource(proxyModel.index(model.index, 0)).row, description.text);
|
||||
}
|
||||
if (shortcode.text !== root.shortcode) {
|
||||
root.model.setEmoticonShortcode(proxyModel.mapToSource(proxyModel.index(model.index, 0)).row, shortcode.text);
|
||||
}
|
||||
if (image.source + "" !== root.url) {
|
||||
root.model.setEmoticonImage(proxyModel.mapToSource(proxyModel.index(model.index, 0)).row, image.source);
|
||||
}
|
||||
}
|
||||
root.closeDialog();
|
||||
}
|
||||
}
|
||||
}
|
||||
property Component openFileDialog: Component {
|
||||
id: openFileDialog
|
||||
|
||||
OpenFileDialog {
|
||||
currentFolder: StandardPaths.standardLocations(StandardPaths.PicturesLocation)[0]
|
||||
parentWindow: root.Window.window
|
||||
}
|
||||
}
|
||||
}
|
||||
119
src/settings/EmoticonFormCard.qml
Normal file
119
src/settings/EmoticonFormCard.qml
Normal file
@@ -0,0 +1,119 @@
|
||||
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
FormCard.FormCard {
|
||||
id: root
|
||||
|
||||
enum EmoticonType {
|
||||
Emojis,
|
||||
Stickers
|
||||
}
|
||||
|
||||
property var emoticonType
|
||||
required property NeoChatConnection connection
|
||||
|
||||
Flow {
|
||||
id: stickerFlow
|
||||
Layout.fillWidth: true
|
||||
Repeater {
|
||||
model: EmoticonFilterModel {
|
||||
id: emoticonFilterModel
|
||||
sourceModel: AccountEmoticonModel {
|
||||
id: stickerModel
|
||||
connection: root.connection
|
||||
}
|
||||
showStickers: root.emoticonType === EmoticonFormCard.Stickers
|
||||
showEmojis: root.emoticonType === EmoticonFormCard.Emojis
|
||||
}
|
||||
|
||||
delegate: FormCard.AbstractFormDelegate {
|
||||
id: stickerDelegate
|
||||
|
||||
width: stickerFlow.width / 4
|
||||
height: width
|
||||
|
||||
onClicked: pageStack.pushDialogLayer(emoticonEditorPage, {
|
||||
description: model.body ?? "",
|
||||
index: model.index,
|
||||
url: model.url,
|
||||
shortcode: model.shortcode,
|
||||
model: stickerModel,
|
||||
proxyModel: emoticonFilterModel,
|
||||
emoticonType: root.emoticonType
|
||||
}, {
|
||||
title: root.emoticonType === EmoticonFormCard.Emojis ? i18nc("@title", "Edit Emoji") : i18nc("@title", "Edit Sticker")
|
||||
})
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
Image {
|
||||
source: model.url
|
||||
Layout.fillWidth: true
|
||||
sourceSize.height: parent.width * 0.8
|
||||
fillMode: Image.PreserveAspectFit
|
||||
autoTransform: true
|
||||
Kirigami.Icon {
|
||||
source: root.emoticonType === EmoticonFormCard.Emojis ? "preferences-desktop-emoticons" : "stickers"
|
||||
anchors.fill: parent
|
||||
visible: parent.status !== Image.Ready
|
||||
}
|
||||
}
|
||||
QQC2.Label {
|
||||
id: descriptionLabel
|
||||
text: model.body ?? i18nc("As in 'This sticker/emoji has no description'", "No Description")
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.Wrap
|
||||
maximumLineCount: 2
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
QQC2.Button {
|
||||
icon.name: "edit-delete"
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.margins: Kirigami.Units.smallSpacing
|
||||
z: 2
|
||||
onClicked: stickerModel.deleteEmoticon(emoticonFilterModel.mapToSource(emoticonFilterModel.index(model.index, 0)).row)
|
||||
}
|
||||
}
|
||||
}
|
||||
FormCard.AbstractFormDelegate {
|
||||
width: stickerFlow.width / 4
|
||||
height: width
|
||||
|
||||
onClicked: pageStack.pushDialogLayer(emoticonEditorPage, {
|
||||
description: "",
|
||||
index: -1,
|
||||
url: "",
|
||||
shortcode: "",
|
||||
model: stickerModel,
|
||||
proxyModel: emoticonFilterModel,
|
||||
newEmoticon: true,
|
||||
emoticonType: root.emoticonType
|
||||
}, {
|
||||
title: root.emoticonType === EmoticonFormCard.Emojis ? i18nc("@title", "Add Emoji") : i18nc("@title", "Add Sticker")
|
||||
})
|
||||
contentItem: ColumnLayout {
|
||||
spacing: 0
|
||||
Kirigami.Icon {
|
||||
source: "list-add"
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
QQC2.Label {
|
||||
text: root.emoticonType === EmoticonFormCard.Emojis ? i18n("Add Emoji") : i18n("Add Sticker")
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
38
src/settings/EmoticonsPage.qml
Normal file
38
src/settings/EmoticonsPage.qml
Normal file
@@ -0,0 +1,38 @@
|
||||
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
FormCard.FormCardPage {
|
||||
id: root
|
||||
|
||||
required property NeoChatConnection connection
|
||||
|
||||
title: i18nc("@title", "Stickers & Emojis")
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18n("Emojis")
|
||||
}
|
||||
EmoticonFormCard {
|
||||
emoticonType: EmoticonFormCard.Emojis
|
||||
connection: root.connection
|
||||
}
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18n("Stickers")
|
||||
}
|
||||
EmoticonFormCard {
|
||||
emoticonType: EmoticonFormCard.Stickers
|
||||
connection: root.connection
|
||||
}
|
||||
|
||||
property Component emoticonEditorPage: Component {
|
||||
id: emoticonEditorPage
|
||||
EmoticonEditorPage {}
|
||||
}
|
||||
}
|
||||
183
src/settings/GlobalNotificationsPage.qml
Normal file
183
src/settings/GlobalNotificationsPage.qml
Normal file
@@ -0,0 +1,183 @@
|
||||
// SPDX-FileCopyrightText: 2022 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
import org.kde.kitemmodels
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
FormCard.FormCardPage {
|
||||
id: root
|
||||
|
||||
required property NeoChatConnection connection
|
||||
|
||||
title: i18nc("@title:window", "Notifications")
|
||||
|
||||
property PushRuleModel pushRuleModel: PushRuleModel {
|
||||
connection: root.connection
|
||||
}
|
||||
|
||||
FormCard.FormCard {
|
||||
Layout.topMargin: Kirigami.Units.largeSpacing
|
||||
FormCard.FormCheckDelegate {
|
||||
text: i18n("Enable notifications for this account")
|
||||
description: i18n("Whether push notifications are generated by your Matrix server")
|
||||
checked: root.pushRuleModel.globalNotificationsEnabled
|
||||
enabled: root.pushRuleModel.globalNotificationsSet
|
||||
onToggled: {
|
||||
root.pushRuleModel.globalNotificationsEnabled = checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18n("Room Notifications")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
Repeater {
|
||||
model: KSortFilterProxyModel {
|
||||
sourceModel: root.pushRuleModel
|
||||
filterRowCallback: function (source_row, source_parent) {
|
||||
let sectionRole = sourceModel.data(sourceModel.index(source_row, 0, source_parent), PushRuleModel.SectionRole);
|
||||
return sectionRole == PushRuleSection.Room;
|
||||
}
|
||||
}
|
||||
|
||||
delegate: ruleDelegate
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18n("@Mentions")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
Repeater {
|
||||
model: KSortFilterProxyModel {
|
||||
sourceModel: root.pushRuleModel
|
||||
filterRowCallback: function (source_row, source_parent) {
|
||||
let sectionRole = sourceModel.data(sourceModel.index(source_row, 0, source_parent), PushRuleModel.SectionRole);
|
||||
return sectionRole == PushRuleSection.Mentions;
|
||||
}
|
||||
}
|
||||
|
||||
delegate: ruleDelegate
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18n("Keywords")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
Repeater {
|
||||
model: KSortFilterProxyModel {
|
||||
sourceModel: root.pushRuleModel
|
||||
|
||||
filterRowCallback: function (source_row, source_parent) {
|
||||
let sectionRole = sourceModel.data(sourceModel.index(source_row, 0, source_parent), PushRuleModel.SectionRole);
|
||||
return sectionRole == PushRuleSection.Keywords;
|
||||
}
|
||||
}
|
||||
|
||||
delegate: ruleDelegate
|
||||
}
|
||||
FormCard.AbstractFormDelegate {
|
||||
Layout.fillWidth: true
|
||||
|
||||
contentItem: RowLayout {
|
||||
Kirigami.ActionTextField {
|
||||
id: keywordAddField
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
placeholderText: i18n("Keyword…")
|
||||
enabled: NotificationsManager.keywordNotificationAction !== PushRuleAction.Unknown
|
||||
|
||||
rightActions: Kirigami.Action {
|
||||
icon.name: "edit-clear"
|
||||
visible: keywordAddField.text.length > 0
|
||||
onTriggered: {
|
||||
keywordAddField.text = "";
|
||||
}
|
||||
}
|
||||
|
||||
onAccepted: {
|
||||
root.pushRuleModel.addKeyword(keywordAddField.text);
|
||||
keywordAddField.text = "";
|
||||
}
|
||||
}
|
||||
QQC2.Button {
|
||||
id: addButton
|
||||
|
||||
text: i18n("Add keyword")
|
||||
Accessible.name: text
|
||||
icon.name: "list-add"
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
enabled: NotificationsManager.keywordNotificationAction !== PushRuleAction.Unknown
|
||||
|
||||
onClicked: {
|
||||
root.pushRuleModel.addKeyword(keywordAddField.text);
|
||||
keywordAddField.text = "";
|
||||
}
|
||||
|
||||
QQC2.ToolTip {
|
||||
text: addButton.text
|
||||
delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18n("Invites")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
Repeater {
|
||||
model: KSortFilterProxyModel {
|
||||
sourceModel: root.pushRuleModel
|
||||
filterRowCallback: function (source_row, source_parent) {
|
||||
let sectionRole = sourceModel.data(sourceModel.index(source_row, 0, source_parent), PushRuleModel.SectionRole);
|
||||
return sectionRole == PushRuleSection.Invites;
|
||||
}
|
||||
}
|
||||
|
||||
delegate: ruleDelegate
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18n("Unknown")
|
||||
visible: unknownModel.rowCount() > 0
|
||||
}
|
||||
FormCard.FormCard {
|
||||
visible: unknownModel.rowCount() > 0
|
||||
|
||||
Repeater {
|
||||
model: KSortFilterProxyModel {
|
||||
id: unknownModel
|
||||
sourceModel: root.pushRuleModel
|
||||
filterRowCallback: function (source_row, source_parent) {
|
||||
let sectionRole = sourceModel.data(sourceModel.index(source_row, 0, source_parent), PushRuleModel.SectionRole);
|
||||
return sectionRole == PushRuleSection.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
delegate: ruleDelegate
|
||||
}
|
||||
}
|
||||
|
||||
property Component ruleDelegate: Component {
|
||||
id: ruleDelegate
|
||||
NotificationRuleItem {
|
||||
onDeleteRule: {
|
||||
root.pushRuleModel.removeKeyword(id);
|
||||
}
|
||||
onActionChanged: action => root.pushRuleModel.setPushRuleAction(id, action)
|
||||
}
|
||||
}
|
||||
}
|
||||
71
src/settings/IgnoredUsersDialog.qml
Normal file
71
src/settings/IgnoredUsersDialog.qml
Normal file
@@ -0,0 +1,71 @@
|
||||
// SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
FormCard.FormCardPage {
|
||||
id: root
|
||||
|
||||
required property NeoChatConnection connection
|
||||
|
||||
title: i18nc("@title", "Ignored Users")
|
||||
|
||||
width: Kirigami.Units.gridUnit * 16
|
||||
height: Kirigami.Units.gridUnit * 32
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18nc("@title:group", "Ignored Users")
|
||||
}
|
||||
|
||||
FormCard.FormCard {
|
||||
FormCard.FormTextDelegate {
|
||||
text: i18nc("Placeholder message when no user is ignored", "You are not ignoring any users")
|
||||
visible: repeater.count === 0
|
||||
}
|
||||
Repeater {
|
||||
id: repeater
|
||||
model: root.connection.ignoredUsers()
|
||||
delegate: FormCard.AbstractFormDelegate {
|
||||
topPadding: Kirigami.Units.smallSpacing
|
||||
bottomPadding: Kirigami.Units.smallSpacing
|
||||
|
||||
background: null
|
||||
contentItem: RowLayout {
|
||||
spacing: 0
|
||||
|
||||
QQC2.Label {
|
||||
Layout.fillWidth: true
|
||||
text: modelData
|
||||
elide: Text.ElideRight
|
||||
Accessible.ignored: true // base class sets this text on root already
|
||||
}
|
||||
|
||||
QQC2.ToolButton {
|
||||
text: i18nc("@action:button", "Unignore this user")
|
||||
icon.name: "list-remove-symbolic"
|
||||
onClicked: root.connection.removeFromIgnoredUsers(root.connection.user(modelData))
|
||||
display: QQC2.Button.IconOnly
|
||||
QQC2.ToolTip.text: text
|
||||
QQC2.ToolTip.visible: hovered
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.connection
|
||||
function onIgnoredUsersListChanged() {
|
||||
repeater.model = root.connection.ignoredUsers();
|
||||
}
|
||||
}
|
||||
}
|
||||
219
src/settings/NeoChatGeneralPage.qml
Normal file
219
src/settings/NeoChatGeneralPage.qml
Normal file
@@ -0,0 +1,219 @@
|
||||
// SPDX-FileCopyrightText: 2020 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-FileCopyrightText: 2021 Carl Schwan <carl@carlschwan.eu>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
|
||||
import org.kde.neochat
|
||||
import org.kde.neochat.config
|
||||
|
||||
FormCard.FormCardPage {
|
||||
title: i18nc("@title:window", "General")
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18n("General settings")
|
||||
visible: Qt.platform.os !== "android"
|
||||
}
|
||||
FormCard.FormCard {
|
||||
FormCard.FormCheckDelegate {
|
||||
id: closeDelegate
|
||||
text: i18n("Show in System Tray")
|
||||
checked: Config.systemTray
|
||||
visible: Controller.supportSystemTray
|
||||
enabled: !Config.isSystemTrayImmutable
|
||||
onToggled: {
|
||||
Config.systemTray = checked;
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormDelegateSeparator {
|
||||
above: closeDelegate
|
||||
below: minimizeDelegate
|
||||
}
|
||||
|
||||
FormCard.FormCheckDelegate {
|
||||
id: minimizeDelegate
|
||||
text: i18n("Minimize to system tray on startup")
|
||||
checked: Config.minimizeToSystemTrayOnStartup
|
||||
visible: Controller.supportSystemTray && !Kirigami.Settings.isMobile
|
||||
enabled: Config.systemTray && !Config.isMinimizeToSystemTrayOnStartupImmutable
|
||||
onToggled: {
|
||||
Config.minimizeToSystemTrayOnStartup = checked;
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormDelegateSeparator {
|
||||
above: minimizeDelegate
|
||||
below: automaticallyDelegate
|
||||
}
|
||||
|
||||
FormCard.FormCheckDelegate {
|
||||
id: automaticallyDelegate
|
||||
text: i18n("Automatically hide/unhide the room information when resizing the window")
|
||||
checked: Config.autoRoomInfoDrawer
|
||||
enabled: !Config.isAutoRoomInfoDrawerImmutable
|
||||
visible: Qt.platform.os !== "android"
|
||||
onToggled: {
|
||||
Config.autoRoomInfoDrawer = checked;
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
FormCard.FormHeader {
|
||||
title: i18n("Room list sort order")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
FormCard.FormRadioDelegate {
|
||||
text: i18nc("As in 'sort something based on last activity'", "Activity")
|
||||
checked: Config.sortOrder === 1
|
||||
enabled: !Config.isSortOrderImmutable
|
||||
onToggled: {
|
||||
Config.sortOrder = 1
|
||||
Config.save()
|
||||
}
|
||||
}
|
||||
FormCard.FormRadioDelegate {
|
||||
text: i18nc("As in 'sort something alphabetically'", "Alphabetical")
|
||||
checked: Config.sortOrder === 0
|
||||
enabled: !Config.isSortOrderImmutable
|
||||
onToggled: {
|
||||
Config.sortOrder = 0
|
||||
Config.save()
|
||||
}
|
||||
}
|
||||
}
|
||||
FormCard.FormHeader {
|
||||
title: i18n("Timeline Events")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
FormCard.FormCheckDelegate {
|
||||
id: showDeletedMessages
|
||||
text: i18n("Show deleted messages")
|
||||
checked: Config.showDeletedMessages
|
||||
enabled: !Config.isShowDeletedMessagesImmutable
|
||||
onToggled: {
|
||||
Config.showDeletedMessages = checked;
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormDelegateSeparator {
|
||||
above: showDeletedMessages
|
||||
below: showStateEvents
|
||||
}
|
||||
|
||||
FormCard.FormCheckDelegate {
|
||||
id: showStateEvents
|
||||
text: i18n("Show state events")
|
||||
checked: Config.showStateEvent
|
||||
enabled: !Config.isShowStateEventImmutable
|
||||
onToggled: {
|
||||
Config.showStateEvent = checked;
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormDelegateSeparator {
|
||||
visible: Config.showStateEvent
|
||||
above: showStateEvents
|
||||
below: showLeaveJoinEventDelegate
|
||||
}
|
||||
|
||||
FormCard.FormCheckDelegate {
|
||||
id: showLeaveJoinEventDelegate
|
||||
visible: Config.showStateEvent
|
||||
text: i18n("Show leave and join events")
|
||||
checked: Config.showLeaveJoinEvent
|
||||
enabled: !Config.isShowLeaveJoinEventImmutable
|
||||
onToggled: {
|
||||
Config.showLeaveJoinEvent = checked;
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormDelegateSeparator {
|
||||
visible: Config.showStateEvent
|
||||
above: showLeaveJoinEventDelegate
|
||||
below: showNameDelegate
|
||||
}
|
||||
|
||||
FormCard.FormCheckDelegate {
|
||||
id: showNameDelegate
|
||||
visible: Config.showStateEvent
|
||||
text: i18n("Show name change events")
|
||||
checked: Config.showRename
|
||||
enabled: !Config.isShowRenameImmutable
|
||||
onToggled: {
|
||||
Config.showRename = checked;
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormDelegateSeparator {
|
||||
visible: Config.showStateEvent
|
||||
above: showNameDelegate
|
||||
below: showAvatarChangeDelegate
|
||||
}
|
||||
|
||||
FormCard.FormCheckDelegate {
|
||||
id: showAvatarChangeDelegate
|
||||
visible: Config.showStateEvent
|
||||
text: i18n("Show avatar update events")
|
||||
checked: Config.showAvatarUpdate
|
||||
enabled: !Config.isShowAvatarUpdateImmutable
|
||||
onToggled: {
|
||||
Config.showAvatarUpdate = checked;
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
FormCard.FormHeader {
|
||||
title: i18nc("Chat Editor", "Editor")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
FormCard.FormCheckDelegate {
|
||||
id: quickEditCheckbox
|
||||
text: i18n("Use s/text/replacement syntax to edit your last message")
|
||||
checked: Config.allowQuickEdit
|
||||
enabled: !Config.isAllowQuickEditImmutable
|
||||
onToggled: {
|
||||
Config.allowQuickEdit = checked;
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
FormCard.FormDelegateSeparator {
|
||||
above: quickEditCheckbox
|
||||
below: typingNotificationsDelegate
|
||||
}
|
||||
FormCard.FormCheckDelegate {
|
||||
id: typingNotificationsDelegate
|
||||
text: i18n("Send typing notifications")
|
||||
checked: Config.typingNotifications
|
||||
enabled: !Config.isTypingNotificationsImmutable
|
||||
onToggled: {
|
||||
Config.typingNotifications = checked;
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
FormCard.FormHeader {
|
||||
title: i18n("Developer Settings")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
FormCard.FormCheckDelegate {
|
||||
text: i18n("Enable developer tools")
|
||||
checked: Config.developerTools
|
||||
enabled: !Config.isDeveloperToolsImmutable
|
||||
onToggled: {
|
||||
Config.developerTools = checked;
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
54
src/settings/NeoChatSecurityPage.qml
Normal file
54
src/settings/NeoChatSecurityPage.qml
Normal file
@@ -0,0 +1,54 @@
|
||||
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
FormCard.FormCardPage {
|
||||
id: root
|
||||
|
||||
required property NeoChatConnection connection
|
||||
|
||||
title: i18nc("@title", "Security")
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18nc("@title", "Keys")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
FormCard.FormTextDelegate {
|
||||
text: connection.deviceKey
|
||||
description: i18n("Device key")
|
||||
}
|
||||
FormCard.FormTextDelegate {
|
||||
text: connection.encryptionKey
|
||||
description: i18n("Encryption key")
|
||||
}
|
||||
FormCard.FormTextDelegate {
|
||||
text: connection.deviceId
|
||||
description: i18n("Device id")
|
||||
}
|
||||
}
|
||||
FormCard.FormHeader {
|
||||
title: i18nc("@title:group", "Ignored Users")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
FormCard.FormButtonDelegate {
|
||||
text: i18nc("@action:button", "Manage ignored users")
|
||||
onClicked: pageStack.pushDialogLayer(ignoredUsersDialogComponent, {}, {
|
||||
title: i18nc("@title:window", "Ignored Users")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: ignoredUsersDialogComponent
|
||||
IgnoredUsersDialog {
|
||||
connection: root.connection
|
||||
}
|
||||
}
|
||||
}
|
||||
106
src/settings/NeoChatSettings.qml
Normal file
106
src/settings/NeoChatSettings.qml
Normal file
@@ -0,0 +1,106 @@
|
||||
// SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.settings as KirigamiSettings
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
KirigamiSettings.CategorizedSettings {
|
||||
id: root
|
||||
|
||||
property NeoChatConnection connection
|
||||
|
||||
objectName: "settingsPage"
|
||||
actions: [
|
||||
KirigamiSettings.SettingAction {
|
||||
actionName: "general"
|
||||
text: i18n("General")
|
||||
icon.name: "org.kde.neochat"
|
||||
page: Qt.resolvedUrl("NeoChatGeneralPage.qml")
|
||||
},
|
||||
KirigamiSettings.SettingAction {
|
||||
actionName: "appearance"
|
||||
text: i18n("Appearance")
|
||||
icon.name: "preferences-desktop-theme-global"
|
||||
page: Qt.resolvedUrl("AppearanceSettingsPage.qml")
|
||||
},
|
||||
KirigamiSettings.SettingAction {
|
||||
actionName: "notifications"
|
||||
text: i18n("Notifications")
|
||||
icon.name: "preferences-desktop-notification"
|
||||
page: Qt.resolvedUrl("GlobalNotificationsPage.qml")
|
||||
initialProperties: {
|
||||
return {
|
||||
connection: root.connection
|
||||
};
|
||||
}
|
||||
},
|
||||
KirigamiSettings.SettingAction {
|
||||
actionName: "security"
|
||||
text: i18n("Security")
|
||||
icon.name: "preferences-security"
|
||||
page: Qt.resolvedUrl("NeoChatSecurityPage.qml")
|
||||
initialProperties: {
|
||||
return {
|
||||
connection: root.connection
|
||||
};
|
||||
}
|
||||
},
|
||||
KirigamiSettings.SettingAction {
|
||||
actionName: "accounts"
|
||||
text: i18n("Accounts")
|
||||
icon.name: "preferences-system-users"
|
||||
page: Qt.resolvedUrl("AccountsPage.qml")
|
||||
},
|
||||
KirigamiSettings.SettingAction {
|
||||
actionName: "emoticons"
|
||||
text: i18n("Stickers & Emojis")
|
||||
icon.name: "preferences-desktop-emoticons"
|
||||
page: Qt.resolvedUrl("EmoticonsPage.qml")
|
||||
initialProperties: {
|
||||
return {
|
||||
connection: root.connection
|
||||
};
|
||||
}
|
||||
},
|
||||
KirigamiSettings.SettingAction {
|
||||
actionName: "spellChecking"
|
||||
text: i18n("Spell Checking")
|
||||
icon.name: "tools-check-spelling"
|
||||
page: Qt.resolvedUrl("SonnetConfigPage.qml")
|
||||
visible: Qt.platform.os !== "android"
|
||||
},
|
||||
KirigamiSettings.SettingAction {
|
||||
actionName: "networkProxy"
|
||||
text: i18n("Network Proxy")
|
||||
icon.name: "network-connect"
|
||||
page: Qt.resolvedUrl("NetworkProxyPage.qml")
|
||||
},
|
||||
KirigamiSettings.SettingAction {
|
||||
actionName: "devices"
|
||||
text: i18n("Devices")
|
||||
icon.name: "computer"
|
||||
page: Qt.resolvedUrl("DevicesPage.qml")
|
||||
initialProperties: {
|
||||
return {
|
||||
connection: root.connection
|
||||
};
|
||||
}
|
||||
},
|
||||
KirigamiSettings.SettingAction {
|
||||
actionName: "aboutNeochat"
|
||||
text: i18n("About NeoChat")
|
||||
icon.name: "help-about"
|
||||
page: Qt.resolvedUrl("About.qml")
|
||||
},
|
||||
KirigamiSettings.SettingAction {
|
||||
actionName: "aboutKDE"
|
||||
text: i18n("About KDE")
|
||||
icon.name: "kde"
|
||||
page: Qt.resolvedUrl("AboutKDE.qml")
|
||||
}
|
||||
]
|
||||
}
|
||||
124
src/settings/NetworkProxyPage.qml
Normal file
124
src/settings/NetworkProxyPage.qml
Normal file
@@ -0,0 +1,124 @@
|
||||
// SPDX-FileCopyrightText: 2022 Gary Wang <wzc782970009@gmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later OR LicenseRef-KDE-Accepted-LGPL
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
|
||||
import org.kde.neochat.config
|
||||
|
||||
FormCard.FormCardPage {
|
||||
id: root
|
||||
|
||||
title: i18nc("@title:window", "Proxy")
|
||||
|
||||
property int currentType
|
||||
property bool proxyConfigChanged: false
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18n("Network Proxy")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
FormCard.FormRadioDelegate {
|
||||
text: i18n("System Default")
|
||||
checked: currentType === 0
|
||||
enabled: !Config.isProxyTypeImmutable
|
||||
onToggled: {
|
||||
currentType = 0;
|
||||
}
|
||||
}
|
||||
FormCard.FormRadioDelegate {
|
||||
text: i18n("HTTP")
|
||||
checked: currentType === 1
|
||||
enabled: !Config.isProxyTypeImmutable
|
||||
onToggled: {
|
||||
currentType = 1;
|
||||
}
|
||||
}
|
||||
FormCard.FormRadioDelegate {
|
||||
text: i18n("Socks5")
|
||||
checked: currentType === 2
|
||||
enabled: !Config.isProxyTypeImmutable
|
||||
onToggled: {
|
||||
currentType = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18n("Proxy Settings")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
FormCard.FormTextFieldDelegate {
|
||||
id: hostField
|
||||
label: i18n("Host")
|
||||
text: Config.proxyHost
|
||||
inputMethodHints: Qt.ImhUrlCharactersOnly
|
||||
onEditingFinished: {
|
||||
proxyConfigChanged = true;
|
||||
}
|
||||
}
|
||||
FormCard.FormSpinBoxDelegate {
|
||||
id: portField
|
||||
label: i18n("Port")
|
||||
value: Config.proxyPort
|
||||
from: 0
|
||||
to: 65536
|
||||
textFromValue: function (value, locale) {
|
||||
return value; // it will add a thousands separator if we don't do this, not sure why
|
||||
}
|
||||
onValueChanged: {
|
||||
proxyConfigChanged = true;
|
||||
}
|
||||
}
|
||||
FormCard.FormTextFieldDelegate {
|
||||
id: userField
|
||||
label: i18n("User")
|
||||
text: Config.proxyUser
|
||||
inputMethodHints: Qt.ImhUrlCharactersOnly
|
||||
onEditingFinished: {
|
||||
proxyConfigChanged = true;
|
||||
}
|
||||
}
|
||||
FormCard.FormTextFieldDelegate {
|
||||
id: passwordField
|
||||
label: i18n("Password")
|
||||
text: Config.proxyPassword
|
||||
echoMode: TextInput.Password
|
||||
inputMethodHints: Qt.ImhUrlCharactersOnly
|
||||
onEditingFinished: {
|
||||
proxyConfigChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer: QQC2.ToolBar {
|
||||
height: visible ? implicitHeight : 0
|
||||
contentItem: RowLayout {
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
QQC2.Button {
|
||||
text: i18n("Apply")
|
||||
enabled: currentType !== Config.proxyType || proxyConfigChanged
|
||||
onClicked: {
|
||||
Config.proxyType = currentType;
|
||||
Config.proxyHost = hostField.text;
|
||||
Config.proxyPort = portField.value;
|
||||
Config.proxyUser = userField.text;
|
||||
Config.proxyPassword = passwordField.text;
|
||||
Config.save();
|
||||
proxyConfigChanged = false;
|
||||
ProxyController.setApplicationProxy();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
currentType = Config.proxyType;
|
||||
}
|
||||
}
|
||||
195
src/settings/NotificationRuleItem.qml
Normal file
195
src/settings/NotificationRuleItem.qml
Normal file
@@ -0,0 +1,195 @@
|
||||
// SPDX-FileCopyrightText: 2022 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
FormCard.AbstractFormDelegate {
|
||||
id: root
|
||||
|
||||
required property string id
|
||||
required property string name
|
||||
required property int ruleAction
|
||||
required property bool highlightable
|
||||
required property bool deletable
|
||||
|
||||
readonly property bool notificationsOn: isNotificationRuleOn(ruleAction)
|
||||
readonly property bool notificationsOnModifiable: !deletable
|
||||
readonly property bool highlightOn: isNotificationRuleHighlight(ruleAction)
|
||||
|
||||
signal actionChanged(int action)
|
||||
signal deleteRule
|
||||
|
||||
enabled: ruleAction !== PushRuleAction.Unknown
|
||||
|
||||
text: name
|
||||
|
||||
onClicked: {
|
||||
notificationAction = nextNotificationRuleAction(notificationAction);
|
||||
}
|
||||
|
||||
contentItem: RowLayout {
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
|
||||
QQC2.Label {
|
||||
Layout.minimumWidth: Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing
|
||||
Layout.minimumHeight: Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing
|
||||
|
||||
text: notificationsOn ? "" : "●"
|
||||
color: Kirigami.Theme.textColor
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
background: Rectangle {
|
||||
visible: notificationsOn
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.Button
|
||||
color: highlightOn && highlightable ? Kirigami.Theme.positiveTextColor : Kirigami.Theme.disabledTextColor
|
||||
opacity: highlightOn && highlightable ? 1 : 0.3
|
||||
radius: height / 2
|
||||
}
|
||||
}
|
||||
QQC2.Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
|
||||
text: root.text
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.Wrap
|
||||
maximumLineCount: 2
|
||||
}
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
|
||||
QQC2.Button {
|
||||
id: onButton
|
||||
|
||||
text: onButton.checked ? i18n("Disable notifications") : i18n("Enable notifications")
|
||||
Accessible.name: text
|
||||
icon.name: checked ? "notifications" : "notifications-disabled"
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
|
||||
visible: root.notificationsOnModifiable
|
||||
checkable: true
|
||||
checked: root.notificationsOn
|
||||
enabled: root.enabled
|
||||
down: checked
|
||||
onToggled: {
|
||||
root.actionChanged(root.notifcationRuleAction());
|
||||
}
|
||||
|
||||
QQC2.ToolTip {
|
||||
text: onButton.text
|
||||
delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
}
|
||||
QQC2.Button {
|
||||
id: noisyButton
|
||||
|
||||
text: noisyButton.checked ? i18n("Mute notifications") : i18n("Unmute notifications")
|
||||
Accessible.name: text
|
||||
icon.name: checked ? "audio-volume-high" : "audio-volume-muted"
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
|
||||
checkable: true
|
||||
checked: isNotificationRuleNoisy(root.ruleAction)
|
||||
enabled: (onButton.checked || !root.notificationsOnModifiable) && root.enabled
|
||||
down: checked
|
||||
onToggled: {
|
||||
root.actionChanged(root.notifcationRuleAction());
|
||||
}
|
||||
|
||||
QQC2.ToolTip {
|
||||
text: noisyButton.text
|
||||
delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
}
|
||||
QQC2.Button {
|
||||
id: highlightButton
|
||||
|
||||
text: highlightButton.checked ? i18nc("As in clicking this button will switch off highlights for messages that match this rule", "Disable message highlights") : i18nc("As in clicking this button will switch on highlights for messages that match this rule", "Enable message highlights")
|
||||
Accessible.name: text
|
||||
icon.name: "draw-highlight"
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
|
||||
visible: root.highlightable
|
||||
checkable: true
|
||||
checked: root.highlightOn
|
||||
enabled: (onButton.checked || !root.notificationsOnModifiable) && root.enabled
|
||||
down: checked
|
||||
onToggled: {
|
||||
root.actionChanged(root.notifcationRuleAction());
|
||||
}
|
||||
|
||||
QQC2.ToolTip {
|
||||
text: highlightButton.text
|
||||
delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
}
|
||||
QQC2.Button {
|
||||
id: deleteButton
|
||||
|
||||
Accessible.name: i18n("Delete keyword")
|
||||
icon.name: "edit-delete-remove"
|
||||
|
||||
visible: root.deletable
|
||||
|
||||
onClicked: {
|
||||
root.deleteRule();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function notifcationRuleAction() {
|
||||
if (onButton.checked) {
|
||||
if (noisyButton.checked && highlightButton.checked && root.highlightable) {
|
||||
return PushRuleAction.NoisyHighlight;
|
||||
} else if (noisyButton.checked) {
|
||||
return PushRuleAction.Noisy;
|
||||
} else if (highlightButton.checked && root.highlightable) {
|
||||
return PushRuleAction.Highlight;
|
||||
} else {
|
||||
return PushRuleAction.On;
|
||||
}
|
||||
} else {
|
||||
return PushRuleAction.Off;
|
||||
}
|
||||
}
|
||||
|
||||
function nextNotificationRuleAction(action) {
|
||||
let finished = false;
|
||||
if (action == PushRuleAction.NoisyHighlight) {
|
||||
action = PushRuleAction.Off;
|
||||
} else {
|
||||
action += 1;
|
||||
}
|
||||
while (!finished) {
|
||||
if (action == PushRuleAction.Off && !root.notificationsOnModifiable) {
|
||||
action = PushRuleAction.On;
|
||||
} else if (action == PushRuleAction.Noisy) {
|
||||
action = PushRuleAction.Highlight;
|
||||
} else if (action == PushRuleAction.Highlight && !root.highlightable) {
|
||||
action = PushRuleAction.Off;
|
||||
} else {
|
||||
finished = true;
|
||||
}
|
||||
}
|
||||
actionChanged(action);
|
||||
}
|
||||
|
||||
function isNotificationRuleOn(action) {
|
||||
return action == PushRuleAction.On || action == PushRuleAction.Noisy || action == PushRuleAction.Highlight || action == PushRuleAction.NoisyHighlight;
|
||||
}
|
||||
|
||||
function isNotificationRuleNoisy(action) {
|
||||
return action == PushRuleAction.Noisy || action == PushRuleAction.NoisyHighlight;
|
||||
}
|
||||
|
||||
function isNotificationRuleHighlight(action) {
|
||||
return action == PushRuleAction.Highlight || action == PushRuleAction.NoisyHighlight;
|
||||
}
|
||||
}
|
||||
469
src/settings/Permissions.qml
Normal file
469
src/settings/Permissions.qml
Normal file
@@ -0,0 +1,469 @@
|
||||
// SPDX-FileCopyrightText: 2022 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
import org.kde.kirigamiaddons.delegates as Delegates
|
||||
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
|
||||
import org.kde.kitemmodels
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
FormCard.FormCardPage {
|
||||
id: root
|
||||
|
||||
property NeoChatRoom room
|
||||
|
||||
title: i18nc('@title:window', 'Permissions')
|
||||
|
||||
property UserListModel userListModel: UserListModel {
|
||||
id: userListModel
|
||||
room: root.room
|
||||
}
|
||||
|
||||
property ListModel powerLevelModel: ListModel {
|
||||
id: powerLevelModel
|
||||
}
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18n("Privileged Users")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
Repeater {
|
||||
model: KSortFilterProxyModel {
|
||||
sourceModel: userListModel
|
||||
sortRoleName: "powerLevel"
|
||||
sortOrder: Qt.DescendingOrder
|
||||
filterRowCallback: function (source_row, source_parent) {
|
||||
let powerLevelRole = sourceModel.data(sourceModel.index(source_row, 0, source_parent), UserListModel.PowerLevelRole);
|
||||
return powerLevelRole > 0;
|
||||
}
|
||||
}
|
||||
delegate: FormCard.FormTextDelegate {
|
||||
text: name
|
||||
textItem.textFormat: Text.PlainText
|
||||
description: userId
|
||||
contentItem.children: RowLayout {
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
QQC2.Label {
|
||||
id: powerLevelLabel
|
||||
visible: !room.canSendState("m.room.power_levels") || (room.getUserPowerLevel(room.localUser.id) <= model.powerLevel && model.userId != room.localUser.id)
|
||||
text: powerLevelString
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
}
|
||||
QQC2.ComboBox {
|
||||
focusPolicy: Qt.NoFocus // provided by parent
|
||||
model: powerLevelModel
|
||||
textRole: "text"
|
||||
valueRole: "powerLevel"
|
||||
visible: !powerLevelLabel.visible
|
||||
Component.onCompleted: {
|
||||
/**
|
||||
* This is very silly but the only way to populate the model with
|
||||
* translated strings. Done here because the model needs to be filled
|
||||
* before the first delegate sets it's current index.
|
||||
*/
|
||||
if (powerLevelModel.count == 0) {
|
||||
powerLevelModel.append({
|
||||
"text": i18n("Member (0)"),
|
||||
"powerLevel": 0
|
||||
});
|
||||
powerLevelModel.append({
|
||||
"text": i18n("Moderator (50)"),
|
||||
"powerLevel": 50
|
||||
});
|
||||
powerLevelModel.append({
|
||||
"text": i18n("Admin (100)"),
|
||||
"powerLevel": 100
|
||||
});
|
||||
}
|
||||
currentIndex = indexOfValue(powerLevel);
|
||||
}
|
||||
onActivated: {
|
||||
room.setUserPowerLevel(userId, currentValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
FormCard.FormDelegateSeparator {
|
||||
below: userListSearchCard
|
||||
}
|
||||
FormCard.AbstractFormDelegate {
|
||||
id: userListSearchCard
|
||||
visible: room.canSendState("m.room.power_levels")
|
||||
|
||||
contentItem: Kirigami.SearchField {
|
||||
id: userListSearchField
|
||||
|
||||
autoAccept: false
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
Keys.onUpPressed: userListView.decrementCurrentIndex()
|
||||
Keys.onDownPressed: userListView.incrementCurrentIndex()
|
||||
|
||||
onAccepted: {
|
||||
let currentUser = userListView.itemAtIndex(userListView.currentIndex);
|
||||
currentUser.action.trigger();
|
||||
}
|
||||
}
|
||||
QQC2.Popup {
|
||||
id: userListSearchPopup
|
||||
|
||||
x: userListSearchField.x
|
||||
y: userListSearchField.y - height
|
||||
width: userListSearchField.width
|
||||
height: {
|
||||
let maxHeight = userListSearchField.mapToGlobal(userListSearchField.x, userListSearchField.y).y - Kirigami.Units.largeSpacing * 3;
|
||||
let minHeight = Kirigami.Units.gridUnit * 2 + userListSearchPopup.padding * 2;
|
||||
let filterContentHeight = userListView.contentHeight + userListSearchPopup.padding * 2;
|
||||
return Math.max(Math.min(filterContentHeight, maxHeight), minHeight);
|
||||
}
|
||||
padding: Kirigami.Units.smallSpacing
|
||||
leftPadding: Kirigami.Units.smallSpacing / 2
|
||||
rightPadding: Kirigami.Units.smallSpacing / 2
|
||||
modal: false
|
||||
onClosed: userListSearchField.text = ""
|
||||
|
||||
background: Kirigami.ShadowedRectangle {
|
||||
property color borderColor: Kirigami.Theme.textColor
|
||||
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
Kirigami.Theme.inherit: false
|
||||
|
||||
radius: 4
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
|
||||
border {
|
||||
color: Qt.rgba(borderColor.r, borderColor.g, borderColor.b, 0.3)
|
||||
width: 1
|
||||
}
|
||||
|
||||
shadow {
|
||||
xOffset: 0
|
||||
yOffset: 4
|
||||
color: Qt.rgba(0, 0, 0, 0.3)
|
||||
size: 8
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: QQC2.ScrollView {
|
||||
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
|
||||
QQC2.ScrollBar.horizontal.policy: QQC2.ScrollBar.AlwaysOff
|
||||
|
||||
ListView {
|
||||
id: userListView
|
||||
clip: true
|
||||
|
||||
model: UserFilterModel {
|
||||
id: userListFilterModel
|
||||
sourceModel: userListModel
|
||||
filterText: userListSearchField.text
|
||||
|
||||
onFilterTextChanged: {
|
||||
if (filterText.length > 0 && !userListSearchPopup.visible) {
|
||||
userListSearchPopup.open();
|
||||
} else if (filterText.length <= 0 && userListSearchPopup.visible) {
|
||||
userListSearchPopup.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delegate: Delegates.RoundedItemDelegate {
|
||||
id: userListItem
|
||||
|
||||
required property string userId
|
||||
required property string avatar
|
||||
required property string name
|
||||
required property int powerLevel
|
||||
required property string powerLevelString
|
||||
|
||||
text: name
|
||||
|
||||
contentItem: RowLayout {
|
||||
KirigamiComponents.Avatar {
|
||||
Layout.preferredWidth: Kirigami.Units.iconSizes.medium
|
||||
Layout.preferredHeight: Kirigami.Units.iconSizes.medium
|
||||
source: userListItem.avatar ? ("image://" + userListItem.avatar) : ""
|
||||
name: userListItem.name
|
||||
}
|
||||
|
||||
Delegates.SubtitleContentItem {
|
||||
itemDelegate: userListItem
|
||||
subtitle: userListItem.userId
|
||||
labelItem.textFormat: Text.PlainText
|
||||
subtitleItem.textFormat: Text.PlainText
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
QQC2.Label {
|
||||
visible: userListItem.powerLevel > 0
|
||||
|
||||
text: userListItem.powerLevelString
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
textFormat: Text.PlainText
|
||||
wrapMode: Text.NoWrap
|
||||
}
|
||||
}
|
||||
|
||||
action: Kirigami.Action {
|
||||
id: editPowerLevelAction
|
||||
onTriggered: {
|
||||
userListSearchPopup.close();
|
||||
let dialog = powerLevelDialog.createObject(applicationWindow().overlay, {
|
||||
room: root.room,
|
||||
userId: userListItem.userId,
|
||||
powerLevel: userListItem.powerLevel
|
||||
});
|
||||
dialog.open();
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: powerLevelDialog
|
||||
PowerLevelDialog {
|
||||
id: powerLevelDialog
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormHeader {
|
||||
visible: room.canSendState("m.room.power_levels")
|
||||
title: i18n("Default permissions")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
visible: room.canSendState("m.room.power_levels")
|
||||
FormCard.FormComboBoxDelegate {
|
||||
text: i18n("Default user power level")
|
||||
description: i18n("This is power level for all new users when joining the room")
|
||||
textRole: "text"
|
||||
valueRole: "powerLevel"
|
||||
model: powerLevelModel
|
||||
Component.onCompleted: currentIndex = indexOfValue(room.defaultUserPowerLevel)
|
||||
onCurrentValueChanged: if (room.canSendState("m.room.power_levels")) {
|
||||
room.defaultUserPowerLevel = currentValue;
|
||||
}
|
||||
}
|
||||
FormCard.FormComboBoxDelegate {
|
||||
text: i18n("Default power level to set the room state")
|
||||
description: i18n("This is used for all state events that do not have their own entry here")
|
||||
textRole: "text"
|
||||
valueRole: "powerLevel"
|
||||
model: powerLevelModel
|
||||
Component.onCompleted: currentIndex = indexOfValue(room.statePowerLevel)
|
||||
onCurrentValueChanged: if (room.canSendState("m.room.power_levels")) {
|
||||
room.statePowerLevel = currentValue;
|
||||
}
|
||||
}
|
||||
FormCard.FormComboBoxDelegate {
|
||||
text: i18n("Default power level to send messages")
|
||||
description: i18n("This is used for all message events that do not have their own entry here")
|
||||
textRole: "text"
|
||||
valueRole: "powerLevel"
|
||||
model: powerLevelModel
|
||||
Component.onCompleted: currentIndex = indexOfValue(room.defaultEventPowerLevel)
|
||||
onCurrentValueChanged: if (room.canSendState("m.room.power_levels")) {
|
||||
room.defaultEventPowerLevel = currentValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormHeader {
|
||||
visible: room.canSendState("m.room.power_levels")
|
||||
title: i18n("Basic permissions")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
visible: room.canSendState("m.room.power_levels")
|
||||
FormCard.FormComboBoxDelegate {
|
||||
text: i18n("Invite users")
|
||||
textRole: "text"
|
||||
valueRole: "powerLevel"
|
||||
model: powerLevelModel
|
||||
Component.onCompleted: currentIndex = indexOfValue(room.invitePowerLevel)
|
||||
onCurrentValueChanged: if (room.canSendState("m.room.power_levels")) {
|
||||
room.invitePowerLevel = currentValue;
|
||||
}
|
||||
}
|
||||
FormCard.FormComboBoxDelegate {
|
||||
text: i18n("Kick users")
|
||||
textRole: "text"
|
||||
valueRole: "powerLevel"
|
||||
model: powerLevelModel
|
||||
Component.onCompleted: currentIndex = indexOfValue(room.kickPowerLevel)
|
||||
onCurrentValueChanged: if (room.canSendState("m.room.power_levels")) {
|
||||
room.kickPowerLevel = currentValue;
|
||||
}
|
||||
}
|
||||
FormCard.FormComboBoxDelegate {
|
||||
text: i18n("Ban users")
|
||||
textRole: "text"
|
||||
valueRole: "powerLevel"
|
||||
model: powerLevelModel
|
||||
Component.onCompleted: currentIndex = indexOfValue(room.banPowerLevel)
|
||||
onCurrentValueChanged: if (room.canSendState("m.room.power_levels")) {
|
||||
room.banPowerLevel = currentValue;
|
||||
}
|
||||
}
|
||||
FormCard.FormComboBoxDelegate {
|
||||
text: i18n("Remove message sent by other users")
|
||||
textRole: "text"
|
||||
valueRole: "powerLevel"
|
||||
model: powerLevelModel
|
||||
Component.onCompleted: currentIndex = indexOfValue(room.redactPowerLevel)
|
||||
onCurrentValueChanged: if (room.canSendState("m.room.power_levels")) {
|
||||
room.redactPowerLevel = currentValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormHeader {
|
||||
visible: room.canSendState("m.room.power_levels")
|
||||
title: i18n("Event permissions")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
visible: room.canSendState("m.room.power_levels")
|
||||
FormCard.FormComboBoxDelegate {
|
||||
text: i18n("Change user permissions")
|
||||
description: "m.room.power_levels"
|
||||
textRole: "text"
|
||||
valueRole: "powerLevel"
|
||||
model: powerLevelModel
|
||||
Component.onCompleted: currentIndex = indexOfValue(room.powerLevelPowerLevel)
|
||||
onCurrentValueChanged: if (room.canSendState("m.room.power_levels")) {
|
||||
room.powerLevelPowerLevel = currentValue;
|
||||
}
|
||||
}
|
||||
FormCard.FormComboBoxDelegate {
|
||||
text: i18n("Change the room name")
|
||||
description: "m.room.name"
|
||||
textRole: "text"
|
||||
valueRole: "powerLevel"
|
||||
model: powerLevelModel
|
||||
Component.onCompleted: currentIndex = indexOfValue(room.namePowerLevel)
|
||||
onCurrentValueChanged: if (room.canSendState("m.room.power_levels")) {
|
||||
room.namePowerLevel = currentValue;
|
||||
}
|
||||
}
|
||||
FormCard.FormComboBoxDelegate {
|
||||
text: i18n("Change the room avatar")
|
||||
description: "m.room.avatar"
|
||||
textRole: "text"
|
||||
valueRole: "powerLevel"
|
||||
model: powerLevelModel
|
||||
Component.onCompleted: currentIndex = indexOfValue(room.avatarPowerLevel)
|
||||
onCurrentValueChanged: if (room.canSendState("m.room.power_levels")) {
|
||||
room.avatarPowerLevel = currentValue;
|
||||
}
|
||||
}
|
||||
FormCard.FormComboBoxDelegate {
|
||||
text: i18n("Change the room canonical alias")
|
||||
description: "m.room.canonical_alias"
|
||||
textRole: "text"
|
||||
valueRole: "powerLevel"
|
||||
model: powerLevelModel
|
||||
Component.onCompleted: currentIndex = indexOfValue(room.canonicalAliasPowerLevel)
|
||||
onCurrentValueChanged: if (room.canSendState("m.room.power_levels")) {
|
||||
room.canonicalAliasPowerLevel = currentValue;
|
||||
}
|
||||
}
|
||||
FormCard.FormComboBoxDelegate {
|
||||
text: i18n("Change the room topic")
|
||||
description: "m.room.topic"
|
||||
textRole: "text"
|
||||
valueRole: "powerLevel"
|
||||
model: powerLevelModel
|
||||
Component.onCompleted: currentIndex = indexOfValue(room.topicPowerLevel)
|
||||
onCurrentValueChanged: if (room.canSendState("m.room.power_levels")) {
|
||||
room.topicPowerLevel = currentValue;
|
||||
}
|
||||
}
|
||||
FormCard.FormComboBoxDelegate {
|
||||
text: i18n("Enable encryption for the room")
|
||||
description: "m.room.encryption"
|
||||
textRole: "text"
|
||||
valueRole: "powerLevel"
|
||||
model: powerLevelModel
|
||||
Component.onCompleted: currentIndex = indexOfValue(room.encryptionPowerLevel)
|
||||
onCurrentValueChanged: if (room.canSendState("m.room.power_levels")) {
|
||||
room.encryptionPowerLevel = currentValue;
|
||||
}
|
||||
}
|
||||
FormCard.FormComboBoxDelegate {
|
||||
text: i18n("Change the room history visibility")
|
||||
description: "m.room.history_visibility"
|
||||
textRole: "text"
|
||||
valueRole: "powerLevel"
|
||||
model: powerLevelModel
|
||||
Component.onCompleted: currentIndex = indexOfValue(room.historyVisibilityPowerLevel)
|
||||
onCurrentValueChanged: if (room.canSendState("m.room.power_levels")) {
|
||||
room.historyVisibilityPowerLevel = currentValue;
|
||||
}
|
||||
}
|
||||
FormCard.FormComboBoxDelegate {
|
||||
text: i18n("Set pinned events")
|
||||
description: "m.room.pinned_events"
|
||||
textRole: "text"
|
||||
valueRole: "powerLevel"
|
||||
model: powerLevelModel
|
||||
Component.onCompleted: currentIndex = indexOfValue(room.pinnedEventsPowerLevel)
|
||||
onCurrentValueChanged: if (room.canSendState("m.room.power_levels")) {
|
||||
room.pinnedEventsPowerLevel = currentValue;
|
||||
}
|
||||
}
|
||||
FormCard.FormComboBoxDelegate {
|
||||
text: i18n("Upgrade the room")
|
||||
description: "m.room.tombstone"
|
||||
textRole: "text"
|
||||
valueRole: "powerLevel"
|
||||
model: powerLevelModel
|
||||
Component.onCompleted: currentIndex = indexOfValue(room.tombstonePowerLevel)
|
||||
onCurrentValueChanged: if (room.canSendState("m.room.power_levels")) {
|
||||
room.tombstonePowerLevel = currentValue;
|
||||
}
|
||||
}
|
||||
FormCard.FormComboBoxDelegate {
|
||||
text: i18n("Set the room server access control list (ACL)")
|
||||
description: "m.room.server_acl"
|
||||
textRole: "text"
|
||||
valueRole: "powerLevel"
|
||||
model: powerLevelModel
|
||||
Component.onCompleted: currentIndex = indexOfValue(room.serverAclPowerLevel)
|
||||
onCurrentValueChanged: if (room.canSendState("m.room.power_levels")) {
|
||||
room.serverAclPowerLevel = currentValue;
|
||||
}
|
||||
}
|
||||
FormCard.FormComboBoxDelegate {
|
||||
visible: room.isSpace
|
||||
text: i18n("Set the children of this space")
|
||||
description: "m.space.child"
|
||||
textRole: "text"
|
||||
valueRole: "powerLevel"
|
||||
model: powerLevelModel
|
||||
Component.onCompleted: currentIndex = indexOfValue(room.spaceChildPowerLevel)
|
||||
onCurrentValueChanged: if (room.canSendState("m.room.power_levels")) {
|
||||
room.spaceChildPowerLevel = currentValue;
|
||||
}
|
||||
}
|
||||
FormCard.FormComboBoxDelegate {
|
||||
text: i18n("Set the parent space of this room")
|
||||
description: "m.space.parent"
|
||||
textRole: "text"
|
||||
valueRole: "powerLevel"
|
||||
model: powerLevelModel
|
||||
Component.onCompleted: currentIndex = indexOfValue(room.spaceChildPowerLevel)
|
||||
onCurrentValueChanged: if (room.canSendState("m.room.power_levels")) {
|
||||
room.spaceParentPowerLevel = currentValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
138
src/settings/PushNotification.qml
Normal file
138
src/settings/PushNotification.qml
Normal file
@@ -0,0 +1,138 @@
|
||||
// SPDX-FileCopyrightText: 2022 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
import org.kde.kitemmodels
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
FormCard.FormCardPage {
|
||||
id: root
|
||||
|
||||
property NeoChatRoom room
|
||||
|
||||
property PushRuleModel pushRuleModel: PushRuleModel {
|
||||
connection: root.room.connection
|
||||
}
|
||||
|
||||
title: i18nc('@title:window', 'Notifications')
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18n("Room notifications setting")
|
||||
}
|
||||
|
||||
FormCard.FormCard {
|
||||
FormCard.FormRadioDelegate {
|
||||
text: i18n("Follow global setting")
|
||||
checked: room.pushNotificationState === PushNotificationState.Default
|
||||
enabled: room.pushNotificationState !== PushNotificationState.Unknown
|
||||
onToggled: {
|
||||
room.pushNotificationState = PushNotificationState.Default;
|
||||
}
|
||||
}
|
||||
FormCard.FormRadioDelegate {
|
||||
text: i18nc("As in 'notify for all messages'", "All")
|
||||
checked: room.pushNotificationState === PushNotificationState.All
|
||||
enabled: room.pushNotificationState !== PushNotificationState.Unknown
|
||||
onToggled: {
|
||||
room.pushNotificationState = PushNotificationState.All;
|
||||
}
|
||||
}
|
||||
FormCard.FormRadioDelegate {
|
||||
text: i18nc("As in 'notify when the user is mentioned or the message contains a set keyword'", "@Mentions and Keywords")
|
||||
checked: room.pushNotificationState === PushNotificationState.MentionKeyword
|
||||
enabled: room.pushNotificationState !== PushNotificationState.Unknown
|
||||
onToggled: {
|
||||
room.pushNotificationState = PushNotificationState.MentionKeyword;
|
||||
}
|
||||
}
|
||||
FormCard.FormRadioDelegate {
|
||||
text: i18nc("As in 'do not notify for any messages'", "Off")
|
||||
checked: room.pushNotificationState === PushNotificationState.Mute
|
||||
enabled: room.pushNotificationState !== PushNotificationState.Unknown
|
||||
onToggled: {
|
||||
room.pushNotificationState = PushNotificationState.Mute;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18n("Keywords")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
Repeater {
|
||||
model: KSortFilterProxyModel {
|
||||
sourceModel: root.pushRuleModel
|
||||
|
||||
filterRowCallback: function (source_row, source_parent) {
|
||||
let sectionRole = sourceModel.data(sourceModel.index(source_row, 0, source_parent), PushRuleModel.SectionRole);
|
||||
let roomIdRole = sourceModel.data(sourceModel.index(source_row, 0, source_parent), PushRuleModel.RoomIdRole);
|
||||
return sectionRole == PushRuleSection.RoomKeywords && roomIdRole == root.room.id;
|
||||
}
|
||||
}
|
||||
|
||||
delegate: ruleDelegate
|
||||
|
||||
Component {
|
||||
id: ruleDelegate
|
||||
NotificationRuleItem {
|
||||
onDeleteRule: {
|
||||
root.pushRuleModel.removeKeyword(id);
|
||||
}
|
||||
onActionChanged: action => root.pushRuleModel.setPushRuleAction(id, action)
|
||||
}
|
||||
}
|
||||
}
|
||||
FormCard.AbstractFormDelegate {
|
||||
Layout.fillWidth: true
|
||||
|
||||
contentItem: RowLayout {
|
||||
Kirigami.ActionTextField {
|
||||
id: keywordAddField
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
placeholderText: i18n("Keyword…")
|
||||
enabled: NotificationsManager.keywordNotificationAction !== PushRuleAction.Unknown
|
||||
|
||||
rightActions: Kirigami.Action {
|
||||
icon.name: "edit-clear"
|
||||
visible: keywordAddField.text.length > 0
|
||||
onTriggered: {
|
||||
keywordAddField.text = "";
|
||||
}
|
||||
}
|
||||
|
||||
onAccepted: {
|
||||
root.pushRuleModel.addKeyword(keywordAddField.text, root.room.id);
|
||||
keywordAddField.text = "";
|
||||
}
|
||||
}
|
||||
QQC2.Button {
|
||||
id: addButton
|
||||
|
||||
text: i18n("Add keyword")
|
||||
Accessible.name: text
|
||||
icon.name: "list-add"
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
enabled: NotificationsManager.keywordNotificationAction !== PushRuleAction.Unknown && keywordAddField.text.length > 0
|
||||
|
||||
onClicked: {
|
||||
root.pushRuleModel.addKeyword(keywordAddField.text, root.room.id);
|
||||
keywordAddField.text = "";
|
||||
}
|
||||
|
||||
QQC2.ToolTip {
|
||||
text: addButton.text
|
||||
delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
425
src/settings/RoomGeneralPage.qml
Normal file
425
src/settings/RoomGeneralPage.qml
Normal file
@@ -0,0 +1,425 @@
|
||||
// SPDX-FileCopyrightText: 2019-2020 Black Hat <bhat@encom.eu.org>
|
||||
// SPDX-FileCopyrightText: 2021 Carl Schwan <carl@carlschwan.eu>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Window
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
FormCard.FormCardPage {
|
||||
id: root
|
||||
|
||||
property NeoChatRoom room
|
||||
required property NeoChatConnection connection
|
||||
|
||||
title: i18n("General")
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18n("Room Information")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
FormCard.AbstractFormDelegate {
|
||||
background: null
|
||||
contentItem: RowLayout {
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
KirigamiComponents.Avatar {
|
||||
id: avatar
|
||||
Layout.alignment: Qt.AlignRight
|
||||
name: room.name
|
||||
source: room.avatarMediaId ? ("image://mxc/" + room.avatarMediaId) : ""
|
||||
implicitWidth: Kirigami.Units.iconSizes.enormous
|
||||
implicitHeight: Kirigami.Units.iconSizes.enormous
|
||||
}
|
||||
QQC2.Button {
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
enabled: room.canSendState("m.room.avatar")
|
||||
visible: enabled
|
||||
icon.name: "cloud-upload"
|
||||
text: i18n("Update avatar")
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
|
||||
onClicked: {
|
||||
const fileDialog = openFileDialog.createObject(QQC2.ApplicationWindow.overlay);
|
||||
fileDialog.chosen.connect(function (path) {
|
||||
if (!path)
|
||||
return;
|
||||
room.changeAvatar(path);
|
||||
});
|
||||
fileDialog.open();
|
||||
}
|
||||
|
||||
QQC2.ToolTip.text: text
|
||||
QQC2.ToolTip.visible: hovered
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
FormCard.FormTextFieldDelegate {
|
||||
id: roomNameField
|
||||
label: i18n("Room name:")
|
||||
text: room.name
|
||||
readOnly: !room.canSendState("m.room.name")
|
||||
}
|
||||
FormCard.AbstractFormDelegate {
|
||||
id: roomTopicField
|
||||
background: Item {}
|
||||
contentItem: ColumnLayout {
|
||||
QQC2.Label {
|
||||
id: roomTopicLabel
|
||||
text: i18n("Room topic:")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
QQC2.TextArea {
|
||||
id: roomTopicTextArea
|
||||
Accessible.description: roomTopicLabel.text
|
||||
Layout.fillWidth: true
|
||||
wrapMode: TextEdit.Wrap
|
||||
text: room.topic
|
||||
readOnly: !room.canSendState("m.room.topic")
|
||||
onTextChanged: roomTopicField.text = text
|
||||
}
|
||||
}
|
||||
}
|
||||
FormCard.AbstractFormDelegate {
|
||||
visible: !roomNameField.readOnly || !roomTopicTextArea.readOnly
|
||||
background: Item {}
|
||||
contentItem: RowLayout {
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
QQC2.Button {
|
||||
Layout.bottomMargin: Kirigami.Units.smallSpacing
|
||||
Layout.topMargin: Kirigami.Units.smallSpacing
|
||||
enabled: room.name !== roomNameField.text || room.topic !== roomTopicField.text
|
||||
text: i18n("Save")
|
||||
onClicked: {
|
||||
if (room.name != roomNameField.text) {
|
||||
room.setName(roomNameField.text);
|
||||
}
|
||||
if (room.topic != roomTopicField.text) {
|
||||
room.setTopic(roomTopicField.text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
FormCard.FormTextDelegate {
|
||||
id: roomIdDelegate
|
||||
text: i18n("Room ID")
|
||||
description: room.id
|
||||
|
||||
contentItem.children: QQC2.Button {
|
||||
visible: roomIdDelegate.hovered
|
||||
text: i18n("Copy room ID to clipboard")
|
||||
icon.name: "edit-copy"
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
|
||||
onClicked: {
|
||||
Clipboard.saveText(room.id);
|
||||
}
|
||||
|
||||
QQC2.ToolTip.text: text
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
QQC2.ToolTip.visible: hovered
|
||||
}
|
||||
}
|
||||
FormCard.FormTextDelegate {
|
||||
text: i18n("Room version")
|
||||
description: room.version
|
||||
|
||||
contentItem.children: QQC2.Button {
|
||||
visible: room.canSwitchVersions()
|
||||
enabled: room.version < room.maxRoomVersion
|
||||
text: i18n("Upgrade Room")
|
||||
icon.name: "arrow-up-double"
|
||||
|
||||
onClicked: {
|
||||
if (room.canSwitchVersions()) {
|
||||
roomUpgradeDialog.currentRoomVersion = room.version;
|
||||
roomUpgradeDialog.open();
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.ToolTip {
|
||||
text: text
|
||||
delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18n("Aliases")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
FormCard.FormTextDelegate {
|
||||
visible: room.aliases.length <= 0
|
||||
text: i18n("No canonical alias set")
|
||||
}
|
||||
Repeater {
|
||||
id: altAliasRepeater
|
||||
model: room.aliases.slice().reverse()
|
||||
|
||||
delegate: FormCard.FormTextDelegate {
|
||||
text: modelData
|
||||
description: room.canonicalAlias.length > 0 && modelData === room.canonicalAlias ? "Canonical alias" : ""
|
||||
contentItem.children: [
|
||||
QQC2.ToolButton {
|
||||
id: setCanonicalAliasButton
|
||||
visible: modelData !== room.canonicalAlias && room.canSendState("m.room.canonical_alias")
|
||||
text: i18n("Make this alias the room's canonical alias")
|
||||
icon.name: "checkmark"
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
|
||||
onClicked: {
|
||||
room.setCanonicalAlias(modelData);
|
||||
}
|
||||
QQC2.ToolTip {
|
||||
text: setCanonicalAliasButton.text
|
||||
delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
},
|
||||
QQC2.ToolButton {
|
||||
id: deleteButton
|
||||
visible: room.canSendState("m.room.canonical_alias")
|
||||
text: i18n("Delete alias")
|
||||
icon.name: "edit-delete-remove"
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
|
||||
onClicked: {
|
||||
room.unmapAlias(modelData);
|
||||
}
|
||||
QQC2.ToolTip {
|
||||
text: deleteButton.text
|
||||
delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
FormCard.AbstractFormDelegate {
|
||||
visible: room.canSendState("m.room.canonical_alias")
|
||||
|
||||
contentItem: RowLayout {
|
||||
Kirigami.ActionTextField {
|
||||
id: aliasAddField
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
placeholderText: i18n("#new_alias:server.org")
|
||||
|
||||
rightActions: Kirigami.Action {
|
||||
icon.name: "edit-clear"
|
||||
visible: aliasAddField.text.length > 0
|
||||
onTriggered: {
|
||||
aliasAddField.text = "";
|
||||
}
|
||||
}
|
||||
|
||||
onAccepted: {
|
||||
room.mapAlias(aliasAddField.text);
|
||||
}
|
||||
}
|
||||
QQC2.Button {
|
||||
id: addButton
|
||||
|
||||
text: i18n("Add new alias")
|
||||
Accessible.name: text
|
||||
icon.name: "list-add"
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
enabled: aliasAddField.text.length > 0
|
||||
|
||||
onClicked: {
|
||||
room.mapAlias(aliasAddField.text);
|
||||
}
|
||||
|
||||
QQC2.ToolTip {
|
||||
text: addButton.text
|
||||
delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18n("URL Previews")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
FormCard.FormCheckDelegate {
|
||||
text: i18n("Enable URL previews by default for room members")
|
||||
checked: room.defaultUrlPreviewState
|
||||
visible: room.canSendState("org.matrix.room.preview_urls")
|
||||
onToggled: {
|
||||
room.defaultUrlPreviewState = checked;
|
||||
}
|
||||
}
|
||||
FormCard.FormCheckDelegate {
|
||||
text: i18n("Enable URL previews")
|
||||
// Most users won't see the above setting so tell them the default.
|
||||
description: room.defaultUrlPreviewState ? i18n("URL previews are enabled by default in this room") : i18n("URL previews are disabled by default in this room")
|
||||
checked: room.urlPreviewEnabled
|
||||
onToggled: {
|
||||
room.urlPreviewEnabled = checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
FormCard.FormHeader {
|
||||
title: i18n("Official Parent Spaces")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
Repeater {
|
||||
id: officalParentRepeater
|
||||
model: root.room.parentIds
|
||||
|
||||
delegate: FormCard.FormTextDelegate {
|
||||
id: officalParentDelegate
|
||||
required property string modelData
|
||||
property NeoChatRoom space: root.connection.room(modelData)
|
||||
text: {
|
||||
if (space) {
|
||||
return space.displayName;
|
||||
} else {
|
||||
return modelData;
|
||||
}
|
||||
}
|
||||
description: {
|
||||
if (space) {
|
||||
if (space.canonicalAlias.length > 0) {
|
||||
return space.canonicalAlias;
|
||||
} else {
|
||||
return modelData;
|
||||
}
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
contentItem.children: RowLayout {
|
||||
QQC2.Label {
|
||||
visible: root.room.canonicalParent === officalParentDelegate.modelData
|
||||
text: i18n("Canonical")
|
||||
}
|
||||
QQC2.ToolButton {
|
||||
visible: root.room.canSendState("m.space.parent") && root.room.canonicalParent !== officalParentDelegate.modelData
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
action: Kirigami.Action {
|
||||
id: canonicalParentAction
|
||||
text: i18n("Make canonical parent")
|
||||
icon.name: "checkmark"
|
||||
onTriggered: root.room.canonicalParent = officalParentDelegate.modelData
|
||||
}
|
||||
QQC2.ToolTip {
|
||||
text: canonicalParentAction.text
|
||||
delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
}
|
||||
QQC2.ToolButton {
|
||||
visible: officalParentDelegate?.space.canSendState("m.space.child") && root.room.canSendState("m.space.parent")
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
text: i18n("Remove parent")
|
||||
icon.name: "edit-delete-remove"
|
||||
onClicked: root.room.removeParent(officalParentDelegate.modelData)
|
||||
|
||||
QQC2.ToolTip.text: text
|
||||
QQC2.ToolTip.visible: hovered
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
FormCard.FormTextDelegate {
|
||||
visible: officalParentRepeater.count <= 0
|
||||
text: i18n("This room has no official parent spaces.")
|
||||
}
|
||||
FormCard.FormButtonDelegate {
|
||||
visible: root.room.canSendState("m.space.parent")
|
||||
text: i18nc("@action:button", "Add new official parent")
|
||||
onClicked: selectParentDialog.createObject(applicationWindow().overlay).open()
|
||||
|
||||
Component {
|
||||
id: selectParentDialog
|
||||
SelectParentDialog {
|
||||
room: root.room
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.InlineMessage {
|
||||
Layout.maximumWidth: Kirigami.Units.gridUnit * 30
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: i18n("This room continues another conversation.")
|
||||
type: Kirigami.MessageType.Information
|
||||
visible: room.predecessorId && room.connection.room(room.predecessorId)
|
||||
actions: Kirigami.Action {
|
||||
text: i18n("See older messages…")
|
||||
onTriggered: {
|
||||
RoomManager.resolveResource(room.predecessorId);
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
Kirigami.InlineMessage {
|
||||
Layout.maximumWidth: Kirigami.Units.gridUnit * 30
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: i18n("This room has been replaced.")
|
||||
type: Kirigami.MessageType.Information
|
||||
visible: room.successorId && room.connection.room(room.successorId)
|
||||
actions: Kirigami.Action {
|
||||
text: i18n("See new room…")
|
||||
onTriggered: {
|
||||
RoomManager.resolveResource(room.successorId);
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property Component openFileDialog: Component {
|
||||
id: openFileDialog
|
||||
|
||||
OpenFileDialog {
|
||||
parentWindow: root.Window.window
|
||||
}
|
||||
}
|
||||
|
||||
property Kirigami.Dialog roomUpgradeDialog: Kirigami.Dialog {
|
||||
id: roomUpgradeDialog
|
||||
|
||||
property var currentRoomVersion
|
||||
|
||||
width: Kirigami.Units.gridUnit * 16
|
||||
|
||||
title: i18n("Upgrade the Room")
|
||||
ColumnLayout {
|
||||
FormCard.FormSpinBoxDelegate {
|
||||
id: spinBox
|
||||
label: i18n("Select new version")
|
||||
from: room.version
|
||||
to: room.maxRoomVersion
|
||||
value: room.version
|
||||
}
|
||||
}
|
||||
customFooterActions: [
|
||||
Kirigami.Action {
|
||||
text: i18n("Confirm")
|
||||
icon.name: "dialog-ok"
|
||||
onTriggered: {
|
||||
room.switchVersion(spinBox.value);
|
||||
roomUpgradeDialog.close();
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
164
src/settings/RoomSecurityPage.qml
Normal file
164
src/settings/RoomSecurityPage.qml
Normal file
@@ -0,0 +1,164 @@
|
||||
// SPDX-FileCopyrightText: 2019-2020 Black Hat <bhat@encom.eu.org>
|
||||
// SPDX-FileCopyrightText: 2021 Carl Schwan <carl@carlschwan.eu>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
FormCard.FormCardPage {
|
||||
id: root
|
||||
|
||||
property NeoChatRoom room
|
||||
property string needUpgradeRoom: i18n("You need to upgrade this room to a newer version to enable this setting.")
|
||||
|
||||
title: i18n("Security")
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18nc("@option:check", "Encryption")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
FormCard.FormSwitchDelegate {
|
||||
id: enableEncryptionSwitch
|
||||
text: i18n("Enable encryption")
|
||||
description: i18nc("option:check", "Once enabled, encryption cannot be disabled.")
|
||||
enabled: room.canEncryptRoom
|
||||
checked: room.usesEncryption
|
||||
onToggled: if (checked) {
|
||||
let dialog = confirmEncryptionDialog.createObject(applicationWindow().overlay, {
|
||||
room: room
|
||||
});
|
||||
dialog.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18nc("@option:check", "Access")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
FormCard.FormRadioDelegate {
|
||||
text: i18nc("@option:check", "Private (invite only)")
|
||||
description: i18n("Only invited people can join.")
|
||||
checked: room.joinRule === "invite"
|
||||
enabled: room.canSendState("m.room.join_rules")
|
||||
onCheckedChanged: if (checked && room.joinRule != "invite") {
|
||||
root.room.joinRule = "invite";
|
||||
}
|
||||
}
|
||||
FormCard.FormRadioDelegate {
|
||||
text: i18nc("@option:check", "Space members")
|
||||
description: i18n("Anyone in the selected spaces can find and join.") + (!["8", "9", "10"].includes(room.version) ? `\n${needUpgradeRoom}` : "")
|
||||
checked: room.joinRule === "restricted"
|
||||
enabled: room.canSendState("m.room.join_rules") && ["8", "9", "10"].includes(room.version)
|
||||
onCheckedChanged: if (checked && room.joinRule != "restricted") {
|
||||
selectSpacesDialog.createObject(applicationWindow().overlay).open();
|
||||
}
|
||||
|
||||
contentItem.children: QQC2.Button {
|
||||
visible: root.room.joinRule === "restricted"
|
||||
text: i18n("Select spaces")
|
||||
icon.name: "list-add"
|
||||
|
||||
onClicked: selectSpacesDialog.createObject(applicationWindow().overlay).open()
|
||||
|
||||
QQC2.ToolTip.text: text
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
QQC2.ToolTip.visible: hovered
|
||||
}
|
||||
|
||||
Component {
|
||||
id: selectSpacesDialog
|
||||
SelectSpacesDialog {
|
||||
room: root.room
|
||||
}
|
||||
}
|
||||
}
|
||||
FormCard.FormRadioDelegate {
|
||||
text: i18nc("@option:check", "Knock")
|
||||
description: i18n("People not in the room need to request an invite to join the room.") + (!["7", "8", "9", "10"].includes(room.version) ? `\n${needUpgradeRoom}` : "")
|
||||
checked: room.joinRule === "knock"
|
||||
// https://spec.matrix.org/v1.4/rooms/#feature-matrix
|
||||
enabled: room.canSendState("m.room.join_rules") && ["7", "8", "9", "10"].includes(room.version)
|
||||
onCheckedChanged: if (checked && room.joinRule != "knock") {
|
||||
root.room.joinRule = "knock";
|
||||
}
|
||||
}
|
||||
FormCard.FormRadioDelegate {
|
||||
text: i18nc("@option:check", "Public")
|
||||
description: i18nc("@option:check", "Anyone can find and join.")
|
||||
checked: room.joinRule === "public"
|
||||
enabled: room.canSendState("m.room.join_rules")
|
||||
onCheckedChanged: if (checked && root.room.joinRule != "public") {
|
||||
root.room.joinRule = "public";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18nc("@option:check", "Message history visibility")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
FormCard.FormRadioDelegate {
|
||||
text: i18nc("@option:check", "Anyone")
|
||||
description: i18nc("@option:check", "Anyone, regardless of whether they have joined, can view history.")
|
||||
checked: room.historyVisibility === "world_readable"
|
||||
enabled: room.canSendState("m.room.history_visibility")
|
||||
onCheckedChanged: if (checked) {
|
||||
room.historyVisibility = "world_readable";
|
||||
}
|
||||
}
|
||||
FormCard.FormRadioDelegate {
|
||||
text: i18nc("@option:check", "Members only")
|
||||
description: i18nc("@option:check", "All members can view the entire message history, even before they joined.")
|
||||
checked: room.historyVisibility === "shared"
|
||||
enabled: room.canSendState("m.room.history_visibility")
|
||||
onCheckedChanged: if (checked) {
|
||||
room.historyVisibility = "shared";
|
||||
}
|
||||
}
|
||||
FormCard.FormRadioDelegate {
|
||||
text: i18nc("@option:check", "Members only (since invite)")
|
||||
description: i18nc("@option:check", "New members can view the message history from the point they were invited to the room.")
|
||||
checked: room.historyVisibility === "invited"
|
||||
enabled: room.canSendState("m.room.history_visibility")
|
||||
onCheckedChanged: if (checked) {
|
||||
room.historyVisibility = "invited";
|
||||
}
|
||||
}
|
||||
FormCard.FormRadioDelegate {
|
||||
text: i18nc("@option:check", "Members only (since joining)")
|
||||
description: i18nc("@option:check", "New members can view the message history from the point they joined the room.")
|
||||
checked: room.historyVisibility === "joined"
|
||||
enabled: room.canSendState("m.room.history_visibility")
|
||||
onCheckedChanged: if (checked) {
|
||||
room.historyVisibility = "joined";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property Component confirmEncryptionDialog: Component {
|
||||
id: confirmEncryptionDialog
|
||||
|
||||
ConfirmEncryptionDialog {
|
||||
onClosed: {
|
||||
// At the point this is executed, the state in the room is not yet changed.
|
||||
// The value will be updated when room.onEncryption() emitted.
|
||||
// This is in case if user simply closed the dialog.
|
||||
enableEncryptionSwitch.checked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property Connections connections: Connections {
|
||||
target: room
|
||||
onEncryption: {
|
||||
enableEncryptionSwitch.checked = room.usesEncryption;
|
||||
}
|
||||
}
|
||||
}
|
||||
65
src/settings/RoomSettings.qml
Normal file
65
src/settings/RoomSettings.qml
Normal file
@@ -0,0 +1,65 @@
|
||||
// SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.settings as KirigamiSettings
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
KirigamiSettings.CategorizedSettings {
|
||||
id: root
|
||||
|
||||
property NeoChatRoom room
|
||||
required property NeoChatConnection connection
|
||||
|
||||
objectName: "settingsPage"
|
||||
actions: [
|
||||
KirigamiSettings.SettingAction {
|
||||
actionName: "general"
|
||||
text: i18n("General")
|
||||
icon.name: "settings-configure"
|
||||
page: Qt.resolvedUrl("RoomGeneralPage.qml")
|
||||
initialProperties: {
|
||||
return {
|
||||
room: root.room,
|
||||
connection: root.connection
|
||||
};
|
||||
}
|
||||
},
|
||||
KirigamiSettings.SettingAction {
|
||||
actionName: "security"
|
||||
text: i18n("Security")
|
||||
icon.name: "security-low"
|
||||
page: Qt.resolvedUrl("RoomSecurityPage.qml")
|
||||
initialProperties: {
|
||||
return {
|
||||
room: root.room
|
||||
};
|
||||
}
|
||||
},
|
||||
KirigamiSettings.SettingAction {
|
||||
actionName: "permissions"
|
||||
text: i18n("Permissions")
|
||||
icon.name: "visibility"
|
||||
page: Qt.resolvedUrl("Permissions.qml")
|
||||
initialProperties: {
|
||||
return {
|
||||
room: root.room
|
||||
};
|
||||
}
|
||||
},
|
||||
KirigamiSettings.SettingAction {
|
||||
actionName: "notifications"
|
||||
text: i18n("Notifications")
|
||||
icon.name: "notifications"
|
||||
page: Qt.resolvedUrl("PushNotification.qml")
|
||||
initialProperties: {
|
||||
return {
|
||||
room: root.room
|
||||
};
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
253
src/settings/SonnetConfigPage.qml
Normal file
253
src/settings/SonnetConfigPage.qml
Normal file
@@ -0,0 +1,253 @@
|
||||
// SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
import QtQml
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.sonnet as Sonnet
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
import org.kde.kirigamiaddons.delegates as Delegates
|
||||
|
||||
Kirigami.ScrollablePage {
|
||||
id: root
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18nc("@title", "Spellchecking")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
id: card
|
||||
property Sonnet.Settings settings: Sonnet.Settings {
|
||||
id: settings
|
||||
}
|
||||
FormCard.FormCheckDelegate {
|
||||
id: enable
|
||||
checked: settings.checkerEnabledByDefault
|
||||
text: i18n("Enable automatic spell checking")
|
||||
onCheckedChanged: {
|
||||
settings.checkerEnabledByDefault = checked;
|
||||
settings.save();
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormDelegateSeparator {
|
||||
below: enable
|
||||
above: skipUppercase
|
||||
}
|
||||
|
||||
FormCard.FormCheckDelegate {
|
||||
id: skipUppercase
|
||||
checked: settings.skipUppercase
|
||||
text: i18n("Ignore uppercase words")
|
||||
onCheckedChanged: {
|
||||
settings.skipUppercase = checked;
|
||||
settings.save();
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormDelegateSeparator {
|
||||
below: skipUppercase
|
||||
above: skipRunTogether
|
||||
}
|
||||
|
||||
FormCard.FormCheckDelegate {
|
||||
id: skipRunTogether
|
||||
checked: settings.skipRunTogether
|
||||
text: i18n("Ignore hyphenated words")
|
||||
onCheckedChanged: {
|
||||
settings.skipRunTogether = checked;
|
||||
settings.save();
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormDelegateSeparator {
|
||||
below: skipRunTogether
|
||||
above: autodetectLanguageCheckbox
|
||||
}
|
||||
|
||||
FormCard.FormCheckDelegate {
|
||||
id: autodetectLanguageCheckbox
|
||||
checked: settings.autodetectLanguage
|
||||
text: i18n("Detect language automatically")
|
||||
onCheckedChanged: {
|
||||
settings.autodetectLanguage = checked;
|
||||
settings.save();
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormDelegateSeparator {
|
||||
below: autodetectLanguageCheckbox
|
||||
above: selectedDefaultLanguage
|
||||
}
|
||||
|
||||
FormCard.FormComboBoxDelegate {
|
||||
id: selectedDefaultLanguage
|
||||
text: i18n("Selected default language:")
|
||||
model: isEmpty ? [
|
||||
{
|
||||
"display": i18n("None")
|
||||
}
|
||||
] : settings.dictionaryModel
|
||||
textRole: "display"
|
||||
displayMode: Kirigami.Settings.isMobile ? FormCard.FormComboBoxDelegate.Dialog : FormCard.FormComboBoxDelegate.Page
|
||||
valueRole: "languageCode"
|
||||
property bool isEmpty: false
|
||||
Component.onCompleted: {
|
||||
if (settings.dictionaryModel.rowCount() === 0) {
|
||||
isEmpty = true;
|
||||
} else {
|
||||
currentIndex = indexOfValue(settings.defaultLanguage);
|
||||
}
|
||||
}
|
||||
onActivated: settings.defaultLanguage = currentValue
|
||||
}
|
||||
|
||||
FormCard.FormDelegateSeparator {
|
||||
below: selectedDefaultLanguage
|
||||
above: spellCheckingLanguage
|
||||
}
|
||||
|
||||
FormCard.FormButtonDelegate {
|
||||
id: spellCheckingLanguage
|
||||
text: i18n("Additional spell checking languages")
|
||||
description: i18n("%1 will provide spell checking and suggestions for the languages listed here when autodetection is enabled.", Qt.application.displayName)
|
||||
enabled: autodetectLanguageCheckbox.checked
|
||||
onClicked: pageStack.pushDialogLayer(spellCheckingLanguageList, {}, {
|
||||
width: pageStack.width - Kirigami.Units.gridUnit * 5,
|
||||
height: pageStack.height - Kirigami.Units.gridUnit * 5
|
||||
})
|
||||
}
|
||||
|
||||
FormCard.FormDelegateSeparator {
|
||||
below: spellCheckingLanguageList
|
||||
above: personalDictionary
|
||||
}
|
||||
|
||||
FormCard.FormButtonDelegate {
|
||||
id: personalDictionary
|
||||
text: i18n("Open Personal Dictionary")
|
||||
onClicked: pageStack.pushDialogLayer(dictionaryPage, {}, {
|
||||
width: pageStack.width - Kirigami.Units.gridUnit * 5,
|
||||
height: pageStack.height - Kirigami.Units.gridUnit * 5
|
||||
})
|
||||
}
|
||||
|
||||
property Component spellCheckingLanguageList: Component {
|
||||
id: spellCheckingLanguageList
|
||||
Kirigami.ScrollablePage {
|
||||
id: scroll
|
||||
title: i18nc("@title:window", "Spell checking languages")
|
||||
ListView {
|
||||
clip: true
|
||||
model: settings.dictionaryModel
|
||||
delegate: QQC2.CheckDelegate {
|
||||
onClicked: model.checked = checked
|
||||
Accessible.description: model.isDefault ? i18n("Default Language") : ''
|
||||
checked: model.checked
|
||||
width: scroll.width
|
||||
contentItem: RowLayout {
|
||||
QQC2.Label {
|
||||
id: label
|
||||
Layout.fillWidth: true
|
||||
text: model.display
|
||||
}
|
||||
Kirigami.Icon {
|
||||
source: "favorite"
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing * 2
|
||||
visible: model.isDefault
|
||||
HoverHandler {
|
||||
id: hover
|
||||
}
|
||||
QQC2.ToolTip {
|
||||
visible: hover.hovered
|
||||
text: i18n("Default Language")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: dictionaryPage
|
||||
Kirigami.ScrollablePage {
|
||||
title: i18n("Spell checking dictionary")
|
||||
footer: QQC2.ToolBar {
|
||||
contentItem: RowLayout {
|
||||
QQC2.TextField {
|
||||
id: dictionaryField
|
||||
Layout.fillWidth: true
|
||||
Accessible.name: placeholderText
|
||||
placeholderText: i18n("Add a new word to your personal dictionary…")
|
||||
}
|
||||
QQC2.Button {
|
||||
text: i18nc("@action:button", "Add word")
|
||||
icon.name: "list-add"
|
||||
enabled: dictionaryField.text.length > 0
|
||||
onClicked: {
|
||||
add(dictionaryField.text);
|
||||
dictionaryField.clear();
|
||||
if (instantApply) {
|
||||
settings.save();
|
||||
}
|
||||
}
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
}
|
||||
}
|
||||
}
|
||||
ListView {
|
||||
topMargin: Math.round(Kirigami.Units.smallSpacing / 2)
|
||||
bottomMargin: Math.round(Kirigami.Units.smallSpacing / 2)
|
||||
|
||||
model: settings.currentIgnoreList
|
||||
delegate: Delegates.RoundedItemDelegate {
|
||||
id: wordDelegate
|
||||
|
||||
required property var modelData
|
||||
|
||||
text: modelData
|
||||
|
||||
contentItem: RowLayout {
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
Delegates.DefaultContentItem {
|
||||
itemDelegate: wordDelegate
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
QQC2.ToolButton {
|
||||
text: i18nc("@action:button", "Delete word")
|
||||
icon.name: "delete"
|
||||
display: QQC2.ToolButton.IconOnly
|
||||
onClicked: {
|
||||
remove(wordDelegate.modelData);
|
||||
if (instantApply) {
|
||||
settings.save();
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.ToolTip.text: text
|
||||
QQC2.ToolTip.visible: hovered
|
||||
QQC2.ToolTip.delay: Kirigami.ToolTip.toolTipDelay
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function add(word: string) {
|
||||
const dictionary = settings.currentIgnoreList;
|
||||
dictionary.push(word);
|
||||
settings.currentIgnoreList = dictionary;
|
||||
}
|
||||
|
||||
function remove(word: string) {
|
||||
settings.currentIgnoreList = settings.currentIgnoreList.filter((value, _, _) => {
|
||||
return value !== word;
|
||||
});
|
||||
}
|
||||
}
|
||||
61
src/settings/ThemeRadioButton.qml
Normal file
61
src/settings/ThemeRadioButton.qml
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright 2021 Marco Martin <mart@kde.org>
|
||||
// Copyright 2018 Furkan Tokac <furkantokac34@gmail.com>
|
||||
// Copyright 2019 Nate Graham <nate@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
QQC2.RadioButton {
|
||||
id: root
|
||||
|
||||
implicitWidth: contentItem.implicitWidth
|
||||
implicitHeight: contentItem.implicitHeight
|
||||
|
||||
property alias innerObject: contentLayout.children
|
||||
property bool thin
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
Kirigami.ShadowedRectangle {
|
||||
implicitWidth: implicitHeight * 1.6
|
||||
implicitHeight: root.thin ? Kirigami.Units.gridUnit * 5 : Kirigami.Units.gridUnit * 6
|
||||
radius: Kirigami.Units.smallSpacing
|
||||
Kirigami.Theme.inherit: false
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
|
||||
shadow.xOffset: 0
|
||||
shadow.yOffset: 2
|
||||
shadow.size: 10
|
||||
shadow.color: Qt.rgba(0, 0, 0, 0.3)
|
||||
|
||||
color: {
|
||||
if (root.checked) {
|
||||
return Kirigami.Theme.highlightColor;
|
||||
} else if (root.hovered) {
|
||||
// Match appearance of hovered list items
|
||||
return Qt.rgba(Kirigami.Theme.highlightColor.r, Kirigami.Theme.highlightColor.g, Kirigami.Theme.highlightColor.b, 0.5);
|
||||
} else {
|
||||
return Kirigami.Theme.backgroundColor;
|
||||
}
|
||||
}
|
||||
ColumnLayout {
|
||||
id: contentLayout
|
||||
anchors.fill: parent
|
||||
anchors.margins: Kirigami.Units.smallSpacing
|
||||
clip: true
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.Label {
|
||||
id: label
|
||||
Layout.fillWidth: true
|
||||
text: root.text
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
|
||||
indicator: Item {}
|
||||
background: Item {}
|
||||
}
|
||||
Reference in New Issue
Block a user