mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-23 19:38:48 +03:00
Lint qml
This commit is contained in:
parent
a8bd8dddbf
commit
da96558bb9
93 changed files with 4804 additions and 6361 deletions
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
FILES=$(find src resources/qml -type f \( -iname "*.cpp" -o -iname "*.h" -o -iname "*.qml" \))
|
FILES=$(find src qml -type f \( -iname "*.cpp" -o -iname "*.h" -o -iname "*.qml" \))
|
||||||
|
|
||||||
reuse addheader --copyright="Nheko Contributors" --license="GPL-3.0-or-later" $FILES
|
reuse addheader --copyright="Nheko Contributors" --license="GPL-3.0-or-later" $FILES
|
||||||
|
|
||||||
|
|
5
.qmlformat.ini
Normal file
5
.qmlformat.ini
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
[General]
|
||||||
|
IndentWidth=4
|
||||||
|
NewlineType=native
|
||||||
|
NormalizeOrder=true
|
||||||
|
UseTabs=false
|
23
.qmllint.ini
Normal file
23
.qmllint.ini
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
[General]
|
||||||
|
AdditionalQmlImportPaths=
|
||||||
|
DisableDefaultImports=false
|
||||||
|
OverwriteImportTypes=
|
||||||
|
ResourcePath=
|
||||||
|
|
||||||
|
[Warnings]
|
||||||
|
AttachedPropertyReuse=disable
|
||||||
|
BadSignalHandler=warning
|
||||||
|
CompilerWarnings=disable
|
||||||
|
ControlsSanity=disable
|
||||||
|
DeferredPropertyId=warning
|
||||||
|
Deprecated=warning
|
||||||
|
ImportFailure=warning
|
||||||
|
InheritanceCycle=warning
|
||||||
|
MultilineStrings=info
|
||||||
|
PropertyAlias=warning
|
||||||
|
RequiredProperty=warning
|
||||||
|
TypeError=warning
|
||||||
|
UnknownProperty=warning
|
||||||
|
UnqualifiedAccess=disable
|
||||||
|
UnusedImports=info
|
||||||
|
WithStatement=warning
|
|
@ -1,9 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
import "ui"
|
||||||
import "./ui"
|
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
import QtQuick.Window 2.15
|
import QtQuick.Window 2.15
|
||||||
|
@ -12,70 +10,54 @@ import im.nheko
|
||||||
AbstractButton {
|
AbstractButton {
|
||||||
id: avatar
|
id: avatar
|
||||||
|
|
||||||
|
property alias color: bg.color
|
||||||
|
property bool crop: true
|
||||||
|
property string displayName
|
||||||
|
property string roomid
|
||||||
|
property alias textColor: label.color
|
||||||
property string url
|
property string url
|
||||||
property string userid
|
property string userid
|
||||||
property string roomid
|
|
||||||
property string displayName
|
|
||||||
property alias textColor: label.color
|
|
||||||
property bool crop: true
|
|
||||||
property alias color: bg.color
|
|
||||||
|
|
||||||
width: 48
|
|
||||||
height: 48
|
height: 48
|
||||||
|
width: 48
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
id: bg
|
id: bg
|
||||||
radius: Settings.avatarCircles ? height / 2 : height / 8
|
|
||||||
color: timelineRoot.palette.alternateBase
|
color: timelineRoot.palette.alternateBase
|
||||||
|
radius: Settings.avatarCircles ? height / 2 : height / 8
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: label
|
id: label
|
||||||
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
color: timelineRoot.palette.text
|
||||||
|
enabled: false
|
||||||
|
font.pixelSize: avatar.height / 2
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
text: TimelineManager.escapeEmoji(displayName ? String.fromCodePoint(displayName.codePointAt(0)) : "")
|
text: TimelineManager.escapeEmoji(displayName ? String.fromCodePoint(displayName.codePointAt(0)) : "")
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
font.pixelSize: avatar.height / 2
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
visible: img.status != Image.Ready && !Settings.useIdenticon
|
visible: img.status != Image.Ready && !Settings.useIdenticon
|
||||||
color: timelineRoot.palette.text
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: identicon
|
id: identicon
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: Settings.useIdenticon && img.status != Image.Ready
|
|
||||||
source: Settings.useIdenticon ? ("image://jdenticon/" + (userid !== "" ? userid : roomid) + "?radius=" + (Settings.avatarCircles ? 100 : 25)) : ""
|
source: Settings.useIdenticon ? ("image://jdenticon/" + (userid !== "" ? userid : roomid) + "?radius=" + (Settings.avatarCircles ? 100 : 25)) : ""
|
||||||
|
visible: Settings.useIdenticon && img.status != Image.Ready
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: img
|
id: img
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
fillMode: avatar.crop ? Image.PreserveAspectCrop : Image.PreserveAspectFit
|
fillMode: avatar.crop ? Image.PreserveAspectCrop : Image.PreserveAspectFit
|
||||||
mipmap: true
|
mipmap: true
|
||||||
smooth: true
|
smooth: true
|
||||||
sourceSize.width: avatar.width * Screen.devicePixelRatio
|
|
||||||
sourceSize.height: avatar.height * Screen.devicePixelRatio
|
|
||||||
source: avatar.url ? (avatar.url + "?radius=" + (Settings.avatarCircles ? 100 : 25) + ((avatar.crop) ? "" : "&scale")) : ""
|
source: avatar.url ? (avatar.url + "?radius=" + (Settings.avatarCircles ? 100 : 25) + ((avatar.crop) ? "" : "&scale")) : ""
|
||||||
|
sourceSize.height: avatar.height * Screen.devicePixelRatio
|
||||||
|
sourceSize.width: avatar.width * Screen.devicePixelRatio
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: onlineIndicator
|
id: onlineIndicator
|
||||||
|
|
||||||
anchors.bottom: avatar.bottom
|
|
||||||
anchors.right: avatar.right
|
|
||||||
visible: !!userid
|
|
||||||
height: avatar.height / 6
|
|
||||||
width: height
|
|
||||||
radius: Settings.avatarCircles ? height / 2 : height / 8
|
|
||||||
color: updatePresence()
|
|
||||||
|
|
||||||
function updatePresence() {
|
function updatePresence() {
|
||||||
switch (Presence.userPresence(userid)) {
|
switch (Presence.userPresence(userid)) {
|
||||||
case "online":
|
case "online":
|
||||||
|
@ -89,22 +71,28 @@ AbstractButton {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
anchors.bottom: avatar.bottom
|
||||||
target: Presence
|
anchors.right: avatar.right
|
||||||
|
color: updatePresence()
|
||||||
|
height: avatar.height / 6
|
||||||
|
radius: Settings.avatarCircles ? height / 2 : height / 8
|
||||||
|
visible: !!userid
|
||||||
|
width: height
|
||||||
|
|
||||||
|
Connections {
|
||||||
function onPresenceChanged(id) {
|
function onPresenceChanged(id) {
|
||||||
if (id == userid) onlineIndicator.color = onlineIndicator.updatePresence();
|
if (id == userid)
|
||||||
|
onlineIndicator.color = onlineIndicator.updatePresence();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
target: Presence
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NhekoCursorShape {
|
NhekoCursorShape {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
Ripple {
|
Ripple {
|
||||||
color: Qt.rgba(timelineRoot.palette.alternateBase.r, timelineRoot.palette.alternateBase.g, timelineRoot.palette.alternateBase.b, 0.5)
|
color: Qt.rgba(timelineRoot.palette.alternateBase.r, timelineRoot.palette.alternateBase.g, timelineRoot.palette.alternateBase.b, 0.5)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts 1.3
|
||||||
|
@ -14,41 +12,28 @@ import QtQml 2.15
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: chatPage
|
id: chatPage
|
||||||
|
|
||||||
color: timelineRoot.palette.window
|
color: timelineRoot.palette.window
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
spacing: 0
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: offlineIndicator
|
id: offlineIndicator
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: offlineLabel.height + Nheko.paddingMedium
|
||||||
color: Nheko.theme.error
|
color: Nheko.theme.error
|
||||||
visible: !TimelineManager.isConnected
|
visible: !TimelineManager.isConnected
|
||||||
Layout.preferredHeight: offlineLabel.height + Nheko.paddingMedium
|
|
||||||
Layout.fillWidth: true
|
|
||||||
z: 1
|
z: 1
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: offlineLabel
|
id: offlineLabel
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: qsTr("No network connection")
|
text: qsTr("No network connection")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AdaptiveLayout {
|
AdaptiveLayout {
|
||||||
id: adaptiveView
|
id: adaptiveView
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
singlePageMode: communityListC.preferredWidth + roomListC.preferredWidth + timlineViewC.minimumWidth > width
|
|
||||||
pageIndex: 1
|
|
||||||
|
|
||||||
Component.onCompleted: initializePageIndex()
|
|
||||||
onSinglePageModeChanged: initializePageIndex()
|
|
||||||
|
|
||||||
function initializePageIndex() {
|
function initializePageIndex() {
|
||||||
if (!singlePageMode)
|
if (!singlePageMode)
|
||||||
adaptiveView.pageIndex = 0;
|
adaptiveView.pageIndex = 0;
|
||||||
|
@ -58,91 +43,80 @@ Rectangle {
|
||||||
adaptiveView.pageIndex = 1;
|
adaptiveView.pageIndex = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
pageIndex: 1
|
||||||
|
singlePageMode: communityListC.preferredWidth + roomListC.preferredWidth + timlineViewC.minimumWidth > width
|
||||||
|
|
||||||
|
Component.onCompleted: initializePageIndex()
|
||||||
|
onSinglePageModeChanged: initializePageIndex()
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: Rooms
|
|
||||||
function onCurrentRoomChanged() {
|
function onCurrentRoomChanged() {
|
||||||
adaptiveView.initializePageIndex();
|
adaptiveView.initializePageIndex();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
target: Rooms
|
||||||
|
}
|
||||||
AdaptiveLayoutElement {
|
AdaptiveLayoutElement {
|
||||||
id: communityListC
|
id: communityListC
|
||||||
|
|
||||||
visible: Settings.groupView
|
|
||||||
minimumWidth: communitiesList.avatarSize * 4 + Nheko.paddingMedium * 2
|
|
||||||
collapsedWidth: communitiesList.avatarSize + 2 * Nheko.paddingMedium
|
collapsedWidth: communitiesList.avatarSize + 2 * Nheko.paddingMedium
|
||||||
preferredWidth: Settings.communityListWidth >= minimumWidth ? Settings.communityListWidth : collapsedWidth
|
|
||||||
maximumWidth: communitiesList.avatarSize * 10 + 2 * Nheko.paddingMedium
|
maximumWidth: communitiesList.avatarSize * 10 + 2 * Nheko.paddingMedium
|
||||||
|
minimumWidth: communitiesList.avatarSize * 4 + Nheko.paddingMedium * 2
|
||||||
|
preferredWidth: Settings.communityListWidth >= minimumWidth ? Settings.communityListWidth : collapsedWidth
|
||||||
|
visible: Settings.groupView
|
||||||
|
|
||||||
CommunitiesList {
|
CommunitiesList {
|
||||||
id: communitiesList
|
id: communitiesList
|
||||||
|
|
||||||
collapsed: parent.collapsed
|
collapsed: parent.collapsed
|
||||||
}
|
}
|
||||||
|
|
||||||
Binding {
|
Binding {
|
||||||
target: Settings
|
delayed: true
|
||||||
property: 'communityListWidth'
|
property: 'communityListWidth'
|
||||||
|
restoreMode: Binding.RestoreBindingOrValue
|
||||||
|
target: Settings
|
||||||
value: communityListC.preferredWidth
|
value: communityListC.preferredWidth
|
||||||
when: !adaptiveView.singlePageMode
|
when: !adaptiveView.singlePageMode
|
||||||
delayed: true
|
|
||||||
restoreMode: Binding.RestoreBindingOrValue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AdaptiveLayoutElement {
|
AdaptiveLayoutElement {
|
||||||
id: roomListC
|
id: roomListC
|
||||||
|
|
||||||
minimumWidth: roomlist.avatarSize * 4 + Nheko.paddingSmall * 2
|
|
||||||
preferredWidth: (Settings.roomListWidth == - 1)
|
|
||||||
? (roomlist.avatarSize * 5 + Nheko.paddingSmall * 2)
|
|
||||||
: (Settings.roomListWidth >= minimumWidth ? Settings.roomListWidth : collapsedWidth)
|
|
||||||
maximumWidth: roomlist.avatarSize * 10 + Nheko.paddingSmall * 2
|
|
||||||
collapsedWidth: roomlist.avatarSize + 2 * Nheko.paddingMedium
|
collapsedWidth: roomlist.avatarSize + 2 * Nheko.paddingMedium
|
||||||
|
maximumWidth: roomlist.avatarSize * 10 + Nheko.paddingSmall * 2
|
||||||
|
minimumWidth: roomlist.avatarSize * 4 + Nheko.paddingSmall * 2
|
||||||
|
preferredWidth: (Settings.roomListWidth == -1) ? (roomlist.avatarSize * 5 + Nheko.paddingSmall * 2) : (Settings.roomListWidth >= minimumWidth ? Settings.roomListWidth : collapsedWidth)
|
||||||
|
|
||||||
RoomList {
|
RoomList {
|
||||||
id: roomlist
|
id: roomlist
|
||||||
|
|
||||||
height: adaptiveView.height
|
|
||||||
collapsed: parent.collapsed
|
collapsed: parent.collapsed
|
||||||
|
height: adaptiveView.height
|
||||||
}
|
}
|
||||||
|
|
||||||
Binding {
|
Binding {
|
||||||
target: Settings
|
delayed: true
|
||||||
property: 'roomListWidth'
|
property: 'roomListWidth'
|
||||||
|
restoreMode: Binding.RestoreBindingOrValue
|
||||||
|
target: Settings
|
||||||
value: roomListC.preferredWidth
|
value: roomListC.preferredWidth
|
||||||
when: !adaptiveView.singlePageMode
|
when: !adaptiveView.singlePageMode
|
||||||
delayed: true
|
|
||||||
restoreMode: Binding.RestoreBindingOrValue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AdaptiveLayoutElement {
|
AdaptiveLayoutElement {
|
||||||
id: timlineViewC
|
id: timlineViewC
|
||||||
|
|
||||||
minimumWidth: fontMetrics.averageCharacterWidth * 40 + Nheko.avatarSize + 2 * Nheko.paddingMedium
|
minimumWidth: fontMetrics.averageCharacterWidth * 40 + Nheko.avatarSize + 2 * Nheko.paddingMedium
|
||||||
|
|
||||||
TimelineView {
|
TimelineView {
|
||||||
id: timeline
|
id: timeline
|
||||||
|
|
||||||
showBackButton: adaptiveView.singlePageMode
|
|
||||||
room: Rooms.currentRoom
|
room: Rooms.currentRoom
|
||||||
roomPreview: Rooms.currentRoomPreview.roomid ? Rooms.currentRoomPreview : null
|
roomPreview: Rooms.currentRoomPreview.roomid ? Rooms.currentRoomPreview : null
|
||||||
|
showBackButton: adaptiveView.singlePageMode
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PrivacyScreen {
|
PrivacyScreen {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: Settings.privacyScreen
|
|
||||||
screenTimeout: Settings.privacyScreenTimeout
|
screenTimeout: Settings.privacyScreenTimeout
|
||||||
timelineRoot: adaptiveView
|
timelineRoot: adaptiveView
|
||||||
|
visible: Settings.privacyScreen
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
import "dialogs"
|
||||||
import "./dialogs"
|
|
||||||
import Qt.labs.platform 1.1 as Platform
|
import Qt.labs.platform 1.1 as Platform
|
||||||
import QtQml 2.12
|
import QtQml 2.12
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
|
@ -13,19 +11,142 @@ import im.nheko
|
||||||
|
|
||||||
Page {
|
Page {
|
||||||
id: communitySidebar
|
id: communitySidebar
|
||||||
|
|
||||||
//leftPadding: Nheko.paddingSmall
|
//leftPadding: Nheko.paddingSmall
|
||||||
//rightPadding: Nheko.paddingSmall
|
//rightPadding: Nheko.paddingSmall
|
||||||
property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 1.6)
|
property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 1.6)
|
||||||
property bool collapsed: false
|
property bool collapsed: false
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: Nheko.theme.sidebarBackground
|
||||||
|
}
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: communitiesList
|
id: communitiesList
|
||||||
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
height: parent.height
|
height: parent.height
|
||||||
model: Communities.filtered()
|
model: Communities.filtered()
|
||||||
|
|
||||||
|
delegate: ItemDelegate {
|
||||||
|
id: communityItem
|
||||||
|
|
||||||
|
property color backgroundColor: timelineRoot.palette.window
|
||||||
|
property color bubbleBackground: timelineRoot.palette.highlight
|
||||||
|
property color bubbleText: timelineRoot.palette.highlightedText
|
||||||
|
property color importantText: timelineRoot.palette.text
|
||||||
|
property color unimportantText: timelineRoot.palette.placeholderText
|
||||||
|
|
||||||
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
|
ToolTip.text: model.tooltip
|
||||||
|
ToolTip.visible: hovered && collapsed
|
||||||
|
height: avatarSize + 2 * Nheko.paddingMedium
|
||||||
|
state: "normal"
|
||||||
|
width: ListView.view.width
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: backgroundColor
|
||||||
|
}
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "highlight"
|
||||||
|
when: (communityItem.hovered || model.hidden) && !(Communities.currentTagId == model.id)
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
backgroundColor: timelineRoot.palette.dark
|
||||||
|
bubbleBackground: timelineRoot.palette.highlight
|
||||||
|
bubbleText: timelineRoot.palette.highlightedText
|
||||||
|
importantText: timelineRoot.palette.brightText
|
||||||
|
target: communityItem
|
||||||
|
unimportantText: timelineRoot.palette.brightText
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "selected"
|
||||||
|
when: Communities.currentTagId == model.id
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
backgroundColor: timelineRoot.palette.highlight
|
||||||
|
bubbleBackground: timelineRoot.palette.highlightedText
|
||||||
|
bubbleText: timelineRoot.palette.highlight
|
||||||
|
importantText: timelineRoot.palette.highlightedText
|
||||||
|
target: communityItem
|
||||||
|
unimportantText: timelineRoot.palette.highlightedText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
onClicked: Communities.setCurrentTagId(model.id)
|
||||||
|
onPressAndHold: communityContextMenu.show(model.id)
|
||||||
|
|
||||||
|
Item {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
TapHandler {
|
||||||
|
acceptedButtons: Qt.RightButton
|
||||||
|
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad
|
||||||
|
gesturePolicy: TapHandler.ReleaseWithinBounds
|
||||||
|
|
||||||
|
onSingleTapped: communityContextMenu.show(model.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RowLayout {
|
||||||
|
id: r
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Nheko.paddingMedium + (communitySidebar.collapsed ? 0 : (fontMetrics.lineSpacing * model.depth))
|
||||||
|
anchors.margins: Nheko.paddingMedium
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
|
ImageButton {
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.preferredHeight: fontMetrics.lineSpacing
|
||||||
|
Layout.preferredWidth: fontMetrics.lineSpacing
|
||||||
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
|
ToolTip.text: model.collapsed ? qsTr("Expand") : qsTr("Collapse")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
height: fontMetrics.lineSpacing
|
||||||
|
hoverEnabled: true
|
||||||
|
image: model.collapsed ? ":/icons/icons/ui/collapsed.svg" : ":/icons/icons/ui/expanded.svg"
|
||||||
|
visible: !communitySidebar.collapsed && model.collapsible
|
||||||
|
width: fontMetrics.lineSpacing
|
||||||
|
|
||||||
|
onClicked: model.collapsed = !model.collapsed
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.preferredWidth: fontMetrics.lineSpacing
|
||||||
|
visible: !communitySidebar.collapsed && !model.collapsible && Communities.containsSubspaces
|
||||||
|
}
|
||||||
|
Avatar {
|
||||||
|
id: avatar
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
color: communityItem.backgroundColor
|
||||||
|
displayName: model.displayName
|
||||||
|
enabled: false
|
||||||
|
height: avatarSize
|
||||||
|
roomid: model.id
|
||||||
|
url: {
|
||||||
|
if (model.avatarUrl.startsWith("mxc://"))
|
||||||
|
return model.avatarUrl.replace("mxc://", "image://MxcImage/");
|
||||||
|
else
|
||||||
|
return "image://colorimage/" + model.avatarUrl + "?" + communityItem.unimportantText;
|
||||||
|
}
|
||||||
|
width: avatarSize
|
||||||
|
}
|
||||||
|
ElidedLabel {
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: communityItem.importantText
|
||||||
|
elideWidth: width
|
||||||
|
fullText: model.displayName
|
||||||
|
textFormat: Text.PlainText
|
||||||
|
visible: !communitySidebar.collapsed
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Platform.Menu {
|
Platform.Menu {
|
||||||
id: communityContextMenu
|
id: communityContextMenu
|
||||||
|
|
||||||
|
@ -38,143 +159,9 @@ Page {
|
||||||
|
|
||||||
Platform.MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Hide rooms with this tag or from this space by default.")
|
text: qsTr("Hide rooms with this tag or from this space by default.")
|
||||||
|
|
||||||
onTriggered: Communities.toggleTagId(communityContextMenu.tagId)
|
onTriggered: Communities.toggleTagId(communityContextMenu.tagId)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: ItemDelegate {
|
|
||||||
id: communityItem
|
|
||||||
|
|
||||||
property color backgroundColor: timelineRoot.palette.window
|
|
||||||
property color importantText: timelineRoot.palette.text
|
|
||||||
property color unimportantText: timelineRoot.palette.placeholderText
|
|
||||||
property color bubbleBackground: timelineRoot.palette.highlight
|
|
||||||
property color bubbleText: timelineRoot.palette.highlightedText
|
|
||||||
|
|
||||||
height: avatarSize + 2 * Nheko.paddingMedium
|
|
||||||
width: ListView.view.width
|
|
||||||
state: "normal"
|
|
||||||
ToolTip.visible: hovered && collapsed
|
|
||||||
ToolTip.text: model.tooltip
|
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
|
||||||
onClicked: Communities.setCurrentTagId(model.id)
|
|
||||||
onPressAndHold: communityContextMenu.show(model.id)
|
|
||||||
states: [
|
|
||||||
State {
|
|
||||||
name: "highlight"
|
|
||||||
when: (communityItem.hovered || model.hidden) && !(Communities.currentTagId == model.id)
|
|
||||||
|
|
||||||
PropertyChanges {
|
|
||||||
target: communityItem
|
|
||||||
backgroundColor: timelineRoot.palette.dark
|
|
||||||
importantText: timelineRoot.palette.brightText
|
|
||||||
unimportantText: timelineRoot.palette.brightText
|
|
||||||
bubbleBackground: timelineRoot.palette.highlight
|
|
||||||
bubbleText: timelineRoot.palette.highlightedText
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
State {
|
|
||||||
name: "selected"
|
|
||||||
when: Communities.currentTagId == model.id
|
|
||||||
|
|
||||||
PropertyChanges {
|
|
||||||
target: communityItem
|
|
||||||
backgroundColor: timelineRoot.palette.highlight
|
|
||||||
importantText: timelineRoot.palette.highlightedText
|
|
||||||
unimportantText: timelineRoot.palette.highlightedText
|
|
||||||
bubbleBackground: timelineRoot.palette.highlightedText
|
|
||||||
bubbleText: timelineRoot.palette.highlight
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
Item {
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
TapHandler {
|
|
||||||
acceptedButtons: Qt.RightButton
|
|
||||||
onSingleTapped: communityContextMenu.show(model.id)
|
|
||||||
gesturePolicy: TapHandler.ReleaseWithinBounds
|
|
||||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: r
|
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: Nheko.paddingMedium
|
|
||||||
anchors.leftMargin: Nheko.paddingMedium + (communitySidebar.collapsed ? 0 : (fontMetrics.lineSpacing * model.depth))
|
|
||||||
|
|
||||||
ImageButton {
|
|
||||||
visible: !communitySidebar.collapsed && model.collapsible
|
|
||||||
Layout.preferredHeight: fontMetrics.lineSpacing
|
|
||||||
Layout.preferredWidth: fontMetrics.lineSpacing
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
height: fontMetrics.lineSpacing
|
|
||||||
width: fontMetrics.lineSpacing
|
|
||||||
image: model.collapsed ? ":/icons/icons/ui/collapsed.svg" : ":/icons/icons/ui/expanded.svg"
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
|
||||||
ToolTip.text: model.collapsed ? qsTr("Expand") : qsTr("Collapse")
|
|
||||||
hoverEnabled: true
|
|
||||||
|
|
||||||
onClicked: model.collapsed = !model.collapsed
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
Layout.preferredWidth: fontMetrics.lineSpacing
|
|
||||||
visible: !communitySidebar.collapsed && !model.collapsible && Communities.containsSubspaces
|
|
||||||
}
|
|
||||||
|
|
||||||
Avatar {
|
|
||||||
id: avatar
|
|
||||||
|
|
||||||
enabled: false
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
height: avatarSize
|
|
||||||
width: avatarSize
|
|
||||||
url: {
|
|
||||||
if (model.avatarUrl.startsWith("mxc://"))
|
|
||||||
return model.avatarUrl.replace("mxc://", "image://MxcImage/");
|
|
||||||
else
|
|
||||||
return "image://colorimage/" + model.avatarUrl + "?" + communityItem.unimportantText;
|
|
||||||
}
|
|
||||||
roomid: model.id
|
|
||||||
displayName: model.displayName
|
|
||||||
color: communityItem.backgroundColor
|
|
||||||
}
|
|
||||||
|
|
||||||
ElidedLabel {
|
|
||||||
visible: !communitySidebar.collapsed
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
color: communityItem.importantText
|
|
||||||
Layout.fillWidth: true
|
|
||||||
elideWidth: width
|
|
||||||
fullText: model.displayName
|
|
||||||
textFormat: Text.PlainText
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: backgroundColor
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: Nheko.theme.sidebarBackground
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,115 +1,90 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import im.nheko
|
import im.nheko
|
||||||
import im.nheko
|
import im.nheko
|
||||||
|
import "ui"
|
||||||
import "./ui/"
|
|
||||||
|
|
||||||
Control {
|
Control {
|
||||||
id: popup
|
id: popup
|
||||||
|
|
||||||
property alias currentIndex: listView.currentIndex
|
|
||||||
property string completerName
|
|
||||||
property var completer
|
|
||||||
property bool bottomToTop: true
|
|
||||||
property bool fullWidth: false
|
|
||||||
property bool centerRowContent: true
|
|
||||||
property int avatarHeight: 24
|
property int avatarHeight: 24
|
||||||
property int avatarWidth: 24
|
property int avatarWidth: 24
|
||||||
|
property bool bottomToTop: true
|
||||||
|
property bool centerRowContent: true
|
||||||
|
property var completer
|
||||||
|
property string completerName
|
||||||
|
property alias count: listView.count
|
||||||
|
property alias currentIndex: listView.currentIndex
|
||||||
|
property bool fullWidth: false
|
||||||
property int rowMargin: 0
|
property int rowMargin: 0
|
||||||
property int rowSpacing: 5
|
property int rowSpacing: 5
|
||||||
property alias count: listView.count
|
|
||||||
|
|
||||||
signal completionClicked(string completion)
|
signal completionClicked(string completion)
|
||||||
signal completionSelected(string id)
|
signal completionSelected(string id)
|
||||||
|
|
||||||
function up() {
|
|
||||||
if (bottomToTop)
|
|
||||||
down_();
|
|
||||||
else
|
|
||||||
up_();
|
|
||||||
}
|
|
||||||
|
|
||||||
function down() {
|
|
||||||
if (bottomToTop)
|
|
||||||
up_();
|
|
||||||
else
|
|
||||||
down_();
|
|
||||||
}
|
|
||||||
|
|
||||||
function up_() {
|
|
||||||
currentIndex = currentIndex - 1;
|
|
||||||
if (currentIndex == -2)
|
|
||||||
currentIndex = listView.count - 1;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function down_() {
|
|
||||||
currentIndex = currentIndex + 1;
|
|
||||||
if (currentIndex >= listView.count)
|
|
||||||
currentIndex = -1;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function currentCompletion() {
|
function currentCompletion() {
|
||||||
if (currentIndex > -1 && currentIndex < listView.count)
|
if (currentIndex > -1 && currentIndex < listView.count)
|
||||||
return completer.completionAt(currentIndex);
|
return completer.completionAt(currentIndex);
|
||||||
else
|
else
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
function down() {
|
||||||
|
if (bottomToTop)
|
||||||
|
up_();
|
||||||
|
else
|
||||||
|
down_();
|
||||||
|
}
|
||||||
|
function down_() {
|
||||||
|
currentIndex = currentIndex + 1;
|
||||||
|
if (currentIndex >= listView.count)
|
||||||
|
currentIndex = -1;
|
||||||
|
}
|
||||||
function finishCompletion() {
|
function finishCompletion() {
|
||||||
if (popup.completerName == "room")
|
if (popup.completerName == "room")
|
||||||
popup.completionSelected(listView.itemAtIndex(currentIndex).modelData.roomid);
|
popup.completionSelected(listView.itemAtIndex(currentIndex).modelData.roomid);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
function up() {
|
||||||
onCompleterNameChanged: {
|
if (bottomToTop)
|
||||||
if (completerName) {
|
down_();
|
||||||
completer = TimelineManager.completerFor(completerName, completerName == "room" ? "" : room.roomId);
|
else
|
||||||
completer.setSearchString("");
|
up_();
|
||||||
} else {
|
}
|
||||||
completer = undefined;
|
function up_() {
|
||||||
}
|
currentIndex = currentIndex - 1;
|
||||||
currentIndex = -1
|
if (currentIndex == -2)
|
||||||
|
currentIndex = listView.count - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bottomPadding: 1
|
bottomPadding: 1
|
||||||
leftPadding: 1
|
leftPadding: 1
|
||||||
topPadding: 1
|
|
||||||
rightPadding: 1
|
rightPadding: 1
|
||||||
|
topPadding: 1
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
border.color: timelineRoot.palette.mid
|
||||||
|
color: timelineRoot.palette.base
|
||||||
|
}
|
||||||
contentItem: ListView {
|
contentItem: ListView {
|
||||||
id: listView
|
id: listView
|
||||||
|
clip: true
|
||||||
|
highlightFollowsCurrentItem: true
|
||||||
|
|
||||||
// If we have fewer than 7 items, just use the list view's content height.
|
// If we have fewer than 7 items, just use the list view's content height.
|
||||||
// Otherwise, we want to show 7 items. Each item consists of row spacing between rows, row margins
|
// Otherwise, we want to show 7 items. Each item consists of row spacing between rows, row margins
|
||||||
// on each side of a row, 1px of padding above the first item and below the last item, and nominally
|
// on each side of a row, 1px of padding above the first item and below the last item, and nominally
|
||||||
// some kind of content height. avatarHeight is used for just about every delegate, so we're using
|
// some kind of content height. avatarHeight is used for just about every delegate, so we're using
|
||||||
// that until we find something better. Put is all together and you have the formula below!
|
// that until we find something better. Put is all together and you have the formula below!
|
||||||
implicitHeight: Math.min(contentHeight, 6*rowSpacing + 7*(popup.avatarHeight + 2*rowMargin))
|
implicitHeight: Math.min(contentHeight, 6 * rowSpacing + 7 * (popup.avatarHeight + 2 * rowMargin))
|
||||||
clip: true
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: deadTimer
|
|
||||||
interval: 50
|
|
||||||
}
|
|
||||||
|
|
||||||
onContentYChanged: deadTimer.restart()
|
|
||||||
|
|
||||||
reuseItems: true
|
|
||||||
implicitWidth: listView.contentItem.childrenRect.width
|
implicitWidth: listView.contentItem.childrenRect.width
|
||||||
model: completer
|
model: completer
|
||||||
verticalLayoutDirection: popup.bottomToTop ? ListView.BottomToTop : ListView.TopToBottom
|
|
||||||
spacing: rowSpacing
|
|
||||||
pixelAligned: true
|
pixelAligned: true
|
||||||
highlightFollowsCurrentItem: true
|
reuseItems: true
|
||||||
|
spacing: rowSpacing
|
||||||
|
verticalLayoutDirection: popup.bottomToTop ? ListView.BottomToTop : ListView.TopToBottom
|
||||||
|
|
||||||
delegate: Rectangle {
|
delegate: Rectangle {
|
||||||
property variant modelData: model
|
property variant modelData: model
|
||||||
|
@ -120,193 +95,176 @@ Control {
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
onPositionChanged: if (!listView.moving && !deadTimer.running) popup.currentIndex = model.index
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
popup.completionClicked(completer.completionAt(model.index));
|
popup.completionClicked(completer.completionAt(model.index));
|
||||||
if (popup.completerName == "room")
|
if (popup.completerName == "room")
|
||||||
popup.completionSelected(model.roomid);
|
popup.completionSelected(model.roomid);
|
||||||
}
|
}
|
||||||
|
onPositionChanged: if (!listView.moving && !deadTimer.running)
|
||||||
|
popup.currentIndex = model.index
|
||||||
}
|
}
|
||||||
Ripple {
|
Ripple {
|
||||||
color: Qt.rgba(timelineRoot.palette.base.r, timelineRoot.palette.base.g, timelineRoot.palette.base.b, 0.5)
|
color: Qt.rgba(timelineRoot.palette.base.r, timelineRoot.palette.base.g, timelineRoot.palette.base.b, 0.5)
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChooser {
|
DelegateChooser {
|
||||||
id: chooser
|
id: chooser
|
||||||
|
|
||||||
roleValue: popup.completerName
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: popup.rowMargin
|
anchors.margins: popup.rowMargin
|
||||||
enabled: false
|
enabled: false
|
||||||
|
roleValue: popup.completerName
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: "user"
|
roleValue: "user"
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: del
|
id: del
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: rowSpacing
|
spacing: rowSpacing
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
height: popup.avatarHeight
|
|
||||||
width: popup.avatarWidth
|
|
||||||
displayName: model.displayName
|
displayName: model.displayName
|
||||||
userid: model.userid
|
height: popup.avatarHeight
|
||||||
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
|
userid: model.userid
|
||||||
|
width: popup.avatarWidth
|
||||||
|
|
||||||
onClicked: popup.completionClicked(completer.completionAt(model.index))
|
onClicked: popup.completionClicked(completer.completionAt(model.index))
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: model.displayName
|
|
||||||
color: model.index == popup.currentIndex ? timelineRoot.palette.highlightedText : timelineRoot.palette.text
|
color: model.index == popup.currentIndex ? timelineRoot.palette.highlightedText : timelineRoot.palette.text
|
||||||
|
text: model.displayName
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: "(" + model.userid + ")"
|
|
||||||
color: model.index == popup.currentIndex ? timelineRoot.palette.highlightedText : timelineRoot.palette.placeholderText
|
color: model.index == popup.currentIndex ? timelineRoot.palette.highlightedText : timelineRoot.palette.placeholderText
|
||||||
|
text: "(" + model.userid + ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: "emoji"
|
roleValue: "emoji"
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: del
|
id: del
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: rowSpacing
|
spacing: rowSpacing
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: model.unicode
|
|
||||||
color: model.index == popup.currentIndex ? timelineRoot.palette.highlightedText : timelineRoot.palette.text
|
color: model.index == popup.currentIndex ? timelineRoot.palette.highlightedText : timelineRoot.palette.text
|
||||||
font: Settings.emojiFont
|
font: Settings.emojiFont
|
||||||
|
text: model.unicode
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: model.shortName
|
|
||||||
color: model.index == popup.currentIndex ? timelineRoot.palette.highlightedText : timelineRoot.palette.text
|
color: model.index == popup.currentIndex ? timelineRoot.palette.highlightedText : timelineRoot.palette.text
|
||||||
|
text: model.shortName
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: "customEmoji"
|
roleValue: "customEmoji"
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: del
|
id: del
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: rowSpacing
|
spacing: rowSpacing
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
height: popup.avatarHeight
|
crop: false
|
||||||
width: popup.avatarWidth
|
|
||||||
displayName: model.shortcode
|
displayName: model.shortcode
|
||||||
|
height: popup.avatarHeight
|
||||||
//userid: model.shortcode
|
//userid: model.shortcode
|
||||||
url: model.url.replace("mxc://", "image://MxcImage/")
|
url: model.url.replace("mxc://", "image://MxcImage/")
|
||||||
|
width: popup.avatarWidth
|
||||||
|
|
||||||
onClicked: popup.completionClicked(completer.completionAt(model.index))
|
onClicked: popup.completionClicked(completer.completionAt(model.index))
|
||||||
crop: false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: model.shortcode
|
|
||||||
color: model.index == popup.currentIndex ? timelineRoot.palette.highlightedText : timelineRoot.palette.text
|
color: model.index == popup.currentIndex ? timelineRoot.palette.highlightedText : timelineRoot.palette.text
|
||||||
|
text: model.shortcode
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: "(" + model.packname + ")"
|
|
||||||
color: model.index == popup.currentIndex ? timelineRoot.palette.highlightedText : timelineRoot.palette.placeholderText
|
color: model.index == popup.currentIndex ? timelineRoot.palette.highlightedText : timelineRoot.palette.placeholderText
|
||||||
|
text: "(" + model.packname + ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: "room"
|
roleValue: "room"
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: del
|
id: del
|
||||||
|
|
||||||
anchors.centerIn: centerRowContent ? parent : undefined
|
anchors.centerIn: centerRowContent ? parent : undefined
|
||||||
spacing: rowSpacing
|
spacing: rowSpacing
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
height: popup.avatarHeight
|
|
||||||
width: popup.avatarWidth
|
|
||||||
displayName: model.roomName
|
displayName: model.roomName
|
||||||
|
height: popup.avatarHeight
|
||||||
roomid: model.roomid
|
roomid: model.roomid
|
||||||
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
|
width: popup.avatarWidth
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
popup.completionClicked(completer.completionAt(model.index));
|
popup.completionClicked(completer.completionAt(model.index));
|
||||||
popup.completionSelected(model.roomid);
|
popup.completionSelected(model.roomid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: model.roomName
|
|
||||||
font.pixelSize: popup.avatarHeight * 0.5
|
|
||||||
color: model.index == popup.currentIndex ? timelineRoot.palette.highlightedText : timelineRoot.palette.text
|
color: model.index == popup.currentIndex ? timelineRoot.palette.highlightedText : timelineRoot.palette.text
|
||||||
|
font.pixelSize: popup.avatarHeight * 0.5
|
||||||
|
text: model.roomName
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: "roomAliases"
|
roleValue: "roomAliases"
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: del
|
id: del
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: rowSpacing
|
spacing: rowSpacing
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
height: popup.avatarHeight
|
|
||||||
width: popup.avatarWidth
|
|
||||||
displayName: model.roomName
|
displayName: model.roomName
|
||||||
|
height: popup.avatarHeight
|
||||||
roomid: model.roomid
|
roomid: model.roomid
|
||||||
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
|
width: popup.avatarWidth
|
||||||
|
|
||||||
onClicked: popup.completionClicked(completer.completionAt(model.index))
|
onClicked: popup.completionClicked(completer.completionAt(model.index))
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: model.roomName
|
|
||||||
color: model.index == popup.currentIndex ? timelineRoot.palette.highlightedText : timelineRoot.palette.text
|
color: model.index == popup.currentIndex ? timelineRoot.palette.highlightedText : timelineRoot.palette.text
|
||||||
|
text: model.roomName
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: "(" + model.roomAlias + ")"
|
|
||||||
color: model.index == popup.currentIndex ? timelineRoot.palette.highlightedText : timelineRoot.palette.placeholderText
|
color: model.index == popup.currentIndex ? timelineRoot.palette.highlightedText : timelineRoot.palette.placeholderText
|
||||||
|
text: "(" + model.roomAlias + ")"
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onContentYChanged: deadTimer.restart()
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: deadTimer
|
||||||
|
interval: 50
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onCompleterNameChanged: {
|
||||||
background: Rectangle {
|
if (completerName) {
|
||||||
color: timelineRoot.palette.base
|
completer = TimelineManager.completerFor(completerName, completerName == "room" ? "" : room.roomId);
|
||||||
border.color: timelineRoot.palette.mid
|
completer.setSearchString("");
|
||||||
|
} else {
|
||||||
|
completer = undefined;
|
||||||
|
}
|
||||||
|
currentIndex = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.5
|
import QtQuick.Controls 2.5
|
||||||
import im.nheko
|
import im.nheko
|
||||||
|
@ -10,21 +8,25 @@ import im.nheko
|
||||||
Label {
|
Label {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property alias fullText: metrics.text
|
|
||||||
property alias elideWidth: metrics.elideWidth
|
property alias elideWidth: metrics.elideWidth
|
||||||
property int fullTextWidth: Math.ceil(metrics.advanceWidth)
|
property alias fullText: metrics.text
|
||||||
|
property int fullTextWidth: Math.ceil(metrics2.advanceWidth + 4)
|
||||||
|
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
text: (textFormat == Text.PlainText) ? metrics.elidedText : TimelineManager.escapeEmoji(TimelineManager.htmlEscape(metrics.elidedText))
|
|
||||||
maximumLineCount: 1
|
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
|
maximumLineCount: 1
|
||||||
|
text: (textFormat == Text.PlainText) ? metrics.elidedText : TimelineManager.escapeEmoji(TimelineManager.htmlEscape(metrics.elidedText))
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
|
|
||||||
TextMetrics {
|
TextMetrics {
|
||||||
id: metrics
|
id: metrics
|
||||||
|
|
||||||
font.pointSize: root.font.pointSize
|
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
|
font: root.font
|
||||||
|
}
|
||||||
|
TextMetrics {
|
||||||
|
id: metrics2
|
||||||
|
//elide: Text.ElideRight
|
||||||
|
font: root.font
|
||||||
|
text: metrics.text
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.1
|
import QtQuick.Controls 2.1
|
||||||
import QtQuick.Window 2.15
|
import QtQuick.Window 2.15
|
||||||
|
@ -12,28 +10,36 @@ Image {
|
||||||
id: stateImg
|
id: stateImg
|
||||||
|
|
||||||
property bool encrypted: false
|
property bool encrypted: false
|
||||||
property int trust: Crypto.Unverified
|
|
||||||
|
|
||||||
property string sourceUrl: {
|
property string sourceUrl: {
|
||||||
if (!encrypted)
|
if (!encrypted)
|
||||||
return "image://colorimage/:/icons/icons/ui/shield-filled-cross.svg?";
|
return "image://colorimage/:/icons/icons/ui/shield-filled-cross.svg?";
|
||||||
|
|
||||||
switch (trust) {
|
switch (trust) {
|
||||||
case Crypto.Verified:
|
case Crypto.Verified:
|
||||||
return "image://colorimage/:/icons/icons/ui/shield-filled-checkmark.svg?";
|
return "image://colorimage/:/icons/icons/ui/shield-filled-checkmark.svg?";
|
||||||
case Crypto.TOFU:
|
case Crypto.TOFU:
|
||||||
return "image://colorimage/:/icons/icons/ui/shield-filled.svg?";
|
return "image://colorimage/:/icons/icons/ui/shield-filled.svg?";
|
||||||
case Crypto.Unverified:
|
case Crypto.Unverified:
|
||||||
return "image://colorimage/:/icons/icons/ui/shield-filled-exclamation-mark.svg?";
|
return "image://colorimage/:/icons/icons/ui/shield-filled-exclamation-mark.svg?";
|
||||||
default:
|
default:
|
||||||
return "image://colorimage/:/icons/icons/ui/shield-filled-cross.svg?";
|
return "image://colorimage/:/icons/icons/ui/shield-filled-cross.svg?";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
property int trust: Crypto.Unverified
|
||||||
|
|
||||||
width: 16
|
ToolTip.text: {
|
||||||
|
if (!encrypted)
|
||||||
|
return qsTr("This message is not encrypted!");
|
||||||
|
switch (trust) {
|
||||||
|
case Crypto.Verified:
|
||||||
|
return qsTr("Encrypted by a verified device");
|
||||||
|
case Crypto.TOFU:
|
||||||
|
return qsTr("Encrypted by an unverified device, but you have trusted that user so far.");
|
||||||
|
default:
|
||||||
|
return qsTr("Encrypted by an unverified device or the key is from an untrusted source like the key backup.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ToolTip.visible: ma.hovered
|
||||||
height: 16
|
height: 16
|
||||||
sourceSize.height: height * Screen.devicePixelRatio
|
|
||||||
sourceSize.width: width * Screen.devicePixelRatio
|
|
||||||
source: {
|
source: {
|
||||||
if (encrypted) {
|
if (encrypted) {
|
||||||
switch (trust) {
|
switch (trust) {
|
||||||
|
@ -48,23 +54,11 @@ Image {
|
||||||
return sourceUrl + Nheko.theme.error;
|
return sourceUrl + Nheko.theme.error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ToolTip.visible: ma.hovered
|
sourceSize.height: height * Screen.devicePixelRatio
|
||||||
ToolTip.text: {
|
sourceSize.width: width * Screen.devicePixelRatio
|
||||||
if (!encrypted)
|
width: 16
|
||||||
return qsTr("This message is not encrypted!");
|
|
||||||
|
|
||||||
switch (trust) {
|
|
||||||
case Crypto.Verified:
|
|
||||||
return qsTr("Encrypted by a verified device");
|
|
||||||
case Crypto.TOFU:
|
|
||||||
return qsTr("Encrypted by an unverified device, but you have trusted that user so far.");
|
|
||||||
default:
|
|
||||||
return qsTr("Encrypted by an unverified device or the key is from an untrusted source like the key backup.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: ma
|
id: ma
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
import "delegates"
|
||||||
import "./delegates/"
|
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import im.nheko
|
import im.nheko
|
||||||
|
@ -17,66 +15,65 @@ Popup {
|
||||||
mid = mid_in;
|
mid = mid_in;
|
||||||
}
|
}
|
||||||
|
|
||||||
x: Math.round(parent.width / 2 - width / 2)
|
leftPadding: 10
|
||||||
y: Math.round(parent.height / 4)
|
|
||||||
modal: true
|
modal: true
|
||||||
palette: timelineRoot.palette
|
palette: timelineRoot.palette
|
||||||
parent: Overlay.overlay
|
parent: Overlay.overlay
|
||||||
width: timelineRoot.width * 0.8
|
|
||||||
leftPadding: 10
|
|
||||||
rightPadding: 10
|
rightPadding: 10
|
||||||
|
width: timelineRoot.width * 0.8
|
||||||
|
x: Math.round(parent.width / 2 - width / 2)
|
||||||
|
y: Math.round(parent.height / 4)
|
||||||
|
|
||||||
|
Overlay.modal: Rectangle {
|
||||||
|
color: Qt.rgba(timelineRoot.palette.window.r, timelineRoot.palette.window.g, timelineRoot.palette.window.b, 0.7)
|
||||||
|
}
|
||||||
|
background: Rectangle {
|
||||||
|
color: timelineRoot.palette.window
|
||||||
|
}
|
||||||
|
|
||||||
onOpened: {
|
onOpened: {
|
||||||
roomTextInput.forceActiveFocus();
|
roomTextInput.forceActiveFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: forwardColumn
|
id: forwardColumn
|
||||||
|
|
||||||
spacing: 5
|
spacing: 5
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: titleLabel
|
id: titleLabel
|
||||||
|
|
||||||
text: qsTr("Forward Message")
|
|
||||||
font.bold: true
|
|
||||||
bottomPadding: 10
|
bottomPadding: 10
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
|
font.bold: true
|
||||||
|
text: qsTr("Forward Message")
|
||||||
}
|
}
|
||||||
|
|
||||||
Reply {
|
Reply {
|
||||||
id: replyPreview
|
id: replyPreview
|
||||||
|
|
||||||
property var modelData: room ? room.getDump(mid, "") : {
|
property var modelData: room ? room.getDump(mid, "") : {}
|
||||||
}
|
|
||||||
|
|
||||||
width: parent.width
|
|
||||||
|
|
||||||
userColor: TimelineManager.userColor(modelData.userId, timelineRoot.palette.window)
|
|
||||||
blurhash: modelData.blurhash ?? ""
|
blurhash: modelData.blurhash ?? ""
|
||||||
body: modelData.body ?? ""
|
body: modelData.body ?? ""
|
||||||
formattedBody: modelData.formattedBody ?? ""
|
encryptionError: modelData.encryptionError ?? ""
|
||||||
eventId: modelData.eventId ?? ""
|
eventId: modelData.eventId ?? ""
|
||||||
filename: modelData.filename ?? ""
|
filename: modelData.filename ?? ""
|
||||||
filesize: modelData.filesize ?? ""
|
filesize: modelData.filesize ?? ""
|
||||||
|
formattedBody: modelData.formattedBody ?? ""
|
||||||
|
isOnlyEmoji: modelData.isOnlyEmoji ?? false
|
||||||
|
originalWidth: modelData.originalWidth ?? 0
|
||||||
proportionalHeight: modelData.proportionalHeight ?? 1
|
proportionalHeight: modelData.proportionalHeight ?? 1
|
||||||
type: modelData.type ?? MtxEvent.UnknownMessage
|
type: modelData.type ?? MtxEvent.UnknownMessage
|
||||||
typeString: modelData.typeString ?? ""
|
typeString: modelData.typeString ?? ""
|
||||||
url: modelData.url ?? ""
|
url: modelData.url ?? ""
|
||||||
originalWidth: modelData.originalWidth ?? 0
|
userColor: TimelineManager.userColor(modelData.userId, timelineRoot.palette.window)
|
||||||
isOnlyEmoji: modelData.isOnlyEmoji ?? false
|
|
||||||
userId: modelData.userId ?? ""
|
userId: modelData.userId ?? ""
|
||||||
userName: modelData.userName ?? ""
|
userName: modelData.userName ?? ""
|
||||||
encryptionError: modelData.encryptionError ?? ""
|
width: parent.width
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
id: roomTextInput
|
id: roomTextInput
|
||||||
|
|
||||||
width: forwardMessagePopup.width - forwardMessagePopup.leftPadding * 2
|
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
onTextEdited: {
|
width: forwardMessagePopup.width - forwardMessagePopup.leftPadding * 2
|
||||||
completerPopup.completer.searchString = text;
|
|
||||||
}
|
|
||||||
Keys.onPressed: {
|
Keys.onPressed: {
|
||||||
if (event.key == Qt.Key_Up || event.key == Qt.Key_Backtab) {
|
if (event.key == Qt.Key_Up || event.key == Qt.Key_Backtab) {
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
|
@ -92,43 +89,31 @@ Popup {
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
onTextEdited: {
|
||||||
|
completerPopup.completer.searchString = text;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Completer {
|
Completer {
|
||||||
id: completerPopup
|
id: completerPopup
|
||||||
|
|
||||||
width: forwardMessagePopup.width - forwardMessagePopup.leftPadding * 2
|
|
||||||
completerName: "room"
|
|
||||||
fullWidth: true
|
|
||||||
centerRowContent: false
|
|
||||||
avatarHeight: 24
|
avatarHeight: 24
|
||||||
avatarWidth: 24
|
avatarWidth: 24
|
||||||
bottomToTop: false
|
bottomToTop: false
|
||||||
|
centerRowContent: false
|
||||||
|
completerName: "room"
|
||||||
|
fullWidth: true
|
||||||
|
width: forwardMessagePopup.width - forwardMessagePopup.leftPadding * 2
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onCompletionSelected(id) {
|
function onCompletionSelected(id) {
|
||||||
room.forwardMessage(messageContextMenu.eventId, id);
|
room.forwardMessage(messageContextMenu.eventId, id);
|
||||||
forwardMessagePopup.close();
|
forwardMessagePopup.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onCountChanged() {
|
function onCountChanged() {
|
||||||
if (completerPopup.count > 0 && (completerPopup.currentIndex < 0 || completerPopup.currentIndex >= completerPopup.count))
|
if (completerPopup.count > 0 && (completerPopup.currentIndex < 0 || completerPopup.currentIndex >= completerPopup.count))
|
||||||
completerPopup.currentIndex = 0;
|
completerPopup.currentIndex = 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
target: completerPopup
|
target: completerPopup
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: timelineRoot.palette.window
|
|
||||||
}
|
|
||||||
|
|
||||||
Overlay.modal: Rectangle {
|
|
||||||
color: Qt.rgba(timelineRoot.palette.window.r, timelineRoot.palette.window.g, timelineRoot.palette.window.b, 0.7)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
import "ui"
|
||||||
import "./ui"
|
|
||||||
import QtQuick 2.3
|
import QtQuick 2.3
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import im.nheko
|
import im.nheko
|
||||||
|
@ -11,36 +9,32 @@ import im.nheko
|
||||||
AbstractButton {
|
AbstractButton {
|
||||||
id: button
|
id: button
|
||||||
|
|
||||||
property alias cursor: mouseArea.cursorShape
|
|
||||||
property string image: undefined
|
|
||||||
property color highlightColor: timelineRoot.palette.highlight
|
|
||||||
property color buttonTextColor: timelineRoot.palette.placeholderText
|
property color buttonTextColor: timelineRoot.palette.placeholderText
|
||||||
property bool changeColorOnHover: true
|
property bool changeColorOnHover: true
|
||||||
|
property alias cursor: mouseArea.cursorShape
|
||||||
|
property color highlightColor: timelineRoot.palette.highlight
|
||||||
|
property string image: undefined
|
||||||
property bool ripple: true
|
property bool ripple: true
|
||||||
|
|
||||||
focusPolicy: Qt.NoFocus
|
focusPolicy: Qt.NoFocus
|
||||||
width: 16
|
|
||||||
height: 16
|
height: 16
|
||||||
|
width: 16
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: buttonImg
|
id: buttonImg
|
||||||
|
|
||||||
// Workaround, can't get icon.source working for now...
|
// Workaround, can't get icon.source working for now...
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
source: image != "" ? ("image://colorimage/" + image + "?" + ((button.hovered && changeColorOnHover) ? highlightColor : buttonTextColor)) : ""
|
|
||||||
fillMode: Image.PreserveAspectFit
|
fillMode: Image.PreserveAspectFit
|
||||||
|
source: image != "" ? ("image://colorimage/" + image + "?" + ((button.hovered && changeColorOnHover) ? highlightColor : buttonTextColor)) : ""
|
||||||
}
|
}
|
||||||
|
|
||||||
NhekoCursorShape {
|
NhekoCursorShape {
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
Ripple {
|
Ripple {
|
||||||
enabled: button.ripple
|
|
||||||
color: Qt.rgba(buttonTextColor.r, buttonTextColor.g, buttonTextColor.b, 0.5)
|
color: Qt.rgba(buttonTextColor.r, buttonTextColor.g, buttonTextColor.b, 0.5)
|
||||||
|
enabled: button.ripple
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.5
|
import QtQuick 2.5
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import im.nheko
|
import im.nheko
|
||||||
|
@ -12,27 +10,27 @@ TextEdit {
|
||||||
|
|
||||||
property alias cursorShape: cs.cursorShape
|
property alias cursorShape: cs.cursorShape
|
||||||
|
|
||||||
textFormat: TextEdit.RichText
|
ToolTip.text: hoveredLink
|
||||||
readOnly: true
|
ToolTip.visible: hoveredLink || false
|
||||||
focus: false
|
|
||||||
wrapMode: Text.Wrap
|
|
||||||
selectByMouse: !Settings.mobileMode
|
|
||||||
// this always has to be enabled, otherwise you can't click links anymore!
|
// this always has to be enabled, otherwise you can't click links anymore!
|
||||||
//enabled: selectByMouse
|
//enabled: selectByMouse
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
onLinkActivated: Nheko.openLink(link)
|
focus: false
|
||||||
ToolTip.visible: hoveredLink || false
|
readOnly: true
|
||||||
ToolTip.text: hoveredLink
|
selectByMouse: !Settings.mobileMode
|
||||||
|
textFormat: TextEdit.RichText
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
|
||||||
// Setting a tooltip delay makes the hover text empty .-.
|
// Setting a tooltip delay makes the hover text empty .-.
|
||||||
//ToolTip.delay: Nheko.tooltipDelay
|
//ToolTip.delay: Nheko.tooltipDelay
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
TimelineManager.fixImageRendering(r.textDocument, r);
|
TimelineManager.fixImageRendering(r.textDocument, r);
|
||||||
}
|
}
|
||||||
|
onLinkActivated: Nheko.openLink(link)
|
||||||
|
|
||||||
NhekoCursorShape {
|
NhekoCursorShape {
|
||||||
id: cs
|
id: cs
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
cursorShape: hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,74 +1,66 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.12
|
import QtQuick.Controls 2.12
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
import im.nheko
|
import im.nheko
|
||||||
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: c
|
id: c
|
||||||
|
|
||||||
property color backgroundColor: timelineRoot.palette.base
|
property color backgroundColor: timelineRoot.palette.base
|
||||||
property alias color: labelC.color
|
property alias color: labelC.color
|
||||||
property alias textPadding: input.padding
|
property alias echoMode: input.echoMode
|
||||||
property alias text: input.text
|
property alias font: input.font
|
||||||
property alias label: labelC.text
|
property alias label: labelC.text
|
||||||
property alias placeholderText: input.placeholderText
|
property alias placeholderText: input.placeholderText
|
||||||
property alias font: input.font
|
|
||||||
property alias echoMode: input.echoMode
|
|
||||||
property alias selectByMouse: input.selectByMouse
|
property alias selectByMouse: input.selectByMouse
|
||||||
|
property alias text: input.text
|
||||||
|
property alias textPadding: input.padding
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: timer
|
|
||||||
interval: 350
|
|
||||||
onTriggered: editingFinished()
|
|
||||||
}
|
|
||||||
|
|
||||||
onTextChanged: timer.restart()
|
|
||||||
|
|
||||||
signal textEdited
|
|
||||||
signal accepted
|
signal accepted
|
||||||
signal editingFinished
|
signal editingFinished
|
||||||
|
signal textEdited
|
||||||
function forceActiveFocus() {
|
|
||||||
input.forceActiveFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
function clear() {
|
function clear() {
|
||||||
input.clear();
|
input.clear();
|
||||||
}
|
}
|
||||||
|
function forceActiveFocus() {
|
||||||
|
input.forceActiveFocus();
|
||||||
|
}
|
||||||
|
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
ToolTip.visible: hover.hovered
|
ToolTip.visible: hover.hovered
|
||||||
|
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
Item {
|
onTextChanged: timer.restart()
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredHeight: labelC.contentHeight
|
|
||||||
Layout.margins: input.padding
|
|
||||||
Layout.bottomMargin: Nheko.paddingSmall
|
|
||||||
visible: labelC.text
|
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: timer
|
||||||
|
interval: 350
|
||||||
|
|
||||||
|
onTriggered: editingFinished()
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.bottomMargin: Nheko.paddingSmall
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.margins: input.padding
|
||||||
|
Layout.preferredHeight: labelC.contentHeight
|
||||||
|
visible: labelC.text
|
||||||
z: 1
|
z: 1
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: labelC
|
id: labelC
|
||||||
|
|
||||||
y: contentHeight + input.padding + Nheko.paddingSmall
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
palette: timelineRoot.palette
|
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
|
enabled: false
|
||||||
|
font.letterSpacing: input.font.pixelSize * 0.02
|
||||||
font.pixelSize: input.font.pixelSize
|
font.pixelSize: input.font.pixelSize
|
||||||
font.weight: Font.DemiBold
|
font.weight: Font.DemiBold
|
||||||
font.letterSpacing: input.font.pixelSize * 0.02
|
palette: timelineRoot.palette
|
||||||
width: parent.width
|
|
||||||
|
|
||||||
state: labelC.text && (input.activeFocus == true || input.text) ? "focused" : ""
|
state: labelC.text && (input.activeFocus == true || input.text) ? "focused" : ""
|
||||||
|
width: parent.width
|
||||||
|
y: contentHeight + input.padding + Nheko.paddingSmall
|
||||||
|
|
||||||
states: State {
|
states: State {
|
||||||
name: "focused"
|
name: "focused"
|
||||||
|
@ -77,76 +69,63 @@ ColumnLayout {
|
||||||
target: labelC
|
target: labelC
|
||||||
y: 0
|
y: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: input
|
|
||||||
opacity: 1
|
opacity: 1
|
||||||
|
target: input
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
transitions: Transition {
|
transitions: Transition {
|
||||||
from: ""
|
from: ""
|
||||||
to: "focused"
|
|
||||||
reversible: true
|
reversible: true
|
||||||
|
to: "focused"
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
target: labelC
|
alwaysRunToEnd: true
|
||||||
|
duration: 210
|
||||||
|
easing.type: Easing.InCubic
|
||||||
properties: "y"
|
properties: "y"
|
||||||
duration: 210
|
target: labelC
|
||||||
easing.type: Easing.InCubic
|
|
||||||
alwaysRunToEnd: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
target: input
|
alwaysRunToEnd: true
|
||||||
properties: "opacity"
|
|
||||||
duration: 210
|
duration: 210
|
||||||
easing.type: Easing.InCubic
|
easing.type: Easing.InCubic
|
||||||
alwaysRunToEnd: true
|
properties: "opacity"
|
||||||
|
target: input
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextField {
|
TextField {
|
||||||
id: input
|
id: input
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
palette: timelineRoot.palette
|
|
||||||
color: labelC.color
|
color: labelC.color
|
||||||
opacity: labelC.text ? 0 : 1
|
|
||||||
focus: true
|
focus: true
|
||||||
|
opacity: labelC.text ? 0 : 1
|
||||||
onTextEdited: c.textEdited()
|
palette: timelineRoot.palette
|
||||||
onAccepted: c.accepted()
|
|
||||||
onEditingFinished: c.editingFinished()
|
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
id: backgroundRect
|
id: backgroundRect
|
||||||
|
|
||||||
color: labelC.text ? "transparent" : backgroundColor
|
color: labelC.text ? "transparent" : backgroundColor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onAccepted: c.accepted()
|
||||||
|
onEditingFinished: c.editingFinished()
|
||||||
|
onTextEdited: c.textEdited()
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: blueBar
|
id: blueBar
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
color: timelineRoot.palette.highlight
|
color: timelineRoot.palette.highlight
|
||||||
height: 1
|
height: 1
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: blackBar
|
id: blackBar
|
||||||
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
height: parent.height*2
|
anchors.top: parent.top
|
||||||
width: 0
|
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
|
height: parent.height * 2
|
||||||
|
width: 0
|
||||||
|
|
||||||
states: State {
|
states: State {
|
||||||
name: "focused"
|
name: "focused"
|
||||||
|
@ -156,29 +135,22 @@ ColumnLayout {
|
||||||
target: blackBar
|
target: blackBar
|
||||||
width: blueBar.width
|
width: blueBar.width
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
transitions: Transition {
|
transitions: Transition {
|
||||||
from: ""
|
from: ""
|
||||||
to: "focused"
|
|
||||||
reversible: true
|
reversible: true
|
||||||
|
to: "focused"
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
target: blackBar
|
alwaysRunToEnd: true
|
||||||
properties: "width"
|
|
||||||
duration: 310
|
duration: 310
|
||||||
easing.type: Easing.InCubic
|
easing.type: Easing.InCubic
|
||||||
alwaysRunToEnd: true
|
properties: "width"
|
||||||
|
target: blackBar
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: hover
|
id: hover
|
||||||
enabled: c.ToolTip.text
|
enabled: c.ToolTip.text
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
import "emoji"
|
||||||
import "./emoji"
|
import "voip"
|
||||||
import "./voip"
|
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
|
@ -14,51 +12,45 @@ import im.nheko
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: inputBar
|
id: inputBar
|
||||||
|
|
||||||
color: timelineRoot.palette.window
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredHeight: row.implicitHeight
|
|
||||||
Layout.minimumHeight: 40
|
|
||||||
property bool showAllButtons: width > 450 || (messageInput.length == 0 && !messageInput.inputMethodComposing)
|
property bool showAllButtons: width > 450 || (messageInput.length == 0 && !messageInput.inputMethodComposing)
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.minimumHeight: 40
|
||||||
|
Layout.preferredHeight: row.implicitHeight
|
||||||
|
color: timelineRoot.palette.window
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: placeCallDialog
|
id: placeCallDialog
|
||||||
|
|
||||||
PlaceCall {
|
PlaceCall {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: screenShareDialog
|
id: screenShareDialog
|
||||||
|
|
||||||
ScreenShare {
|
ScreenShare {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: row
|
id: row
|
||||||
|
|
||||||
visible: room ? room.permissions.canSend(MtxEvent.TextMessage) : false
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
visible: room ? room.permissions.canSend(MtxEvent.TextMessage) : false
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
visible: CallManager.callsSupported && showAllButtons
|
|
||||||
opacity: CallManager.haveCallInvite ? 0.3 : 1
|
|
||||||
Layout.alignment: Qt.AlignBottom
|
Layout.alignment: Qt.AlignBottom
|
||||||
hoverEnabled: true
|
|
||||||
width: 22
|
|
||||||
height: 22
|
|
||||||
image: CallManager.isOnCall ? ":/icons/icons/ui/end-call.svg" : ":/icons/icons/ui/place-call.svg"
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: CallManager.isOnCall ? qsTr("Hang up") : qsTr("Place a call")
|
|
||||||
Layout.margins: 8
|
Layout.margins: 8
|
||||||
|
ToolTip.text: CallManager.isOnCall ? qsTr("Hang up") : qsTr("Place a call")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
height: 22
|
||||||
|
hoverEnabled: true
|
||||||
|
image: CallManager.isOnCall ? ":/icons/icons/ui/end-call.svg" : ":/icons/icons/ui/place-call.svg"
|
||||||
|
opacity: CallManager.haveCallInvite ? 0.3 : 1
|
||||||
|
visible: CallManager.callsSupported && showAllButtons
|
||||||
|
width: 22
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (room) {
|
if (room) {
|
||||||
if (CallManager.haveCallInvite) {
|
if (CallManager.haveCallInvite) {
|
||||||
return ;
|
return;
|
||||||
} else if (CallManager.isOnCall) {
|
} else if (CallManager.isOnCall) {
|
||||||
CallManager.hangUp();
|
CallManager.hangUp();
|
||||||
} else {
|
} else {
|
||||||
|
@ -69,18 +61,18 @@ Rectangle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
visible: showAllButtons
|
|
||||||
Layout.alignment: Qt.AlignBottom
|
Layout.alignment: Qt.AlignBottom
|
||||||
hoverEnabled: true
|
|
||||||
width: 22
|
|
||||||
height: 22
|
|
||||||
image: ":/icons/icons/ui/attach.svg"
|
|
||||||
Layout.margins: 8
|
Layout.margins: 8
|
||||||
onClicked: room.input.openFileSelection()
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Send a file")
|
ToolTip.text: qsTr("Send a file")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
height: 22
|
||||||
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/attach.svg"
|
||||||
|
visible: showAllButtons
|
||||||
|
width: 22
|
||||||
|
|
||||||
|
onClicked: room.input.openFileSelection()
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
@ -91,101 +83,57 @@ Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
running: parent.visible
|
running: parent.visible
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollView {
|
ScrollView {
|
||||||
id: textInput
|
id: textInput
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
Layout.maximumHeight: Window.height / 4
|
Layout.maximumHeight: Window.height / 4
|
||||||
Layout.minimumHeight: fontMetrics.lineSpacing
|
Layout.minimumHeight: fontMetrics.lineSpacing
|
||||||
Layout.preferredHeight: contentHeight
|
Layout.preferredHeight: contentHeight
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||||
|
|
||||||
contentWidth: availableWidth
|
contentWidth: availableWidth
|
||||||
|
|
||||||
TextArea {
|
TextArea {
|
||||||
id: messageInput
|
id: messageInput
|
||||||
|
|
||||||
property int completerTriggeredAt: 0
|
property int completerTriggeredAt: 0
|
||||||
|
property string lastChar
|
||||||
|
|
||||||
function insertCompletion(completion) {
|
function insertCompletion(completion) {
|
||||||
messageInput.remove(completerTriggeredAt, cursorPosition);
|
messageInput.remove(completerTriggeredAt, cursorPosition);
|
||||||
messageInput.insert(cursorPosition, completion);
|
messageInput.insert(cursorPosition, completion);
|
||||||
}
|
}
|
||||||
|
|
||||||
function openCompleter(pos, type) {
|
function openCompleter(pos, type) {
|
||||||
if (popup.opened) return;
|
if (popup.opened)
|
||||||
|
return;
|
||||||
completerTriggeredAt = pos;
|
completerTriggeredAt = pos;
|
||||||
completer.completerName = type;
|
completer.completerName = type;
|
||||||
popup.open();
|
popup.open();
|
||||||
completer.completer.setSearchString(messageInput.getText(completerTriggeredAt, cursorPosition)+messageInput.preeditText);
|
completer.completer.setSearchString(messageInput.getText(completerTriggeredAt, cursorPosition) + messageInput.preeditText);
|
||||||
}
|
}
|
||||||
|
|
||||||
function positionCursorAtEnd() {
|
function positionCursorAtEnd() {
|
||||||
cursorPosition = messageInput.length;
|
cursorPosition = messageInput.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
function positionCursorAtStart() {
|
function positionCursorAtStart() {
|
||||||
cursorPosition = 0;
|
cursorPosition = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
selectByMouse: true
|
background: null
|
||||||
|
bottomPadding: 8
|
||||||
|
color: timelineRoot.palette.text
|
||||||
|
focus: true
|
||||||
|
leftPadding: inputBar.showAllButtons ? 0 : 8
|
||||||
|
padding: 0
|
||||||
placeholderText: qsTr("Write a message...")
|
placeholderText: qsTr("Write a message...")
|
||||||
placeholderTextColor: timelineRoot.palette.placeholderText
|
placeholderTextColor: timelineRoot.palette.placeholderText
|
||||||
color: timelineRoot.palette.text
|
selectByMouse: true
|
||||||
width: textInput.width
|
|
||||||
verticalAlignment: TextEdit.AlignVCenter
|
|
||||||
wrapMode: TextEdit.Wrap
|
|
||||||
padding: 0
|
|
||||||
topPadding: 8
|
topPadding: 8
|
||||||
bottomPadding: 8
|
verticalAlignment: TextEdit.AlignVCenter
|
||||||
leftPadding: inputBar.showAllButtons? 0 : 8
|
width: textInput.width
|
||||||
focus: true
|
wrapMode: TextEdit.Wrap
|
||||||
property string lastChar
|
|
||||||
onTextChanged: {
|
|
||||||
if (room)
|
|
||||||
room.input.updateState(selectionStart, selectionEnd, cursorPosition, text);
|
|
||||||
forceActiveFocus();
|
|
||||||
if (cursorPosition > 0)
|
|
||||||
lastChar = text.charAt(cursorPosition-1)
|
|
||||||
else
|
|
||||||
lastChar = ''
|
|
||||||
if (lastChar == '@') {
|
|
||||||
messageInput.openCompleter(selectionStart-1, "user");
|
|
||||||
} else if (lastChar == ':') {
|
|
||||||
messageInput.openCompleter(selectionStart-1, "emoji");
|
|
||||||
} else if (lastChar == '#') {
|
|
||||||
messageInput.openCompleter(selectionStart-1, "roomAliases");
|
|
||||||
} else if (lastChar == "~") {
|
|
||||||
messageInput.openCompleter(selectionStart-1, "customEmoji");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onCursorPositionChanged: {
|
|
||||||
if (!room)
|
|
||||||
return ;
|
|
||||||
|
|
||||||
room.input.updateState(selectionStart, selectionEnd, cursorPosition, text);
|
|
||||||
if (popup.opened && cursorPosition <= completerTriggeredAt)
|
|
||||||
popup.close();
|
|
||||||
|
|
||||||
if (popup.opened)
|
|
||||||
completer.completer.setSearchString(messageInput.getText(completerTriggeredAt, cursorPosition)+messageInput.preeditText);
|
|
||||||
|
|
||||||
}
|
|
||||||
onPreeditTextChanged: {
|
|
||||||
if (popup.opened)
|
|
||||||
completer.completer.setSearchString(messageInput.getText(completerTriggeredAt, cursorPosition)+messageInput.preeditText);
|
|
||||||
}
|
|
||||||
onSelectionStartChanged: room.input.updateState(selectionStart, selectionEnd, cursorPosition, text)
|
|
||||||
onSelectionEndChanged: room.input.updateState(selectionStart, selectionEnd, cursorPosition, text)
|
|
||||||
// Ensure that we get escape key press events first.
|
|
||||||
Keys.onShortcutOverride: event.accepted = (popup.opened && (event.key === Qt.Key_Escape || event.key === Qt.Key_Tab || event.key === Qt.Key_Enter || event.key === Qt.Key_Space))
|
|
||||||
Keys.onPressed: {
|
Keys.onPressed: {
|
||||||
if (event.matches(StandardKey.Paste)) {
|
if (event.matches(StandardKey.Paste)) {
|
||||||
room.input.paste(false);
|
room.input.paste(false);
|
||||||
|
@ -194,10 +142,8 @@ Rectangle {
|
||||||
// close popup if user enters space after colon
|
// close popup if user enters space after colon
|
||||||
if (cursorPosition == completerTriggeredAt + 1)
|
if (cursorPosition == completerTriggeredAt + 1)
|
||||||
popup.close();
|
popup.close();
|
||||||
|
|
||||||
if (popup.opened && completer.count <= 0)
|
if (popup.opened && completer.count <= 0)
|
||||||
popup.close();
|
popup.close();
|
||||||
|
|
||||||
} else if (event.modifiers == Qt.ControlModifier && event.key == Qt.Key_U) {
|
} else if (event.modifiers == Qt.ControlModifier && event.key == Qt.Key_U) {
|
||||||
messageInput.clear();
|
messageInput.clear();
|
||||||
} else if (event.modifiers == Qt.ControlModifier && event.key == Qt.Key_P) {
|
} else if (event.modifiers == Qt.ControlModifier && event.key == Qt.Key_P) {
|
||||||
|
@ -212,7 +158,8 @@ Rectangle {
|
||||||
completer.completerName = "";
|
completer.completerName = "";
|
||||||
popup.close();
|
popup.close();
|
||||||
} else if (event.matches(StandardKey.InsertLineSeparator)) {
|
} else if (event.matches(StandardKey.InsertLineSeparator)) {
|
||||||
if (popup.opened) popup.close();
|
if (popup.opened)
|
||||||
|
popup.close();
|
||||||
} else if (event.matches(StandardKey.InsertParagraphSeparator)) {
|
} else if (event.matches(StandardKey.InsertParagraphSeparator)) {
|
||||||
if (popup.opened) {
|
if (popup.opened) {
|
||||||
var currentCompletion = completer.currentCompletion();
|
var currentCompletion = completer.currentCompletion();
|
||||||
|
@ -242,16 +189,16 @@ Rectangle {
|
||||||
console.log('"' + t + '"');
|
console.log('"' + t + '"');
|
||||||
if (t == '@') {
|
if (t == '@') {
|
||||||
messageInput.openCompleter(pos, "user");
|
messageInput.openCompleter(pos, "user");
|
||||||
return ;
|
return;
|
||||||
} else if (t == ' ' || t == '\t') {
|
} else if (t == ' ' || t == '\t') {
|
||||||
messageInput.openCompleter(pos + 1, "user");
|
messageInput.openCompleter(pos + 1, "user");
|
||||||
return ;
|
return;
|
||||||
} else if (t == ':') {
|
} else if (t == ':') {
|
||||||
messageInput.openCompleter(pos, "emoji");
|
messageInput.openCompleter(pos, "emoji");
|
||||||
return ;
|
return;
|
||||||
} else if (t == '~') {
|
} else if (t == '~') {
|
||||||
messageInput.openCompleter(pos, "customEmoji");
|
messageInput.openCompleter(pos, "customEmoji");
|
||||||
return ;
|
return;
|
||||||
}
|
}
|
||||||
pos = pos - 1;
|
pos = pos - 1;
|
||||||
}
|
}
|
||||||
|
@ -301,21 +248,53 @@ Rectangle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
background: null
|
// Ensure that we get escape key press events first.
|
||||||
|
Keys.onShortcutOverride: event.accepted = (popup.opened && (event.key === Qt.Key_Escape || event.key === Qt.Key_Tab || event.key === Qt.Key_Enter || event.key === Qt.Key_Space))
|
||||||
|
onCursorPositionChanged: {
|
||||||
|
if (!room)
|
||||||
|
return;
|
||||||
|
room.input.updateState(selectionStart, selectionEnd, cursorPosition, text);
|
||||||
|
if (popup.opened && cursorPosition <= completerTriggeredAt)
|
||||||
|
popup.close();
|
||||||
|
if (popup.opened)
|
||||||
|
completer.completer.setSearchString(messageInput.getText(completerTriggeredAt, cursorPosition) + messageInput.preeditText);
|
||||||
|
}
|
||||||
|
onPreeditTextChanged: {
|
||||||
|
if (popup.opened)
|
||||||
|
completer.completer.setSearchString(messageInput.getText(completerTriggeredAt, cursorPosition) + messageInput.preeditText);
|
||||||
|
}
|
||||||
|
onSelectionEndChanged: room.input.updateState(selectionStart, selectionEnd, cursorPosition, text)
|
||||||
|
onSelectionStartChanged: room.input.updateState(selectionStart, selectionEnd, cursorPosition, text)
|
||||||
|
onTextChanged: {
|
||||||
|
if (room)
|
||||||
|
room.input.updateState(selectionStart, selectionEnd, cursorPosition, text);
|
||||||
|
forceActiveFocus();
|
||||||
|
if (cursorPosition > 0)
|
||||||
|
lastChar = text.charAt(cursorPosition - 1);
|
||||||
|
else
|
||||||
|
lastChar = '';
|
||||||
|
if (lastChar == '@') {
|
||||||
|
messageInput.openCompleter(selectionStart - 1, "user");
|
||||||
|
} else if (lastChar == ':') {
|
||||||
|
messageInput.openCompleter(selectionStart - 1, "emoji");
|
||||||
|
} else if (lastChar == '#') {
|
||||||
|
messageInput.openCompleter(selectionStart - 1, "roomAliases");
|
||||||
|
} else if (lastChar == "~") {
|
||||||
|
messageInput.openCompleter(selectionStart - 1, "customEmoji");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onRoomChanged() {
|
function onRoomChanged() {
|
||||||
messageInput.clear();
|
messageInput.clear();
|
||||||
if (room)
|
if (room)
|
||||||
messageInput.append(room.input.text);
|
messageInput.append(room.input.text);
|
||||||
|
|
||||||
completer.completerName = "";
|
completer.completerName = "";
|
||||||
messageInput.forceActiveFocus();
|
messageInput.forceActiveFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
target: timelineView
|
target: timelineView
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onCompletionClicked(completion) {
|
function onCompletionClicked(completion) {
|
||||||
messageInput.insertCompletion(completion);
|
messageInput.insertCompletion(completion);
|
||||||
|
@ -323,49 +302,42 @@ Rectangle {
|
||||||
|
|
||||||
target: completer
|
target: completer
|
||||||
}
|
}
|
||||||
|
|
||||||
Popup {
|
Popup {
|
||||||
id: popup
|
id: popup
|
||||||
|
background: null
|
||||||
|
padding: 0
|
||||||
x: messageInput.positionToRectangle(messageInput.completerTriggeredAt).x
|
x: messageInput.positionToRectangle(messageInput.completerTriggeredAt).x
|
||||||
y: messageInput.positionToRectangle(messageInput.completerTriggeredAt).y - height
|
y: messageInput.positionToRectangle(messageInput.completerTriggeredAt).y - height
|
||||||
|
|
||||||
background: null
|
enter: Transition {
|
||||||
padding: 0
|
NumberAnimation {
|
||||||
|
duration: 100
|
||||||
|
from: 0
|
||||||
|
property: "opacity"
|
||||||
|
to: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exit: Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 100
|
||||||
|
from: 1
|
||||||
|
property: "opacity"
|
||||||
|
to: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Completer {
|
Completer {
|
||||||
anchors.fill: parent
|
|
||||||
id: completer
|
id: completer
|
||||||
|
anchors.fill: parent
|
||||||
rowMargin: 2
|
rowMargin: 2
|
||||||
rowSpacing: 0
|
rowSpacing: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
enter: Transition {
|
|
||||||
NumberAnimation {
|
|
||||||
property: "opacity"
|
|
||||||
from: 0
|
|
||||||
to: 1
|
|
||||||
duration: 100
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
exit: Transition {
|
|
||||||
NumberAnimation {
|
|
||||||
property: "opacity"
|
|
||||||
from: 1
|
|
||||||
to: 0
|
|
||||||
duration: 100
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onInsertText(text) {
|
function onInsertText(text) {
|
||||||
messageInput.remove(messageInput.selectionStart, messageInput.selectionEnd);
|
messageInput.remove(messageInput.selectionStart, messageInput.selectionEnd);
|
||||||
messageInput.insert(messageInput.cursorPosition, text);
|
messageInput.insert(messageInput.cursorPosition, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onTextChanged(newText) {
|
function onTextChanged(newText) {
|
||||||
messageInput.text = newText;
|
messageInput.text = newText;
|
||||||
messageInput.cursorPosition = newText.length;
|
messageInput.cursorPosition = newText.length;
|
||||||
|
@ -374,20 +346,17 @@ Rectangle {
|
||||||
ignoreUnknownSignals: true
|
ignoreUnknownSignals: true
|
||||||
target: room ? room.input : null
|
target: room ? room.input : null
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onReplyChanged() {
|
function onEditChanged() {
|
||||||
messageInput.forceActiveFocus();
|
messageInput.forceActiveFocus();
|
||||||
}
|
}
|
||||||
|
function onReplyChanged() {
|
||||||
function onEditChanged() {
|
|
||||||
messageInput.forceActiveFocus();
|
messageInput.forceActiveFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
ignoreUnknownSignals: true
|
ignoreUnknownSignals: true
|
||||||
target: room
|
target: room
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onFocusInput() {
|
function onFocusInput() {
|
||||||
messageInput.forceActiveFocus();
|
messageInput.forceActiveFocus();
|
||||||
|
@ -395,83 +364,74 @@ Rectangle {
|
||||||
|
|
||||||
target: TimelineManager
|
target: TimelineManager
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
|
acceptedButtons: Qt.MiddleButton
|
||||||
// workaround for wrong cursor shape on some platforms
|
// workaround for wrong cursor shape on some platforms
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
acceptedButtons: Qt.MiddleButton
|
|
||||||
cursorShape: Qt.IBeamCursor
|
cursorShape: Qt.IBeamCursor
|
||||||
|
|
||||||
onClicked: room.input.paste(true)
|
onClicked: room.input.paste(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: stickerButton
|
id: stickerButton
|
||||||
visible: showAllButtons
|
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
|
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
|
||||||
Layout.margins: 8
|
Layout.margins: 8
|
||||||
hoverEnabled: true
|
|
||||||
width: 22
|
|
||||||
height: 22
|
|
||||||
image: ":/icons/icons/ui/sticky-note-solid.svg"
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Stickers")
|
ToolTip.text: qsTr("Stickers")
|
||||||
onClicked: stickerPopup.visible ? stickerPopup.close() : stickerPopup.show(stickerButton, room.roomId, function(row) {
|
ToolTip.visible: hovered
|
||||||
room.input.sticker(stickerPopup.model.sourceModel, row);
|
height: 22
|
||||||
TimelineManager.focusMessageInput();
|
hoverEnabled: true
|
||||||
})
|
image: ":/icons/icons/ui/sticky-note-solid.svg"
|
||||||
|
visible: showAllButtons
|
||||||
|
width: 22
|
||||||
|
|
||||||
|
onClicked: stickerPopup.visible ? stickerPopup.close() : stickerPopup.show(stickerButton, room.roomId, function (row) {
|
||||||
|
room.input.sticker(stickerPopup.model.sourceModel, row);
|
||||||
|
TimelineManager.focusMessageInput();
|
||||||
|
})
|
||||||
|
|
||||||
StickerPicker {
|
StickerPicker {
|
||||||
id: stickerPopup
|
id: stickerPopup
|
||||||
|
|
||||||
colors: timelineRoot.palette
|
colors: timelineRoot.palette
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: emojiButton
|
id: emojiButton
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
|
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
|
||||||
Layout.margins: 8
|
Layout.margins: 8
|
||||||
hoverEnabled: true
|
|
||||||
width: 22
|
|
||||||
height: 22
|
|
||||||
image: ":/icons/icons/ui/smile.svg"
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Emoji")
|
ToolTip.text: qsTr("Emoji")
|
||||||
onClicked: emojiPopup.visible ? emojiPopup.close() : emojiPopup.show(emojiButton, function(emoji) {
|
ToolTip.visible: hovered
|
||||||
messageInput.insert(messageInput.cursorPosition, emoji);
|
height: 22
|
||||||
TimelineManager.focusMessageInput();
|
hoverEnabled: true
|
||||||
})
|
image: ":/icons/icons/ui/smile.svg"
|
||||||
}
|
width: 22
|
||||||
|
|
||||||
|
onClicked: emojiPopup.visible ? emojiPopup.close() : emojiPopup.show(emojiButton, function (emoji) {
|
||||||
|
messageInput.insert(messageInput.cursorPosition, emoji);
|
||||||
|
TimelineManager.focusMessageInput();
|
||||||
|
})
|
||||||
|
}
|
||||||
ImageButton {
|
ImageButton {
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
|
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
|
||||||
Layout.margins: 8
|
Layout.margins: 8
|
||||||
hoverEnabled: true
|
|
||||||
width: 22
|
|
||||||
height: 22
|
|
||||||
image: ":/icons/icons/ui/send.svg"
|
|
||||||
Layout.rightMargin: 8
|
Layout.rightMargin: 8
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Send")
|
ToolTip.text: qsTr("Send")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
height: 22
|
||||||
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/send.svg"
|
||||||
|
width: 22
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
room.input.send();
|
room.input.send();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
visible: room ? (!room.permissions.canSend(MtxEvent.TextMessage)) : false
|
|
||||||
text: qsTr("You don't have permission to send messages in this room")
|
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
|
text: qsTr("You don't have permission to send messages in this room")
|
||||||
|
visible: room ? (!room.permissions.canSend(MtxEvent.TextMessage)) : false
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
|
@ -10,38 +8,41 @@ import im.nheko
|
||||||
|
|
||||||
BusyIndicator {
|
BusyIndicator {
|
||||||
id: control
|
id: control
|
||||||
|
|
||||||
contentItem: Item {
|
contentItem: Item {
|
||||||
implicitWidth: 64
|
|
||||||
implicitHeight: 64
|
implicitHeight: 64
|
||||||
|
implicitWidth: 64
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: item
|
id: item
|
||||||
|
|
||||||
height: Math.min(parent.height, parent.width)
|
height: Math.min(parent.height, parent.width)
|
||||||
width: height
|
|
||||||
opacity: control.running ? 1 : 0
|
opacity: control.running ? 1 : 0
|
||||||
|
width: height
|
||||||
|
|
||||||
RotationAnimator {
|
Behavior on opacity {
|
||||||
target: item
|
OpacityAnimator {
|
||||||
running: control.visible && control.running
|
duration: 250
|
||||||
from: 0
|
}
|
||||||
to: 360
|
|
||||||
loops: Animation.Infinite
|
|
||||||
duration: 2000
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RotationAnimator {
|
||||||
|
duration: 2000
|
||||||
|
from: 0
|
||||||
|
loops: Animation.Infinite
|
||||||
|
running: control.visible && control.running
|
||||||
|
target: item
|
||||||
|
to: 360
|
||||||
|
}
|
||||||
Repeater {
|
Repeater {
|
||||||
id: repeater
|
id: repeater
|
||||||
|
|
||||||
model: 6
|
model: 6
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
implicitWidth: radius * 2
|
|
||||||
implicitHeight: radius * 2
|
|
||||||
radius: item.height / 6
|
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
|
implicitHeight: radius * 2
|
||||||
|
implicitWidth: radius * 2
|
||||||
opacity: (index + 2) / (repeater.count + 2)
|
opacity: (index + 2) / (repeater.count + 2)
|
||||||
|
radius: item.height / 6
|
||||||
|
|
||||||
transform: [
|
transform: [
|
||||||
Translate {
|
Translate {
|
||||||
y: -Math.min(item.width, item.height) * 0.5 + item.height / 6
|
y: -Math.min(item.width, item.height) * 0.5 + item.height / 6
|
||||||
|
@ -53,18 +54,7 @@ BusyIndicator {
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
OpacityAnimator {
|
|
||||||
duration: 250
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,39 +1,33 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
import im.nheko
|
import im.nheko
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
implicitHeight: warningRect.visible ? warningDisplay.implicitHeight : 0
|
|
||||||
height: implicitHeight
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
height: implicitHeight
|
||||||
|
implicitHeight: warningRect.visible ? warningDisplay.implicitHeight : 0
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: warningRect
|
id: warningRect
|
||||||
|
|
||||||
visible: (room && room.permissions.canPingRoom() && room.input.containsAtRoom)
|
|
||||||
color: timelineRoot.palette.base
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
color: timelineRoot.palette.base
|
||||||
|
visible: (room && room.permissions.canPingRoom() && room.input.containsAtRoom)
|
||||||
z: 3
|
z: 3
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: warningDisplay
|
id: warningDisplay
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: 10
|
anchors.leftMargin: 10
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.rightMargin: 10
|
anchors.rightMargin: 10
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
color: Nheko.theme.red
|
color: Nheko.theme.red
|
||||||
text: qsTr("You are about to notify the whole room")
|
text: qsTr("You are about to notify the whole room")
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
//import QtGraphicalEffects 1.0
|
//import QtGraphicalEffects 1.0
|
||||||
|
@ -11,8 +10,8 @@ import im.nheko
|
||||||
Item {
|
Item {
|
||||||
id: privacyScreen
|
id: privacyScreen
|
||||||
|
|
||||||
property var timelineRoot
|
|
||||||
property int screenTimeout
|
property int screenTimeout
|
||||||
|
property var timelineRoot
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onFocusChanged() {
|
function onFocusChanged() {
|
||||||
|
@ -22,29 +21,26 @@ Item {
|
||||||
} else {
|
} else {
|
||||||
if (timelineRoot.visible)
|
if (timelineRoot.visible)
|
||||||
screenSaverTimer.start();
|
screenSaverTimer.start();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
target: TimelineManager
|
target: TimelineManager
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: screenSaverTimer
|
id: screenSaverTimer
|
||||||
|
|
||||||
interval: screenTimeout * 1000
|
interval: screenTimeout * 1000
|
||||||
running: !MainWindow.active
|
running: !MainWindow.active
|
||||||
|
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
screenSaver.state = "Visible";
|
screenSaver.state = "Visible";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: screenSaver
|
id: screenSaver
|
||||||
|
|
||||||
state: "Invisible"
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
state: "Invisible"
|
||||||
visible: false
|
visible: false
|
||||||
|
|
||||||
states: [
|
states: [
|
||||||
State {
|
State {
|
||||||
name: "Visible"
|
name: "Visible"
|
||||||
|
@ -53,26 +49,22 @@ Item {
|
||||||
target: screenSaver
|
target: screenSaver
|
||||||
visible: true
|
visible: true
|
||||||
}
|
}
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: screenSaver
|
|
||||||
opacity: 1
|
opacity: 1
|
||||||
|
target: screenSaver
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "Invisible"
|
name: "Invisible"
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: screenSaver
|
|
||||||
opacity: 0
|
opacity: 0
|
||||||
|
target: screenSaver
|
||||||
}
|
}
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: screenSaver
|
target: screenSaver
|
||||||
visible: false
|
visible: false
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
transitions: [
|
transitions: [
|
||||||
|
@ -82,20 +74,17 @@ Item {
|
||||||
|
|
||||||
SequentialAnimation {
|
SequentialAnimation {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
target: screenSaver
|
|
||||||
property: "opacity"
|
|
||||||
duration: 250
|
duration: 250
|
||||||
easing.type: Easing.InQuad
|
easing.type: Easing.InQuad
|
||||||
}
|
property: "opacity"
|
||||||
|
target: screenSaver
|
||||||
NumberAnimation {
|
}
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 0
|
||||||
|
property: "visible"
|
||||||
target: screenSaver
|
target: screenSaver
|
||||||
property: "visible"
|
|
||||||
duration: 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
Transition {
|
Transition {
|
||||||
from: "Invisible"
|
from: "Invisible"
|
||||||
|
@ -103,20 +92,17 @@ Item {
|
||||||
|
|
||||||
SequentialAnimation {
|
SequentialAnimation {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
target: screenSaver
|
|
||||||
property: "visible"
|
|
||||||
duration: 0
|
duration: 0
|
||||||
}
|
property: "visible"
|
||||||
|
|
||||||
NumberAnimation {
|
|
||||||
target: screenSaver
|
target: screenSaver
|
||||||
property: "opacity"
|
}
|
||||||
|
NumberAnimation {
|
||||||
duration: 500
|
duration: 500
|
||||||
easing.type: Easing.InQuad
|
easing.type: Easing.InQuad
|
||||||
|
property: "opacity"
|
||||||
|
target: screenSaver
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -127,7 +113,5 @@ Item {
|
||||||
// source: timelineRoot
|
// source: timelineRoot
|
||||||
// radius: 50
|
// radius: 50
|
||||||
//}
|
//}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
import QtQuick.Layouts 1.15
|
import QtQuick.Layouts 1.15
|
||||||
|
@ -12,33 +10,35 @@ Popup {
|
||||||
id: quickSwitcher
|
id: quickSwitcher
|
||||||
|
|
||||||
property int textHeight: Math.round(Qt.application.font.pixelSize * 2.4)
|
property int textHeight: Math.round(Qt.application.font.pixelSize * 2.4)
|
||||||
|
property int textMargin: Nheko.paddingSmall
|
||||||
|
|
||||||
background: null
|
background: null
|
||||||
width: Math.min(Math.max(Math.round(parent.width / 2),450),parent.width) // limiting width to parent.width/2 can be a bit narrow
|
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
||||||
|
modal: true
|
||||||
|
palette: timelineRoot.palette
|
||||||
|
parent: Overlay.overlay
|
||||||
|
width: Math.min(Math.max(Math.round(parent.width / 2), 450), parent.width) // limiting width to parent.width/2 can be a bit narrow
|
||||||
x: Math.round(parent.width / 2 - contentWidth / 2)
|
x: Math.round(parent.width / 2 - contentWidth / 2)
|
||||||
y: Math.round(parent.height / 4)
|
y: Math.round(parent.height / 4)
|
||||||
modal: true
|
|
||||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
Overlay.modal: Rectangle {
|
||||||
parent: Overlay.overlay
|
color: "#aa1E1E1E"
|
||||||
palette: timelineRoot.palette
|
}
|
||||||
|
|
||||||
onOpened: {
|
onOpened: {
|
||||||
roomTextInput.forceActiveFocus();
|
roomTextInput.forceActiveFocus();
|
||||||
}
|
}
|
||||||
property int textMargin: Nheko.paddingSmall
|
|
||||||
|
|
||||||
Column{
|
Column {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
spacing: 1
|
spacing: 1
|
||||||
|
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
id: roomTextInput
|
id: roomTextInput
|
||||||
|
|
||||||
width: parent.width
|
|
||||||
font.pixelSize: Math.ceil(quickSwitcher.textHeight * 0.6)
|
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
onTextEdited: {
|
font.pixelSize: Math.ceil(quickSwitcher.textHeight * 0.6)
|
||||||
completerPopup.completer.searchString = text;
|
width: parent.width
|
||||||
}
|
|
||||||
Keys.onPressed: {
|
Keys.onPressed: {
|
||||||
if (event.key == Qt.Key_Up || event.key == Qt.Key_Backtab) {
|
if (event.key == Qt.Key_Up || event.key == Qt.Key_Backtab) {
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
|
@ -46,49 +46,42 @@ Popup {
|
||||||
} else if (event.key == Qt.Key_Down || event.key == Qt.Key_Tab) {
|
} else if (event.key == Qt.Key_Down || event.key == Qt.Key_Tab) {
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
if (event.key == Qt.Key_Tab && (event.modifiers & Qt.ShiftModifier))
|
if (event.key == Qt.Key_Tab && (event.modifiers & Qt.ShiftModifier))
|
||||||
completerPopup.up();
|
completerPopup.up();
|
||||||
else
|
else
|
||||||
completerPopup.down();
|
completerPopup.down();
|
||||||
} else if (event.matches(StandardKey.InsertParagraphSeparator)) {
|
} else if (event.matches(StandardKey.InsertParagraphSeparator)) {
|
||||||
completerPopup.finishCompletion();
|
completerPopup.finishCompletion();
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
onTextEdited: {
|
||||||
|
completerPopup.completer.searchString = text;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Completer {
|
Completer {
|
||||||
id: completerPopup
|
id: completerPopup
|
||||||
|
|
||||||
visible: roomTextInput.text.length > 0
|
|
||||||
width: parent.width
|
|
||||||
completerName: "room"
|
|
||||||
bottomToTop: false
|
|
||||||
fullWidth: true
|
|
||||||
avatarHeight: quickSwitcher.textHeight
|
avatarHeight: quickSwitcher.textHeight
|
||||||
avatarWidth: quickSwitcher.textHeight
|
avatarWidth: quickSwitcher.textHeight
|
||||||
|
bottomToTop: false
|
||||||
centerRowContent: false
|
centerRowContent: false
|
||||||
|
completerName: "room"
|
||||||
|
fullWidth: true
|
||||||
rowMargin: Math.round(quickSwitcher.textMargin / 2)
|
rowMargin: Math.round(quickSwitcher.textMargin / 2)
|
||||||
rowSpacing: quickSwitcher.textMargin
|
rowSpacing: quickSwitcher.textMargin
|
||||||
|
visible: roomTextInput.text.length > 0
|
||||||
|
width: parent.width
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onCompletionSelected(id) {
|
function onCompletionSelected(id) {
|
||||||
Rooms.setCurrentRoom(id);
|
Rooms.setCurrentRoom(id);
|
||||||
quickSwitcher.close();
|
quickSwitcher.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onCountChanged() {
|
function onCountChanged() {
|
||||||
if (completerPopup.count > 0 && (completerPopup.currentIndex < 0 || completerPopup.currentIndex >= completerPopup.count))
|
if (completerPopup.count > 0 && (completerPopup.currentIndex < 0 || completerPopup.currentIndex >= completerPopup.count))
|
||||||
completerPopup.currentIndex = 0;
|
completerPopup.currentIndex = 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
target: completerPopup
|
target: completerPopup
|
||||||
}
|
}
|
||||||
|
|
||||||
Overlay.modal: Rectangle {
|
|
||||||
color: "#aa1E1E1E"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.6
|
import QtQuick 2.6
|
||||||
import QtQuick.Controls 2.2
|
import QtQuick.Controls 2.2
|
||||||
import im.nheko
|
import im.nheko
|
||||||
|
@ -12,58 +10,54 @@ import im.nheko
|
||||||
Flow {
|
Flow {
|
||||||
id: reactionFlow
|
id: reactionFlow
|
||||||
|
|
||||||
|
property string eventId
|
||||||
|
|
||||||
// highlight colors for selfReactedEvent background
|
// highlight colors for selfReactedEvent background
|
||||||
property real highlightHue: timelineRoot.palette.highlight.hslHue
|
property real highlightHue: timelineRoot.palette.highlight.hslHue
|
||||||
property real highlightSat: timelineRoot.palette.highlight.hslSaturation
|
|
||||||
property real highlightLight: timelineRoot.palette.highlight.hslLightness
|
property real highlightLight: timelineRoot.palette.highlight.hslLightness
|
||||||
property string eventId
|
property real highlightSat: timelineRoot.palette.highlight.hslSaturation
|
||||||
property alias reactions: repeater.model
|
property alias reactions: repeater.model
|
||||||
|
|
||||||
spacing: 4
|
spacing: 4
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
id: repeater
|
id: repeater
|
||||||
|
|
||||||
delegate: AbstractButton {
|
delegate: AbstractButton {
|
||||||
id: reaction
|
id: reaction
|
||||||
|
|
||||||
hoverEnabled: true
|
|
||||||
implicitWidth: contentItem.childrenRect.width + contentItem.leftPadding * 2
|
|
||||||
implicitHeight: contentItem.childrenRect.height
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
onClicked: {
|
ToolTip.visible: hovered
|
||||||
console.debug("Picked " + modelData.key + "in response to " + reactionFlow.eventId + ". selfReactedEvent: " + modelData.selfReactedEvent);
|
hoverEnabled: true
|
||||||
room.input.reaction(reactionFlow.eventId, modelData.key);
|
implicitHeight: contentItem.childrenRect.height
|
||||||
}
|
implicitWidth: contentItem.childrenRect.width + contentItem.leftPadding * 2
|
||||||
Component.onCompleted: {
|
|
||||||
ToolTip.text = Qt.binding(function() {
|
|
||||||
if (textMetrics.elidedText === textMetrics.text) {
|
|
||||||
return modelData.users;
|
|
||||||
}
|
|
||||||
return modelData.displayKey + "\n" + modelData.users;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
border.color: (reaction.hovered || modelData.selfReactedEvent !== '') ? timelineRoot.palette.highlight : timelineRoot.palette.text
|
||||||
|
border.width: 1
|
||||||
|
color: modelData.selfReactedEvent !== '' ? Qt.hsla(highlightHue, highlightSat, highlightLight, 0.2) : timelineRoot.palette.window
|
||||||
|
implicitHeight: reaction.implicitHeight
|
||||||
|
implicitWidth: reaction.implicitWidth
|
||||||
|
radius: reaction.height / 2
|
||||||
|
}
|
||||||
contentItem: Row {
|
contentItem: Row {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: reactionText.implicitHeight / 4
|
|
||||||
leftPadding: reactionText.implicitHeight / 2
|
leftPadding: reactionText.implicitHeight / 2
|
||||||
rightPadding: reactionText.implicitHeight / 2
|
rightPadding: reactionText.implicitHeight / 2
|
||||||
|
spacing: reactionText.implicitHeight / 4
|
||||||
|
|
||||||
TextMetrics {
|
TextMetrics {
|
||||||
id: textMetrics
|
id: textMetrics
|
||||||
|
|
||||||
font.family: Settings.emojiFont
|
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
elideWidth: 150
|
elideWidth: 150
|
||||||
|
font.family: Settings.emojiFont
|
||||||
text: modelData.displayKey
|
text: modelData.displayKey
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: reactionText
|
id: reactionText
|
||||||
|
|
||||||
anchors.baseline: reactionCounter.baseline
|
anchors.baseline: reactionCounter.baseline
|
||||||
|
color: reaction.hovered ? timelineRoot.palette.highlight : timelineRoot.palette.text
|
||||||
|
font.family: Settings.emojiFont
|
||||||
|
maximumLineCount: 1
|
||||||
text: {
|
text: {
|
||||||
// When an emoji font is selected that doesn't have …, it is dropped from elidedText. So we add it back.
|
// When an emoji font is selected that doesn't have …, it is dropped from elidedText. So we add it back.
|
||||||
if (textMetrics.elidedText !== modelData.displayKey) {
|
if (textMetrics.elidedText !== modelData.displayKey) {
|
||||||
|
@ -73,42 +67,34 @@ Flow {
|
||||||
}
|
}
|
||||||
return textMetrics.elidedText;
|
return textMetrics.elidedText;
|
||||||
}
|
}
|
||||||
font.family: Settings.emojiFont
|
|
||||||
color: reaction.hovered ? timelineRoot.palette.highlight : timelineRoot.palette.text
|
|
||||||
maximumLineCount: 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: divider
|
id: divider
|
||||||
|
color: (reaction.hovered || modelData.selfReactedEvent !== '') ? timelineRoot.palette.highlight : timelineRoot.palette.text
|
||||||
height: Math.floor(reactionCounter.implicitHeight * 1.4)
|
height: Math.floor(reactionCounter.implicitHeight * 1.4)
|
||||||
width: 1
|
width: 1
|
||||||
color: (reaction.hovered || modelData.selfReactedEvent !== '') ? timelineRoot.palette.highlight : timelineRoot.palette.text
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: reactionCounter
|
id: reactionCounter
|
||||||
|
|
||||||
anchors.verticalCenter: divider.verticalCenter
|
anchors.verticalCenter: divider.verticalCenter
|
||||||
text: modelData.count
|
|
||||||
font: reaction.font
|
|
||||||
color: reaction.hovered ? timelineRoot.palette.highlight : timelineRoot.palette.text
|
color: reaction.hovered ? timelineRoot.palette.highlight : timelineRoot.palette.text
|
||||||
|
font: reaction.font
|
||||||
|
text: modelData.count
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
Component.onCompleted: {
|
||||||
anchors.centerIn: parent
|
ToolTip.text = Qt.binding(function () {
|
||||||
implicitWidth: reaction.implicitWidth
|
if (textMetrics.elidedText === textMetrics.text) {
|
||||||
implicitHeight: reaction.implicitHeight
|
return modelData.users;
|
||||||
border.color: (reaction.hovered || modelData.selfReactedEvent !== '') ? timelineRoot.palette.highlight : timelineRoot.palette.text
|
}
|
||||||
color: modelData.selfReactedEvent !== '' ? Qt.hsla(highlightHue, highlightSat, highlightLight, 0.2) : timelineRoot.palette.window
|
return modelData.displayKey + "\n" + modelData.users;
|
||||||
border.width: 1
|
});
|
||||||
radius: reaction.height / 2
|
}
|
||||||
|
onClicked: {
|
||||||
|
console.debug("Picked " + modelData.key + "in response to " + reactionFlow.eventId + ". selfReactedEvent: " + modelData.selfReactedEvent);
|
||||||
|
room.input.reaction(reactionFlow.eventId, modelData.key);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
import "delegates"
|
||||||
import "./delegates/"
|
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
|
@ -11,76 +9,71 @@ import im.nheko
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: replyPopup
|
id: replyPopup
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
visible: room && (room.reply || room.edit)
|
color: timelineRoot.palette.window
|
||||||
// Height of child, plus margins, plus border
|
// Height of child, plus margins, plus border
|
||||||
implicitHeight: (room && room.reply ? replyPreview.height : closeEditButton.height) + Nheko.paddingSmall
|
implicitHeight: (room && room.reply ? replyPreview.height : closeEditButton.height) + Nheko.paddingSmall
|
||||||
color: timelineRoot.palette.window
|
visible: room && (room.reply || room.edit)
|
||||||
z: 3
|
z: 3
|
||||||
|
|
||||||
Reply {
|
Reply {
|
||||||
id: replyPreview
|
id: replyPreview
|
||||||
|
|
||||||
property var modelData: room ? room.getDump(room.reply, room.id) : {
|
property var modelData: room ? room.getDump(room.reply, room.id) : {}
|
||||||
}
|
|
||||||
|
|
||||||
visible: room && room.reply
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: replyPopup.width < 450? Nheko.paddingSmall : (CallManager.callsSupported? 2*(22+16) : 1*(22+16))
|
anchors.leftMargin: replyPopup.width < 450 ? Nheko.paddingSmall : (CallManager.callsSupported ? 2 * (22 + 16) : 1 * (22 + 16))
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.rightMargin: replyPopup.width < 450? 2*(22+16) : 3*(22+16)
|
anchors.rightMargin: replyPopup.width < 450 ? 2 * (22 + 16) : 3 * (22 + 16)
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.topMargin: Nheko.paddingSmall
|
anchors.topMargin: Nheko.paddingSmall
|
||||||
userColor: TimelineManager.userColor(modelData.userId, timelineRoot.palette.window)
|
|
||||||
blurhash: modelData.blurhash ?? ""
|
blurhash: modelData.blurhash ?? ""
|
||||||
body: modelData.body ?? ""
|
body: modelData.body ?? ""
|
||||||
formattedBody: modelData.formattedBody ?? ""
|
encryptionError: modelData.encryptionError ?? 0
|
||||||
eventId: modelData.eventId ?? ""
|
eventId: modelData.eventId ?? ""
|
||||||
filename: modelData.filename ?? ""
|
filename: modelData.filename ?? ""
|
||||||
filesize: modelData.filesize ?? ""
|
filesize: modelData.filesize ?? ""
|
||||||
|
formattedBody: modelData.formattedBody ?? ""
|
||||||
|
isOnlyEmoji: modelData.isOnlyEmoji ?? false
|
||||||
|
originalWidth: modelData.originalWidth ?? 0
|
||||||
proportionalHeight: modelData.proportionalHeight ?? 1
|
proportionalHeight: modelData.proportionalHeight ?? 1
|
||||||
type: modelData.type ?? MtxEvent.UnknownMessage
|
type: modelData.type ?? MtxEvent.UnknownMessage
|
||||||
typeString: modelData.typeString ?? ""
|
typeString: modelData.typeString ?? ""
|
||||||
url: modelData.url ?? ""
|
url: modelData.url ?? ""
|
||||||
originalWidth: modelData.originalWidth ?? 0
|
userColor: TimelineManager.userColor(modelData.userId, timelineRoot.palette.window)
|
||||||
isOnlyEmoji: modelData.isOnlyEmoji ?? false
|
|
||||||
userId: modelData.userId ?? ""
|
userId: modelData.userId ?? ""
|
||||||
userName: modelData.userName ?? ""
|
userName: modelData.userName ?? ""
|
||||||
encryptionError: modelData.encryptionError ?? 0
|
visible: room && room.reply
|
||||||
width: parent.width
|
width: parent.width
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: closeReplyButton
|
id: closeReplyButton
|
||||||
|
ToolTip.text: qsTr("Close")
|
||||||
visible: room && room.reply
|
ToolTip.visible: closeReplyButton.hovered
|
||||||
|
anchors.margins: Nheko.paddingSmall
|
||||||
anchors.right: replyPreview.right
|
anchors.right: replyPreview.right
|
||||||
anchors.top: replyPreview.top
|
anchors.top: replyPreview.top
|
||||||
anchors.margins: Nheko.paddingSmall
|
|
||||||
hoverEnabled: true
|
|
||||||
width: 16
|
|
||||||
height: 16
|
height: 16
|
||||||
|
hoverEnabled: true
|
||||||
image: ":/icons/icons/ui/dismiss.svg"
|
image: ":/icons/icons/ui/dismiss.svg"
|
||||||
ToolTip.visible: closeReplyButton.hovered
|
visible: room && room.reply
|
||||||
ToolTip.text: qsTr("Close")
|
width: 16
|
||||||
|
|
||||||
onClicked: room.reply = undefined
|
onClicked: room.reply = undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: closeEditButton
|
id: closeEditButton
|
||||||
|
ToolTip.text: qsTr("Cancel Edit")
|
||||||
visible: room && room.edit
|
ToolTip.visible: closeEditButton.hovered
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.margins: 8
|
anchors.margins: 8
|
||||||
|
anchors.right: parent.right
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
|
height: 22
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
image: ":/icons/icons/ui/dismiss_edit.svg"
|
image: ":/icons/icons/ui/dismiss_edit.svg"
|
||||||
|
visible: room && room.edit
|
||||||
width: 22
|
width: 22
|
||||||
height: 22
|
|
||||||
ToolTip.visible: closeEditButton.hovered
|
|
||||||
ToolTip.text: qsTr("Cancel Edit")
|
|
||||||
onClicked: room.edit = undefined
|
onClicked: room.edit = undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
308
qml/Root.qml
308
qml/Root.qml
|
@ -1,15 +1,13 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
import "delegates"
|
||||||
import "./delegates"
|
import "device-verification"
|
||||||
import "./device-verification"
|
import "dialogs"
|
||||||
import "./dialogs"
|
import "emoji"
|
||||||
import "./emoji"
|
import "pages"
|
||||||
import "./pages"
|
import "voip"
|
||||||
import "./voip"
|
import "ui"
|
||||||
import "./ui"
|
|
||||||
import Qt.labs.platform 1.1 as Platform
|
import Qt.labs.platform 1.1 as Platform
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
|
@ -19,6 +17,15 @@ import im.nheko
|
||||||
|
|
||||||
Pane {
|
Pane {
|
||||||
id: timelineRoot
|
id: timelineRoot
|
||||||
|
function destroyOnClose(obj) {
|
||||||
|
if (obj.closing != undefined)
|
||||||
|
obj.closing.connect(() => obj.destroy(1000));
|
||||||
|
else if (obj.aboutToHide != undefined)
|
||||||
|
obj.aboutToHide.connect(() => obj.destroy(1000));
|
||||||
|
}
|
||||||
|
function destroyOnClosed(obj) {
|
||||||
|
obj.aboutToHide.connect(() => obj.destroy(1000));
|
||||||
|
}
|
||||||
|
|
||||||
background: null
|
background: null
|
||||||
padding: 0
|
padding: 0
|
||||||
|
@ -33,270 +40,203 @@ Pane {
|
||||||
// running: true
|
// running: true
|
||||||
// repeat: true
|
// repeat: true
|
||||||
//}
|
//}
|
||||||
|
|
||||||
EmojiPicker {
|
EmojiPicker {
|
||||||
id: emojiPopup
|
id: emojiPopup
|
||||||
|
|
||||||
colors: palette
|
colors: palette
|
||||||
model: TimelineManager.completerFor("allemoji", "")
|
model: TimelineManager.completerFor("allemoji", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: userProfileComponent
|
id: userProfileComponent
|
||||||
|
|
||||||
UserProfile {
|
UserProfile {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: roomSettingsComponent
|
id: roomSettingsComponent
|
||||||
|
|
||||||
RoomSettings {
|
RoomSettings {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: roomMembersComponent
|
id: roomMembersComponent
|
||||||
|
|
||||||
RoomMembers {
|
RoomMembers {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: mobileCallInviteDialog
|
id: mobileCallInviteDialog
|
||||||
|
|
||||||
CallInvite {
|
CallInvite {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: quickSwitcherComponent
|
id: quickSwitcherComponent
|
||||||
|
|
||||||
QuickSwitcher {
|
QuickSwitcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: deviceVerificationDialog
|
id: deviceVerificationDialog
|
||||||
|
|
||||||
DeviceVerification {
|
DeviceVerification {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: inviteDialog
|
id: inviteDialog
|
||||||
|
|
||||||
InviteDialog {
|
InviteDialog {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: packSettingsComponent
|
id: packSettingsComponent
|
||||||
|
|
||||||
ImagePackSettingsDialog {
|
ImagePackSettingsDialog {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: readReceiptsDialog
|
id: readReceiptsDialog
|
||||||
|
|
||||||
ReadReceipts {
|
ReadReceipts {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: rawMessageDialog
|
id: rawMessageDialog
|
||||||
|
|
||||||
RawMessageDialog {
|
RawMessageDialog {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: logoutDialog
|
id: logoutDialog
|
||||||
|
|
||||||
LogoutDialog {
|
LogoutDialog {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: joinRoomDialog
|
id: joinRoomDialog
|
||||||
|
|
||||||
JoinRoomDialog {
|
JoinRoomDialog {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: leaveRoomComponent
|
id: leaveRoomComponent
|
||||||
|
|
||||||
LeaveRoomDialog {
|
LeaveRoomDialog {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: imageOverlay
|
id: imageOverlay
|
||||||
|
|
||||||
ImageOverlay {
|
ImageOverlay {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: userSettingsPage
|
id: userSettingsPage
|
||||||
|
|
||||||
UserSettingsPage {
|
UserSettingsPage {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: StandardKey.Quit
|
sequence: StandardKey.Quit
|
||||||
|
|
||||||
onActivated: Qt.quit()
|
onActivated: Qt.quit()
|
||||||
}
|
}
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: "Ctrl+K"
|
sequence: "Ctrl+K"
|
||||||
|
|
||||||
onActivated: {
|
onActivated: {
|
||||||
var quickSwitch = quickSwitcherComponent.createObject(timelineRoot);
|
var quickSwitch = quickSwitcherComponent.createObject(timelineRoot);
|
||||||
quickSwitch.open();
|
quickSwitch.open();
|
||||||
destroyOnClosed(quickSwitch);
|
destroyOnClosed(quickSwitch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
// Add alternative shortcut, because sometimes Alt+A is stolen by the TextEdit
|
// Add alternative shortcut, because sometimes Alt+A is stolen by the TextEdit
|
||||||
sequences: ["Alt+A", "Ctrl+Shift+A"]
|
sequences: ["Alt+A", "Ctrl+Shift+A"]
|
||||||
|
|
||||||
onActivated: Rooms.nextRoomWithActivity()
|
onActivated: Rooms.nextRoomWithActivity()
|
||||||
}
|
}
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: "Ctrl+Down"
|
sequence: "Ctrl+Down"
|
||||||
|
|
||||||
onActivated: Rooms.nextRoom()
|
onActivated: Rooms.nextRoom()
|
||||||
}
|
}
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: "Ctrl+Up"
|
sequence: "Ctrl+Up"
|
||||||
|
|
||||||
onActivated: Rooms.previousRoom()
|
onActivated: Rooms.previousRoom()
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
|
function onOpenJoinRoomDialog() {
|
||||||
|
var dialog = joinRoomDialog.createObject(timelineRoot);
|
||||||
|
dialog.show();
|
||||||
|
destroyOnClose(dialog);
|
||||||
|
}
|
||||||
function onOpenLogoutDialog() {
|
function onOpenLogoutDialog() {
|
||||||
var dialog = logoutDialog.createObject(timelineRoot);
|
var dialog = logoutDialog.createObject(timelineRoot);
|
||||||
dialog.open();
|
dialog.open();
|
||||||
destroyOnClose(dialog);
|
destroyOnClose(dialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onOpenJoinRoomDialog() {
|
|
||||||
var dialog = joinRoomDialog.createObject(timelineRoot);
|
|
||||||
dialog.show();
|
|
||||||
destroyOnClose(dialog);
|
|
||||||
}
|
|
||||||
|
|
||||||
target: Nheko
|
target: Nheko
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onNewDeviceVerificationRequest(flow) {
|
function onNewDeviceVerificationRequest(flow) {
|
||||||
var dialog = deviceVerificationDialog.createObject(timelineRoot, {
|
var dialog = deviceVerificationDialog.createObject(timelineRoot, {
|
||||||
"flow": flow
|
"flow": flow
|
||||||
});
|
});
|
||||||
dialog.show();
|
dialog.show();
|
||||||
destroyOnClose(dialog);
|
destroyOnClose(dialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
target: VerificationManager
|
target: VerificationManager
|
||||||
}
|
}
|
||||||
|
|
||||||
function destroyOnClose(obj) {
|
|
||||||
if (obj.closing != undefined) obj.closing.connect(() => obj.destroy(1000));
|
|
||||||
else if (obj.aboutToHide != undefined) obj.aboutToHide.connect(() => obj.destroy(1000));
|
|
||||||
}
|
|
||||||
|
|
||||||
function destroyOnClosed(obj) {
|
|
||||||
obj.aboutToHide.connect(() => obj.destroy(1000));
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
|
function onOpenInviteUsersDialog(invitees) {
|
||||||
|
var dialog = inviteDialog.createObject(timelineRoot, {
|
||||||
|
"roomId": Rooms.currentRoom.roomId,
|
||||||
|
"plainRoomName": Rooms.currentRoom.plainRoomName,
|
||||||
|
"invitees": invitees
|
||||||
|
});
|
||||||
|
dialog.show();
|
||||||
|
destroyOnClose(dialog);
|
||||||
|
}
|
||||||
|
function onOpenLeaveRoomDialog(roomid, reason) {
|
||||||
|
var dialog = leaveRoomComponent.createObject(timelineRoot, {
|
||||||
|
"roomId": roomid,
|
||||||
|
"reason": reason
|
||||||
|
});
|
||||||
|
dialog.open();
|
||||||
|
destroyOnClose(dialog);
|
||||||
|
}
|
||||||
function onOpenProfile(profile) {
|
function onOpenProfile(profile) {
|
||||||
var userProfile = userProfileComponent.createObject(timelineRoot, {
|
var userProfile = userProfileComponent.createObject(timelineRoot, {
|
||||||
"profile": profile
|
"profile": profile
|
||||||
});
|
});
|
||||||
userProfile.show();
|
userProfile.show();
|
||||||
destroyOnClose(userProfile);
|
destroyOnClose(userProfile);
|
||||||
}
|
}
|
||||||
|
function onOpenRoomMembersDialog(members, room) {
|
||||||
|
var membersDialog = roomMembersComponent.createObject(timelineRoot, {
|
||||||
|
"members": members,
|
||||||
|
"room": room
|
||||||
|
});
|
||||||
|
membersDialog.show();
|
||||||
|
destroyOnClose(membersDialog);
|
||||||
|
}
|
||||||
|
function onOpenRoomSettingsDialog(settings) {
|
||||||
|
var roomSettings = roomSettingsComponent.createObject(timelineRoot, {
|
||||||
|
"roomSettings": settings
|
||||||
|
});
|
||||||
|
roomSettings.show();
|
||||||
|
destroyOnClose(roomSettings);
|
||||||
|
}
|
||||||
|
function onShowImageOverlay(room, eventId, url, proportionalHeight, originalWidth) {
|
||||||
|
var dialog = imageOverlay.createObject(timelineRoot, {
|
||||||
|
"room": room,
|
||||||
|
"eventId": eventId,
|
||||||
|
"url": url,
|
||||||
|
"originalWidth": originalWidth ?? 0,
|
||||||
|
"proportionalHeight": proportionalHeight ?? 0
|
||||||
|
});
|
||||||
|
dialog.showFullScreen();
|
||||||
|
destroyOnClose(dialog);
|
||||||
|
}
|
||||||
function onShowImagePackSettings(room, packlist) {
|
function onShowImagePackSettings(room, packlist) {
|
||||||
var packSet = packSettingsComponent.createObject(timelineRoot, {
|
var packSet = packSettingsComponent.createObject(timelineRoot, {
|
||||||
"room": room,
|
"room": room,
|
||||||
"packlist": packlist
|
"packlist": packlist
|
||||||
});
|
});
|
||||||
packSet.show();
|
packSet.show();
|
||||||
destroyOnClose(packSet);
|
destroyOnClose(packSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onOpenRoomMembersDialog(members, room) {
|
|
||||||
var membersDialog = roomMembersComponent.createObject(timelineRoot, {
|
|
||||||
"members": members,
|
|
||||||
"room": room
|
|
||||||
});
|
|
||||||
membersDialog.show();
|
|
||||||
destroyOnClose(membersDialog);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onOpenRoomSettingsDialog(settings) {
|
|
||||||
var roomSettings = roomSettingsComponent.createObject(timelineRoot, {
|
|
||||||
"roomSettings": settings
|
|
||||||
});
|
|
||||||
roomSettings.show();
|
|
||||||
destroyOnClose(roomSettings);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onOpenInviteUsersDialog(invitees) {
|
|
||||||
var dialog = inviteDialog.createObject(timelineRoot, {
|
|
||||||
"roomId": Rooms.currentRoom.roomId,
|
|
||||||
"plainRoomName": Rooms.currentRoom.plainRoomName,
|
|
||||||
"invitees": invitees
|
|
||||||
});
|
|
||||||
dialog.show();
|
|
||||||
destroyOnClose(dialog);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onOpenLeaveRoomDialog(roomid, reason) {
|
|
||||||
var dialog = leaveRoomComponent.createObject(timelineRoot, {
|
|
||||||
"roomId": roomid,
|
|
||||||
"reason": reason
|
|
||||||
});
|
|
||||||
dialog.open();
|
|
||||||
destroyOnClose(dialog);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onShowImageOverlay(room, eventId, url, proportionalHeight, originalWidth) {
|
|
||||||
var dialog = imageOverlay.createObject(timelineRoot, {
|
|
||||||
"room": room,
|
|
||||||
"eventId": eventId,
|
|
||||||
"url": url,
|
|
||||||
"originalWidth": originalWidth ?? 0,
|
|
||||||
"proportionalHeight": proportionalHeight ?? 0
|
|
||||||
});
|
|
||||||
dialog.showFullScreen();
|
|
||||||
destroyOnClose(dialog);
|
|
||||||
}
|
|
||||||
|
|
||||||
target: TimelineManager
|
target: TimelineManager
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onNewInviteState() {
|
function onNewInviteState() {
|
||||||
if (CallManager.haveCallInvite && Settings.mobileMode) {
|
if (CallManager.haveCallInvite && Settings.mobileMode) {
|
||||||
|
@ -308,144 +248,122 @@ Pane {
|
||||||
|
|
||||||
target: CallManager
|
target: CallManager
|
||||||
}
|
}
|
||||||
|
|
||||||
SelfVerificationCheck {
|
SelfVerificationCheck {
|
||||||
}
|
}
|
||||||
|
|
||||||
InputDialog {
|
InputDialog {
|
||||||
id: uiaPassPrompt
|
id: uiaPassPrompt
|
||||||
|
|
||||||
echoMode: TextInput.Password
|
echoMode: TextInput.Password
|
||||||
title: UIA.title
|
|
||||||
prompt: qsTr("Please enter your login password to continue:")
|
prompt: qsTr("Please enter your login password to continue:")
|
||||||
onAccepted: (t) => {
|
title: UIA.title
|
||||||
|
|
||||||
|
onAccepted: t => {
|
||||||
return UIA.continuePassword(t);
|
return UIA.continuePassword(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
InputDialog {
|
InputDialog {
|
||||||
id: uiaEmailPrompt
|
id: uiaEmailPrompt
|
||||||
|
|
||||||
title: UIA.title
|
|
||||||
prompt: qsTr("Please enter a valid email address to continue:")
|
prompt: qsTr("Please enter a valid email address to continue:")
|
||||||
onAccepted: (t) => {
|
title: UIA.title
|
||||||
|
|
||||||
|
onAccepted: t => {
|
||||||
return UIA.continueEmail(t);
|
return UIA.continueEmail(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PhoneNumberInputDialog {
|
PhoneNumberInputDialog {
|
||||||
id: uiaPhoneNumberPrompt
|
id: uiaPhoneNumberPrompt
|
||||||
|
|
||||||
title: UIA.title
|
|
||||||
prompt: qsTr("Please enter a valid phone number to continue:")
|
prompt: qsTr("Please enter a valid phone number to continue:")
|
||||||
|
title: UIA.title
|
||||||
|
|
||||||
onAccepted: (p, t) => {
|
onAccepted: (p, t) => {
|
||||||
return UIA.continuePhoneNumber(p, t);
|
return UIA.continuePhoneNumber(p, t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
InputDialog {
|
InputDialog {
|
||||||
id: uiaTokenPrompt
|
id: uiaTokenPrompt
|
||||||
|
|
||||||
title: UIA.title
|
|
||||||
prompt: qsTr("Please enter the token, which has been sent to you:")
|
prompt: qsTr("Please enter the token, which has been sent to you:")
|
||||||
onAccepted: (t) => {
|
title: UIA.title
|
||||||
|
|
||||||
|
onAccepted: t => {
|
||||||
return UIA.submit3pidToken(t);
|
return UIA.submit3pidToken(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform.MessageDialog {
|
Platform.MessageDialog {
|
||||||
id: uiaErrorDialog
|
id: uiaErrorDialog
|
||||||
|
|
||||||
buttons: Platform.MessageDialog.Ok
|
buttons: Platform.MessageDialog.Ok
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform.MessageDialog {
|
Platform.MessageDialog {
|
||||||
id: uiaConfirmationLinkDialog
|
id: uiaConfirmationLinkDialog
|
||||||
|
|
||||||
buttons: Platform.MessageDialog.Ok
|
buttons: Platform.MessageDialog.Ok
|
||||||
text: qsTr("Wait for the confirmation link to arrive, then continue.")
|
text: qsTr("Wait for the confirmation link to arrive, then continue.")
|
||||||
|
|
||||||
onAccepted: UIA.continue3pidReceived()
|
onAccepted: UIA.continue3pidReceived()
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onPassword() {
|
|
||||||
console.log("UIA: password needed");
|
|
||||||
uiaPassPrompt.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
function onEmail() {
|
|
||||||
uiaEmailPrompt.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
function onPhoneNumber() {
|
|
||||||
uiaPhoneNumberPrompt.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
function onPrompt3pidToken() {
|
|
||||||
uiaTokenPrompt.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
function onConfirm3pidToken() {
|
function onConfirm3pidToken() {
|
||||||
uiaConfirmationLinkDialog.open();
|
uiaConfirmationLinkDialog.open();
|
||||||
}
|
}
|
||||||
|
function onEmail() {
|
||||||
|
uiaEmailPrompt.show();
|
||||||
|
}
|
||||||
function onError(msg) {
|
function onError(msg) {
|
||||||
uiaErrorDialog.text = msg;
|
uiaErrorDialog.text = msg;
|
||||||
uiaErrorDialog.open();
|
uiaErrorDialog.open();
|
||||||
}
|
}
|
||||||
|
function onPassword() {
|
||||||
|
console.log("UIA: password needed");
|
||||||
|
uiaPassPrompt.show();
|
||||||
|
}
|
||||||
|
function onPhoneNumber() {
|
||||||
|
uiaPhoneNumberPrompt.show();
|
||||||
|
}
|
||||||
|
function onPrompt3pidToken() {
|
||||||
|
uiaTokenPrompt.show();
|
||||||
|
}
|
||||||
|
|
||||||
target: UIA
|
target: UIA
|
||||||
}
|
}
|
||||||
|
|
||||||
StackView {
|
StackView {
|
||||||
id: mainWindow
|
id: mainWindow
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
initialItem: welcomePage
|
initialItem: welcomePage
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: welcomePage
|
id: welcomePage
|
||||||
|
|
||||||
WelcomePage {
|
WelcomePage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: chatPage
|
id: chatPage
|
||||||
|
|
||||||
ChatPage {
|
ChatPage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: loginPage
|
id: loginPage
|
||||||
|
|
||||||
LoginPage {
|
LoginPage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: registerPage
|
id: registerPage
|
||||||
|
|
||||||
RegisterPage {
|
RegisterPage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Snackbar {
|
||||||
Snackbar { id: snackbar }
|
id: snackbar
|
||||||
|
}
|
||||||
Connections {
|
Connections {
|
||||||
function onSwitchToChatPage() {
|
|
||||||
mainWindow.replace(null, chatPage);
|
|
||||||
}
|
|
||||||
function onSwitchToLoginPage(error) {
|
|
||||||
mainWindow.replace(welcomePage, {}, loginPage, {"error": error}, StackView.PopTransition);
|
|
||||||
}
|
|
||||||
function onShowNotification(msg) {
|
function onShowNotification(msg) {
|
||||||
snackbar.showNotification(msg);
|
snackbar.showNotification(msg);
|
||||||
console.log("New snack: " + msg);
|
console.log("New snack: " + msg);
|
||||||
}
|
}
|
||||||
|
function onSwitchToChatPage() {
|
||||||
|
mainWindow.replace(null, chatPage);
|
||||||
|
}
|
||||||
|
function onSwitchToLoginPage(error) {
|
||||||
|
mainWindow.replace(welcomePage, {}, loginPage, {
|
||||||
|
"error": error
|
||||||
|
}, StackView.PopTransition);
|
||||||
|
}
|
||||||
|
|
||||||
target: MainWindow
|
target: MainWindow
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
import "components"
|
||||||
import "./components/"
|
|
||||||
import Qt.labs.platform 1.1 as P
|
import Qt.labs.platform 1.1 as P
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
|
@ -11,68 +9,61 @@ import QtQuick.Layouts 1.3
|
||||||
import im.nheko
|
import im.nheko
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
visible: false
|
|
||||||
enabled: false
|
enabled: false
|
||||||
|
visible: false
|
||||||
|
|
||||||
Dialog {
|
Dialog {
|
||||||
id: showRecoverKeyDialog
|
id: showRecoverKeyDialog
|
||||||
|
|
||||||
property string recoveryKey: ""
|
property string recoveryKey: ""
|
||||||
|
|
||||||
parent: Overlay.overlay
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
height: content.height + implicitFooterHeight + implicitHeaderHeight
|
|
||||||
width: content.width
|
|
||||||
padding: 0
|
|
||||||
modal: true
|
|
||||||
standardButtons: Dialog.Ok
|
|
||||||
closePolicy: Popup.NoAutoClose
|
closePolicy: Popup.NoAutoClose
|
||||||
|
height: content.height + implicitFooterHeight + implicitHeaderHeight
|
||||||
ColumnLayout {
|
modal: true
|
||||||
id: content
|
padding: 0
|
||||||
|
parent: Overlay.overlay
|
||||||
spacing: 0
|
standardButtons: Dialog.Ok
|
||||||
|
width: content.width
|
||||||
Label {
|
|
||||||
Layout.margins: Nheko.paddingMedium
|
|
||||||
Layout.maximumWidth: (Overlay.overlay ? Overlay.overlay.width : 400) - Nheko.paddingMedium * 4
|
|
||||||
Layout.fillWidth: true
|
|
||||||
text: qsTr("This is your recovery key. You will need it to restore access to your encrypted messages and verification keys. Keep this safe. Don't share it with anyone and don't lose it! Do not pass go! Do not collect $200!")
|
|
||||||
color: timelineRoot.palette.text
|
|
||||||
wrapMode: Text.Wrap
|
|
||||||
}
|
|
||||||
|
|
||||||
TextEdit {
|
|
||||||
Layout.maximumWidth: (Overlay.overlay ? Overlay.overlay.width : 400) - Nheko.paddingMedium * 4
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
horizontalAlignment: TextEdit.AlignHCenter
|
|
||||||
verticalAlignment: TextEdit.AlignVCenter
|
|
||||||
readOnly: true
|
|
||||||
selectByMouse: true
|
|
||||||
text: showRecoverKeyDialog.recoveryKey
|
|
||||||
color: timelineRoot.palette.text
|
|
||||||
font.bold: true
|
|
||||||
wrapMode: TextEdit.Wrap
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: timelineRoot.palette.window
|
|
||||||
border.color: Nheko.theme.separator
|
border.color: Nheko.theme.separator
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
color: timelineRoot.palette.window
|
||||||
radius: Nheko.paddingSmall
|
radius: Nheko.paddingSmall
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
ColumnLayout {
|
||||||
|
id: content
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.margins: Nheko.paddingMedium
|
||||||
|
Layout.maximumWidth: (Overlay.overlay ? Overlay.overlay.width : 400) - Nheko.paddingMedium * 4
|
||||||
|
color: timelineRoot.palette.text
|
||||||
|
text: qsTr("This is your recovery key. You will need it to restore access to your encrypted messages and verification keys. Keep this safe. Don't share it with anyone and don't lose it! Do not pass go! Do not collect $200!")
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
}
|
||||||
|
TextEdit {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.maximumWidth: (Overlay.overlay ? Overlay.overlay.width : 400) - Nheko.paddingMedium * 4
|
||||||
|
color: timelineRoot.palette.text
|
||||||
|
font.bold: true
|
||||||
|
horizontalAlignment: TextEdit.AlignHCenter
|
||||||
|
readOnly: true
|
||||||
|
selectByMouse: true
|
||||||
|
text: showRecoverKeyDialog.recoveryKey
|
||||||
|
verticalAlignment: TextEdit.AlignVCenter
|
||||||
|
wrapMode: TextEdit.Wrap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
P.MessageDialog {
|
P.MessageDialog {
|
||||||
id: successDialog
|
id: successDialog
|
||||||
|
|
||||||
buttons: P.MessageDialog.Ok
|
buttons: P.MessageDialog.Ok
|
||||||
text: qsTr("Encryption setup successfully")
|
text: qsTr("Encryption setup successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
P.MessageDialog {
|
P.MessageDialog {
|
||||||
id: failureDialog
|
id: failureDialog
|
||||||
|
|
||||||
|
@ -81,199 +72,185 @@ Item {
|
||||||
buttons: P.MessageDialog.Ok
|
buttons: P.MessageDialog.Ok
|
||||||
text: qsTr("Failed to setup encryption: %1").arg(errorMessage)
|
text: qsTr("Failed to setup encryption: %1").arg(errorMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
MainWindowDialog {
|
MainWindowDialog {
|
||||||
id: bootstrapCrosssigning
|
id: bootstrapCrosssigning
|
||||||
|
background: Rectangle {
|
||||||
|
border.color: Nheko.theme.separator
|
||||||
|
border.width: 1
|
||||||
|
color: timelineRoot.palette.window
|
||||||
|
radius: Nheko.paddingSmall
|
||||||
|
}
|
||||||
|
|
||||||
onAccepted: SelfVerificationStatus.setupCrosssigning(storeSecretsOnline.checked, usePassword.checked ? passwordField.text : "", useOnlineKeyBackup.checked)
|
onAccepted: SelfVerificationStatus.setupCrosssigning(storeSecretsOnline.checked, usePassword.checked ? passwordField.text : "", useOnlineKeyBackup.checked)
|
||||||
|
|
||||||
GridLayout {
|
GridLayout {
|
||||||
id: grid
|
id: grid
|
||||||
|
columnSpacing: 0
|
||||||
width: bootstrapCrosssigning.useableWidth
|
|
||||||
columns: 2
|
columns: 2
|
||||||
rowSpacing: 0
|
rowSpacing: 0
|
||||||
columnSpacing: 0
|
width: bootstrapCrosssigning.useableWidth
|
||||||
z: 1
|
z: 1
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.margins: Nheko.paddingMedium
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.columnSpan: 2
|
Layout.columnSpan: 2
|
||||||
|
Layout.margins: Nheko.paddingMedium
|
||||||
|
color: timelineRoot.palette.text
|
||||||
font.pointSize: fontMetrics.font.pointSize * 2
|
font.pointSize: fontMetrics.font.pointSize * 2
|
||||||
text: qsTr("Setup Encryption")
|
text: qsTr("Setup Encryption")
|
||||||
color: timelineRoot.palette.text
|
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.margins: Nheko.paddingMedium
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
Layout.columnSpan: 2
|
Layout.columnSpan: 2
|
||||||
|
Layout.margins: Nheko.paddingMedium
|
||||||
Layout.maximumWidth: grid.width - Nheko.paddingMedium * 2
|
Layout.maximumWidth: grid.width - Nheko.paddingMedium * 2
|
||||||
text: qsTr("Hello and welcome to Matrix!\nIt seems like you are new. Before you can securely encrypt your messages, we need to setup a few small things. You can either press accept immediately or adjust a few basic options. We also try to explain a few of the basics. You can skip those parts, but they might prove to be helpful!")
|
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
|
text: qsTr("Hello and welcome to Matrix!\nIt seems like you are new. Before you can securely encrypt your messages, we need to setup a few small things. You can either press accept immediately or adjust a few basic options. We also try to explain a few of the basics. You can skip those parts, but they might prove to be helpful!")
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.margins: Nheko.paddingMedium
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
Layout.columnSpan: 1
|
Layout.columnSpan: 1
|
||||||
|
Layout.margins: Nheko.paddingMedium
|
||||||
Layout.maximumWidth: Math.floor(grid.width / 2) - Nheko.paddingMedium * 2
|
Layout.maximumWidth: Math.floor(grid.width / 2) - Nheko.paddingMedium * 2
|
||||||
text: "Store secrets online.\nYou have a few secrets to make all the encryption magic work. While you can keep them stored only locally, we recommend storing them encrypted on the server. Otherwise it will be painful to recover them. Only disable this if you are paranoid and like losing your data!"
|
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
|
text: "Store secrets online.\nYou have a few secrets to make all the encryption magic work. While you can keep them stored only locally, we recommend storing them encrypted on the server. Otherwise it will be painful to recover them. Only disable this if you are paranoid and like losing your data!"
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.margins: Nheko.paddingMedium
|
|
||||||
Layout.preferredHeight: storeSecretsOnline.height
|
|
||||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.margins: Nheko.paddingMedium
|
||||||
|
Layout.preferredHeight: storeSecretsOnline.height
|
||||||
|
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
id: storeSecretsOnline
|
id: storeSecretsOnline
|
||||||
|
|
||||||
checked: true
|
checked: true
|
||||||
|
|
||||||
onClicked: console.log("Store secrets toggled: " + checked)
|
onClicked: console.log("Store secrets toggled: " + checked)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.margins: Nheko.paddingMedium
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
Layout.columnSpan: 1
|
Layout.columnSpan: 1
|
||||||
Layout.rowSpan: 2
|
Layout.margins: Nheko.paddingMedium
|
||||||
Layout.maximumWidth: Math.floor(grid.width / 2) - Nheko.paddingMedium * 2
|
Layout.maximumWidth: Math.floor(grid.width / 2) - Nheko.paddingMedium * 2
|
||||||
visible: storeSecretsOnline.checked
|
Layout.rowSpan: 2
|
||||||
text: "Set an online backup password.\nWe recommend you DON'T set a password and instead only rely on the recovery key. You will get a recovery key in any case when storing the cross-signing secrets online, but passwords are usually not very random, so they are easier to attack than a completely random recovery key. If you choose to use a password, DON'T make it the same as your login password, otherwise your server can read all your encrypted messages. (You don't want that.)"
|
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
|
text: "Set an online backup password.\nWe recommend you DON'T set a password and instead only rely on the recovery key. You will get a recovery key in any case when storing the cross-signing secrets online, but passwords are usually not very random, so they are easier to attack than a completely random recovery key. If you choose to use a password, DON'T make it the same as your login password, otherwise your server can read all your encrypted messages. (You don't want that.)"
|
||||||
|
visible: storeSecretsOnline.checked
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.margins: Nheko.paddingMedium
|
|
||||||
Layout.topMargin: Nheko.paddingLarge
|
|
||||||
Layout.preferredHeight: storeSecretsOnline.height
|
|
||||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||||
Layout.rowSpan: usePassword.checked ? 1 : 2
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.margins: Nheko.paddingMedium
|
||||||
|
Layout.preferredHeight: storeSecretsOnline.height
|
||||||
|
Layout.rowSpan: usePassword.checked ? 1 : 2
|
||||||
|
Layout.topMargin: Nheko.paddingLarge
|
||||||
visible: storeSecretsOnline.checked
|
visible: storeSecretsOnline.checked
|
||||||
|
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
id: usePassword
|
id: usePassword
|
||||||
|
|
||||||
checked: false
|
checked: false
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
id: passwordField
|
id: passwordField
|
||||||
|
|
||||||
Layout.margins: Nheko.paddingMedium
|
|
||||||
Layout.maximumWidth: Math.floor(grid.width / 2) - Nheko.paddingMedium * 2
|
|
||||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||||
Layout.columnSpan: 1
|
Layout.columnSpan: 1
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
visible: storeSecretsOnline.checked && usePassword.checked
|
|
||||||
echoMode: TextInput.Password
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
Layout.margins: Nheko.paddingMedium
|
Layout.margins: Nheko.paddingMedium
|
||||||
|
Layout.maximumWidth: Math.floor(grid.width / 2) - Nheko.paddingMedium * 2
|
||||||
|
echoMode: TextInput.Password
|
||||||
|
visible: storeSecretsOnline.checked && usePassword.checked
|
||||||
|
}
|
||||||
|
Label {
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
Layout.columnSpan: 1
|
Layout.columnSpan: 1
|
||||||
|
Layout.margins: Nheko.paddingMedium
|
||||||
Layout.maximumWidth: Math.floor(grid.width / 2) - Nheko.paddingMedium * 2
|
Layout.maximumWidth: Math.floor(grid.width / 2) - Nheko.paddingMedium * 2
|
||||||
text: "Use online key backup.\nStore the keys for your messages securely encrypted online. In general you do want this, because it protects your messages from becoming unreadable, if you log out by accident. It does however carry a small security risk, if you ever share your recovery key by accident. Currently this also has some other weaknesses, that might allow the server to insert new keys into your backup. The server will however never be able to read your messages."
|
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
|
text: "Use online key backup.\nStore the keys for your messages securely encrypted online. In general you do want this, because it protects your messages from becoming unreadable, if you log out by accident. It does however carry a small security risk, if you ever share your recovery key by accident. Currently this also has some other weaknesses, that might allow the server to insert new keys into your backup. The server will however never be able to read your messages."
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.margins: Nheko.paddingMedium
|
|
||||||
Layout.preferredHeight: storeSecretsOnline.height
|
|
||||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.margins: Nheko.paddingMedium
|
||||||
|
Layout.preferredHeight: storeSecretsOnline.height
|
||||||
|
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
id: useOnlineKeyBackup
|
id: useOnlineKeyBackup
|
||||||
|
|
||||||
checked: true
|
checked: true
|
||||||
|
|
||||||
onClicked: console.log("Online key backup toggled: " + checked)
|
onClicked: console.log("Online key backup toggled: " + checked)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: timelineRoot.palette.window
|
|
||||||
border.color: Nheko.theme.separator
|
|
||||||
border.width: 1
|
|
||||||
radius: Nheko.paddingSmall
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MainWindowDialog {
|
MainWindowDialog {
|
||||||
id: verifyMasterKey
|
id: verifyMasterKey
|
||||||
|
|
||||||
standardButtons: Dialog.Cancel
|
standardButtons: Dialog.Cancel
|
||||||
|
|
||||||
GridLayout {
|
GridLayout {
|
||||||
id: masterGrid
|
id: masterGrid
|
||||||
|
|
||||||
width: verifyMasterKey.useableWidth
|
|
||||||
columns: 1
|
columns: 1
|
||||||
|
width: verifyMasterKey.useableWidth
|
||||||
z: 1
|
z: 1
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.margins: Nheko.paddingMedium
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.margins: Nheko.paddingMedium
|
||||||
|
color: timelineRoot.palette.text
|
||||||
//Layout.columnSpan: 2
|
//Layout.columnSpan: 2
|
||||||
font.pointSize: fontMetrics.font.pointSize * 2
|
font.pointSize: fontMetrics.font.pointSize * 2
|
||||||
text: qsTr("Activate Encryption")
|
text: qsTr("Activate Encryption")
|
||||||
color: timelineRoot.palette.text
|
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.margins: Nheko.paddingMedium
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
|
Layout.margins: Nheko.paddingMedium
|
||||||
//Layout.columnSpan: 2
|
//Layout.columnSpan: 2
|
||||||
Layout.maximumWidth: grid.width - Nheko.paddingMedium * 2
|
Layout.maximumWidth: grid.width - Nheko.paddingMedium * 2
|
||||||
text: qsTr("It seems like you have encryption already configured for this account. To be able to access your encrypted messages and make this device appear as trusted, you can either verify an existing device or (if you have one) enter your recovery passphrase. Please select one of the options below.\nIf you choose verify, you need to have the other device available. If you choose \"enter passphrase\", you will need your recovery key or passphrase. If you click cancel, you can choose to verify yourself at a later point.")
|
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
|
text: qsTr("It seems like you have encryption already configured for this account. To be able to access your encrypted messages and make this device appear as trusted, you can either verify an existing device or (if you have one) enter your recovery passphrase. Please select one of the options below.\nIf you choose verify, you need to have the other device available. If you choose \"enter passphrase\", you will need your recovery key or passphrase. If you click cancel, you can choose to verify yourself at a later point.")
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
}
|
}
|
||||||
|
|
||||||
FlatButton {
|
FlatButton {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
text: qsTr("verify")
|
text: qsTr("verify")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
SelfVerificationStatus.verifyMasterKey();
|
SelfVerificationStatus.verifyMasterKey();
|
||||||
verifyMasterKey.close();
|
verifyMasterKey.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FlatButton {
|
FlatButton {
|
||||||
visible: SelfVerificationStatus.hasSSSS
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
text: qsTr("enter passphrase")
|
text: qsTr("enter passphrase")
|
||||||
|
visible: SelfVerificationStatus.hasSSSS
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
SelfVerificationStatus.verifyMasterKeyWithPassphrase();
|
SelfVerificationStatus.verifyMasterKeyWithPassphrase();
|
||||||
verifyMasterKey.close();
|
verifyMasterKey.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
|
function onSetupCompleted() {
|
||||||
|
successDialog.open();
|
||||||
|
}
|
||||||
|
function onSetupFailed(m) {
|
||||||
|
failureDialog.errorMessage = m;
|
||||||
|
failureDialog.open();
|
||||||
|
}
|
||||||
|
function onShowRecoveryKey(key) {
|
||||||
|
showRecoverKeyDialog.recoveryKey = key;
|
||||||
|
showRecoverKeyDialog.open();
|
||||||
|
}
|
||||||
function onStatusChanged() {
|
function onStatusChanged() {
|
||||||
console.log("STATUS CHANGED: " + SelfVerificationStatus.status);
|
console.log("STATUS CHANGED: " + SelfVerificationStatus.status);
|
||||||
if (SelfVerificationStatus.status == SelfVerificationStatus.NoMasterKey) {
|
if (SelfVerificationStatus.status == SelfVerificationStatus.NoMasterKey) {
|
||||||
|
@ -286,21 +263,6 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onShowRecoveryKey(key) {
|
|
||||||
showRecoverKeyDialog.recoveryKey = key;
|
|
||||||
showRecoverKeyDialog.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
function onSetupCompleted() {
|
|
||||||
successDialog.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
function onSetupFailed(m) {
|
|
||||||
failureDialog.errorMessage = m;
|
|
||||||
failureDialog.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
target: SelfVerificationStatus
|
target: SelfVerificationStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.5
|
import QtQuick 2.5
|
||||||
import QtQuick.Controls 2.1
|
import QtQuick.Controls 2.1
|
||||||
import im.nheko
|
import im.nheko
|
||||||
|
@ -10,15 +8,9 @@ import im.nheko
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: indicator
|
id: indicator
|
||||||
|
|
||||||
required property int status
|
|
||||||
required property string eventId
|
required property string eventId
|
||||||
|
required property int status
|
||||||
|
|
||||||
width: 16
|
|
||||||
height: 16
|
|
||||||
hoverEnabled: true
|
|
||||||
changeColorOnHover: (status == MtxEvent.Read)
|
|
||||||
cursor: (status == MtxEvent.Read) ? Qt.PointingHandCursor : Qt.ArrowCursor
|
|
||||||
ToolTip.visible: hovered && status != MtxEvent.Empty
|
|
||||||
ToolTip.text: {
|
ToolTip.text: {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case MtxEvent.Failed:
|
case MtxEvent.Failed:
|
||||||
|
@ -33,11 +25,11 @@ ImageButton {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onClicked: {
|
ToolTip.visible: hovered && status != MtxEvent.Empty
|
||||||
if (status == MtxEvent.Read)
|
changeColorOnHover: (status == MtxEvent.Read)
|
||||||
room.showReadReceipts(eventId);
|
cursor: (status == MtxEvent.Read) ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
|
height: 16
|
||||||
}
|
hoverEnabled: true
|
||||||
image: {
|
image: {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case MtxEvent.Failed:
|
case MtxEvent.Failed:
|
||||||
|
@ -52,4 +44,10 @@ ImageButton {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
width: 16
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
if (status == MtxEvent.Read)
|
||||||
|
room.showReadReceipts(eventId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
import "delegates"
|
||||||
import "./delegates"
|
import "emoji"
|
||||||
import "./emoji"
|
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
|
@ -14,69 +12,42 @@ import im.nheko
|
||||||
AbstractButton {
|
AbstractButton {
|
||||||
id: r
|
id: r
|
||||||
|
|
||||||
required property double proportionalHeight
|
|
||||||
required property int type
|
|
||||||
required property string typeString
|
|
||||||
required property int originalWidth
|
|
||||||
required property string blurhash
|
required property string blurhash
|
||||||
required property string body
|
required property string body
|
||||||
required property string formattedBody
|
required property string callType
|
||||||
|
required property int duration
|
||||||
|
required property int encryptionError
|
||||||
required property string eventId
|
required property string eventId
|
||||||
required property string filename
|
required property string filename
|
||||||
required property string filesize
|
required property string filesize
|
||||||
required property string url
|
required property string formattedBody
|
||||||
required property string thumbnailUrl
|
|
||||||
required property bool isOnlyEmoji
|
|
||||||
required property bool isSender
|
|
||||||
required property bool isEncrypted
|
|
||||||
required property bool isEditable
|
required property bool isEditable
|
||||||
required property bool isEdited
|
required property bool isEdited
|
||||||
|
required property bool isEncrypted
|
||||||
|
required property bool isOnlyEmoji
|
||||||
|
required property bool isSender
|
||||||
required property bool isStateEvent
|
required property bool isStateEvent
|
||||||
|
required property int originalWidth
|
||||||
|
required property double proportionalHeight
|
||||||
|
required property var reactions
|
||||||
|
required property int relatedEventCacheBuster
|
||||||
required property string replyTo
|
required property string replyTo
|
||||||
|
required property string roomName
|
||||||
|
required property string roomTopic
|
||||||
|
required property int status
|
||||||
|
required property string thumbnailUrl
|
||||||
|
required property var timestamp
|
||||||
|
required property int trustlevel
|
||||||
|
required property int type
|
||||||
|
required property string typeString
|
||||||
|
required property string url
|
||||||
required property string userId
|
required property string userId
|
||||||
required property string userName
|
required property string userName
|
||||||
required property string roomTopic
|
|
||||||
required property string roomName
|
|
||||||
required property string callType
|
|
||||||
required property var reactions
|
|
||||||
required property int trustlevel
|
|
||||||
required property int encryptionError
|
|
||||||
required property int duration
|
|
||||||
required property var timestamp
|
|
||||||
required property int status
|
|
||||||
required property int relatedEventCacheBuster
|
|
||||||
|
|
||||||
|
height: row.height + (reactionRow.height > 0 ? reactionRow.height - 2 : 0)
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: row.height+(reactionRow.height > 0 ? reactionRow.height-2 : 0 )
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
color: (Settings.messageHoverHighlight && hovered) ? timelineRoot.palette.alternateBase : "transparent"
|
|
||||||
anchors.fill: parent
|
|
||||||
// this looks better without margins
|
|
||||||
TapHandler {
|
|
||||||
acceptedButtons: Qt.RightButton
|
|
||||||
onSingleTapped: messageContextMenu.show(eventId, type, isSender, isEncrypted, isEditable, contentItem.child.hoveredLink, contentItem.child.copyText)
|
|
||||||
gesturePolicy: TapHandler.ReleaseWithinBounds
|
|
||||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
onPressAndHold: messageContextMenu.show(eventId, type, isSender, isEncrypted, isEditable, contentItem.child.hoveredLink, contentItem.child.copyText)
|
|
||||||
onDoubleClicked: chat.model.reply = eventId
|
|
||||||
|
|
||||||
DragHandler {
|
|
||||||
id: draghandler
|
|
||||||
yAxis.enabled: false
|
|
||||||
xAxis.maximum: 100
|
|
||||||
xAxis.minimum: -100
|
|
||||||
onActiveChanged: {
|
|
||||||
if(!active && (x < -70 || x > 70))
|
|
||||||
chat.model.reply = eventId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
states: State {
|
states: State {
|
||||||
name: "dragging"
|
name: "dragging"
|
||||||
when: draghandler.active
|
when: draghandler.active
|
||||||
|
@ -84,212 +55,234 @@ AbstractButton {
|
||||||
transitions: Transition {
|
transitions: Transition {
|
||||||
from: "dragging"
|
from: "dragging"
|
||||||
to: ""
|
to: ""
|
||||||
|
|
||||||
PropertyAnimation {
|
PropertyAnimation {
|
||||||
target: r
|
|
||||||
properties: "x"
|
|
||||||
easing.type: Easing.InOutQuad
|
|
||||||
to: 0
|
|
||||||
duration: 100
|
duration: 100
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
properties: "x"
|
||||||
|
target: r
|
||||||
|
to: 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
let link = contentItem.child.linkAt != undefined && contentItem.child.linkAt(pressX-row.x-msg.x, pressY-row.y-msg.y-contentItem.y);
|
let link = contentItem.child.linkAt != undefined && contentItem.child.linkAt(pressX - row.x - msg.x, pressY - row.y - msg.y - contentItem.y);
|
||||||
if (link) {
|
if (link) {
|
||||||
Nheko.openLink(link)
|
Nheko.openLink(link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
onDoubleClicked: chat.model.reply = eventId
|
||||||
|
onPressAndHold: messageContextMenu.show(eventId, type, isSender, isEncrypted, isEditable, contentItem.child.hoveredLink, contentItem.child.copyText)
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: row
|
anchors.fill: parent
|
||||||
property bool bubbleOnRight : isSender && Settings.bubbles
|
color: (Settings.messageHoverHighlight && hovered) ? timelineRoot.palette.alternateBase : "transparent"
|
||||||
anchors.leftMargin: isStateEvent || Settings.smallAvatars? 0 : Nheko.avatarSize+8 // align bubble with section header
|
|
||||||
anchors.left: isStateEvent? undefined : (bubbleOnRight? undefined : parent.left)
|
// this looks better without margins
|
||||||
anchors.right: isStateEvent? undefined: (bubbleOnRight? parent.right : undefined)
|
TapHandler {
|
||||||
anchors.horizontalCenter: isStateEvent? parent.horizontalCenter : undefined
|
acceptedButtons: Qt.RightButton
|
||||||
property int maxWidth: (parent.width-(Settings.smallAvatars || isStateEvent? 0 : Nheko.avatarSize+8))*(Settings.bubbles && !isStateEvent? 0.9 : 1)
|
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad
|
||||||
width: Settings.bubbles? Math.min(maxWidth,Math.max(reply.implicitWidth+8,contentItem.implicitWidth+metadata.width+20)) : maxWidth
|
gesturePolicy: TapHandler.ReleaseWithinBounds
|
||||||
height: msg.height+msg.anchors.margins*2
|
|
||||||
|
onSingleTapped: messageContextMenu.show(eventId, type, isSender, isEncrypted, isEditable, contentItem.child.hoveredLink, contentItem.child.copyText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DragHandler {
|
||||||
|
id: draghandler
|
||||||
|
xAxis.maximum: 100
|
||||||
|
xAxis.minimum: -100
|
||||||
|
yAxis.enabled: false
|
||||||
|
|
||||||
|
onActiveChanged: {
|
||||||
|
if (!active && (x < -70 || x > 70))
|
||||||
|
chat.model.reply = eventId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
id: row
|
||||||
|
|
||||||
property color userColor: TimelineManager.userColor(userId, timelineRoot.palette.base)
|
|
||||||
property color bgColor: timelineRoot.palette.base
|
property color bgColor: timelineRoot.palette.base
|
||||||
|
property bool bubbleOnRight: isSender && Settings.bubbles
|
||||||
|
property int maxWidth: (parent.width - (Settings.smallAvatars || isStateEvent ? 0 : Nheko.avatarSize + 8)) * (Settings.bubbles && !isStateEvent ? 0.9 : 1)
|
||||||
|
property color userColor: TimelineManager.userColor(userId, timelineRoot.palette.base)
|
||||||
|
|
||||||
|
anchors.horizontalCenter: isStateEvent ? parent.horizontalCenter : undefined
|
||||||
|
anchors.left: isStateEvent ? undefined : (bubbleOnRight ? undefined : parent.left)
|
||||||
|
anchors.leftMargin: isStateEvent || Settings.smallAvatars ? 0 : Nheko.avatarSize + 8 // align bubble with section header
|
||||||
|
anchors.right: isStateEvent ? undefined : (bubbleOnRight ? parent.right : undefined)
|
||||||
color: (Settings.bubbles && !isStateEvent) ? Qt.tint(bgColor, Qt.hsla(userColor.hslHue, 0.5, userColor.hslLightness, 0.2)) : "#00000000"
|
color: (Settings.bubbles && !isStateEvent) ? Qt.tint(bgColor, Qt.hsla(userColor.hslHue, 0.5, userColor.hslLightness, 0.2)) : "#00000000"
|
||||||
|
height: msg.height + msg.anchors.margins * 2
|
||||||
radius: 4
|
radius: 4
|
||||||
|
width: Settings.bubbles ? Math.min(maxWidth, Math.max(reply.implicitWidth + 8, contentItem.implicitWidth + metadata.width + 20)) : maxWidth
|
||||||
|
|
||||||
GridLayout {
|
GridLayout {
|
||||||
|
id: msg
|
||||||
|
columnSpacing: 2
|
||||||
|
columns: Settings.bubbles ? 1 : 2
|
||||||
|
rowSpacing: 0
|
||||||
|
rows: Settings.bubbles ? 3 : 2
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
left: parent.left
|
left: parent.left
|
||||||
top: parent.top
|
|
||||||
right: parent.right
|
|
||||||
margins: (Settings.bubbles && ! isStateEvent)? 4 : 2
|
|
||||||
leftMargin: 4
|
leftMargin: 4
|
||||||
|
margins: (Settings.bubbles && !isStateEvent) ? 4 : 2
|
||||||
|
right: parent.right
|
||||||
|
top: parent.top
|
||||||
}
|
}
|
||||||
id: msg
|
|
||||||
rowSpacing: 0
|
|
||||||
columnSpacing: 2
|
|
||||||
columns: Settings.bubbles? 1 : 2
|
|
||||||
rows: Settings.bubbles? 3 : 2
|
|
||||||
|
|
||||||
// fancy reply, if this is a reply
|
// fancy reply, if this is a reply
|
||||||
Reply {
|
Reply {
|
||||||
Layout.row: 0
|
|
||||||
Layout.column: 0
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.maximumWidth: Settings.bubbles? Number.MAX_VALUE : implicitWidth
|
|
||||||
Layout.bottomMargin: visible? 2 : 0
|
|
||||||
Layout.preferredHeight: height
|
|
||||||
id: reply
|
id: reply
|
||||||
|
|
||||||
function fromModel(role) {
|
function fromModel(role) {
|
||||||
return replyTo != "" ? room.dataById(replyTo, role, r.eventId) : null;
|
return replyTo != "" ? room.dataById(replyTo, role, r.eventId) : null;
|
||||||
}
|
}
|
||||||
visible: replyTo
|
|
||||||
userColor: r.relatedEventCacheBuster, TimelineManager.userColor(userId, timelineRoot.palette.base)
|
Layout.bottomMargin: visible ? 2 : 0
|
||||||
|
Layout.column: 0
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.maximumWidth: Settings.bubbles ? Number.MAX_VALUE : implicitWidth
|
||||||
|
Layout.preferredHeight: height
|
||||||
|
Layout.row: 0
|
||||||
blurhash: r.relatedEventCacheBuster, fromModel(Room.Blurhash) ?? ""
|
blurhash: r.relatedEventCacheBuster, fromModel(Room.Blurhash) ?? ""
|
||||||
body: r.relatedEventCacheBuster, fromModel(Room.Body) ?? ""
|
body: r.relatedEventCacheBuster, fromModel(Room.Body) ?? ""
|
||||||
formattedBody: r.relatedEventCacheBuster, fromModel(Room.FormattedBody) ?? ""
|
callType: r.relatedEventCacheBuster, fromModel(Room.Voip) ?? ""
|
||||||
|
duration: r.relatedEventCacheBuster, fromModel(Room.Duration) ?? 0
|
||||||
|
encryptionError: r.relatedEventCacheBuster, fromModel(Room.EncryptionError) ?? 0
|
||||||
eventId: fromModel(Room.EventId) ?? ""
|
eventId: fromModel(Room.EventId) ?? ""
|
||||||
filename: r.relatedEventCacheBuster, fromModel(Room.Filename) ?? ""
|
filename: r.relatedEventCacheBuster, fromModel(Room.Filename) ?? ""
|
||||||
filesize: r.relatedEventCacheBuster, fromModel(Room.Filesize) ?? ""
|
filesize: r.relatedEventCacheBuster, fromModel(Room.Filesize) ?? ""
|
||||||
|
formattedBody: r.relatedEventCacheBuster, fromModel(Room.FormattedBody) ?? ""
|
||||||
|
isOnlyEmoji: r.relatedEventCacheBuster, fromModel(Room.IsOnlyEmoji) ?? false
|
||||||
|
isStateEvent: r.relatedEventCacheBuster, fromModel(Room.IsStateEvent) ?? false
|
||||||
|
originalWidth: r.relatedEventCacheBuster, fromModel(Room.OriginalWidth) ?? 0
|
||||||
proportionalHeight: r.relatedEventCacheBuster, fromModel(Room.ProportionalHeight) ?? 1
|
proportionalHeight: r.relatedEventCacheBuster, fromModel(Room.ProportionalHeight) ?? 1
|
||||||
|
relatedEventCacheBuster: r.relatedEventCacheBuster, fromModel(Room.RelatedEventCacheBuster) ?? 0
|
||||||
|
roomName: r.relatedEventCacheBuster, fromModel(Room.RoomName) ?? ""
|
||||||
|
roomTopic: r.relatedEventCacheBuster, fromModel(Room.RoomTopic) ?? ""
|
||||||
|
thumbnailUrl: r.relatedEventCacheBuster, fromModel(Room.ThumbnailUrl) ?? ""
|
||||||
type: r.relatedEventCacheBuster, fromModel(Room.Type) ?? MtxEvent.UnknownMessage
|
type: r.relatedEventCacheBuster, fromModel(Room.Type) ?? MtxEvent.UnknownMessage
|
||||||
typeString: r.relatedEventCacheBuster, fromModel(Room.TypeString) ?? ""
|
typeString: r.relatedEventCacheBuster, fromModel(Room.TypeString) ?? ""
|
||||||
url: r.relatedEventCacheBuster, fromModel(Room.Url) ?? ""
|
url: r.relatedEventCacheBuster, fromModel(Room.Url) ?? ""
|
||||||
originalWidth: r.relatedEventCacheBuster, fromModel(Room.OriginalWidth) ?? 0
|
userColor: r.relatedEventCacheBuster, TimelineManager.userColor(userId, timelineRoot.palette.base)
|
||||||
isOnlyEmoji: r.relatedEventCacheBuster, fromModel(Room.IsOnlyEmoji) ?? false
|
|
||||||
isStateEvent: r.relatedEventCacheBuster, fromModel(Room.IsStateEvent) ?? false
|
|
||||||
userId: r.relatedEventCacheBuster, fromModel(Room.UserId) ?? ""
|
userId: r.relatedEventCacheBuster, fromModel(Room.UserId) ?? ""
|
||||||
userName: r.relatedEventCacheBuster, fromModel(Room.UserName) ?? ""
|
userName: r.relatedEventCacheBuster, fromModel(Room.UserName) ?? ""
|
||||||
thumbnailUrl: r.relatedEventCacheBuster, fromModel(Room.ThumbnailUrl) ?? ""
|
visible: replyTo
|
||||||
duration: r.relatedEventCacheBuster, fromModel(Room.Duration) ?? 0
|
|
||||||
roomTopic: r.relatedEventCacheBuster, fromModel(Room.RoomTopic) ?? ""
|
|
||||||
roomName: r.relatedEventCacheBuster, fromModel(Room.RoomName) ?? ""
|
|
||||||
callType: r.relatedEventCacheBuster, fromModel(Room.Voip) ?? ""
|
|
||||||
encryptionError: r.relatedEventCacheBuster, fromModel(Room.EncryptionError) ?? 0
|
|
||||||
relatedEventCacheBuster: r.relatedEventCacheBuster, fromModel(Room.RelatedEventCacheBuster) ?? 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// actual message content
|
// actual message content
|
||||||
MessageDelegate {
|
MessageDelegate {
|
||||||
Layout.row: 1
|
id: contentItem
|
||||||
Layout.column: 0
|
Layout.column: 0
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: height
|
Layout.preferredHeight: height
|
||||||
id: contentItem
|
Layout.row: 1
|
||||||
|
|
||||||
blurhash: r.blurhash
|
blurhash: r.blurhash
|
||||||
body: r.body
|
body: r.body
|
||||||
formattedBody: r.formattedBody
|
callType: r.callType
|
||||||
|
duration: r.duration
|
||||||
|
encryptionError: r.encryptionError
|
||||||
eventId: r.eventId
|
eventId: r.eventId
|
||||||
filename: r.filename
|
filename: r.filename
|
||||||
filesize: r.filesize
|
filesize: r.filesize
|
||||||
|
formattedBody: r.formattedBody
|
||||||
|
isOnlyEmoji: r.isOnlyEmoji
|
||||||
|
isReply: false
|
||||||
|
isStateEvent: r.isStateEvent
|
||||||
|
metadataWidth: metadata.width
|
||||||
|
originalWidth: r.originalWidth
|
||||||
proportionalHeight: r.proportionalHeight
|
proportionalHeight: r.proportionalHeight
|
||||||
|
relatedEventCacheBuster: r.relatedEventCacheBuster
|
||||||
|
roomName: r.roomName
|
||||||
|
roomTopic: r.roomTopic
|
||||||
|
thumbnailUrl: r.thumbnailUrl
|
||||||
type: r.type
|
type: r.type
|
||||||
typeString: r.typeString ?? ""
|
typeString: r.typeString ?? ""
|
||||||
url: r.url
|
url: r.url
|
||||||
thumbnailUrl: r.thumbnailUrl
|
|
||||||
duration: r.duration
|
|
||||||
originalWidth: r.originalWidth
|
|
||||||
isOnlyEmoji: r.isOnlyEmoji
|
|
||||||
isStateEvent: r.isStateEvent
|
|
||||||
userId: r.userId
|
userId: r.userId
|
||||||
userName: r.userName
|
userName: r.userName
|
||||||
roomTopic: r.roomTopic
|
|
||||||
roomName: r.roomName
|
|
||||||
callType: r.callType
|
|
||||||
encryptionError: r.encryptionError
|
|
||||||
relatedEventCacheBuster: r.relatedEventCacheBuster
|
|
||||||
isReply: false
|
|
||||||
metadataWidth: metadata.width
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: metadata
|
id: metadata
|
||||||
Layout.column: Settings.bubbles? 0 : 1
|
|
||||||
Layout.row: Settings.bubbles? 2 : 0
|
property int iconSize: Math.floor(fontMetrics.ascent * scaling)
|
||||||
Layout.rowSpan: Settings.bubbles? 1 : 2
|
property double scaling: Settings.bubbles ? 0.75 : 1
|
||||||
Layout.bottomMargin: -2
|
|
||||||
Layout.topMargin: (contentItem.fitsMetadata && Settings.bubbles)? -height-Layout.bottomMargin : 0
|
|
||||||
Layout.alignment: Qt.AlignTop | Qt.AlignRight
|
Layout.alignment: Qt.AlignTop | Qt.AlignRight
|
||||||
|
Layout.bottomMargin: -2
|
||||||
|
Layout.column: Settings.bubbles ? 0 : 1
|
||||||
Layout.preferredWidth: implicitWidth
|
Layout.preferredWidth: implicitWidth
|
||||||
visible: !isStateEvent
|
Layout.row: Settings.bubbles ? 2 : 0
|
||||||
|
Layout.rowSpan: Settings.bubbles ? 1 : 2
|
||||||
|
Layout.topMargin: (contentItem.fitsMetadata && Settings.bubbles) ? -height - Layout.bottomMargin : 0
|
||||||
spacing: 2
|
spacing: 2
|
||||||
|
visible: !isStateEvent
|
||||||
property double scaling: Settings.bubbles? 0.75 : 1
|
|
||||||
|
|
||||||
property int iconSize: Math.floor(fontMetrics.ascent*scaling)
|
|
||||||
|
|
||||||
StatusIndicator {
|
StatusIndicator {
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||||
height: parent.iconSize
|
|
||||||
width: parent.iconSize
|
|
||||||
status: r.status
|
|
||||||
eventId: r.eventId
|
|
||||||
anchors.verticalCenter: ts.verticalCenter
|
anchors.verticalCenter: ts.verticalCenter
|
||||||
}
|
eventId: r.eventId
|
||||||
|
|
||||||
Image {
|
|
||||||
visible: isEdited || eventId == chat.model.edit
|
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
|
||||||
height: parent.iconSize
|
height: parent.iconSize
|
||||||
|
status: r.status
|
||||||
width: parent.iconSize
|
width: parent.iconSize
|
||||||
sourceSize.width: parent.iconSize * Screen.devicePixelRatio
|
}
|
||||||
sourceSize.height: parent.iconSize * Screen.devicePixelRatio
|
Image {
|
||||||
source: "image://colorimage/:/icons/icons/ui/edit.svg?" + ((eventId == chat.model.edit) ? timelineRoot.palette.highlight : timelineRoot.palette.placeholderText)
|
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||||
ToolTip.visible: editHovered.hovered
|
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
ToolTip.text: qsTr("Edited")
|
ToolTip.text: qsTr("Edited")
|
||||||
|
ToolTip.visible: editHovered.hovered
|
||||||
anchors.verticalCenter: ts.verticalCenter
|
anchors.verticalCenter: ts.verticalCenter
|
||||||
|
height: parent.iconSize
|
||||||
|
source: "image://colorimage/:/icons/icons/ui/edit.svg?" + ((eventId == chat.model.edit) ? timelineRoot.palette.highlight : timelineRoot.palette.placeholderText)
|
||||||
|
sourceSize.height: parent.iconSize * Screen.devicePixelRatio
|
||||||
|
sourceSize.width: parent.iconSize * Screen.devicePixelRatio
|
||||||
|
visible: isEdited || eventId == chat.model.edit
|
||||||
|
width: parent.iconSize
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: editHovered
|
id: editHovered
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EncryptionIndicator {
|
EncryptionIndicator {
|
||||||
visible: room.isEncrypted
|
|
||||||
encrypted: isEncrypted
|
|
||||||
trust: trustlevel
|
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||||
height: parent.iconSize
|
|
||||||
width: parent.iconSize
|
|
||||||
sourceSize.width: parent.iconSize * Screen.devicePixelRatio
|
|
||||||
sourceSize.height: parent.iconSize * Screen.devicePixelRatio
|
|
||||||
anchors.verticalCenter: ts.verticalCenter
|
anchors.verticalCenter: ts.verticalCenter
|
||||||
|
encrypted: isEncrypted
|
||||||
|
height: parent.iconSize
|
||||||
|
sourceSize.height: parent.iconSize * Screen.devicePixelRatio
|
||||||
|
sourceSize.width: parent.iconSize * Screen.devicePixelRatio
|
||||||
|
trust: trustlevel
|
||||||
|
visible: room.isEncrypted
|
||||||
|
width: parent.iconSize
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: ts
|
id: ts
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||||
Layout.preferredWidth: implicitWidth
|
Layout.preferredWidth: implicitWidth
|
||||||
text: timestamp.toLocaleTimeString(Locale.ShortFormat)
|
|
||||||
color: timelineRoot.palette.inactive.text
|
|
||||||
ToolTip.visible: ma.hovered
|
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
ToolTip.text: Qt.formatDateTime(timestamp, Qt.DefaultLocaleLongDate)
|
ToolTip.text: Qt.formatDateTime(timestamp, Qt.DefaultLocaleLongDate)
|
||||||
font.pointSize: fontMetrics.font.pointSize*parent.scaling
|
ToolTip.visible: ma.hovered
|
||||||
|
color: timelineRoot.palette.inactive.text
|
||||||
|
font.pointSize: fontMetrics.font.pointSize * parent.scaling
|
||||||
|
text: timestamp.toLocaleTimeString(Locale.ShortFormat)
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: ma
|
id: ma
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reactions {
|
Reactions {
|
||||||
|
id: reactionRow
|
||||||
|
eventId: r.eventId
|
||||||
|
layoutDirection: row.bubbleOnRight ? Qt.RightToLeft : Qt.LeftToRight
|
||||||
|
reactions: r.reactions
|
||||||
|
width: row.maxWidth
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
|
left: row.bubbleOnRight ? undefined : row.left
|
||||||
|
right: row.bubbleOnRight ? row.right : undefined
|
||||||
top: row.bottom
|
top: row.bottom
|
||||||
topMargin: -2
|
topMargin: -2
|
||||||
left: row.bubbleOnRight? undefined : row.left
|
|
||||||
right: row.bubbleOnRight? row.right : undefined
|
|
||||||
}
|
}
|
||||||
width: row.maxWidth
|
|
||||||
layoutDirection: row.bubbleOnRight? Qt.RightToLeft : Qt.LeftToRight
|
|
||||||
|
|
||||||
id: reactionRow
|
|
||||||
|
|
||||||
reactions: r.reactions
|
|
||||||
eventId: r.eventId
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
import "components"
|
||||||
import "./components"
|
import "delegates"
|
||||||
import "./delegates"
|
import "device-verification"
|
||||||
import "./device-verification"
|
import "emoji"
|
||||||
import "./emoji"
|
import "ui"
|
||||||
import "./ui"
|
import "voip"
|
||||||
import "./voip"
|
|
||||||
import Qt.labs.platform 1.1 as Platform
|
import Qt.labs.platform 1.1 as Platform
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.5
|
import QtQuick.Controls 2.5
|
||||||
|
@ -23,55 +21,50 @@ Item {
|
||||||
property var room: null
|
property var room: null
|
||||||
property var roomPreview: null
|
property var roomPreview: null
|
||||||
property bool showBackButton: false
|
property bool showBackButton: false
|
||||||
|
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: StandardKey.Close
|
sequence: StandardKey.Close
|
||||||
|
|
||||||
onActivated: Rooms.resetCurrentRoom()
|
onActivated: Rooms.resetCurrentRoom()
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
visible: !room && !TimelineManager.isInitialSync && (!roomPreview || !roomPreview.roomid)
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: qsTr("No room open")
|
|
||||||
font.pointSize: 24
|
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
|
font.pointSize: 24
|
||||||
|
text: qsTr("No room open")
|
||||||
|
visible: !room && !TimelineManager.isInitialSync && (!roomPreview || !roomPreview.roomid)
|
||||||
}
|
}
|
||||||
|
|
||||||
Spinner {
|
Spinner {
|
||||||
visible: TimelineManager.isInitialSync
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
foreground: timelineRoot.palette.mid
|
foreground: timelineRoot.palette.mid
|
||||||
running: TimelineManager.isInitialSync
|
|
||||||
// height is somewhat arbitrary here... don't set width because width scales w/ height
|
// height is somewhat arbitrary here... don't set width because width scales w/ height
|
||||||
height: parent.height / 16
|
height: parent.height / 16
|
||||||
|
running: TimelineManager.isInitialSync
|
||||||
|
visible: TimelineManager.isInitialSync
|
||||||
z: 3
|
z: 3
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: timelineLayout
|
id: timelineLayout
|
||||||
|
|
||||||
visible: room != null && !room.isSpace
|
|
||||||
enabled: visible
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
enabled: visible
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
visible: room != null && !room.isSpace
|
||||||
|
|
||||||
TopBar {
|
TopBar {
|
||||||
showBackButton: timelineView.showBackButton
|
showBackButton: timelineView.showBackButton
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
color: Nheko.theme.separator
|
||||||
height: 1
|
height: 1
|
||||||
z: 3
|
z: 3
|
||||||
color: Nheko.theme.separator
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: msgView
|
id: msgView
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
color: timelineRoot.palette.base
|
color: timelineRoot.palette.base
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
|
@ -80,7 +73,6 @@ Item {
|
||||||
|
|
||||||
StackLayout {
|
StackLayout {
|
||||||
id: stackLayout
|
id: stackLayout
|
||||||
|
|
||||||
currentIndex: 0
|
currentIndex: 0
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
|
@ -90,129 +82,107 @@ Item {
|
||||||
|
|
||||||
target: timelineView
|
target: timelineView
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageView {
|
MessageView {
|
||||||
implicitHeight: msgView.height - typingIndicator.height
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
implicitHeight: msgView.height - typingIndicator.height
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
source: CallManager.isOnCall && CallManager.callType != Voip.VOICE ? "voip/VideoCall.qml" : ""
|
source: CallManager.isOnCall && CallManager.callType != Voip.VOICE ? "voip/VideoCall.qml" : ""
|
||||||
|
|
||||||
onLoaded: TimelineManager.setVideoCallItem()
|
onLoaded: TimelineManager.setVideoCallItem()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TypingIndicator {
|
TypingIndicator {
|
||||||
id: typingIndicator
|
id: typingIndicator
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CallInviteBar {
|
CallInviteBar {
|
||||||
id: callInviteBar
|
id: callInviteBar
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
z: 3
|
z: 3
|
||||||
}
|
}
|
||||||
|
|
||||||
ActiveCallBar {
|
ActiveCallBar {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
z: 3
|
z: 3
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
z: 3
|
|
||||||
height: 1
|
|
||||||
color: Nheko.theme.separator
|
color: Nheko.theme.separator
|
||||||
|
height: 1
|
||||||
|
z: 3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
UploadBox {
|
UploadBox {
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationWarning {
|
NotificationWarning {
|
||||||
}
|
}
|
||||||
|
|
||||||
ReplyPopup {
|
ReplyPopup {
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageInput {
|
MessageInput {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: preview
|
id: preview
|
||||||
|
|
||||||
|
property string avatarUrl: room ? room.roomAvatarUrl : (roomPreview ? roomPreview.roomAvatarUrl : "")
|
||||||
property string roomId: room ? room.roomId : (roomPreview ? roomPreview.roomid : "")
|
property string roomId: room ? room.roomId : (roomPreview ? roomPreview.roomid : "")
|
||||||
property string roomName: room ? room.roomName : (roomPreview ? roomPreview.roomName : "")
|
property string roomName: room ? room.roomName : (roomPreview ? roomPreview.roomName : "")
|
||||||
property string roomTopic: room ? room.roomTopic : (roomPreview ? roomPreview.roomTopic : "")
|
property string roomTopic: room ? room.roomTopic : (roomPreview ? roomPreview.roomTopic : "")
|
||||||
property string avatarUrl: room ? room.roomAvatarUrl : (roomPreview ? roomPreview.roomAvatarUrl : "")
|
|
||||||
|
|
||||||
visible: room != null && room.isSpace || roomPreview != null
|
|
||||||
enabled: visible
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Nheko.paddingLarge
|
anchors.margins: Nheko.paddingLarge
|
||||||
|
enabled: visible
|
||||||
spacing: Nheko.paddingLarge
|
spacing: Nheko.paddingLarge
|
||||||
|
visible: room != null && room.isSpace || roomPreview != null
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
url: parent.avatarUrl.replace("mxc://", "image://MxcImage/")
|
Layout.alignment: Qt.AlignHCenter
|
||||||
roomid: parent.roomId
|
|
||||||
displayName: parent.roomName
|
displayName: parent.roomName
|
||||||
height: 130
|
|
||||||
width: 130
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
enabled: false
|
enabled: false
|
||||||
|
height: 130
|
||||||
|
roomid: parent.roomId
|
||||||
|
url: parent.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
|
width: 130
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
text: preview.roomName == "" ? qsTr("No preview available") : preview.roomName
|
|
||||||
font.pixelSize: 24
|
font.pixelSize: 24
|
||||||
|
text: preview.roomName == "" ? qsTr("No preview available") : preview.roomName
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
|
ToolTip.text: qsTr("Settings")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
hoverEnabled: true
|
||||||
image: ":/icons/icons/ui/settings.svg"
|
image: ":/icons/icons/ui/settings.svg"
|
||||||
visible: !!room
|
visible: !!room
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Settings")
|
|
||||||
onClicked: TimelineManager.openRoomSettings(room.roomId)
|
onClicked: TimelineManager.openRoomSettings(room.roomId)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
visible: !!room
|
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
|
visible: !!room
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
text: qsTr("%1 member(s)").arg(room ? room.roomMemberCount : 0)
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
text: qsTr("%1 member(s)").arg(room ? room.roomMemberCount : 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
image: ":/icons/icons/ui/people.svg"
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("View members of %1").arg(room.roomName)
|
ToolTip.text: qsTr("View members of %1").arg(room.roomName)
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/people.svg"
|
||||||
|
|
||||||
onClicked: TimelineManager.openRoomMembers(room)
|
onClicked: TimelineManager.openRoomMembers(room)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollView {
|
ScrollView {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
@ -220,97 +190,88 @@ Item {
|
||||||
Layout.rightMargin: Nheko.paddingLarge
|
Layout.rightMargin: Nheko.paddingLarge
|
||||||
|
|
||||||
TextArea {
|
TextArea {
|
||||||
text: TimelineManager.escapeEmoji(preview.roomTopic)
|
|
||||||
wrapMode: TextEdit.WordWrap
|
|
||||||
textFormat: TextEdit.RichText
|
|
||||||
readOnly: true
|
|
||||||
background: null
|
background: null
|
||||||
selectByMouse: true
|
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
horizontalAlignment: TextEdit.AlignHCenter
|
horizontalAlignment: TextEdit.AlignHCenter
|
||||||
|
readOnly: true
|
||||||
|
selectByMouse: true
|
||||||
|
text: TimelineManager.escapeEmoji(preview.roomTopic)
|
||||||
|
textFormat: TextEdit.RichText
|
||||||
|
wrapMode: TextEdit.WordWrap
|
||||||
|
|
||||||
onLinkActivated: Nheko.openLink(link)
|
onLinkActivated: Nheko.openLink(link)
|
||||||
|
|
||||||
NhekoCursorShape {
|
NhekoCursorShape {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FlatButton {
|
FlatButton {
|
||||||
visible: roomPreview && !roomPreview.isInvite
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
text: qsTr("join the conversation")
|
text: qsTr("join the conversation")
|
||||||
|
visible: roomPreview && !roomPreview.isInvite
|
||||||
|
|
||||||
onClicked: Rooms.joinPreview(roomPreview.roomid)
|
onClicked: Rooms.joinPreview(roomPreview.roomid)
|
||||||
}
|
}
|
||||||
|
|
||||||
FlatButton {
|
FlatButton {
|
||||||
visible: roomPreview && roomPreview.isInvite
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
text: qsTr("accept invite")
|
text: qsTr("accept invite")
|
||||||
|
visible: roomPreview && roomPreview.isInvite
|
||||||
|
|
||||||
onClicked: Rooms.acceptInvite(roomPreview.roomid)
|
onClicked: Rooms.acceptInvite(roomPreview.roomid)
|
||||||
}
|
}
|
||||||
|
|
||||||
FlatButton {
|
FlatButton {
|
||||||
visible: roomPreview && roomPreview.isInvite
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
text: qsTr("decline invite")
|
text: qsTr("decline invite")
|
||||||
|
visible: roomPreview && roomPreview.isInvite
|
||||||
|
|
||||||
onClicked: Rooms.declineInvite(roomPreview.roomid)
|
onClicked: Rooms.declineInvite(roomPreview.roomid)
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
visible: room != null
|
|
||||||
Layout.preferredHeight: Math.ceil(fontMetrics.lineSpacing * 2)
|
Layout.preferredHeight: Math.ceil(fontMetrics.lineSpacing * 2)
|
||||||
|
visible: room != null
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: backToRoomsButton
|
id: backToRoomsButton
|
||||||
|
ToolTip.text: qsTr("Back to room list")
|
||||||
anchors.top: parent.top
|
ToolTip.visible: hovered
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.margins: Nheko.paddingMedium
|
anchors.margins: Nheko.paddingMedium
|
||||||
width: Nheko.avatarSize
|
anchors.top: parent.top
|
||||||
height: Nheko.avatarSize
|
|
||||||
visible: (room == null || room.isSpace) && showBackButton
|
|
||||||
enabled: visible
|
enabled: visible
|
||||||
|
height: Nheko.avatarSize
|
||||||
image: ":/icons/icons/ui/angle-arrow-left.svg"
|
image: ":/icons/icons/ui/angle-arrow-left.svg"
|
||||||
ToolTip.visible: hovered
|
visible: (room == null || room.isSpace) && showBackButton
|
||||||
ToolTip.text: qsTr("Back to room list")
|
width: Nheko.avatarSize
|
||||||
|
|
||||||
onClicked: Rooms.resetCurrentRoom()
|
onClicked: Rooms.resetCurrentRoom()
|
||||||
}
|
}
|
||||||
|
|
||||||
NhekoDropArea {
|
NhekoDropArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
roomid: room ? room.roomId : ""
|
roomid: room ? room.roomId : ""
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onOpenReadReceiptsDialog(rr) {
|
function onOpenReadReceiptsDialog(rr) {
|
||||||
var dialog = readReceiptsDialog.createObject(timelineRoot, {
|
var dialog = readReceiptsDialog.createObject(timelineRoot, {
|
||||||
"readReceipts": rr,
|
"readReceipts": rr,
|
||||||
"room": room
|
"room": room
|
||||||
});
|
});
|
||||||
dialog.show();
|
dialog.show();
|
||||||
timelineRoot.destroyOnClose(dialog);
|
timelineRoot.destroyOnClose(dialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onShowRawMessageDialog(rawMessage) {
|
function onShowRawMessageDialog(rawMessage) {
|
||||||
var dialog = rawMessageDialog.createObject(timelineRoot, {
|
var dialog = rawMessageDialog.createObject(timelineRoot, {
|
||||||
"rawMessage": rawMessage
|
"rawMessage": rawMessage
|
||||||
});
|
});
|
||||||
dialog.show();
|
dialog.show();
|
||||||
timelineRoot.destroyOnClose(dialog);
|
timelineRoot.destroyOnClose(dialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
target: room
|
target: room
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.5
|
import QtQuick 2.5
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.12
|
import QtQuick.Controls 2.12
|
||||||
|
@ -10,36 +8,31 @@ import im.nheko
|
||||||
|
|
||||||
Switch {
|
Switch {
|
||||||
id: toggleButton
|
id: toggleButton
|
||||||
|
|
||||||
implicitWidth: indicatorItem.width
|
implicitWidth: indicatorItem.width
|
||||||
|
|
||||||
indicator: Item {
|
indicator: Item {
|
||||||
id: indicatorItem
|
id: indicatorItem
|
||||||
|
|
||||||
implicitWidth: 48
|
|
||||||
implicitHeight: 24
|
implicitHeight: 24
|
||||||
|
implicitWidth: 48
|
||||||
y: parent.height / 2 - height / 2
|
y: parent.height / 2 - height / 2
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
border.color: "#cccccc"
|
||||||
|
color: toggleButton.checked ? "skyblue" : "grey"
|
||||||
height: 3 * parent.height / 4
|
height: 3 * parent.height / 4
|
||||||
radius: height / 2
|
radius: height / 2
|
||||||
width: parent.width - height
|
width: parent.width - height
|
||||||
x: radius
|
x: radius
|
||||||
y: parent.height / 2 - height / 2
|
y: parent.height / 2 - height / 2
|
||||||
color: toggleButton.checked ? "skyblue" : "grey"
|
|
||||||
border.color: "#cccccc"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
x: toggleButton.checked ? parent.width - width : 0
|
border.color: "#ebebeb"
|
||||||
y: parent.height / 2 - height / 2
|
color: toggleButton.enabled ? "whitesmoke" : "#cccccc"
|
||||||
width: parent.height
|
|
||||||
height: width
|
height: width
|
||||||
radius: width / 2
|
radius: width / 2
|
||||||
color: toggleButton.enabled ? "whitesmoke" : "#cccccc"
|
width: parent.height
|
||||||
border.color: "#ebebeb"
|
x: toggleButton.checked ? parent.width - width : 0
|
||||||
|
y: parent.height / 2 - height / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
278
qml/TopBar.qml
278
qml/TopBar.qml
|
@ -1,190 +1,144 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import Qt.labs.platform 1.1 as Platform
|
import Qt.labs.platform 1.1 as Platform
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
import QtQuick.Window 2.15
|
import QtQuick.Window 2.15
|
||||||
import im.nheko
|
import im.nheko
|
||||||
|
import "delegates"
|
||||||
import "./delegates"
|
|
||||||
|
|
||||||
Pane {
|
Pane {
|
||||||
id: topBar
|
id: topBar
|
||||||
|
|
||||||
property bool showBackButton: false
|
|
||||||
property string roomName: room ? room.roomName : qsTr("No room selected")
|
|
||||||
property string roomId: room ? room.roomId : ""
|
|
||||||
property string avatarUrl: room ? room.roomAvatarUrl : ""
|
property string avatarUrl: room ? room.roomAvatarUrl : ""
|
||||||
property string roomTopic: room ? room.roomTopic : ""
|
|
||||||
property bool isEncrypted: room ? room.isEncrypted : false
|
|
||||||
property int trustlevel: room ? room.trustlevel : Crypto.Unverified
|
|
||||||
property bool isDirect: room ? room.isDirect : false
|
|
||||||
property string directChatOtherUserId: room ? room.directChatOtherUserId : ""
|
property string directChatOtherUserId: room ? room.directChatOtherUserId : ""
|
||||||
|
property bool isDirect: room ? room.isDirect : false
|
||||||
|
property bool isEncrypted: room ? room.isEncrypted : false
|
||||||
|
property string roomId: room ? room.roomId : ""
|
||||||
|
property string roomName: room ? room.roomName : qsTr("No room selected")
|
||||||
|
property string roomTopic: room ? room.roomTopic : ""
|
||||||
|
property bool showBackButton: false
|
||||||
|
property int trustlevel: room ? room.trustlevel : Crypto.Unverified
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
implicitHeight: topBarC.height + Nheko.paddingMedium * 2
|
implicitHeight: topBarC.height + Nheko.paddingMedium * 2
|
||||||
|
padding: 0
|
||||||
z: 3
|
z: 3
|
||||||
|
|
||||||
padding: 0
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: timelineRoot.palette.window
|
color: timelineRoot.palette.window
|
||||||
}
|
}
|
||||||
|
|
||||||
TapHandler {
|
|
||||||
onSingleTapped: {
|
|
||||||
if (eventPoint.position.y > topBar.height - (pinnedMessages.visible ? pinnedMessages.height : 0) - (widgets.visible ? widgets.height : 0)) {
|
|
||||||
eventPoint.accepted = true
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (showBackButton && eventPoint.position.x < Nheko.paddingMedium + backToRoomsButton.width) {
|
|
||||||
eventPoint.accepted = true
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (eventPoint.position.x > topBar.width - Nheko.paddingMedium - roomOptionsButton.width) {
|
|
||||||
eventPoint.accepted = true
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (room) {
|
|
||||||
let p = topBar.mapToItem(roomTopicC, eventPoint.position.x, eventPoint.position.y);
|
|
||||||
let link = roomTopicC.linkAt(p.x, p.y);
|
|
||||||
|
|
||||||
if (link) {
|
|
||||||
Nheko.openLink(link);
|
|
||||||
} else {
|
|
||||||
TimelineManager.openRoomSettings(room.roomId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
eventPoint.accepted = true;
|
|
||||||
}
|
|
||||||
gesturePolicy: TapHandler.ReleaseWithinBounds
|
|
||||||
}
|
|
||||||
|
|
||||||
HoverHandler {
|
|
||||||
grabPermissions: PointerHandler.TakeOverForbidden | PointerHandler.CanTakeOverFromAnything
|
|
||||||
}
|
|
||||||
|
|
||||||
contentItem: Item {
|
contentItem: Item {
|
||||||
GridLayout {
|
GridLayout {
|
||||||
id: topBarC
|
id: topBarC
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.margins: Nheko.paddingMedium
|
anchors.margins: Nheko.paddingMedium
|
||||||
|
anchors.right: parent.right
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
columnSpacing: Nheko.paddingSmall
|
columnSpacing: Nheko.paddingSmall
|
||||||
rowSpacing: Nheko.paddingSmall
|
rowSpacing: Nheko.paddingSmall
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: backToRoomsButton
|
id: backToRoomsButton
|
||||||
|
|
||||||
Layout.column: 0
|
|
||||||
Layout.row: 0
|
|
||||||
Layout.rowSpan: 2
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.column: 0
|
||||||
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
|
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
|
||||||
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
|
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
|
||||||
visible: showBackButton
|
Layout.row: 0
|
||||||
image: ":/icons/icons/ui/angle-arrow-left.svg"
|
Layout.rowSpan: 2
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Back to room list")
|
ToolTip.text: qsTr("Back to room list")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
image: ":/icons/icons/ui/angle-arrow-left.svg"
|
||||||
|
visible: showBackButton
|
||||||
|
|
||||||
onClicked: Rooms.resetCurrentRoom()
|
onClicked: Rooms.resetCurrentRoom()
|
||||||
}
|
}
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
Layout.column: 1
|
Layout.column: 1
|
||||||
Layout.row: 0
|
Layout.row: 0
|
||||||
Layout.rowSpan: 2
|
Layout.rowSpan: 2
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
width: Nheko.avatarSize
|
|
||||||
height: Nheko.avatarSize
|
|
||||||
url: avatarUrl.replace("mxc://", "image://MxcImage/")
|
|
||||||
roomid: roomId
|
|
||||||
userid: isDirect ? directChatOtherUserId : ""
|
|
||||||
displayName: roomName
|
displayName: roomName
|
||||||
enabled: false
|
enabled: false
|
||||||
|
height: Nheko.avatarSize
|
||||||
|
roomid: roomId
|
||||||
|
url: avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
|
userid: isDirect ? directChatOtherUserId : ""
|
||||||
|
width: Nheko.avatarSize
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.column: 2
|
Layout.column: 2
|
||||||
|
Layout.fillWidth: true
|
||||||
Layout.row: 0
|
Layout.row: 0
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
font.pointSize: fontMetrics.font.pointSize * 1.1
|
|
||||||
text: roomName
|
|
||||||
maximumLineCount: 1
|
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
|
font.pointSize: fontMetrics.font.pointSize * 1.1
|
||||||
|
maximumLineCount: 1
|
||||||
|
text: roomName
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
id: roomTopicC
|
id: roomTopicC
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.column: 2
|
Layout.column: 2
|
||||||
Layout.row: 1
|
Layout.fillWidth: true
|
||||||
Layout.maximumHeight: fontMetrics.lineSpacing * 2 // show 2 lines
|
Layout.maximumHeight: fontMetrics.lineSpacing * 2 // show 2 lines
|
||||||
selectByMouse: false
|
Layout.row: 1
|
||||||
enabled: false
|
|
||||||
clip: true
|
clip: true
|
||||||
|
enabled: false
|
||||||
|
selectByMouse: false
|
||||||
text: roomTopic
|
text: roomTopic
|
||||||
}
|
}
|
||||||
|
|
||||||
AbstractButton {
|
AbstractButton {
|
||||||
Layout.column: 3
|
Layout.column: 3
|
||||||
Layout.row: 0
|
|
||||||
Layout.rowSpan: 2
|
|
||||||
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
|
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
|
||||||
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
|
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
|
||||||
|
Layout.row: 0
|
||||||
contentItem: EncryptionIndicator {
|
Layout.rowSpan: 2
|
||||||
sourceSize.height: parent.Layout.preferredHeight * Screen.devicePixelRatio
|
|
||||||
sourceSize.width: parent.Layout.preferredWidth * Screen.devicePixelRatio
|
|
||||||
visible: isEncrypted
|
|
||||||
encrypted: isEncrypted
|
|
||||||
trust: trustlevel
|
|
||||||
enabled: false
|
|
||||||
}
|
|
||||||
|
|
||||||
background: null
|
|
||||||
|
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
ToolTip.text: {
|
ToolTip.text: {
|
||||||
if (!isEncrypted)
|
if (!isEncrypted)
|
||||||
return qsTr("This room is not encrypted!");
|
return qsTr("This room is not encrypted!");
|
||||||
|
|
||||||
switch (trustlevel) {
|
switch (trustlevel) {
|
||||||
case Crypto.Verified:
|
case Crypto.Verified:
|
||||||
return qsTr("This room contains only verified devices.");
|
return qsTr("This room contains only verified devices.");
|
||||||
case Crypto.TOFU:
|
case Crypto.TOFU:
|
||||||
return qsTr("This room contains verified devices and devices which have never changed their master key.");
|
return qsTr("This room contains verified devices and devices which have never changed their master key.");
|
||||||
default:
|
default:
|
||||||
return qsTr("This room contains unverified devices!");
|
return qsTr("This room contains unverified devices!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
|
background: null
|
||||||
|
|
||||||
|
contentItem: EncryptionIndicator {
|
||||||
|
enabled: false
|
||||||
|
encrypted: isEncrypted
|
||||||
|
sourceSize.height: parent.Layout.preferredHeight * Screen.devicePixelRatio
|
||||||
|
sourceSize.width: parent.Layout.preferredWidth * Screen.devicePixelRatio
|
||||||
|
trust: trustlevel
|
||||||
|
visible: isEncrypted
|
||||||
|
}
|
||||||
|
|
||||||
onClicked: TimelineManager.openRoomMembers(room)
|
onClicked: TimelineManager.openRoomMembers(room)
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: pinButton
|
id: pinButton
|
||||||
|
|
||||||
property bool pinsShown: !Settings.hiddenPins.includes(roomId)
|
property bool pinsShown: !Settings.hiddenPins.includes(roomId)
|
||||||
|
|
||||||
visible: !!room && room.pinnedMessages.length > 0
|
|
||||||
Layout.column: 4
|
|
||||||
Layout.row: 0
|
|
||||||
Layout.rowSpan: 2
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.column: 4
|
||||||
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
|
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
|
||||||
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
|
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
|
||||||
image: pinsShown ? ":/icons/icons/ui/pin.svg" : ":/icons/icons/ui/pin-off.svg"
|
Layout.row: 0
|
||||||
ToolTip.visible: hovered
|
Layout.rowSpan: 2
|
||||||
ToolTip.text: qsTr("Show or hide pinned messages")
|
ToolTip.text: qsTr("Show or hide pinned messages")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
image: pinsShown ? ":/icons/icons/ui/pin.svg" : ":/icons/icons/ui/pin-off.svg"
|
||||||
|
visible: !!room && room.pinnedMessages.length > 0
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
var ps = Settings.hiddenPins;
|
var ps = Settings.hiddenPins;
|
||||||
if (pinsShown) {
|
if (pinsShown) {
|
||||||
|
@ -197,156 +151,168 @@ Pane {
|
||||||
}
|
}
|
||||||
Settings.hiddenPins = ps;
|
Settings.hiddenPins = ps;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: roomOptionsButton
|
id: roomOptionsButton
|
||||||
|
|
||||||
visible: !!room
|
|
||||||
Layout.column: 5
|
|
||||||
Layout.row: 0
|
|
||||||
Layout.rowSpan: 2
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.column: 5
|
||||||
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
|
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
|
||||||
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
|
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
|
||||||
image: ":/icons/icons/ui/options.svg"
|
Layout.row: 0
|
||||||
ToolTip.visible: hovered
|
Layout.rowSpan: 2
|
||||||
ToolTip.text: qsTr("Room options")
|
ToolTip.text: qsTr("Room options")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
image: ":/icons/icons/ui/options.svg"
|
||||||
|
visible: !!room
|
||||||
|
|
||||||
onClicked: roomOptionsMenu.open(roomOptionsButton)
|
onClicked: roomOptionsMenu.open(roomOptionsButton)
|
||||||
|
|
||||||
Platform.Menu {
|
Platform.Menu {
|
||||||
id: roomOptionsMenu
|
id: roomOptionsMenu
|
||||||
|
|
||||||
Platform.MenuItem {
|
Platform.MenuItem {
|
||||||
visible: room ? room.permissions.canInvite() : false
|
|
||||||
text: qsTr("Invite users")
|
text: qsTr("Invite users")
|
||||||
|
visible: room ? room.permissions.canInvite() : false
|
||||||
|
|
||||||
onTriggered: TimelineManager.openInviteUsers(roomId)
|
onTriggered: TimelineManager.openInviteUsers(roomId)
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform.MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Members")
|
text: qsTr("Members")
|
||||||
|
|
||||||
onTriggered: TimelineManager.openRoomMembers(room)
|
onTriggered: TimelineManager.openRoomMembers(room)
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform.MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Leave room")
|
text: qsTr("Leave room")
|
||||||
|
|
||||||
onTriggered: TimelineManager.openLeaveRoomDialog(roomId)
|
onTriggered: TimelineManager.openLeaveRoomDialog(roomId)
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform.MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Settings")
|
text: qsTr("Settings")
|
||||||
|
|
||||||
onTriggered: TimelineManager.openRoomSettings(roomId)
|
onTriggered: TimelineManager.openRoomSettings(roomId)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollView {
|
ScrollView {
|
||||||
id: pinnedMessages
|
id: pinnedMessages
|
||||||
|
|
||||||
Layout.row: 2
|
|
||||||
Layout.column: 2
|
Layout.column: 2
|
||||||
Layout.columnSpan: 3
|
Layout.columnSpan: 3
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: Math.min(contentHeight, Nheko.avatarSize * 4)
|
Layout.preferredHeight: Math.min(contentHeight, Nheko.avatarSize * 4)
|
||||||
|
Layout.row: 2
|
||||||
visible: !!room && room.pinnedMessages.length > 0 && !Settings.hiddenPins.includes(roomId)
|
|
||||||
clip: true
|
|
||||||
|
|
||||||
palette: timelineRoot.palette
|
|
||||||
ScrollBar.horizontal.visible: false
|
ScrollBar.horizontal.visible: false
|
||||||
|
clip: true
|
||||||
|
palette: timelineRoot.palette
|
||||||
|
visible: !!room && room.pinnedMessages.length > 0 && !Settings.hiddenPins.includes(roomId)
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
|
|
||||||
spacing: Nheko.paddingSmall
|
|
||||||
model: room ? room.pinnedMessages : undefined
|
model: room ? room.pinnedMessages : undefined
|
||||||
|
spacing: Nheko.paddingSmall
|
||||||
|
|
||||||
delegate: RowLayout {
|
delegate: RowLayout {
|
||||||
required property string modelData
|
required property string modelData
|
||||||
|
|
||||||
width: ListView.view.width
|
|
||||||
height: implicitHeight
|
height: implicitHeight
|
||||||
|
width: ListView.view.width
|
||||||
|
|
||||||
Reply {
|
Reply {
|
||||||
property var e: room ? room.getDump(modelData, "") : {}
|
property var e: room ? room.getDump(modelData, "") : {}
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: height
|
Layout.preferredHeight: height
|
||||||
|
|
||||||
userColor: TimelineManager.userColor(e.userId, timelineRoot.palette.window)
|
|
||||||
blurhash: e.blurhash ?? ""
|
blurhash: e.blurhash ?? ""
|
||||||
body: e.body ?? ""
|
body: e.body ?? ""
|
||||||
formattedBody: e.formattedBody ?? ""
|
encryptionError: e.encryptionError ?? ""
|
||||||
eventId: e.eventId ?? ""
|
eventId: e.eventId ?? ""
|
||||||
filename: e.filename ?? ""
|
filename: e.filename ?? ""
|
||||||
filesize: e.filesize ?? ""
|
filesize: e.filesize ?? ""
|
||||||
|
formattedBody: e.formattedBody ?? ""
|
||||||
|
isOnlyEmoji: e.isOnlyEmoji ?? false
|
||||||
|
originalWidth: e.originalWidth ?? 0
|
||||||
proportionalHeight: e.proportionalHeight ?? 1
|
proportionalHeight: e.proportionalHeight ?? 1
|
||||||
type: e.type ?? MtxEvent.UnknownMessage
|
type: e.type ?? MtxEvent.UnknownMessage
|
||||||
typeString: e.typeString ?? ""
|
typeString: e.typeString ?? ""
|
||||||
url: e.url ?? ""
|
url: e.url ?? ""
|
||||||
originalWidth: e.originalWidth ?? 0
|
userColor: TimelineManager.userColor(e.userId, timelineRoot.palette.window)
|
||||||
isOnlyEmoji: e.isOnlyEmoji ?? false
|
|
||||||
userId: e.userId ?? ""
|
userId: e.userId ?? ""
|
||||||
userName: e.userName ?? ""
|
userName: e.userName ?? ""
|
||||||
encryptionError: e.encryptionError ?? ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: deletePinButton
|
id: deletePinButton
|
||||||
|
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
||||||
Layout.preferredHeight: 16
|
Layout.preferredHeight: 16
|
||||||
Layout.preferredWidth: 16
|
Layout.preferredWidth: 16
|
||||||
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
ToolTip.text: qsTr("Unpin")
|
||||||
visible: room.permissions.canChange(MtxEvent.PinnedEvents)
|
ToolTip.visible: hovered
|
||||||
|
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
image: ":/icons/icons/ui/dismiss.svg"
|
image: ":/icons/icons/ui/dismiss.svg"
|
||||||
ToolTip.visible: hovered
|
visible: room.permissions.canChange(MtxEvent.PinnedEvents)
|
||||||
ToolTip.text: qsTr("Unpin")
|
|
||||||
|
|
||||||
onClicked: room.unpin(modelData)
|
onClicked: room.unpin(modelData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollView {
|
ScrollView {
|
||||||
id: widgets
|
id: widgets
|
||||||
|
|
||||||
Layout.row: 3
|
|
||||||
Layout.column: 2
|
Layout.column: 2
|
||||||
Layout.columnSpan: 3
|
Layout.columnSpan: 3
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: Math.min(contentHeight, Nheko.avatarSize * 1.5)
|
Layout.preferredHeight: Math.min(contentHeight, Nheko.avatarSize * 1.5)
|
||||||
|
Layout.row: 3
|
||||||
visible: !!room && room.widgetLinks.length > 0 && !Settings.hiddenWidgets.includes(roomId)
|
|
||||||
clip: true
|
|
||||||
|
|
||||||
palette: timelineRoot.palette
|
|
||||||
ScrollBar.horizontal.visible: false
|
ScrollBar.horizontal.visible: false
|
||||||
|
clip: true
|
||||||
|
palette: timelineRoot.palette
|
||||||
|
visible: !!room && room.widgetLinks.length > 0 && !Settings.hiddenWidgets.includes(roomId)
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
|
|
||||||
spacing: Nheko.paddingSmall
|
|
||||||
model: room ? room.widgetLinks : undefined
|
model: room ? room.widgetLinks : undefined
|
||||||
|
spacing: Nheko.paddingSmall
|
||||||
|
|
||||||
delegate: MatrixText {
|
delegate: MatrixText {
|
||||||
required property var modelData
|
required property var modelData
|
||||||
|
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
text: modelData
|
text: modelData
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NhekoCursorShape {
|
NhekoCursorShape {
|
||||||
anchors.fill: parent
|
|
||||||
anchors.bottomMargin: (pinnedMessages.visible ? pinnedMessages.height : 0) + (widgets.visible ? widgets.height : 0)
|
anchors.bottomMargin: (pinnedMessages.visible ? pinnedMessages.height : 0) + (widgets.visible ? widgets.height : 0)
|
||||||
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TapHandler {
|
||||||
|
gesturePolicy: TapHandler.ReleaseWithinBounds
|
||||||
|
|
||||||
|
onSingleTapped: {
|
||||||
|
if (eventPoint.position.y > topBar.height - (pinnedMessages.visible ? pinnedMessages.height : 0) - (widgets.visible ? widgets.height : 0)) {
|
||||||
|
eventPoint.accepted = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (showBackButton && eventPoint.position.x < Nheko.paddingMedium + backToRoomsButton.width) {
|
||||||
|
eventPoint.accepted = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (eventPoint.position.x > topBar.width - Nheko.paddingMedium - roomOptionsButton.width) {
|
||||||
|
eventPoint.accepted = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (room) {
|
||||||
|
let p = topBar.mapToItem(roomTopicC, eventPoint.position.x, eventPoint.position.y);
|
||||||
|
let link = roomTopicC.linkAt(p.x, p.y);
|
||||||
|
if (link) {
|
||||||
|
Nheko.openLink(link);
|
||||||
|
} else {
|
||||||
|
TimelineManager.openRoomSettings(room.roomId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
eventPoint.accepted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HoverHandler {
|
||||||
|
grabPermissions: PointerHandler.TakeOverForbidden | PointerHandler.CanTakeOverFromAnything
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,38 +1,32 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
import im.nheko
|
import im.nheko
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
implicitHeight: Math.max(fontMetrics.height * 1.2, typingDisplay.height)
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
implicitHeight: Math.max(fontMetrics.height * 1.2, typingDisplay.height)
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: typingRect
|
id: typingRect
|
||||||
|
|
||||||
visible: (room && room.typingUsers.length > 0)
|
|
||||||
color: timelineRoot.palette.base
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
color: timelineRoot.palette.base
|
||||||
|
visible: (room && room.typingUsers.length > 0)
|
||||||
z: 3
|
z: 3
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: typingDisplay
|
id: typingDisplay
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: 10
|
anchors.leftMargin: 10
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.rightMargin: 10
|
anchors.rightMargin: 10
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
text: room ? room.formatTypingUsers(room.typingUsers, timelineRoot.palette.base) : ""
|
text: room ? room.formatTypingUsers(room.typingUsers, timelineRoot.palette.base) : ""
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
import "components"
|
||||||
import "./components"
|
import "ui"
|
||||||
import "./ui"
|
|
||||||
|
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.5
|
import QtQuick.Controls 2.5
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts 1.3
|
||||||
|
@ -12,31 +9,30 @@ import im.nheko
|
||||||
|
|
||||||
Page {
|
Page {
|
||||||
id: uploadPopup
|
id: uploadPopup
|
||||||
visible: room && room.input.uploads.length > 0
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: 200
|
Layout.preferredHeight: 200
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
padding: Nheko.paddingMedium
|
padding: Nheko.paddingMedium
|
||||||
|
visible: room && room.input.uploads.length > 0
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: timelineRoot.palette.base
|
||||||
|
}
|
||||||
contentItem: ListView {
|
contentItem: ListView {
|
||||||
id: uploadsList
|
id: uploadsList
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
model: room ? room.input.uploads : undefined
|
||||||
|
orientation: ListView.Horizontal
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
|
width: Math.min(contentWidth, parent.availableWidth)
|
||||||
|
|
||||||
ScrollBar.horizontal: ScrollBar {
|
ScrollBar.horizontal: ScrollBar {
|
||||||
id: scr
|
id: scr
|
||||||
}
|
}
|
||||||
|
|
||||||
orientation: ListView.Horizontal
|
|
||||||
width: Math.min(contentWidth, parent.availableWidth)
|
|
||||||
model: room ? room.input.uploads : undefined
|
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
|
|
||||||
delegate: Pane {
|
delegate: Pane {
|
||||||
|
height: uploadPopup.availableHeight - buttons.height - (scr.visible ? scr.height : 0)
|
||||||
padding: Nheko.paddingSmall
|
padding: Nheko.paddingSmall
|
||||||
height: uploadPopup.availableHeight - buttons.height - (scr.visible? scr.height : 0)
|
|
||||||
width: uploadPopup.availableHeight - buttons.height
|
width: uploadPopup.availableHeight - buttons.height
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
|
@ -45,45 +41,45 @@ Page {
|
||||||
}
|
}
|
||||||
contentItem: ColumnLayout {
|
contentItem: ColumnLayout {
|
||||||
Image {
|
Image {
|
||||||
|
property string typeStr: switch (modelData.mediaType) {
|
||||||
|
case MediaUpload.Video:
|
||||||
|
return "video-file";
|
||||||
|
case MediaUpload.Audio:
|
||||||
|
return "music";
|
||||||
|
case MediaUpload.Image:
|
||||||
|
return "image";
|
||||||
|
default:
|
||||||
|
return "zip";
|
||||||
|
}
|
||||||
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
mipmap: true
|
||||||
|
smooth: true
|
||||||
|
source: (modelData.thumbnail != "") ? modelData.thumbnail : ("image://colorimage/:/icons/icons/ui/" + typeStr + ".svg?" + timelineRoot.palette.placeholderText)
|
||||||
sourceSize.height: height
|
sourceSize.height: height
|
||||||
sourceSize.width: width
|
sourceSize.width: width
|
||||||
fillMode: Image.PreserveAspectFit
|
|
||||||
smooth: true
|
|
||||||
mipmap: true
|
|
||||||
|
|
||||||
property string typeStr: switch(modelData.mediaType) {
|
|
||||||
case MediaUpload.Video: return "video-file";
|
|
||||||
case MediaUpload.Audio: return "music";
|
|
||||||
case MediaUpload.Image: return "image";
|
|
||||||
default: return "zip";
|
|
||||||
}
|
|
||||||
source: (modelData.thumbnail != "") ? modelData.thumbnail : ("image://colorimage/:/icons/icons/ui/"+typeStr+".svg?" + timelineRoot.palette.placeholderText)
|
|
||||||
}
|
}
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: modelData.filename
|
text: modelData.filename
|
||||||
|
|
||||||
onTextEdited: modelData.filename = text
|
onTextEdited: modelData.filename = text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
footer: DialogButtonBox {
|
footer: DialogButtonBox {
|
||||||
id: buttons
|
id: buttons
|
||||||
|
|
||||||
standardButtons: DialogButtonBox.Cancel
|
standardButtons: DialogButtonBox.Cancel
|
||||||
Button {
|
|
||||||
text: qsTr("Upload %n file(s)", "", (room ? room.input.uploads.length : 0))
|
|
||||||
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
|
||||||
}
|
|
||||||
onAccepted: room.input.acceptUploads()
|
onAccepted: room.input.acceptUploads()
|
||||||
onRejected: room.input.declineUploads()
|
onRejected: room.input.declineUploads()
|
||||||
}
|
|
||||||
|
|
||||||
background: Rectangle {
|
Button {
|
||||||
color: timelineRoot.palette.base
|
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
||||||
|
text: qsTr("Upload %n file(s)", "", (room ? room.input.uploads.length : 0))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.5
|
import QtQuick.Controls 2.5
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
|
@ -12,67 +10,18 @@ Container {
|
||||||
//Component.onCompleted: {
|
//Component.onCompleted: {
|
||||||
// parent.width = Qt.binding(function() { return calculatedWidth; })
|
// parent.width = Qt.binding(function() { return calculatedWidth; })
|
||||||
//}
|
//}
|
||||||
|
|
||||||
id: container
|
id: container
|
||||||
|
|
||||||
property bool singlePageMode: width < 800
|
property Component handle: Rectangle {
|
||||||
property int splitterGrabMargin: Nheko.paddingSmall
|
anchors.right: parent.right
|
||||||
property alias pageIndex: view.currentIndex
|
|
||||||
property Component handle
|
|
||||||
property Component handleToucharea
|
|
||||||
|
|
||||||
onSinglePageModeChanged: if (!singlePageMode) pageIndex = 0
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
for (var i = 0; i < count - 1; i++) {
|
|
||||||
let handle_ = handle.createObject(contentChildren[i]);
|
|
||||||
let split_ = handleToucharea.createObject(contentChildren[i]);
|
|
||||||
contentChildren[i].width = Qt.binding(function() {
|
|
||||||
return split_.calculatedWidth;
|
|
||||||
});
|
|
||||||
contentChildren[i].splitterWidth = Qt.binding(function() {
|
|
||||||
return handle_.width;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
contentChildren[count - 1].width = Qt.binding(function() {
|
|
||||||
if (container.singlePageMode) {
|
|
||||||
return container.width;
|
|
||||||
} else {
|
|
||||||
var w = container.width;
|
|
||||||
for (var i = 0; i < count - 1; i++) {
|
|
||||||
if (contentChildren[i].width)
|
|
||||||
w = w - contentChildren[i].width;
|
|
||||||
|
|
||||||
}
|
|
||||||
return w;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
contentChildren[count - 1].splitterWidth = 0;
|
|
||||||
for (var i = 0; i < count; i++) {
|
|
||||||
contentChildren[i].height = Qt.binding(function() {
|
|
||||||
return container.height;
|
|
||||||
});
|
|
||||||
contentChildren[i].children[0].height = Qt.binding(function() {
|
|
||||||
return container.height;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handle: Rectangle {
|
|
||||||
z: 3
|
|
||||||
color: Nheko.theme.separator
|
color: Nheko.theme.separator
|
||||||
height: container.height
|
height: container.height
|
||||||
width: visible ? 1 : 0
|
width: visible ? 1 : 0
|
||||||
anchors.right: parent.right
|
z: 3
|
||||||
}
|
}
|
||||||
|
property Component handleToucharea: Item {
|
||||||
handleToucharea: Item {
|
|
||||||
id: splitter
|
id: splitter
|
||||||
|
|
||||||
property int minimumWidth: parent.minimumWidth
|
|
||||||
property int maximumWidth: parent.maximumWidth
|
|
||||||
property int collapsedWidth: parent.collapsedWidth
|
|
||||||
property bool collapsible: parent.collapsible
|
|
||||||
property int calculatedWidth: {
|
property int calculatedWidth: {
|
||||||
if (!visible)
|
if (!visible)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -81,6 +30,10 @@ Container {
|
||||||
else
|
else
|
||||||
return (collapsible && x < minimumWidth) ? collapsedWidth : x;
|
return (collapsible && x < minimumWidth) ? collapsedWidth : x;
|
||||||
}
|
}
|
||||||
|
property int collapsedWidth: parent.collapsedWidth
|
||||||
|
property bool collapsible: parent.collapsible
|
||||||
|
property int maximumWidth: parent.maximumWidth
|
||||||
|
property int minimumWidth: parent.minimumWidth
|
||||||
|
|
||||||
enabled: !container.singlePageMode
|
enabled: !container.singlePageMode
|
||||||
height: container.height
|
height: container.height
|
||||||
|
@ -89,49 +42,82 @@ Container {
|
||||||
z: 3
|
z: 3
|
||||||
|
|
||||||
NhekoCursorShape {
|
NhekoCursorShape {
|
||||||
|
cursorShape: Qt.SizeHorCursor
|
||||||
height: parent.height
|
height: parent.height
|
||||||
width: container.splitterGrabMargin * 2
|
width: container.splitterGrabMargin * 2
|
||||||
x: -container.splitterGrabMargin
|
x: -container.splitterGrabMargin
|
||||||
cursorShape: Qt.SizeHorCursor
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DragHandler {
|
DragHandler {
|
||||||
id: dragHandler
|
id: dragHandler
|
||||||
|
|
||||||
enabled: !container.singlePageMode
|
enabled: !container.singlePageMode
|
||||||
xAxis.enabled: true
|
|
||||||
yAxis.enabled: false
|
|
||||||
xAxis.minimum: splitter.minimumWidth - 1
|
|
||||||
xAxis.maximum: splitter.maximumWidth
|
|
||||||
margin: container.splitterGrabMargin
|
|
||||||
grabPermissions: PointerHandler.CanTakeOverFromAnything | PointerHandler.ApprovesTakeOverByHandlersOfSameType
|
grabPermissions: PointerHandler.CanTakeOverFromAnything | PointerHandler.ApprovesTakeOverByHandlersOfSameType
|
||||||
|
margin: container.splitterGrabMargin
|
||||||
|
xAxis.enabled: true
|
||||||
|
xAxis.maximum: splitter.maximumWidth
|
||||||
|
xAxis.minimum: splitter.minimumWidth - 1
|
||||||
|
yAxis.enabled: false
|
||||||
|
|
||||||
onActiveChanged: {
|
onActiveChanged: {
|
||||||
if (!active) {
|
if (!active) {
|
||||||
splitter.x = splitter.calculatedWidth;
|
splitter.x = splitter.calculatedWidth;
|
||||||
splitter.parent.preferredWidth = splitter.calculatedWidth;
|
splitter.parent.preferredWidth = splitter.calculatedWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
enabled: !container.singlePageMode
|
enabled: !container.singlePageMode
|
||||||
margin: container.splitterGrabMargin
|
margin: container.splitterGrabMargin
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
property alias pageIndex: view.currentIndex
|
||||||
|
property bool singlePageMode: width < 800
|
||||||
|
property int splitterGrabMargin: Nheko.paddingSmall
|
||||||
|
|
||||||
contentItem: ListView {
|
contentItem: ListView {
|
||||||
id: view
|
id: view
|
||||||
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
model: container.contentModel
|
currentIndex: container.singlePageMode ? container.pageIndex : 0
|
||||||
snapMode: ListView.SnapOneItem
|
highlightMoveDuration: container.singlePageMode ? 200 : 0
|
||||||
orientation: ListView.Horizontal
|
|
||||||
highlightRangeMode: ListView.StrictlyEnforceRange
|
highlightRangeMode: ListView.StrictlyEnforceRange
|
||||||
interactive: singlePageMode
|
interactive: singlePageMode
|
||||||
highlightMoveDuration: container.singlePageMode ? 200 : 0
|
model: container.contentModel
|
||||||
currentIndex: container.singlePageMode ? container.pageIndex : 0
|
orientation: ListView.Horizontal
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
snapMode: ListView.SnapOneItem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
for (var i = 0; i < count - 1; i++) {
|
||||||
|
let handle_ = handle.createObject(contentChildren[i]);
|
||||||
|
let split_ = handleToucharea.createObject(contentChildren[i]);
|
||||||
|
contentChildren[i].width = Qt.binding(function () {
|
||||||
|
return split_.calculatedWidth;
|
||||||
|
});
|
||||||
|
contentChildren[i].splitterWidth = Qt.binding(function () {
|
||||||
|
return handle_.width;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
contentChildren[count - 1].width = Qt.binding(function () {
|
||||||
|
if (container.singlePageMode) {
|
||||||
|
return container.width;
|
||||||
|
} else {
|
||||||
|
var w = container.width;
|
||||||
|
for (var i = 0; i < count - 1; i++) {
|
||||||
|
if (contentChildren[i].width)
|
||||||
|
w = w - contentChildren[i].width;
|
||||||
|
}
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
contentChildren[count - 1].splitterWidth = 0;
|
||||||
|
for (var i = 0; i < count; i++) {
|
||||||
|
contentChildren[i].height = Qt.binding(function () {
|
||||||
|
return container.height;
|
||||||
|
});
|
||||||
|
contentChildren[i].children[0].height = Qt.binding(function () {
|
||||||
|
return container.height;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onSinglePageModeChanged: if (!singlePageMode)
|
||||||
|
pageIndex = 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +1,25 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.5
|
import QtQuick.Controls 2.5
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
property int minimumWidth: 100
|
property bool collapsed: width < minimumWidth
|
||||||
property int maximumWidth: 400
|
|
||||||
property int collapsedWidth: 40
|
property int collapsedWidth: 40
|
||||||
property bool collapsible: true
|
property bool collapsible: true
|
||||||
property bool collapsed: width < minimumWidth
|
property int maximumWidth: 400
|
||||||
property int splitterWidth: 1
|
property int minimumWidth: 100
|
||||||
property int preferredWidth: 100
|
property int preferredWidth: 100
|
||||||
|
property int splitterWidth: 1
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
children[0].width = Qt.binding(() => {
|
children[0].width = Qt.binding(() => {
|
||||||
return parent.singlePageMode ? parent.width : width - splitterWidth;
|
return parent.singlePageMode ? parent.width : width - splitterWidth;
|
||||||
});
|
});
|
||||||
children[0].height = Qt.binding(() => {
|
children[0].height = Qt.binding(() => {
|
||||||
return parent.height;
|
return parent.height;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
import "../"
|
||||||
import ".."
|
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
import QtQuick.Layouts 1.15
|
import QtQuick.Layouts 1.15
|
||||||
|
@ -12,86 +10,81 @@ import im.nheko
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: tile
|
id: tile
|
||||||
|
|
||||||
property color background: timelineRoot.palette.window
|
|
||||||
property color importantText: timelineRoot.palette.text
|
|
||||||
property color unimportantText: timelineRoot.palette.placeholderText
|
|
||||||
property color bubbleBackground: timelineRoot.palette.highlight
|
|
||||||
property color bubbleText: timelineRoot.palette.highlightedText
|
|
||||||
property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 2.3)
|
property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 2.3)
|
||||||
required property string avatarUrl
|
required property string avatarUrl
|
||||||
required property string title
|
property color background: timelineRoot.palette.window
|
||||||
required property string subtitle
|
property color bubbleBackground: timelineRoot.palette.highlight
|
||||||
required property int index
|
property color bubbleText: timelineRoot.palette.highlightedText
|
||||||
required property int selectedIndex
|
|
||||||
property bool crop: true
|
property bool crop: true
|
||||||
|
property color importantText: timelineRoot.palette.text
|
||||||
|
required property int index
|
||||||
property alias roomid: avatar.roomid
|
property alias roomid: avatar.roomid
|
||||||
|
required property int selectedIndex
|
||||||
|
required property string subtitle
|
||||||
|
required property string title
|
||||||
|
property color unimportantText: timelineRoot.palette.placeholderText
|
||||||
property alias userid: avatar.userid
|
property alias userid: avatar.userid
|
||||||
|
|
||||||
color: background
|
color: background
|
||||||
height: avatarSize + 2 * Nheko.paddingMedium
|
height: avatarSize + 2 * Nheko.paddingMedium
|
||||||
width: ListView.view.width
|
|
||||||
state: "normal"
|
state: "normal"
|
||||||
|
width: ListView.view.width
|
||||||
|
|
||||||
states: [
|
states: [
|
||||||
State {
|
State {
|
||||||
name: "highlight"
|
name: "highlight"
|
||||||
when: hovered.hovered && !(index == selectedIndex)
|
when: hovered.hovered && !(index == selectedIndex)
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: tile
|
|
||||||
background: timelineRoot.palette.dark
|
background: timelineRoot.palette.dark
|
||||||
importantText: timelineRoot.palette.brightText
|
|
||||||
unimportantText: timelineRoot.palette.brightText
|
|
||||||
bubbleBackground: timelineRoot.palette.highlight
|
bubbleBackground: timelineRoot.palette.highlight
|
||||||
bubbleText: timelineRoot.palette.highlightedText
|
bubbleText: timelineRoot.palette.highlightedText
|
||||||
|
importantText: timelineRoot.palette.brightText
|
||||||
|
target: tile
|
||||||
|
unimportantText: timelineRoot.palette.brightText
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "selected"
|
name: "selected"
|
||||||
when: index == selectedIndex
|
when: index == selectedIndex
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: tile
|
|
||||||
background: timelineRoot.palette.highlight
|
background: timelineRoot.palette.highlight
|
||||||
importantText: timelineRoot.palette.highlightedText
|
|
||||||
unimportantText: timelineRoot.palette.highlightedText
|
|
||||||
bubbleBackground: timelineRoot.palette.highlightedText
|
bubbleBackground: timelineRoot.palette.highlightedText
|
||||||
bubbleText: timelineRoot.palette.highlight
|
bubbleText: timelineRoot.palette.highlight
|
||||||
|
importantText: timelineRoot.palette.highlightedText
|
||||||
|
target: tile
|
||||||
|
unimportantText: timelineRoot.palette.highlightedText
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: hovered
|
id: hovered
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Nheko.paddingMedium
|
anchors.margins: Nheko.paddingMedium
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
id: avatar
|
id: avatar
|
||||||
|
|
||||||
enabled: false
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
height: avatarSize
|
|
||||||
width: avatarSize
|
|
||||||
url: tile.avatarUrl.replace("mxc://", "image://MxcImage/")
|
|
||||||
displayName: title
|
|
||||||
crop: tile.crop
|
crop: tile.crop
|
||||||
|
displayName: title
|
||||||
|
enabled: false
|
||||||
|
height: avatarSize
|
||||||
|
url: tile.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
|
width: avatarSize
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: textContent
|
id: textContent
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.minimumWidth: 100
|
Layout.minimumWidth: 100
|
||||||
width: parent.width - avatar.width
|
|
||||||
Layout.preferredWidth: parent.width - avatar.width
|
Layout.preferredWidth: parent.width - avatar.width
|
||||||
spacing: Nheko.paddingSmall
|
spacing: Nheko.paddingSmall
|
||||||
|
width: parent.width - avatar.width
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
@ -104,33 +97,25 @@ Rectangle {
|
||||||
fullText: title
|
fullText: title
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
ElidedLabel {
|
ElidedLabel {
|
||||||
color: tile.unimportantText
|
color: tile.unimportantText
|
||||||
font.pixelSize: fontMetrics.font.pixelSize * 0.9
|
|
||||||
elideWidth: textContent.width - Nheko.paddingSmall
|
elideWidth: textContent.width - Nheko.paddingSmall
|
||||||
|
font.pixelSize: fontMetrics.font.pixelSize * 0.9
|
||||||
fullText: subtitle
|
fullText: subtitle
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
//import QtGraphicalEffects 1.12
|
//import QtGraphicalEffects 1.12
|
||||||
|
@ -13,11 +12,18 @@ import im.nheko
|
||||||
Button {
|
Button {
|
||||||
id: control
|
id: control
|
||||||
|
|
||||||
|
property string iconImage: ""
|
||||||
|
|
||||||
|
hoverEnabled: true
|
||||||
implicitHeight: Math.ceil(control.contentItem.implicitHeight * 1.70)
|
implicitHeight: Math.ceil(control.contentItem.implicitHeight * 1.70)
|
||||||
implicitWidth: Math.ceil(control.contentItem.implicitWidth + control.contentItem.implicitHeight)
|
implicitWidth: Math.ceil(control.contentItem.implicitWidth + control.contentItem.implicitHeight)
|
||||||
hoverEnabled: true
|
|
||||||
|
|
||||||
property string iconImage: ""
|
background: Rectangle {
|
||||||
|
color: Qt.lighter(timelineRoot.palette.dark, control.down ? 1.4 : (control.hovered ? 1.2 : 1))
|
||||||
|
//height: control.contentItem.implicitHeight * 2
|
||||||
|
//width: control.contentItem.implicitWidth * 2
|
||||||
|
radius: height / 8
|
||||||
|
}
|
||||||
|
|
||||||
//DropShadow {
|
//DropShadow {
|
||||||
// anchors.fill: control.background
|
// anchors.fill: control.background
|
||||||
|
@ -29,38 +35,29 @@ Button {
|
||||||
// color: "#80000000"
|
// color: "#80000000"
|
||||||
// source: control.background
|
// source: control.background
|
||||||
//}
|
//}
|
||||||
|
|
||||||
contentItem: RowLayout {
|
contentItem: RowLayout {
|
||||||
spacing: 0
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
Image {
|
spacing: 0
|
||||||
Layout.leftMargin: Nheko.paddingMedium
|
|
||||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
|
|
||||||
Layout.preferredHeight: fontMetrics.font.pixelSize * 1.5
|
|
||||||
Layout.preferredWidth: fontMetrics.font.pixelSize * 1.5
|
|
||||||
visible: !!iconImage
|
|
||||||
source: iconImage
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Image {
|
||||||
|
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
|
||||||
|
Layout.leftMargin: Nheko.paddingMedium
|
||||||
|
Layout.preferredHeight: fontMetrics.font.pixelSize * 1.5
|
||||||
|
Layout.preferredWidth: fontMetrics.font.pixelSize * 1.5
|
||||||
|
source: iconImage
|
||||||
|
visible: !!iconImage
|
||||||
|
}
|
||||||
Text {
|
Text {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
text: control.text
|
//font.capitalization: Font.AllUppercase
|
||||||
|
color: timelineRoot.palette.light
|
||||||
|
elide: Text.ElideRight
|
||||||
//font: control.font
|
//font: control.font
|
||||||
font.capitalization: Font.AllUppercase
|
font.capitalization: Font.AllUppercase
|
||||||
font.pointSize: Math.ceil(fontMetrics.font.pointSize * 1.5)
|
font.pointSize: Math.ceil(fontMetrics.font.pointSize * 1.5)
|
||||||
//font.capitalization: Font.AllUppercase
|
|
||||||
color: timelineRoot.palette.light
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: control.text
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
elide: Text.ElideRight
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
//height: control.contentItem.implicitHeight * 2
|
|
||||||
//width: control.contentItem.implicitWidth * 2
|
|
||||||
radius: height / 8
|
|
||||||
color: Qt.lighter(timelineRoot.palette.dark, control.down ? 1.4 : (control.hovered ? 1.2 : 1))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import Qt.labs.platform 1.1 as P
|
import Qt.labs.platform 1.1 as P
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
|
@ -13,30 +11,28 @@ Dialog {
|
||||||
default property alias inner: scroll.data
|
default property alias inner: scroll.data
|
||||||
property int useableWidth: scroll.width - scroll.ScrollBar.vertical.width
|
property int useableWidth: scroll.width - scroll.ScrollBar.vertical.width
|
||||||
|
|
||||||
parent: Overlay.overlay
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
height: (Math.floor(parent.height / 2) - Nheko.paddingLarge) * 2
|
|
||||||
width: (Math.floor(parent.width / 2) - Nheko.paddingLarge) * 2
|
|
||||||
padding: 0
|
|
||||||
modal: true
|
|
||||||
standardButtons: Dialog.Ok | Dialog.Cancel
|
|
||||||
closePolicy: Popup.NoAutoClose
|
closePolicy: Popup.NoAutoClose
|
||||||
|
height: (Math.floor(parent.height / 2) - Nheko.paddingLarge) * 2
|
||||||
|
modal: true
|
||||||
|
padding: 0
|
||||||
|
parent: Overlay.overlay
|
||||||
|
standardButtons: Dialog.Ok | Dialog.Cancel
|
||||||
|
width: (Math.floor(parent.width / 2) - Nheko.paddingLarge) * 2
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
border.color: Nheko.theme.separator
|
||||||
|
border.width: 1
|
||||||
|
color: timelineRoot.palette.window
|
||||||
|
radius: Nheko.paddingSmall
|
||||||
|
}
|
||||||
contentChildren: [
|
contentChildren: [
|
||||||
ScrollView {
|
ScrollView {
|
||||||
id: scroll
|
id: scroll
|
||||||
|
|
||||||
clip: true
|
|
||||||
anchors.fill: parent
|
|
||||||
ScrollBar.horizontal.visible: false
|
ScrollBar.horizontal.visible: false
|
||||||
ScrollBar.vertical.visible: true
|
ScrollBar.vertical.visible: true
|
||||||
|
anchors.fill: parent
|
||||||
|
clip: true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: timelineRoot.palette.window
|
|
||||||
border.color: Nheko.theme.separator
|
|
||||||
border.width: 1
|
|
||||||
radius: Nheko.paddingSmall
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import "../ui"
|
import "../ui"
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
|
@ -11,37 +9,32 @@ import im.nheko // for cursor shape
|
||||||
AbstractButton {
|
AbstractButton {
|
||||||
id: button
|
id: button
|
||||||
|
|
||||||
|
property color buttonTextColor: timelineRoot.palette.placeholderText
|
||||||
property alias cursor: mouseArea.cursorShape
|
property alias cursor: mouseArea.cursorShape
|
||||||
property color highlightColor: timelineRoot.palette.highlight
|
property color highlightColor: timelineRoot.palette.highlight
|
||||||
property color buttonTextColor: timelineRoot.palette.placeholderText
|
|
||||||
|
|
||||||
focusPolicy: Qt.NoFocus
|
focusPolicy: Qt.NoFocus
|
||||||
width: buttonText.implicitWidth
|
|
||||||
height: buttonText.implicitHeight
|
height: buttonText.implicitHeight
|
||||||
implicitWidth: buttonText.implicitWidth
|
|
||||||
implicitHeight: buttonText.implicitHeight
|
implicitHeight: buttonText.implicitHeight
|
||||||
|
implicitWidth: buttonText.implicitWidth
|
||||||
|
width: buttonText.implicitWidth
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: buttonText
|
id: buttonText
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
padding: 0
|
|
||||||
text: button.text
|
|
||||||
color: button.hovered ? highlightColor : buttonTextColor
|
color: button.hovered ? highlightColor : buttonTextColor
|
||||||
font: button.font
|
font: button.font
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
padding: 0
|
||||||
|
text: button.text
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
NhekoCursorShape {
|
NhekoCursorShape {
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
Ripple {
|
Ripple {
|
||||||
color: Qt.rgba(buttonTextColor.r, buttonTextColor.g, buttonTextColor.b, 0.5)
|
color: Qt.rgba(buttonTextColor.r, buttonTextColor.g, buttonTextColor.b, 0.5)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
import "../"
|
||||||
import ".."
|
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
import QtQuick.Layouts 1.15
|
import QtQuick.Layouts 1.15
|
||||||
|
@ -15,32 +13,31 @@ Rectangle {
|
||||||
required property int encryptionError
|
required property int encryptionError
|
||||||
required property string eventId
|
required property string eventId
|
||||||
|
|
||||||
radius: fontMetrics.lineSpacing / 2 + Nheko.paddingMedium
|
|
||||||
width: parent.width? parent.width : 0
|
|
||||||
implicitWidth: encryptedText.implicitWidth+24+Nheko.paddingMedium*3 // Column doesn't provide a useful implicitWidth, should be replaced by ColumnLayout
|
|
||||||
height: contents.implicitHeight + Nheko.paddingMedium * 2
|
|
||||||
color: timelineRoot.palette.alternateBase
|
color: timelineRoot.palette.alternateBase
|
||||||
|
height: contents.implicitHeight + Nheko.paddingMedium * 2
|
||||||
|
implicitWidth: encryptedText.implicitWidth + 24 + Nheko.paddingMedium * 3 // Column doesn't provide a useful implicitWidth, should be replaced by ColumnLayout
|
||||||
|
radius: fontMetrics.lineSpacing / 2 + Nheko.paddingMedium
|
||||||
|
width: parent.width ? parent.width : 0
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: contents
|
id: contents
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Nheko.paddingMedium
|
anchors.margins: Nheko.paddingMedium
|
||||||
spacing: Nheko.paddingMedium
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
source: "image://colorimage/:/icons/icons/ui/shield-filled-cross.svg?" + Nheko.theme.error
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
width: 24
|
|
||||||
height: width
|
height: width
|
||||||
|
source: "image://colorimage/:/icons/icons/ui/shield-filled-cross.svg?" + Nheko.theme.error
|
||||||
|
width: 24
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
spacing: Nheko.paddingSmall
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
spacing: Nheko.paddingSmall
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
id: encryptedText
|
id: encryptedText
|
||||||
|
color: timelineRoot.palette.text
|
||||||
text: {
|
text: {
|
||||||
switch (encryptionError) {
|
switch (encryptionError) {
|
||||||
case Olm.MissingSession:
|
case Olm.MissingSession:
|
||||||
|
@ -59,19 +56,15 @@ Rectangle {
|
||||||
return qsTr("Unknown decryption error");
|
return qsTr("Unknown decryption error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
color: timelineRoot.palette.text
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
palette: timelineRoot.palette
|
palette: timelineRoot.palette
|
||||||
visible: encryptionError == Olm.MissingSession || encryptionError == Olm.MissingSessionIndex
|
|
||||||
text: qsTr("Request key")
|
text: qsTr("Request key")
|
||||||
|
visible: encryptionError == Olm.MissingSession || encryptionError == Olm.MissingSessionIndex
|
||||||
|
|
||||||
onClicked: room.requestKeyForEvent(eventId)
|
onClicked: room.requestKeyForEvent(eventId)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
import im.nheko
|
import im.nheko
|
||||||
|
@ -11,86 +9,71 @@ Item {
|
||||||
required property string eventId
|
required property string eventId
|
||||||
required property string filename
|
required property string filename
|
||||||
required property string filesize
|
required property string filesize
|
||||||
|
|
||||||
height: row.height + (Settings.bubbles? 16: 24)
|
|
||||||
width: parent.width
|
|
||||||
implicitWidth: row.implicitWidth+metadataWidth
|
|
||||||
property int metadataWidth
|
|
||||||
property bool fitsMetadata: true
|
property bool fitsMetadata: true
|
||||||
|
property int metadataWidth
|
||||||
|
|
||||||
|
height: row.height + (Settings.bubbles ? 16 : 24)
|
||||||
|
implicitWidth: row.implicitWidth + metadataWidth
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: row
|
id: row
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
width: parent.width - (Settings.bubbles? 16 : 24)
|
|
||||||
spacing: 15
|
spacing: 15
|
||||||
|
width: parent.width - (Settings.bubbles ? 16 : 24)
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: button
|
id: button
|
||||||
|
|
||||||
color: timelineRoot.palette.light
|
color: timelineRoot.palette.light
|
||||||
radius: 22
|
|
||||||
height: 44
|
height: 44
|
||||||
|
radius: 22
|
||||||
width: 44
|
width: 44
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: img
|
id: img
|
||||||
|
anchors.centerIn: parent
|
||||||
|
fillMode: Image.Pad
|
||||||
height: 40
|
height: 40
|
||||||
width: 40
|
source: "qrc:/icons/icons/ui/download.svg"
|
||||||
sourceSize.height: 40
|
sourceSize.height: 40
|
||||||
sourceSize.width: 40
|
sourceSize.width: 40
|
||||||
|
width: 40
|
||||||
anchors.centerIn: parent
|
|
||||||
source: "qrc:/icons/icons/ui/download.svg"
|
|
||||||
fillMode: Image.Pad
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TapHandler {
|
TapHandler {
|
||||||
onSingleTapped: room.saveMedia(eventId)
|
|
||||||
gesturePolicy: TapHandler.ReleaseWithinBounds
|
gesturePolicy: TapHandler.ReleaseWithinBounds
|
||||||
}
|
|
||||||
|
|
||||||
|
onSingleTapped: room.saveMedia(eventId)
|
||||||
|
}
|
||||||
NhekoCursorShape {
|
NhekoCursorShape {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: col
|
id: col
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: filename_
|
id: filename_
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
color: timelineRoot.palette.text
|
||||||
|
elide: Text.ElideRight
|
||||||
text: filename
|
text: filename
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
elide: Text.ElideRight
|
|
||||||
color: timelineRoot.palette.text
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: filesize_
|
id: filesize_
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
color: timelineRoot.palette.text
|
||||||
|
elide: Text.ElideRight
|
||||||
text: filesize
|
text: filesize
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
elide: Text.ElideRight
|
|
||||||
color: timelineRoot.palette.text
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
color: timelineRoot.palette.alternateBase
|
|
||||||
z: -1
|
|
||||||
radius: 10
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
color: timelineRoot.palette.alternateBase
|
||||||
|
radius: 10
|
||||||
visible: !Settings.bubbles // the bubble in a bubble looks odd
|
visible: !Settings.bubbles // the bubble in a bubble looks odd
|
||||||
|
z: -1
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,102 +1,85 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Window 2.15
|
import QtQuick.Window 2.15
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import im.nheko
|
import im.nheko
|
||||||
|
|
||||||
AbstractButton {
|
AbstractButton {
|
||||||
required property int type
|
|
||||||
required property int originalWidth
|
|
||||||
required property double proportionalHeight
|
|
||||||
required property string url
|
|
||||||
required property string blurhash
|
required property string blurhash
|
||||||
required property string body
|
required property string body
|
||||||
required property string filename
|
|
||||||
required property bool isReply
|
|
||||||
required property string eventId
|
|
||||||
property double divisor: isReply ? 5 : 3
|
property double divisor: isReply ? 5 : 3
|
||||||
|
required property string eventId
|
||||||
property int tempWidth: originalWidth < 1? 400: originalWidth
|
required property string filename
|
||||||
|
property bool fitsMetadata: (parent.width - width) > metadataWidth + 4
|
||||||
implicitWidth: Math.round(tempWidth*Math.min((timelineView.height/divisor)/(tempWidth*proportionalHeight), 1))
|
required property bool isReply
|
||||||
width: Math.min(parent?.width ?? implicitWidth,implicitWidth)
|
|
||||||
height: width*proportionalHeight
|
|
||||||
hoverEnabled: true
|
|
||||||
|
|
||||||
property int metadataWidth
|
property int metadataWidth
|
||||||
property bool fitsMetadata: (parent.width - width) > metadataWidth+4
|
required property int originalWidth
|
||||||
|
required property double proportionalHeight
|
||||||
|
property int tempWidth: originalWidth < 1 ? 400 : originalWidth
|
||||||
|
required property int type
|
||||||
|
required property string url
|
||||||
|
|
||||||
|
height: width * proportionalHeight
|
||||||
|
hoverEnabled: true
|
||||||
|
implicitWidth: Math.round(tempWidth * Math.min((timelineView.height / divisor) / (tempWidth * proportionalHeight), 1))
|
||||||
|
width: Math.min(parent?.width ?? implicitWidth, implicitWidth)
|
||||||
|
|
||||||
|
onClicked: Settings.openImageExternal ? room.openMedia(eventId) : TimelineManager.openImageOverlay(room, url, eventId)
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: blurhash_
|
id: blurhash_
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: img.status != Image.Ready
|
|
||||||
source: blurhash ? ("image://blurhash/" + blurhash) : ("image://colorimage/:/icons/icons/ui/image-failed.svg?" + timelineRoot.palette.placeholderText)
|
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
fillMode: Image.PreserveAspectFit
|
fillMode: Image.PreserveAspectFit
|
||||||
sourceSize.width: parent.width * Screen.devicePixelRatio
|
source: blurhash ? ("image://blurhash/" + blurhash) : ("image://colorimage/:/icons/icons/ui/image-failed.svg?" + timelineRoot.palette.placeholderText)
|
||||||
sourceSize.height: parent.height * Screen.devicePixelRatio
|
sourceSize.height: parent.height * Screen.devicePixelRatio
|
||||||
|
sourceSize.width: parent.width * Screen.devicePixelRatio
|
||||||
|
visible: img.status != Image.Ready
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: img
|
id: img
|
||||||
|
|
||||||
visible: !mxcimage.loaded
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
source: url.replace("mxc://", "image://MxcImage/") + "?scale"
|
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
fillMode: Image.PreserveAspectFit
|
fillMode: Image.PreserveAspectFit
|
||||||
smooth: true
|
|
||||||
mipmap: true
|
mipmap: true
|
||||||
|
smooth: true
|
||||||
|
source: url.replace("mxc://", "image://MxcImage/") + "?scale"
|
||||||
|
sourceSize.height: Math.min(Screen.desktopAvailableHeight, (originalWidth < 1 ? Screen.desktopAvailableHeight : originalWidth * proportionalHeight)) * Screen.devicePixelRatio
|
||||||
sourceSize.width: Math.min(Screen.desktopAvailableWidth, originalWidth < 1 ? Screen.desktopAvailableWidth : originalWidth) * Screen.devicePixelRatio
|
sourceSize.width: Math.min(Screen.desktopAvailableWidth, originalWidth < 1 ? Screen.desktopAvailableWidth : originalWidth) * Screen.devicePixelRatio
|
||||||
sourceSize.height: Math.min(Screen.desktopAvailableHeight, (originalWidth < 1 ? Screen.desktopAvailableHeight : originalWidth*proportionalHeight)) * Screen.devicePixelRatio
|
visible: !mxcimage.loaded
|
||||||
}
|
}
|
||||||
|
|
||||||
MxcAnimatedImage {
|
MxcAnimatedImage {
|
||||||
id: mxcimage
|
id: mxcimage
|
||||||
|
|
||||||
visible: loaded
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
roomm: room
|
|
||||||
play: !Settings.animateImagesOnHover || parent.hovered
|
|
||||||
eventId: parent.eventId
|
eventId: parent.eventId
|
||||||
|
play: !Settings.animateImagesOnHover || parent.hovered
|
||||||
|
roomm: room
|
||||||
|
visible: loaded
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked :Settings.openImageExternal ? room.openMedia(eventId) : TimelineManager.openImageOverlay(room, url, eventId);
|
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: overlay
|
id: overlay
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: parent.hovered
|
visible: parent.hovered
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: container
|
id: container
|
||||||
|
|
||||||
width: parent.width
|
|
||||||
implicitHeight: imgcaption.implicitHeight
|
|
||||||
anchors.bottom: overlay.bottom
|
anchors.bottom: overlay.bottom
|
||||||
color: timelineRoot.palette.window
|
color: timelineRoot.palette.window
|
||||||
|
implicitHeight: imgcaption.implicitHeight
|
||||||
opacity: 0.75
|
opacity: 0.75
|
||||||
|
width: parent.width
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: imgcaption
|
id: imgcaption
|
||||||
|
|
||||||
anchors.fill: container
|
anchors.fill: container
|
||||||
|
color: timelineRoot.palette.text
|
||||||
elide: Text.ElideMiddle
|
elide: Text.ElideMiddle
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
// See this MSC: https://github.com/matrix-org/matrix-doc/pull/2530
|
// See this MSC: https://github.com/matrix-org/matrix-doc/pull/2530
|
||||||
text: filename ? filename : body
|
text: filename ? filename : body
|
||||||
color: timelineRoot.palette.text
|
verticalAlignment: Text.AlignVCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.6
|
import QtQuick 2.6
|
||||||
import QtQuick.Controls 2.1
|
import QtQuick.Controls 2.1
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
|
@ -12,35 +10,35 @@ import im.nheko
|
||||||
Item {
|
Item {
|
||||||
id: d
|
id: d
|
||||||
|
|
||||||
required property bool isReply
|
|
||||||
property alias child: chooser.child
|
|
||||||
implicitWidth: (chooser.child && chooser.child.implicitWidth) ? chooser.child.implicitWidth : 0
|
|
||||||
required property double proportionalHeight
|
|
||||||
required property int type
|
|
||||||
required property string typeString
|
|
||||||
required property int originalWidth
|
|
||||||
required property int duration
|
|
||||||
required property string blurhash
|
required property string blurhash
|
||||||
required property string body
|
required property string body
|
||||||
required property string formattedBody
|
required property string callType
|
||||||
|
property alias child: chooser.child
|
||||||
|
required property int duration
|
||||||
|
required property int encryptionError
|
||||||
required property string eventId
|
required property string eventId
|
||||||
required property string filename
|
required property string filename
|
||||||
required property string filesize
|
required property string filesize
|
||||||
required property string url
|
property bool fitsMetadata: (chooser.child && chooser.child.fitsMetadata) ? chooser.child.fitsMetadata : false
|
||||||
required property string thumbnailUrl
|
required property string formattedBody
|
||||||
required property bool isOnlyEmoji
|
required property bool isOnlyEmoji
|
||||||
|
required property bool isReply
|
||||||
required property bool isStateEvent
|
required property bool isStateEvent
|
||||||
|
property int metadataWidth
|
||||||
|
required property int originalWidth
|
||||||
|
required property double proportionalHeight
|
||||||
|
required property int relatedEventCacheBuster
|
||||||
|
required property string roomName
|
||||||
|
required property string roomTopic
|
||||||
|
required property string thumbnailUrl
|
||||||
|
required property int type
|
||||||
|
required property string typeString
|
||||||
|
required property string url
|
||||||
required property string userId
|
required property string userId
|
||||||
required property string userName
|
required property string userName
|
||||||
required property string roomTopic
|
|
||||||
required property string roomName
|
|
||||||
required property string callType
|
|
||||||
required property int encryptionError
|
|
||||||
required property int relatedEventCacheBuster
|
|
||||||
property bool fitsMetadata: (chooser.child && chooser.child.fitsMetadata) ? chooser.child.fitsMetadata : false
|
|
||||||
property int metadataWidth
|
|
||||||
|
|
||||||
height: chooser.child ? chooser.child.height : Nheko.paddingLarge
|
height: chooser.child ? chooser.child.height : Nheko.paddingLarge
|
||||||
|
implicitWidth: (chooser.child && chooser.child.implicitWidth) ? chooser.child.implicitWidth : 0
|
||||||
|
|
||||||
DelegateChooser {
|
DelegateChooser {
|
||||||
id: chooser
|
id: chooser
|
||||||
|
@ -48,97 +46,84 @@ Item {
|
||||||
//role: "type" //< not supported in our custom implementation, have to use roleValue
|
//role: "type" //< not supported in our custom implementation, have to use roleValue
|
||||||
roleValue: type
|
roleValue: type
|
||||||
//anchors.fill: parent
|
//anchors.fill: parent
|
||||||
|
width: parent.width ? parent.width : 0 // this should get rid of "cannot read property 'width' of null"
|
||||||
width: parent.width? parent.width: 0 // this should get rid of "cannot read property 'width' of null"
|
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.UnknownMessage
|
roleValue: MtxEvent.UnknownMessage
|
||||||
|
|
||||||
Placeholder {
|
Placeholder {
|
||||||
typeString: d.typeString
|
|
||||||
text: "Unretrieved event"
|
text: "Unretrieved event"
|
||||||
|
typeString: d.typeString
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.TextMessage
|
roleValue: MtxEvent.TextMessage
|
||||||
|
|
||||||
TextMessage {
|
TextMessage {
|
||||||
formatted: d.formattedBody
|
|
||||||
body: d.body
|
body: d.body
|
||||||
|
formatted: d.formattedBody
|
||||||
isOnlyEmoji: d.isOnlyEmoji
|
isOnlyEmoji: d.isOnlyEmoji
|
||||||
isReply: d.isReply
|
isReply: d.isReply
|
||||||
metadataWidth: d.metadataWidth
|
metadataWidth: d.metadataWidth
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.NoticeMessage
|
roleValue: MtxEvent.NoticeMessage
|
||||||
|
|
||||||
NoticeMessage {
|
NoticeMessage {
|
||||||
formatted: d.formattedBody
|
|
||||||
body: d.body
|
body: d.body
|
||||||
|
formatted: d.formattedBody
|
||||||
isOnlyEmoji: d.isOnlyEmoji
|
isOnlyEmoji: d.isOnlyEmoji
|
||||||
isReply: d.isReply
|
isReply: d.isReply
|
||||||
isStateEvent: d.isStateEvent
|
isStateEvent: d.isStateEvent
|
||||||
metadataWidth: d.metadataWidth
|
metadataWidth: d.metadataWidth
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.EmoteMessage
|
roleValue: MtxEvent.EmoteMessage
|
||||||
|
|
||||||
NoticeMessage {
|
NoticeMessage {
|
||||||
formatted: TimelineManager.escapeEmoji(d.userName) + " " + d.formattedBody
|
|
||||||
color: TimelineManager.userColor(d.userId, timelineRoot.palette.base)
|
|
||||||
body: d.body
|
body: d.body
|
||||||
|
color: TimelineManager.userColor(d.userId, timelineRoot.palette.base)
|
||||||
|
formatted: TimelineManager.escapeEmoji(d.userName) + " " + d.formattedBody
|
||||||
isOnlyEmoji: d.isOnlyEmoji
|
isOnlyEmoji: d.isOnlyEmoji
|
||||||
isReply: d.isReply
|
isReply: d.isReply
|
||||||
isStateEvent: d.isStateEvent
|
isStateEvent: d.isStateEvent
|
||||||
metadataWidth: d.metadataWidth
|
metadataWidth: d.metadataWidth
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.ImageMessage
|
roleValue: MtxEvent.ImageMessage
|
||||||
|
|
||||||
ImageMessage {
|
ImageMessage {
|
||||||
type: d.type
|
|
||||||
originalWidth: d.originalWidth
|
|
||||||
proportionalHeight: d.proportionalHeight
|
|
||||||
url: d.url
|
|
||||||
blurhash: d.blurhash
|
blurhash: d.blurhash
|
||||||
body: d.body
|
body: d.body
|
||||||
|
eventId: d.eventId
|
||||||
filename: d.filename
|
filename: d.filename
|
||||||
isReply: d.isReply
|
isReply: d.isReply
|
||||||
eventId: d.eventId
|
|
||||||
metadataWidth: d.metadataWidth
|
metadataWidth: d.metadataWidth
|
||||||
|
originalWidth: d.originalWidth
|
||||||
|
proportionalHeight: d.proportionalHeight
|
||||||
|
type: d.type
|
||||||
|
url: d.url
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.Sticker
|
roleValue: MtxEvent.Sticker
|
||||||
|
|
||||||
ImageMessage {
|
ImageMessage {
|
||||||
type: d.type
|
|
||||||
originalWidth: d.originalWidth
|
|
||||||
proportionalHeight: d.proportionalHeight
|
|
||||||
url: d.url
|
|
||||||
blurhash: d.blurhash
|
blurhash: d.blurhash
|
||||||
body: d.body
|
body: d.body
|
||||||
|
eventId: d.eventId
|
||||||
filename: d.filename
|
filename: d.filename
|
||||||
isReply: d.isReply
|
isReply: d.isReply
|
||||||
eventId: d.eventId
|
|
||||||
metadataWidth: d.metadataWidth
|
metadataWidth: d.metadataWidth
|
||||||
|
originalWidth: d.originalWidth
|
||||||
|
proportionalHeight: d.proportionalHeight
|
||||||
|
type: d.type
|
||||||
|
url: d.url
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.FileMessage
|
roleValue: MtxEvent.FileMessage
|
||||||
|
|
||||||
|
@ -148,45 +133,39 @@ Item {
|
||||||
filesize: d.filesize
|
filesize: d.filesize
|
||||||
metadataWidth: d.metadataWidth
|
metadataWidth: d.metadataWidth
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.VideoMessage
|
roleValue: MtxEvent.VideoMessage
|
||||||
|
|
||||||
PlayableMediaMessage {
|
PlayableMediaMessage {
|
||||||
proportionalHeight: d.proportionalHeight
|
|
||||||
type: d.type
|
|
||||||
originalWidth: d.originalWidth
|
|
||||||
thumbnailUrl: d.thumbnailUrl
|
|
||||||
eventId: d.eventId
|
|
||||||
url: d.url
|
|
||||||
body: d.body
|
body: d.body
|
||||||
filesize: d.filesize
|
|
||||||
duration: d.duration
|
duration: d.duration
|
||||||
|
eventId: d.eventId
|
||||||
|
filesize: d.filesize
|
||||||
metadataWidth: d.metadataWidth
|
metadataWidth: d.metadataWidth
|
||||||
|
originalWidth: d.originalWidth
|
||||||
|
proportionalHeight: d.proportionalHeight
|
||||||
|
thumbnailUrl: d.thumbnailUrl
|
||||||
|
type: d.type
|
||||||
|
url: d.url
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.AudioMessage
|
roleValue: MtxEvent.AudioMessage
|
||||||
|
|
||||||
PlayableMediaMessage {
|
PlayableMediaMessage {
|
||||||
proportionalHeight: d.proportionalHeight
|
|
||||||
type: d.type
|
|
||||||
originalWidth: d.originalWidth
|
|
||||||
thumbnailUrl: d.thumbnailUrl
|
|
||||||
eventId: d.eventId
|
|
||||||
url: d.url
|
|
||||||
body: d.body
|
body: d.body
|
||||||
filesize: d.filesize
|
|
||||||
duration: d.duration
|
duration: d.duration
|
||||||
|
eventId: d.eventId
|
||||||
|
filesize: d.filesize
|
||||||
metadataWidth: d.metadataWidth
|
metadataWidth: d.metadataWidth
|
||||||
|
originalWidth: d.originalWidth
|
||||||
|
proportionalHeight: d.proportionalHeight
|
||||||
|
thumbnailUrl: d.thumbnailUrl
|
||||||
|
type: d.type
|
||||||
|
url: d.url
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.Redacted
|
roleValue: MtxEvent.Redacted
|
||||||
|
|
||||||
|
@ -194,27 +173,22 @@ Item {
|
||||||
metadataWidth: d.metadataWidth
|
metadataWidth: d.metadataWidth
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.Redaction
|
roleValue: MtxEvent.Redaction
|
||||||
|
|
||||||
Pill {
|
Pill {
|
||||||
text: qsTr("%1 removed a message").arg(d.userName)
|
|
||||||
isStateEvent: d.isStateEvent
|
isStateEvent: d.isStateEvent
|
||||||
|
text: qsTr("%1 removed a message").arg(d.userName)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.Encryption
|
roleValue: MtxEvent.Encryption
|
||||||
|
|
||||||
Pill {
|
Pill {
|
||||||
text: qsTr("%1 enabled encryption").arg(d.userName)
|
|
||||||
isStateEvent: d.isStateEvent
|
isStateEvent: d.isStateEvent
|
||||||
|
text: qsTr("%1 enabled encryption").arg(d.userName)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.Encrypted
|
roleValue: MtxEvent.Encrypted
|
||||||
|
|
||||||
|
@ -222,122 +196,100 @@ Item {
|
||||||
encryptionError: d.encryptionError
|
encryptionError: d.encryptionError
|
||||||
eventId: d.eventId
|
eventId: d.eventId
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.Name
|
roleValue: MtxEvent.Name
|
||||||
|
|
||||||
NoticeMessage {
|
NoticeMessage {
|
||||||
body: formatted
|
body: formatted
|
||||||
|
formatted: d.roomName ? qsTr("%2 changed the room name to: %1").arg(d.roomName).arg(d.userName) : qsTr("%1 removed the room name").arg(d.userName)
|
||||||
isOnlyEmoji: false
|
isOnlyEmoji: false
|
||||||
isReply: d.isReply
|
isReply: d.isReply
|
||||||
isStateEvent: d.isStateEvent
|
isStateEvent: d.isStateEvent
|
||||||
formatted: d.roomName ? qsTr("%2 changed the room name to: %1").arg(d.roomName).arg(d.userName) : qsTr("%1 removed the room name").arg(d.userName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.Topic
|
roleValue: MtxEvent.Topic
|
||||||
|
|
||||||
NoticeMessage {
|
NoticeMessage {
|
||||||
body: formatted
|
body: formatted
|
||||||
|
formatted: d.roomTopic ? qsTr("%2 changed the topic to: %1").arg(d.roomTopic).arg(d.userName) : qsTr("%1 removed the topic").arg(d.userName)
|
||||||
isOnlyEmoji: false
|
isOnlyEmoji: false
|
||||||
isReply: d.isReply
|
isReply: d.isReply
|
||||||
isStateEvent: d.isStateEvent
|
isStateEvent: d.isStateEvent
|
||||||
formatted: d.roomTopic ? qsTr("%2 changed the topic to: %1").arg(d.roomTopic).arg(d.userName): qsTr("%1 removed the topic").arg(d.userName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.Avatar
|
roleValue: MtxEvent.Avatar
|
||||||
|
|
||||||
NoticeMessage {
|
NoticeMessage {
|
||||||
body: formatted
|
body: formatted
|
||||||
|
formatted: qsTr("%1 changed the room avatar").arg(d.userName)
|
||||||
isOnlyEmoji: false
|
isOnlyEmoji: false
|
||||||
isReply: d.isReply
|
isReply: d.isReply
|
||||||
isStateEvent: d.isStateEvent
|
isStateEvent: d.isStateEvent
|
||||||
formatted: qsTr("%1 changed the room avatar").arg(d.userName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.PinnedEvents
|
roleValue: MtxEvent.PinnedEvents
|
||||||
|
|
||||||
NoticeMessage {
|
NoticeMessage {
|
||||||
body: formatted
|
body: formatted
|
||||||
|
formatted: qsTr("%1 changed the pinned messages.").arg(d.userName)
|
||||||
isOnlyEmoji: false
|
isOnlyEmoji: false
|
||||||
isReply: d.isReply
|
isReply: d.isReply
|
||||||
isStateEvent: d.isStateEvent
|
isStateEvent: d.isStateEvent
|
||||||
formatted: qsTr("%1 changed the pinned messages.").arg(d.userName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.ImagePackInRoom
|
roleValue: MtxEvent.ImagePackInRoom
|
||||||
|
|
||||||
NoticeMessage {
|
NoticeMessage {
|
||||||
body: formatted
|
body: formatted
|
||||||
|
formatted: d.relatedEventCacheBuster, room.formatImagePackEvent(d.eventId)
|
||||||
isOnlyEmoji: false
|
isOnlyEmoji: false
|
||||||
isReply: d.isReply
|
isReply: d.isReply
|
||||||
isStateEvent: d.isStateEvent
|
isStateEvent: d.isStateEvent
|
||||||
formatted: d.relatedEventCacheBuster, room.formatImagePackEvent(d.eventId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.CanonicalAlias
|
roleValue: MtxEvent.CanonicalAlias
|
||||||
|
|
||||||
NoticeMessage {
|
NoticeMessage {
|
||||||
body: formatted
|
body: formatted
|
||||||
|
formatted: qsTr("%1 changed the addresses for this room.").arg(d.userName)
|
||||||
isOnlyEmoji: false
|
isOnlyEmoji: false
|
||||||
isReply: d.isReply
|
isReply: d.isReply
|
||||||
isStateEvent: d.isStateEvent
|
isStateEvent: d.isStateEvent
|
||||||
formatted: qsTr("%1 changed the addresses for this room.").arg(d.userName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.SpaceParent
|
roleValue: MtxEvent.SpaceParent
|
||||||
|
|
||||||
NoticeMessage {
|
NoticeMessage {
|
||||||
body: formatted
|
body: formatted
|
||||||
|
formatted: qsTr("%1 changed the parent spaces for this room.").arg(d.userName)
|
||||||
isOnlyEmoji: false
|
isOnlyEmoji: false
|
||||||
isReply: d.isReply
|
isReply: d.isReply
|
||||||
isStateEvent: d.isStateEvent
|
isStateEvent: d.isStateEvent
|
||||||
formatted: qsTr("%1 changed the parent spaces for this room.").arg(d.userName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.RoomCreate
|
roleValue: MtxEvent.RoomCreate
|
||||||
|
|
||||||
NoticeMessage {
|
NoticeMessage {
|
||||||
body: formatted
|
body: formatted
|
||||||
|
formatted: qsTr("%1 created and configured room: %2").arg(d.userName).arg(room.roomId)
|
||||||
isOnlyEmoji: false
|
isOnlyEmoji: false
|
||||||
isReply: d.isReply
|
isReply: d.isReply
|
||||||
isStateEvent: d.isStateEvent
|
isStateEvent: d.isStateEvent
|
||||||
formatted: qsTr("%1 created and configured room: %2").arg(d.userName).arg(room.roomId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.CallInvite
|
roleValue: MtxEvent.CallInvite
|
||||||
|
|
||||||
NoticeMessage {
|
NoticeMessage {
|
||||||
body: formatted
|
body: formatted
|
||||||
isOnlyEmoji: false
|
|
||||||
isReply: d.isReply
|
|
||||||
isStateEvent: d.isStateEvent
|
|
||||||
formatted: {
|
formatted: {
|
||||||
switch (d.callType) {
|
switch (d.callType) {
|
||||||
case "voice":
|
case "voice":
|
||||||
|
@ -348,101 +300,88 @@ Item {
|
||||||
return qsTr("%1 placed a call.").arg(d.userName);
|
return qsTr("%1 placed a call.").arg(d.userName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
isOnlyEmoji: false
|
||||||
|
isReply: d.isReply
|
||||||
|
isStateEvent: d.isStateEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.CallAnswer
|
roleValue: MtxEvent.CallAnswer
|
||||||
|
|
||||||
NoticeMessage {
|
NoticeMessage {
|
||||||
body: formatted
|
body: formatted
|
||||||
|
formatted: qsTr("%1 answered the call.").arg(d.userName)
|
||||||
isOnlyEmoji: false
|
isOnlyEmoji: false
|
||||||
isReply: d.isReply
|
isReply: d.isReply
|
||||||
isStateEvent: d.isStateEvent
|
isStateEvent: d.isStateEvent
|
||||||
formatted: qsTr("%1 answered the call.").arg(d.userName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.CallHangUp
|
roleValue: MtxEvent.CallHangUp
|
||||||
|
|
||||||
NoticeMessage {
|
NoticeMessage {
|
||||||
body: formatted
|
body: formatted
|
||||||
|
formatted: qsTr("%1 ended the call.").arg(d.userName)
|
||||||
isOnlyEmoji: false
|
isOnlyEmoji: false
|
||||||
isReply: d.isReply
|
isReply: d.isReply
|
||||||
isStateEvent: d.isStateEvent
|
isStateEvent: d.isStateEvent
|
||||||
formatted: qsTr("%1 ended the call.").arg(d.userName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.CallCandidates
|
roleValue: MtxEvent.CallCandidates
|
||||||
|
|
||||||
NoticeMessage {
|
NoticeMessage {
|
||||||
body: formatted
|
body: formatted
|
||||||
|
formatted: qsTr("%1 is negotiating the call...").arg(d.userName)
|
||||||
isOnlyEmoji: false
|
isOnlyEmoji: false
|
||||||
isReply: d.isReply
|
isReply: d.isReply
|
||||||
isStateEvent: d.isStateEvent
|
isStateEvent: d.isStateEvent
|
||||||
formatted: qsTr("%1 is negotiating the call...").arg(d.userName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.PowerLevels
|
roleValue: MtxEvent.PowerLevels
|
||||||
|
|
||||||
NoticeMessage {
|
NoticeMessage {
|
||||||
body: formatted
|
body: formatted
|
||||||
|
formatted: d.relatedEventCacheBuster, room.formatPowerLevelEvent(d.eventId)
|
||||||
isOnlyEmoji: false
|
isOnlyEmoji: false
|
||||||
isReply: d.isReply
|
isReply: d.isReply
|
||||||
isStateEvent: d.isStateEvent
|
isStateEvent: d.isStateEvent
|
||||||
formatted: d.relatedEventCacheBuster, room.formatPowerLevelEvent(d.eventId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.RoomJoinRules
|
roleValue: MtxEvent.RoomJoinRules
|
||||||
|
|
||||||
NoticeMessage {
|
NoticeMessage {
|
||||||
body: formatted
|
body: formatted
|
||||||
|
formatted: d.relatedEventCacheBuster, room.formatJoinRuleEvent(d.eventId)
|
||||||
isOnlyEmoji: false
|
isOnlyEmoji: false
|
||||||
isReply: d.isReply
|
isReply: d.isReply
|
||||||
isStateEvent: d.isStateEvent
|
isStateEvent: d.isStateEvent
|
||||||
formatted: d.relatedEventCacheBuster, room.formatJoinRuleEvent(d.eventId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.RoomHistoryVisibility
|
roleValue: MtxEvent.RoomHistoryVisibility
|
||||||
|
|
||||||
NoticeMessage {
|
NoticeMessage {
|
||||||
body: formatted
|
body: formatted
|
||||||
|
formatted: d.relatedEventCacheBuster, room.formatHistoryVisibilityEvent(d.eventId)
|
||||||
isOnlyEmoji: false
|
isOnlyEmoji: false
|
||||||
isReply: d.isReply
|
isReply: d.isReply
|
||||||
isStateEvent: d.isStateEvent
|
isStateEvent: d.isStateEvent
|
||||||
formatted: d.relatedEventCacheBuster, room.formatHistoryVisibilityEvent(d.eventId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.RoomGuestAccess
|
roleValue: MtxEvent.RoomGuestAccess
|
||||||
|
|
||||||
NoticeMessage {
|
NoticeMessage {
|
||||||
body: formatted
|
body: formatted
|
||||||
|
formatted: d.relatedEventCacheBuster, room.formatGuestAccessEvent(d.eventId)
|
||||||
isOnlyEmoji: false
|
isOnlyEmoji: false
|
||||||
isReply: d.isReply
|
isReply: d.isReply
|
||||||
isStateEvent: d.isStateEvent
|
isStateEvent: d.isStateEvent
|
||||||
formatted: d.relatedEventCacheBuster, room.formatGuestAccessEvent(d.eventId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.Member
|
roleValue: MtxEvent.Member
|
||||||
|
|
||||||
|
@ -450,149 +389,125 @@ Item {
|
||||||
width: parent?.width
|
width: parent?.width
|
||||||
|
|
||||||
NoticeMessage {
|
NoticeMessage {
|
||||||
|
Layout.fillWidth: true
|
||||||
body: formatted
|
body: formatted
|
||||||
|
formatted: d.relatedEventCacheBuster, room.formatMemberEvent(d.eventId)
|
||||||
isOnlyEmoji: false
|
isOnlyEmoji: false
|
||||||
isReply: d.isReply
|
isReply: d.isReply
|
||||||
isStateEvent: d.isStateEvent
|
isStateEvent: d.isStateEvent
|
||||||
Layout.fillWidth: true
|
|
||||||
formatted: d.relatedEventCacheBuster, room.formatMemberEvent(d.eventId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
visible: d.relatedEventCacheBuster, room.showAcceptKnockButton(d.eventId)
|
|
||||||
palette: timelineRoot.palette
|
palette: timelineRoot.palette
|
||||||
text: qsTr("Allow them in")
|
text: qsTr("Allow them in")
|
||||||
|
visible: d.relatedEventCacheBuster, room.showAcceptKnockButton(d.eventId)
|
||||||
|
|
||||||
onClicked: room.acceptKnock(eventId)
|
onClicked: room.acceptKnock(eventId)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.KeyVerificationRequest
|
roleValue: MtxEvent.KeyVerificationRequest
|
||||||
|
|
||||||
NoticeMessage {
|
NoticeMessage {
|
||||||
body: formatted
|
body: formatted
|
||||||
|
formatted: "KeyVerificationRequest"
|
||||||
isOnlyEmoji: false
|
isOnlyEmoji: false
|
||||||
isReply: d.isReply
|
isReply: d.isReply
|
||||||
isStateEvent: d.isStateEvent
|
isStateEvent: d.isStateEvent
|
||||||
formatted: "KeyVerificationRequest"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.KeyVerificationStart
|
roleValue: MtxEvent.KeyVerificationStart
|
||||||
|
|
||||||
NoticeMessage {
|
NoticeMessage {
|
||||||
body: formatted
|
body: formatted
|
||||||
|
formatted: "KeyVerificationStart"
|
||||||
isOnlyEmoji: false
|
isOnlyEmoji: false
|
||||||
isReply: d.isReply
|
isReply: d.isReply
|
||||||
isStateEvent: d.isStateEvent
|
isStateEvent: d.isStateEvent
|
||||||
formatted: "KeyVerificationStart"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.KeyVerificationReady
|
roleValue: MtxEvent.KeyVerificationReady
|
||||||
|
|
||||||
NoticeMessage {
|
NoticeMessage {
|
||||||
body: formatted
|
body: formatted
|
||||||
|
formatted: "KeyVerificationReady"
|
||||||
isOnlyEmoji: false
|
isOnlyEmoji: false
|
||||||
isReply: d.isReply
|
isReply: d.isReply
|
||||||
isStateEvent: d.isStateEvent
|
isStateEvent: d.isStateEvent
|
||||||
formatted: "KeyVerificationReady"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.KeyVerificationCancel
|
roleValue: MtxEvent.KeyVerificationCancel
|
||||||
|
|
||||||
NoticeMessage {
|
NoticeMessage {
|
||||||
body: formatted
|
body: formatted
|
||||||
|
formatted: "KeyVerificationCancel"
|
||||||
isOnlyEmoji: false
|
isOnlyEmoji: false
|
||||||
isReply: d.isReply
|
isReply: d.isReply
|
||||||
isStateEvent: d.isStateEvent
|
isStateEvent: d.isStateEvent
|
||||||
formatted: "KeyVerificationCancel"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.KeyVerificationKey
|
roleValue: MtxEvent.KeyVerificationKey
|
||||||
|
|
||||||
NoticeMessage {
|
NoticeMessage {
|
||||||
body: formatted
|
body: formatted
|
||||||
|
formatted: "KeyVerificationKey"
|
||||||
isOnlyEmoji: false
|
isOnlyEmoji: false
|
||||||
isReply: d.isReply
|
isReply: d.isReply
|
||||||
isStateEvent: d.isStateEvent
|
isStateEvent: d.isStateEvent
|
||||||
formatted: "KeyVerificationKey"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.KeyVerificationMac
|
roleValue: MtxEvent.KeyVerificationMac
|
||||||
|
|
||||||
NoticeMessage {
|
NoticeMessage {
|
||||||
body: formatted
|
body: formatted
|
||||||
isOnlyEmoji: false
|
|
||||||
isReply: d.isReply
|
|
||||||
isStateEvent: d.isStateEvent
|
|
||||||
formatted: "KeyVerificationMac"
|
formatted: "KeyVerificationMac"
|
||||||
|
isOnlyEmoji: false
|
||||||
|
isReply: d.isReply
|
||||||
|
isStateEvent: d.isStateEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.KeyVerificationDone
|
roleValue: MtxEvent.KeyVerificationDone
|
||||||
|
|
||||||
NoticeMessage {
|
NoticeMessage {
|
||||||
body: formatted
|
body: formatted
|
||||||
|
formatted: "KeyVerificationDone"
|
||||||
isOnlyEmoji: false
|
isOnlyEmoji: false
|
||||||
isReply: d.isReply
|
isReply: d.isReply
|
||||||
isStateEvent: d.isStateEvent
|
isStateEvent: d.isStateEvent
|
||||||
formatted: "KeyVerificationDone"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.KeyVerificationDone
|
roleValue: MtxEvent.KeyVerificationDone
|
||||||
|
|
||||||
NoticeMessage {
|
NoticeMessage {
|
||||||
body: formatted
|
body: formatted
|
||||||
|
formatted: "KeyVerificationDone"
|
||||||
isOnlyEmoji: false
|
isOnlyEmoji: false
|
||||||
isReply: d.isReply
|
isReply: d.isReply
|
||||||
isStateEvent: d.isStateEvent
|
isStateEvent: d.isStateEvent
|
||||||
formatted: "KeyVerificationDone"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.KeyVerificationAccept
|
roleValue: MtxEvent.KeyVerificationAccept
|
||||||
|
|
||||||
NoticeMessage {
|
NoticeMessage {
|
||||||
body: formatted
|
body: formatted
|
||||||
|
formatted: "KeyVerificationAccept"
|
||||||
isOnlyEmoji: false
|
isOnlyEmoji: false
|
||||||
isReply: d.isReply
|
isReply: d.isReply
|
||||||
isStateEvent: d.isStateEvent
|
isStateEvent: d.isStateEvent
|
||||||
formatted: "KeyVerificationAccept"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
Placeholder {
|
Placeholder {
|
||||||
typeString: d.typeString
|
typeString: d.typeString
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,14 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.5
|
import QtQuick 2.5
|
||||||
import im.nheko
|
import im.nheko
|
||||||
|
|
||||||
|
|
||||||
TextMessage {
|
TextMessage {
|
||||||
property bool isStateEvent
|
property bool isStateEvent
|
||||||
font.italic: true
|
|
||||||
color: timelineRoot.palette.placeholderText
|
color: timelineRoot.palette.placeholderText
|
||||||
font.pointSize: isStateEvent? 0.8*Settings.fontSize : Settings.fontSize
|
font.italic: true
|
||||||
horizontalAlignment: isStateEvent? Text.AlignHCenter : undefined
|
font.pointSize: isStateEvent ? 0.8 * Settings.fontSize : Settings.fontSize
|
||||||
|
horizontalAlignment: isStateEvent ? Text.AlignHCenter : undefined
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,20 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.5
|
import QtQuick 2.5
|
||||||
import QtQuick.Controls 2.1
|
import QtQuick.Controls 2.1
|
||||||
import im.nheko
|
import im.nheko
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
property bool isStateEvent
|
property bool isStateEvent
|
||||||
|
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
height: Math.round(fontMetrics.height * 1.4)
|
height: Math.round(fontMetrics.height * 1.4)
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
width: contentWidth * 1.2
|
width: contentWidth * 1.2
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
radius: parent.height / 2
|
|
||||||
color: timelineRoot.palette.alternateBase
|
color: timelineRoot.palette.alternateBase
|
||||||
|
radius: parent.height / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
import "../"
|
||||||
import ".."
|
|
||||||
import im.nheko
|
import im.nheko
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
required property string typeString
|
required property string typeString
|
||||||
|
|
||||||
text: qsTr("unimplemented event: ") + typeString
|
// width: parent.width
|
||||||
// width: parent.width
|
|
||||||
color: timelineRoot.palette.inactive.text
|
color: timelineRoot.palette.inactive.text
|
||||||
|
text: qsTr("unimplemented event: ") + typeString
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import "../"
|
import "../"
|
||||||
import "../ui/media"
|
import "../ui/media"
|
||||||
import QtMultimedia
|
import QtMultimedia
|
||||||
|
@ -14,99 +12,88 @@ import im.nheko
|
||||||
Item {
|
Item {
|
||||||
id: content
|
id: content
|
||||||
|
|
||||||
required property double proportionalHeight
|
|
||||||
required property int type
|
|
||||||
required property int originalWidth
|
|
||||||
required property int duration
|
|
||||||
required property string thumbnailUrl
|
|
||||||
required property string eventId
|
|
||||||
required property string url
|
|
||||||
required property string body
|
required property string body
|
||||||
required property string filesize
|
|
||||||
property double divisor: isReply ? 4 : 2
|
property double divisor: isReply ? 4 : 2
|
||||||
property int tempWidth: originalWidth < 1? 400: originalWidth
|
required property int duration
|
||||||
implicitWidth: type == MtxEvent.VideoMessage ? Math.round(tempWidth*Math.min((timelineView.height/divisor)/(tempWidth*proportionalHeight), 1)) : 500
|
required property string eventId
|
||||||
width: Math.min(parent.width, implicitWidth)
|
required property string filesize
|
||||||
height: (type == MtxEvent.VideoMessage ? width*proportionalHeight : 80) + fileInfoLabel.height
|
property bool fitsMetadata: (parent.width - fileInfoLabel.width) > metadataWidth + 4
|
||||||
implicitHeight: height
|
|
||||||
|
|
||||||
property int metadataWidth
|
property int metadataWidth
|
||||||
property bool fitsMetadata: (parent.width - fileInfoLabel.width) > metadataWidth+4
|
required property int originalWidth
|
||||||
|
required property double proportionalHeight
|
||||||
|
property int tempWidth: originalWidth < 1 ? 400 : originalWidth
|
||||||
|
required property string thumbnailUrl
|
||||||
|
required property int type
|
||||||
|
required property string url
|
||||||
|
|
||||||
|
height: (type == MtxEvent.VideoMessage ? width * proportionalHeight : 80) + fileInfoLabel.height
|
||||||
|
implicitHeight: height
|
||||||
|
implicitWidth: type == MtxEvent.VideoMessage ? Math.round(tempWidth * Math.min((timelineView.height / divisor) / (tempWidth * proportionalHeight), 1)) : 500
|
||||||
|
width: Math.min(parent.width, implicitWidth)
|
||||||
|
|
||||||
MxcMedia {
|
MxcMedia {
|
||||||
id: mxcmedia
|
id: mxcmedia
|
||||||
|
|
||||||
roomm: room
|
roomm: room
|
||||||
|
videoOutput: videoOutput
|
||||||
|
|
||||||
audioOutput: AudioOutput {
|
audioOutput: AudioOutput {
|
||||||
muted: mediaControls.muted
|
muted: mediaControls.muted
|
||||||
volume: mediaControls.desiredVolume
|
volume: mediaControls.desiredVolume
|
||||||
}
|
}
|
||||||
videoOutput: videoOutput
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: videoContainer
|
id: videoContainer
|
||||||
|
|
||||||
color: type == MtxEvent.VideoMessage ? timelineRoot.palette.window : "transparent"
|
color: type == MtxEvent.VideoMessage ? timelineRoot.palette.window : "transparent"
|
||||||
width: parent.width
|
|
||||||
height: parent.height - fileInfoLabel.height
|
height: parent.height - fileInfoLabel.height
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
TapHandler {
|
TapHandler {
|
||||||
onTapped: Settings.openVideoExternal ? room.openMedia(eventId) : mediaControls.showControls()
|
onTapped: Settings.openVideoExternal ? room.openMedia(eventId) : mediaControls.showControls()
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
source: thumbnailUrl ? thumbnailUrl.replace("mxc://", "image://MxcImage/") + "?scale" : ""
|
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
fillMode: Image.PreserveAspectFit
|
fillMode: Image.PreserveAspectFit
|
||||||
|
source: thumbnailUrl ? thumbnailUrl.replace("mxc://", "image://MxcImage/") + "?scale" : ""
|
||||||
|
|
||||||
VideoOutput {
|
VideoOutput {
|
||||||
id: videoOutput
|
id: videoOutput
|
||||||
|
|
||||||
visible: type == MtxEvent.VideoMessage
|
|
||||||
clip: true
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
clip: true
|
||||||
fillMode: VideoOutput.PreserveAspectFit
|
fillMode: VideoOutput.PreserveAspectFit
|
||||||
//flushMode: VideoOutput.FirstFrame
|
//flushMode: VideoOutput.FirstFrame
|
||||||
orientation: mxcmedia.orientation
|
orientation: mxcmedia.orientation
|
||||||
|
visible: type == MtxEvent.VideoMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaControls {
|
MediaControls {
|
||||||
id: mediaControls
|
id: mediaControls
|
||||||
|
anchors.bottom: fileInfoLabel.top
|
||||||
anchors.left: content.left
|
anchors.left: content.left
|
||||||
anchors.right: content.right
|
anchors.right: content.right
|
||||||
anchors.bottom: fileInfoLabel.top
|
|
||||||
playingVideo: type == MtxEvent.VideoMessage
|
|
||||||
positionValue: mxcmedia.position
|
|
||||||
duration: mediaLoaded ? mxcmedia.duration : content.duration
|
duration: mediaLoaded ? mxcmedia.duration : content.duration
|
||||||
mediaLoaded: mxcmedia.loaded
|
mediaLoaded: mxcmedia.loaded
|
||||||
mediaState: mxcmedia.state
|
mediaState: mxcmedia.state
|
||||||
onPositionChanged: mxcmedia.position = position
|
playingVideo: type == MtxEvent.VideoMessage
|
||||||
onPlayPauseActivated: mxcmedia.state == MediaPlayer.PlayingState ? mxcmedia.pause() : mxcmedia.play()
|
positionValue: mxcmedia.position
|
||||||
|
|
||||||
onLoadActivated: mxcmedia.eventId = eventId
|
onLoadActivated: mxcmedia.eventId = eventId
|
||||||
|
onPlayPauseActivated: mxcmedia.state == MediaPlayer.PlayingState ? mxcmedia.pause() : mxcmedia.play()
|
||||||
|
onPositionChanged: mxcmedia.position = position
|
||||||
}
|
}
|
||||||
|
|
||||||
// information about file name and file size
|
// information about file name and file size
|
||||||
Label {
|
Label {
|
||||||
id: fileInfoLabel
|
id: fileInfoLabel
|
||||||
|
|
||||||
anchors.bottom: content.bottom
|
anchors.bottom: content.bottom
|
||||||
|
color: timelineRoot.palette.text
|
||||||
|
elide: Text.ElideRight
|
||||||
text: body + " [" + filesize + "]"
|
text: body + " [" + filesize + "]"
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
elide: Text.ElideRight
|
|
||||||
color: timelineRoot.palette.text
|
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: timelineRoot.palette.base
|
color: timelineRoot.palette.base
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,49 +1,49 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
import QtQuick.Layouts 1.15
|
import QtQuick.Layouts 1.15
|
||||||
import im.nheko
|
import im.nheko
|
||||||
|
|
||||||
Rectangle{
|
Rectangle {
|
||||||
|
property bool fitsMetadata: parent.width - redactedLayout.width > metadataWidth + 4
|
||||||
|
property int metadataWidth
|
||||||
|
|
||||||
|
color: timelineRoot.palette.alternateBase
|
||||||
height: redactedLayout.implicitHeight + Nheko.paddingSmall
|
height: redactedLayout.implicitHeight + Nheko.paddingSmall
|
||||||
implicitWidth: redactedLayout.implicitWidth + 2 * Nheko.paddingMedium
|
implicitWidth: redactedLayout.implicitWidth + 2 * Nheko.paddingMedium
|
||||||
width: Math.min(parent.width,implicitWidth+1)
|
|
||||||
radius: fontMetrics.lineSpacing / 2 + 2 * Nheko.paddingSmall
|
radius: fontMetrics.lineSpacing / 2 + 2 * Nheko.paddingSmall
|
||||||
color: timelineRoot.palette.alternateBase
|
width: Math.min(parent.width, implicitWidth + 1)
|
||||||
property int metadataWidth
|
|
||||||
property bool fitsMetadata: parent.width - redactedLayout.width > metadataWidth + 4
|
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: redactedLayout
|
id: redactedLayout
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
width: parent.width - 2 * Nheko.paddingMedium
|
|
||||||
spacing: Nheko.paddingSmall
|
spacing: Nheko.paddingSmall
|
||||||
|
width: parent.width - 2 * Nheko.paddingMedium
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: trashImg
|
id: trashImg
|
||||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||||
Layout.preferredWidth: fontMetrics.font.pixelSize
|
|
||||||
Layout.preferredHeight: fontMetrics.font.pixelSize
|
Layout.preferredHeight: fontMetrics.font.pixelSize
|
||||||
|
Layout.preferredWidth: fontMetrics.font.pixelSize
|
||||||
source: "image://colorimage/:/icons/icons/ui/delete.svg?" + timelineRoot.palette.text
|
source: "image://colorimage/:/icons/icons/ui/delete.svg?" + timelineRoot.palette.text
|
||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
id: redactedLabel
|
id: redactedLabel
|
||||||
Layout.margins: 0
|
|
||||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
|
||||||
Layout.preferredWidth: implicitWidth
|
|
||||||
Layout.fillWidth: true
|
|
||||||
property var redactedPair: room.formatRedactedEvent(eventId)
|
|
||||||
text: redactedPair["first"]
|
|
||||||
wrapMode: Label.WordWrap
|
|
||||||
color: timelineRoot.palette.text
|
|
||||||
|
|
||||||
|
property var redactedPair: room.formatRedactedEvent(eventId)
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.margins: 0
|
||||||
|
Layout.preferredWidth: implicitWidth
|
||||||
ToolTip.text: redactedPair["second"]
|
ToolTip.text: redactedPair["second"]
|
||||||
ToolTip.visible: hh.hovered
|
ToolTip.visible: hh.hovered
|
||||||
|
color: timelineRoot.palette.text
|
||||||
|
text: redactedPair["first"]
|
||||||
|
wrapMode: Label.WordWrap
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: hh
|
id: hh
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import Qt.labs.platform 1.1 as Platform
|
import Qt.labs.platform 1.1 as Platform
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
|
@ -14,129 +12,124 @@ import "../"
|
||||||
AbstractButton {
|
AbstractButton {
|
||||||
id: r
|
id: r
|
||||||
|
|
||||||
property color userColor: "red"
|
|
||||||
property double proportionalHeight
|
|
||||||
property int type
|
|
||||||
property string typeString
|
|
||||||
property int originalWidth
|
|
||||||
property string blurhash
|
property string blurhash
|
||||||
property string body
|
property string body
|
||||||
property string formattedBody
|
|
||||||
property string eventId
|
|
||||||
property string filename
|
|
||||||
property string filesize
|
|
||||||
property string url
|
|
||||||
property bool isOnlyEmoji
|
|
||||||
property bool isStateEvent
|
|
||||||
property string userId
|
|
||||||
property string userName
|
|
||||||
property string thumbnailUrl
|
|
||||||
property string roomTopic
|
|
||||||
property string roomName
|
|
||||||
property string callType
|
property string callType
|
||||||
property int duration
|
property int duration
|
||||||
property int encryptionError
|
property int encryptionError
|
||||||
property int relatedEventCacheBuster
|
property string eventId
|
||||||
|
property string filename
|
||||||
|
property string filesize
|
||||||
|
property string formattedBody
|
||||||
|
property bool isOnlyEmoji
|
||||||
|
property bool isStateEvent
|
||||||
property int maxWidth
|
property int maxWidth
|
||||||
|
property int originalWidth
|
||||||
|
property double proportionalHeight
|
||||||
|
property int relatedEventCacheBuster
|
||||||
|
property string roomName
|
||||||
|
property string roomTopic
|
||||||
|
property string thumbnailUrl
|
||||||
|
property int type
|
||||||
|
property string typeString
|
||||||
|
property string url
|
||||||
|
property color userColor: "red"
|
||||||
|
property string userId
|
||||||
|
property string userName
|
||||||
|
|
||||||
height: replyContainer.height
|
height: replyContainer.height
|
||||||
implicitHeight: replyContainer.height
|
implicitHeight: replyContainer.height
|
||||||
implicitWidth: visible? colorLine.width+Math.max(replyContainer.implicitWidth,userName_.fullTextWidth) : 0 // visible? seems to be causing issues
|
implicitWidth: visible ? colorLine.width + Math.max(replyContainer.implicitWidth, userName_.fullTextWidth) : 0 // visible? seems to be causing issues
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
let link = reply.child.linkAt != undefined && reply.child.linkAt(pressX - colorLine.width, pressY - userName_.implicitHeight);
|
||||||
|
if (link) {
|
||||||
|
Nheko.openLink(link);
|
||||||
|
} else {
|
||||||
|
room.showEvent(r.eventId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onPressAndHold: replyContextMenu.show(reply.child.copyText, reply.child.linkAt(pressX - colorLine.width, pressY - userName_.implicitHeight), r.eventId)
|
||||||
|
|
||||||
NhekoCursorShape {
|
NhekoCursorShape {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: colorLine
|
id: colorLine
|
||||||
|
|
||||||
anchors.top: replyContainer.top
|
|
||||||
anchors.bottom: replyContainer.bottom
|
anchors.bottom: replyContainer.bottom
|
||||||
width: 4
|
anchors.top: replyContainer.top
|
||||||
color: TimelineManager.userColor(userId, timelineRoot.palette.base)
|
color: TimelineManager.userColor(userId, timelineRoot.palette.base)
|
||||||
|
width: 4
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
let link = reply.child.linkAt != undefined && reply.child.linkAt(pressX-colorLine.width, pressY - userName_.implicitHeight);
|
|
||||||
if (link) {
|
|
||||||
Nheko.openLink(link)
|
|
||||||
} else {
|
|
||||||
room.showEvent(r.eventId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onPressAndHold: replyContextMenu.show(reply.child.copyText, reply.child.linkAt(pressX-colorLine.width, pressY - userName_.implicitHeight), r.eventId)
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: replyContainer
|
id: replyContainer
|
||||||
|
|
||||||
anchors.left: colorLine.right
|
anchors.left: colorLine.right
|
||||||
width: parent.width - 4
|
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
width: parent.width - 4
|
||||||
|
|
||||||
TapHandler {
|
TapHandler {
|
||||||
acceptedButtons: Qt.RightButton
|
acceptedButtons: Qt.RightButton
|
||||||
onSingleTapped: replyContextMenu.show(reply.child.copyText, reply.child.linkAt(eventPoint.position.x, eventPoint.position.y - userName_.implicitHeight), r.eventId)
|
|
||||||
gesturePolicy: TapHandler.ReleaseWithinBounds
|
|
||||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad
|
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad
|
||||||
}
|
gesturePolicy: TapHandler.ReleaseWithinBounds
|
||||||
|
|
||||||
|
onSingleTapped: replyContextMenu.show(reply.child.copyText, reply.child.linkAt(eventPoint.position.x, eventPoint.position.y - userName_.implicitHeight), r.eventId)
|
||||||
|
}
|
||||||
AbstractButton {
|
AbstractButton {
|
||||||
Layout.leftMargin: 4
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 4
|
||||||
|
|
||||||
contentItem: ElidedLabel {
|
contentItem: ElidedLabel {
|
||||||
id: userName_
|
id: userName_
|
||||||
fullText: userName
|
|
||||||
color: r.userColor
|
color: r.userColor
|
||||||
|
elideWidth: width
|
||||||
|
fullText: userName
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
width: parent.width
|
width: parent.width
|
||||||
elideWidth: width
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: room.openUserProfile(userId)
|
onClicked: room.openUserProfile(userId)
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageDelegate {
|
MessageDelegate {
|
||||||
|
id: reply
|
||||||
|
Layout.fillWidth: true
|
||||||
Layout.leftMargin: 4
|
Layout.leftMargin: 4
|
||||||
Layout.preferredHeight: height
|
Layout.preferredHeight: height
|
||||||
id: reply
|
|
||||||
blurhash: r.blurhash
|
blurhash: r.blurhash
|
||||||
body: r.body
|
body: r.body
|
||||||
formattedBody: r.formattedBody
|
callType: r.callType
|
||||||
|
duration: r.duration
|
||||||
|
// This is disabled so that left clicking the reply goes to its location
|
||||||
|
enabled: false
|
||||||
|
encryptionError: r.encryptionError
|
||||||
eventId: r.eventId
|
eventId: r.eventId
|
||||||
filename: r.filename
|
filename: r.filename
|
||||||
filesize: r.filesize
|
filesize: r.filesize
|
||||||
|
formattedBody: r.formattedBody
|
||||||
|
isOnlyEmoji: r.isOnlyEmoji
|
||||||
|
isReply: true
|
||||||
|
isStateEvent: r.isStateEvent
|
||||||
|
originalWidth: r.originalWidth
|
||||||
proportionalHeight: r.proportionalHeight
|
proportionalHeight: r.proportionalHeight
|
||||||
|
relatedEventCacheBuster: r.relatedEventCacheBuster
|
||||||
|
roomName: r.roomName
|
||||||
|
roomTopic: r.roomTopic
|
||||||
|
thumbnailUrl: r.thumbnailUrl
|
||||||
type: r.type
|
type: r.type
|
||||||
typeString: r.typeString ?? ""
|
typeString: r.typeString ?? ""
|
||||||
url: r.url
|
url: r.url
|
||||||
thumbnailUrl: r.thumbnailUrl
|
|
||||||
duration: r.duration
|
|
||||||
originalWidth: r.originalWidth
|
|
||||||
isOnlyEmoji: r.isOnlyEmoji
|
|
||||||
isStateEvent: r.isStateEvent
|
|
||||||
userId: r.userId
|
userId: r.userId
|
||||||
userName: r.userName
|
userName: r.userName
|
||||||
roomTopic: r.roomTopic
|
|
||||||
roomName: r.roomName
|
|
||||||
callType: r.callType
|
|
||||||
relatedEventCacheBuster: r.relatedEventCacheBuster
|
|
||||||
encryptionError: r.encryptionError
|
|
||||||
// This is disabled so that left clicking the reply goes to its location
|
|
||||||
enabled: false
|
|
||||||
Layout.fillWidth: true
|
|
||||||
isReply: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: backgroundItem
|
id: backgroundItem
|
||||||
|
|
||||||
z: -1
|
|
||||||
anchors.fill: replyContainer
|
|
||||||
property color userColor: TimelineManager.userColor(userId, timelineRoot.palette.base)
|
|
||||||
property color bgColor: timelineRoot.palette.base
|
property color bgColor: timelineRoot.palette.base
|
||||||
color: Qt.tint(bgColor, Qt.hsla(userColor.hslHue, 0.5, userColor.hslLightness, 0.1))
|
property color userColor: TimelineManager.userColor(userId, timelineRoot.palette.base)
|
||||||
}
|
|
||||||
|
|
||||||
|
anchors.fill: replyContainer
|
||||||
|
color: Qt.tint(bgColor, Qt.hsla(userColor.hslHue, 0.5, userColor.hslLightness, 0.1))
|
||||||
|
z: -1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,25 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
import "../"
|
||||||
import ".."
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import im.nheko
|
import im.nheko
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
required property string body
|
required property string body
|
||||||
|
property string copyText: selectedText ? getText(selectionStart, selectionEnd) : body
|
||||||
|
property bool fitsMetadata: positionAt(width, height - 4) == positionAt(width - metadataWidth - 10, height - 4)
|
||||||
|
required property string formatted
|
||||||
required property bool isOnlyEmoji
|
required property bool isOnlyEmoji
|
||||||
required property bool isReply
|
required property bool isReply
|
||||||
required property string formatted
|
|
||||||
property string copyText: selectedText ? getText(selectionStart, selectionEnd) : body
|
|
||||||
property int metadataWidth
|
property int metadataWidth
|
||||||
property bool fitsMetadata: positionAt(width,height-4) == positionAt(width-metadataWidth-10, height-4)
|
|
||||||
|
clip: isReply
|
||||||
|
enabled: !Settings.mobileMode
|
||||||
|
font.pointSize: (Settings.enlargeEmojiOnlyMessages && isOnlyEmoji > 0 && isOnlyEmoji < 4) ? Settings.fontSize * 3 : Settings.fontSize
|
||||||
|
height: isReply ? Math.round(Math.min(timelineView.height / 8, implicitHeight)) : implicitHeight
|
||||||
|
selectByMouse: !Settings.mobileMode && !isReply
|
||||||
|
|
||||||
// table border-collapse doesn't seem to work
|
// table border-collapse doesn't seem to work
|
||||||
text: "
|
text: "
|
||||||
|
@ -37,16 +41,10 @@ MatrixText {
|
||||||
</style>
|
</style>
|
||||||
" + formatted.replace(/<pre>/g, "<pre style='white-space: pre-wrap; background-color: " + timelineRoot.palette.alternateBase + "'>").replace(/<del>/g, "<s>").replace(/<\/del>/g, "</s>").replace(/<strike>/g, "<s>").replace(/<\/strike>/g, "</s>")
|
" + formatted.replace(/<pre>/g, "<pre style='white-space: pre-wrap; background-color: " + timelineRoot.palette.alternateBase + "'>").replace(/<del>/g, "<s>").replace(/<\/del>/g, "</s>").replace(/<strike>/g, "<s>").replace(/<\/strike>/g, "</s>")
|
||||||
width: parent?.width
|
width: parent?.width
|
||||||
height: isReply ? Math.round(Math.min(timelineView.height / 8, implicitHeight)) : implicitHeight
|
|
||||||
clip: isReply
|
|
||||||
selectByMouse: !Settings.mobileMode && !isReply
|
|
||||||
enabled: !Settings.mobileMode
|
|
||||||
font.pointSize: (Settings.enlargeEmojiOnlyMessages && isOnlyEmoji > 0 && isOnlyEmoji < 4) ? Settings.fontSize * 3 : Settings.fontSize
|
|
||||||
|
|
||||||
NhekoCursorShape {
|
NhekoCursorShape {
|
||||||
enabled: isReply
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
enabled: isReply
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.10
|
import QtQuick 2.10
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import QtQuick.Window 2.13
|
import QtQuick.Window 2.13
|
||||||
|
@ -13,74 +11,56 @@ ApplicationWindow {
|
||||||
|
|
||||||
property var flow
|
property var flow
|
||||||
|
|
||||||
onClosing: VerificationManager.removeVerificationFlow(flow)
|
color: timelineRoot.palette.window
|
||||||
title: stack.currentItem.title_
|
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
|
minimumHeight: stack.implicitHeight
|
||||||
modality: Qt.NonModal
|
modality: Qt.NonModal
|
||||||
palette: timelineRoot.palette
|
palette: timelineRoot.palette
|
||||||
color: timelineRoot.palette.window
|
title: stack.currentItem.title_
|
||||||
minimumHeight: stack.implicitHeight
|
|
||||||
width: stack.implicitWidth
|
width: stack.implicitWidth
|
||||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
|
||||||
|
onClosing: VerificationManager.removeVerificationFlow(flow)
|
||||||
|
|
||||||
StackView {
|
StackView {
|
||||||
id: stack
|
id: stack
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
initialItem: newVerificationRequest
|
|
||||||
implicitWidth: currentItem.implicitWidth
|
|
||||||
implicitHeight: currentItem.implicitHeight
|
implicitHeight: currentItem.implicitHeight
|
||||||
|
implicitWidth: currentItem.implicitWidth
|
||||||
|
initialItem: newVerificationRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: newVerificationRequest
|
id: newVerificationRequest
|
||||||
|
|
||||||
NewVerificationRequest {
|
NewVerificationRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: waiting
|
id: waiting
|
||||||
|
|
||||||
Waiting {
|
Waiting {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: success
|
id: success
|
||||||
|
|
||||||
Success {
|
Success {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: failed
|
id: failed
|
||||||
|
|
||||||
Failed {
|
Failed {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: digitVerification
|
id: digitVerification
|
||||||
|
|
||||||
DigitVerification {
|
DigitVerification {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: emojiVerification
|
id: emojiVerification
|
||||||
|
|
||||||
EmojiVerification {
|
EmojiVerification {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
state: flow.state
|
state: flow.state
|
||||||
|
|
||||||
states: [
|
states: [
|
||||||
State {
|
State {
|
||||||
name: "PromptStartVerification"
|
name: "PromptStartVerification"
|
||||||
|
@ -88,7 +68,6 @@ ApplicationWindow {
|
||||||
StateChangeScript {
|
StateChangeScript {
|
||||||
script: stack.replace(newVerificationRequest)
|
script: stack.replace(newVerificationRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "CompareEmoji"
|
name: "CompareEmoji"
|
||||||
|
@ -96,7 +75,6 @@ ApplicationWindow {
|
||||||
StateChangeScript {
|
StateChangeScript {
|
||||||
script: stack.replace(emojiVerification)
|
script: stack.replace(emojiVerification)
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "CompareNumber"
|
name: "CompareNumber"
|
||||||
|
@ -104,7 +82,6 @@ ApplicationWindow {
|
||||||
StateChangeScript {
|
StateChangeScript {
|
||||||
script: stack.replace(digitVerification)
|
script: stack.replace(digitVerification)
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "WaitingForKeys"
|
name: "WaitingForKeys"
|
||||||
|
@ -112,7 +89,6 @@ ApplicationWindow {
|
||||||
StateChangeScript {
|
StateChangeScript {
|
||||||
script: stack.replace(waiting)
|
script: stack.replace(waiting)
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "WaitingForOtherToAccept"
|
name: "WaitingForOtherToAccept"
|
||||||
|
@ -120,7 +96,6 @@ ApplicationWindow {
|
||||||
StateChangeScript {
|
StateChangeScript {
|
||||||
script: stack.replace(waiting)
|
script: stack.replace(waiting)
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "WaitingForMac"
|
name: "WaitingForMac"
|
||||||
|
@ -128,7 +103,6 @@ ApplicationWindow {
|
||||||
StateChangeScript {
|
StateChangeScript {
|
||||||
script: stack.replace(waiting)
|
script: stack.replace(waiting)
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "Success"
|
name: "Success"
|
||||||
|
@ -136,7 +110,6 @@ ApplicationWindow {
|
||||||
StateChangeScript {
|
StateChangeScript {
|
||||||
script: stack.replace(success)
|
script: stack.replace(success)
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "Failed"
|
name: "Failed"
|
||||||
|
@ -144,9 +117,7 @@ ApplicationWindow {
|
||||||
StateChangeScript {
|
StateChangeScript {
|
||||||
script: stack.replace(failed)
|
script: stack.replace(failed)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.3
|
import QtQuick 2.3
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import QtQuick.Layouts 1.10
|
import QtQuick.Layouts 1.10
|
||||||
|
@ -10,6 +8,7 @@ import im.nheko
|
||||||
|
|
||||||
Pane {
|
Pane {
|
||||||
property string title: qsTr("Verification Code")
|
property string title: qsTr("Verification Code")
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: timelineRoot.palette.window
|
color: timelineRoot.palette.window
|
||||||
}
|
}
|
||||||
|
@ -19,61 +18,57 @@ Pane {
|
||||||
spacing: 16
|
spacing: 16
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.preferredWidth: 400
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
wrapMode: Text.Wrap
|
Layout.preferredWidth: 400
|
||||||
text: qsTr("Please verify the following digits. You should see the same numbers on both sides. If they differ, please press 'They do not match!' to abort verification!")
|
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
|
text: qsTr("Please verify the following digits. You should see the same numbers on both sides. If they differ, please press 'They do not match!' to abort verification!")
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Item { Layout.fillHeight: true; }
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
color: timelineRoot.palette.text
|
||||||
font.pixelSize: Qt.application.font.pixelSize * 2
|
font.pixelSize: Qt.application.font.pixelSize * 2
|
||||||
text: flow.sasList[0]
|
text: flow.sasList[0]
|
||||||
color: timelineRoot.palette.text
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
color: timelineRoot.palette.text
|
||||||
font.pixelSize: Qt.application.font.pixelSize * 2
|
font.pixelSize: Qt.application.font.pixelSize * 2
|
||||||
text: flow.sasList[1]
|
text: flow.sasList[1]
|
||||||
color: timelineRoot.palette.text
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
color: timelineRoot.palette.text
|
||||||
font.pixelSize: Qt.application.font.pixelSize * 2
|
font.pixelSize: Qt.application.font.pixelSize * 2
|
||||||
text: flow.sasList[2]
|
text: flow.sasList[2]
|
||||||
color: timelineRoot.palette.text
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Item { Layout.fillHeight: true; }
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Button {
|
Button {
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
text: qsTr("They do not match!")
|
text: qsTr("They do not match!")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
flow.cancel();
|
flow.cancel();
|
||||||
dialog.close();
|
dialog.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
text: qsTr("They match!")
|
text: qsTr("They match!")
|
||||||
|
|
||||||
onClicked: flow.next()
|
onClicked: flow.next()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,15 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.3
|
import QtQuick 2.3
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import QtQuick.Layouts 1.10
|
import QtQuick.Layouts 1.10
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
color: "red"
|
color: "red"
|
||||||
|
height: Qt.application.font.pixelSize * 4
|
||||||
implicitHeight: Qt.application.font.pixelSize * 4
|
implicitHeight: Qt.application.font.pixelSize * 4
|
||||||
implicitWidth: col.width
|
implicitWidth: col.width
|
||||||
height: Qt.application.font.pixelSize * 4
|
|
||||||
width: col.width
|
width: col.width
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
|
@ -22,17 +20,14 @@ Rectangle {
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
height: font.pixelSize * 2
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
text: col.emoji.emoji
|
|
||||||
font.pixelSize: Qt.application.font.pixelSize * 2
|
font.pixelSize: Qt.application.font.pixelSize * 2
|
||||||
|
height: font.pixelSize * 2
|
||||||
|
text: col.emoji.emoji
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
||||||
text: col.emoji.description
|
text: col.emoji.description
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.3
|
import QtQuick 2.3
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import QtQuick.Layouts 1.10
|
import QtQuick.Layouts 1.10
|
||||||
|
@ -10,6 +8,7 @@ import im.nheko
|
||||||
|
|
||||||
Pane {
|
Pane {
|
||||||
property string title: qsTr("Verification Code")
|
property string title: qsTr("Verification Code")
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: timelineRoot.palette.window
|
color: timelineRoot.palette.window
|
||||||
}
|
}
|
||||||
|
@ -19,345 +18,345 @@ Pane {
|
||||||
spacing: 16
|
spacing: 16
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.preferredWidth: 400
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
wrapMode: Text.Wrap
|
Layout.preferredWidth: 400
|
||||||
text: qsTr("Please verify the following emoji. You should see the same emoji on both sides. If they differ, please press 'They do not match!' to abort verification!")
|
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
|
text: qsTr("Please verify the following emoji. You should see the same emoji on both sides. If they differ, please press 'They do not match!' to abort verification!")
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Item { Layout.fillHeight: true; }
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: emojis
|
id: emojis
|
||||||
|
|
||||||
property var mapping: [{
|
property var mapping: [{
|
||||||
"number": 0,
|
"number": 0,
|
||||||
"emoji": "🐶",
|
"emoji": "🐶",
|
||||||
"description": "Dog",
|
"description": "Dog",
|
||||||
"unicode": "U+1F436"
|
"unicode": "U+1F436"
|
||||||
}, {
|
}, {
|
||||||
"number": 1,
|
"number": 1,
|
||||||
"emoji": "🐱",
|
"emoji": "🐱",
|
||||||
"description": "Cat",
|
"description": "Cat",
|
||||||
"unicode": "U+1F431"
|
"unicode": "U+1F431"
|
||||||
}, {
|
}, {
|
||||||
"number": 2,
|
"number": 2,
|
||||||
"emoji": "🦁",
|
"emoji": "🦁",
|
||||||
"description": "Lion",
|
"description": "Lion",
|
||||||
"unicode": "U+1F981"
|
"unicode": "U+1F981"
|
||||||
}, {
|
}, {
|
||||||
"number": 3,
|
"number": 3,
|
||||||
"emoji": "🐎",
|
"emoji": "🐎",
|
||||||
"description": "Horse",
|
"description": "Horse",
|
||||||
"unicode": "U+1F40E"
|
"unicode": "U+1F40E"
|
||||||
}, {
|
}, {
|
||||||
"number": 4,
|
"number": 4,
|
||||||
"emoji": "🦄",
|
"emoji": "🦄",
|
||||||
"description": "Unicorn",
|
"description": "Unicorn",
|
||||||
"unicode": "U+1F984"
|
"unicode": "U+1F984"
|
||||||
}, {
|
}, {
|
||||||
"number": 5,
|
"number": 5,
|
||||||
"emoji": "🐷",
|
"emoji": "🐷",
|
||||||
"description": "Pig",
|
"description": "Pig",
|
||||||
"unicode": "U+1F437"
|
"unicode": "U+1F437"
|
||||||
}, {
|
}, {
|
||||||
"number": 6,
|
"number": 6,
|
||||||
"emoji": "🐘",
|
"emoji": "🐘",
|
||||||
"description": "Elephant",
|
"description": "Elephant",
|
||||||
"unicode": "U+1F418"
|
"unicode": "U+1F418"
|
||||||
}, {
|
}, {
|
||||||
"number": 7,
|
"number": 7,
|
||||||
"emoji": "🐰",
|
"emoji": "🐰",
|
||||||
"description": "Rabbit",
|
"description": "Rabbit",
|
||||||
"unicode": "U+1F430"
|
"unicode": "U+1F430"
|
||||||
}, {
|
}, {
|
||||||
"number": 8,
|
"number": 8,
|
||||||
"emoji": "🐼",
|
"emoji": "🐼",
|
||||||
"description": "Panda",
|
"description": "Panda",
|
||||||
"unicode": "U+1F43C"
|
"unicode": "U+1F43C"
|
||||||
}, {
|
}, {
|
||||||
"number": 9,
|
"number": 9,
|
||||||
"emoji": "🐓",
|
"emoji": "🐓",
|
||||||
"description": "Rooster",
|
"description": "Rooster",
|
||||||
"unicode": "U+1F413"
|
"unicode": "U+1F413"
|
||||||
}, {
|
}, {
|
||||||
"number": 10,
|
"number": 10,
|
||||||
"emoji": "🐧",
|
"emoji": "🐧",
|
||||||
"description": "Penguin",
|
"description": "Penguin",
|
||||||
"unicode": "U+1F427"
|
"unicode": "U+1F427"
|
||||||
}, {
|
}, {
|
||||||
"number": 11,
|
"number": 11,
|
||||||
"emoji": "🐢",
|
"emoji": "🐢",
|
||||||
"description": "Turtle",
|
"description": "Turtle",
|
||||||
"unicode": "U+1F422"
|
"unicode": "U+1F422"
|
||||||
}, {
|
}, {
|
||||||
"number": 12,
|
"number": 12,
|
||||||
"emoji": "🐟",
|
"emoji": "🐟",
|
||||||
"description": "Fish",
|
"description": "Fish",
|
||||||
"unicode": "U+1F41F"
|
"unicode": "U+1F41F"
|
||||||
}, {
|
}, {
|
||||||
"number": 13,
|
"number": 13,
|
||||||
"emoji": "🐙",
|
"emoji": "🐙",
|
||||||
"description": "Octopus",
|
"description": "Octopus",
|
||||||
"unicode": "U+1F419"
|
"unicode": "U+1F419"
|
||||||
}, {
|
}, {
|
||||||
"number": 14,
|
"number": 14,
|
||||||
"emoji": "🦋",
|
"emoji": "🦋",
|
||||||
"description": "Butterfly",
|
"description": "Butterfly",
|
||||||
"unicode": "U+1F98B"
|
"unicode": "U+1F98B"
|
||||||
}, {
|
}, {
|
||||||
"number": 15,
|
"number": 15,
|
||||||
"emoji": "🌷",
|
"emoji": "🌷",
|
||||||
"description": "Flower",
|
"description": "Flower",
|
||||||
"unicode": "U+1F337"
|
"unicode": "U+1F337"
|
||||||
}, {
|
}, {
|
||||||
"number": 16,
|
"number": 16,
|
||||||
"emoji": "🌳",
|
"emoji": "🌳",
|
||||||
"description": "Tree",
|
"description": "Tree",
|
||||||
"unicode": "U+1F333"
|
"unicode": "U+1F333"
|
||||||
}, {
|
}, {
|
||||||
"number": 17,
|
"number": 17,
|
||||||
"emoji": "🌵",
|
"emoji": "🌵",
|
||||||
"description": "Cactus",
|
"description": "Cactus",
|
||||||
"unicode": "U+1F335"
|
"unicode": "U+1F335"
|
||||||
}, {
|
}, {
|
||||||
"number": 18,
|
"number": 18,
|
||||||
"emoji": "🍄",
|
"emoji": "🍄",
|
||||||
"description": "Mushroom",
|
"description": "Mushroom",
|
||||||
"unicode": "U+1F344"
|
"unicode": "U+1F344"
|
||||||
}, {
|
}, {
|
||||||
"number": 19,
|
"number": 19,
|
||||||
"emoji": "🌏",
|
"emoji": "🌏",
|
||||||
"description": "Globe",
|
"description": "Globe",
|
||||||
"unicode": "U+1F30F"
|
"unicode": "U+1F30F"
|
||||||
}, {
|
}, {
|
||||||
"number": 20,
|
"number": 20,
|
||||||
"emoji": "🌙",
|
"emoji": "🌙",
|
||||||
"description": "Moon",
|
"description": "Moon",
|
||||||
"unicode": "U+1F319"
|
"unicode": "U+1F319"
|
||||||
}, {
|
}, {
|
||||||
"number": 21,
|
"number": 21,
|
||||||
"emoji": "☁️",
|
"emoji": "☁️",
|
||||||
"description": "Cloud",
|
"description": "Cloud",
|
||||||
"unicode": "U+2601U+FE0F"
|
"unicode": "U+2601U+FE0F"
|
||||||
}, {
|
}, {
|
||||||
"number": 22,
|
"number": 22,
|
||||||
"emoji": "🔥",
|
"emoji": "🔥",
|
||||||
"description": "Fire",
|
"description": "Fire",
|
||||||
"unicode": "U+1F525"
|
"unicode": "U+1F525"
|
||||||
}, {
|
}, {
|
||||||
"number": 23,
|
"number": 23,
|
||||||
"emoji": "🍌",
|
"emoji": "🍌",
|
||||||
"description": "Banana",
|
"description": "Banana",
|
||||||
"unicode": "U+1F34C"
|
"unicode": "U+1F34C"
|
||||||
}, {
|
}, {
|
||||||
"number": 24,
|
"number": 24,
|
||||||
"emoji": "🍎",
|
"emoji": "🍎",
|
||||||
"description": "Apple",
|
"description": "Apple",
|
||||||
"unicode": "U+1F34E"
|
"unicode": "U+1F34E"
|
||||||
}, {
|
}, {
|
||||||
"number": 25,
|
"number": 25,
|
||||||
"emoji": "🍓",
|
"emoji": "🍓",
|
||||||
"description": "Strawberry",
|
"description": "Strawberry",
|
||||||
"unicode": "U+1F353"
|
"unicode": "U+1F353"
|
||||||
}, {
|
}, {
|
||||||
"number": 26,
|
"number": 26,
|
||||||
"emoji": "🌽",
|
"emoji": "🌽",
|
||||||
"description": "Corn",
|
"description": "Corn",
|
||||||
"unicode": "U+1F33D"
|
"unicode": "U+1F33D"
|
||||||
}, {
|
}, {
|
||||||
"number": 27,
|
"number": 27,
|
||||||
"emoji": "🍕",
|
"emoji": "🍕",
|
||||||
"description": "Pizza",
|
"description": "Pizza",
|
||||||
"unicode": "U+1F355"
|
"unicode": "U+1F355"
|
||||||
}, {
|
}, {
|
||||||
"number": 28,
|
"number": 28,
|
||||||
"emoji": "🎂",
|
"emoji": "🎂",
|
||||||
"description": "Cake",
|
"description": "Cake",
|
||||||
"unicode": "U+1F382"
|
"unicode": "U+1F382"
|
||||||
}, {
|
}, {
|
||||||
"number": 29,
|
"number": 29,
|
||||||
"emoji": "❤️",
|
"emoji": "❤️",
|
||||||
"description": "Heart",
|
"description": "Heart",
|
||||||
"unicode": "U+2764U+FE0F"
|
"unicode": "U+2764U+FE0F"
|
||||||
}, {
|
}, {
|
||||||
"number": 30,
|
"number": 30,
|
||||||
"emoji": "😀",
|
"emoji": "😀",
|
||||||
"description": "Smiley",
|
"description": "Smiley",
|
||||||
"unicode": "U+1F600"
|
"unicode": "U+1F600"
|
||||||
}, {
|
}, {
|
||||||
"number": 31,
|
"number": 31,
|
||||||
"emoji": "🤖",
|
"emoji": "🤖",
|
||||||
"description": "Robot",
|
"description": "Robot",
|
||||||
"unicode": "U+1F916"
|
"unicode": "U+1F916"
|
||||||
}, {
|
}, {
|
||||||
"number": 32,
|
"number": 32,
|
||||||
"emoji": "🎩",
|
"emoji": "🎩",
|
||||||
"description": "Hat",
|
"description": "Hat",
|
||||||
"unicode": "U+1F3A9"
|
"unicode": "U+1F3A9"
|
||||||
}, {
|
}, {
|
||||||
"number": 33,
|
"number": 33,
|
||||||
"emoji": "👓",
|
"emoji": "👓",
|
||||||
"description": "Glasses",
|
"description": "Glasses",
|
||||||
"unicode": "U+1F453"
|
"unicode": "U+1F453"
|
||||||
}, {
|
}, {
|
||||||
"number": 34,
|
"number": 34,
|
||||||
"emoji": "🔧",
|
"emoji": "🔧",
|
||||||
"description": "Spanner",
|
"description": "Spanner",
|
||||||
"unicode": "U+1F527"
|
"unicode": "U+1F527"
|
||||||
}, {
|
}, {
|
||||||
"number": 35,
|
"number": 35,
|
||||||
"emoji": "🎅",
|
"emoji": "🎅",
|
||||||
"description": "Santa",
|
"description": "Santa",
|
||||||
"unicode": "U+1F385"
|
"unicode": "U+1F385"
|
||||||
}, {
|
}, {
|
||||||
"number": 36,
|
"number": 36,
|
||||||
"emoji": "👍",
|
"emoji": "👍",
|
||||||
"description": "Thumbs Up",
|
"description": "Thumbs Up",
|
||||||
"unicode": "U+1F44D"
|
"unicode": "U+1F44D"
|
||||||
}, {
|
}, {
|
||||||
"number": 37,
|
"number": 37,
|
||||||
"emoji": "☂️",
|
"emoji": "☂️",
|
||||||
"description": "Umbrella",
|
"description": "Umbrella",
|
||||||
"unicode": "U+2602U+FE0F"
|
"unicode": "U+2602U+FE0F"
|
||||||
}, {
|
}, {
|
||||||
"number": 38,
|
"number": 38,
|
||||||
"emoji": "⌛",
|
"emoji": "⌛",
|
||||||
"description": "Hourglass",
|
"description": "Hourglass",
|
||||||
"unicode": "U+231B"
|
"unicode": "U+231B"
|
||||||
}, {
|
}, {
|
||||||
"number": 39,
|
"number": 39,
|
||||||
"emoji": "⏰",
|
"emoji": "⏰",
|
||||||
"description": "Clock",
|
"description": "Clock",
|
||||||
"unicode": "U+23F0"
|
"unicode": "U+23F0"
|
||||||
}, {
|
}, {
|
||||||
"number": 40,
|
"number": 40,
|
||||||
"emoji": "🎁",
|
"emoji": "🎁",
|
||||||
"description": "Gift",
|
"description": "Gift",
|
||||||
"unicode": "U+1F381"
|
"unicode": "U+1F381"
|
||||||
}, {
|
}, {
|
||||||
"number": 41,
|
"number": 41,
|
||||||
"emoji": "💡",
|
"emoji": "💡",
|
||||||
"description": "Light Bulb",
|
"description": "Light Bulb",
|
||||||
"unicode": "U+1F4A1"
|
"unicode": "U+1F4A1"
|
||||||
}, {
|
}, {
|
||||||
"number": 42,
|
"number": 42,
|
||||||
"emoji": "📕",
|
"emoji": "📕",
|
||||||
"description": "Book",
|
"description": "Book",
|
||||||
"unicode": "U+1F4D5"
|
"unicode": "U+1F4D5"
|
||||||
}, {
|
}, {
|
||||||
"number": 43,
|
"number": 43,
|
||||||
"emoji": "✏️",
|
"emoji": "✏️",
|
||||||
"description": "Pencil",
|
"description": "Pencil",
|
||||||
"unicode": "U+270FU+FE0F"
|
"unicode": "U+270FU+FE0F"
|
||||||
}, {
|
}, {
|
||||||
"number": 44,
|
"number": 44,
|
||||||
"emoji": "📎",
|
"emoji": "📎",
|
||||||
"description": "Paperclip",
|
"description": "Paperclip",
|
||||||
"unicode": "U+1F4CE"
|
"unicode": "U+1F4CE"
|
||||||
}, {
|
}, {
|
||||||
"number": 45,
|
"number": 45,
|
||||||
"emoji": "✂️",
|
"emoji": "✂️",
|
||||||
"description": "Scissors",
|
"description": "Scissors",
|
||||||
"unicode": "U+2702U+FE0F"
|
"unicode": "U+2702U+FE0F"
|
||||||
}, {
|
}, {
|
||||||
"number": 46,
|
"number": 46,
|
||||||
"emoji": "🔒",
|
"emoji": "🔒",
|
||||||
"description": "Lock",
|
"description": "Lock",
|
||||||
"unicode": "U+1F512"
|
"unicode": "U+1F512"
|
||||||
}, {
|
}, {
|
||||||
"number": 47,
|
"number": 47,
|
||||||
"emoji": "🔑",
|
"emoji": "🔑",
|
||||||
"description": "Key",
|
"description": "Key",
|
||||||
"unicode": "U+1F511"
|
"unicode": "U+1F511"
|
||||||
}, {
|
}, {
|
||||||
"number": 48,
|
"number": 48,
|
||||||
"emoji": "🔨",
|
"emoji": "🔨",
|
||||||
"description": "Hammer",
|
"description": "Hammer",
|
||||||
"unicode": "U+1F528"
|
"unicode": "U+1F528"
|
||||||
}, {
|
}, {
|
||||||
"number": 49,
|
"number": 49,
|
||||||
"emoji": "☎️",
|
"emoji": "☎️",
|
||||||
"description": "Telephone",
|
"description": "Telephone",
|
||||||
"unicode": "U+260EU+FE0F"
|
"unicode": "U+260EU+FE0F"
|
||||||
}, {
|
}, {
|
||||||
"number": 50,
|
"number": 50,
|
||||||
"emoji": "🏁",
|
"emoji": "🏁",
|
||||||
"description": "Flag",
|
"description": "Flag",
|
||||||
"unicode": "U+1F3C1"
|
"unicode": "U+1F3C1"
|
||||||
}, {
|
}, {
|
||||||
"number": 51,
|
"number": 51,
|
||||||
"emoji": "🚂",
|
"emoji": "🚂",
|
||||||
"description": "Train",
|
"description": "Train",
|
||||||
"unicode": "U+1F682"
|
"unicode": "U+1F682"
|
||||||
}, {
|
}, {
|
||||||
"number": 52,
|
"number": 52,
|
||||||
"emoji": "🚲",
|
"emoji": "🚲",
|
||||||
"description": "Bicycle",
|
"description": "Bicycle",
|
||||||
"unicode": "U+1F6B2"
|
"unicode": "U+1F6B2"
|
||||||
}, {
|
}, {
|
||||||
"number": 53,
|
"number": 53,
|
||||||
"emoji": "✈️",
|
"emoji": "✈️",
|
||||||
"description": "Aeroplane",
|
"description": "Aeroplane",
|
||||||
"unicode": "U+2708U+FE0F"
|
"unicode": "U+2708U+FE0F"
|
||||||
}, {
|
}, {
|
||||||
"number": 54,
|
"number": 54,
|
||||||
"emoji": "🚀",
|
"emoji": "🚀",
|
||||||
"description": "Rocket",
|
"description": "Rocket",
|
||||||
"unicode": "U+1F680"
|
"unicode": "U+1F680"
|
||||||
}, {
|
}, {
|
||||||
"number": 55,
|
"number": 55,
|
||||||
"emoji": "🏆",
|
"emoji": "🏆",
|
||||||
"description": "Trophy",
|
"description": "Trophy",
|
||||||
"unicode": "U+1F3C6"
|
"unicode": "U+1F3C6"
|
||||||
}, {
|
}, {
|
||||||
"number": 56,
|
"number": 56,
|
||||||
"emoji": "⚽",
|
"emoji": "⚽",
|
||||||
"description": "Ball",
|
"description": "Ball",
|
||||||
"unicode": "U+26BD"
|
"unicode": "U+26BD"
|
||||||
}, {
|
}, {
|
||||||
"number": 57,
|
"number": 57,
|
||||||
"emoji": "🎸",
|
"emoji": "🎸",
|
||||||
"description": "Guitar",
|
"description": "Guitar",
|
||||||
"unicode": "U+1F3B8"
|
"unicode": "U+1F3B8"
|
||||||
}, {
|
}, {
|
||||||
"number": 58,
|
"number": 58,
|
||||||
"emoji": "🎺",
|
"emoji": "🎺",
|
||||||
"description": "Trumpet",
|
"description": "Trumpet",
|
||||||
"unicode": "U+1F3BA"
|
"unicode": "U+1F3BA"
|
||||||
}, {
|
}, {
|
||||||
"number": 59,
|
"number": 59,
|
||||||
"emoji": "🔔",
|
"emoji": "🔔",
|
||||||
"description": "Bell",
|
"description": "Bell",
|
||||||
"unicode": "U+1F514"
|
"unicode": "U+1F514"
|
||||||
}, {
|
}, {
|
||||||
"number": 60,
|
"number": 60,
|
||||||
"emoji": "⚓",
|
"emoji": "⚓",
|
||||||
"description": "Anchor",
|
"description": "Anchor",
|
||||||
"unicode": "U+2693"
|
"unicode": "U+2693"
|
||||||
}, {
|
}, {
|
||||||
"number": 61,
|
"number": 61,
|
||||||
"emoji": "🎧",
|
"emoji": "🎧",
|
||||||
"description": "Headphones",
|
"description": "Headphones",
|
||||||
"unicode": "U+1F3A7"
|
"unicode": "U+1F3A7"
|
||||||
}, {
|
}, {
|
||||||
"number": 62,
|
"number": 62,
|
||||||
"emoji": "📁",
|
"emoji": "📁",
|
||||||
"description": "Folder",
|
"description": "Folder",
|
||||||
"unicode": "U+1F4C1"
|
"unicode": "U+1F4C1"
|
||||||
}, {
|
}, {
|
||||||
"number": 63,
|
"number": 63,
|
||||||
"emoji": "📌",
|
"emoji": "📌",
|
||||||
"description": "Pin",
|
"description": "Pin",
|
||||||
"unicode": "U+1F4CC"
|
"unicode": "U+1F4CC"
|
||||||
}]
|
}]
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
id: repeater
|
id: repeater
|
||||||
|
|
||||||
model: 7
|
model: 7
|
||||||
|
|
||||||
delegate: Rectangle {
|
delegate: Rectangle {
|
||||||
|
@ -376,49 +375,42 @@ Pane {
|
||||||
Label {
|
Label {
|
||||||
//height: font.pixelSize * 2
|
//height: font.pixelSize * 2
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
text: col.emoji.emoji
|
|
||||||
font.pixelSize: Qt.application.font.pixelSize * 2
|
|
||||||
font.family: Settings.emojiFont
|
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
|
font.family: Settings.emojiFont
|
||||||
|
font.pixelSize: Qt.application.font.pixelSize * 2
|
||||||
|
text: col.emoji.emoji
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
||||||
text: col.emoji.description
|
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
|
text: col.emoji.description
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Item { Layout.fillHeight: true; }
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Button {
|
Button {
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
text: qsTr("They do not match!")
|
text: qsTr("They do not match!")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
flow.cancel();
|
flow.cancel();
|
||||||
dialog.close();
|
dialog.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
text: qsTr("They match!")
|
text: qsTr("They match!")
|
||||||
|
|
||||||
onClicked: flow.next()
|
onClicked: flow.next()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.3
|
import QtQuick 2.3
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import QtQuick.Layouts 1.10
|
import QtQuick.Layouts 1.10
|
||||||
|
@ -10,6 +8,7 @@ import im.nheko
|
||||||
|
|
||||||
Pane {
|
Pane {
|
||||||
property string title: qsTr("Verification failed")
|
property string title: qsTr("Verification failed")
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: timelineRoot.palette.window
|
color: timelineRoot.palette.window
|
||||||
}
|
}
|
||||||
|
@ -20,10 +19,9 @@ Pane {
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: content
|
id: content
|
||||||
|
|
||||||
Layout.preferredWidth: 400
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
wrapMode: Text.Wrap
|
Layout.preferredWidth: 400
|
||||||
|
color: timelineRoot.palette.text
|
||||||
text: {
|
text: {
|
||||||
switch (flow.error) {
|
switch (flow.error) {
|
||||||
case DeviceVerificationFlow.UnknownMethod:
|
case DeviceVerificationFlow.UnknownMethod:
|
||||||
|
@ -42,25 +40,22 @@ Pane {
|
||||||
return qsTr("Unknown verification error.");
|
return qsTr("Unknown verification error.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
color: timelineRoot.palette.text
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Item { Layout.fillHeight: true; }
|
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
text: qsTr("Close")
|
text: qsTr("Close")
|
||||||
|
|
||||||
onClicked: dialog.close()
|
onClicked: dialog.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.3
|
import QtQuick 2.3
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import QtQuick.Layouts 1.10
|
import QtQuick.Layouts 1.10
|
||||||
|
@ -10,6 +8,7 @@ import im.nheko
|
||||||
|
|
||||||
Pane {
|
Pane {
|
||||||
property string title: flow.sender ? qsTr("Send Verification Request") : qsTr("Received Verification Request")
|
property string title: flow.sender ? qsTr("Send Verification Request") : qsTr("Received Verification Request")
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: timelineRoot.palette.window
|
color: timelineRoot.palette.window
|
||||||
}
|
}
|
||||||
|
@ -19,11 +18,10 @@ Pane {
|
||||||
spacing: 16
|
spacing: 16
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
// Self verification
|
|
||||||
|
|
||||||
Layout.preferredWidth: 400
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
wrapMode: Text.Wrap
|
// Self verification
|
||||||
|
Layout.preferredWidth: 400
|
||||||
|
color: timelineRoot.palette.text
|
||||||
text: {
|
text: {
|
||||||
if (flow.sender) {
|
if (flow.sender) {
|
||||||
if (flow.isSelfVerification)
|
if (flow.isSelfVerification)
|
||||||
|
@ -42,34 +40,31 @@ Pane {
|
||||||
return qsTr("Your device (%1) has requested to be verified.").arg(flow.deviceId);
|
return qsTr("Your device (%1) has requested to be verified.").arg(flow.deviceId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
color: timelineRoot.palette.text
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Item { Layout.fillHeight: true; }
|
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Button {
|
Button {
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
text: flow.sender ? qsTr("Cancel") : qsTr("Deny")
|
text: flow.sender ? qsTr("Cancel") : qsTr("Deny")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
flow.cancel();
|
flow.cancel();
|
||||||
dialog.close();
|
dialog.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
text: flow.sender ? qsTr("Start verification") : qsTr("Accept")
|
text: flow.sender ? qsTr("Start verification") : qsTr("Accept")
|
||||||
|
|
||||||
onClicked: flow.next()
|
onClicked: flow.next()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.3
|
import QtQuick 2.3
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import QtQuick.Layouts 1.10
|
import QtQuick.Layouts 1.10
|
||||||
|
@ -10,6 +8,7 @@ import im.nheko
|
||||||
|
|
||||||
Pane {
|
Pane {
|
||||||
property string title: qsTr("Successful Verification")
|
property string title: qsTr("Successful Verification")
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: timelineRoot.palette.window
|
color: timelineRoot.palette.window
|
||||||
}
|
}
|
||||||
|
@ -20,30 +19,26 @@ Pane {
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: content
|
id: content
|
||||||
|
|
||||||
Layout.preferredWidth: 400
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
wrapMode: Text.Wrap
|
Layout.preferredWidth: 400
|
||||||
text: qsTr("Verification successful! Both sides verified their devices!")
|
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
|
text: qsTr("Verification successful! Both sides verified their devices!")
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Item { Layout.fillHeight: true; }
|
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
text: qsTr("Close")
|
text: qsTr("Close")
|
||||||
|
|
||||||
onClicked: dialog.close()
|
onClicked: dialog.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import "../ui"
|
import "../ui"
|
||||||
import QtQuick 2.3
|
import QtQuick 2.3
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
|
@ -11,6 +9,7 @@ import im.nheko
|
||||||
|
|
||||||
Pane {
|
Pane {
|
||||||
property string title: qsTr("Waiting for other party…")
|
property string title: qsTr("Waiting for other party…")
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: timelineRoot.palette.window
|
color: timelineRoot.palette.window
|
||||||
}
|
}
|
||||||
|
@ -21,10 +20,9 @@ Pane {
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: content
|
id: content
|
||||||
|
|
||||||
Layout.preferredWidth: 400
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
wrapMode: Text.Wrap
|
Layout.preferredWidth: 400
|
||||||
|
color: timelineRoot.palette.text
|
||||||
text: {
|
text: {
|
||||||
switch (flow.state) {
|
switch (flow.state) {
|
||||||
case "WaitingForOtherToAccept":
|
case "WaitingForOtherToAccept":
|
||||||
|
@ -35,33 +33,32 @@ Pane {
|
||||||
return qsTr("Waiting for other side to complete the verification process.");
|
return qsTr("Waiting for other side to complete the verification process.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
color: timelineRoot.palette.text
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Item { Layout.fillHeight: true; }
|
|
||||||
Spinner {
|
Spinner {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
foreground: timelineRoot.palette.mid
|
foreground: timelineRoot.palette.mid
|
||||||
}
|
}
|
||||||
Item { Layout.fillHeight: true; }
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Button {
|
Button {
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
text: qsTr("Cancel")
|
text: qsTr("Cancel")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
flow.cancel();
|
flow.cancel();
|
||||||
dialog.close();
|
dialog.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
import "../"
|
||||||
import ".."
|
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Window 2.13
|
import QtQuick.Window 2.13
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts 1.3
|
||||||
|
@ -13,13 +11,31 @@ import im.nheko
|
||||||
|
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
id: createDirectRoot
|
id: createDirectRoot
|
||||||
title: qsTr("Create Direct Chat")
|
|
||||||
|
property bool otherUserHasE2ee: profile ? profile.deviceList.rowCount() > 0 : true
|
||||||
property var profile
|
property var profile
|
||||||
property bool otherUserHasE2ee: profile? profile.deviceList.rowCount() > 0 : true
|
|
||||||
minimumHeight: layout.implicitHeight + footer.implicitHeight + Nheko.paddingLarge*2
|
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
|
minimumHeight: layout.implicitHeight + footer.implicitHeight + Nheko.paddingLarge * 2
|
||||||
minimumWidth: Math.max(footer.implicitWidth, layout.implicitWidth)
|
minimumWidth: Math.max(footer.implicitWidth, layout.implicitWidth)
|
||||||
modality: Qt.NonModal
|
modality: Qt.NonModal
|
||||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
title: qsTr("Create Direct Chat")
|
||||||
|
|
||||||
|
footer: DialogButtonBox {
|
||||||
|
standardButtons: DialogButtonBox.Cancel
|
||||||
|
|
||||||
|
onAccepted: {
|
||||||
|
profile.startChat(encryption.checked);
|
||||||
|
createDirectRoot.close();
|
||||||
|
}
|
||||||
|
onRejected: createDirectRoot.close()
|
||||||
|
|
||||||
|
Button {
|
||||||
|
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
||||||
|
enabled: userID.isValidMxid
|
||||||
|
text: "Start Direct Chat"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onVisibilityChanged: {
|
onVisibilityChanged: {
|
||||||
userID.forceActiveFocus();
|
userID.forceActiveFocus();
|
||||||
|
@ -27,90 +43,79 @@ ApplicationWindow {
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: StandardKey.Cancel
|
sequence: StandardKey.Cancel
|
||||||
|
|
||||||
onActivated: createDirectRoot.close()
|
onActivated: createDirectRoot.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: layout
|
id: layout
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Nheko.paddingLarge
|
anchors.margins: Nheko.paddingLarge
|
||||||
spacing: userID.height/4
|
spacing: userID.height / 4
|
||||||
|
|
||||||
GridLayout {
|
GridLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
rows: 2
|
columnSpacing: Nheko.paddingMedium
|
||||||
columns: 2
|
columns: 2
|
||||||
rowSpacing: Nheko.paddingSmall
|
rowSpacing: Nheko.paddingSmall
|
||||||
columnSpacing: Nheko.paddingMedium
|
rows: 2
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
Layout.rowSpan: 2
|
|
||||||
Layout.preferredWidth: Nheko.avatarSize
|
|
||||||
Layout.preferredHeight: Nheko.avatarSize
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
userid: profile? profile.userid : ""
|
Layout.preferredHeight: Nheko.avatarSize
|
||||||
url: profile? profile.avatarUrl.replace("mxc://", "image://MxcImage/") : null
|
Layout.preferredWidth: Nheko.avatarSize
|
||||||
displayName: profile? profile.displayName : ""
|
Layout.rowSpan: 2
|
||||||
|
displayName: profile ? profile.displayName : ""
|
||||||
enabled: false
|
enabled: false
|
||||||
|
url: profile ? profile.avatarUrl.replace("mxc://", "image://MxcImage/") : null
|
||||||
|
userid: profile ? profile.userid : ""
|
||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: profile? profile.displayName : ""
|
|
||||||
color: TimelineManager.userColor(userID.text, timelineRoot.palette.window)
|
color: TimelineManager.userColor(userID.text, timelineRoot.palette.window)
|
||||||
font.pointSize: fontMetrics.font.pointSize
|
font.pointSize: fontMetrics.font.pointSize
|
||||||
|
text: profile ? profile.displayName : ""
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: userID.text
|
|
||||||
color: timelineRoot.palette.placeholderText
|
color: timelineRoot.palette.placeholderText
|
||||||
font.pointSize: fontMetrics.font.pointSize * 0.9
|
font.pointSize: fontMetrics.font.pointSize * 0.9
|
||||||
|
text: userID.text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
id: userID
|
id: userID
|
||||||
|
|
||||||
property bool isValidMxid: text.match("@.+?:.{3,}")
|
property bool isValidMxid: text.match("@.+?:.{3,}")
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
focus: true
|
focus: true
|
||||||
label: qsTr("User to invite")
|
label: qsTr("User to invite")
|
||||||
placeholderText: qsTr("@user:server.tld")
|
placeholderText: qsTr("@user:server.tld")
|
||||||
|
|
||||||
onTextChanged: {
|
onTextChanged: {
|
||||||
if(isValidMxid) {
|
if (isValidMxid) {
|
||||||
profile = TimelineManager.getGlobalUserProfile(text);
|
profile = TimelineManager.getGlobalUserProfile(text);
|
||||||
} else
|
} else
|
||||||
profile = null;
|
profile = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
text: qsTr("Encryption")
|
Layout.fillWidth: true
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
|
text: qsTr("Encryption")
|
||||||
}
|
}
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
id: encryption
|
id: encryption
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
checked: otherUserHasE2ee
|
checked: otherUserHasE2ee
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Item {
|
||||||
Item {Layout.fillHeight: true}
|
Layout.fillHeight: true
|
||||||
}
|
|
||||||
footer: DialogButtonBox {
|
|
||||||
standardButtons: DialogButtonBox.Cancel
|
|
||||||
Button {
|
|
||||||
text: "Start Direct Chat"
|
|
||||||
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
|
||||||
enabled: userID.isValidMxid
|
|
||||||
}
|
|
||||||
onRejected: createDirectRoot.close();
|
|
||||||
onAccepted: {
|
|
||||||
profile.startChat(encryption.checked)
|
|
||||||
createDirectRoot.close()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
import "../"
|
||||||
import ".."
|
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Window 2.13
|
import QtQuick.Window 2.13
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts 1.3
|
||||||
|
@ -12,11 +10,32 @@ import im.nheko
|
||||||
|
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
id: createRoomRoot
|
id: createRoomRoot
|
||||||
title: qsTr("Create Room")
|
|
||||||
minimumWidth: Math.max(rootLayout.implicitWidth+2*rootLayout.anchors.margins, footer.implicitWidth + Nheko.paddingLarge)
|
|
||||||
minimumHeight: rootLayout.implicitHeight+footer.implicitHeight+2*rootLayout.anchors.margins
|
|
||||||
modality: Qt.NonModal
|
|
||||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
|
minimumHeight: rootLayout.implicitHeight + footer.implicitHeight + 2 * rootLayout.anchors.margins
|
||||||
|
minimumWidth: Math.max(rootLayout.implicitWidth + 2 * rootLayout.anchors.margins, footer.implicitWidth + Nheko.paddingLarge)
|
||||||
|
modality: Qt.NonModal
|
||||||
|
title: qsTr("Create Room")
|
||||||
|
|
||||||
|
footer: DialogButtonBox {
|
||||||
|
standardButtons: DialogButtonBox.Cancel
|
||||||
|
|
||||||
|
onAccepted: {
|
||||||
|
var preset = 0;
|
||||||
|
if (isPublic.checked) {
|
||||||
|
preset = 1;
|
||||||
|
} else {
|
||||||
|
preset = isTrusted.checked ? 2 : 0;
|
||||||
|
}
|
||||||
|
Nheko.createRoom(newRoomName.text, newRoomTopic.text, newRoomAlias.text, isEncrypted.checked, preset);
|
||||||
|
createRoomRoot.close();
|
||||||
|
}
|
||||||
|
onRejected: createRoomRoot.close()
|
||||||
|
|
||||||
|
Button {
|
||||||
|
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
||||||
|
text: qsTr("Create Room")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onVisibilityChanged: {
|
onVisibilityChanged: {
|
||||||
newRoomName.forceActiveFocus();
|
newRoomName.forceActiveFocus();
|
||||||
|
@ -24,6 +43,7 @@ ApplicationWindow {
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: StandardKey.Cancel
|
sequence: StandardKey.Cancel
|
||||||
|
|
||||||
onActivated: createRoomRoot.close()
|
onActivated: createRoomRoot.close()
|
||||||
}
|
}
|
||||||
GridLayout {
|
GridLayout {
|
||||||
|
@ -37,7 +57,6 @@ ApplicationWindow {
|
||||||
id: newRoomName
|
id: newRoomName
|
||||||
Layout.columnSpan: 2
|
Layout.columnSpan: 2
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
focus: true
|
focus: true
|
||||||
label: qsTr("Name")
|
label: qsTr("Name")
|
||||||
placeholderText: qsTr("No name")
|
placeholderText: qsTr("No name")
|
||||||
|
@ -46,23 +65,21 @@ ApplicationWindow {
|
||||||
id: newRoomTopic
|
id: newRoomTopic
|
||||||
Layout.columnSpan: 2
|
Layout.columnSpan: 2
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
focus: true
|
focus: true
|
||||||
label: qsTr("Topic")
|
label: qsTr("Topic")
|
||||||
placeholderText: qsTr("No topic")
|
placeholderText: qsTr("No topic")
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.preferredHeight: newRoomName.height / 2
|
Layout.preferredHeight: newRoomName.height / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.columnSpan: 2
|
Layout.columnSpan: 2
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.preferredWidth: implicitWidth
|
Layout.preferredWidth: implicitWidth
|
||||||
text: qsTr("#")
|
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
|
text: qsTr("#")
|
||||||
}
|
}
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
id: newRoomAlias
|
id: newRoomAlias
|
||||||
|
@ -70,88 +87,73 @@ ApplicationWindow {
|
||||||
placeholderText: qsTr("Alias")
|
placeholderText: qsTr("Alias")
|
||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
Layout.preferredWidth: implicitWidth
|
|
||||||
property string userName: userInfoGrid.profile.userid
|
property string userName: userInfoGrid.profile.userid
|
||||||
text: userName.substring(userName.indexOf(":"))
|
|
||||||
|
Layout.preferredWidth: implicitWidth
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
|
text: userName.substring(userName.indexOf(":"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
Layout.preferredWidth: implicitWidth
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
text: qsTr("Public")
|
Layout.preferredWidth: implicitWidth
|
||||||
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
|
ToolTip.text: qsTr("Public rooms can be joined by anyone, private rooms need explicit invites.")
|
||||||
|
ToolTip.visible: privateHover.hovered
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
|
text: qsTr("Public")
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: privateHover
|
id: privateHover
|
||||||
}
|
}
|
||||||
ToolTip.visible: privateHover.hovered
|
|
||||||
ToolTip.text: qsTr("Public rooms can be joined by anyone, private rooms need explicit invites.")
|
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
|
||||||
}
|
}
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
|
id: isPublic
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
Layout.preferredWidth: implicitWidth
|
Layout.preferredWidth: implicitWidth
|
||||||
id: isPublic
|
|
||||||
checked: false
|
checked: false
|
||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
Layout.preferredWidth: implicitWidth
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
text: qsTr("Trusted")
|
Layout.preferredWidth: implicitWidth
|
||||||
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
|
ToolTip.text: qsTr("All invitees are given the same power level as the creator")
|
||||||
|
ToolTip.visible: trustedHover.hovered
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
|
text: qsTr("Trusted")
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: trustedHover
|
id: trustedHover
|
||||||
}
|
}
|
||||||
ToolTip.visible: trustedHover.hovered
|
|
||||||
ToolTip.text: qsTr("All invitees are given the same power level as the creator")
|
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
|
||||||
}
|
}
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
|
id: isTrusted
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
Layout.preferredWidth: implicitWidth
|
Layout.preferredWidth: implicitWidth
|
||||||
id: isTrusted
|
|
||||||
checked: false
|
checked: false
|
||||||
enabled: !isPublic.checked
|
enabled: !isPublic.checked
|
||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
Layout.preferredWidth: implicitWidth
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
text: qsTr("Encryption")
|
Layout.preferredWidth: implicitWidth
|
||||||
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
|
ToolTip.text: qsTr("Caution: Encryption cannot be disabled")
|
||||||
|
ToolTip.visible: encryptionHover.hovered
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
|
text: qsTr("Encryption")
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: encryptionHover
|
id: encryptionHover
|
||||||
}
|
}
|
||||||
ToolTip.visible: encryptionHover.hovered
|
|
||||||
ToolTip.text: qsTr("Caution: Encryption cannot be disabled")
|
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
|
||||||
}
|
}
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
|
id: isEncrypted
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
Layout.preferredWidth: implicitWidth
|
Layout.preferredWidth: implicitWidth
|
||||||
id: isEncrypted
|
|
||||||
checked: false
|
checked: false
|
||||||
}
|
}
|
||||||
|
Item {
|
||||||
Item {Layout.fillHeight: true}
|
Layout.fillHeight: true
|
||||||
}
|
|
||||||
footer: DialogButtonBox {
|
|
||||||
standardButtons: DialogButtonBox.Cancel
|
|
||||||
Button {
|
|
||||||
text: qsTr("Create Room")
|
|
||||||
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
|
||||||
}
|
|
||||||
onRejected: createRoomRoot.close();
|
|
||||||
onAccepted: {
|
|
||||||
var preset = 0;
|
|
||||||
|
|
||||||
if (isPublic.checked) {
|
|
||||||
preset = 1;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
preset = isTrusted.checked ? 2 : 0;
|
|
||||||
}
|
|
||||||
Nheko.createRoom(newRoomName.text, newRoomTopic.text, newRoomAlias.text, isEncrypted.checked, preset)
|
|
||||||
createRoomRoot.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
import "../"
|
||||||
import ".."
|
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.5
|
import QtQuick.Controls 2.5
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts 1.3
|
||||||
|
@ -11,117 +9,108 @@ import im.nheko
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
id: hiddenEventsDialog
|
id: hiddenEventsDialog
|
||||||
|
|
||||||
property string roomid: ""
|
|
||||||
property string roomName: ""
|
|
||||||
property var onAccepted: undefined
|
property var onAccepted: undefined
|
||||||
|
property string roomName: ""
|
||||||
|
property string roomid: ""
|
||||||
|
|
||||||
modality: Qt.NonModal
|
|
||||||
flags: Qt.Dialog | Qt.WindowTitleHint
|
flags: Qt.Dialog | Qt.WindowTitleHint
|
||||||
minimumWidth: 250
|
|
||||||
minimumHeight: 220
|
minimumHeight: 220
|
||||||
|
minimumWidth: 250
|
||||||
HiddenEvents {
|
modality: Qt.NonModal
|
||||||
id: hiddenEvents
|
|
||||||
|
|
||||||
roomid: hiddenEventsDialog.roomid
|
|
||||||
}
|
|
||||||
|
|
||||||
title: {
|
title: {
|
||||||
if (roomid) {
|
if (roomid) {
|
||||||
return qsTr("Hidden events for %1").arg(roomName);
|
return qsTr("Hidden events for %1").arg(roomName);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return qsTr("Hidden events");
|
return qsTr("Hidden events");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Shortcut {
|
|
||||||
sequence: StandardKey.Cancel
|
|
||||||
onActivated: dbb.rejected()
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
anchors.margins: Nheko.paddingMedium
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
MatrixText {
|
|
||||||
id: promptLabel
|
|
||||||
text: {
|
|
||||||
if (roomid) {
|
|
||||||
return qsTr("These events will be <b>shown</b> in %1:").arg(roomName);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return qsTr("These events will be <b>shown</b> in all rooms:");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 1.2)
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: false
|
|
||||||
}
|
|
||||||
|
|
||||||
GridLayout {
|
|
||||||
columns: 2
|
|
||||||
rowSpacing: Nheko.paddingMedium
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
|
|
||||||
MatrixText {
|
|
||||||
text: qsTr("User events")
|
|
||||||
ToolTip.text: qsTr("Joins, leaves, avatar and name changes, bans, …")
|
|
||||||
ToolTip.visible: hh1.hovered
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
HoverHandler {
|
|
||||||
id: hh1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ToggleButton {
|
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
checked: !hiddenEvents.hiddenEvents.includes(MtxEvent.Member)
|
|
||||||
onToggled: hiddenEvents.toggle(MtxEvent.Member)
|
|
||||||
}
|
|
||||||
|
|
||||||
MatrixText {
|
|
||||||
text: qsTr("Power level changes")
|
|
||||||
ToolTip.text: qsTr("Sent when a moderator is added/removed or the permissions of a room are changed.")
|
|
||||||
ToolTip.visible: hh2.hovered
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
HoverHandler {
|
|
||||||
id: hh2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ToggleButton {
|
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
checked: !hiddenEvents.hiddenEvents.includes(MtxEvent.PowerLevels)
|
|
||||||
onToggled: hiddenEvents.toggle(MtxEvent.PowerLevels)
|
|
||||||
}
|
|
||||||
|
|
||||||
MatrixText {
|
|
||||||
text: qsTr("Stickers")
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
|
|
||||||
ToggleButton {
|
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
checked: !hiddenEvents.hiddenEvents.includes(MtxEvent.Sticker)
|
|
||||||
onToggled: hiddenEvents.toggle(MtxEvent.Sticker)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
footer: DialogButtonBox {
|
footer: DialogButtonBox {
|
||||||
id: dbb
|
id: dbb
|
||||||
|
|
||||||
standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
|
standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
|
||||||
|
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
hiddenEvents.save();
|
hiddenEvents.save();
|
||||||
hiddenEventsDialog.close();
|
hiddenEventsDialog.close();
|
||||||
}
|
}
|
||||||
onRejected: hiddenEventsDialog.close();
|
onRejected: hiddenEventsDialog.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HiddenEvents {
|
||||||
|
id: hiddenEvents
|
||||||
|
roomid: hiddenEventsDialog.roomid
|
||||||
|
}
|
||||||
|
Shortcut {
|
||||||
|
sequence: StandardKey.Cancel
|
||||||
|
|
||||||
|
onActivated: dbb.rejected()
|
||||||
|
}
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Nheko.paddingMedium
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
|
MatrixText {
|
||||||
|
id: promptLabel
|
||||||
|
Layout.fillHeight: false
|
||||||
|
Layout.fillWidth: true
|
||||||
|
font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 1.2)
|
||||||
|
text: {
|
||||||
|
if (roomid) {
|
||||||
|
return qsTr("These events will be <b>shown</b> in %1:").arg(roomName);
|
||||||
|
} else {
|
||||||
|
return qsTr("These events will be <b>shown</b> in all rooms:");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GridLayout {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
columns: 2
|
||||||
|
rowSpacing: Nheko.paddingMedium
|
||||||
|
|
||||||
|
MatrixText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
ToolTip.text: qsTr("Joins, leaves, avatar and name changes, bans, …")
|
||||||
|
ToolTip.visible: hh1.hovered
|
||||||
|
text: qsTr("User events")
|
||||||
|
|
||||||
|
HoverHandler {
|
||||||
|
id: hh1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ToggleButton {
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
|
checked: !hiddenEvents.hiddenEvents.includes(MtxEvent.Member)
|
||||||
|
|
||||||
|
onToggled: hiddenEvents.toggle(MtxEvent.Member)
|
||||||
|
}
|
||||||
|
MatrixText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
ToolTip.text: qsTr("Sent when a moderator is added/removed or the permissions of a room are changed.")
|
||||||
|
ToolTip.visible: hh2.hovered
|
||||||
|
text: qsTr("Power level changes")
|
||||||
|
|
||||||
|
HoverHandler {
|
||||||
|
id: hh2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ToggleButton {
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
|
checked: !hiddenEvents.hiddenEvents.includes(MtxEvent.PowerLevels)
|
||||||
|
|
||||||
|
onToggled: hiddenEvents.toggle(MtxEvent.PowerLevels)
|
||||||
|
}
|
||||||
|
MatrixText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: qsTr("Stickers")
|
||||||
|
}
|
||||||
|
ToggleButton {
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
|
checked: !hiddenEvents.hiddenEvents.includes(MtxEvent.Sticker)
|
||||||
|
|
||||||
|
onToggled: hiddenEvents.toggle(MtxEvent.Sticker)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,113 +1,99 @@
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Window 2.15
|
import QtQuick.Window 2.15
|
||||||
|
import "../"
|
||||||
import ".."
|
|
||||||
|
|
||||||
import im.nheko
|
import im.nheko
|
||||||
|
|
||||||
Window {
|
Window {
|
||||||
id: imageOverlay
|
id: imageOverlay
|
||||||
|
|
||||||
required property string url
|
|
||||||
required property string eventId
|
required property string eventId
|
||||||
required property Room room
|
|
||||||
required property int originalWidth
|
required property int originalWidth
|
||||||
required property double proportionalHeight
|
required property double proportionalHeight
|
||||||
|
required property Room room
|
||||||
flags: Qt.FramelessWindowHint
|
required property string url
|
||||||
|
|
||||||
//visibility: Window.FullScreen
|
//visibility: Window.FullScreen
|
||||||
color: Qt.rgba(0.2,0.2,0.2,0.66)
|
color: Qt.rgba(0.2, 0.2, 0.2, 0.66)
|
||||||
|
flags: Qt.FramelessWindowHint
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: StandardKey.Cancel
|
sequence: StandardKey.Cancel
|
||||||
|
|
||||||
onActivated: imageOverlay.close()
|
onActivated: imageOverlay.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: imgContainer
|
id: imgContainer
|
||||||
|
|
||||||
property int imgSrcWidth: (originalWidth && originalWidth > 200) ? originalWidth : Screen.width
|
|
||||||
property int imgSrcHeight: proportionalHeight ? imgSrcWidth * proportionalHeight : Screen.height
|
property int imgSrcHeight: proportionalHeight ? imgSrcWidth * proportionalHeight : Screen.height
|
||||||
|
property int imgSrcWidth: (originalWidth && originalWidth > 200) ? originalWidth : Screen.width
|
||||||
|
|
||||||
height: Math.min(parent.height, imgSrcHeight)
|
height: Math.min(parent.height, imgSrcHeight)
|
||||||
width: Math.min(parent.width, imgSrcWidth)
|
width: Math.min(parent.width, imgSrcWidth)
|
||||||
|
|
||||||
x: (parent.width - width)
|
x: (parent.width - width)
|
||||||
y: (parent.height - height)
|
y: (parent.height - height)
|
||||||
|
|
||||||
|
onScaleChanged: {
|
||||||
|
if (scale > 10)
|
||||||
|
scale = 10;
|
||||||
|
if (scale < 0.1)
|
||||||
|
scale = 0.1;
|
||||||
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: img
|
id: img
|
||||||
|
|
||||||
visible: !mxcimage.loaded
|
property bool loaded: status == Image.Ready
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
source: url.replace("mxc://", "image://MxcImage/")
|
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
fillMode: Image.PreserveAspectFit
|
fillMode: Image.PreserveAspectFit
|
||||||
smooth: true
|
|
||||||
mipmap: true
|
mipmap: true
|
||||||
property bool loaded: status == Image.Ready
|
smooth: true
|
||||||
|
source: url.replace("mxc://", "image://MxcImage/")
|
||||||
|
visible: !mxcimage.loaded
|
||||||
}
|
}
|
||||||
|
|
||||||
MxcAnimatedImage {
|
MxcAnimatedImage {
|
||||||
id: mxcimage
|
id: mxcimage
|
||||||
|
|
||||||
visible: loaded
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
roomm: imageOverlay.room
|
|
||||||
play: !Settings.animateImagesOnHover || mouseArea.hovered
|
|
||||||
eventId: imageOverlay.eventId
|
eventId: imageOverlay.eventId
|
||||||
}
|
play: !Settings.animateImagesOnHover || mouseArea.hovered
|
||||||
|
roomm: imageOverlay.room
|
||||||
onScaleChanged: {
|
visible: loaded
|
||||||
if (scale > 10) scale = 10;
|
|
||||||
if (scale < 0.1) scale = 0.1
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
|
|
||||||
PinchHandler {
|
PinchHandler {
|
||||||
target: imgContainer
|
|
||||||
maximumScale: 10
|
maximumScale: 10
|
||||||
minimumScale: 0.1
|
minimumScale: 0.1
|
||||||
|
target: imgContainer
|
||||||
}
|
}
|
||||||
|
|
||||||
WheelHandler {
|
WheelHandler {
|
||||||
property: "scale"
|
property: "scale"
|
||||||
target: imgContainer
|
target: imgContainer
|
||||||
}
|
}
|
||||||
|
|
||||||
DragHandler {
|
DragHandler {
|
||||||
target: imgContainer
|
target: imgContainer
|
||||||
}
|
}
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.margins: Nheko.paddingLarge
|
anchors.margins: Nheko.paddingLarge
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
spacing: Nheko.paddingMedium
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
height: 48
|
height: 48
|
||||||
width: 48
|
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
image: ":/icons/icons/ui/download.svg"
|
image: ":/icons/icons/ui/download.svg"
|
||||||
|
width: 48
|
||||||
|
|
||||||
//ToolTip.visible: hovered
|
//ToolTip.visible: hovered
|
||||||
//ToolTip.delay: Nheko.tooltipDelay
|
//ToolTip.delay: Nheko.tooltipDelay
|
||||||
//ToolTip.text: qsTr("Download")
|
//ToolTip.text: qsTr("Download")
|
||||||
|
@ -122,14 +108,14 @@ Window {
|
||||||
}
|
}
|
||||||
ImageButton {
|
ImageButton {
|
||||||
height: 48
|
height: 48
|
||||||
width: 48
|
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
image: ":/icons/icons/ui/dismiss.svg"
|
image: ":/icons/icons/ui/dismiss.svg"
|
||||||
|
width: 48
|
||||||
|
|
||||||
//ToolTip.visible: hovered
|
//ToolTip.visible: hovered
|
||||||
//ToolTip.delay: Nheko.tooltipDelay
|
//ToolTip.delay: Nheko.tooltipDelay
|
||||||
//ToolTip.text: qsTr("Close")
|
//ToolTip.text: qsTr("Close")
|
||||||
onClicked: imageOverlay.close()
|
onClicked: imageOverlay.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
import "../"
|
||||||
import ".."
|
|
||||||
import "../components"
|
import "../components"
|
||||||
import Qt.labs.platform 1.1
|
import Qt.labs.platform 1.1
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
|
@ -15,316 +13,23 @@ ApplicationWindow {
|
||||||
id: win
|
id: win
|
||||||
|
|
||||||
property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 2.3)
|
property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 2.3)
|
||||||
property SingleImagePackModel imagePack
|
|
||||||
property int currentImageIndex: -1
|
property int currentImageIndex: -1
|
||||||
|
property SingleImagePackModel imagePack
|
||||||
readonly property int stickerDim: 128
|
readonly property int stickerDim: 128
|
||||||
readonly property int stickerDimPad: 128 + Nheko.paddingSmall
|
readonly property int stickerDimPad: 128 + Nheko.paddingSmall
|
||||||
|
|
||||||
title: qsTr("Editing image pack")
|
|
||||||
height: 600
|
|
||||||
width: 600
|
|
||||||
palette: timelineRoot.palette
|
|
||||||
color: timelineRoot.palette.base
|
color: timelineRoot.palette.base
|
||||||
modality: Qt.WindowModal
|
|
||||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
|
height: 600
|
||||||
AdaptiveLayout {
|
modality: Qt.WindowModal
|
||||||
id: adaptiveView
|
palette: timelineRoot.palette
|
||||||
|
title: qsTr("Editing image pack")
|
||||||
anchors.fill: parent
|
width: 600
|
||||||
singlePageMode: false
|
|
||||||
pageIndex: 0
|
|
||||||
|
|
||||||
AdaptiveLayoutElement {
|
|
||||||
id: packlistC
|
|
||||||
|
|
||||||
visible: Settings.groupView
|
|
||||||
minimumWidth: 200
|
|
||||||
collapsedWidth: 200
|
|
||||||
preferredWidth: 300
|
|
||||||
maximumWidth: 300
|
|
||||||
clip: true
|
|
||||||
|
|
||||||
ListView {
|
|
||||||
//required property bool isEmote
|
|
||||||
//required property bool isSticker
|
|
||||||
|
|
||||||
model: imagePack
|
|
||||||
|
|
||||||
|
|
||||||
header: AvatarListTile {
|
|
||||||
title: imagePack.packname
|
|
||||||
avatarUrl: imagePack.avatarUrl
|
|
||||||
roomid: imagePack.statekey
|
|
||||||
subtitle: imagePack.statekey
|
|
||||||
index: -1
|
|
||||||
selectedIndex: currentImageIndex
|
|
||||||
|
|
||||||
TapHandler {
|
|
||||||
onSingleTapped: currentImageIndex = -1
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
height: parent.height - Nheko.paddingSmall * 2
|
|
||||||
width: 3
|
|
||||||
color: timelineRoot.palette.highlight
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
footer: Button {
|
|
||||||
palette: timelineRoot.palette
|
|
||||||
onClicked: addFilesDialog.open()
|
|
||||||
width: ListView.view.width
|
|
||||||
text: qsTr("Add images")
|
|
||||||
|
|
||||||
FileDialog {
|
|
||||||
id: addFilesDialog
|
|
||||||
|
|
||||||
folder: StandardPaths.writableLocation(StandardPaths.PicturesLocation)
|
|
||||||
fileMode: FileDialog.OpenFiles
|
|
||||||
nameFilters: [qsTr("Images (*.png *.webp *.gif *.jpg *.jpeg)")]
|
|
||||||
title: qsTr("Select images for pack")
|
|
||||||
acceptLabel: qsTr("Add to pack")
|
|
||||||
onAccepted: imagePack.addStickers(files)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate: AvatarListTile {
|
|
||||||
id: packItem
|
|
||||||
|
|
||||||
property color background: timelineRoot.palette.window
|
|
||||||
property color importantText: timelineRoot.palette.text
|
|
||||||
property color unimportantText: timelineRoot.palette.placeholderText
|
|
||||||
property color bubbleBackground: timelineRoot.palette.highlight
|
|
||||||
property color bubbleText: timelineRoot.palette.highlightedText
|
|
||||||
required property string shortCode
|
|
||||||
required property string url
|
|
||||||
required property string body
|
|
||||||
|
|
||||||
title: shortCode
|
|
||||||
subtitle: body
|
|
||||||
avatarUrl: url
|
|
||||||
selectedIndex: currentImageIndex
|
|
||||||
crop: false
|
|
||||||
|
|
||||||
TapHandler {
|
|
||||||
onSingleTapped: currentImageIndex = index
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
AdaptiveLayoutElement {
|
|
||||||
id: packinfoC
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
color: timelineRoot.palette.window
|
|
||||||
|
|
||||||
GridLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: Nheko.paddingMedium
|
|
||||||
visible: currentImageIndex == -1
|
|
||||||
enabled: visible
|
|
||||||
columns: 2
|
|
||||||
rowSpacing: Nheko.paddingLarge
|
|
||||||
|
|
||||||
Avatar {
|
|
||||||
Layout.columnSpan: 2
|
|
||||||
url: imagePack.avatarUrl.replace("mxc://", "image://MxcImage/")
|
|
||||||
displayName: imagePack.packname
|
|
||||||
roomid: imagePack.statekey
|
|
||||||
height: 130
|
|
||||||
width: 130
|
|
||||||
crop: false
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
|
|
||||||
ImageButton {
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Change the overview image for this pack")
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.leftMargin: Nheko.paddingMedium
|
|
||||||
anchors.topMargin: Nheko.paddingMedium
|
|
||||||
image: ":/icons/icons/ui/edit.svg"
|
|
||||||
onClicked: addAvatarDialog.open()
|
|
||||||
|
|
||||||
FileDialog {
|
|
||||||
id: addAvatarDialog
|
|
||||||
|
|
||||||
folder: StandardPaths.writableLocation(StandardPaths.PicturesLocation)
|
|
||||||
fileMode: FileDialog.OpenFile
|
|
||||||
nameFilters: [qsTr("Overview Image (*.png *.webp *.jpg *.jpeg)")]
|
|
||||||
title: qsTr("Select overview image for pack")
|
|
||||||
onAccepted: imagePack.setAvatar(file)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MatrixTextField {
|
|
||||||
id: statekeyField
|
|
||||||
|
|
||||||
visible: imagePack.roomid
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.columnSpan: 2
|
|
||||||
label: qsTr("State key")
|
|
||||||
text: imagePack.statekey
|
|
||||||
onTextEdited: imagePack.statekey = text
|
|
||||||
}
|
|
||||||
|
|
||||||
MatrixTextField {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.columnSpan: 2
|
|
||||||
label: qsTr("Packname")
|
|
||||||
text: imagePack.packname
|
|
||||||
onTextEdited: imagePack.packname = text
|
|
||||||
}
|
|
||||||
|
|
||||||
MatrixTextField {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.columnSpan: 2
|
|
||||||
label: qsTr("Attribution")
|
|
||||||
text: imagePack.attribution
|
|
||||||
onTextEdited: imagePack.attribution = text
|
|
||||||
}
|
|
||||||
|
|
||||||
MatrixText {
|
|
||||||
Layout.margins: statekeyField.textPadding
|
|
||||||
font.weight: Font.DemiBold
|
|
||||||
font.letterSpacing: font.pixelSize * 0.02
|
|
||||||
text: qsTr("Use as Emoji")
|
|
||||||
}
|
|
||||||
|
|
||||||
ToggleButton {
|
|
||||||
checked: imagePack.isEmotePack
|
|
||||||
onCheckedChanged: imagePack.isEmotePack = checked
|
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
}
|
|
||||||
|
|
||||||
MatrixText {
|
|
||||||
Layout.margins: statekeyField.textPadding
|
|
||||||
font.weight: Font.DemiBold
|
|
||||||
font.letterSpacing: font.pixelSize * 0.02
|
|
||||||
text: qsTr("Use as Sticker")
|
|
||||||
}
|
|
||||||
|
|
||||||
ToggleButton {
|
|
||||||
checked: imagePack.isStickerPack
|
|
||||||
onCheckedChanged: imagePack.isStickerPack = checked
|
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
Layout.columnSpan: 2
|
|
||||||
Layout.fillHeight: true
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
GridLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: Nheko.paddingMedium
|
|
||||||
visible: currentImageIndex >= 0
|
|
||||||
enabled: visible
|
|
||||||
columns: 2
|
|
||||||
rowSpacing: Nheko.paddingLarge
|
|
||||||
|
|
||||||
Avatar {
|
|
||||||
Layout.columnSpan: 2
|
|
||||||
url: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.Url).replace("mxc://", "image://MxcImage/") + "?scale"
|
|
||||||
displayName: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.ShortCode)
|
|
||||||
roomid: displayName
|
|
||||||
height: 130
|
|
||||||
width: 130
|
|
||||||
crop: false
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
MatrixTextField {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.columnSpan: 2
|
|
||||||
label: qsTr("Shortcode")
|
|
||||||
text: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.ShortCode)
|
|
||||||
onTextEdited: imagePack.setData(imagePack.index(currentImageIndex, 0), text, SingleImagePackModel.ShortCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
MatrixTextField {
|
|
||||||
id: bodyField
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.columnSpan: 2
|
|
||||||
label: qsTr("Body")
|
|
||||||
text: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.Body)
|
|
||||||
onTextEdited: imagePack.setData(imagePack.index(currentImageIndex, 0), text, SingleImagePackModel.Body)
|
|
||||||
}
|
|
||||||
|
|
||||||
MatrixText {
|
|
||||||
Layout.margins: bodyField.textPadding
|
|
||||||
font.weight: Font.DemiBold
|
|
||||||
font.letterSpacing: font.pixelSize * 0.02
|
|
||||||
text: qsTr("Use as Emoji")
|
|
||||||
}
|
|
||||||
|
|
||||||
ToggleButton {
|
|
||||||
checked: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.IsEmote)
|
|
||||||
onCheckedChanged: imagePack.setData(imagePack.index(currentImageIndex, 0), checked, SingleImagePackModel.IsEmote)
|
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
}
|
|
||||||
|
|
||||||
MatrixText {
|
|
||||||
Layout.margins: bodyField.textPadding
|
|
||||||
font.weight: Font.DemiBold
|
|
||||||
font.letterSpacing: font.pixelSize * 0.02
|
|
||||||
text: qsTr("Use as Sticker")
|
|
||||||
}
|
|
||||||
|
|
||||||
ToggleButton {
|
|
||||||
checked: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.IsSticker)
|
|
||||||
onCheckedChanged: imagePack.setData(imagePack.index(currentImageIndex, 0), checked, SingleImagePackModel.IsSticker)
|
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
}
|
|
||||||
|
|
||||||
MatrixText {
|
|
||||||
Layout.margins: bodyField.textPadding
|
|
||||||
font.weight: Font.DemiBold
|
|
||||||
font.letterSpacing: font.pixelSize * 0.02
|
|
||||||
text: qsTr("Remove from pack")
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
text: qsTr("Remove")
|
|
||||||
onClicked: {
|
|
||||||
let temp = currentImageIndex;
|
|
||||||
currentImageIndex = -1;
|
|
||||||
imagePack.remove(temp);
|
|
||||||
}
|
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
Layout.columnSpan: 2
|
|
||||||
Layout.fillHeight: true
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
footer: DialogButtonBox {
|
footer: DialogButtonBox {
|
||||||
id: buttons
|
id: buttons
|
||||||
|
|
||||||
standardButtons: DialogButtonBox.Save | DialogButtonBox.Cancel
|
standardButtons: DialogButtonBox.Save | DialogButtonBox.Cancel
|
||||||
|
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
imagePack.save();
|
imagePack.save();
|
||||||
win.close();
|
win.close();
|
||||||
|
@ -332,4 +37,269 @@ ApplicationWindow {
|
||||||
onRejected: win.close()
|
onRejected: win.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AdaptiveLayout {
|
||||||
|
id: adaptiveView
|
||||||
|
anchors.fill: parent
|
||||||
|
pageIndex: 0
|
||||||
|
singlePageMode: false
|
||||||
|
|
||||||
|
AdaptiveLayoutElement {
|
||||||
|
id: packlistC
|
||||||
|
clip: true
|
||||||
|
collapsedWidth: 200
|
||||||
|
maximumWidth: 300
|
||||||
|
minimumWidth: 200
|
||||||
|
preferredWidth: 300
|
||||||
|
visible: Settings.groupView
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
//required property bool isEmote
|
||||||
|
//required property bool isSticker
|
||||||
|
model: imagePack
|
||||||
|
|
||||||
|
delegate: AvatarListTile {
|
||||||
|
id: packItem
|
||||||
|
|
||||||
|
property color background: timelineRoot.palette.window
|
||||||
|
required property string body
|
||||||
|
property color bubbleBackground: timelineRoot.palette.highlight
|
||||||
|
property color bubbleText: timelineRoot.palette.highlightedText
|
||||||
|
property color importantText: timelineRoot.palette.text
|
||||||
|
required property string shortCode
|
||||||
|
property color unimportantText: timelineRoot.palette.placeholderText
|
||||||
|
required property string url
|
||||||
|
|
||||||
|
avatarUrl: url
|
||||||
|
crop: false
|
||||||
|
selectedIndex: currentImageIndex
|
||||||
|
subtitle: body
|
||||||
|
title: shortCode
|
||||||
|
|
||||||
|
TapHandler {
|
||||||
|
onSingleTapped: currentImageIndex = index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
footer: Button {
|
||||||
|
palette: timelineRoot.palette
|
||||||
|
text: qsTr("Add images")
|
||||||
|
width: ListView.view.width
|
||||||
|
|
||||||
|
onClicked: addFilesDialog.open()
|
||||||
|
|
||||||
|
FileDialog {
|
||||||
|
id: addFilesDialog
|
||||||
|
acceptLabel: qsTr("Add to pack")
|
||||||
|
fileMode: FileDialog.OpenFiles
|
||||||
|
folder: StandardPaths.writableLocation(StandardPaths.PicturesLocation)
|
||||||
|
nameFilters: [qsTr("Images (*.png *.webp *.gif *.jpg *.jpeg)")]
|
||||||
|
title: qsTr("Select images for pack")
|
||||||
|
|
||||||
|
onAccepted: imagePack.addStickers(files)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
header: AvatarListTile {
|
||||||
|
avatarUrl: imagePack.avatarUrl
|
||||||
|
index: -1
|
||||||
|
roomid: imagePack.statekey
|
||||||
|
selectedIndex: currentImageIndex
|
||||||
|
subtitle: imagePack.statekey
|
||||||
|
title: imagePack.packname
|
||||||
|
|
||||||
|
TapHandler {
|
||||||
|
onSingleTapped: currentImageIndex = -1
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
color: timelineRoot.palette.highlight
|
||||||
|
height: parent.height - Nheko.paddingSmall * 2
|
||||||
|
width: 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AdaptiveLayoutElement {
|
||||||
|
id: packinfoC
|
||||||
|
Rectangle {
|
||||||
|
color: timelineRoot.palette.window
|
||||||
|
|
||||||
|
GridLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Nheko.paddingMedium
|
||||||
|
columns: 2
|
||||||
|
enabled: visible
|
||||||
|
rowSpacing: Nheko.paddingLarge
|
||||||
|
visible: currentImageIndex == -1
|
||||||
|
|
||||||
|
Avatar {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.columnSpan: 2
|
||||||
|
crop: false
|
||||||
|
displayName: imagePack.packname
|
||||||
|
height: 130
|
||||||
|
roomid: imagePack.statekey
|
||||||
|
url: imagePack.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
|
width: 130
|
||||||
|
|
||||||
|
ImageButton {
|
||||||
|
ToolTip.text: qsTr("Change the overview image for this pack")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Nheko.paddingMedium
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: Nheko.paddingMedium
|
||||||
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/edit.svg"
|
||||||
|
|
||||||
|
onClicked: addAvatarDialog.open()
|
||||||
|
|
||||||
|
FileDialog {
|
||||||
|
id: addAvatarDialog
|
||||||
|
fileMode: FileDialog.OpenFile
|
||||||
|
folder: StandardPaths.writableLocation(StandardPaths.PicturesLocation)
|
||||||
|
nameFilters: [qsTr("Overview Image (*.png *.webp *.jpg *.jpeg)")]
|
||||||
|
title: qsTr("Select overview image for pack")
|
||||||
|
|
||||||
|
onAccepted: imagePack.setAvatar(file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MatrixTextField {
|
||||||
|
id: statekeyField
|
||||||
|
Layout.columnSpan: 2
|
||||||
|
Layout.fillWidth: true
|
||||||
|
label: qsTr("State key")
|
||||||
|
text: imagePack.statekey
|
||||||
|
visible: imagePack.roomid
|
||||||
|
|
||||||
|
onTextEdited: imagePack.statekey = text
|
||||||
|
}
|
||||||
|
MatrixTextField {
|
||||||
|
Layout.columnSpan: 2
|
||||||
|
Layout.fillWidth: true
|
||||||
|
label: qsTr("Packname")
|
||||||
|
text: imagePack.packname
|
||||||
|
|
||||||
|
onTextEdited: imagePack.packname = text
|
||||||
|
}
|
||||||
|
MatrixTextField {
|
||||||
|
Layout.columnSpan: 2
|
||||||
|
Layout.fillWidth: true
|
||||||
|
label: qsTr("Attribution")
|
||||||
|
text: imagePack.attribution
|
||||||
|
|
||||||
|
onTextEdited: imagePack.attribution = text
|
||||||
|
}
|
||||||
|
MatrixText {
|
||||||
|
Layout.margins: statekeyField.textPadding
|
||||||
|
font.letterSpacing: font.pixelSize * 0.02
|
||||||
|
font.weight: Font.DemiBold
|
||||||
|
text: qsTr("Use as Emoji")
|
||||||
|
}
|
||||||
|
ToggleButton {
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
|
checked: imagePack.isEmotePack
|
||||||
|
|
||||||
|
onCheckedChanged: imagePack.isEmotePack = checked
|
||||||
|
}
|
||||||
|
MatrixText {
|
||||||
|
Layout.margins: statekeyField.textPadding
|
||||||
|
font.letterSpacing: font.pixelSize * 0.02
|
||||||
|
font.weight: Font.DemiBold
|
||||||
|
text: qsTr("Use as Sticker")
|
||||||
|
}
|
||||||
|
ToggleButton {
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
|
checked: imagePack.isStickerPack
|
||||||
|
|
||||||
|
onCheckedChanged: imagePack.isStickerPack = checked
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.columnSpan: 2
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GridLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Nheko.paddingMedium
|
||||||
|
columns: 2
|
||||||
|
enabled: visible
|
||||||
|
rowSpacing: Nheko.paddingLarge
|
||||||
|
visible: currentImageIndex >= 0
|
||||||
|
|
||||||
|
Avatar {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.columnSpan: 2
|
||||||
|
crop: false
|
||||||
|
displayName: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.ShortCode)
|
||||||
|
height: 130
|
||||||
|
roomid: displayName
|
||||||
|
url: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.Url).replace("mxc://", "image://MxcImage/") + "?scale"
|
||||||
|
width: 130
|
||||||
|
}
|
||||||
|
MatrixTextField {
|
||||||
|
Layout.columnSpan: 2
|
||||||
|
Layout.fillWidth: true
|
||||||
|
label: qsTr("Shortcode")
|
||||||
|
text: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.ShortCode)
|
||||||
|
|
||||||
|
onTextEdited: imagePack.setData(imagePack.index(currentImageIndex, 0), text, SingleImagePackModel.ShortCode)
|
||||||
|
}
|
||||||
|
MatrixTextField {
|
||||||
|
id: bodyField
|
||||||
|
Layout.columnSpan: 2
|
||||||
|
Layout.fillWidth: true
|
||||||
|
label: qsTr("Body")
|
||||||
|
text: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.Body)
|
||||||
|
|
||||||
|
onTextEdited: imagePack.setData(imagePack.index(currentImageIndex, 0), text, SingleImagePackModel.Body)
|
||||||
|
}
|
||||||
|
MatrixText {
|
||||||
|
Layout.margins: bodyField.textPadding
|
||||||
|
font.letterSpacing: font.pixelSize * 0.02
|
||||||
|
font.weight: Font.DemiBold
|
||||||
|
text: qsTr("Use as Emoji")
|
||||||
|
}
|
||||||
|
ToggleButton {
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
|
checked: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.IsEmote)
|
||||||
|
|
||||||
|
onCheckedChanged: imagePack.setData(imagePack.index(currentImageIndex, 0), checked, SingleImagePackModel.IsEmote)
|
||||||
|
}
|
||||||
|
MatrixText {
|
||||||
|
Layout.margins: bodyField.textPadding
|
||||||
|
font.letterSpacing: font.pixelSize * 0.02
|
||||||
|
font.weight: Font.DemiBold
|
||||||
|
text: qsTr("Use as Sticker")
|
||||||
|
}
|
||||||
|
ToggleButton {
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
|
checked: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.IsSticker)
|
||||||
|
|
||||||
|
onCheckedChanged: imagePack.setData(imagePack.index(currentImageIndex, 0), checked, SingleImagePackModel.IsSticker)
|
||||||
|
}
|
||||||
|
MatrixText {
|
||||||
|
Layout.margins: bodyField.textPadding
|
||||||
|
font.letterSpacing: font.pixelSize * 0.02
|
||||||
|
font.weight: Font.DemiBold
|
||||||
|
text: qsTr("Remove from pack")
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
|
text: qsTr("Remove")
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
let temp = currentImageIndex;
|
||||||
|
currentImageIndex = -1;
|
||||||
|
imagePack.remove(temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.columnSpan: 2
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
import "../"
|
||||||
import ".."
|
|
||||||
import "../components"
|
import "../components"
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.12
|
import QtQuick.Controls 2.12
|
||||||
|
@ -13,96 +11,70 @@ import im.nheko
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
id: win
|
id: win
|
||||||
|
|
||||||
property Room room
|
|
||||||
property ImagePackListModel packlist
|
|
||||||
property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 2.3)
|
property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 2.3)
|
||||||
property SingleImagePackModel currentPack: packlist.packAt(currentPackIndex)
|
property SingleImagePackModel currentPack: packlist.packAt(currentPackIndex)
|
||||||
property int currentPackIndex: 0
|
property int currentPackIndex: 0
|
||||||
|
property ImagePackListModel packlist
|
||||||
|
property Room room
|
||||||
readonly property int stickerDim: 128
|
readonly property int stickerDim: 128
|
||||||
readonly property int stickerDimPad: 128 + Nheko.paddingSmall
|
readonly property int stickerDimPad: 128 + Nheko.paddingSmall
|
||||||
|
|
||||||
title: qsTr("Image pack settings")
|
|
||||||
height: 600
|
|
||||||
width: 800
|
|
||||||
palette: timelineRoot.palette
|
|
||||||
color: timelineRoot.palette.base
|
color: timelineRoot.palette.base
|
||||||
modality: Qt.NonModal
|
|
||||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
|
height: 600
|
||||||
|
modality: Qt.NonModal
|
||||||
|
palette: timelineRoot.palette
|
||||||
|
title: qsTr("Image pack settings")
|
||||||
|
width: 800
|
||||||
|
|
||||||
|
footer: DialogButtonBox {
|
||||||
|
id: buttons
|
||||||
|
Button {
|
||||||
|
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
||||||
|
text: qsTr("Close")
|
||||||
|
|
||||||
|
onClicked: win.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: packEditor
|
id: packEditor
|
||||||
|
|
||||||
ImagePackEditorDialog {
|
ImagePackEditorDialog {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AdaptiveLayout {
|
AdaptiveLayout {
|
||||||
id: adaptiveView
|
id: adaptiveView
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
singlePageMode: false
|
|
||||||
pageIndex: 0
|
pageIndex: 0
|
||||||
|
singlePageMode: false
|
||||||
|
|
||||||
AdaptiveLayoutElement {
|
AdaptiveLayoutElement {
|
||||||
id: packlistC
|
id: packlistC
|
||||||
|
|
||||||
visible: Settings.groupView
|
|
||||||
minimumWidth: 200
|
|
||||||
collapsedWidth: 200
|
collapsedWidth: 200
|
||||||
preferredWidth: 300
|
|
||||||
maximumWidth: 300
|
maximumWidth: 300
|
||||||
|
minimumWidth: 200
|
||||||
|
preferredWidth: 300
|
||||||
|
visible: Settings.groupView
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
model: packlist
|
|
||||||
clip: true
|
clip: true
|
||||||
|
model: packlist
|
||||||
|
|
||||||
footer: ColumnLayout {
|
|
||||||
Button {
|
|
||||||
palette: timelineRoot.palette
|
|
||||||
onClicked: {
|
|
||||||
var dialog = packEditor.createObject(timelineRoot, {
|
|
||||||
"imagePack": packlist.newPack(false)
|
|
||||||
});
|
|
||||||
dialog.show();
|
|
||||||
timelineRoot.destroyOnClose(dialog);
|
|
||||||
}
|
|
||||||
width: packlistC.width
|
|
||||||
visible: !packlist.containsAccountPack
|
|
||||||
text: qsTr("Create account pack")
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
palette: timelineRoot.palette
|
|
||||||
onClicked: {
|
|
||||||
var dialog = packEditor.createObject(timelineRoot, {
|
|
||||||
"imagePack": packlist.newPack(true)
|
|
||||||
});
|
|
||||||
dialog.show();
|
|
||||||
timelineRoot.destroyOnClose(dialog);
|
|
||||||
}
|
|
||||||
width: packlistC.width
|
|
||||||
visible: room.permissions.canChange(MtxEvent.ImagePackInRoom)
|
|
||||||
text: qsTr("New room pack")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate: AvatarListTile {
|
delegate: AvatarListTile {
|
||||||
id: packItem
|
id: packItem
|
||||||
|
|
||||||
property color background: timelineRoot.palette.window
|
property color background: timelineRoot.palette.window
|
||||||
property color importantText: timelineRoot.palette.text
|
|
||||||
property color unimportantText: timelineRoot.palette.placeholderText
|
|
||||||
property color bubbleBackground: timelineRoot.palette.highlight
|
property color bubbleBackground: timelineRoot.palette.highlight
|
||||||
property color bubbleText: timelineRoot.palette.highlightedText
|
property color bubbleText: timelineRoot.palette.highlightedText
|
||||||
required property string displayName
|
required property string displayName
|
||||||
required property bool fromAccountData
|
required property bool fromAccountData
|
||||||
required property bool fromCurrentRoom
|
required property bool fromCurrentRoom
|
||||||
|
property color importantText: timelineRoot.palette.text
|
||||||
required property string statekey
|
required property string statekey
|
||||||
|
property color unimportantText: timelineRoot.palette.placeholderText
|
||||||
|
|
||||||
title: displayName
|
roomid: statekey
|
||||||
|
selectedIndex: currentPackIndex
|
||||||
subtitle: {
|
subtitle: {
|
||||||
if (fromAccountData)
|
if (fromAccountData)
|
||||||
return qsTr("Private pack");
|
return qsTr("Private pack");
|
||||||
|
@ -111,31 +83,55 @@ ApplicationWindow {
|
||||||
else
|
else
|
||||||
return qsTr("Globally enabled pack");
|
return qsTr("Globally enabled pack");
|
||||||
}
|
}
|
||||||
selectedIndex: currentPackIndex
|
title: displayName
|
||||||
roomid: statekey
|
|
||||||
|
|
||||||
TapHandler {
|
TapHandler {
|
||||||
onSingleTapped: currentPackIndex = index
|
onSingleTapped: currentPackIndex = index
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
footer: ColumnLayout {
|
||||||
|
Button {
|
||||||
|
palette: timelineRoot.palette
|
||||||
|
text: qsTr("Create account pack")
|
||||||
|
visible: !packlist.containsAccountPack
|
||||||
|
width: packlistC.width
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
var dialog = packEditor.createObject(timelineRoot, {
|
||||||
|
"imagePack": packlist.newPack(false)
|
||||||
|
});
|
||||||
|
dialog.show();
|
||||||
|
timelineRoot.destroyOnClose(dialog);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
palette: timelineRoot.palette
|
||||||
|
text: qsTr("New room pack")
|
||||||
|
visible: room.permissions.canChange(MtxEvent.ImagePackInRoom)
|
||||||
|
width: packlistC.width
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
var dialog = packEditor.createObject(timelineRoot, {
|
||||||
|
"imagePack": packlist.newPack(true)
|
||||||
|
});
|
||||||
|
dialog.show();
|
||||||
|
timelineRoot.destroyOnClose(dialog);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AdaptiveLayoutElement {
|
AdaptiveLayoutElement {
|
||||||
id: packinfoC
|
id: packinfoC
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
color: timelineRoot.palette.window
|
color: timelineRoot.palette.window
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: packinfo
|
id: packinfo
|
||||||
|
|
||||||
property string packName: currentPack ? currentPack.packname : ""
|
|
||||||
property string attribution: currentPack ? currentPack.attribution : ""
|
property string attribution: currentPack ? currentPack.attribution : ""
|
||||||
property string avatarUrl: currentPack ? currentPack.avatarUrl : ""
|
property string avatarUrl: currentPack ? currentPack.avatarUrl : ""
|
||||||
|
property string packName: currentPack ? currentPack.packname : ""
|
||||||
property string statekey: currentPack ? currentPack.statekey : ""
|
property string statekey: currentPack ? currentPack.statekey : ""
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
@ -143,117 +139,92 @@ ApplicationWindow {
|
||||||
spacing: Nheko.paddingLarge
|
spacing: Nheko.paddingLarge
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
url: packinfo.avatarUrl.replace("mxc://", "image://MxcImage/")
|
|
||||||
displayName: packinfo.packName
|
|
||||||
roomid: packinfo.statekey
|
|
||||||
height: 100
|
|
||||||
width: 100
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
displayName: packinfo.packName
|
||||||
enabled: false
|
enabled: false
|
||||||
|
height: 100
|
||||||
|
roomid: packinfo.statekey
|
||||||
|
url: packinfo.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
|
width: 100
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
text: packinfo.packName
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.preferredWidth: packinfoC.width - Nheko.paddingLarge * 2
|
||||||
font.pixelSize: Math.ceil(fontMetrics.pixelSize * 1.1)
|
font.pixelSize: Math.ceil(fontMetrics.pixelSize * 1.1)
|
||||||
horizontalAlignment: TextEdit.AlignHCenter
|
horizontalAlignment: TextEdit.AlignHCenter
|
||||||
|
text: packinfo.packName
|
||||||
|
}
|
||||||
|
MatrixText {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.preferredWidth: packinfoC.width - Nheko.paddingLarge * 2
|
Layout.preferredWidth: packinfoC.width - Nheko.paddingLarge * 2
|
||||||
}
|
horizontalAlignment: TextEdit.AlignHCenter
|
||||||
|
|
||||||
MatrixText {
|
|
||||||
text: packinfo.attribution
|
text: packinfo.attribution
|
||||||
wrapMode: TextEdit.Wrap
|
wrapMode: TextEdit.Wrap
|
||||||
horizontalAlignment: TextEdit.AlignHCenter
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.preferredWidth: packinfoC.width - Nheko.paddingLarge * 2
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GridLayout {
|
GridLayout {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
visible: currentPack && currentPack.roomid != ""
|
|
||||||
columns: 2
|
columns: 2
|
||||||
rowSpacing: Nheko.paddingMedium
|
rowSpacing: Nheko.paddingMedium
|
||||||
|
visible: currentPack && currentPack.roomid != ""
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
text: qsTr("Enable globally")
|
text: qsTr("Enable globally")
|
||||||
}
|
}
|
||||||
|
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
ToolTip.text: qsTr("Enables this pack to be used in all rooms")
|
ToolTip.text: qsTr("Enables this pack to be used in all rooms")
|
||||||
checked: currentPack ? currentPack.isGloballyEnabled : false
|
checked: currentPack ? currentPack.isGloballyEnabled : false
|
||||||
|
|
||||||
onCheckedChanged: currentPack.isGloballyEnabled = checked
|
onCheckedChanged: currentPack.isGloballyEnabled = checked
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
text: qsTr("Edit")
|
|
||||||
enabled: currentPack.canEdit
|
enabled: currentPack.canEdit
|
||||||
|
text: qsTr("Edit")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
var dialog = packEditor.createObject(timelineRoot, {
|
var dialog = packEditor.createObject(timelineRoot, {
|
||||||
"imagePack": currentPack
|
"imagePack": currentPack
|
||||||
});
|
});
|
||||||
dialog.show();
|
dialog.show();
|
||||||
timelineRoot.destroyOnClose(dialog);
|
timelineRoot.destroyOnClose(dialog);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GridView {
|
GridView {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
model: currentPack
|
|
||||||
cellWidth: stickerDimPad
|
|
||||||
cellHeight: stickerDimPad
|
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
cacheBuffer: 500
|
||||||
|
cellHeight: stickerDimPad
|
||||||
|
cellWidth: stickerDimPad
|
||||||
clip: true
|
clip: true
|
||||||
currentIndex: -1 // prevent sorting from stealing focus
|
currentIndex: -1 // prevent sorting from stealing focus
|
||||||
cacheBuffer: 500
|
model: currentPack
|
||||||
|
|
||||||
|
|
||||||
// Individual emoji
|
// Individual emoji
|
||||||
delegate: AbstractButton {
|
delegate: AbstractButton {
|
||||||
width: stickerDim
|
|
||||||
height: stickerDim
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.text: ":" + model.shortCode + ": - " + model.body
|
ToolTip.text: ":" + model.shortCode + ": - " + model.body
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
|
height: stickerDim
|
||||||
contentItem: Image {
|
hoverEnabled: true
|
||||||
height: stickerDim
|
width: stickerDim
|
||||||
width: stickerDim
|
|
||||||
source: model.url.replace("mxc://", "image://MxcImage/") + "?scale"
|
|
||||||
fillMode: Image.PreserveAspectFit
|
|
||||||
}
|
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: hovered ? timelineRoot.palette.highlight : 'transparent'
|
color: hovered ? timelineRoot.palette.highlight : 'transparent'
|
||||||
radius: 5
|
radius: 5
|
||||||
}
|
}
|
||||||
|
contentItem: Image {
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
height: stickerDim
|
||||||
|
source: model.url.replace("mxc://", "image://MxcImage/") + "?scale"
|
||||||
|
width: stickerDim
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
footer: DialogButtonBox {
|
|
||||||
id: buttons
|
|
||||||
|
|
||||||
Button {
|
|
||||||
text: qsTr("Close")
|
|
||||||
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
|
||||||
onClicked: win.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
import "../"
|
||||||
import ".."
|
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.5
|
import QtQuick.Controls 2.5
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts 1.3
|
||||||
|
@ -12,53 +10,26 @@ import im.nheko
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
id: inputDialog
|
id: inputDialog
|
||||||
|
|
||||||
property alias prompt: promptLabel.text
|
|
||||||
property alias echoMode: statusInput.echoMode
|
property alias echoMode: statusInput.echoMode
|
||||||
property var onAccepted: undefined
|
property var onAccepted: undefined
|
||||||
|
property alias prompt: promptLabel.text
|
||||||
modality: Qt.NonModal
|
|
||||||
flags: Qt.Dialog
|
|
||||||
width: 350
|
|
||||||
height: fontMetrics.lineSpacing * 7
|
|
||||||
|
|
||||||
function forceActiveFocus() {
|
function forceActiveFocus() {
|
||||||
statusInput.forceActiveFocus();
|
statusInput.forceActiveFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
Shortcut {
|
flags: Qt.Dialog
|
||||||
sequence: StandardKey.Cancel
|
height: fontMetrics.lineSpacing * 7
|
||||||
onActivated: dbb.rejected()
|
modality: Qt.NonModal
|
||||||
}
|
width: 350
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
anchors.margins: Nheko.paddingMedium
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
Label {
|
|
||||||
id: promptLabel
|
|
||||||
|
|
||||||
color: timelineRoot.palette.text
|
|
||||||
}
|
|
||||||
|
|
||||||
MatrixTextField {
|
|
||||||
id: statusInput
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
onAccepted: dbb.accepted()
|
|
||||||
focus: true
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
footer: DialogButtonBox {
|
footer: DialogButtonBox {
|
||||||
id: dbb
|
id: dbb
|
||||||
|
|
||||||
standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
|
standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
|
||||||
|
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
if (inputDialog.onAccepted)
|
if (inputDialog.onAccepted)
|
||||||
inputDialog.onAccepted(statusInput.text);
|
inputDialog.onAccepted(statusInput.text);
|
||||||
|
|
||||||
inputDialog.close();
|
inputDialog.close();
|
||||||
}
|
}
|
||||||
onRejected: {
|
onRejected: {
|
||||||
|
@ -66,4 +37,26 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Shortcut {
|
||||||
|
sequence: StandardKey.Cancel
|
||||||
|
|
||||||
|
onActivated: dbb.rejected()
|
||||||
|
}
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Nheko.paddingMedium
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: promptLabel
|
||||||
|
color: timelineRoot.palette.text
|
||||||
|
}
|
||||||
|
MatrixTextField {
|
||||||
|
id: statusInput
|
||||||
|
Layout.fillWidth: true
|
||||||
|
focus: true
|
||||||
|
|
||||||
|
onAccepted: dbb.accepted()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
import "../"
|
||||||
import ".."
|
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.12
|
import QtQuick.Controls 2.12
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
|
@ -12,9 +10,9 @@ import im.nheko
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
id: inviteDialogRoot
|
id: inviteDialogRoot
|
||||||
|
|
||||||
property string roomId
|
|
||||||
property string plainRoomName
|
|
||||||
property InviteesModel invitees
|
property InviteesModel invitees
|
||||||
|
property string plainRoomName
|
||||||
|
property string roomId
|
||||||
|
|
||||||
function addInvite() {
|
function addInvite() {
|
||||||
if (inviteeEntry.isValidMxid) {
|
if (inviteeEntry.isValidMxid) {
|
||||||
|
@ -22,43 +20,57 @@ ApplicationWindow {
|
||||||
inviteeEntry.clear();
|
inviteeEntry.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function cleanUpAndClose() {
|
function cleanUpAndClose() {
|
||||||
if (inviteeEntry.isValidMxid)
|
if (inviteeEntry.isValidMxid)
|
||||||
addInvite();
|
addInvite();
|
||||||
|
|
||||||
invitees.accept();
|
invitees.accept();
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
title: qsTr("Invite users to %1").arg(plainRoomName)
|
|
||||||
height: 380
|
|
||||||
width: 340
|
|
||||||
palette: timelineRoot.palette
|
|
||||||
color: timelineRoot.palette.window
|
color: timelineRoot.palette.window
|
||||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
|
height: 380
|
||||||
|
palette: timelineRoot.palette
|
||||||
|
title: qsTr("Invite users to %1").arg(plainRoomName)
|
||||||
|
width: 340
|
||||||
|
|
||||||
|
footer: DialogButtonBox {
|
||||||
|
id: buttons
|
||||||
|
Button {
|
||||||
|
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
||||||
|
enabled: invitees.count > 0
|
||||||
|
text: qsTr("Invite")
|
||||||
|
|
||||||
|
onClicked: cleanUpAndClose()
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
DialogButtonBox.buttonRole: DialogButtonBox.DestructiveRole
|
||||||
|
text: qsTr("Cancel")
|
||||||
|
|
||||||
|
onClicked: inviteDialogRoot.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: "Ctrl+Enter"
|
sequence: "Ctrl+Enter"
|
||||||
|
|
||||||
onActivated: cleanUpAndClose()
|
onActivated: cleanUpAndClose()
|
||||||
}
|
}
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: StandardKey.Cancel
|
sequence: StandardKey.Cancel
|
||||||
|
|
||||||
onActivated: inviteDialogRoot.close()
|
onActivated: inviteDialogRoot.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Nheko.paddingMedium
|
anchors.margins: Nheko.paddingMedium
|
||||||
spacing: Nheko.paddingMedium
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("User ID to invite")
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
|
text: qsTr("User ID to invite")
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
spacing: Nheko.paddingMedium
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
|
@ -67,120 +79,88 @@ ApplicationWindow {
|
||||||
|
|
||||||
property bool isValidMxid: text.match("@.+?:.{3,}")
|
property bool isValidMxid: text.match("@.+?:.{3,}")
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
backgroundColor: timelineRoot.palette.window
|
backgroundColor: timelineRoot.palette.window
|
||||||
placeholderText: qsTr("@joe:matrix.org", "Example user id. The name 'joe' can be localized however you want.")
|
placeholderText: qsTr("@joe:matrix.org", "Example user id. The name 'joe' can be localized however you want.")
|
||||||
Layout.fillWidth: true
|
|
||||||
onAccepted: {
|
|
||||||
if (isValidMxid)
|
|
||||||
addInvite();
|
|
||||||
|
|
||||||
}
|
|
||||||
Component.onCompleted: forceActiveFocus()
|
Component.onCompleted: forceActiveFocus()
|
||||||
Keys.onShortcutOverride: event.accepted = ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && (event.modifiers & Qt.ControlModifier))
|
|
||||||
Keys.onPressed: {
|
Keys.onPressed: {
|
||||||
if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && (event.modifiers === Qt.ControlModifier))
|
if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && (event.modifiers === Qt.ControlModifier))
|
||||||
cleanUpAndClose();
|
cleanUpAndClose();
|
||||||
|
}
|
||||||
|
Keys.onShortcutOverride: event.accepted = ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && (event.modifiers & Qt.ControlModifier))
|
||||||
|
onAccepted: {
|
||||||
|
if (isValidMxid)
|
||||||
|
addInvite();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: qsTr("Add")
|
|
||||||
enabled: inviteeEntry.isValidMxid
|
enabled: inviteeEntry.isValidMxid
|
||||||
|
text: qsTr("Add")
|
||||||
|
|
||||||
onClicked: addInvite()
|
onClicked: addInvite()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: inviteesList
|
id: inviteesList
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
model: invitees
|
model: invitees
|
||||||
|
|
||||||
delegate: ItemDelegate {
|
delegate: ItemDelegate {
|
||||||
id: del
|
id: del
|
||||||
|
height: layout.implicitHeight + Nheko.paddingSmall * 2
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
width: ListView.view.width
|
width: ListView.view.width
|
||||||
height: layout.implicitHeight + Nheko.paddingSmall * 2
|
|
||||||
onClicked: TimelineManager.openGlobalUserProfile(model.mxid)
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: del.hovered ? timelineRoot.palette.dark : inviteDialogRoot.color
|
color: del.hovered ? timelineRoot.palette.dark : inviteDialogRoot.color
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onClicked: TimelineManager.openGlobalUserProfile(model.mxid)
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: layout
|
id: layout
|
||||||
|
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
width: del.width - Nheko.paddingSmall * 2
|
width: del.width - Nheko.paddingSmall * 2
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
width: Nheko.avatarSize
|
|
||||||
height: Nheko.avatarSize
|
|
||||||
userid: model.mxid
|
|
||||||
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
|
||||||
displayName: model.displayName
|
displayName: model.displayName
|
||||||
enabled: false
|
enabled: false
|
||||||
|
height: Nheko.avatarSize
|
||||||
|
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
|
userid: model.mxid
|
||||||
|
width: Nheko.avatarSize
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
spacing: Nheko.paddingSmall
|
spacing: Nheko.paddingSmall
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: model.displayName
|
|
||||||
color: TimelineManager.userColor(model ? model.mxid : "", del.background.color)
|
color: TimelineManager.userColor(model ? model.mxid : "", del.background.color)
|
||||||
font.pointSize: fontMetrics.font.pointSize
|
font.pointSize: fontMetrics.font.pointSize
|
||||||
|
text: model.displayName
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: model.mxid
|
|
||||||
color: del.hovered ? timelineRoot.palette.brightText : timelineRoot.palette.placeholderText
|
color: del.hovered ? timelineRoot.palette.brightText : timelineRoot.palette.placeholderText
|
||||||
font.pointSize: fontMetrics.font.pointSize * 0.9
|
font.pointSize: fontMetrics.font.pointSize * 0.9
|
||||||
|
text: model.mxid
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
image: ":/icons/icons/ui/dismiss.svg"
|
image: ":/icons/icons/ui/dismiss.svg"
|
||||||
|
|
||||||
onClicked: invitees.removeUser(model.mxid)
|
onClicked: invitees.removeUser(model.mxid)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NhekoCursorShape {
|
NhekoCursorShape {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
footer: DialogButtonBox {
|
|
||||||
id: buttons
|
|
||||||
|
|
||||||
Button {
|
|
||||||
text: qsTr("Invite")
|
|
||||||
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
|
||||||
enabled: invitees.count > 0
|
|
||||||
onClicked: cleanUpAndClose()
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
text: qsTr("Cancel")
|
|
||||||
DialogButtonBox.buttonRole: DialogButtonBox.DestructiveRole
|
|
||||||
onClicked: inviteDialogRoot.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
import "../"
|
||||||
import ".."
|
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.5
|
import QtQuick.Controls 2.5
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts 1.3
|
||||||
|
@ -11,50 +9,18 @@ import im.nheko
|
||||||
|
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
id: joinRoomRoot
|
id: joinRoomRoot
|
||||||
|
|
||||||
title: qsTr("Join room")
|
|
||||||
modality: Qt.WindowModal
|
|
||||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
|
||||||
palette: timelineRoot.palette
|
|
||||||
color: timelineRoot.palette.window
|
color: timelineRoot.palette.window
|
||||||
width: 350
|
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
height: fontMetrics.lineSpacing * 7
|
height: fontMetrics.lineSpacing * 7
|
||||||
|
modality: Qt.WindowModal
|
||||||
Shortcut {
|
palette: timelineRoot.palette
|
||||||
sequence: StandardKey.Cancel
|
title: qsTr("Join room")
|
||||||
onActivated: dbb.rejected()
|
width: 350
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
anchors.margins: Nheko.paddingMedium
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
Label {
|
|
||||||
id: promptLabel
|
|
||||||
|
|
||||||
text: qsTr("Room ID or alias")
|
|
||||||
color: timelineRoot.palette.text
|
|
||||||
}
|
|
||||||
|
|
||||||
MatrixTextField {
|
|
||||||
id: input
|
|
||||||
|
|
||||||
focus: true
|
|
||||||
Layout.fillWidth: true
|
|
||||||
onAccepted: {
|
|
||||||
if (input.text.match("#.+?:.{3,}"))
|
|
||||||
dbb.accepted();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
footer: DialogButtonBox {
|
footer: DialogButtonBox {
|
||||||
id: dbb
|
id: dbb
|
||||||
|
|
||||||
standardButtons: DialogButtonBox.Cancel
|
standardButtons: DialogButtonBox.Cancel
|
||||||
|
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
Nheko.joinRoom(input.text);
|
Nheko.joinRoom(input.text);
|
||||||
joinRoomRoot.close();
|
joinRoomRoot.close();
|
||||||
|
@ -64,11 +30,36 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: "Join"
|
|
||||||
enabled: input.text.match("#.+?:.{3,}")
|
|
||||||
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
||||||
|
enabled: input.text.match("#.+?:.{3,}")
|
||||||
|
text: "Join"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Shortcut {
|
||||||
|
sequence: StandardKey.Cancel
|
||||||
|
|
||||||
|
onActivated: dbb.rejected()
|
||||||
|
}
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Nheko.paddingMedium
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: promptLabel
|
||||||
|
color: timelineRoot.palette.text
|
||||||
|
text: qsTr("Room ID or alias")
|
||||||
|
}
|
||||||
|
MatrixTextField {
|
||||||
|
id: input
|
||||||
|
Layout.fillWidth: true
|
||||||
|
focus: true
|
||||||
|
|
||||||
|
onAccepted: {
|
||||||
|
if (input.text.match("#.+?:.{3,}"))
|
||||||
|
dbb.accepted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import Qt.labs.platform 1.1 as P
|
import Qt.labs.platform 1.1 as P
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
|
@ -11,12 +9,13 @@ import im.nheko
|
||||||
P.MessageDialog {
|
P.MessageDialog {
|
||||||
id: leaveRoomRoot
|
id: leaveRoomRoot
|
||||||
|
|
||||||
required property string roomId
|
|
||||||
property string reason: ""
|
property string reason: ""
|
||||||
|
required property string roomId
|
||||||
|
|
||||||
title: qsTr("Leave room")
|
|
||||||
text: qsTr("Are you sure you want to leave?")
|
|
||||||
modality: Qt.ApplicationModal
|
|
||||||
buttons: P.MessageDialog.Ok | P.MessageDialog.Cancel
|
buttons: P.MessageDialog.Ok | P.MessageDialog.Cancel
|
||||||
|
modality: Qt.ApplicationModal
|
||||||
|
text: qsTr("Are you sure you want to leave?")
|
||||||
|
title: qsTr("Leave room")
|
||||||
|
|
||||||
onAccepted: Rooms.leave(roomId, reason)
|
onAccepted: Rooms.leave(roomId, reason)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import Qt.labs.platform 1.1 as P
|
import Qt.labs.platform 1.1 as P
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
|
@ -10,11 +8,11 @@ import im.nheko
|
||||||
|
|
||||||
P.MessageDialog {
|
P.MessageDialog {
|
||||||
id: logoutRoot
|
id: logoutRoot
|
||||||
|
|
||||||
title: qsTr("Log out")
|
|
||||||
text: CallManager.isOnCall ? qsTr("A call is in progress. Log out?") : qsTr("Are you sure you want to log out?")
|
|
||||||
modality: Qt.WindowModal
|
|
||||||
flags: Qt.Tool | Qt.WindowStaysOnTopHint | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
|
||||||
buttons: P.MessageDialog.Ok | P.MessageDialog.Cancel
|
buttons: P.MessageDialog.Ok | P.MessageDialog.Cancel
|
||||||
|
flags: Qt.Tool | Qt.WindowStaysOnTopHint | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
|
modality: Qt.WindowModal
|
||||||
|
text: CallManager.isOnCall ? qsTr("A call is in progress. Log out?") : qsTr("Are you sure you want to log out?")
|
||||||
|
title: qsTr("Log out")
|
||||||
|
|
||||||
onAccepted: Nheko.logout()
|
onAccepted: Nheko.logout()
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
import im.nheko
|
import im.nheko
|
||||||
|
@ -12,44 +10,40 @@ ApplicationWindow {
|
||||||
|
|
||||||
property alias rawMessage: rawMessageView.text
|
property alias rawMessage: rawMessageView.text
|
||||||
|
|
||||||
height: 420
|
|
||||||
width: 420
|
|
||||||
palette: timelineRoot.palette
|
|
||||||
color: timelineRoot.palette.window
|
color: timelineRoot.palette.window
|
||||||
flags: Qt.Tool | Qt.WindowStaysOnTopHint | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
flags: Qt.Tool | Qt.WindowStaysOnTopHint | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
|
height: 420
|
||||||
|
palette: timelineRoot.palette
|
||||||
|
width: 420
|
||||||
|
|
||||||
|
footer: DialogButtonBox {
|
||||||
|
standardButtons: DialogButtonBox.Ok
|
||||||
|
|
||||||
|
onAccepted: rawMessageRoot.close()
|
||||||
|
}
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: StandardKey.Cancel
|
sequence: StandardKey.Cancel
|
||||||
|
|
||||||
onActivated: rawMessageRoot.close()
|
onActivated: rawMessageRoot.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollView {
|
ScrollView {
|
||||||
anchors.margins: Nheko.paddingMedium
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
palette: timelineRoot.palette
|
anchors.margins: Nheko.paddingMedium
|
||||||
padding: Nheko.paddingMedium
|
padding: Nheko.paddingMedium
|
||||||
|
palette: timelineRoot.palette
|
||||||
|
|
||||||
TextArea {
|
TextArea {
|
||||||
id: rawMessageView
|
id: rawMessageView
|
||||||
|
anchors.fill: parent
|
||||||
font: Nheko.monospaceFont()
|
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
|
font: Nheko.monospaceFont()
|
||||||
readOnly: true
|
readOnly: true
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: timelineRoot.palette.base
|
color: timelineRoot.palette.base
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
footer: DialogButtonBox {
|
|
||||||
standardButtons: DialogButtonBox.Ok
|
|
||||||
onAccepted: rawMessageRoot.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
import "../"
|
||||||
import ".."
|
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
import QtQuick.Layouts 1.15
|
import QtQuick.Layouts 1.15
|
||||||
|
@ -15,19 +13,25 @@ ApplicationWindow {
|
||||||
property ReadReceiptsProxy readReceipts
|
property ReadReceiptsProxy readReceipts
|
||||||
property Room room
|
property Room room
|
||||||
|
|
||||||
|
color: timelineRoot.palette.window
|
||||||
|
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
height: 380
|
height: 380
|
||||||
width: 340
|
|
||||||
minimumHeight: 380
|
minimumHeight: 380
|
||||||
minimumWidth: headerTitle.width + 2 * Nheko.paddingMedium
|
minimumWidth: headerTitle.width + 2 * Nheko.paddingMedium
|
||||||
palette: timelineRoot.palette
|
palette: timelineRoot.palette
|
||||||
color: timelineRoot.palette.window
|
width: 340
|
||||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
|
||||||
|
footer: DialogButtonBox {
|
||||||
|
standardButtons: DialogButtonBox.Ok
|
||||||
|
|
||||||
|
onAccepted: readReceiptsRoot.close()
|
||||||
|
}
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: StandardKey.Cancel
|
sequence: StandardKey.Cancel
|
||||||
|
|
||||||
onActivated: readReceiptsRoot.close()
|
onActivated: readReceiptsRoot.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Nheko.paddingMedium
|
anchors.margins: Nheko.paddingMedium
|
||||||
|
@ -35,97 +39,78 @@ ApplicationWindow {
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: headerTitle
|
id: headerTitle
|
||||||
|
|
||||||
color: timelineRoot.palette.text
|
|
||||||
Layout.alignment: Qt.AlignCenter
|
Layout.alignment: Qt.AlignCenter
|
||||||
text: qsTr("Read receipts")
|
color: timelineRoot.palette.text
|
||||||
font.pointSize: fontMetrics.font.pointSize * 1.5
|
font.pointSize: fontMetrics.font.pointSize * 1.5
|
||||||
|
text: qsTr("Read receipts")
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollView {
|
ScrollView {
|
||||||
palette: timelineRoot.palette
|
|
||||||
padding: Nheko.paddingMedium
|
|
||||||
ScrollBar.horizontal.visible: false
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.minimumHeight: 200
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.minimumHeight: 200
|
||||||
|
ScrollBar.horizontal.visible: false
|
||||||
|
padding: Nheko.paddingMedium
|
||||||
|
palette: timelineRoot.palette
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: readReceiptsList
|
id: readReceiptsList
|
||||||
|
|
||||||
clip: true
|
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
clip: true
|
||||||
model: readReceipts
|
model: readReceipts
|
||||||
|
|
||||||
delegate: ItemDelegate {
|
delegate: ItemDelegate {
|
||||||
id: del
|
id: del
|
||||||
|
ToolTip.text: model.mxid
|
||||||
onClicked: room.openUserProfile(model.mxid)
|
ToolTip.visible: hovered
|
||||||
padding: Nheko.paddingMedium
|
|
||||||
width: ListView.view.width
|
|
||||||
height: receiptLayout.implicitHeight + Nheko.paddingSmall * 2
|
height: receiptLayout.implicitHeight + Nheko.paddingSmall * 2
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
ToolTip.visible: hovered
|
padding: Nheko.paddingMedium
|
||||||
ToolTip.text: model.mxid
|
width: ListView.view.width
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: del.hovered ? timelineRoot.palette.dark : readReceiptsRoot.color
|
color: del.hovered ? timelineRoot.palette.dark : readReceiptsRoot.color
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onClicked: room.openUserProfile(model.mxid)
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: receiptLayout
|
id: receiptLayout
|
||||||
|
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Nheko.paddingSmall
|
anchors.margins: Nheko.paddingSmall
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
width: Nheko.avatarSize
|
|
||||||
height: Nheko.avatarSize
|
|
||||||
userid: model.mxid
|
|
||||||
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
|
||||||
displayName: model.displayName
|
displayName: model.displayName
|
||||||
enabled: false
|
enabled: false
|
||||||
|
height: Nheko.avatarSize
|
||||||
|
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
|
userid: model.mxid
|
||||||
|
width: Nheko.avatarSize
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
spacing: Nheko.paddingSmall
|
spacing: Nheko.paddingSmall
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: model.displayName
|
|
||||||
color: TimelineManager.userColor(model ? model.mxid : "", timelineRoot.palette.window)
|
color: TimelineManager.userColor(model ? model.mxid : "", timelineRoot.palette.window)
|
||||||
font.pointSize: fontMetrics.font.pointSize
|
font.pointSize: fontMetrics.font.pointSize
|
||||||
|
text: model.displayName
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: model.timestamp
|
|
||||||
color: timelineRoot.palette.placeholderText
|
color: timelineRoot.palette.placeholderText
|
||||||
font.pointSize: fontMetrics.font.pointSize * 0.9
|
font.pointSize: fontMetrics.font.pointSize * 0.9
|
||||||
|
text: model.timestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NhekoCursorShape {
|
NhekoCursorShape {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
footer: DialogButtonBox {
|
|
||||||
standardButtons: DialogButtonBox.Ok
|
|
||||||
onAccepted: readReceiptsRoot.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
import "../"
|
||||||
import ".."
|
|
||||||
import "../ui"
|
import "../ui"
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
|
@ -13,181 +11,157 @@ import im.nheko
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
id: roomDirectoryWindow
|
id: roomDirectoryWindow
|
||||||
|
|
||||||
property RoomDirectoryModel publicRooms
|
property RoomDirectoryModel publicRooms: RoomDirectoryModel {
|
||||||
|
}
|
||||||
|
|
||||||
visible: true
|
|
||||||
minimumWidth: 340
|
|
||||||
minimumHeight: 340
|
|
||||||
height: 420
|
|
||||||
width: 650
|
|
||||||
palette: timelineRoot.palette
|
|
||||||
color: timelineRoot.palette.window
|
color: timelineRoot.palette.window
|
||||||
modality: Qt.WindowModal
|
|
||||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
|
height: 420
|
||||||
|
minimumHeight: 340
|
||||||
|
minimumWidth: 340
|
||||||
|
modality: Qt.WindowModal
|
||||||
|
palette: timelineRoot.palette
|
||||||
title: qsTr("Explore Public Rooms")
|
title: qsTr("Explore Public Rooms")
|
||||||
|
visible: true
|
||||||
|
width: 650
|
||||||
|
|
||||||
|
header: RowLayout {
|
||||||
|
id: searchBarLayout
|
||||||
|
implicitHeight: roomSearch.height
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
MatrixTextField {
|
||||||
|
id: roomSearch
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: timelineRoot.palette.text
|
||||||
|
focus: true
|
||||||
|
font.pixelSize: fontMetrics.font.pixelSize
|
||||||
|
placeholderText: qsTr("Search for public rooms")
|
||||||
|
selectByMouse: true
|
||||||
|
|
||||||
|
onTextChanged: searchTimer.restart()
|
||||||
|
}
|
||||||
|
MatrixTextField {
|
||||||
|
id: chooseServer
|
||||||
|
Layout.maximumWidth: 0.3 * header.width
|
||||||
|
Layout.minimumWidth: 0.3 * header.width
|
||||||
|
color: timelineRoot.palette.text
|
||||||
|
placeholderText: qsTr("Choose custom homeserver")
|
||||||
|
|
||||||
|
onTextChanged: publicRooms.setMatrixServer(text)
|
||||||
|
}
|
||||||
|
Timer {
|
||||||
|
id: searchTimer
|
||||||
|
interval: 350
|
||||||
|
|
||||||
|
onTriggered: roomDirView.model.setSearchTerm(roomSearch.text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: StandardKey.Cancel
|
sequence: StandardKey.Cancel
|
||||||
|
|
||||||
onActivated: roomDirectoryWindow.close()
|
onActivated: roomDirectoryWindow.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: roomDirView
|
id: roomDirView
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
model: publicRooms
|
model: publicRooms
|
||||||
|
|
||||||
delegate: Rectangle {
|
delegate: Rectangle {
|
||||||
id: roomDirDelegate
|
id: roomDirDelegate
|
||||||
|
|
||||||
|
property int avatarSize: fontMetrics.height * 3.2
|
||||||
property color background: timelineRoot.palette.window
|
property color background: timelineRoot.palette.window
|
||||||
property color importantText: timelineRoot.palette.text
|
property color importantText: timelineRoot.palette.text
|
||||||
property color unimportantText: timelineRoot.palette.placeholderText
|
property color unimportantText: timelineRoot.palette.placeholderText
|
||||||
property int avatarSize: fontMetrics.height * 3.2
|
|
||||||
|
|
||||||
color: background
|
color: background
|
||||||
height: avatarSize + Nheko.paddingLarge
|
height: avatarSize + Nheko.paddingLarge
|
||||||
width: ListView.view.width
|
width: ListView.view.width
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Nheko.paddingMedium
|
anchors.margins: Nheko.paddingMedium
|
||||||
implicitHeight: textContent.implicitHeight
|
implicitHeight: textContent.implicitHeight
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
id: roomAvatar
|
id: roomAvatar
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
Layout.rightMargin: Nheko.paddingMedium
|
Layout.rightMargin: Nheko.paddingMedium
|
||||||
width: avatarSize
|
|
||||||
height: avatarSize
|
|
||||||
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
|
||||||
roomid: model.roomid
|
|
||||||
displayName: model.name
|
displayName: model.name
|
||||||
|
height: avatarSize
|
||||||
|
roomid: model.roomid
|
||||||
|
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
|
width: avatarSize
|
||||||
}
|
}
|
||||||
|
|
||||||
GridLayout {
|
GridLayout {
|
||||||
id: textContent
|
id: textContent
|
||||||
rows: 2
|
|
||||||
columns: 2
|
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
width: parent.width - avatar.width
|
|
||||||
Layout.preferredWidth: parent.width - avatar.width
|
Layout.preferredWidth: parent.width - avatar.width
|
||||||
|
columns: 2
|
||||||
|
rows: 2
|
||||||
|
width: parent.width - avatar.width
|
||||||
|
|
||||||
ElidedLabel {
|
ElidedLabel {
|
||||||
Layout.row: 0
|
|
||||||
Layout.column: 0
|
Layout.column: 0
|
||||||
Layout.fillWidth:true
|
Layout.fillWidth: true
|
||||||
|
Layout.row: 0
|
||||||
color: roomDirDelegate.importantText
|
color: roomDirDelegate.importantText
|
||||||
elideWidth: width
|
elideWidth: width
|
||||||
fullText: model.name
|
fullText: model.name
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: roomTopic
|
id: roomTopic
|
||||||
|
|
||||||
color: roomDirDelegate.unimportantText
|
|
||||||
Layout.row: 1
|
|
||||||
Layout.column: 0
|
Layout.column: 0
|
||||||
font.pointSize: fontMetrics.font.pointSize*0.9
|
|
||||||
elide: Text.ElideRight
|
|
||||||
maximumLineCount: 2
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.row: 1
|
||||||
|
color: roomDirDelegate.unimportantText
|
||||||
|
elide: Text.ElideRight
|
||||||
|
font.pointSize: fontMetrics.font.pointSize * 0.9
|
||||||
|
maximumLineCount: 2
|
||||||
text: model.topic
|
text: model.topic
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.row: 0
|
|
||||||
Layout.column: 1
|
|
||||||
id: roomCount
|
id: roomCount
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.column: 1
|
||||||
|
Layout.row: 0
|
||||||
color: roomDirDelegate.unimportantText
|
color: roomDirDelegate.unimportantText
|
||||||
font.pointSize: fontMetrics.font.pointSize*0.9
|
font.pointSize: fontMetrics.font.pointSize * 0.9
|
||||||
text: model.numMembers.toString()
|
text: model.numMembers.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
Layout.row: 1
|
|
||||||
Layout.column: 1
|
|
||||||
id: joinRoomButton
|
id: joinRoomButton
|
||||||
|
Layout.column: 1
|
||||||
|
Layout.row: 1
|
||||||
enabled: model.canJoin
|
enabled: model.canJoin
|
||||||
text: "Join"
|
text: "Join"
|
||||||
|
|
||||||
onClicked: publicRooms.joinRoom(model.index)
|
onClicked: publicRooms.joinRoom(model.index)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
footer: Item {
|
footer: Item {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
width: parent.width
|
anchors.margins: Nheko.paddingLarge
|
||||||
visible: !publicRooms.reachedEndOfPagination && publicRooms.loadingMoreRooms
|
|
||||||
// hacky but works
|
// hacky but works
|
||||||
height: loadingSpinner.height + 2 * Nheko.paddingLarge
|
height: loadingSpinner.height + 2 * Nheko.paddingLarge
|
||||||
anchors.margins: Nheko.paddingLarge
|
visible: !publicRooms.reachedEndOfPagination && publicRooms.loadingMoreRooms
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
Spinner {
|
Spinner {
|
||||||
id: loadingSpinner
|
id: loadingSpinner
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
anchors.margins: Nheko.paddingLarge
|
anchors.margins: Nheko.paddingLarge
|
||||||
running: visible
|
|
||||||
foreground: timelineRoot.palette.mid
|
foreground: timelineRoot.palette.mid
|
||||||
|
running: visible
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
publicRooms: RoomDirectoryModel {
|
|
||||||
}
|
|
||||||
|
|
||||||
header: RowLayout {
|
|
||||||
id: searchBarLayout
|
|
||||||
|
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
width: parent.width
|
|
||||||
implicitHeight: roomSearch.height
|
|
||||||
|
|
||||||
MatrixTextField {
|
|
||||||
id: roomSearch
|
|
||||||
|
|
||||||
focus: true
|
|
||||||
Layout.fillWidth: true
|
|
||||||
selectByMouse: true
|
|
||||||
font.pixelSize: fontMetrics.font.pixelSize
|
|
||||||
color: timelineRoot.palette.text
|
|
||||||
placeholderText: qsTr("Search for public rooms")
|
|
||||||
onTextChanged: searchTimer.restart()
|
|
||||||
|
|
||||||
Component.onCompleted: forceActiveFocus()
|
|
||||||
}
|
|
||||||
|
|
||||||
MatrixTextField {
|
|
||||||
id: chooseServer
|
|
||||||
|
|
||||||
Layout.minimumWidth: 0.3 * header.width
|
|
||||||
Layout.maximumWidth: 0.3 * header.width
|
|
||||||
color: timelineRoot.palette.text
|
|
||||||
placeholderText: qsTr("Choose custom homeserver")
|
|
||||||
onTextChanged: publicRooms.setMatrixServer(text)
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: searchTimer
|
|
||||||
|
|
||||||
interval: 350
|
|
||||||
onTriggered: roomDirView.model.setSearchTerm(roomSearch.text)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
import "../"
|
||||||
import ".."
|
|
||||||
import "../ui"
|
import "../ui"
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.12
|
import QtQuick.Controls 2.12
|
||||||
|
@ -17,19 +15,25 @@ ApplicationWindow {
|
||||||
property MemberList members
|
property MemberList members
|
||||||
property Room room
|
property Room room
|
||||||
|
|
||||||
title: qsTr("Members of %1").arg(members.roomName)
|
|
||||||
height: 650
|
|
||||||
width: 420
|
|
||||||
minimumHeight: 420
|
|
||||||
palette: timelineRoot.palette
|
|
||||||
color: timelineRoot.palette.window
|
color: timelineRoot.palette.window
|
||||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
|
height: 650
|
||||||
|
minimumHeight: 420
|
||||||
|
palette: timelineRoot.palette
|
||||||
|
title: qsTr("Members of %1").arg(members.roomName)
|
||||||
|
width: 420
|
||||||
|
|
||||||
|
footer: DialogButtonBox {
|
||||||
|
standardButtons: DialogButtonBox.Ok
|
||||||
|
|
||||||
|
onAccepted: roomMembersRoot.close()
|
||||||
|
}
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: StandardKey.Cancel
|
sequence: StandardKey.Cancel
|
||||||
|
|
||||||
onActivated: roomMembersRoot.close()
|
onActivated: roomMembersRoot.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Nheko.paddingMedium
|
anchors.margins: Nheko.paddingMedium
|
||||||
|
@ -37,143 +41,135 @@ ApplicationWindow {
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
id: roomAvatar
|
id: roomAvatar
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
width: 130
|
displayName: members.roomName
|
||||||
height: width
|
height: width
|
||||||
roomid: members.roomId
|
roomid: members.roomId
|
||||||
displayName: members.roomName
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
url: members.avatarUrl.replace("mxc://", "image://MxcImage/")
|
url: members.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
|
width: 130
|
||||||
|
|
||||||
onClicked: TimelineManager.openRoomSettings(members.roomId)
|
onClicked: TimelineManager.openRoomSettings(members.roomId)
|
||||||
}
|
}
|
||||||
|
|
||||||
ElidedLabel {
|
ElidedLabel {
|
||||||
font.pixelSize: fontMetrics.font.pixelSize * 2
|
|
||||||
fullText: qsTr("%n people in %1", "Summary above list of members", members.memberCount).arg(members.roomName)
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
elideWidth: parent.width - Nheko.paddingMedium
|
elideWidth: parent.width - Nheko.paddingMedium
|
||||||
|
font.pixelSize: fontMetrics.font.pixelSize * 2
|
||||||
|
fullText: qsTr("%n people in %1", "Summary above list of members", members.memberCount).arg(members.roomName)
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
image: ":/icons/icons/ui/add-square-button.svg"
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Invite more people")
|
ToolTip.text: qsTr("Invite more people")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/add-square-button.svg"
|
||||||
|
|
||||||
onClicked: TimelineManager.openInviteUsers(members.roomId)
|
onClicked: TimelineManager.openInviteUsers(members.roomId)
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
id: searchBar
|
id: searchBar
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
placeholderText: qsTr("Search...")
|
placeholderText: qsTr("Search...")
|
||||||
onTextChanged: members.setFilterString(text)
|
|
||||||
|
|
||||||
Component.onCompleted: forceActiveFocus()
|
Component.onCompleted: forceActiveFocus()
|
||||||
|
onTextChanged: members.setFilterString(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
spacing: Nheko.paddingMedium
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("Sort by: ")
|
|
||||||
color: Nheko.colors.text
|
color: Nheko.colors.text
|
||||||
|
text: qsTr("Sort by: ")
|
||||||
}
|
}
|
||||||
|
|
||||||
ComboBox {
|
ComboBox {
|
||||||
model: ListModel {
|
Layout.fillWidth: true
|
||||||
ListElement { data: MemberList.Mxid; text: qsTr("User ID") }
|
|
||||||
ListElement { data: MemberList.DisplayName; text: qsTr("Display name") }
|
|
||||||
ListElement { data: MemberList.Powerlevel; text: qsTr("Power level") }
|
|
||||||
}
|
|
||||||
textRole: "text"
|
textRole: "text"
|
||||||
valueRole: "data"
|
valueRole: "data"
|
||||||
|
|
||||||
|
model: ListModel {
|
||||||
|
ListElement {
|
||||||
|
data: MemberList.Mxid
|
||||||
|
text: qsTr("User ID")
|
||||||
|
}
|
||||||
|
ListElement {
|
||||||
|
data: MemberList.DisplayName
|
||||||
|
text: qsTr("Display name")
|
||||||
|
}
|
||||||
|
ListElement {
|
||||||
|
data: MemberList.Powerlevel
|
||||||
|
text: qsTr("Power level")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onCurrentValueChanged: members.sortBy(currentValue)
|
onCurrentValueChanged: members.sortBy(currentValue)
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollView {
|
ScrollView {
|
||||||
palette: timelineRoot.palette
|
|
||||||
padding: Nheko.paddingMedium
|
|
||||||
ScrollBar.horizontal.visible: false
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.minimumHeight: 200
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.minimumHeight: 200
|
||||||
|
ScrollBar.horizontal.visible: false
|
||||||
|
padding: Nheko.paddingMedium
|
||||||
|
palette: timelineRoot.palette
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: memberList
|
id: memberList
|
||||||
|
|
||||||
clip: true
|
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
clip: true
|
||||||
model: members
|
model: members
|
||||||
|
|
||||||
|
|
||||||
delegate: ItemDelegate {
|
delegate: ItemDelegate {
|
||||||
id: del
|
id: del
|
||||||
|
|
||||||
onClicked: Rooms.currentRoom.openUserProfile(model.mxid)
|
|
||||||
padding: Nheko.paddingMedium
|
|
||||||
width: ListView.view.width
|
|
||||||
height: memberLayout.implicitHeight + Nheko.paddingSmall * 2
|
height: memberLayout.implicitHeight + Nheko.paddingSmall * 2
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
|
padding: Nheko.paddingMedium
|
||||||
|
width: ListView.view.width
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: del.hovered ? timelineRoot.palette.dark : roomMembersRoot.color
|
color: del.hovered ? timelineRoot.palette.dark : roomMembersRoot.color
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onClicked: Rooms.currentRoom.openUserProfile(model.mxid)
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: memberLayout
|
id: memberLayout
|
||||||
|
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
width: parent.width - Nheko.paddingSmall * 2
|
width: parent.width - Nheko.paddingSmall * 2
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
id: avatar
|
id: avatar
|
||||||
|
|
||||||
width: Nheko.avatarSize
|
|
||||||
height: Nheko.avatarSize
|
|
||||||
userid: model.mxid
|
|
||||||
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
|
||||||
displayName: model.displayName
|
displayName: model.displayName
|
||||||
enabled: false
|
enabled: false
|
||||||
|
height: Nheko.avatarSize
|
||||||
|
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
|
userid: model.mxid
|
||||||
|
width: Nheko.avatarSize
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
spacing: Nheko.paddingSmall
|
spacing: Nheko.paddingSmall
|
||||||
|
|
||||||
ElidedLabel {
|
ElidedLabel {
|
||||||
fullText: model.displayName
|
|
||||||
color: TimelineManager.userColor(model ? model.mxid : "", del.background.color)
|
color: TimelineManager.userColor(model ? model.mxid : "", del.background.color)
|
||||||
|
elideWidth: del.width - Nheko.paddingMedium * 2 - avatar.width - encryptInd.width
|
||||||
font.pixelSize: fontMetrics.font.pixelSize
|
font.pixelSize: fontMetrics.font.pixelSize
|
||||||
elideWidth: del.width - Nheko.paddingMedium * 2 - avatar.width - encryptInd.width
|
fullText: model.displayName
|
||||||
}
|
}
|
||||||
|
|
||||||
ElidedLabel {
|
ElidedLabel {
|
||||||
fullText: model.mxid
|
|
||||||
color: del.hovered ? timelineRoot.palette.brightText : timelineRoot.palette.placeholderText
|
color: del.hovered ? timelineRoot.palette.brightText : timelineRoot.palette.placeholderText
|
||||||
font.pixelSize: Math.ceil(fontMetrics.font.pixelSize * 0.9)
|
|
||||||
elideWidth: del.width - Nheko.paddingMedium * 2 - avatar.width - encryptInd.width
|
elideWidth: del.width - Nheko.paddingMedium * 2 - avatar.width - encryptInd.width
|
||||||
|
font.pixelSize: Math.ceil(fontMetrics.font.pixelSize * 0.9)
|
||||||
|
fullText: model.mxid
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
EncryptionIndicator {
|
EncryptionIndicator {
|
||||||
id: encryptInd
|
id: encryptInd
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
visible: room.isEncrypted
|
|
||||||
encrypted: room.isEncrypted
|
|
||||||
trust: encrypted ? model.trustlevel : Crypto.Unverified
|
|
||||||
ToolTip.text: {
|
ToolTip.text: {
|
||||||
if (!encrypted)
|
if (!encrypted)
|
||||||
return qsTr("This room is not encrypted!");
|
return qsTr("This room is not encrypted!");
|
||||||
|
|
||||||
switch (trust) {
|
switch (trust) {
|
||||||
case Crypto.Verified:
|
case Crypto.Verified:
|
||||||
return qsTr("This user is verified.");
|
return qsTr("This user is verified.");
|
||||||
|
@ -183,42 +179,30 @@ ApplicationWindow {
|
||||||
return qsTr("This user has unverified devices!");
|
return qsTr("This user has unverified devices!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
encrypted: room.isEncrypted
|
||||||
|
trust: encrypted ? model.trustlevel : Crypto.Unverified
|
||||||
|
visible: room.isEncrypted
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NhekoCursorShape {
|
NhekoCursorShape {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
footer: Item {
|
footer: Item {
|
||||||
width: parent.width
|
anchors.margins: Nheko.paddingMedium
|
||||||
visible: (members.numUsersLoaded < members.memberCount) && members.loadingMoreMembers
|
|
||||||
// use the default height if it's visible, otherwise no height at all
|
// use the default height if it's visible, otherwise no height at all
|
||||||
height: membersLoadingSpinner.implicitHeight
|
height: membersLoadingSpinner.implicitHeight
|
||||||
anchors.margins: Nheko.paddingMedium
|
visible: (members.numUsersLoaded < members.memberCount) && members.loadingMoreMembers
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
Spinner {
|
Spinner {
|
||||||
id: membersLoadingSpinner
|
id: membersLoadingSpinner
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
implicitHeight: parent.visible ? 35 : 0
|
implicitHeight: parent.visible ? 35 : 0
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
footer: DialogButtonBox {
|
|
||||||
standardButtons: DialogButtonBox.Ok
|
|
||||||
onAccepted: roomMembersRoot.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
import "../"
|
||||||
import ".."
|
|
||||||
import "../ui"
|
import "../ui"
|
||||||
import Qt.labs.platform 1.1 as Platform
|
import Qt.labs.platform 1.1 as Platform
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
|
@ -18,112 +16,109 @@ ApplicationWindow {
|
||||||
|
|
||||||
property var roomSettings
|
property var roomSettings
|
||||||
|
|
||||||
minimumWidth: 340
|
|
||||||
minimumHeight: 450
|
|
||||||
width: 450
|
|
||||||
height: 680
|
|
||||||
palette: timelineRoot.palette
|
|
||||||
color: timelineRoot.palette.window
|
color: timelineRoot.palette.window
|
||||||
modality: Qt.NonModal
|
|
||||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
|
height: 680
|
||||||
|
minimumHeight: 450
|
||||||
|
minimumWidth: 340
|
||||||
|
modality: Qt.NonModal
|
||||||
|
palette: timelineRoot.palette
|
||||||
title: qsTr("Room Settings")
|
title: qsTr("Room Settings")
|
||||||
|
width: 450
|
||||||
|
|
||||||
|
footer: DialogButtonBox {
|
||||||
|
standardButtons: DialogButtonBox.Ok
|
||||||
|
|
||||||
|
onAccepted: close()
|
||||||
|
}
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: StandardKey.Cancel
|
sequence: StandardKey.Cancel
|
||||||
|
|
||||||
onActivated: roomSettingsDialog.close()
|
onActivated: roomSettingsDialog.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
Flickable {
|
Flickable {
|
||||||
id: flickable
|
id: flickable
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
clip: true
|
clip: true
|
||||||
flickableDirection: Flickable.VerticalFlick
|
|
||||||
contentWidth: roomSettingsDialog.width
|
|
||||||
contentHeight: contentLayout1.height
|
contentHeight: contentLayout1.height
|
||||||
|
contentWidth: roomSettingsDialog.width
|
||||||
|
flickableDirection: Flickable.VerticalFlick
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: contentLayout1
|
id: contentLayout1
|
||||||
width: parent.width
|
|
||||||
spacing: Nheko.paddingMedium
|
spacing: Nheko.paddingMedium
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.topMargin: Nheko.paddingMedium
|
Layout.topMargin: Nheko.paddingMedium
|
||||||
url: roomSettings.roomAvatarUrl.replace("mxc://", "image://MxcImage/")
|
|
||||||
roomid: roomSettings.roomId
|
|
||||||
displayName: roomSettings.roomName
|
displayName: roomSettings.roomName
|
||||||
height: 130
|
height: 130
|
||||||
|
roomid: roomSettings.roomId
|
||||||
|
url: roomSettings.roomAvatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
width: 130
|
width: 130
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (roomSettings.canChangeAvatar)
|
if (roomSettings.canChangeAvatar)
|
||||||
roomSettings.updateAvatar();
|
roomSettings.updateAvatar();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Spinner {
|
Spinner {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
visible: roomSettings.isLoading
|
|
||||||
foreground: timelineRoot.palette.mid
|
foreground: timelineRoot.palette.mid
|
||||||
running: roomSettings.isLoading
|
running: roomSettings.isLoading
|
||||||
|
visible: roomSettings.isLoading
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: errorText
|
id: errorText
|
||||||
|
|
||||||
color: "red"
|
|
||||||
visible: opacity > 0
|
|
||||||
opacity: 0
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
wrapMode: Text.Wrap // somehow still doesn't wrap
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
color: "red"
|
||||||
|
opacity: 0
|
||||||
|
visible: opacity > 0
|
||||||
|
wrapMode: Text.Wrap // somehow still doesn't wrap
|
||||||
}
|
}
|
||||||
|
|
||||||
SequentialAnimation {
|
SequentialAnimation {
|
||||||
id: hideErrorAnimation
|
id: hideErrorAnimation
|
||||||
|
|
||||||
running: false
|
running: false
|
||||||
|
|
||||||
PauseAnimation {
|
PauseAnimation {
|
||||||
duration: 4000
|
duration: 4000
|
||||||
}
|
}
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
target: errorText
|
|
||||||
property: 'opacity'
|
|
||||||
to: 0
|
|
||||||
duration: 1000
|
duration: 1000
|
||||||
|
property: 'opacity'
|
||||||
|
target: errorText
|
||||||
|
to: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: roomSettings
|
|
||||||
function onDisplayError(errorMessage) {
|
function onDisplayError(errorMessage) {
|
||||||
errorText.text = errorMessage;
|
errorText.text = errorMessage;
|
||||||
errorText.opacity = 1;
|
errorText.opacity = 1;
|
||||||
hideErrorAnimation.restart();
|
hideErrorAnimation.restart();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
target: roomSettings
|
||||||
|
}
|
||||||
TextEdit {
|
TextEdit {
|
||||||
id: roomName
|
id: roomName
|
||||||
|
|
||||||
property bool isNameEditingAllowed: false
|
property bool isNameEditingAllowed: false
|
||||||
|
|
||||||
readOnly: !isNameEditingAllowed
|
|
||||||
textFormat: isNameEditingAllowed ? TextEdit.PlainText : TextEdit.RichText
|
|
||||||
text: isNameEditingAllowed ? roomSettings.plainRoomName : roomSettings.roomName
|
|
||||||
font.pixelSize: fontMetrics.font.pixelSize * 2
|
|
||||||
color: timelineRoot.palette.text
|
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.maximumWidth: parent.width - (Nheko.paddingSmall * 2) - nameChangeButton.anchors.leftMargin - (nameChangeButton.width * 2)
|
Layout.maximumWidth: parent.width - (Nheko.paddingSmall * 2) - nameChangeButton.anchors.leftMargin - (nameChangeButton.width * 2)
|
||||||
|
color: timelineRoot.palette.text
|
||||||
|
font.pixelSize: fontMetrics.font.pixelSize * 2
|
||||||
horizontalAlignment: TextEdit.AlignHCenter
|
horizontalAlignment: TextEdit.AlignHCenter
|
||||||
wrapMode: TextEdit.Wrap
|
readOnly: !isNameEditingAllowed
|
||||||
selectByMouse: true
|
selectByMouse: true
|
||||||
|
text: isNameEditingAllowed ? roomSettings.plainRoomName : roomSettings.roomName
|
||||||
|
textFormat: isNameEditingAllowed ? TextEdit.PlainText : TextEdit.RichText
|
||||||
|
wrapMode: TextEdit.Wrap
|
||||||
|
|
||||||
Keys.onShortcutOverride: event.key === Qt.Key_Enter
|
|
||||||
Keys.onPressed: {
|
Keys.onPressed: {
|
||||||
if (event.matches(StandardKey.InsertLineSeparator) || event.matches(StandardKey.InsertParagraphSeparator)) {
|
if (event.matches(StandardKey.InsertLineSeparator) || event.matches(StandardKey.InsertParagraphSeparator)) {
|
||||||
roomSettings.changeName(roomName.text);
|
roomSettings.changeName(roomName.text);
|
||||||
|
@ -131,18 +126,20 @@ ApplicationWindow {
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Keys.onShortcutOverride: event.key === Qt.Key_Enter
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: nameChangeButton
|
id: nameChangeButton
|
||||||
visible: roomSettings.canChangeName
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
anchors.leftMargin: Nheko.paddingSmall
|
ToolTip.text: qsTr("Change name of this room")
|
||||||
|
ToolTip.visible: hovered
|
||||||
anchors.left: roomName.right
|
anchors.left: roomName.right
|
||||||
|
anchors.leftMargin: Nheko.paddingSmall
|
||||||
anchors.verticalCenter: roomName.verticalCenter
|
anchors.verticalCenter: roomName.verticalCenter
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Change name of this room")
|
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
|
||||||
image: roomName.isNameEditingAllowed ? ":/icons/icons/ui/checkmark.svg" : ":/icons/icons/ui/edit.svg"
|
image: roomName.isNameEditingAllowed ? ":/icons/icons/ui/checkmark.svg" : ":/icons/icons/ui/edit.svg"
|
||||||
|
visible: roomSettings.canChangeName
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (roomName.isNameEditingAllowed) {
|
if (roomName.isNameEditingAllowed) {
|
||||||
roomSettings.changeName(roomName.text);
|
roomSettings.changeName(roomName.text);
|
||||||
|
@ -154,65 +151,60 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Label {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
color: timelineRoot.palette.text
|
||||||
|
text: qsTr("%n member(s)", "", roomSettings.memberCount)
|
||||||
|
|
||||||
Label {
|
TapHandler {
|
||||||
text: qsTr("%n member(s)", "", roomSettings.memberCount)
|
onSingleTapped: TimelineManager.openRoomMembers(Rooms.getRoomById(roomSettings.roomId))
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
color: timelineRoot.palette.text
|
|
||||||
|
|
||||||
TapHandler {
|
|
||||||
onSingleTapped: TimelineManager.openRoomMembers(Rooms.getRoomById(roomSettings.roomId))
|
|
||||||
}
|
|
||||||
|
|
||||||
NhekoCursorShape {
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
anchors.fill: parent
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
NhekoCursorShape {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
}
|
||||||
|
}
|
||||||
TextArea {
|
TextArea {
|
||||||
id: roomTopic
|
id: roomTopic
|
||||||
|
|
||||||
property bool cut: implicitHeight > 100
|
property bool cut: implicitHeight > 100
|
||||||
|
property bool isTopicEditingAllowed: false
|
||||||
property bool showMore: false
|
property bool showMore: false
|
||||||
clip: true
|
|
||||||
Layout.maximumHeight: showMore? Number.POSITIVE_INFINITY : 100
|
|
||||||
Layout.preferredHeight: implicitHeight
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.leftMargin: Nheko.paddingLarge
|
Layout.leftMargin: Nheko.paddingLarge
|
||||||
|
Layout.maximumHeight: showMore ? Number.POSITIVE_INFINITY : 100
|
||||||
|
Layout.preferredHeight: implicitHeight
|
||||||
Layout.rightMargin: Nheko.paddingLarge
|
Layout.rightMargin: Nheko.paddingLarge
|
||||||
|
|
||||||
property bool isTopicEditingAllowed: false
|
|
||||||
|
|
||||||
readOnly: !isTopicEditingAllowed
|
|
||||||
textFormat: isTopicEditingAllowed ? TextEdit.PlainText : TextEdit.RichText
|
|
||||||
text: isTopicEditingAllowed ? roomSettings.plainRoomTopic : roomSettings.roomTopic
|
|
||||||
wrapMode: TextEdit.WordWrap
|
|
||||||
background: null
|
background: null
|
||||||
selectByMouse: !Settings.mobileMode
|
clip: true
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
horizontalAlignment: TextEdit.AlignHCenter
|
horizontalAlignment: TextEdit.AlignHCenter
|
||||||
|
readOnly: !isTopicEditingAllowed
|
||||||
|
selectByMouse: !Settings.mobileMode
|
||||||
|
text: isTopicEditingAllowed ? roomSettings.plainRoomTopic : roomSettings.roomTopic
|
||||||
|
textFormat: isTopicEditingAllowed ? TextEdit.PlainText : TextEdit.RichText
|
||||||
|
wrapMode: TextEdit.WordWrap
|
||||||
|
|
||||||
onLinkActivated: Nheko.openLink(link)
|
onLinkActivated: Nheko.openLink(link)
|
||||||
|
|
||||||
NhekoCursorShape {
|
NhekoCursorShape {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: topicChangeButton
|
id: topicChangeButton
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
visible: roomSettings.canChangeTopic
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Change topic of this room")
|
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
|
ToolTip.text: qsTr("Change topic of this room")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
hoverEnabled: true
|
||||||
image: roomTopic.isTopicEditingAllowed ? ":/icons/icons/ui/checkmark.svg" : ":/icons/icons/ui/edit.svg"
|
image: roomTopic.isTopicEditingAllowed ? ":/icons/icons/ui/checkmark.svg" : ":/icons/icons/ui/edit.svg"
|
||||||
|
visible: roomSettings.canChangeTopic
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (roomTopic.isTopicEditingAllowed) {
|
if (roomTopic.isTopicEditingAllowed) {
|
||||||
roomSettings.changeTopic(roomTopic.text);
|
roomSettings.changeTopic(roomTopic.text);
|
||||||
|
@ -225,223 +217,207 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
id: showMorePlaceholder
|
id: showMorePlaceholder
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.preferredHeight: showMoreButton.height
|
Layout.preferredHeight: showMoreButton.height
|
||||||
Layout.preferredWidth: showMoreButton.width
|
Layout.preferredWidth: showMoreButton.width
|
||||||
visible: roomTopic.cut
|
visible: roomTopic.cut
|
||||||
}
|
}
|
||||||
|
|
||||||
GridLayout {
|
GridLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.margins: Nheko.paddingMedium
|
||||||
columns: 2
|
columns: 2
|
||||||
rowSpacing: Nheko.paddingMedium
|
rowSpacing: Nheko.paddingMedium
|
||||||
Layout.margins: Nheko.paddingMedium
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("SETTINGS")
|
|
||||||
font.bold: true
|
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
|
font.bold: true
|
||||||
|
text: qsTr("SETTINGS")
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("Notifications")
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
|
text: qsTr("Notifications")
|
||||||
}
|
}
|
||||||
|
|
||||||
ComboBox {
|
ComboBox {
|
||||||
model: [qsTr("Muted"), qsTr("Mentions only"), qsTr("All messages")]
|
Layout.fillWidth: true
|
||||||
currentIndex: roomSettings.notifications
|
currentIndex: roomSettings.notifications
|
||||||
|
model: [qsTr("Muted"), qsTr("Mentions only"), qsTr("All messages")]
|
||||||
|
|
||||||
onActivated: {
|
onActivated: {
|
||||||
roomSettings.changeNotifications(index);
|
roomSettings.changeNotifications(index);
|
||||||
}
|
}
|
||||||
Layout.fillWidth: true
|
|
||||||
WheelHandler{} // suppress scrolling changing values
|
|
||||||
}
|
|
||||||
|
|
||||||
|
WheelHandler {
|
||||||
|
} // suppress scrolling changing values
|
||||||
|
}
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("Room access")
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
|
text: qsTr("Room access")
|
||||||
}
|
}
|
||||||
|
|
||||||
ComboBox {
|
ComboBox {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
currentIndex: roomSettings.accessJoinRules
|
||||||
enabled: roomSettings.canChangeJoinRules
|
enabled: roomSettings.canChangeJoinRules
|
||||||
model: {
|
model: {
|
||||||
let opts = [qsTr("Anyone and guests"), qsTr("Anyone"), qsTr("Invited users")];
|
let opts = [qsTr("Anyone and guests"), qsTr("Anyone"), qsTr("Invited users")];
|
||||||
if (roomSettings.supportsKnocking)
|
if (roomSettings.supportsKnocking)
|
||||||
opts.push(qsTr("By knocking"));
|
opts.push(qsTr("By knocking"));
|
||||||
|
|
||||||
if (roomSettings.supportsRestricted)
|
if (roomSettings.supportsRestricted)
|
||||||
opts.push(qsTr("Restricted by membership in other rooms"));
|
opts.push(qsTr("Restricted by membership in other rooms"));
|
||||||
|
|
||||||
return opts;
|
return opts;
|
||||||
}
|
}
|
||||||
currentIndex: roomSettings.accessJoinRules
|
|
||||||
onActivated: {
|
onActivated: {
|
||||||
roomSettings.changeAccessRules(index);
|
roomSettings.changeAccessRules(index);
|
||||||
}
|
}
|
||||||
Layout.fillWidth: true
|
|
||||||
WheelHandler{} // suppress scrolling changing values
|
|
||||||
}
|
|
||||||
|
|
||||||
|
WheelHandler {
|
||||||
|
} // suppress scrolling changing values
|
||||||
|
}
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("Encryption")
|
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
|
text: qsTr("Encryption")
|
||||||
}
|
}
|
||||||
|
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
id: encryptionToggle
|
id: encryptionToggle
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
checked: roomSettings.isEncryptionEnabled
|
checked: roomSettings.isEncryptionEnabled
|
||||||
|
|
||||||
onCheckedChanged: {
|
onCheckedChanged: {
|
||||||
if (roomSettings.isEncryptionEnabled) {
|
if (roomSettings.isEncryptionEnabled) {
|
||||||
checked = true;
|
checked = true;
|
||||||
return ;
|
return;
|
||||||
}
|
}
|
||||||
confirmEncryptionDialog.open();
|
confirmEncryptionDialog.open();
|
||||||
}
|
}
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform.MessageDialog {
|
Platform.MessageDialog {
|
||||||
id: confirmEncryptionDialog
|
id: confirmEncryptionDialog
|
||||||
|
buttons: Platform.MessageDialog.Ok | Platform.MessageDialog.Cancel
|
||||||
title: qsTr("End-to-End Encryption")
|
modality: Qt.NonModal
|
||||||
text: qsTr("Encryption is currently experimental and things might break unexpectedly. <br>
|
text: qsTr("Encryption is currently experimental and things might break unexpectedly. <br>
|
||||||
Please take note that it can't be disabled afterwards.")
|
Please take note that it can't be disabled afterwards.")
|
||||||
modality: Qt.NonModal
|
title: qsTr("End-to-End Encryption")
|
||||||
|
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
if (roomSettings.isEncryptionEnabled)
|
if (roomSettings.isEncryptionEnabled)
|
||||||
return ;
|
return;
|
||||||
|
|
||||||
roomSettings.enableEncryption();
|
roomSettings.enableEncryption();
|
||||||
}
|
}
|
||||||
onRejected: {
|
onRejected: {
|
||||||
encryptionToggle.checked = false;
|
encryptionToggle.checked = false;
|
||||||
}
|
}
|
||||||
buttons: Platform.MessageDialog.Ok | Platform.MessageDialog.Cancel
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
color: timelineRoot.palette.text
|
||||||
text: qsTr("Sticker & Emote Settings")
|
text: qsTr("Sticker & Emote Settings")
|
||||||
color: timelineRoot.palette.text
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: qsTr("Change")
|
|
||||||
ToolTip.text: qsTr("Change what packs are enabled, remove packs or create new ones")
|
|
||||||
onClicked: TimelineManager.openImagePackSettings(roomSettings.roomId)
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
}
|
ToolTip.text: qsTr("Change what packs are enabled, remove packs or create new ones")
|
||||||
|
text: qsTr("Change")
|
||||||
|
|
||||||
|
onClicked: TimelineManager.openImagePackSettings(roomSettings.roomId)
|
||||||
|
}
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("Hidden events")
|
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
|
text: qsTr("Hidden events")
|
||||||
}
|
}
|
||||||
|
|
||||||
HiddenEventsDialog {
|
HiddenEventsDialog {
|
||||||
id: hiddenEventsDialog
|
id: hiddenEventsDialog
|
||||||
roomid: roomSettings.roomId
|
|
||||||
roomName: roomSettings.roomName
|
roomName: roomSettings.roomName
|
||||||
|
roomid: roomSettings.roomId
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: qsTr("Configure")
|
|
||||||
ToolTip.text: qsTr("Select events to hide in this room")
|
|
||||||
onClicked: hiddenEventsDialog.show()
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
}
|
ToolTip.text: qsTr("Select events to hide in this room")
|
||||||
|
text: qsTr("Configure")
|
||||||
|
|
||||||
|
onClicked: hiddenEventsDialog.show()
|
||||||
|
}
|
||||||
Item {
|
Item {
|
||||||
// for adding extra space between sections
|
// for adding extra space between sections
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
// for adding extra space between sections
|
// for adding extra space between sections
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("INFO")
|
color: timelineRoot.palette.text
|
||||||
font.bold: true
|
font.bold: true
|
||||||
color: timelineRoot.palette.text
|
text: qsTr("INFO")
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("Internal ID")
|
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
|
text: qsTr("Internal ID")
|
||||||
}
|
}
|
||||||
|
AbstractButton {
|
||||||
AbstractButton { // AbstractButton does not allow setting text color
|
// AbstractButton does not allow setting text color
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: idLabel.height
|
Layout.preferredHeight: idLabel.height
|
||||||
Label { // TextEdit does not trigger onClicked
|
|
||||||
|
onClicked: {
|
||||||
|
textEdit.selectAll();
|
||||||
|
textEdit.copy();
|
||||||
|
toolTipTimer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
// TextEdit does not trigger onClicked
|
||||||
id: idLabel
|
id: idLabel
|
||||||
text: roomSettings.roomId
|
|
||||||
font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 0.8)
|
|
||||||
color: timelineRoot.palette.text
|
|
||||||
width: parent.width
|
|
||||||
horizontalAlignment: Text.AlignRight
|
|
||||||
wrapMode: Text.WrapAnywhere
|
|
||||||
ToolTip.text: qsTr("Copied to clipboard")
|
ToolTip.text: qsTr("Copied to clipboard")
|
||||||
ToolTip.visible: toolTipTimer.running
|
ToolTip.visible: toolTipTimer.running
|
||||||
}
|
color: timelineRoot.palette.text
|
||||||
TextEdit{ // label does not allow selection
|
font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 0.8)
|
||||||
id: textEdit
|
horizontalAlignment: Text.AlignRight
|
||||||
visible: false
|
|
||||||
text: roomSettings.roomId
|
text: roomSettings.roomId
|
||||||
|
width: parent.width
|
||||||
|
wrapMode: Text.WrapAnywhere
|
||||||
}
|
}
|
||||||
onClicked: {
|
TextEdit {
|
||||||
textEdit.selectAll()
|
// label does not allow selection
|
||||||
textEdit.copy()
|
id: textEdit
|
||||||
toolTipTimer.start()
|
text: roomSettings.roomId
|
||||||
|
visible: false
|
||||||
}
|
}
|
||||||
Timer {
|
Timer {
|
||||||
id: toolTipTimer
|
id: toolTipTimer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("Room Version")
|
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
|
text: qsTr("Room Version")
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: roomSettings.roomVersion
|
|
||||||
font.pixelSize: fontMetrics.font.pixelSize
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
|
font.pixelSize: fontMetrics.font.pixelSize
|
||||||
|
text: roomSettings.roomVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Button {
|
Button {
|
||||||
id: showMoreButton
|
id: showMoreButton
|
||||||
anchors.horizontalCenter: flickable.horizontalCenter
|
anchors.horizontalCenter: flickable.horizontalCenter
|
||||||
y: Math.min(showMorePlaceholder.y+contentLayout1.y-flickable.contentY,flickable.height-height)
|
text: roomTopic.showMore ? qsTr("show less") : qsTr("show more")
|
||||||
visible: roomTopic.cut
|
visible: roomTopic.cut
|
||||||
text: roomTopic.showMore? qsTr("show less") : qsTr("show more")
|
y: Math.min(showMorePlaceholder.y + contentLayout1.y - flickable.contentY, flickable.height - height)
|
||||||
onClicked: {roomTopic.showMore = !roomTopic.showMore
|
|
||||||
console.log(flickable.visibleArea)
|
onClicked: {
|
||||||
|
roomTopic.showMore = !roomTopic.showMore;
|
||||||
|
console.log(flickable.visibleArea);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
footer: DialogButtonBox {
|
|
||||||
standardButtons: DialogButtonBox.Ok
|
|
||||||
onAccepted: close()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
import "../"
|
||||||
import ".."
|
|
||||||
import "../device-verification"
|
import "../device-verification"
|
||||||
import "../ui"
|
import "../ui"
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
|
@ -18,101 +16,232 @@ ApplicationWindow {
|
||||||
|
|
||||||
property var profile
|
property var profile
|
||||||
|
|
||||||
height: 650
|
|
||||||
width: 420
|
|
||||||
minimumWidth: 150
|
|
||||||
minimumHeight: 150
|
|
||||||
palette: timelineRoot.palette
|
|
||||||
color: timelineRoot.palette.window
|
color: timelineRoot.palette.window
|
||||||
title: profile.isGlobalUserProfile ? qsTr("Global User Profile") : qsTr("Room User Profile")
|
|
||||||
modality: Qt.NonModal
|
|
||||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
|
height: 650
|
||||||
|
minimumHeight: 150
|
||||||
|
minimumWidth: 150
|
||||||
|
modality: Qt.NonModal
|
||||||
|
palette: timelineRoot.palette
|
||||||
|
title: profile.isGlobalUserProfile ? qsTr("Global User Profile") : qsTr("Room User Profile")
|
||||||
|
width: 420
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: StandardKey.Cancel
|
sequence: StandardKey.Cancel
|
||||||
|
|
||||||
onActivated: userProfileDialog.close()
|
onActivated: userProfileDialog.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: devicelist
|
id: devicelist
|
||||||
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
clip: true
|
|
||||||
spacing: 8
|
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
|
||||||
model: profile.deviceList
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: 10
|
anchors.margins: 10
|
||||||
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
clip: true
|
||||||
footerPositioning: ListView.OverlayFooter
|
footerPositioning: ListView.OverlayFooter
|
||||||
|
model: profile.deviceList
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
delegate: RowLayout {
|
||||||
|
required property string deviceId
|
||||||
|
required property string deviceName
|
||||||
|
required property string lastIp
|
||||||
|
required property var lastTs
|
||||||
|
required property int verificationStatus
|
||||||
|
|
||||||
|
spacing: 4
|
||||||
|
width: devicelist.width
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Text {
|
||||||
|
Layout.alignment: Qt.AlignLeft
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: timelineRoot.palette.text
|
||||||
|
elide: Text.ElideRight
|
||||||
|
font.bold: true
|
||||||
|
text: deviceId
|
||||||
|
}
|
||||||
|
Image {
|
||||||
|
Layout.preferredHeight: 16
|
||||||
|
Layout.preferredWidth: 16
|
||||||
|
source: {
|
||||||
|
switch (verificationStatus) {
|
||||||
|
case VerificationStatus.VERIFIED:
|
||||||
|
return "image://colorimage/:/icons/icons/ui/shield-filled-checkmark.svg?green";
|
||||||
|
case VerificationStatus.UNVERIFIED:
|
||||||
|
return "image://colorimage/:/icons/icons/ui/shield-filled-exclamation-mark.svg?#d6c020";
|
||||||
|
case VerificationStatus.SELF:
|
||||||
|
return "image://colorimage/:/icons/icons/ui/checkmark.svg?green";
|
||||||
|
default:
|
||||||
|
return "image://colorimage/:/icons/icons/ui/shield-filled-cross.svg?#d6c020";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sourceSize.height: 16 * Screen.devicePixelRatio
|
||||||
|
sourceSize.width: 16 * Screen.devicePixelRatio
|
||||||
|
visible: profile.isSelf && verificationStatus != VerificationStatus.NOT_APPLICABLE
|
||||||
|
}
|
||||||
|
ImageButton {
|
||||||
|
Layout.alignment: Qt.AlignTop
|
||||||
|
ToolTip.text: qsTr("Sign out this device.")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/power-off.svg"
|
||||||
|
visible: profile.isSelf
|
||||||
|
|
||||||
|
onClicked: profile.signOutDevice(deviceId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RowLayout {
|
||||||
|
id: deviceNameRow
|
||||||
|
|
||||||
|
property bool isEditingAllowed
|
||||||
|
|
||||||
|
TextInput {
|
||||||
|
id: deviceNameField
|
||||||
|
Layout.alignment: Qt.AlignLeft
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: timelineRoot.palette.text
|
||||||
|
readOnly: !deviceNameRow.isEditingAllowed
|
||||||
|
selectByMouse: true
|
||||||
|
text: deviceName
|
||||||
|
|
||||||
|
onAccepted: {
|
||||||
|
profile.changeDeviceName(deviceId, deviceNameField.text);
|
||||||
|
deviceNameRow.isEditingAllowed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImageButton {
|
||||||
|
ToolTip.text: qsTr("Change device name.")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
hoverEnabled: true
|
||||||
|
image: deviceNameRow.isEditingAllowed ? ":/icons/icons/ui/checkmark.svg" : ":/icons/icons/ui/edit.svg"
|
||||||
|
visible: profile.isSelf
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
if (deviceNameRow.isEditingAllowed) {
|
||||||
|
profile.changeDeviceName(deviceId, deviceNameField.text);
|
||||||
|
deviceNameRow.isEditingAllowed = false;
|
||||||
|
} else {
|
||||||
|
deviceNameRow.isEditingAllowed = true;
|
||||||
|
deviceNameField.focus = true;
|
||||||
|
deviceNameField.selectAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
Layout.alignment: Qt.AlignLeft
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: timelineRoot.palette.text
|
||||||
|
elide: Text.ElideRight
|
||||||
|
text: qsTr("Last seen %1 from %2").arg(new Date(lastTs).toLocaleString(Locale.ShortFormat)).arg(lastIp ? lastIp : "???")
|
||||||
|
visible: profile.isSelf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Image {
|
||||||
|
Layout.preferredHeight: 16
|
||||||
|
Layout.preferredWidth: 16
|
||||||
|
source: {
|
||||||
|
switch (verificationStatus) {
|
||||||
|
case VerificationStatus.VERIFIED:
|
||||||
|
return "image://colorimage/:/icons/icons/ui/shield-filled-checkmark.svg?green";
|
||||||
|
case VerificationStatus.UNVERIFIED:
|
||||||
|
return "image://colorimage/:/icons/icons/ui/shield-filled-exclamation-mark.svg?#d6c020";
|
||||||
|
case VerificationStatus.SELF:
|
||||||
|
return "image://colorimage/:/icons/icons/ui/checkmark.svg?green";
|
||||||
|
default:
|
||||||
|
return "image://colorimage/:/icons/icons/ui/shield-filled.svg?red";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
visible: !profile.isSelf && verificationStatus != VerificationStatus.NOT_APPLICABLE
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
id: verifyButton
|
||||||
|
text: (verificationStatus != VerificationStatus.VERIFIED) ? qsTr("Verify") : qsTr("Unverify")
|
||||||
|
visible: verificationStatus == VerificationStatus.UNVERIFIED && (profile.isSelf || !profile.userVerificationEnabled)
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
if (verificationStatus == VerificationStatus.VERIFIED)
|
||||||
|
profile.unverify(deviceId);
|
||||||
|
else
|
||||||
|
profile.verify(deviceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
footer: DialogButtonBox {
|
||||||
|
alignment: Qt.AlignRight
|
||||||
|
standardButtons: DialogButtonBox.Ok
|
||||||
|
width: devicelist.width
|
||||||
|
z: 2
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: timelineRoot.palette.window
|
||||||
|
}
|
||||||
|
|
||||||
|
onAccepted: userProfileDialog.close()
|
||||||
|
}
|
||||||
header: ColumnLayout {
|
header: ColumnLayout {
|
||||||
id: contentL
|
id: contentL
|
||||||
|
|
||||||
width: devicelist.width
|
|
||||||
spacing: 10
|
spacing: 10
|
||||||
|
width: devicelist.width
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
id: displayAvatar
|
id: displayAvatar
|
||||||
|
|
||||||
url: profile.avatarUrl.replace("mxc://", "image://MxcImage/")
|
|
||||||
height: 130
|
|
||||||
width: 130
|
|
||||||
displayName: profile.displayName
|
|
||||||
userid: profile.userid
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
displayName: profile.displayName
|
||||||
|
height: 130
|
||||||
|
url: profile.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
|
userid: profile.userid
|
||||||
|
width: 130
|
||||||
|
|
||||||
onClicked: TimelineManager.openImageOverlay(null, profile.avatarUrl, "")
|
onClicked: TimelineManager.openImageOverlay(null, profile.avatarUrl, "")
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: profile.isGlobalUserProfile ? qsTr("Change avatar globally.") : qsTr("Change avatar. Will only apply to this room.")
|
ToolTip.text: profile.isGlobalUserProfile ? qsTr("Change avatar globally.") : qsTr("Change avatar. Will only apply to this room.")
|
||||||
|
ToolTip.visible: hovered
|
||||||
anchors.left: displayAvatar.left
|
anchors.left: displayAvatar.left
|
||||||
anchors.top: displayAvatar.top
|
|
||||||
anchors.leftMargin: Nheko.paddingMedium
|
anchors.leftMargin: Nheko.paddingMedium
|
||||||
|
anchors.top: displayAvatar.top
|
||||||
anchors.topMargin: Nheko.paddingMedium
|
anchors.topMargin: Nheko.paddingMedium
|
||||||
visible: profile.isSelf
|
hoverEnabled: true
|
||||||
image: ":/icons/icons/ui/edit.svg"
|
image: ":/icons/icons/ui/edit.svg"
|
||||||
|
visible: profile.isSelf
|
||||||
|
|
||||||
onClicked: profile.changeAvatar()
|
onClicked: profile.changeAvatar()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Spinner {
|
Spinner {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
foreground: timelineRoot.palette.mid
|
||||||
running: profile.isLoading
|
running: profile.isLoading
|
||||||
visible: profile.isLoading
|
visible: profile.isLoading
|
||||||
foreground: timelineRoot.palette.mid
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: errorText
|
id: errorText
|
||||||
|
|
||||||
color: "red"
|
|
||||||
visible: opacity > 0
|
|
||||||
opacity: 0
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
color: "red"
|
||||||
|
opacity: 0
|
||||||
|
visible: opacity > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
SequentialAnimation {
|
SequentialAnimation {
|
||||||
id: hideErrorAnimation
|
id: hideErrorAnimation
|
||||||
|
|
||||||
running: false
|
running: false
|
||||||
|
|
||||||
PauseAnimation {
|
PauseAnimation {
|
||||||
duration: 4000
|
duration: 4000
|
||||||
}
|
}
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
target: errorText
|
|
||||||
property: 'opacity'
|
|
||||||
to: 0
|
|
||||||
duration: 1000
|
duration: 1000
|
||||||
|
property: 'opacity'
|
||||||
|
target: errorText
|
||||||
|
to: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onDisplayError(errorMessage) {
|
function onDisplayError(errorMessage) {
|
||||||
errorText.text = errorMessage;
|
errorText.text = errorMessage;
|
||||||
|
@ -122,22 +251,22 @@ ApplicationWindow {
|
||||||
|
|
||||||
target: profile
|
target: profile
|
||||||
}
|
}
|
||||||
|
|
||||||
TextInput {
|
TextInput {
|
||||||
id: displayUsername
|
id: displayUsername
|
||||||
|
|
||||||
property bool isUsernameEditingAllowed
|
property bool isUsernameEditingAllowed
|
||||||
|
|
||||||
readOnly: !isUsernameEditingAllowed
|
|
||||||
text: profile.displayName
|
|
||||||
font.pixelSize: 20
|
|
||||||
color: TimelineManager.userColor(profile.userid, timelineRoot.palette.window)
|
|
||||||
font.bold: true
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.maximumWidth: parent.width - (Nheko.paddingSmall * 2) - usernameChangeButton.anchors.leftMargin - (usernameChangeButton.width * 2)
|
Layout.maximumWidth: parent.width - (Nheko.paddingSmall * 2) - usernameChangeButton.anchors.leftMargin - (usernameChangeButton.width * 2)
|
||||||
|
color: TimelineManager.userColor(profile.userid, timelineRoot.palette.window)
|
||||||
|
font.bold: true
|
||||||
|
font.pixelSize: 20
|
||||||
horizontalAlignment: TextInput.AlignHCenter
|
horizontalAlignment: TextInput.AlignHCenter
|
||||||
wrapMode: TextInput.Wrap
|
readOnly: !isUsernameEditingAllowed
|
||||||
selectByMouse: true
|
selectByMouse: true
|
||||||
|
text: profile.displayName
|
||||||
|
wrapMode: TextInput.Wrap
|
||||||
|
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
profile.changeUsername(displayUsername.text);
|
profile.changeUsername(displayUsername.text);
|
||||||
displayUsername.isUsernameEditingAllowed = false;
|
displayUsername.isUsernameEditingAllowed = false;
|
||||||
|
@ -145,14 +274,15 @@ ApplicationWindow {
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: usernameChangeButton
|
id: usernameChangeButton
|
||||||
visible: profile.isSelf
|
ToolTip.text: profile.isGlobalUserProfile ? qsTr("Change display name globally.") : qsTr("Change display name. Will only apply to this room.")
|
||||||
anchors.leftMargin: Nheko.paddingSmall
|
ToolTip.visible: hovered
|
||||||
anchors.left: displayUsername.right
|
anchors.left: displayUsername.right
|
||||||
|
anchors.leftMargin: Nheko.paddingSmall
|
||||||
anchors.verticalCenter: displayUsername.verticalCenter
|
anchors.verticalCenter: displayUsername.verticalCenter
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: profile.isGlobalUserProfile ? qsTr("Change display name globally.") : qsTr("Change display name. Will only apply to this room.")
|
|
||||||
image: displayUsername.isUsernameEditingAllowed ? ":/icons/icons/ui/checkmark.svg" : ":/icons/icons/ui/edit.svg"
|
image: displayUsername.isUsernameEditingAllowed ? ":/icons/icons/ui/checkmark.svg" : ":/icons/icons/ui/edit.svg"
|
||||||
|
visible: profile.isSelf
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (displayUsername.isUsernameEditingAllowed) {
|
if (displayUsername.isUsernameEditingAllowed) {
|
||||||
profile.changeUsername(displayUsername.text);
|
profile.changeUsername(displayUsername.text);
|
||||||
|
@ -164,63 +294,54 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
text: profile.userid
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
text: profile.userid
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
visible: !profile.isGlobalUserProfile
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
spacing: Nheko.paddingSmall
|
spacing: Nheko.paddingSmall
|
||||||
|
visible: !profile.isGlobalUserProfile
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
id: displayRoomname
|
id: displayRoomname
|
||||||
|
Layout.maximumWidth: parent.parent.width - (parent.spacing * 3) - 16
|
||||||
text: qsTr("Room: %1").arg(profile.room ? profile.room.roomName : "")
|
|
||||||
ToolTip.text: qsTr("This is a room-specific profile. The user's name and avatar may be different from their global versions.")
|
ToolTip.text: qsTr("This is a room-specific profile. The user's name and avatar may be different from their global versions.")
|
||||||
ToolTip.visible: ma.hovered
|
ToolTip.visible: ma.hovered
|
||||||
Layout.maximumWidth: parent.parent.width - (parent.spacing * 3) - 16
|
|
||||||
horizontalAlignment: TextEdit.AlignHCenter
|
horizontalAlignment: TextEdit.AlignHCenter
|
||||||
|
text: qsTr("Room: %1").arg(profile.room ? profile.room.roomName : "")
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: ma
|
id: ma
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
image: ":/icons/icons/ui/world.svg"
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Open the global profile for this user.")
|
ToolTip.text: qsTr("Open the global profile for this user.")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/world.svg"
|
||||||
|
|
||||||
onClicked: profile.openGlobalProfile()
|
onClicked: profile.openGlobalProfile()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
id: verifyUserButton
|
id: verifyUserButton
|
||||||
|
|
||||||
text: qsTr("Verify")
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
enabled: profile.userVerified != Crypto.Verified
|
enabled: profile.userVerified != Crypto.Verified
|
||||||
|
text: qsTr("Verify")
|
||||||
visible: profile.userVerified != Crypto.Verified && !profile.isSelf && profile.userVerificationEnabled
|
visible: profile.userVerified != Crypto.Verified && !profile.isSelf && profile.userVerificationEnabled
|
||||||
|
|
||||||
onClicked: profile.verify()
|
onClicked: profile.verify()
|
||||||
}
|
}
|
||||||
|
|
||||||
EncryptionIndicator {
|
EncryptionIndicator {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.preferredHeight: 16
|
Layout.preferredHeight: 16
|
||||||
Layout.preferredWidth: 16
|
Layout.preferredWidth: 16
|
||||||
|
ToolTip.visible: false
|
||||||
encrypted: profile.userVerificationEnabled
|
encrypted: profile.userVerificationEnabled
|
||||||
trust: profile.userVerified
|
trust: profile.userVerified
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
ToolTip.visible: false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
// ImageButton{
|
// ImageButton{
|
||||||
// image:":/icons/icons/ui/volume-off-indicator.svg"
|
// image:":/icons/icons/ui/volume-off-indicator.svg"
|
||||||
|
@ -234,202 +355,45 @@ ApplicationWindow {
|
||||||
// profile.ignoreUser()
|
// profile.ignoreUser()
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.bottomMargin: 10
|
Layout.bottomMargin: 10
|
||||||
spacing: Nheko.paddingSmall
|
spacing: Nheko.paddingSmall
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
image: ":/icons/icons/ui/chat.svg"
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Start a private chat.")
|
ToolTip.text: qsTr("Start a private chat.")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/chat.svg"
|
||||||
|
|
||||||
onClicked: profile.startChat()
|
onClicked: profile.startChat()
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
image: ":/icons/icons/ui/round-remove-button.svg"
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Kick the user.")
|
ToolTip.text: qsTr("Kick the user.")
|
||||||
onClicked: profile.kickUser()
|
ToolTip.visible: hovered
|
||||||
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/round-remove-button.svg"
|
||||||
visible: !profile.isGlobalUserProfile && profile.room.permissions.canKick()
|
visible: !profile.isGlobalUserProfile && profile.room.permissions.canKick()
|
||||||
}
|
|
||||||
|
|
||||||
|
onClicked: profile.kickUser()
|
||||||
|
}
|
||||||
ImageButton {
|
ImageButton {
|
||||||
image: ":/icons/icons/ui/ban.svg"
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Ban the user.")
|
ToolTip.text: qsTr("Ban the user.")
|
||||||
onClicked: profile.banUser()
|
|
||||||
visible: !profile.isGlobalUserProfile && profile.room.permissions.canBan()
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageButton {
|
|
||||||
image: ":/icons/icons/ui/refresh.svg"
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/ban.svg"
|
||||||
|
visible: !profile.isGlobalUserProfile && profile.room.permissions.canBan()
|
||||||
|
|
||||||
|
onClicked: profile.banUser()
|
||||||
|
}
|
||||||
|
ImageButton {
|
||||||
ToolTip.text: qsTr("Refresh device list.")
|
ToolTip.text: qsTr("Refresh device list.")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/refresh.svg"
|
||||||
|
|
||||||
onClicked: profile.refreshDevices()
|
onClicked: profile.refreshDevices()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: RowLayout {
|
|
||||||
required property int verificationStatus
|
|
||||||
required property string deviceId
|
|
||||||
required property string deviceName
|
|
||||||
required property string lastIp
|
|
||||||
required property var lastTs
|
|
||||||
|
|
||||||
width: devicelist.width
|
|
||||||
spacing: 4
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
Text {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
|
||||||
elide: Text.ElideRight
|
|
||||||
font.bold: true
|
|
||||||
color: timelineRoot.palette.text
|
|
||||||
text: deviceId
|
|
||||||
}
|
|
||||||
|
|
||||||
Image {
|
|
||||||
Layout.preferredHeight: 16
|
|
||||||
Layout.preferredWidth: 16
|
|
||||||
visible: profile.isSelf && verificationStatus != VerificationStatus.NOT_APPLICABLE
|
|
||||||
sourceSize.height: 16 * Screen.devicePixelRatio
|
|
||||||
sourceSize.width: 16 * Screen.devicePixelRatio
|
|
||||||
source: {
|
|
||||||
switch (verificationStatus) {
|
|
||||||
case VerificationStatus.VERIFIED:
|
|
||||||
return "image://colorimage/:/icons/icons/ui/shield-filled-checkmark.svg?green";
|
|
||||||
case VerificationStatus.UNVERIFIED:
|
|
||||||
return "image://colorimage/:/icons/icons/ui/shield-filled-exclamation-mark.svg?#d6c020";
|
|
||||||
case VerificationStatus.SELF:
|
|
||||||
return "image://colorimage/:/icons/icons/ui/checkmark.svg?green";
|
|
||||||
default:
|
|
||||||
return "image://colorimage/:/icons/icons/ui/shield-filled-cross.svg?#d6c020";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageButton {
|
|
||||||
Layout.alignment: Qt.AlignTop
|
|
||||||
image: ":/icons/icons/ui/power-off.svg"
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Sign out this device.")
|
|
||||||
onClicked: profile.signOutDevice(deviceId)
|
|
||||||
visible: profile.isSelf
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: deviceNameRow
|
|
||||||
|
|
||||||
property bool isEditingAllowed
|
|
||||||
|
|
||||||
TextInput {
|
|
||||||
id: deviceNameField
|
|
||||||
|
|
||||||
readOnly: !deviceNameRow.isEditingAllowed
|
|
||||||
text: deviceName
|
|
||||||
color: timelineRoot.palette.text
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
|
||||||
Layout.fillWidth: true
|
|
||||||
selectByMouse: true
|
|
||||||
onAccepted: {
|
|
||||||
profile.changeDeviceName(deviceId, deviceNameField.text);
|
|
||||||
deviceNameRow.isEditingAllowed = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageButton {
|
|
||||||
visible: profile.isSelf
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Change device name.")
|
|
||||||
image: deviceNameRow.isEditingAllowed ? ":/icons/icons/ui/checkmark.svg" : ":/icons/icons/ui/edit.svg"
|
|
||||||
onClicked: {
|
|
||||||
if (deviceNameRow.isEditingAllowed) {
|
|
||||||
profile.changeDeviceName(deviceId, deviceNameField.text);
|
|
||||||
deviceNameRow.isEditingAllowed = false;
|
|
||||||
} else {
|
|
||||||
deviceNameRow.isEditingAllowed = true;
|
|
||||||
deviceNameField.focus = true;
|
|
||||||
deviceNameField.selectAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
visible: profile.isSelf
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
|
||||||
elide: Text.ElideRight
|
|
||||||
color: timelineRoot.palette.text
|
|
||||||
text: qsTr("Last seen %1 from %2").arg(new Date(lastTs).toLocaleString(Locale.ShortFormat)).arg(lastIp ? lastIp : "???")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Image {
|
|
||||||
Layout.preferredHeight: 16
|
|
||||||
Layout.preferredWidth: 16
|
|
||||||
visible: !profile.isSelf && verificationStatus != VerificationStatus.NOT_APPLICABLE
|
|
||||||
source: {
|
|
||||||
switch (verificationStatus) {
|
|
||||||
case VerificationStatus.VERIFIED:
|
|
||||||
return "image://colorimage/:/icons/icons/ui/shield-filled-checkmark.svg?green";
|
|
||||||
case VerificationStatus.UNVERIFIED:
|
|
||||||
return "image://colorimage/:/icons/icons/ui/shield-filled-exclamation-mark.svg?#d6c020";
|
|
||||||
case VerificationStatus.SELF:
|
|
||||||
return "image://colorimage/:/icons/icons/ui/checkmark.svg?green";
|
|
||||||
default:
|
|
||||||
return "image://colorimage/:/icons/icons/ui/shield-filled.svg?red";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
id: verifyButton
|
|
||||||
|
|
||||||
visible: verificationStatus == VerificationStatus.UNVERIFIED && (profile.isSelf || !profile.userVerificationEnabled)
|
|
||||||
text: (verificationStatus != VerificationStatus.VERIFIED) ? qsTr("Verify") : qsTr("Unverify")
|
|
||||||
onClicked: {
|
|
||||||
if (verificationStatus == VerificationStatus.VERIFIED)
|
|
||||||
profile.unverify(deviceId);
|
|
||||||
else
|
|
||||||
profile.verify(deviceId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
footer: DialogButtonBox {
|
|
||||||
z: 2
|
|
||||||
width: devicelist.width
|
|
||||||
alignment: Qt.AlignRight
|
|
||||||
standardButtons: DialogButtonBox.Ok
|
|
||||||
onAccepted: userProfileDialog.close()
|
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
color: timelineRoot.palette.window
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import "../"
|
import "../"
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
|
@ -15,12 +13,12 @@ Menu {
|
||||||
|
|
||||||
property var callback
|
property var callback
|
||||||
property var colors
|
property var colors
|
||||||
property alias model: gridView.model
|
|
||||||
property var textArea
|
|
||||||
property string emojiCategory: "people"
|
property string emojiCategory: "people"
|
||||||
property real highlightHue: timelineRoot.palette.highlight.hslHue
|
property real highlightHue: timelineRoot.palette.highlight.hslHue
|
||||||
property real highlightSat: timelineRoot.palette.highlight.hslSaturation
|
|
||||||
property real highlightLight: timelineRoot.palette.highlight.hslLightness
|
property real highlightLight: timelineRoot.palette.highlight.hslLightness
|
||||||
|
property real highlightSat: timelineRoot.palette.highlight.hslSaturation
|
||||||
|
property alias model: gridView.model
|
||||||
|
property var textArea
|
||||||
|
|
||||||
function show(showAt, callback) {
|
function show(showAt, callback) {
|
||||||
console.debug("Showing emojiPicker");
|
console.debug("Showing emojiPicker");
|
||||||
|
@ -28,13 +26,13 @@ Menu {
|
||||||
popup(showAt ? showAt : null);
|
popup(showAt ? showAt : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
margins: 0
|
|
||||||
bottomPadding: 1
|
bottomPadding: 1
|
||||||
leftPadding: 1
|
|
||||||
rightPadding: 1
|
|
||||||
modal: true
|
|
||||||
focus: true
|
|
||||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
||||||
|
focus: true
|
||||||
|
leftPadding: 1
|
||||||
|
margins: 0
|
||||||
|
modal: true
|
||||||
|
rightPadding: 1
|
||||||
//height: columnView.implicitHeight + 4
|
//height: columnView.implicitHeight + 4
|
||||||
//width: columnView.implicitWidth
|
//width: columnView.implicitWidth
|
||||||
width: 7 * 52 + 20
|
width: 7 * 52 + 20
|
||||||
|
@ -46,28 +44,27 @@ Menu {
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: columnView
|
id: columnView
|
||||||
|
|
||||||
spacing: 0
|
|
||||||
anchors.leftMargin: 3
|
|
||||||
anchors.rightMargin: 3
|
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 3
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 3
|
||||||
anchors.topMargin: 2
|
anchors.topMargin: 2
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
// Search field
|
// Search field
|
||||||
TextField {
|
TextField {
|
||||||
id: emojiSearch
|
id: emojiSearch
|
||||||
|
|
||||||
Layout.topMargin: 3
|
|
||||||
Layout.preferredWidth: 7 * 52 + 20 - 6
|
Layout.preferredWidth: 7 * 52 + 20 - 6
|
||||||
palette: timelineRoot.palette
|
Layout.topMargin: 3
|
||||||
background: null
|
background: null
|
||||||
placeholderTextColor: timelineRoot.palette.placeholderText
|
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
|
palette: timelineRoot.palette
|
||||||
placeholderText: qsTr("Search")
|
placeholderText: qsTr("Search")
|
||||||
selectByMouse: true
|
placeholderTextColor: timelineRoot.palette.placeholderText
|
||||||
rightPadding: clearSearch.width
|
rightPadding: clearSearch.width
|
||||||
|
selectByMouse: true
|
||||||
|
|
||||||
onTextChanged: searchTimer.restart()
|
onTextChanged: searchTimer.restart()
|
||||||
onVisibleChanged: {
|
onVisibleChanged: {
|
||||||
if (visible)
|
if (visible)
|
||||||
|
@ -78,74 +75,71 @@ Menu {
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: searchTimer
|
id: searchTimer
|
||||||
|
|
||||||
interval: 350 // tweak as needed?
|
interval: 350 // tweak as needed?
|
||||||
|
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
emojiPopup.model.searchString = emojiSearch.text;
|
emojiPopup.model.searchString = emojiSearch.text;
|
||||||
emojiPopup.model.category = Emoji.Category.Search;
|
emojiPopup.model.category = Emoji.Category.Search;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ToolButton {
|
ToolButton {
|
||||||
id: clearSearch
|
id: clearSearch
|
||||||
|
|
||||||
visible: emojiSearch.text !== ''
|
|
||||||
icon.source: "image://colorimage/:/icons/icons/ui/round-remove-button.svg?" + (clearSearch.hovered ? timelineRoot.palette.highlight : timelineRoot.palette.placeholderText)
|
|
||||||
focusPolicy: Qt.NoFocus
|
|
||||||
onClicked: emojiSearch.clear()
|
|
||||||
hoverEnabled: true
|
|
||||||
background: null
|
background: null
|
||||||
|
focusPolicy: Qt.NoFocus
|
||||||
|
hoverEnabled: true
|
||||||
|
icon.source: "image://colorimage/:/icons/icons/ui/round-remove-button.svg?" + (clearSearch.hovered ? timelineRoot.palette.highlight : timelineRoot.palette.placeholderText)
|
||||||
|
visible: emojiSearch.text !== ''
|
||||||
|
|
||||||
|
onClicked: emojiSearch.clear()
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
verticalCenter: parent.verticalCenter
|
|
||||||
right: parent.right
|
right: parent.right
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
// clear the default hover effects.
|
// clear the default hover effects.
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
height: parent.height - 2 * Nheko.paddingSmall
|
height: parent.height - 2 * Nheko.paddingSmall
|
||||||
width: height
|
|
||||||
source: "image://colorimage/:/icons/icons/ui/round-remove-button.svg?" + (clearSearch.hovered ? timelineRoot.palette.highlight : timelineRoot.palette.placeholderText)
|
source: "image://colorimage/:/icons/icons/ui/round-remove-button.svg?" + (clearSearch.hovered ? timelineRoot.palette.highlight : timelineRoot.palette.placeholderText)
|
||||||
|
width: height
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
verticalCenter: parent.verticalCenter
|
|
||||||
right: parent.right
|
|
||||||
margins: Nheko.paddingSmall
|
margins: Nheko.paddingSmall
|
||||||
|
right: parent.right
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// emoji grid
|
// emoji grid
|
||||||
GridView {
|
GridView {
|
||||||
id: gridView
|
id: gridView
|
||||||
|
Layout.leftMargin: 4
|
||||||
Layout.preferredHeight: cellHeight * 5
|
Layout.preferredHeight: cellHeight * 5
|
||||||
Layout.preferredWidth: 7 * 52 + 20
|
Layout.preferredWidth: 7 * 52 + 20
|
||||||
Layout.leftMargin: 4
|
|
||||||
cellWidth: 52
|
|
||||||
cellHeight: 52
|
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
cacheBuffer: 500
|
||||||
|
cellHeight: 52
|
||||||
|
cellWidth: 52
|
||||||
clip: true
|
clip: true
|
||||||
currentIndex: -1 // prevent sorting from stealing focus
|
currentIndex: -1 // prevent sorting from stealing focus
|
||||||
cacheBuffer: 500
|
|
||||||
|
|
||||||
|
ScrollBar.vertical: ScrollBar {
|
||||||
|
id: emojiScroll
|
||||||
|
}
|
||||||
|
|
||||||
// Individual emoji
|
// Individual emoji
|
||||||
delegate: AbstractButton {
|
delegate: AbstractButton {
|
||||||
width: 48
|
|
||||||
height: 48
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.text: model.shortName
|
ToolTip.text: model.shortName
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
// TODO: maybe add favorites at some point?
|
height: 48
|
||||||
onClicked: {
|
hoverEnabled: true
|
||||||
console.debug("Picked " + model.unicode);
|
width: 48
|
||||||
emojiPopup.close();
|
|
||||||
callback(model.unicode);
|
background: Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: hovered ? timelineRoot.palette.highlight : 'transparent'
|
||||||
|
radius: 5
|
||||||
}
|
}
|
||||||
|
|
||||||
// give the emoji a little oomf
|
// give the emoji a little oomf
|
||||||
|
@ -159,97 +153,73 @@ Menu {
|
||||||
// color: "#80000000"
|
// color: "#80000000"
|
||||||
// source: parent.contentItem
|
// source: parent.contentItem
|
||||||
// }
|
// }
|
||||||
|
|
||||||
contentItem: Text {
|
contentItem: Text {
|
||||||
horizontalAlignment: Text.AlignHCenter
|
color: timelineRoot.palette.text
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
font.family: Settings.emojiFont
|
font.family: Settings.emojiFont
|
||||||
font.pixelSize: 36
|
font.pixelSize: 36
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
text: model.unicode.replace('\ufe0f', '')
|
text: model.unicode.replace('\ufe0f', '')
|
||||||
color: timelineRoot.palette.text
|
verticalAlignment: Text.AlignVCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
// TODO: maybe add favorites at some point?
|
||||||
anchors.fill: parent
|
onClicked: {
|
||||||
color: hovered ? timelineRoot.palette.highlight : 'transparent'
|
console.debug("Picked " + model.unicode);
|
||||||
radius: 5
|
emojiPopup.close();
|
||||||
|
callback(model.unicode);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollBar.vertical: ScrollBar {
|
|
||||||
id: emojiScroll
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Separator
|
// Separator
|
||||||
Rectangle {
|
Rectangle {
|
||||||
visible: emojiSearch.text === ''
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: 1
|
Layout.preferredHeight: 1
|
||||||
color: emojiPopup.Nheko.theme.separator
|
color: emojiPopup.Nheko.theme.separator
|
||||||
|
visible: emojiSearch.text === ''
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category picker row
|
// Category picker row
|
||||||
RowLayout {
|
RowLayout {
|
||||||
visible: emojiSearch.text === ''
|
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
||||||
Layout.bottomMargin: 0
|
Layout.bottomMargin: 0
|
||||||
Layout.preferredHeight: 42
|
Layout.preferredHeight: 42
|
||||||
implicitHeight: 42
|
implicitHeight: 42
|
||||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
visible: emojiSearch.text === ''
|
||||||
|
|
||||||
// Display the normal categories
|
// Display the normal categories
|
||||||
Repeater {
|
Repeater {
|
||||||
|
|
||||||
model: [
|
model: [
|
||||||
// TODO: Would like to get 'simple' icons for the categories
|
// TODO: Would like to get 'simple' icons for the categories
|
||||||
{
|
{
|
||||||
image: ":/icons/icons/emoji-categories/people.svg",
|
"image": ":/icons/icons/emoji-categories/people.svg",
|
||||||
category: Emoji.Category.People
|
"category": Emoji.Category.People
|
||||||
},
|
}, {
|
||||||
|
"image": ":/icons/icons/emoji-categories/nature.svg",
|
||||||
{
|
"category": Emoji.Category.Nature
|
||||||
image: ":/icons/icons/emoji-categories/nature.svg",
|
}, {
|
||||||
category: Emoji.Category.Nature
|
"image": ":/icons/icons/emoji-categories/foods.svg",
|
||||||
},
|
"category": Emoji.Category.Food
|
||||||
|
}, {
|
||||||
{
|
"image": ":/icons/icons/emoji-categories/activity.svg",
|
||||||
image: ":/icons/icons/emoji-categories/foods.svg",
|
"category": Emoji.Category.Activity
|
||||||
category: Emoji.Category.Food
|
}, {
|
||||||
},
|
"image": ":/icons/icons/emoji-categories/travel.svg",
|
||||||
|
"category": Emoji.Category.Travel
|
||||||
{
|
}, {
|
||||||
image: ":/icons/icons/emoji-categories/activity.svg",
|
"image": ":/icons/icons/emoji-categories/objects.svg",
|
||||||
category: Emoji.Category.Activity
|
"category": Emoji.Category.Objects
|
||||||
},
|
}, {
|
||||||
|
"image": ":/icons/icons/emoji-categories/symbols.svg",
|
||||||
{
|
"category": Emoji.Category.Symbols
|
||||||
image: ":/icons/icons/emoji-categories/travel.svg",
|
}, {
|
||||||
category: Emoji.Category.Travel
|
"image": ":/icons/icons/emoji-categories/flags.svg",
|
||||||
},
|
"category": Emoji.Category.Flags
|
||||||
|
}]
|
||||||
{
|
|
||||||
image: ":/icons/icons/emoji-categories/objects.svg",
|
|
||||||
category: Emoji.Category.Objects
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
image: ":/icons/icons/emoji-categories/symbols.svg",
|
|
||||||
category: Emoji.Category.Symbols
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
image: ":/icons/icons/emoji-categories/flags.svg",
|
|
||||||
category: Emoji.Category.Flags
|
|
||||||
}
|
|
||||||
|
|
||||||
]
|
|
||||||
|
|
||||||
delegate: AbstractButton {
|
delegate: AbstractButton {
|
||||||
Layout.preferredWidth: 36
|
|
||||||
Layout.preferredHeight: 36
|
Layout.preferredHeight: 36
|
||||||
hoverEnabled: true
|
Layout.preferredWidth: 36
|
||||||
ToolTip.text: {
|
ToolTip.text: {
|
||||||
switch (modelData.category) {
|
switch (modelData.category) {
|
||||||
case Emoji.Category.People:
|
case Emoji.Category.People:
|
||||||
|
@ -271,6 +241,27 @@ Menu {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
|
hoverEnabled: true
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
border.color: emojiPopup.model.category === modelData.category ? timelineRoot.palette.highlight : 'transparent'
|
||||||
|
color: emojiPopup.model.category === modelData.category ? Qt.hsla(highlightHue, highlightSat, highlightLight, 0.2) : 'transparent'
|
||||||
|
radius: 5
|
||||||
|
}
|
||||||
|
contentItem: Image {
|
||||||
|
fillMode: Image.Pad
|
||||||
|
height: 32
|
||||||
|
horizontalAlignment: Image.AlignHCenter
|
||||||
|
mipmap: true
|
||||||
|
smooth: true
|
||||||
|
source: "image://colorimage/" + modelData.image + "?" + (hovered ? timelineRoot.palette.highlight : timelineRoot.palette.placeholderText)
|
||||||
|
sourceSize.height: 32 * Screen.devicePixelRatio
|
||||||
|
sourceSize.width: 32 * Screen.devicePixelRatio
|
||||||
|
verticalAlignment: Image.AlignVCenter
|
||||||
|
width: 32
|
||||||
|
}
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
//emojiPopup.model.category = model.category;
|
//emojiPopup.model.category = model.category;
|
||||||
gridView.positionViewAtIndex(emojiPopup.model.sourceModel.categoryToIndex(modelData.category), GridView.Beginning);
|
gridView.positionViewAtIndex(emojiPopup.model.sourceModel.categoryToIndex(modelData.category), GridView.Beginning);
|
||||||
|
@ -278,40 +269,14 @@ Menu {
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onPressed: mouse.accepted = false
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
}
|
|
||||||
|
|
||||||
contentItem: Image {
|
onPressed: mouse.accepted = false
|
||||||
horizontalAlignment: Image.AlignHCenter
|
|
||||||
verticalAlignment: Image.AlignVCenter
|
|
||||||
fillMode: Image.Pad
|
|
||||||
height: 32
|
|
||||||
width: 32
|
|
||||||
smooth: true
|
|
||||||
mipmap: true
|
|
||||||
sourceSize.width: 32 * Screen.devicePixelRatio
|
|
||||||
sourceSize.height: 32 * Screen.devicePixelRatio
|
|
||||||
source: "image://colorimage/" + modelData.image + "?" + (hovered ? timelineRoot.palette.highlight : timelineRoot.palette.placeholderText)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
color: emojiPopup.model.category === modelData.category ? Qt.hsla(highlightHue, highlightSat, highlightLight, 0.2) : 'transparent'
|
|
||||||
radius: 5
|
|
||||||
border.color: emojiPopup.model.category === modelData.category ? timelineRoot.palette.highlight : 'transparent'
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import "../"
|
import "../"
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
|
@ -14,15 +12,15 @@ Menu {
|
||||||
|
|
||||||
property var callback
|
property var callback
|
||||||
property var colors
|
property var colors
|
||||||
property string roomid
|
|
||||||
property alias model: gridView.model
|
|
||||||
property var textArea
|
|
||||||
property real highlightHue: timelineRoot.palette.highlight.hslHue
|
property real highlightHue: timelineRoot.palette.highlight.hslHue
|
||||||
property real highlightSat: timelineRoot.palette.highlight.hslSaturation
|
|
||||||
property real highlightLight: timelineRoot.palette.highlight.hslLightness
|
property real highlightLight: timelineRoot.palette.highlight.hslLightness
|
||||||
|
property real highlightSat: timelineRoot.palette.highlight.hslSaturation
|
||||||
|
property alias model: gridView.model
|
||||||
|
property string roomid
|
||||||
readonly property int stickerDim: 128
|
readonly property int stickerDim: 128
|
||||||
readonly property int stickerDimPad: 128 + Nheko.paddingSmall
|
readonly property int stickerDimPad: 128 + Nheko.paddingSmall
|
||||||
readonly property int stickersPerRow: 3
|
readonly property int stickersPerRow: 3
|
||||||
|
property var textArea
|
||||||
|
|
||||||
function show(showAt, roomid_, callback) {
|
function show(showAt, roomid_, callback) {
|
||||||
console.debug("Showing sticker picker");
|
console.debug("Showing sticker picker");
|
||||||
|
@ -31,13 +29,13 @@ Menu {
|
||||||
popup(showAt ? showAt : null);
|
popup(showAt ? showAt : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
margins: 0
|
|
||||||
bottomPadding: 1
|
bottomPadding: 1
|
||||||
leftPadding: 1
|
|
||||||
rightPadding: 1
|
|
||||||
modal: true
|
|
||||||
focus: true
|
|
||||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
||||||
|
focus: true
|
||||||
|
leftPadding: 1
|
||||||
|
margins: 0
|
||||||
|
modal: true
|
||||||
|
rightPadding: 1
|
||||||
width: stickersPerRow * stickerDimPad + 20
|
width: stickersPerRow * stickerDimPad + 20
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
@ -47,28 +45,27 @@ Menu {
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: columnView
|
id: columnView
|
||||||
|
|
||||||
spacing: 0
|
|
||||||
anchors.leftMargin: 3
|
|
||||||
anchors.rightMargin: 3
|
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 3
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 3
|
||||||
anchors.topMargin: 2
|
anchors.topMargin: 2
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
// Search field
|
// Search field
|
||||||
TextField {
|
TextField {
|
||||||
id: emojiSearch
|
id: emojiSearch
|
||||||
|
|
||||||
Layout.topMargin: 3
|
|
||||||
Layout.preferredWidth: stickersPerRow * stickerDimPad + 20 - 6
|
Layout.preferredWidth: stickersPerRow * stickerDimPad + 20 - 6
|
||||||
palette: timelineRoot.palette
|
Layout.topMargin: 3
|
||||||
background: null
|
background: null
|
||||||
placeholderTextColor: timelineRoot.palette.placeholderText
|
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
|
palette: timelineRoot.palette
|
||||||
placeholderText: qsTr("Search")
|
placeholderText: qsTr("Search")
|
||||||
selectByMouse: true
|
placeholderTextColor: timelineRoot.palette.placeholderText
|
||||||
rightPadding: clearSearch.width
|
rightPadding: clearSearch.width
|
||||||
|
selectByMouse: true
|
||||||
|
|
||||||
onTextChanged: searchTimer.restart()
|
onTextChanged: searchTimer.restart()
|
||||||
onVisibleChanged: {
|
onVisibleChanged: {
|
||||||
if (visible)
|
if (visible)
|
||||||
|
@ -79,97 +76,85 @@ Menu {
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: searchTimer
|
id: searchTimer
|
||||||
|
|
||||||
interval: 350 // tweak as needed?
|
interval: 350 // tweak as needed?
|
||||||
|
|
||||||
onTriggered: stickerPopup.model.searchString = emojiSearch.text
|
onTriggered: stickerPopup.model.searchString = emojiSearch.text
|
||||||
}
|
}
|
||||||
|
|
||||||
ToolButton {
|
ToolButton {
|
||||||
id: clearSearch
|
id: clearSearch
|
||||||
|
|
||||||
visible: emojiSearch.text !== ''
|
|
||||||
icon.source: "image://colorimage/:/icons/icons/ui/round-remove-button.svg?" + (clearSearch.hovered ? timelineRoot.palette.highlight : timelineRoot.palette.placeholderText)
|
|
||||||
focusPolicy: Qt.NoFocus
|
|
||||||
onClicked: emojiSearch.clear()
|
|
||||||
hoverEnabled: true
|
|
||||||
background: null
|
background: null
|
||||||
|
focusPolicy: Qt.NoFocus
|
||||||
|
hoverEnabled: true
|
||||||
|
icon.source: "image://colorimage/:/icons/icons/ui/round-remove-button.svg?" + (clearSearch.hovered ? timelineRoot.palette.highlight : timelineRoot.palette.placeholderText)
|
||||||
|
visible: emojiSearch.text !== ''
|
||||||
|
|
||||||
|
onClicked: emojiSearch.clear()
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
verticalCenter: parent.verticalCenter
|
|
||||||
right: parent.right
|
right: parent.right
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
// clear the default hover effects.
|
// clear the default hover effects.
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
height: parent.height - 2 * Nheko.paddingSmall
|
height: parent.height - 2 * Nheko.paddingSmall
|
||||||
width: height
|
|
||||||
source: "image://colorimage/:/icons/icons/ui/round-remove-button.svg?" + (clearSearch.hovered ? timelineRoot.palette.highlight : timelineRoot.palette.placeholderText)
|
source: "image://colorimage/:/icons/icons/ui/round-remove-button.svg?" + (clearSearch.hovered ? timelineRoot.palette.highlight : timelineRoot.palette.placeholderText)
|
||||||
|
width: height
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
verticalCenter: parent.verticalCenter
|
|
||||||
right: parent.right
|
|
||||||
margins: Nheko.paddingSmall
|
margins: Nheko.paddingSmall
|
||||||
|
right: parent.right
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// emoji grid
|
// emoji grid
|
||||||
GridView {
|
GridView {
|
||||||
id: gridView
|
id: gridView
|
||||||
|
Layout.leftMargin: 4
|
||||||
model: roomid ? TimelineManager.completerFor("stickers", roomid) : null
|
|
||||||
Layout.preferredHeight: cellHeight * 3.5
|
Layout.preferredHeight: cellHeight * 3.5
|
||||||
Layout.preferredWidth: stickersPerRow * stickerDimPad + 20
|
Layout.preferredWidth: stickersPerRow * stickerDimPad + 20
|
||||||
Layout.leftMargin: 4
|
|
||||||
cellWidth: stickerDimPad
|
|
||||||
cellHeight: stickerDimPad
|
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
cacheBuffer: 500
|
||||||
|
cellHeight: stickerDimPad
|
||||||
|
cellWidth: stickerDimPad
|
||||||
clip: true
|
clip: true
|
||||||
currentIndex: -1 // prevent sorting from stealing focus
|
currentIndex: -1 // prevent sorting from stealing focus
|
||||||
cacheBuffer: 500
|
model: roomid ? TimelineManager.completerFor("stickers", roomid) : null
|
||||||
|
|
||||||
|
ScrollBar.vertical: ScrollBar {
|
||||||
|
id: emojiScroll
|
||||||
|
}
|
||||||
|
|
||||||
// Individual emoji
|
// Individual emoji
|
||||||
delegate: AbstractButton {
|
delegate: AbstractButton {
|
||||||
width: stickerDim
|
|
||||||
height: stickerDim
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.text: ":" + model.shortcode + ": - " + model.body
|
ToolTip.text: ":" + model.shortcode + ": - " + model.body
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
// TODO: maybe add favorites at some point?
|
height: stickerDim
|
||||||
onClicked: {
|
hoverEnabled: true
|
||||||
console.debug("Picked " + model.shortcode);
|
width: stickerDim
|
||||||
stickerPopup.close();
|
|
||||||
callback(model.originalRow);
|
|
||||||
}
|
|
||||||
|
|
||||||
contentItem: Image {
|
|
||||||
height: stickerDim
|
|
||||||
width: stickerDim
|
|
||||||
source: model.url.replace("mxc://", "image://MxcImage/") + "?scale"
|
|
||||||
fillMode: Image.PreserveAspectFit
|
|
||||||
}
|
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: hovered ? timelineRoot.palette.highlight : 'transparent'
|
color: hovered ? timelineRoot.palette.highlight : 'transparent'
|
||||||
radius: 5
|
radius: 5
|
||||||
}
|
}
|
||||||
|
contentItem: Image {
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
height: stickerDim
|
||||||
|
source: model.url.replace("mxc://", "image://MxcImage/") + "?scale"
|
||||||
|
width: stickerDim
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: maybe add favorites at some point?
|
||||||
|
onClicked: {
|
||||||
|
console.debug("Picked " + model.shortcode);
|
||||||
|
stickerPopup.close();
|
||||||
|
callback(model.originalRow);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollBar.vertical: ScrollBar {
|
|
||||||
id: emojiScroll
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,189 +1,172 @@
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
import QtQuick.Window 2.15
|
import QtQuick.Window 2.15
|
||||||
import im.nheko
|
import im.nheko
|
||||||
import "../components/"
|
import "../components"
|
||||||
import "../ui/"
|
import "../ui"
|
||||||
import "../"
|
import "../"
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: loginPage
|
id: loginPage
|
||||||
property int maxExpansion: 400
|
|
||||||
|
|
||||||
property string error: login.error
|
property string error: login.error
|
||||||
|
property int maxExpansion: 400
|
||||||
|
|
||||||
Login {
|
Login {
|
||||||
id: login
|
id: login
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollView {
|
ScrollView {
|
||||||
id: scroll
|
id: scroll
|
||||||
|
|
||||||
clip: false
|
|
||||||
palette: timelineRoot.palette
|
|
||||||
ScrollBar.horizontal.visible: false
|
ScrollBar.horizontal.visible: false
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
|
anchors.margins: Nheko.paddingLarge
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
height: Math.min(loginPage.height, col.implicitHeight)
|
clip: false
|
||||||
anchors.margins: Nheko.paddingLarge
|
|
||||||
|
|
||||||
contentWidth: availableWidth
|
contentWidth: availableWidth
|
||||||
|
height: Math.min(loginPage.height, col.implicitHeight)
|
||||||
|
palette: timelineRoot.palette
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: col
|
id: col
|
||||||
|
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
width: Math.min(loginPage.maxExpansion, scroll.width- Nheko.paddingLarge*2)
|
spacing: Nheko.paddingMedium
|
||||||
|
width: Math.min(loginPage.maxExpansion, scroll.width - Nheko.paddingLarge * 2)
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
source: "qrc:/logos/login.png"
|
|
||||||
height: 128
|
height: 128
|
||||||
|
source: "qrc:/logos/login.png"
|
||||||
width: 128
|
width: 128
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
spacing: Nheko.paddingLarge
|
spacing: Nheko.paddingLarge
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
id: matrixIdLabel
|
id: matrixIdLabel
|
||||||
|
Keys.forwardTo: [pwBtn, ssoRepeater]
|
||||||
|
ToolTip.text: qsTr("Your login name. A mxid should start with @ followed by the user id. After the user id you need to include your server name after a :.\nYou can also put your homeserver address there, if your server doesn't support .well-known lookup.\nExample: @user:server.my\nIf Nheko fails to discover your homeserver, it will show you a field to enter the server manually.")
|
||||||
label: qsTr("Matrix ID")
|
label: qsTr("Matrix ID")
|
||||||
placeholderText: qsTr("e.g @joe:matrix.org")
|
placeholderText: qsTr("e.g @joe:matrix.org")
|
||||||
|
|
||||||
onEditingFinished: login.mxid = text
|
onEditingFinished: login.mxid = text
|
||||||
|
|
||||||
ToolTip.text: qsTr("Your login name. A mxid should start with @ followed by the user id. After the user id you need to include your server name after a :.\nYou can also put your homeserver address there, if your server doesn't support .well-known lookup.\nExample: @user:server.my\nIf Nheko fails to discover your homeserver, it will show you a field to enter the server manually.")
|
|
||||||
Keys.forwardTo: [pwBtn, ssoRepeater]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Spinner {
|
Spinner {
|
||||||
height: matrixIdLabel.height/2
|
|
||||||
Layout.alignment: Qt.AlignBottom
|
Layout.alignment: Qt.AlignBottom
|
||||||
|
|
||||||
visible: running
|
|
||||||
running: login.lookingUpHs
|
|
||||||
foreground: timelineRoot.palette.mid
|
foreground: timelineRoot.palette.mid
|
||||||
|
height: matrixIdLabel.height / 2
|
||||||
|
running: login.lookingUpHs
|
||||||
|
visible: running
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
textFormat: Text.PlainText
|
|
||||||
color: Nheko.theme.error
|
color: Nheko.theme.error
|
||||||
text: login.mxidError
|
text: login.mxidError
|
||||||
|
textFormat: Text.PlainText
|
||||||
visible: text
|
visible: text
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
id: passwordLabel
|
id: passwordLabel
|
||||||
Layout.fillWidth: true
|
|
||||||
label: qsTr("Password")
|
|
||||||
echoMode: TextInput.Password
|
|
||||||
ToolTip.text: qsTr("Your password.")
|
|
||||||
visible: login.passwordSupported
|
|
||||||
Keys.forwardTo: [pwBtn, ssoRepeater]
|
Keys.forwardTo: [pwBtn, ssoRepeater]
|
||||||
|
Layout.fillWidth: true
|
||||||
|
ToolTip.text: qsTr("Your password.")
|
||||||
|
echoMode: TextInput.Password
|
||||||
|
label: qsTr("Password")
|
||||||
|
visible: login.passwordSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
id: deviceNameLabel
|
id: deviceNameLabel
|
||||||
|
Keys.forwardTo: [pwBtn, ssoRepeater]
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
ToolTip.text: qsTr("A name for this device, which will be shown to others, when verifying your devices. If none is provided a default is used.")
|
||||||
label: qsTr("Device name")
|
label: qsTr("Device name")
|
||||||
placeholderText: login.initialDeviceName()
|
placeholderText: login.initialDeviceName()
|
||||||
ToolTip.text: qsTr("A name for this device, which will be shown to others, when verifying your devices. If none is provided a default is used.")
|
|
||||||
Keys.forwardTo: [pwBtn, ssoRepeater]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
id: hsLabel
|
id: hsLabel
|
||||||
enabled: visible
|
Keys.forwardTo: [pwBtn, ssoRepeater]
|
||||||
visible: login.homeserverNeeded
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
ToolTip.text: qsTr("The address that can be used to contact you homeservers client API.\nExample: https://server.my:8787")
|
||||||
|
enabled: visible
|
||||||
label: qsTr("Homeserver address")
|
label: qsTr("Homeserver address")
|
||||||
placeholderText: qsTr("server.my:8787")
|
placeholderText: qsTr("server.my:8787")
|
||||||
text: login.homeserver
|
text: login.homeserver
|
||||||
onEditingFinished: login.homeserver = text
|
visible: login.homeserverNeeded
|
||||||
ToolTip.text: qsTr("The address that can be used to contact you homeservers client API.\nExample: https://server.my:8787")
|
|
||||||
Keys.forwardTo: [pwBtn, ssoRepeater]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
onEditingFinished: login.homeserver = text
|
||||||
|
}
|
||||||
Item {
|
Item {
|
||||||
height: Nheko.avatarSize
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
height: Nheko.avatarSize
|
||||||
|
|
||||||
Spinner {
|
Spinner {
|
||||||
height: parent.height
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
|
||||||
visible: running
|
|
||||||
running: login.loggingIn
|
|
||||||
foreground: timelineRoot.palette.mid
|
foreground: timelineRoot.palette.mid
|
||||||
|
height: parent.height
|
||||||
|
running: login.loggingIn
|
||||||
|
visible: running
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
textFormat: Text.PlainText
|
|
||||||
color: Nheko.theme.error
|
color: Nheko.theme.error
|
||||||
text: loginPage.error
|
text: loginPage.error
|
||||||
|
textFormat: Text.PlainText
|
||||||
visible: text
|
visible: text
|
||||||
}
|
}
|
||||||
|
|
||||||
FlatButton {
|
FlatButton {
|
||||||
id: pwBtn
|
id: pwBtn
|
||||||
visible: login.passwordSupported
|
|
||||||
enabled: login.homeserverValid && matrixIdLabel.text == login.mxid && login.homeserver == hsLabel.text
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
text: qsTr("LOGIN")
|
|
||||||
function pwLogin() {
|
function pwLogin() {
|
||||||
login.onLoginButtonClicked(Login.Password, matrixIdLabel.text, passwordLabel.text, deviceNameLabel.text)
|
login.onLoginButtonClicked(Login.Password, matrixIdLabel.text, passwordLabel.text, deviceNameLabel.text);
|
||||||
}
|
}
|
||||||
onClicked: pwBtn.pwLogin()
|
|
||||||
|
Keys.enabled: pwBtn.enabled && login.passwordSupported
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
enabled: login.homeserverValid && matrixIdLabel.text == login.mxid && login.homeserver == hsLabel.text
|
||||||
|
text: qsTr("LOGIN")
|
||||||
|
visible: login.passwordSupported
|
||||||
|
|
||||||
Keys.onEnterPressed: pwBtn.pwLogin()
|
Keys.onEnterPressed: pwBtn.pwLogin()
|
||||||
Keys.onReturnPressed: pwBtn.pwLogin()
|
Keys.onReturnPressed: pwBtn.pwLogin()
|
||||||
Keys.enabled: pwBtn.enabled && login.passwordSupported
|
onClicked: pwBtn.pwLogin()
|
||||||
}
|
}
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
id: ssoRepeater
|
id: ssoRepeater
|
||||||
|
|
||||||
model: login.identityProviders
|
model: login.identityProviders
|
||||||
|
|
||||||
delegate: FlatButton {
|
delegate: FlatButton {
|
||||||
id: ssoBtn
|
id: ssoBtn
|
||||||
visible: login.ssoSupported
|
|
||||||
enabled: login.homeserverValid && matrixIdLabel.text == login.mxid && login.homeserver == hsLabel.text
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
text: modelData.name
|
|
||||||
iconImage: modelData.avatarUrl.replace("mxc://", "image://MxcImage/")
|
|
||||||
function ssoLogin() {
|
function ssoLogin() {
|
||||||
login.onLoginButtonClicked(Login.SSO, matrixIdLabel.text, modelData.id, deviceNameLabel.text)
|
login.onLoginButtonClicked(Login.SSO, matrixIdLabel.text, modelData.id, deviceNameLabel.text);
|
||||||
}
|
}
|
||||||
onClicked: ssoBtn.ssoLogin()
|
|
||||||
|
Keys.enabled: ssoBtn.enabled && !login.passwordSupported
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
enabled: login.homeserverValid && matrixIdLabel.text == login.mxid && login.homeserver == hsLabel.text
|
||||||
|
iconImage: modelData.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
|
text: modelData.name
|
||||||
|
visible: login.ssoSupported
|
||||||
|
|
||||||
Keys.onEnterPressed: ssoBtn.ssoLogin()
|
Keys.onEnterPressed: ssoBtn.ssoLogin()
|
||||||
Keys.onReturnPressed: ssoBtn.ssoLogin()
|
Keys.onReturnPressed: ssoBtn.ssoLogin()
|
||||||
Keys.enabled: ssoBtn.enabled && !login.passwordSupported
|
onClicked: ssoBtn.ssoLogin()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
anchors.top: parent.top
|
ToolTip.text: qsTr("Back")
|
||||||
|
ToolTip.visible: hovered
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.margins: Nheko.paddingMedium
|
anchors.margins: Nheko.paddingMedium
|
||||||
width: Nheko.avatarSize
|
anchors.top: parent.top
|
||||||
height: Nheko.avatarSize
|
height: Nheko.avatarSize
|
||||||
image: ":/icons/icons/ui/angle-arrow-left.svg"
|
image: ":/icons/icons/ui/angle-arrow-left.svg"
|
||||||
ToolTip.visible: hovered
|
width: Nheko.avatarSize
|
||||||
ToolTip.text: qsTr("Back")
|
|
||||||
onClicked: mainWindow.pop()
|
onClicked: mainWindow.pop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,215 +1,192 @@
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
import QtQuick.Window 2.15
|
import QtQuick.Window 2.15
|
||||||
import im.nheko
|
import im.nheko
|
||||||
import "../components/"
|
import "../components"
|
||||||
import "../ui/"
|
import "../ui"
|
||||||
import "../"
|
import "../"
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: registrationPage
|
id: registrationPage
|
||||||
property int maxExpansion: 400
|
|
||||||
|
|
||||||
property string error: regis.error
|
property string error: regis.error
|
||||||
|
property int maxExpansion: 400
|
||||||
|
|
||||||
Registration {
|
Registration {
|
||||||
id: regis
|
id: regis
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollView {
|
ScrollView {
|
||||||
id: scroll
|
id: scroll
|
||||||
|
|
||||||
clip: false
|
|
||||||
palette: timelineRoot.palette
|
|
||||||
ScrollBar.horizontal.visible: false
|
ScrollBar.horizontal.visible: false
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
|
anchors.margins: Nheko.paddingLarge
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
height: Math.min(registrationPage.height, col.implicitHeight)
|
clip: false
|
||||||
anchors.margins: Nheko.paddingLarge
|
|
||||||
|
|
||||||
contentWidth: availableWidth
|
contentWidth: availableWidth
|
||||||
|
height: Math.min(registrationPage.height, col.implicitHeight)
|
||||||
|
palette: timelineRoot.palette
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: col
|
id: col
|
||||||
|
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
width: Math.min(registrationPage.maxExpansion, scroll.width- Nheko.paddingLarge*2)
|
spacing: Nheko.paddingMedium
|
||||||
|
width: Math.min(registrationPage.maxExpansion, scroll.width - Nheko.paddingLarge * 2)
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
source: "qrc:/logos/login.png"
|
|
||||||
height: 128
|
height: 128
|
||||||
|
source: "qrc:/logos/login.png"
|
||||||
width: 128
|
width: 128
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
spacing: Nheko.paddingLarge
|
spacing: Nheko.paddingLarge
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
id: hsLabel
|
id: hsLabel
|
||||||
|
ToolTip.text: qsTr("A server that allows registration. Since matrix is decentralized, you need to first find a server you can register on or host your own.")
|
||||||
label: qsTr("Homeserver")
|
label: qsTr("Homeserver")
|
||||||
placeholderText: qsTr("your.server")
|
placeholderText: qsTr("your.server")
|
||||||
|
|
||||||
onEditingFinished: regis.setServer(text)
|
onEditingFinished: regis.setServer(text)
|
||||||
|
|
||||||
ToolTip.text: qsTr("A server that allows registration. Since matrix is decentralized, you need to first find a server you can register on or host your own.")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Spinner {
|
Spinner {
|
||||||
height: hsLabel.height/2
|
|
||||||
Layout.alignment: Qt.AlignBottom
|
Layout.alignment: Qt.AlignBottom
|
||||||
|
|
||||||
visible: running
|
|
||||||
running: regis.lookingUpHs
|
|
||||||
foreground: timelineRoot.palette.mid
|
foreground: timelineRoot.palette.mid
|
||||||
|
height: hsLabel.height / 2
|
||||||
|
running: regis.lookingUpHs
|
||||||
|
visible: running
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
textFormat: Text.PlainText
|
|
||||||
color: Nheko.theme.error
|
color: Nheko.theme.error
|
||||||
text: regis.hsError
|
text: regis.hsError
|
||||||
|
textFormat: Text.PlainText
|
||||||
visible: text
|
visible: text
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
spacing: Nheko.paddingLarge
|
spacing: Nheko.paddingLarge
|
||||||
|
|
||||||
visible: regis.supported
|
visible: regis.supported
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
id: usernameLabel
|
id: usernameLabel
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
label: qsTr("Username")
|
|
||||||
ToolTip.text: qsTr("The username must not be empty, and must contain only the characters a-z, 0-9, ., _, =, -, and /.")
|
ToolTip.text: qsTr("The username must not be empty, and must contain only the characters a-z, 0-9, ., _, =, -, and /.")
|
||||||
|
label: qsTr("Username")
|
||||||
|
|
||||||
onEditingFinished: regis.checkUsername(text)
|
onEditingFinished: regis.checkUsername(text)
|
||||||
}
|
}
|
||||||
Spinner {
|
Spinner {
|
||||||
height: usernameLabel.height/2
|
|
||||||
Layout.alignment: Qt.AlignBottom
|
Layout.alignment: Qt.AlignBottom
|
||||||
|
|
||||||
visible: running
|
|
||||||
running: regis.lookingUpUsername
|
|
||||||
foreground: timelineRoot.palette.mid
|
foreground: timelineRoot.palette.mid
|
||||||
|
height: usernameLabel.height / 2
|
||||||
|
running: regis.lookingUpUsername
|
||||||
|
visible: running
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
width: usernameLabel.height/2
|
|
||||||
height: width
|
|
||||||
Layout.preferredHeight: usernameLabel.height/2
|
|
||||||
Layout.preferredWidth: usernameLabel.height/2
|
|
||||||
Layout.alignment: Qt.AlignBottom
|
Layout.alignment: Qt.AlignBottom
|
||||||
source: regis.usernameAvailable ? ("image://colorimage/:/icons/icons/ui/checkmark.svg?green") : ("image://colorimage/:/icons/icons/ui/dismiss.svg?"+Nheko.theme.error)
|
Layout.preferredHeight: usernameLabel.height / 2
|
||||||
visible: regis.usernameAvailable || regis.usernameUnavailable
|
Layout.preferredWidth: usernameLabel.height / 2
|
||||||
ToolTip.visible: ma.hovered
|
|
||||||
ToolTip.text: qsTr("Back")
|
ToolTip.text: qsTr("Back")
|
||||||
|
ToolTip.visible: ma.hovered
|
||||||
|
height: width
|
||||||
|
source: regis.usernameAvailable ? ("image://colorimage/:/icons/icons/ui/checkmark.svg?green") : ("image://colorimage/:/icons/icons/ui/dismiss.svg?" + Nheko.theme.error)
|
||||||
sourceSize.height: height * Screen.devicePixelRatio
|
sourceSize.height: height * Screen.devicePixelRatio
|
||||||
sourceSize.width: width * Screen.devicePixelRatio
|
sourceSize.width: width * Screen.devicePixelRatio
|
||||||
|
visible: regis.usernameAvailable || regis.usernameUnavailable
|
||||||
|
width: usernameLabel.height / 2
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: ma
|
id: ma
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
textFormat: Text.PlainText
|
|
||||||
color: Nheko.theme.error
|
color: Nheko.theme.error
|
||||||
text: regis.usernameError
|
text: regis.usernameError
|
||||||
|
textFormat: Text.PlainText
|
||||||
visible: text
|
visible: text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
visible: regis.supported
|
|
||||||
id: passwordLabel
|
id: passwordLabel
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
label: qsTr("Password")
|
|
||||||
echoMode: TextInput.Password
|
|
||||||
ToolTip.text: qsTr("Please choose a secure password. The exact requirements for password strength may depend on your server.")
|
ToolTip.text: qsTr("Please choose a secure password. The exact requirements for password strength may depend on your server.")
|
||||||
}
|
echoMode: TextInput.Password
|
||||||
|
label: qsTr("Password")
|
||||||
MatrixTextField {
|
|
||||||
visible: regis.supported
|
visible: regis.supported
|
||||||
|
}
|
||||||
|
MatrixTextField {
|
||||||
id: passwordConfirmationLabel
|
id: passwordConfirmationLabel
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
label: qsTr("Password confirmation")
|
|
||||||
echoMode: TextInput.Password
|
echoMode: TextInput.Password
|
||||||
}
|
label: qsTr("Password confirmation")
|
||||||
|
|
||||||
MatrixText {
|
|
||||||
visible: regis.supported
|
visible: regis.supported
|
||||||
textFormat: Text.PlainText
|
}
|
||||||
|
MatrixText {
|
||||||
color: Nheko.theme.error
|
color: Nheko.theme.error
|
||||||
text: passwordLabel.text != passwordConfirmationLabel.text ? qsTr("Your passwords do not match!") : ""
|
text: passwordLabel.text != passwordConfirmationLabel.text ? qsTr("Your passwords do not match!") : ""
|
||||||
}
|
textFormat: Text.PlainText
|
||||||
|
|
||||||
MatrixTextField {
|
|
||||||
visible: regis.supported
|
visible: regis.supported
|
||||||
|
}
|
||||||
|
MatrixTextField {
|
||||||
id: deviceNameLabel
|
id: deviceNameLabel
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
ToolTip.text: qsTr("A name for this device, which will be shown to others, when verifying your devices. If none is provided a default is used.")
|
||||||
label: qsTr("Device name")
|
label: qsTr("Device name")
|
||||||
placeholderText: regis.initialDeviceName()
|
placeholderText: regis.initialDeviceName()
|
||||||
ToolTip.text: qsTr("A name for this device, which will be shown to others, when verifying your devices. If none is provided a default is used.")
|
visible: regis.supported
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
height: Nheko.avatarSize
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
height: Nheko.avatarSize
|
||||||
|
|
||||||
Spinner {
|
Spinner {
|
||||||
height: parent.height
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
|
||||||
visible: running
|
|
||||||
running: regis.registering
|
|
||||||
foreground: timelineRoot.palette.mid
|
foreground: timelineRoot.palette.mid
|
||||||
|
height: parent.height
|
||||||
|
running: regis.registering
|
||||||
|
visible: running
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
textFormat: Text.PlainText
|
|
||||||
color: Nheko.theme.error
|
color: Nheko.theme.error
|
||||||
text: registrationPage.error
|
text: registrationPage.error
|
||||||
|
textFormat: Text.PlainText
|
||||||
visible: text
|
visible: text
|
||||||
}
|
}
|
||||||
|
|
||||||
FlatButton {
|
FlatButton {
|
||||||
id: regisBtn
|
id: regisBtn
|
||||||
visible: regis.supported
|
|
||||||
enabled: usernameLabel.text && passwordLabel.text && passwordLabel.text == passwordConfirmationLabel.text
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
text: qsTr("REGISTER")
|
|
||||||
function register() {
|
function register() {
|
||||||
regis.startRegistration(usernameLabel.text, passwordLabel.text, deviceNameLabel.text)
|
regis.startRegistration(usernameLabel.text, passwordLabel.text, deviceNameLabel.text);
|
||||||
}
|
}
|
||||||
onClicked: regisBtn.register()
|
|
||||||
|
Keys.enabled: regisBtn.enabled && regis.supported
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
enabled: usernameLabel.text && passwordLabel.text && passwordLabel.text == passwordConfirmationLabel.text
|
||||||
|
text: qsTr("REGISTER")
|
||||||
|
visible: regis.supported
|
||||||
|
|
||||||
Keys.onEnterPressed: regisBtn.register()
|
Keys.onEnterPressed: regisBtn.register()
|
||||||
Keys.onReturnPressed: regisBtn.register()
|
Keys.onReturnPressed: regisBtn.register()
|
||||||
Keys.enabled: regisBtn.enabled && regis.supported
|
onClicked: regisBtn.register()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
anchors.top: parent.top
|
ToolTip.text: qsTr("Back")
|
||||||
|
ToolTip.visible: hovered
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.margins: Nheko.paddingMedium
|
anchors.margins: Nheko.paddingMedium
|
||||||
width: Nheko.avatarSize
|
anchors.top: parent.top
|
||||||
height: Nheko.avatarSize
|
height: Nheko.avatarSize
|
||||||
image: ":/icons/icons/ui/angle-arrow-left.svg"
|
image: ":/icons/icons/ui/angle-arrow-left.svg"
|
||||||
ToolTip.visible: hovered
|
width: Nheko.avatarSize
|
||||||
ToolTip.text: qsTr("Back")
|
|
||||||
onClicked: mainWindow.pop()
|
onClicked: mainWindow.pop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
import "../"
|
||||||
import ".."
|
|
||||||
import "../ui"
|
import "../ui"
|
||||||
import Qt.labs.platform 1.1 as Platform
|
import Qt.labs.platform 1.1 as Platform
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
|
@ -18,92 +16,94 @@ Rectangle {
|
||||||
|
|
||||||
property int collapsePoint: 800
|
property int collapsePoint: 800
|
||||||
property bool collapsed: width < collapsePoint
|
property bool collapsed: width < collapsePoint
|
||||||
|
|
||||||
color: timelineRoot.palette.window
|
color: timelineRoot.palette.window
|
||||||
|
|
||||||
ScrollView {
|
ScrollView {
|
||||||
id: scroll
|
id: scroll
|
||||||
|
|
||||||
palette: timelineRoot.palette
|
|
||||||
ScrollBar.horizontal.visible: false
|
ScrollBar.horizontal.visible: false
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.topMargin: (collapsed? backButton.height : 0)+Nheko.paddingLarge
|
anchors.topMargin: (collapsed ? backButton.height : 0) + Nheko.paddingLarge
|
||||||
leftPadding: collapsed? Nheko.paddingMedium : Nheko.paddingLarge
|
|
||||||
bottomPadding: Nheko.paddingLarge
|
bottomPadding: Nheko.paddingLarge
|
||||||
contentWidth: availableWidth
|
contentWidth: availableWidth
|
||||||
|
leftPadding: collapsed ? Nheko.paddingMedium : Nheko.paddingLarge
|
||||||
|
palette: timelineRoot.palette
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: grid
|
id: grid
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: userSettingsDialog.collapsed ? 0 : (userSettingsDialog.width - userSettingsDialog.collapsePoint) * 0.4 + Nheko.paddingLarge
|
||||||
|
anchors.rightMargin: anchors.leftMargin
|
||||||
spacing: Nheko.paddingMedium
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.leftMargin: userSettingsDialog.collapsed ? 0 : (userSettingsDialog.width-userSettingsDialog.collapsePoint) * 0.4 + Nheko.paddingLarge
|
|
||||||
anchors.rightMargin: anchors.leftMargin
|
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
|
Layout.fillWidth: true
|
||||||
model: UserSettingsModel
|
model: UserSettingsModel
|
||||||
Layout.fillWidth:true
|
|
||||||
|
|
||||||
delegate: GridLayout {
|
delegate: GridLayout {
|
||||||
columns: collapsed? 1 : 2
|
|
||||||
rows: collapsed? 2: 1
|
|
||||||
required property var model
|
|
||||||
id: r
|
id: r
|
||||||
|
|
||||||
|
required property var model
|
||||||
|
|
||||||
|
columns: collapsed ? 1 : 2
|
||||||
|
rows: collapsed ? 2 : 1
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
Layout.fillWidth: true
|
|
||||||
color: timelineRoot.palette.text
|
|
||||||
text: model.name
|
|
||||||
//Layout.column: 0
|
//Layout.column: 0
|
||||||
Layout.columnSpan: (model.type == UserSettingsModel.SectionTitle && !userSettingsDialog.collapsed) ? 2 : 1
|
Layout.columnSpan: (model.type == UserSettingsModel.SectionTitle && !userSettingsDialog.collapsed) ? 2 : 1
|
||||||
|
Layout.fillWidth: true
|
||||||
//Layout.row: model.index
|
//Layout.row: model.index
|
||||||
//Layout.minimumWidth: implicitWidth
|
//Layout.minimumWidth: implicitWidth
|
||||||
Layout.leftMargin: model.type == UserSettingsModel.SectionTitle ? 0 : Nheko.paddingMedium
|
Layout.leftMargin: model.type == UserSettingsModel.SectionTitle ? 0 : Nheko.paddingMedium
|
||||||
Layout.topMargin: model.type == UserSettingsModel.SectionTitle ? Nheko.paddingLarge : 0
|
Layout.topMargin: model.type == UserSettingsModel.SectionTitle ? Nheko.paddingLarge : 0
|
||||||
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
|
ToolTip.text: model.description ?? ""
|
||||||
|
ToolTip.visible: hovered.hovered && model.description
|
||||||
|
color: timelineRoot.palette.text
|
||||||
font.pointSize: 1.1 * fontMetrics.font.pointSize
|
font.pointSize: 1.1 * fontMetrics.font.pointSize
|
||||||
|
text: model.name
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: hovered
|
id: hovered
|
||||||
enabled: model.description ?? false
|
enabled: model.description ?? false
|
||||||
}
|
}
|
||||||
ToolTip.visible: hovered.hovered && model.description
|
|
||||||
ToolTip.text: model.description ?? ""
|
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
|
||||||
wrapMode: Text.Wrap
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChooser {
|
DelegateChooser {
|
||||||
id: chooser
|
id: chooser
|
||||||
|
|
||||||
roleValue: model.type
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
|
|
||||||
Layout.columnSpan: (model.type == UserSettingsModel.SectionTitle && !userSettingsDialog.collapsed) ? 2 : 1
|
Layout.columnSpan: (model.type == UserSettingsModel.SectionTitle && !userSettingsDialog.collapsed) ? 2 : 1
|
||||||
|
Layout.fillWidth: model.type == UserSettingsModel.SectionTitle || model.type == UserSettingsModel.Options || model.type == UserSettingsModel.Number
|
||||||
|
Layout.maximumWidth: model.type == UserSettingsModel.SectionTitle ? Number.POSITIVE_INFINITY : 400
|
||||||
Layout.preferredHeight: child.height
|
Layout.preferredHeight: child.height
|
||||||
Layout.preferredWidth: Math.min(child.implicitWidth, child.width || 1000)
|
Layout.preferredWidth: Math.min(child.implicitWidth, child.width || 1000)
|
||||||
Layout.maximumWidth: model.type == UserSettingsModel.SectionTitle ? Number.POSITIVE_INFINITY : 400
|
|
||||||
Layout.fillWidth: model.type == UserSettingsModel.SectionTitle || model.type == UserSettingsModel.Options || model.type == UserSettingsModel.Number
|
|
||||||
Layout.rightMargin: model.type == UserSettingsModel.SectionTitle ? 0 : Nheko.paddingMedium
|
Layout.rightMargin: model.type == UserSettingsModel.SectionTitle ? 0 : Nheko.paddingMedium
|
||||||
|
roleValue: model.type
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: UserSettingsModel.Toggle
|
roleValue: UserSettingsModel.Toggle
|
||||||
|
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
checked: model.value
|
checked: model.value
|
||||||
onCheckedChanged: model.value = checked
|
|
||||||
enabled: model.enabled
|
enabled: model.enabled
|
||||||
|
|
||||||
|
onCheckedChanged: model.value = checked
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: UserSettingsModel.Options
|
roleValue: UserSettingsModel.Options
|
||||||
|
|
||||||
ComboBox {
|
ComboBox {
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
width: Math.min(parent.width, implicitWidth)
|
|
||||||
model: r.model.values
|
|
||||||
currentIndex: r.model.value
|
currentIndex: r.model.value
|
||||||
|
model: r.model.values
|
||||||
|
width: Math.min(parent.width, implicitWidth)
|
||||||
|
|
||||||
onCurrentIndexChanged: r.model.value = currentIndex
|
onCurrentIndexChanged: r.model.value = currentIndex
|
||||||
|
|
||||||
WheelHandler{} // suppress scrolling changing values
|
WheelHandler {
|
||||||
|
} // suppress scrolling changing values
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
|
@ -111,15 +111,17 @@ Rectangle {
|
||||||
|
|
||||||
SpinBox {
|
SpinBox {
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
width: Math.min(parent.width, implicitWidth)
|
|
||||||
from: model.valueLowerBound
|
|
||||||
to: model.valueUpperBound
|
|
||||||
stepSize: model.valueStep
|
|
||||||
value: model.value
|
|
||||||
onValueChanged: model.value = value
|
|
||||||
editable: true
|
editable: true
|
||||||
|
from: model.valueLowerBound
|
||||||
|
stepSize: model.valueStep
|
||||||
|
to: model.valueUpperBound
|
||||||
|
value: model.value
|
||||||
|
width: Math.min(parent.width, implicitWidth)
|
||||||
|
|
||||||
WheelHandler{} // suppress scrolling changing values
|
onValueChanged: model.value = value
|
||||||
|
|
||||||
|
WheelHandler {
|
||||||
|
} // suppress scrolling changing values
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
|
@ -128,56 +130,58 @@ Rectangle {
|
||||||
SpinBox {
|
SpinBox {
|
||||||
id: spinbox
|
id: spinbox
|
||||||
|
|
||||||
readonly property double div: 100
|
|
||||||
readonly property int decimals: 2
|
readonly property int decimals: 2
|
||||||
|
readonly property double div: 100
|
||||||
anchors.right: parent.right
|
|
||||||
width: Math.min(parent.width, implicitWidth)
|
|
||||||
from: model.valueLowerBound * div
|
|
||||||
to: model.valueUpperBound * div
|
|
||||||
stepSize: model.valueStep * div
|
|
||||||
value: model.value * div
|
|
||||||
onValueChanged: model.value = value/div
|
|
||||||
editable: true
|
|
||||||
|
|
||||||
property real realValue: value / div
|
property real realValue: value / div
|
||||||
|
|
||||||
|
anchors.right: parent.right
|
||||||
|
editable: true
|
||||||
|
from: model.valueLowerBound * div
|
||||||
|
stepSize: model.valueStep * div
|
||||||
|
textFromValue: function (value, locale) {
|
||||||
|
return Number(value / spinbox.div).toLocaleString(locale, 'f', spinbox.decimals);
|
||||||
|
}
|
||||||
|
to: model.valueUpperBound * div
|
||||||
|
value: model.value * div
|
||||||
|
valueFromText: function (text, locale) {
|
||||||
|
return Number.fromLocaleString(locale, text) * spinbox.div;
|
||||||
|
}
|
||||||
|
width: Math.min(parent.width, implicitWidth)
|
||||||
|
|
||||||
validator: DoubleValidator {
|
validator: DoubleValidator {
|
||||||
bottom: Math.min(spinbox.from/spinbox.div, spinbox.to/spinbox.div)
|
bottom: Math.min(spinbox.from / spinbox.div, spinbox.to / spinbox.div)
|
||||||
top: Math.max(spinbox.from/spinbox.div, spinbox.to/spinbox.div)
|
top: Math.max(spinbox.from / spinbox.div, spinbox.to / spinbox.div)
|
||||||
}
|
}
|
||||||
|
|
||||||
textFromValue: function(value, locale) {
|
onValueChanged: model.value = value / div
|
||||||
return Number(value / spinbox.div).toLocaleString(locale, 'f', spinbox.decimals)
|
|
||||||
}
|
|
||||||
|
|
||||||
valueFromText: function(text, locale) {
|
WheelHandler {
|
||||||
return Number.fromLocaleString(locale, text) * spinbox.div
|
} // suppress scrolling changing values
|
||||||
}
|
|
||||||
|
|
||||||
WheelHandler{} // suppress scrolling changing values
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: UserSettingsModel.ReadOnlyText
|
roleValue: UserSettingsModel.ReadOnlyText
|
||||||
|
|
||||||
TextEdit {
|
TextEdit {
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
text: model.value
|
|
||||||
readOnly: true
|
readOnly: true
|
||||||
selectByMouse: !Settings.mobileMode
|
selectByMouse: !Settings.mobileMode
|
||||||
|
text: model.value
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: UserSettingsModel.SectionTitle
|
roleValue: UserSettingsModel.SectionTitle
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
width: grid.width
|
|
||||||
height: fontMetrics.lineSpacing
|
height: fontMetrics.lineSpacing
|
||||||
|
width: grid.width
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.topMargin: Nheko.paddingSmall
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: Nheko.paddingSmall
|
||||||
color: timelineRoot.palette.placeholderText
|
color: timelineRoot.palette.placeholderText
|
||||||
height: 1
|
height: 1
|
||||||
}
|
}
|
||||||
|
@ -185,6 +189,7 @@ Rectangle {
|
||||||
}
|
}
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: UserSettingsModel.KeyStatus
|
roleValue: UserSettingsModel.KeyStatus
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
color: model.good ? "green" : Nheko.theme.error
|
color: model.good ? "green" : Nheko.theme.error
|
||||||
text: model.value ? qsTr("CACHED") : qsTr("NOT CACHED")
|
text: model.value ? qsTr("CACHED") : qsTr("NOT CACHED")
|
||||||
|
@ -192,26 +197,32 @@ Rectangle {
|
||||||
}
|
}
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: UserSettingsModel.SessionKeyImportExport
|
roleValue: UserSettingsModel.SessionKeyImportExport
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Button {
|
Button {
|
||||||
text: qsTr("IMPORT")
|
text: qsTr("IMPORT")
|
||||||
|
|
||||||
onClicked: UserSettingsModel.importSessionKeys()
|
onClicked: UserSettingsModel.importSessionKeys()
|
||||||
}
|
}
|
||||||
Button {
|
Button {
|
||||||
text: qsTr("EXPORT")
|
text: qsTr("EXPORT")
|
||||||
|
|
||||||
onClicked: UserSettingsModel.exportSessionKeys()
|
onClicked: UserSettingsModel.exportSessionKeys()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: UserSettingsModel.XSignKeysRequestDownload
|
roleValue: UserSettingsModel.XSignKeysRequestDownload
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Button {
|
Button {
|
||||||
text: qsTr("DOWNLOAD")
|
text: qsTr("DOWNLOAD")
|
||||||
|
|
||||||
onClicked: UserSettingsModel.downloadCrossSigningSecrets()
|
onClicked: UserSettingsModel.downloadCrossSigningSecrets()
|
||||||
}
|
}
|
||||||
Button {
|
Button {
|
||||||
text: qsTr("REQUEST")
|
text: qsTr("REQUEST")
|
||||||
|
|
||||||
onClicked: UserSettingsModel.requestCrossSigningSecrets()
|
onClicked: UserSettingsModel.requestCrossSigningSecrets()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -226,19 +237,17 @@ Rectangle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: backButton
|
id: backButton
|
||||||
anchors.top: parent.top
|
ToolTip.text: qsTr("Back")
|
||||||
|
ToolTip.visible: hovered
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.margins: Nheko.paddingMedium
|
anchors.margins: Nheko.paddingMedium
|
||||||
width: Nheko.avatarSize
|
anchors.top: parent.top
|
||||||
height: Nheko.avatarSize
|
height: Nheko.avatarSize
|
||||||
image: ":/icons/icons/ui/angle-arrow-left.svg"
|
image: ":/icons/icons/ui/angle-arrow-left.svg"
|
||||||
ToolTip.visible: hovered
|
width: Nheko.avatarSize
|
||||||
ToolTip.text: qsTr("Back")
|
|
||||||
onClicked: mainWindow.pop()
|
onClicked: mainWindow.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,64 +1,61 @@
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
import QtQuick.Window 2.15
|
import QtQuick.Window 2.15
|
||||||
import im.nheko
|
import im.nheko
|
||||||
import "../components/"
|
import "../components"
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
Item {
|
Item {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
source: "qrc:/logos/splash.png"
|
|
||||||
height: 256
|
height: 256
|
||||||
|
source: "qrc:/logos/splash.png"
|
||||||
width: 256
|
width: 256
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.margins: Nheko.paddingLarge
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.bottomMargin: 0
|
Layout.bottomMargin: 0
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: qsTr("Welcome to nheko! The desktop client for the Matrix protocol.")
|
Layout.margins: Nheko.paddingLarge
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
font.pointSize: fontMetrics.font.pointSize*2
|
font.pointSize: fontMetrics.font.pointSize * 2
|
||||||
wrapMode: Text.Wrap
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: qsTr("Welcome to nheko! The desktop client for the Matrix protocol.")
|
||||||
|
wrapMode: Text.Wrap
|
||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
Layout.margins: Nheko.paddingLarge
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: qsTr("Enjoy your stay!")
|
Layout.margins: Nheko.paddingLarge
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
font.pointSize: fontMetrics.font.pointSize*1.5
|
font.pointSize: fontMetrics.font.pointSize * 1.5
|
||||||
wrapMode: Text.Wrap
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: qsTr("Enjoy your stay!")
|
||||||
|
wrapMode: Text.Wrap
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
FlatButton {
|
FlatButton {
|
||||||
Layout.margins: Nheko.paddingLarge
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.margins: Nheko.paddingLarge
|
||||||
text: qsTr("REGISTER")
|
text: qsTr("REGISTER")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
mainWindow.push(registerPage);
|
mainWindow.push(registerPage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FlatButton {
|
FlatButton {
|
||||||
Layout.margins: Nheko.paddingLarge
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.margins: Nheko.paddingLarge
|
||||||
text: qsTr("LOGIN")
|
text: qsTr("LOGIN")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
mainWindow.push(loginPage);
|
mainWindow.push(loginPage);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
import im.nheko
|
import im.nheko
|
||||||
|
@ -10,42 +8,39 @@ import im.nheko
|
||||||
Slider {
|
Slider {
|
||||||
id: control
|
id: control
|
||||||
|
|
||||||
property color progressColor: timelineRoot.palette.highlight
|
|
||||||
property bool alwaysShowSlider: true
|
property bool alwaysShowSlider: true
|
||||||
|
property color progressColor: timelineRoot.palette.highlight
|
||||||
property int sliderRadius: 16
|
property int sliderRadius: 16
|
||||||
|
|
||||||
value: 0
|
|
||||||
implicitHeight: sliderRadius
|
implicitHeight: sliderRadius
|
||||||
padding: 0
|
padding: 0
|
||||||
|
value: 0
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
|
color: timelineRoot.palette.placeholderText
|
||||||
|
height: implicitHeight
|
||||||
|
implicitHeight: control.sliderRadius / 4
|
||||||
|
implicitWidth: 200
|
||||||
|
radius: height / 2
|
||||||
|
width: control.availableWidth - handle.width
|
||||||
x: control.leftPadding + handle.width / 2
|
x: control.leftPadding + handle.width / 2
|
||||||
y: control.topPadding + control.availableHeight / 2 - height / 2
|
y: control.topPadding + control.availableHeight / 2 - height / 2
|
||||||
implicitWidth: 200
|
|
||||||
implicitHeight: control.sliderRadius / 4
|
|
||||||
width: control.availableWidth - handle.width
|
|
||||||
height: implicitHeight
|
|
||||||
radius: height / 2
|
|
||||||
color: timelineRoot.palette.placeholderText
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: control.visualPosition * parent.width
|
|
||||||
height: parent.height
|
|
||||||
color: control.progressColor
|
color: control.progressColor
|
||||||
|
height: parent.height
|
||||||
radius: 2
|
radius: 2
|
||||||
|
width: control.visualPosition * parent.width
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handle: Rectangle {
|
handle: Rectangle {
|
||||||
|
border.color: control.progressColor
|
||||||
|
color: control.progressColor
|
||||||
|
implicitHeight: control.sliderRadius
|
||||||
|
implicitWidth: control.sliderRadius
|
||||||
|
radius: control.sliderRadius / 2
|
||||||
|
visible: Settings.mobileMode || control.alwaysShowSlider || control.hovered || control.pressed
|
||||||
x: control.leftPadding + control.visualPosition * background.width
|
x: control.leftPadding + control.visualPosition * background.width
|
||||||
y: control.topPadding + control.availableHeight / 2 - height / 2
|
y: control.topPadding + control.availableHeight / 2 - height / 2
|
||||||
implicitWidth: control.sliderRadius
|
|
||||||
implicitHeight: control.sliderRadius
|
|
||||||
radius: control.sliderRadius / 2
|
|
||||||
color: control.progressColor
|
|
||||||
visible: Settings.mobileMode || control.alwaysShowSlider || control.hovered || control.pressed
|
|
||||||
border.color: control.progressColor
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
|
|
||||||
|
@ -11,28 +9,21 @@ Item {
|
||||||
|
|
||||||
property color color: "#22000000"
|
property color color: "#22000000"
|
||||||
property real maxRadius: Math.max(width, height)
|
property real maxRadius: Math.max(width, height)
|
||||||
|
readonly property real opacityAnimationDuration: 300
|
||||||
readonly property real radiusAnimationRate: 0.05
|
readonly property real radiusAnimationRate: 0.05
|
||||||
readonly property real radiusTailAnimationRate: 0.5
|
readonly property real radiusTailAnimationRate: 0.5
|
||||||
readonly property real opacityAnimationDuration: 300
|
|
||||||
property var rippleTarget: parent
|
property var rippleTarget: parent
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
PointHandler {
|
PointHandler {
|
||||||
id: ph
|
id: ph
|
||||||
|
|
||||||
onGrabChanged: {
|
|
||||||
circle.centerX = point.position.x
|
|
||||||
circle.centerY = point.position.y
|
|
||||||
}
|
|
||||||
|
|
||||||
target: Rectangle {
|
target: Rectangle {
|
||||||
id: backgroundLayer
|
id: backgroundLayer
|
||||||
parent: rippleTarget
|
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: "transparent"
|
|
||||||
clip: true
|
clip: true
|
||||||
|
color: "transparent"
|
||||||
|
parent: rippleTarget
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: circle
|
id: circle
|
||||||
|
@ -40,15 +31,14 @@ Item {
|
||||||
property real centerX
|
property real centerX
|
||||||
property real centerY
|
property real centerY
|
||||||
|
|
||||||
|
color: ripple.color
|
||||||
|
height: radius * 2
|
||||||
|
radius: 0
|
||||||
|
state: ph.active ? "ACTIVE" : "NORMAL"
|
||||||
|
width: radius * 2
|
||||||
x: centerX - radius
|
x: centerX - radius
|
||||||
y: centerY - radius
|
y: centerY - radius
|
||||||
|
|
||||||
height: radius*2
|
|
||||||
width: radius*2
|
|
||||||
radius: 0
|
|
||||||
color: ripple.color
|
|
||||||
|
|
||||||
state: ph.active ? "ACTIVE" : "NORMAL"
|
|
||||||
states: [
|
states: [
|
||||||
State {
|
State {
|
||||||
name: "NORMAL"
|
name: "NORMAL"
|
||||||
|
@ -65,26 +55,29 @@ Item {
|
||||||
SequentialAnimation {
|
SequentialAnimation {
|
||||||
//PropertyAction { target: circle; property: "centerX"; value: ph.point.position.x }
|
//PropertyAction { target: circle; property: "centerX"; value: ph.point.position.x }
|
||||||
//PropertyAction { target: circle; property: "centerY"; value: ph.point.position.y }
|
//PropertyAction { target: circle; property: "centerY"; value: ph.point.position.y }
|
||||||
PropertyAction { target: circle; property: "visible"; value: true }
|
PropertyAction {
|
||||||
PropertyAction { target: circle; property: "opacity"; value: 1 }
|
property: "visible"
|
||||||
|
target: circle
|
||||||
|
value: true
|
||||||
|
}
|
||||||
|
PropertyAction {
|
||||||
|
property: "opacity"
|
||||||
|
target: circle
|
||||||
|
value: 1
|
||||||
|
}
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
id: radius_animation
|
id: radius_animation
|
||||||
|
|
||||||
target: circle
|
|
||||||
properties: "radius"
|
|
||||||
from: 0
|
|
||||||
to: ripple.maxRadius
|
|
||||||
duration: ripple.maxRadius / ripple.radiusAnimationRate
|
duration: ripple.maxRadius / ripple.radiusAnimationRate
|
||||||
|
from: 0
|
||||||
|
properties: "radius"
|
||||||
|
target: circle
|
||||||
|
to: ripple.maxRadius
|
||||||
|
|
||||||
easing {
|
easing {
|
||||||
type: Easing.OutQuad
|
type: Easing.OutQuad
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
Transition {
|
Transition {
|
||||||
from: "ACTIVE"
|
from: "ACTIVE"
|
||||||
|
@ -94,38 +87,41 @@ Item {
|
||||||
ParallelAnimation {
|
ParallelAnimation {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
id: radius_tail_animation
|
id: radius_tail_animation
|
||||||
|
|
||||||
target: circle
|
|
||||||
properties: "radius"
|
|
||||||
to: ripple.maxRadius
|
|
||||||
duration: ripple.maxRadius / ripple.radiusTailAnimationRate
|
duration: ripple.maxRadius / ripple.radiusTailAnimationRate
|
||||||
|
properties: "radius"
|
||||||
|
target: circle
|
||||||
|
to: ripple.maxRadius
|
||||||
|
|
||||||
easing {
|
easing {
|
||||||
type: Easing.Linear
|
type: Easing.Linear
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
id: opacity_animation
|
id: opacity_animation
|
||||||
|
|
||||||
target: circle
|
|
||||||
properties: "opacity"
|
|
||||||
to: 0
|
|
||||||
duration: ripple.opacityAnimationDuration
|
duration: ripple.opacityAnimationDuration
|
||||||
|
properties: "opacity"
|
||||||
|
target: circle
|
||||||
|
to: 0
|
||||||
|
|
||||||
easing {
|
easing {
|
||||||
type: Easing.InQuad
|
type: Easing.InQuad
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
PropertyAction { target: circle; property: "visible"; value: false }
|
PropertyAction {
|
||||||
|
property: "visible"
|
||||||
|
target: circle
|
||||||
|
value: false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onGrabChanged: {
|
||||||
|
circle.centerX = point.position.x;
|
||||||
|
circle.centerY = point.position.y;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
import im.nheko
|
import im.nheko
|
||||||
|
@ -9,8 +7,8 @@ import im.nheko
|
||||||
Popup {
|
Popup {
|
||||||
id: snackbar
|
id: snackbar
|
||||||
|
|
||||||
property var messages: []
|
|
||||||
property string currentMessage: ""
|
property string currentMessage: ""
|
||||||
|
property var messages: []
|
||||||
|
|
||||||
function showNotification(msg) {
|
function showNotification(msg) {
|
||||||
messages.push(msg);
|
messages.push(msg);
|
||||||
|
@ -21,10 +19,58 @@ Popup {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
opacity: 0
|
||||||
id: dismissTimer
|
padding: Nheko.paddingLarge
|
||||||
interval: 10000
|
parent: Overlay.overlay
|
||||||
onTriggered: snackbar.close()
|
x: (parent.width - width) / 2
|
||||||
|
y: -100
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: timelineRoot.palette.dark
|
||||||
|
opacity: 0.8
|
||||||
|
radius: Nheko.paddingLarge
|
||||||
|
}
|
||||||
|
contentItem: Label {
|
||||||
|
color: timelineRoot.palette.light
|
||||||
|
font.bold: true
|
||||||
|
text: snackbar.currentMessage
|
||||||
|
width: Math.max(Overlay.overlay ? Overlay.overlay.width / 2 : 0, 400)
|
||||||
|
}
|
||||||
|
enter: Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 200
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
from: 0.0
|
||||||
|
property: "opacity"
|
||||||
|
target: snackbar
|
||||||
|
to: 1.0
|
||||||
|
}
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 1000
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
from: -100
|
||||||
|
properties: "y"
|
||||||
|
target: snackbar
|
||||||
|
to: 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exit: Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 300
|
||||||
|
easing.type: Easing.InCubic
|
||||||
|
from: 1.0
|
||||||
|
property: "opacity"
|
||||||
|
target: snackbar
|
||||||
|
to: 0.0
|
||||||
|
}
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 300
|
||||||
|
easing.type: Easing.InCubic
|
||||||
|
from: 100
|
||||||
|
properties: "y"
|
||||||
|
target: snackbar
|
||||||
|
to: -100
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onAboutToHide: {
|
onAboutToHide: {
|
||||||
|
@ -38,61 +84,10 @@ Popup {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parent: Overlay.overlay
|
Timer {
|
||||||
opacity: 0
|
id: dismissTimer
|
||||||
y: -100
|
interval: 10000
|
||||||
x: (parent.width - width)/2
|
|
||||||
padding: Nheko.paddingLarge
|
|
||||||
|
|
||||||
contentItem: Label {
|
onTriggered: snackbar.close()
|
||||||
color: timelineRoot.palette.light
|
|
||||||
width: Math.max(Overlay.overlay? Overlay.overlay.width/2 : 0, 400)
|
|
||||||
text: snackbar.currentMessage
|
|
||||||
font.bold: true
|
|
||||||
}
|
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
radius: Nheko.paddingLarge
|
|
||||||
color: timelineRoot.palette.dark
|
|
||||||
opacity: 0.8
|
|
||||||
}
|
|
||||||
|
|
||||||
enter: Transition {
|
|
||||||
NumberAnimation {
|
|
||||||
target: snackbar
|
|
||||||
property: "opacity"
|
|
||||||
from: 0.0
|
|
||||||
to: 1.0
|
|
||||||
duration: 200
|
|
||||||
easing.type: Easing.OutCubic
|
|
||||||
}
|
|
||||||
NumberAnimation {
|
|
||||||
target: snackbar
|
|
||||||
properties: "y"
|
|
||||||
from: -100
|
|
||||||
to: 100
|
|
||||||
duration: 1000
|
|
||||||
easing.type: Easing.OutCubic
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exit: Transition {
|
|
||||||
NumberAnimation {
|
|
||||||
target: snackbar
|
|
||||||
property: "opacity"
|
|
||||||
from: 1.0
|
|
||||||
to: 0.0
|
|
||||||
duration: 300
|
|
||||||
easing.type: Easing.InCubic
|
|
||||||
}
|
|
||||||
NumberAnimation {
|
|
||||||
target: snackbar
|
|
||||||
properties: "y"
|
|
||||||
to: -100
|
|
||||||
from: 100
|
|
||||||
duration: 300
|
|
||||||
easing.type: Easing.InCubic
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,143 +1,116 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
import "animations"
|
||||||
import "./animations"
|
|
||||||
//import QtGraphicalEffects 1.12
|
//import QtGraphicalEffects 1.12
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: spinner
|
id: spinner
|
||||||
|
|
||||||
property int spacing: 0
|
|
||||||
property bool running: true
|
|
||||||
property var foreground: "#333"
|
|
||||||
readonly property int barCount: 6
|
|
||||||
readonly property real a: Math.PI / 6
|
readonly property real a: Math.PI / 6
|
||||||
readonly property var colors: ["#c0def5", "#87aade", "white"]
|
|
||||||
readonly property var anims: [anim1, anim2, anim3, anim4, anim5, anim6]
|
readonly property var anims: [anim1, anim2, anim3, anim4, anim5, anim6]
|
||||||
readonly property int pauseDuration: barCount * 150
|
readonly property int barCount: 6
|
||||||
|
readonly property var colors: ["#c0def5", "#87aade", "white"]
|
||||||
|
property var foreground: "#333"
|
||||||
readonly property int glowDuration: 300
|
readonly property int glowDuration: 300
|
||||||
|
readonly property int pauseDuration: barCount * 150
|
||||||
|
property bool running: true
|
||||||
|
property int spacing: 0
|
||||||
|
|
||||||
height: 40
|
height: 40
|
||||||
width: barCount * (height * 0.375)
|
width: barCount * (height * 0.375)
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: row
|
id: row
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: rect1
|
|
||||||
|
|
||||||
width: ((spinner.width / spinner.barCount) - (spinner.spacing)) * 1.5
|
|
||||||
height: spinner.height / 3.5
|
|
||||||
color: "white"
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: rect2
|
|
||||||
|
|
||||||
width: (spinner.width / spinner.barCount) - spinner.spacing
|
|
||||||
height: spinner.height
|
|
||||||
color: spinner.colors[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: rect3
|
|
||||||
|
|
||||||
width: (spinner.width / spinner.barCount) - spinner.spacing
|
|
||||||
height: spinner.height
|
|
||||||
color: spinner.colors[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: rect4
|
|
||||||
|
|
||||||
width: (spinner.width / spinner.barCount) - spinner.spacing
|
|
||||||
height: spinner.height
|
|
||||||
color: spinner.colors[2]
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: rect5
|
|
||||||
|
|
||||||
width: (spinner.width / (spinner.barCount + 1)) - spinner.spacing
|
|
||||||
height: spinner.height / 3.5
|
|
||||||
color: "white"
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: rect6
|
|
||||||
|
|
||||||
width: (spinner.width / spinner.barCount) - spinner.spacing
|
|
||||||
height: spinner.height
|
|
||||||
color: "white"
|
|
||||||
}
|
|
||||||
|
|
||||||
BlinkAnimation {
|
|
||||||
id: anim1
|
|
||||||
|
|
||||||
target: rect1
|
|
||||||
running: spinner.running
|
|
||||||
pauseDuration: spinner.pauseDuration
|
|
||||||
glowDuration: spinner.glowDuration
|
|
||||||
offset: 0 / spinner.barCount
|
|
||||||
}
|
|
||||||
|
|
||||||
BlinkAnimation {
|
|
||||||
id: anim2
|
|
||||||
|
|
||||||
target: rect2
|
|
||||||
running: spinner.running
|
|
||||||
pauseDuration: spinner.pauseDuration
|
|
||||||
glowDuration: spinner.glowDuration
|
|
||||||
offset: 1 / spinner.barCount
|
|
||||||
}
|
|
||||||
|
|
||||||
BlinkAnimation {
|
|
||||||
id: anim3
|
|
||||||
|
|
||||||
target: rect3
|
|
||||||
running: spinner.running
|
|
||||||
pauseDuration: spinner.pauseDuration
|
|
||||||
glowDuration: spinner.glowDuration
|
|
||||||
offset: 2 / spinner.barCount
|
|
||||||
}
|
|
||||||
|
|
||||||
BlinkAnimation {
|
|
||||||
id: anim4
|
|
||||||
|
|
||||||
target: rect4
|
|
||||||
running: spinner.running
|
|
||||||
pauseDuration: spinner.pauseDuration
|
|
||||||
glowDuration: spinner.glowDuration
|
|
||||||
offset: 3 / spinner.barCount
|
|
||||||
}
|
|
||||||
|
|
||||||
BlinkAnimation {
|
|
||||||
id: anim5
|
|
||||||
|
|
||||||
target: rect5
|
|
||||||
running: spinner.running
|
|
||||||
pauseDuration: spinner.pauseDuration
|
|
||||||
glowDuration: spinner.glowDuration
|
|
||||||
offset: 4 / spinner.barCount
|
|
||||||
}
|
|
||||||
|
|
||||||
BlinkAnimation {
|
|
||||||
id: anim6
|
|
||||||
|
|
||||||
target: rect6
|
|
||||||
running: spinner.running
|
|
||||||
pauseDuration: spinner.pauseDuration
|
|
||||||
glowDuration: spinner.glowDuration
|
|
||||||
offset: 5 / spinner.barCount
|
|
||||||
}
|
|
||||||
|
|
||||||
transform: Matrix4x4 {
|
transform: Matrix4x4 {
|
||||||
matrix: Qt.matrix4x4(Math.cos(spinner.a), -Math.sin(spinner.a), 0, 0, 0, Math.cos(spinner.a), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
|
matrix: Qt.matrix4x4(Math.cos(spinner.a), -Math.sin(spinner.a), 0, 0, 0, Math.cos(spinner.a), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: rect1
|
||||||
|
color: "white"
|
||||||
|
height: spinner.height / 3.5
|
||||||
|
width: ((spinner.width / spinner.barCount) - (spinner.spacing)) * 1.5
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
id: rect2
|
||||||
|
color: spinner.colors[0]
|
||||||
|
height: spinner.height
|
||||||
|
width: (spinner.width / spinner.barCount) - spinner.spacing
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
id: rect3
|
||||||
|
color: spinner.colors[1]
|
||||||
|
height: spinner.height
|
||||||
|
width: (spinner.width / spinner.barCount) - spinner.spacing
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
id: rect4
|
||||||
|
color: spinner.colors[2]
|
||||||
|
height: spinner.height
|
||||||
|
width: (spinner.width / spinner.barCount) - spinner.spacing
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
id: rect5
|
||||||
|
color: "white"
|
||||||
|
height: spinner.height / 3.5
|
||||||
|
width: (spinner.width / (spinner.barCount + 1)) - spinner.spacing
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
id: rect6
|
||||||
|
color: "white"
|
||||||
|
height: spinner.height
|
||||||
|
width: (spinner.width / spinner.barCount) - spinner.spacing
|
||||||
|
}
|
||||||
|
BlinkAnimation {
|
||||||
|
id: anim1
|
||||||
|
glowDuration: spinner.glowDuration
|
||||||
|
offset: 0 / spinner.barCount
|
||||||
|
pauseDuration: spinner.pauseDuration
|
||||||
|
running: spinner.running
|
||||||
|
target: rect1
|
||||||
|
}
|
||||||
|
BlinkAnimation {
|
||||||
|
id: anim2
|
||||||
|
glowDuration: spinner.glowDuration
|
||||||
|
offset: 1 / spinner.barCount
|
||||||
|
pauseDuration: spinner.pauseDuration
|
||||||
|
running: spinner.running
|
||||||
|
target: rect2
|
||||||
|
}
|
||||||
|
BlinkAnimation {
|
||||||
|
id: anim3
|
||||||
|
glowDuration: spinner.glowDuration
|
||||||
|
offset: 2 / spinner.barCount
|
||||||
|
pauseDuration: spinner.pauseDuration
|
||||||
|
running: spinner.running
|
||||||
|
target: rect3
|
||||||
|
}
|
||||||
|
BlinkAnimation {
|
||||||
|
id: anim4
|
||||||
|
glowDuration: spinner.glowDuration
|
||||||
|
offset: 3 / spinner.barCount
|
||||||
|
pauseDuration: spinner.pauseDuration
|
||||||
|
running: spinner.running
|
||||||
|
target: rect4
|
||||||
|
}
|
||||||
|
BlinkAnimation {
|
||||||
|
id: anim5
|
||||||
|
glowDuration: spinner.glowDuration
|
||||||
|
offset: 4 / spinner.barCount
|
||||||
|
pauseDuration: spinner.pauseDuration
|
||||||
|
running: spinner.running
|
||||||
|
target: rect5
|
||||||
|
}
|
||||||
|
BlinkAnimation {
|
||||||
|
id: anim6
|
||||||
|
glowDuration: spinner.glowDuration
|
||||||
|
offset: 5 / spinner.barCount
|
||||||
|
pauseDuration: spinner.pauseDuration
|
||||||
|
running: spinner.running
|
||||||
|
target: rect6
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Glow {
|
//Glow {
|
||||||
|
@ -152,5 +125,4 @@ Item {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
//}
|
//}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +1,26 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
|
|
||||||
SequentialAnimation {
|
SequentialAnimation {
|
||||||
property alias target: numberAnimation.target
|
|
||||||
property alias glowDuration: numberAnimation.duration
|
property alias glowDuration: numberAnimation.duration
|
||||||
property int pauseDuration: 150
|
|
||||||
property double offset: 0
|
property double offset: 0
|
||||||
|
property int pauseDuration: 150
|
||||||
|
property alias target: numberAnimation.target
|
||||||
|
|
||||||
loops: Animation.Infinite
|
loops: Animation.Infinite
|
||||||
|
|
||||||
PauseAnimation {
|
PauseAnimation {
|
||||||
duration: pauseDuration * offset
|
duration: pauseDuration * offset
|
||||||
}
|
}
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
id: numberAnimation
|
id: numberAnimation
|
||||||
|
|
||||||
property: "opacity"
|
|
||||||
from: 0
|
from: 0
|
||||||
|
property: "opacity"
|
||||||
to: 1
|
to: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
PauseAnimation {
|
PauseAnimation {
|
||||||
duration: pauseDuration * (1 - offset)
|
duration: pauseDuration * (1 - offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import "../"
|
import "../"
|
||||||
import "../../"
|
import "../../"
|
||||||
import QtMultimedia 5.15
|
import QtMultimedia 5.15
|
||||||
|
@ -15,27 +13,22 @@ Rectangle {
|
||||||
id: control
|
id: control
|
||||||
|
|
||||||
property alias desiredVolume: volumeSlider.desiredVolume
|
property alias desiredVolume: volumeSlider.desiredVolume
|
||||||
|
property var duration
|
||||||
|
property bool mediaLoaded: false
|
||||||
|
property var mediaState
|
||||||
property bool muted: false
|
property bool muted: false
|
||||||
property bool playingVideo: false
|
property bool playingVideo: false
|
||||||
property var mediaState
|
|
||||||
property bool mediaLoaded: false
|
|
||||||
property var duration
|
|
||||||
property var positionValue: 0
|
|
||||||
property var position
|
property var position
|
||||||
|
property var positionValue: 0
|
||||||
property bool shouldShowControls: !playingVideo || playerMouseArea.shouldShowControls || volumeSlider.state == "shown"
|
property bool shouldShowControls: !playingVideo || playerMouseArea.shouldShowControls || volumeSlider.state == "shown"
|
||||||
|
|
||||||
signal playPauseActivated()
|
signal loadActivated
|
||||||
signal loadActivated()
|
signal playPauseActivated
|
||||||
|
|
||||||
function showControls() {
|
|
||||||
controlHideTimer.restart();
|
|
||||||
}
|
|
||||||
|
|
||||||
function durationToString(duration) {
|
function durationToString(duration) {
|
||||||
function maybeZeroPrepend(time) {
|
function maybeZeroPrepend(time) {
|
||||||
return (time < 10) ? "0" + time.toString() : time.toString();
|
return (time < 10) ? "0" + time.toString() : time.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
var totalSeconds = Math.floor(duration / 1000);
|
var totalSeconds = Math.floor(duration / 1000);
|
||||||
var seconds = totalSeconds % 60;
|
var seconds = totalSeconds % 60;
|
||||||
var minutes = (Math.floor(totalSeconds / 60)) % 60;
|
var minutes = (Math.floor(totalSeconds / 60)) % 60;
|
||||||
|
@ -46,16 +39,25 @@ Rectangle {
|
||||||
var hh = hours.toString();
|
var hh = hours.toString();
|
||||||
if (hours < 1)
|
if (hours < 1)
|
||||||
return mm + ":" + ss;
|
return mm + ":" + ss;
|
||||||
|
|
||||||
return hh + ":" + mm + ":" + ss;
|
return hh + ":" + mm + ":" + ss;
|
||||||
}
|
}
|
||||||
|
function showControls() {
|
||||||
|
controlHideTimer.restart();
|
||||||
|
}
|
||||||
|
|
||||||
color: {
|
color: {
|
||||||
var wc = timelineRoot.palette.alternateBase;
|
var wc = timelineRoot.palette.alternateBase;
|
||||||
return Qt.rgba(wc.r, wc.g, wc.b, 0.5);
|
return Qt.rgba(wc.r, wc.g, wc.b, 0.5);
|
||||||
}
|
}
|
||||||
opacity: control.shouldShowControls ? 1 : 0
|
|
||||||
height: controlLayout.implicitHeight
|
height: controlLayout.implicitHeight
|
||||||
|
opacity: control.shouldShowControls ? 1 : 0
|
||||||
|
|
||||||
|
// Fade controls in/out
|
||||||
|
Behavior on opacity {
|
||||||
|
OpacityAnimator {
|
||||||
|
duration: 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: playerMouseArea
|
id: playerMouseArea
|
||||||
|
@ -64,41 +66,38 @@ Rectangle {
|
||||||
|
|
||||||
onHoveredChanged: showControls()
|
onHoveredChanged: showControls()
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: controlLayout
|
id: controlLayout
|
||||||
|
|
||||||
enabled: control.shouldShowControls
|
|
||||||
spacing: 0
|
|
||||||
anchors.bottom: control.bottom
|
anchors.bottom: control.bottom
|
||||||
anchors.left: control.left
|
anchors.left: control.left
|
||||||
anchors.right: control.right
|
anchors.right: control.right
|
||||||
|
enabled: control.shouldShowControls
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
NhekoSlider {
|
NhekoSlider {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.leftMargin: Nheko.paddingSmall
|
Layout.leftMargin: Nheko.paddingSmall
|
||||||
Layout.rightMargin: Nheko.paddingSmall
|
Layout.rightMargin: Nheko.paddingSmall
|
||||||
|
alwaysShowSlider: false
|
||||||
enabled: control.mediaLoaded
|
enabled: control.mediaLoaded
|
||||||
value: control.positionValue
|
|
||||||
onMoved: control.position = value
|
|
||||||
from: 0
|
from: 0
|
||||||
to: control.duration
|
to: control.duration
|
||||||
alwaysShowSlider: false
|
value: control.positionValue
|
||||||
}
|
|
||||||
|
|
||||||
|
onMoved: control.position = value
|
||||||
|
}
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
Layout.margins: Nheko.paddingSmall
|
Layout.margins: Nheko.paddingSmall
|
||||||
spacing: Nheko.paddingSmall
|
spacing: Nheko.paddingSmall
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
// Cache/Play/pause button
|
// Cache/Play/pause button
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: playbackStateImage
|
id: playbackStateImage
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
buttonTextColor: timelineRoot.palette.text
|
|
||||||
Layout.preferredHeight: 24
|
Layout.preferredHeight: 24
|
||||||
Layout.preferredWidth: 24
|
Layout.preferredWidth: 24
|
||||||
|
buttonTextColor: timelineRoot.palette.text
|
||||||
image: {
|
image: {
|
||||||
if (control.mediaLoaded) {
|
if (control.mediaLoaded) {
|
||||||
if (control.mediaState == MediaPlayer.PlayingState)
|
if (control.mediaState == MediaPlayer.PlayingState)
|
||||||
|
@ -109,38 +108,48 @@ Rectangle {
|
||||||
return ":/icons/icons/ui/download.svg";
|
return ":/icons/icons/ui/download.svg";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: control.mediaLoaded ? control.playPauseActivated() : control.loadActivated()
|
onClicked: control.mediaLoaded ? control.playPauseActivated() : control.loadActivated()
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: volumeButton
|
id: volumeButton
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
buttonTextColor: timelineRoot.palette.text
|
|
||||||
Layout.preferredHeight: 24
|
Layout.preferredHeight: 24
|
||||||
Layout.preferredWidth: 24
|
Layout.preferredWidth: 24
|
||||||
|
buttonTextColor: timelineRoot.palette.text
|
||||||
image: {
|
image: {
|
||||||
if (control.muted || control.desiredVolume <= 0)
|
if (control.muted || control.desiredVolume <= 0)
|
||||||
return ":/icons/icons/ui/volume-off-indicator.svg";
|
return ":/icons/icons/ui/volume-off-indicator.svg";
|
||||||
else
|
else
|
||||||
return ":/icons/icons/ui/volume-up.svg";
|
return ":/icons/icons/ui/volume-up.svg";
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: control.muted = !control.muted
|
onClicked: control.muted = !control.muted
|
||||||
}
|
}
|
||||||
|
|
||||||
NhekoSlider {
|
NhekoSlider {
|
||||||
id: volumeSlider
|
id: volumeSlider
|
||||||
|
|
||||||
property real desiredVolume: QtMultimedia.convertVolume(volumeSlider.value, QtMultimedia.LogarithmicVolumeScale, QtMultimedia.LinearVolumeScale)
|
property real desiredVolume: QtMultimedia.convertVolume(volumeSlider.value, QtMultimedia.LogarithmicVolumeScale, QtMultimedia.LinearVolumeScale)
|
||||||
|
|
||||||
state: ""
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
Layout.preferredWidth: 0
|
Layout.preferredWidth: 0
|
||||||
opacity: 0
|
opacity: 0
|
||||||
orientation: Qt.Horizontal
|
orientation: Qt.Horizontal
|
||||||
|
state: ""
|
||||||
value: 1
|
value: 1
|
||||||
onDesiredVolumeChanged: {
|
|
||||||
control.muted = !(desiredVolume > 0);
|
states: State {
|
||||||
|
name: "shown"
|
||||||
|
when: Settings.mobileMode || volumeButton.hovered || volumeSlider.hovered || volumeSlider.pressed
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
Layout.preferredWidth: 100
|
||||||
|
target: volumeSlider
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
opacity: 1
|
||||||
|
target: volumeSlider
|
||||||
|
}
|
||||||
}
|
}
|
||||||
transitions: [
|
transitions: [
|
||||||
Transition {
|
Transition {
|
||||||
|
@ -151,20 +160,16 @@ Rectangle {
|
||||||
PauseAnimation {
|
PauseAnimation {
|
||||||
duration: 50
|
duration: 50
|
||||||
}
|
}
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: 100
|
duration: 100
|
||||||
properties: "opacity"
|
|
||||||
easing.type: Easing.InQuad
|
easing.type: Easing.InQuad
|
||||||
|
properties: "opacity"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
properties: "Layout.preferredWidth"
|
|
||||||
duration: 150
|
duration: 150
|
||||||
|
properties: "Layout.preferredWidth"
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
Transition {
|
Transition {
|
||||||
from: "shown"
|
from: "shown"
|
||||||
|
@ -174,72 +179,40 @@ Rectangle {
|
||||||
PauseAnimation {
|
PauseAnimation {
|
||||||
duration: 100
|
duration: 100
|
||||||
}
|
}
|
||||||
|
|
||||||
ParallelAnimation {
|
ParallelAnimation {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: 100
|
duration: 100
|
||||||
properties: "opacity"
|
|
||||||
easing.type: Easing.InQuad
|
easing.type: Easing.InQuad
|
||||||
|
properties: "opacity"
|
||||||
}
|
}
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
properties: "Layout.preferredWidth"
|
|
||||||
duration: 150
|
duration: 150
|
||||||
|
properties: "Layout.preferredWidth"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
states: State {
|
onDesiredVolumeChanged: {
|
||||||
name: "shown"
|
control.muted = !(desiredVolume > 0);
|
||||||
when: Settings.mobileMode || volumeButton.hovered || volumeSlider.hovered || volumeSlider.pressed
|
|
||||||
|
|
||||||
PropertyChanges {
|
|
||||||
target: volumeSlider
|
|
||||||
Layout.preferredWidth: 100
|
|
||||||
}
|
|
||||||
|
|
||||||
PropertyChanges {
|
|
||||||
target: volumeSlider
|
|
||||||
opacity: 1
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
text: (!control.mediaLoaded ? "-- " : durationToString(control.positionValue)) + " / " + durationToString(control.duration)
|
|
||||||
color: timelineRoot.palette.text
|
color: timelineRoot.palette.text
|
||||||
|
text: (!control.mediaLoaded ? "-- " : durationToString(control.positionValue)) + " / " + durationToString(control.duration)
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// For hiding controls on stationary cursor
|
// For hiding controls on stationary cursor
|
||||||
Timer {
|
Timer {
|
||||||
id: controlHideTimer
|
id: controlHideTimer
|
||||||
|
|
||||||
interval: 1500 //ms
|
interval: 1500 //ms
|
||||||
repeat: false
|
repeat: false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fade controls in/out
|
|
||||||
Behavior on opacity {
|
|
||||||
OpacityAnimator {
|
|
||||||
duration: 100
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import "../"
|
import "../"
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
|
@ -10,51 +8,46 @@ import QtQuick.Layouts 1.2
|
||||||
import im.nheko
|
import im.nheko
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
visible: CallManager.isOnCall
|
|
||||||
color: callInviteBar.color
|
color: callInviteBar.color
|
||||||
implicitHeight: visible ? rowLayout.height + 8 : 0
|
implicitHeight: visible ? rowLayout.height + 8 : 0
|
||||||
|
visible: CallManager.isOnCall
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (CallManager.callType != Voip.VOICE)
|
if (CallManager.callType != Voip.VOICE)
|
||||||
stackLayout.currentIndex = stackLayout.currentIndex ? 0 : 1;
|
stackLayout.currentIndex = stackLayout.currentIndex ? 0 : 1;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: rowLayout
|
id: rowLayout
|
||||||
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 8
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
anchors.leftMargin: 8
|
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
width: Nheko.avatarSize
|
displayName: CallManager.callPartyDisplayName
|
||||||
height: Nheko.avatarSize
|
height: Nheko.avatarSize
|
||||||
url: CallManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/")
|
url: CallManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
userid: CallManager.callParty
|
userid: CallManager.callParty
|
||||||
displayName: CallManager.callPartyDisplayName
|
width: Nheko.avatarSize
|
||||||
|
|
||||||
onClicked: TimelineManager.openImageOverlay(room, room.avatarUrl(userid), room.data.eventId)
|
onClicked: TimelineManager.openImageOverlay(room, room.avatarUrl(userid), room.data.eventId)
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.leftMargin: 8
|
Layout.leftMargin: 8
|
||||||
|
color: "#000000"
|
||||||
font.pointSize: fontMetrics.font.pointSize * 1.1
|
font.pointSize: fontMetrics.font.pointSize * 1.1
|
||||||
text: CallManager.callPartyDisplayName
|
text: CallManager.callPartyDisplayName
|
||||||
color: "#000000"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: callTypeIcon
|
id: callTypeIcon
|
||||||
|
|
||||||
Layout.leftMargin: 4
|
Layout.leftMargin: 4
|
||||||
Layout.preferredWidth: 24
|
|
||||||
Layout.preferredHeight: 24
|
Layout.preferredHeight: 24
|
||||||
|
Layout.preferredWidth: 24
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
states: [
|
states: [
|
||||||
State {
|
State {
|
||||||
|
@ -62,41 +55,35 @@ Rectangle {
|
||||||
when: CallManager.callType == Voip.VOICE
|
when: CallManager.callType == Voip.VOICE
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: callTypeIcon
|
|
||||||
source: "qrc:/icons/icons/ui/place-call.svg"
|
source: "qrc:/icons/icons/ui/place-call.svg"
|
||||||
|
target: callTypeIcon
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "VIDEO"
|
name: "VIDEO"
|
||||||
when: CallManager.callType == Voip.VIDEO
|
when: CallManager.callType == Voip.VIDEO
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: callTypeIcon
|
|
||||||
source: "qrc:/icons/icons/ui/video.svg"
|
source: "qrc:/icons/icons/ui/video.svg"
|
||||||
|
target: callTypeIcon
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "SCREEN"
|
name: "SCREEN"
|
||||||
when: CallManager.callType == Voip.SCREEN
|
when: CallManager.callType == Voip.SCREEN
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: callTypeIcon
|
|
||||||
source: "qrc:/icons/icons/ui/screen-share.svg"
|
source: "qrc:/icons/icons/ui/screen-share.svg"
|
||||||
|
target: callTypeIcon
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: callStateLabel
|
id: callStateLabel
|
||||||
|
|
||||||
font.pointSize: fontMetrics.font.pointSize * 1.1
|
|
||||||
color: "#000000"
|
color: "#000000"
|
||||||
|
font.pointSize: fontMetrics.font.pointSize * 1.1
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
states: [
|
states: [
|
||||||
State {
|
State {
|
||||||
|
@ -107,7 +94,6 @@ Rectangle {
|
||||||
target: callStateLabel
|
target: callStateLabel
|
||||||
text: qsTr("Calling...")
|
text: qsTr("Calling...")
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "CONNECTING"
|
name: "CONNECTING"
|
||||||
|
@ -117,7 +103,6 @@ Rectangle {
|
||||||
target: callStateLabel
|
target: callStateLabel
|
||||||
text: qsTr("Connecting...")
|
text: qsTr("Connecting...")
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "ANSWERSENT"
|
name: "ANSWERSENT"
|
||||||
|
@ -127,7 +112,6 @@ Rectangle {
|
||||||
target: callStateLabel
|
target: callStateLabel
|
||||||
text: qsTr("Connecting...")
|
text: qsTr("Connecting...")
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "CONNECTED"
|
name: "CONNECTED"
|
||||||
|
@ -137,17 +121,14 @@ Rectangle {
|
||||||
target: callStateLabel
|
target: callStateLabel
|
||||||
text: "00:00"
|
text: "00:00"
|
||||||
}
|
}
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: callTimer
|
|
||||||
startTime: Math.floor((new Date()).getTime() / 1000)
|
startTime: Math.floor((new Date()).getTime() / 1000)
|
||||||
|
target: callTimer
|
||||||
}
|
}
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: stackLayout
|
|
||||||
currentIndex: CallManager.callType != Voip.VOICE ? 1 : 0
|
currentIndex: CallManager.callType != Voip.VOICE ? 1 : 0
|
||||||
|
target: stackLayout
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "DISCONNECTED"
|
name: "DISCONNECTED"
|
||||||
|
@ -157,16 +138,13 @@ Rectangle {
|
||||||
target: callStateLabel
|
target: callStateLabel
|
||||||
text: ""
|
text: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: stackLayout
|
|
||||||
currentIndex: 0
|
currentIndex: 0
|
||||||
|
target: stackLayout
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: callTimer
|
id: callTimer
|
||||||
|
|
||||||
|
@ -177,8 +155,9 @@ Rectangle {
|
||||||
}
|
}
|
||||||
|
|
||||||
interval: 1000
|
interval: 1000
|
||||||
running: CallManager.callState == Voip.CONNECTED
|
|
||||||
repeat: true
|
repeat: true
|
||||||
|
running: CallManager.callState == Voip.CONNECTED
|
||||||
|
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
var d = new Date();
|
var d = new Date();
|
||||||
let seconds = Math.floor(d.getTime() / 1000 - startTime);
|
let seconds = Math.floor(d.getTime() / 1000 - startTime);
|
||||||
|
@ -188,44 +167,40 @@ Rectangle {
|
||||||
callStateLabel.text = (h ? (pad(h) + ":") : "") + pad(m) + ":" + pad(s);
|
callStateLabel.text = (h ? (pad(h) + ":") : "") + pad(m) + ":" + pad(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
visible: CallManager.callType == Voip.SCREEN && CallManager.callState == Voip.CONNECTED
|
|
||||||
text: qsTr("You are screen sharing")
|
|
||||||
font.pointSize: fontMetrics.font.pointSize * 1.1
|
|
||||||
color: "#000000"
|
color: "#000000"
|
||||||
|
font.pointSize: fontMetrics.font.pointSize * 1.1
|
||||||
|
text: qsTr("You are screen sharing")
|
||||||
|
visible: CallManager.callType == Voip.SCREEN && CallManager.callState == Voip.CONNECTED
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
|
ToolTip.text: qsTr("Hide/Show Picture-in-Picture")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
buttonTextColor: "#000000"
|
||||||
|
height: 24
|
||||||
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/picture-in-picture.svg"
|
||||||
visible: CallManager.haveLocalPiP
|
visible: CallManager.haveLocalPiP
|
||||||
width: 24
|
width: 24
|
||||||
height: 24
|
|
||||||
buttonTextColor: "#000000"
|
|
||||||
image: ":/icons/icons/ui/picture-in-picture.svg"
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Hide/Show Picture-in-Picture")
|
|
||||||
onClicked: CallManager.toggleLocalPiP()
|
onClicked: CallManager.toggleLocalPiP()
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
Layout.leftMargin: 8
|
Layout.leftMargin: 8
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
width: 24
|
|
||||||
height: 24
|
|
||||||
buttonTextColor: "#000000"
|
|
||||||
image: CallManager.isMicMuted ? ":/icons/icons/ui/microphone-unmute.svg" : ":/icons/icons/ui/microphone-mute.svg"
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: CallManager.isMicMuted ? qsTr("Unmute Mic") : qsTr("Mute Mic")
|
ToolTip.text: CallManager.isMicMuted ? qsTr("Unmute Mic") : qsTr("Mute Mic")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
buttonTextColor: "#000000"
|
||||||
|
height: 24
|
||||||
|
hoverEnabled: true
|
||||||
|
image: CallManager.isMicMuted ? ":/icons/icons/ui/microphone-unmute.svg" : ":/icons/icons/ui/microphone-mute.svg"
|
||||||
|
width: 24
|
||||||
|
|
||||||
onClicked: CallManager.toggleMicMute()
|
onClicked: CallManager.toggleMicMute()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
|
@ -11,79 +9,68 @@ import im.nheko
|
||||||
Popup {
|
Popup {
|
||||||
modal: true
|
modal: true
|
||||||
palette: timelineRoot.palette
|
palette: timelineRoot.palette
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
border.color: timelineRoot.palette.windowText
|
||||||
|
color: timelineRoot.palette.window
|
||||||
|
}
|
||||||
|
|
||||||
// only set the anchors on Qt 5.12 or higher
|
// only set the anchors on Qt 5.12 or higher
|
||||||
// see https://doc.qt.io/qt-5/qml-qtquick-controls2-popup.html#anchors.centerIn-prop
|
// see https://doc.qt.io/qt-5/qml-qtquick-controls2-popup.html#anchors.centerIn-prop
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (anchors)
|
if (anchors)
|
||||||
anchors.centerIn = parent;
|
anchors.centerIn = parent;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
spacing: 16
|
spacing: 16
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
spacing: 8
|
|
||||||
Layout.topMargin: 8
|
|
||||||
Layout.leftMargin: 8
|
Layout.leftMargin: 8
|
||||||
Layout.rightMargin: 8
|
Layout.rightMargin: 8
|
||||||
|
Layout.topMargin: 8
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Image {
|
Image {
|
||||||
Layout.preferredWidth: 22
|
|
||||||
Layout.preferredHeight: 22
|
Layout.preferredHeight: 22
|
||||||
|
Layout.preferredWidth: 22
|
||||||
source: "image://colorimage/:/icons/icons/ui/microphone-unmute.svg?" + timelineRoot.palette.windowText
|
source: "image://colorimage/:/icons/icons/ui/microphone-unmute.svg?" + timelineRoot.palette.windowText
|
||||||
}
|
}
|
||||||
|
|
||||||
ComboBox {
|
ComboBox {
|
||||||
id: micCombo
|
id: micCombo
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
model: CallManager.mics
|
model: CallManager.mics
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
visible: CallManager.callType == Voip.VIDEO && CallManager.cameras.length > 0
|
visible: CallManager.callType == Voip.VIDEO && CallManager.cameras.length > 0
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
Layout.preferredWidth: 22
|
|
||||||
Layout.preferredHeight: 22
|
Layout.preferredHeight: 22
|
||||||
|
Layout.preferredWidth: 22
|
||||||
source: "image://colorimage/:/icons/icons/ui/video-call.svg?" + timelineRoot.palette.windowText
|
source: "image://colorimage/:/icons/icons/ui/video-call.svg?" + timelineRoot.palette.windowText
|
||||||
}
|
}
|
||||||
|
|
||||||
ComboBox {
|
ComboBox {
|
||||||
id: cameraCombo
|
id: cameraCombo
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
model: CallManager.cameras
|
model: CallManager.cameras
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DialogButtonBox {
|
DialogButtonBox {
|
||||||
Layout.leftMargin: 128
|
Layout.leftMargin: 128
|
||||||
standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
|
standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
|
||||||
|
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
Settings.microphone = micCombo.currentText;
|
Settings.microphone = micCombo.currentText;
|
||||||
if (cameraCombo.visible)
|
if (cameraCombo.visible)
|
||||||
Settings.camera = cameraCombo.currentText;
|
Settings.camera = cameraCombo.currentText;
|
||||||
|
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
onRejected: {
|
onRejected: {
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: timelineRoot.palette.window
|
|
||||||
border.color: timelineRoot.palette.windowText
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import "../"
|
import "../"
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
|
@ -11,54 +9,51 @@ import im.nheko
|
||||||
|
|
||||||
Popup {
|
Popup {
|
||||||
id: callInv
|
id: callInv
|
||||||
|
|
||||||
closePolicy: Popup.NoAutoClose
|
closePolicy: Popup.NoAutoClose
|
||||||
width: parent.width
|
|
||||||
height: parent.height
|
height: parent.height
|
||||||
palette: timelineRoot.palette
|
palette: timelineRoot.palette
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
border.color: timelineRoot.palette.windowText
|
||||||
|
color: timelineRoot.palette.window
|
||||||
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: deviceError
|
id: deviceError
|
||||||
|
|
||||||
DeviceError {
|
DeviceError {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onNewInviteState() {
|
function onNewInviteState() {
|
||||||
if (!CallManager.haveCallInvite)
|
if (!CallManager.haveCallInvite)
|
||||||
close();
|
close();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
target: CallManager
|
target: CallManager
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.top: parent.top
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.alignment: Qt.AlignCenter
|
Layout.alignment: Qt.AlignCenter
|
||||||
Layout.topMargin: callInv.parent.height / 25
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: CallManager.callPartyDisplayName
|
Layout.topMargin: callInv.parent.height / 25
|
||||||
font.pointSize: fontMetrics.font.pointSize * 2
|
|
||||||
color: timelineRoot.palette.windowText
|
color: timelineRoot.palette.windowText
|
||||||
|
font.pointSize: fontMetrics.font.pointSize * 2
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: CallManager.callPartyDisplayName
|
||||||
}
|
}
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
Layout.alignment: Qt.AlignCenter
|
Layout.alignment: Qt.AlignCenter
|
||||||
Layout.preferredHeight: callInv.height / 5
|
Layout.preferredHeight: callInv.height / 5
|
||||||
Layout.preferredWidth: callInv.height / 5
|
Layout.preferredWidth: callInv.height / 5
|
||||||
|
displayName: CallManager.callPartyDisplayName
|
||||||
url: CallManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/")
|
url: CallManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
userid: CallManager.callParty
|
userid: CallManager.callParty
|
||||||
displayName: CallManager.callPartyDisplayName
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
Layout.alignment: Qt.AlignCenter
|
Layout.alignment: Qt.AlignCenter
|
||||||
Layout.bottomMargin: callInv.height / 25
|
Layout.bottomMargin: callInv.height / 25
|
||||||
|
@ -67,20 +62,17 @@ Popup {
|
||||||
property string image: CallManager.callType == Voip.VIDEO ? ":/icons/icons/ui/video.svg" : ":/icons/icons/ui/place-call.svg"
|
property string image: CallManager.callType == Voip.VIDEO ? ":/icons/icons/ui/video.svg" : ":/icons/icons/ui/place-call.svg"
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignCenter
|
Layout.alignment: Qt.AlignCenter
|
||||||
Layout.preferredWidth: callInv.height / 10
|
|
||||||
Layout.preferredHeight: callInv.height / 10
|
Layout.preferredHeight: callInv.height / 10
|
||||||
|
Layout.preferredWidth: callInv.height / 10
|
||||||
source: "image://colorimage/" + image + "?" + timelineRoot.palette.windowText
|
source: "image://colorimage/" + image + "?" + timelineRoot.palette.windowText
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.alignment: Qt.AlignCenter
|
Layout.alignment: Qt.AlignCenter
|
||||||
text: CallManager.callType == Voip.VIDEO ? qsTr("Video Call") : qsTr("Voice Call")
|
|
||||||
font.pointSize: fontMetrics.font.pointSize * 2
|
|
||||||
color: timelineRoot.palette.windowText
|
color: timelineRoot.palette.windowText
|
||||||
|
font.pointSize: fontMetrics.font.pointSize * 2
|
||||||
|
text: CallManager.callType == Voip.VIDEO ? qsTr("Video Call") : qsTr("Voice Call")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: deviceCombos
|
id: deviceCombos
|
||||||
|
|
||||||
|
@ -93,41 +85,32 @@ Popup {
|
||||||
Layout.alignment: Qt.AlignCenter
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
Layout.preferredWidth: deviceCombos.imageSize
|
|
||||||
Layout.preferredHeight: deviceCombos.imageSize
|
Layout.preferredHeight: deviceCombos.imageSize
|
||||||
|
Layout.preferredWidth: deviceCombos.imageSize
|
||||||
source: "image://colorimage/:/icons/icons/ui/microphone-unmute.svg?" + timelineRoot.palette.windowText
|
source: "image://colorimage/:/icons/icons/ui/microphone-unmute.svg?" + timelineRoot.palette.windowText
|
||||||
}
|
}
|
||||||
|
|
||||||
ComboBox {
|
ComboBox {
|
||||||
id: micCombo
|
id: micCombo
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
model: CallManager.mics
|
model: CallManager.mics
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
visible: CallManager.callType == Voip.VIDEO && CallManager.cameras.length > 0
|
|
||||||
Layout.alignment: Qt.AlignCenter
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
visible: CallManager.callType == Voip.VIDEO && CallManager.cameras.length > 0
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
Layout.preferredWidth: deviceCombos.imageSize
|
|
||||||
Layout.preferredHeight: deviceCombos.imageSize
|
Layout.preferredHeight: deviceCombos.imageSize
|
||||||
|
Layout.preferredWidth: deviceCombos.imageSize
|
||||||
source: "image://colorimage/:/icons/icons/ui/video.svg?" + timelineRoot.palette.windowText
|
source: "image://colorimage/:/icons/icons/ui/video.svg?" + timelineRoot.palette.windowText
|
||||||
}
|
}
|
||||||
|
|
||||||
ComboBox {
|
ComboBox {
|
||||||
id: cameraCombo
|
id: cameraCombo
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
model: CallManager.cameras
|
model: CallManager.cameras
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: buttonLayout
|
id: buttonLayout
|
||||||
|
|
||||||
|
@ -136,9 +119,9 @@ Popup {
|
||||||
function validateMic() {
|
function validateMic() {
|
||||||
if (CallManager.mics.length == 0) {
|
if (CallManager.mics.length == 0) {
|
||||||
var dialog = deviceError.createObject(timelineRoot, {
|
var dialog = deviceError.createObject(timelineRoot, {
|
||||||
"errorString": qsTr("No microphone found."),
|
"errorString": qsTr("No microphone found."),
|
||||||
"image": ":/icons/icons/ui/place-call.svg"
|
"image": ":/icons/icons/ui/place-call.svg"
|
||||||
});
|
});
|
||||||
dialog.open();
|
dialog.open();
|
||||||
timelineRoot.destroyOnClose(dialog);
|
timelineRoot.destroyOnClose(dialog);
|
||||||
return false;
|
return false;
|
||||||
|
@ -150,60 +133,48 @@ Popup {
|
||||||
spacing: callInv.height / 6
|
spacing: callInv.height / 6
|
||||||
|
|
||||||
RoundButton {
|
RoundButton {
|
||||||
implicitWidth: buttonLayout.buttonSize
|
|
||||||
implicitHeight: buttonLayout.buttonSize
|
implicitHeight: buttonLayout.buttonSize
|
||||||
onClicked: {
|
implicitWidth: buttonLayout.buttonSize
|
||||||
CallManager.hangUp();
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
radius: buttonLayout.buttonSize / 2
|
|
||||||
color: "#ff0000"
|
color: "#ff0000"
|
||||||
|
radius: buttonLayout.buttonSize / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
contentItem: Image {
|
contentItem: Image {
|
||||||
source: "image://colorimage/:/icons/icons/ui/end-call.svg?#ffffff"
|
source: "image://colorimage/:/icons/icons/ui/end-call.svg?#ffffff"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
CallManager.hangUp();
|
||||||
|
close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RoundButton {
|
RoundButton {
|
||||||
id: acceptButton
|
id: acceptButton
|
||||||
|
|
||||||
property string image: CallManager.callType == Voip.VIDEO ? ":/icons/icons/ui/video.svg" : ":/icons/icons/ui/place-call.svg"
|
property string image: CallManager.callType == Voip.VIDEO ? ":/icons/icons/ui/video.svg" : ":/icons/icons/ui/place-call.svg"
|
||||||
|
|
||||||
implicitWidth: buttonLayout.buttonSize
|
|
||||||
implicitHeight: buttonLayout.buttonSize
|
implicitHeight: buttonLayout.buttonSize
|
||||||
|
implicitWidth: buttonLayout.buttonSize
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: "#00ff00"
|
||||||
|
radius: buttonLayout.buttonSize / 2
|
||||||
|
}
|
||||||
|
contentItem: Image {
|
||||||
|
source: "image://colorimage/" + acceptButton.image + "?#ffffff"
|
||||||
|
}
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (buttonLayout.validateMic()) {
|
if (buttonLayout.validateMic()) {
|
||||||
Settings.microphone = micCombo.currentText;
|
Settings.microphone = micCombo.currentText;
|
||||||
if (cameraCombo.visible)
|
if (cameraCombo.visible)
|
||||||
Settings.camera = cameraCombo.currentText;
|
Settings.camera = cameraCombo.currentText;
|
||||||
|
|
||||||
CallManager.acceptInvite();
|
CallManager.acceptInvite();
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
radius: buttonLayout.buttonSize / 2
|
|
||||||
color: "#00ff00"
|
|
||||||
}
|
|
||||||
|
|
||||||
contentItem: Image {
|
|
||||||
source: "image://colorimage/" + acceptButton.image + "?#ffffff"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: timelineRoot.palette.window
|
|
||||||
border.color: timelineRoot.palette.windowText
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import "../"
|
import "../"
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
|
@ -10,129 +8,117 @@ import QtQuick.Layouts 1.2
|
||||||
import im.nheko
|
import im.nheko
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
visible: CallManager.haveCallInvite && !Settings.mobileMode
|
|
||||||
color: "#2ECC71"
|
color: "#2ECC71"
|
||||||
implicitHeight: visible ? rowLayout.height + 8 : 0
|
implicitHeight: visible ? rowLayout.height + 8 : 0
|
||||||
|
visible: CallManager.haveCallInvite && !Settings.mobileMode
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: devicesDialog
|
id: devicesDialog
|
||||||
|
|
||||||
CallDevices {
|
CallDevices {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: deviceError
|
id: deviceError
|
||||||
|
|
||||||
DeviceError {
|
DeviceError {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: rowLayout
|
id: rowLayout
|
||||||
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 8
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
anchors.leftMargin: 8
|
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
width: Nheko.avatarSize
|
displayName: CallManager.callPartyDisplayName
|
||||||
height: Nheko.avatarSize
|
height: Nheko.avatarSize
|
||||||
url: CallManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/")
|
url: CallManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
userid: CallManager.callParty
|
userid: CallManager.callParty
|
||||||
displayName: CallManager.callPartyDisplayName
|
width: Nheko.avatarSize
|
||||||
|
|
||||||
onClicked: TimelineManager.openImageOverlay(room, room.avatarUrl(userid), room.data.eventId)
|
onClicked: TimelineManager.openImageOverlay(room, room.avatarUrl(userid), room.data.eventId)
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.leftMargin: 8
|
Layout.leftMargin: 8
|
||||||
|
color: "#000000"
|
||||||
font.pointSize: fontMetrics.font.pointSize * 1.1
|
font.pointSize: fontMetrics.font.pointSize * 1.1
|
||||||
text: CallManager.callPartyDisplayName
|
text: CallManager.callPartyDisplayName
|
||||||
color: "#000000"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
Layout.leftMargin: 4
|
Layout.leftMargin: 4
|
||||||
Layout.preferredWidth: 24
|
|
||||||
Layout.preferredHeight: 24
|
Layout.preferredHeight: 24
|
||||||
|
Layout.preferredWidth: 24
|
||||||
source: CallManager.callType == Voip.CallType.VIDEO ? "qrc:/icons/icons/ui/video.svg" : "qrc:/icons/icons/ui/place-call.svg"
|
source: CallManager.callType == Voip.CallType.VIDEO ? "qrc:/icons/icons/ui/video.svg" : "qrc:/icons/icons/ui/place-call.svg"
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
color: "#000000"
|
||||||
font.pointSize: fontMetrics.font.pointSize * 1.1
|
font.pointSize: fontMetrics.font.pointSize * 1.1
|
||||||
text: CallManager.callType == Voip.VIDEO ? qsTr("Video Call") : qsTr("Voice Call")
|
text: CallManager.callType == Voip.VIDEO ? qsTr("Video Call") : qsTr("Voice Call")
|
||||||
color: "#000000"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
width: 20
|
|
||||||
height: 20
|
|
||||||
buttonTextColor: "#000000"
|
|
||||||
image: ":/icons/icons/ui/settings.svg"
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Devices")
|
ToolTip.text: qsTr("Devices")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
buttonTextColor: "#000000"
|
||||||
|
height: 20
|
||||||
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/settings.svg"
|
||||||
|
width: 20
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
var dialog = devicesDialog.createObject(timelineRoot);
|
var dialog = devicesDialog.createObject(timelineRoot);
|
||||||
dialog.open();
|
dialog.open();
|
||||||
timelineRoot.destroyOnClose(dialog);
|
timelineRoot.destroyOnClose(dialog);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
Layout.rightMargin: 4
|
Layout.rightMargin: 4
|
||||||
icon.source: CallManager.callType == Voip.VIDEO ? "qrc:/icons/icons/ui/video.svg" : "qrc:/icons/icons/ui/place-call.svg"
|
icon.source: CallManager.callType == Voip.VIDEO ? "qrc:/icons/icons/ui/video.svg" : "qrc:/icons/icons/ui/place-call.svg"
|
||||||
text: qsTr("Accept")
|
|
||||||
palette: timelineRoot.palette
|
palette: timelineRoot.palette
|
||||||
|
text: qsTr("Accept")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (CallManager.mics.length == 0) {
|
if (CallManager.mics.length == 0) {
|
||||||
var dialog = deviceError.createObject(timelineRoot, {
|
var dialog = deviceError.createObject(timelineRoot, {
|
||||||
"errorString": qsTr("No microphone found."),
|
"errorString": qsTr("No microphone found."),
|
||||||
"image": ":/icons/icons/ui/place-call.svg"
|
"image": ":/icons/icons/ui/place-call.svg"
|
||||||
});
|
});
|
||||||
dialog.open();
|
dialog.open();
|
||||||
timelineRoot.destroyOnClose(dialog);
|
timelineRoot.destroyOnClose(dialog);
|
||||||
return ;
|
return;
|
||||||
} else if (!CallManager.mics.includes(Settings.microphone)) {
|
} else if (!CallManager.mics.includes(Settings.microphone)) {
|
||||||
var dialog = deviceError.createObject(timelineRoot, {
|
var dialog = deviceError.createObject(timelineRoot, {
|
||||||
"errorString": qsTr("Unknown microphone: %1").arg(Settings.microphone),
|
"errorString": qsTr("Unknown microphone: %1").arg(Settings.microphone),
|
||||||
"image": ":/icons/icons/ui/place-call.svg"
|
"image": ":/icons/icons/ui/place-call.svg"
|
||||||
});
|
});
|
||||||
dialog.open();
|
dialog.open();
|
||||||
timelineRoot.destroyOnClose(dialog);
|
timelineRoot.destroyOnClose(dialog);
|
||||||
return ;
|
return;
|
||||||
}
|
}
|
||||||
if (CallManager.callType == Voip.VIDEO && CallManager.cameras.length > 0 && !CallManager.cameras.includes(Settings.camera)) {
|
if (CallManager.callType == Voip.VIDEO && CallManager.cameras.length > 0 && !CallManager.cameras.includes(Settings.camera)) {
|
||||||
var dialog = deviceError.createObject(timelineRoot, {
|
var dialog = deviceError.createObject(timelineRoot, {
|
||||||
"errorString": qsTr("Unknown camera: %1").arg(Settings.camera),
|
"errorString": qsTr("Unknown camera: %1").arg(Settings.camera),
|
||||||
"image": ":/icons/icons/ui/video.svg"
|
"image": ":/icons/icons/ui/video.svg"
|
||||||
});
|
});
|
||||||
dialog.open();
|
dialog.open();
|
||||||
timelineRoot.destroyOnClose(dialog);
|
timelineRoot.destroyOnClose(dialog);
|
||||||
return ;
|
return;
|
||||||
}
|
}
|
||||||
CallManager.acceptInvite();
|
CallManager.acceptInvite();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
icon.source: "qrc:/icons/icons/ui/end-call.svg"
|
icon.source: "qrc:/icons/icons/ui/end-call.svg"
|
||||||
text: qsTr("Decline")
|
|
||||||
palette: timelineRoot.palette
|
palette: timelineRoot.palette
|
||||||
|
text: qsTr("Decline")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
CallManager.hangUp();
|
CallManager.hangUp();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
|
@ -13,31 +11,28 @@ Popup {
|
||||||
property var image
|
property var image
|
||||||
|
|
||||||
modal: true
|
modal: true
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
border.color: timelineRoot.palette.windowText
|
||||||
|
color: timelineRoot.palette.window
|
||||||
|
}
|
||||||
|
|
||||||
// only set the anchors on Qt 5.12 or higher
|
// only set the anchors on Qt 5.12 or higher
|
||||||
// see https://doc.qt.io/qt-5/qml-qtquick-controls2-popup.html#anchors.centerIn-prop
|
// see https://doc.qt.io/qt-5/qml-qtquick-controls2-popup.html#anchors.centerIn-prop
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (anchors)
|
if (anchors)
|
||||||
anchors.centerIn = parent;
|
anchors.centerIn = parent;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Image {
|
Image {
|
||||||
Layout.preferredWidth: 16
|
|
||||||
Layout.preferredHeight: 16
|
Layout.preferredHeight: 16
|
||||||
|
Layout.preferredWidth: 16
|
||||||
source: "image://colorimage/" + image + "?" + timelineRoot.palette.windowText
|
source: "image://colorimage/" + image + "?" + timelineRoot.palette.windowText
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: errorString
|
|
||||||
color: timelineRoot.palette.windowText
|
color: timelineRoot.palette.windowText
|
||||||
|
text: errorString
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: timelineRoot.palette.window
|
|
||||||
border.color: timelineRoot.palette.windowText
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import "../"
|
import "../"
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
|
@ -11,52 +9,49 @@ import im.nheko
|
||||||
|
|
||||||
Popup {
|
Popup {
|
||||||
modal: true
|
modal: true
|
||||||
|
palette: timelineRoot.palette
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
border.color: timelineRoot.palette.windowText
|
||||||
|
color: timelineRoot.palette.window
|
||||||
|
}
|
||||||
|
|
||||||
// only set the anchors on Qt 5.12 or higher
|
// only set the anchors on Qt 5.12 or higher
|
||||||
// see https://doc.qt.io/qt-5/qml-qtquick-controls2-popup.html#anchors.centerIn-prop
|
// see https://doc.qt.io/qt-5/qml-qtquick-controls2-popup.html#anchors.centerIn-prop
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (anchors)
|
if (anchors)
|
||||||
anchors.centerIn = parent;
|
anchors.centerIn = parent;
|
||||||
|
|
||||||
}
|
}
|
||||||
palette: timelineRoot.palette
|
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: deviceError
|
id: deviceError
|
||||||
|
|
||||||
DeviceError {
|
DeviceError {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: columnLayout
|
id: columnLayout
|
||||||
|
|
||||||
spacing: 16
|
spacing: 16
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.topMargin: 8
|
|
||||||
Layout.leftMargin: 8
|
Layout.leftMargin: 8
|
||||||
|
Layout.topMargin: 8
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("Place a call to %1?").arg(room.roomName)
|
|
||||||
color: timelineRoot.palette.windowText
|
color: timelineRoot.palette.windowText
|
||||||
|
text: qsTr("Place a call to %1?").arg(room.roomName)
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: buttonLayout
|
id: buttonLayout
|
||||||
|
|
||||||
function validateMic() {
|
function validateMic() {
|
||||||
if (CallManager.mics.length == 0) {
|
if (CallManager.mics.length == 0) {
|
||||||
var dialog = deviceError.createObject(timelineRoot, {
|
var dialog = deviceError.createObject(timelineRoot, {
|
||||||
"errorString": qsTr("No microphone found."),
|
"errorString": qsTr("No microphone found."),
|
||||||
"image": ":/icons/icons/ui/place-call.svg"
|
"image": ":/icons/icons/ui/place-call.svg"
|
||||||
});
|
});
|
||||||
dialog.open();
|
dialog.open();
|
||||||
timelineRoot.destroyOnClose(dialog);
|
timelineRoot.destroyOnClose(dialog);
|
||||||
return false;
|
return false;
|
||||||
|
@ -69,17 +64,18 @@ Popup {
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
Layout.rightMargin: cameraCombo.visible ? 16 : 64
|
Layout.rightMargin: cameraCombo.visible ? 16 : 64
|
||||||
width: Nheko.avatarSize
|
|
||||||
height: Nheko.avatarSize
|
|
||||||
url: room.roomAvatarUrl.replace("mxc://", "image://MxcImage/")
|
|
||||||
displayName: room.roomName
|
displayName: room.roomName
|
||||||
|
height: Nheko.avatarSize
|
||||||
roomid: room.roomId
|
roomid: room.roomId
|
||||||
|
url: room.roomAvatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
|
width: Nheko.avatarSize
|
||||||
|
|
||||||
onClicked: TimelineManager.openImageOverlay(room, room.avatarUrl(userid), room.data.eventId)
|
onClicked: TimelineManager.openImageOverlay(room, room.avatarUrl(userid), room.data.eventId)
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: qsTr("Voice")
|
|
||||||
icon.source: "qrc:/icons/icons/ui/place-call.svg"
|
icon.source: "qrc:/icons/icons/ui/place-call.svg"
|
||||||
|
text: qsTr("Voice")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (buttonLayout.validateMic()) {
|
if (buttonLayout.validateMic()) {
|
||||||
Settings.microphone = micCombo.currentText;
|
Settings.microphone = micCombo.currentText;
|
||||||
|
@ -88,11 +84,11 @@ Popup {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
visible: CallManager.cameras.length > 0
|
|
||||||
text: qsTr("Video")
|
|
||||||
icon.source: "qrc:/icons/icons/ui/video.svg"
|
icon.source: "qrc:/icons/icons/ui/video.svg"
|
||||||
|
text: qsTr("Video")
|
||||||
|
visible: CallManager.cameras.length > 0
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (buttonLayout.validateMic()) {
|
if (buttonLayout.validateMic()) {
|
||||||
Settings.microphone = micCombo.currentText;
|
Settings.microphone = micCombo.currentText;
|
||||||
|
@ -102,16 +98,15 @@ Popup {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
visible: CallManager.screenShareSupported
|
|
||||||
text: qsTr("Screen")
|
|
||||||
icon.source: "qrc:/icons/icons/ui/screen-share.svg"
|
icon.source: "qrc:/icons/icons/ui/screen-share.svg"
|
||||||
|
text: qsTr("Screen")
|
||||||
|
visible: CallManager.screenShareSupported
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (buttonLayout.validateMic()) {
|
if (buttonLayout.validateMic()) {
|
||||||
Settings.microphone = micCombo.currentText;
|
Settings.microphone = micCombo.currentText;
|
||||||
Settings.camera = cameraCombo.currentText;
|
Settings.camera = cameraCombo.currentText;
|
||||||
|
|
||||||
var dialog = screenShareDialog.createObject(timelineRoot);
|
var dialog = screenShareDialog.createObject(timelineRoot);
|
||||||
dialog.open();
|
dialog.open();
|
||||||
timelineRoot.destroyOnClose(dialog);
|
timelineRoot.destroyOnClose(dialog);
|
||||||
|
@ -119,67 +114,50 @@ Popup {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: qsTr("Cancel")
|
text: qsTr("Cancel")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
spacing: 8
|
spacing: 8
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
Layout.bottomMargin: cameraCombo.visible ? 0 : 8
|
||||||
Layout.leftMargin: 8
|
Layout.leftMargin: 8
|
||||||
Layout.rightMargin: 8
|
Layout.rightMargin: 8
|
||||||
Layout.bottomMargin: cameraCombo.visible ? 0 : 8
|
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
Layout.preferredWidth: 22
|
|
||||||
Layout.preferredHeight: 22
|
Layout.preferredHeight: 22
|
||||||
|
Layout.preferredWidth: 22
|
||||||
source: "image://colorimage/:/icons/icons/ui/microphone-unmute.svg?" + timelineRoot.palette.windowText
|
source: "image://colorimage/:/icons/icons/ui/microphone-unmute.svg?" + timelineRoot.palette.windowText
|
||||||
}
|
}
|
||||||
|
|
||||||
ComboBox {
|
ComboBox {
|
||||||
id: micCombo
|
id: micCombo
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
model: CallManager.mics
|
model: CallManager.mics
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
visible: CallManager.cameras.length > 0
|
Layout.bottomMargin: 8
|
||||||
Layout.leftMargin: 8
|
Layout.leftMargin: 8
|
||||||
Layout.rightMargin: 8
|
Layout.rightMargin: 8
|
||||||
Layout.bottomMargin: 8
|
visible: CallManager.cameras.length > 0
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
Layout.preferredWidth: 22
|
|
||||||
Layout.preferredHeight: 22
|
Layout.preferredHeight: 22
|
||||||
|
Layout.preferredWidth: 22
|
||||||
source: "image://colorimage/:/icons/icons/ui/video.svg?" + timelineRoot.palette.windowText
|
source: "image://colorimage/:/icons/icons/ui/video.svg?" + timelineRoot.palette.windowText
|
||||||
}
|
}
|
||||||
|
|
||||||
ComboBox {
|
ComboBox {
|
||||||
id: cameraCombo
|
id: cameraCombo
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
model: CallManager.cameras
|
model: CallManager.cameras
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: timelineRoot.palette.window
|
|
||||||
border.color: timelineRoot.palette.windowText
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import "../"
|
import "../"
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
|
@ -10,154 +8,129 @@ import QtQuick.Layouts 1.2
|
||||||
import im.nheko
|
import im.nheko
|
||||||
|
|
||||||
Popup {
|
Popup {
|
||||||
|
anchors.centerIn: parent
|
||||||
modal: true
|
modal: true
|
||||||
|
palette: timelineRoot.palette
|
||||||
|
|
||||||
anchors.centerIn: parent;
|
background: Rectangle {
|
||||||
|
border.color: timelineRoot.palette.windowText
|
||||||
|
color: timelineRoot.palette.window
|
||||||
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
frameRateCombo.currentIndex = frameRateCombo.find(Settings.screenShareFrameRate);
|
frameRateCombo.currentIndex = frameRateCombo.find(Settings.screenShareFrameRate);
|
||||||
}
|
}
|
||||||
palette: timelineRoot.palette
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
Label {
|
Label {
|
||||||
Layout.topMargin: 16
|
Layout.alignment: Qt.AlignLeft
|
||||||
Layout.bottomMargin: 16
|
Layout.bottomMargin: 16
|
||||||
Layout.leftMargin: 8
|
Layout.leftMargin: 8
|
||||||
Layout.rightMargin: 8
|
Layout.rightMargin: 8
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.topMargin: 16
|
||||||
text: qsTr("Share desktop with %1?").arg(room.roomName)
|
|
||||||
color: timelineRoot.palette.windowText
|
color: timelineRoot.palette.windowText
|
||||||
|
text: qsTr("Share desktop with %1?").arg(room.roomName)
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
Layout.bottomMargin: 8
|
||||||
Layout.leftMargin: 8
|
Layout.leftMargin: 8
|
||||||
Layout.rightMargin: 8
|
Layout.rightMargin: 8
|
||||||
Layout.bottomMargin: 8
|
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
text: qsTr("Window:")
|
|
||||||
color: timelineRoot.palette.windowText
|
color: timelineRoot.palette.windowText
|
||||||
|
text: qsTr("Window:")
|
||||||
}
|
}
|
||||||
|
|
||||||
ComboBox {
|
ComboBox {
|
||||||
id: windowCombo
|
id: windowCombo
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
model: CallManager.windowList()
|
model: CallManager.windowList()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
Layout.bottomMargin: 8
|
||||||
Layout.leftMargin: 8
|
Layout.leftMargin: 8
|
||||||
Layout.rightMargin: 8
|
Layout.rightMargin: 8
|
||||||
Layout.bottomMargin: 8
|
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
text: qsTr("Frame rate:")
|
|
||||||
color: timelineRoot.palette.windowText
|
color: timelineRoot.palette.windowText
|
||||||
|
text: qsTr("Frame rate:")
|
||||||
}
|
}
|
||||||
|
|
||||||
ComboBox {
|
ComboBox {
|
||||||
id: frameRateCombo
|
id: frameRateCombo
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
model: ["25", "20", "15", "10", "5", "2", "1"]
|
model: ["25", "20", "15", "10", "5", "2", "1"]
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GridLayout {
|
GridLayout {
|
||||||
|
Layout.margins: 8
|
||||||
columns: 2
|
columns: 2
|
||||||
rowSpacing: 10
|
rowSpacing: 10
|
||||||
Layout.margins: 8
|
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
text: qsTr("Include your camera picture-in-picture")
|
text: qsTr("Include your camera picture-in-picture")
|
||||||
}
|
}
|
||||||
|
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
id: pipCheckBox
|
id: pipCheckBox
|
||||||
|
|
||||||
enabled: CallManager.cameras.length > 0
|
|
||||||
checked: CallManager.cameras.length > 0 && Settings.screenSharePiP
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
|
checked: CallManager.cameras.length > 0 && Settings.screenSharePiP
|
||||||
|
enabled: CallManager.cameras.length > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
text: qsTr("Request remote camera")
|
|
||||||
ToolTip.text: qsTr("View your callee's camera like a regular video call")
|
ToolTip.text: qsTr("View your callee's camera like a regular video call")
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
|
text: qsTr("Request remote camera")
|
||||||
}
|
}
|
||||||
|
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
id: remoteVideoCheckBox
|
id: remoteVideoCheckBox
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
checked: Settings.screenShareRemoteVideo
|
|
||||||
ToolTip.text: qsTr("View your callee's camera like a regular video call")
|
ToolTip.text: qsTr("View your callee's camera like a regular video call")
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
|
checked: Settings.screenShareRemoteVideo
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
text: qsTr("Hide mouse cursor")
|
text: qsTr("Hide mouse cursor")
|
||||||
}
|
}
|
||||||
|
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
id: hideCursorCheckBox
|
id: hideCursorCheckBox
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
checked: Settings.screenShareHideCursor
|
checked: Settings.screenShareHideCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.margins: 8
|
Layout.margins: 8
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: qsTr("Share")
|
|
||||||
icon.source: "qrc:/icons/icons/ui/screen-share.svg"
|
icon.source: "qrc:/icons/icons/ui/screen-share.svg"
|
||||||
|
text: qsTr("Share")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
Settings.screenShareFrameRate = frameRateCombo.currentText;
|
Settings.screenShareFrameRate = frameRateCombo.currentText;
|
||||||
Settings.screenSharePiP = pipCheckBox.checked;
|
Settings.screenSharePiP = pipCheckBox.checked;
|
||||||
Settings.screenShareRemoteVideo = remoteVideoCheckBox.checked;
|
Settings.screenShareRemoteVideo = remoteVideoCheckBox.checked;
|
||||||
Settings.screenShareHideCursor = hideCursorCheckBox.checked;
|
Settings.screenShareHideCursor = hideCursorCheckBox.checked;
|
||||||
|
|
||||||
CallManager.sendInvite(room.roomId, Voip.SCREEN, windowCombo.currentIndex);
|
CallManager.sendInvite(room.roomId, Voip.SCREEN, windowCombo.currentIndex);
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: qsTr("Preview")
|
text: qsTr("Preview")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
CallManager.previewWindow(windowCombo.currentIndex);
|
CallManager.previewWindow(windowCombo.currentIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: qsTr("Cancel")
|
text: qsTr("Cancel")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: timelineRoot.palette.window
|
|
||||||
border.color: timelineRoot.palette.windowText
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import org.freedesktop.gstreamer.GLVideoItem 1.0
|
import org.freedesktop.gstreamer.GLVideoItem 1.0
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue