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
|
||||
|
||||
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
|
||||
|
||||
|
|
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: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import "./ui"
|
||||
import "ui"
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Window 2.15
|
||||
|
@ -12,70 +10,54 @@ import im.nheko
|
|||
AbstractButton {
|
||||
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 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
|
||||
width: 48
|
||||
|
||||
background: Rectangle {
|
||||
id: bg
|
||||
radius: Settings.avatarCircles ? height / 2 : height / 8
|
||||
color: timelineRoot.palette.alternateBase
|
||||
radius: Settings.avatarCircles ? height / 2 : height / 8
|
||||
}
|
||||
|
||||
Label {
|
||||
id: label
|
||||
|
||||
enabled: false
|
||||
|
||||
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)) : "")
|
||||
textFormat: Text.RichText
|
||||
font.pixelSize: avatar.height / 2
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
visible: img.status != Image.Ready && !Settings.useIdenticon
|
||||
color: timelineRoot.palette.text
|
||||
}
|
||||
|
||||
Image {
|
||||
id: identicon
|
||||
|
||||
anchors.fill: parent
|
||||
visible: Settings.useIdenticon && img.status != Image.Ready
|
||||
source: Settings.useIdenticon ? ("image://jdenticon/" + (userid !== "" ? userid : roomid) + "?radius=" + (Settings.avatarCircles ? 100 : 25)) : ""
|
||||
visible: Settings.useIdenticon && img.status != Image.Ready
|
||||
}
|
||||
|
||||
Image {
|
||||
id: img
|
||||
|
||||
anchors.fill: parent
|
||||
asynchronous: true
|
||||
fillMode: avatar.crop ? Image.PreserveAspectCrop : Image.PreserveAspectFit
|
||||
mipmap: 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")) : ""
|
||||
|
||||
sourceSize.height: avatar.height * Screen.devicePixelRatio
|
||||
sourceSize.width: avatar.width * Screen.devicePixelRatio
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
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() {
|
||||
switch (Presence.userPresence(userid)) {
|
||||
case "online":
|
||||
|
@ -89,22 +71,28 @@ AbstractButton {
|
|||
}
|
||||
}
|
||||
|
||||
anchors.bottom: avatar.bottom
|
||||
anchors.right: avatar.right
|
||||
color: updatePresence()
|
||||
height: avatar.height / 6
|
||||
radius: Settings.avatarCircles ? height / 2 : height / 8
|
||||
visible: !!userid
|
||||
width: height
|
||||
|
||||
Connections {
|
||||
target: Presence
|
||||
|
||||
function onPresenceChanged(id) {
|
||||
if (id == userid) onlineIndicator.color = onlineIndicator.updatePresence();
|
||||
}
|
||||
}
|
||||
if (id == userid)
|
||||
onlineIndicator.color = onlineIndicator.updatePresence();
|
||||
}
|
||||
|
||||
target: Presence
|
||||
}
|
||||
}
|
||||
NhekoCursorShape {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
|
||||
Ripple {
|
||||
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: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.3
|
||||
|
@ -14,41 +12,28 @@ import QtQml 2.15
|
|||
|
||||
Rectangle {
|
||||
id: chatPage
|
||||
|
||||
color: timelineRoot.palette.window
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 0
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
Rectangle {
|
||||
id: offlineIndicator
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: offlineLabel.height + Nheko.paddingMedium
|
||||
color: Nheko.theme.error
|
||||
visible: !TimelineManager.isConnected
|
||||
Layout.preferredHeight: offlineLabel.height + Nheko.paddingMedium
|
||||
Layout.fillWidth: true
|
||||
z: 1
|
||||
|
||||
Label {
|
||||
id: offlineLabel
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: qsTr("No network connection")
|
||||
}
|
||||
}
|
||||
|
||||
AdaptiveLayout {
|
||||
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() {
|
||||
if (!singlePageMode)
|
||||
adaptiveView.pageIndex = 0;
|
||||
|
@ -58,91 +43,80 @@ Rectangle {
|
|||
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 {
|
||||
target: Rooms
|
||||
function onCurrentRoomChanged() {
|
||||
adaptiveView.initializePageIndex();
|
||||
}
|
||||
}
|
||||
|
||||
target: Rooms
|
||||
}
|
||||
AdaptiveLayoutElement {
|
||||
id: communityListC
|
||||
|
||||
visible: Settings.groupView
|
||||
minimumWidth: communitiesList.avatarSize * 4 + Nheko.paddingMedium * 2
|
||||
collapsedWidth: communitiesList.avatarSize + 2 * Nheko.paddingMedium
|
||||
preferredWidth: Settings.communityListWidth >= minimumWidth ? Settings.communityListWidth : collapsedWidth
|
||||
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 {
|
||||
id: communitiesList
|
||||
|
||||
collapsed: parent.collapsed
|
||||
}
|
||||
|
||||
Binding {
|
||||
target: Settings
|
||||
delayed: true
|
||||
property: 'communityListWidth'
|
||||
restoreMode: Binding.RestoreBindingOrValue
|
||||
target: Settings
|
||||
value: communityListC.preferredWidth
|
||||
when: !adaptiveView.singlePageMode
|
||||
delayed: true
|
||||
restoreMode: Binding.RestoreBindingOrValue
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
AdaptiveLayoutElement {
|
||||
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
|
||||
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 {
|
||||
id: roomlist
|
||||
|
||||
height: adaptiveView.height
|
||||
collapsed: parent.collapsed
|
||||
height: adaptiveView.height
|
||||
}
|
||||
|
||||
Binding {
|
||||
target: Settings
|
||||
delayed: true
|
||||
property: 'roomListWidth'
|
||||
restoreMode: Binding.RestoreBindingOrValue
|
||||
target: Settings
|
||||
value: roomListC.preferredWidth
|
||||
when: !adaptiveView.singlePageMode
|
||||
delayed: true
|
||||
restoreMode: Binding.RestoreBindingOrValue
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
AdaptiveLayoutElement {
|
||||
id: timlineViewC
|
||||
|
||||
minimumWidth: fontMetrics.averageCharacterWidth * 40 + Nheko.avatarSize + 2 * Nheko.paddingMedium
|
||||
|
||||
TimelineView {
|
||||
id: timeline
|
||||
|
||||
showBackButton: adaptiveView.singlePageMode
|
||||
room: Rooms.currentRoom
|
||||
roomPreview: Rooms.currentRoomPreview.roomid ? Rooms.currentRoomPreview : null
|
||||
showBackButton: adaptiveView.singlePageMode
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
PrivacyScreen {
|
||||
anchors.fill: parent
|
||||
visible: Settings.privacyScreen
|
||||
screenTimeout: Settings.privacyScreenTimeout
|
||||
timelineRoot: adaptiveView
|
||||
visible: Settings.privacyScreen
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import "./dialogs"
|
||||
import "dialogs"
|
||||
import Qt.labs.platform 1.1 as Platform
|
||||
import QtQml 2.12
|
||||
import QtQuick 2.12
|
||||
|
@ -13,19 +11,142 @@ import im.nheko
|
|||
|
||||
Page {
|
||||
id: communitySidebar
|
||||
|
||||
//leftPadding: Nheko.paddingSmall
|
||||
//rightPadding: Nheko.paddingSmall
|
||||
property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 1.6)
|
||||
property bool collapsed: false
|
||||
|
||||
background: Rectangle {
|
||||
color: Nheko.theme.sidebarBackground
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: communitiesList
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: parent.height
|
||||
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 {
|
||||
id: communityContextMenu
|
||||
|
||||
|
@ -38,143 +159,9 @@ Page {
|
|||
|
||||
Platform.MenuItem {
|
||||
text: qsTr("Hide rooms with this tag or from this space by default.")
|
||||
|
||||
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,92 +1,77 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import im.nheko
|
||||
import im.nheko
|
||||
|
||||
import "./ui/"
|
||||
import "ui"
|
||||
|
||||
Control {
|
||||
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 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 rowSpacing: 5
|
||||
property alias count: listView.count
|
||||
|
||||
signal completionClicked(string completion)
|
||||
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() {
|
||||
if (currentIndex > -1 && currentIndex < listView.count)
|
||||
return completer.completionAt(currentIndex);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
function down() {
|
||||
if (bottomToTop)
|
||||
up_();
|
||||
else
|
||||
down_();
|
||||
}
|
||||
function down_() {
|
||||
currentIndex = currentIndex + 1;
|
||||
if (currentIndex >= listView.count)
|
||||
currentIndex = -1;
|
||||
}
|
||||
function finishCompletion() {
|
||||
if (popup.completerName == "room")
|
||||
popup.completionSelected(listView.itemAtIndex(currentIndex).modelData.roomid);
|
||||
|
||||
}
|
||||
|
||||
onCompleterNameChanged: {
|
||||
if (completerName) {
|
||||
completer = TimelineManager.completerFor(completerName, completerName == "room" ? "" : room.roomId);
|
||||
completer.setSearchString("");
|
||||
} else {
|
||||
completer = undefined;
|
||||
function up() {
|
||||
if (bottomToTop)
|
||||
down_();
|
||||
else
|
||||
up_();
|
||||
}
|
||||
currentIndex = -1
|
||||
function up_() {
|
||||
currentIndex = currentIndex - 1;
|
||||
if (currentIndex == -2)
|
||||
currentIndex = listView.count - 1;
|
||||
}
|
||||
|
||||
bottomPadding: 1
|
||||
leftPadding: 1
|
||||
topPadding: 1
|
||||
rightPadding: 1
|
||||
topPadding: 1
|
||||
|
||||
background: Rectangle {
|
||||
border.color: timelineRoot.palette.mid
|
||||
color: timelineRoot.palette.base
|
||||
}
|
||||
contentItem: ListView {
|
||||
id: listView
|
||||
clip: true
|
||||
highlightFollowsCurrentItem: true
|
||||
|
||||
// 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
|
||||
|
@ -94,22 +79,12 @@ Control {
|
|||
// 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!
|
||||
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
|
||||
model: completer
|
||||
verticalLayoutDirection: popup.bottomToTop ? ListView.BottomToTop : ListView.TopToBottom
|
||||
spacing: rowSpacing
|
||||
pixelAligned: true
|
||||
highlightFollowsCurrentItem: true
|
||||
reuseItems: true
|
||||
spacing: rowSpacing
|
||||
verticalLayoutDirection: popup.bottomToTop ? ListView.BottomToTop : ListView.TopToBottom
|
||||
|
||||
delegate: Rectangle {
|
||||
property variant modelData: model
|
||||
|
@ -120,193 +95,176 @@ Control {
|
|||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onPositionChanged: if (!listView.moving && !deadTimer.running) popup.currentIndex = model.index
|
||||
|
||||
onClicked: {
|
||||
popup.completionClicked(completer.completionAt(model.index));
|
||||
if (popup.completerName == "room")
|
||||
popup.completionSelected(model.roomid);
|
||||
}
|
||||
onPositionChanged: if (!listView.moving && !deadTimer.running)
|
||||
popup.currentIndex = model.index
|
||||
}
|
||||
Ripple {
|
||||
color: Qt.rgba(timelineRoot.palette.base.r, timelineRoot.palette.base.g, timelineRoot.palette.base.b, 0.5)
|
||||
}
|
||||
|
||||
DelegateChooser {
|
||||
id: chooser
|
||||
|
||||
roleValue: popup.completerName
|
||||
anchors.fill: parent
|
||||
anchors.margins: popup.rowMargin
|
||||
enabled: false
|
||||
roleValue: popup.completerName
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "user"
|
||||
|
||||
RowLayout {
|
||||
id: del
|
||||
|
||||
anchors.centerIn: parent
|
||||
spacing: rowSpacing
|
||||
|
||||
Avatar {
|
||||
height: popup.avatarHeight
|
||||
width: popup.avatarWidth
|
||||
displayName: model.displayName
|
||||
userid: model.userid
|
||||
height: popup.avatarHeight
|
||||
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||
userid: model.userid
|
||||
width: popup.avatarWidth
|
||||
|
||||
onClicked: popup.completionClicked(completer.completionAt(model.index))
|
||||
}
|
||||
|
||||
Label {
|
||||
text: model.displayName
|
||||
color: model.index == popup.currentIndex ? timelineRoot.palette.highlightedText : timelineRoot.palette.text
|
||||
text: model.displayName
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "(" + model.userid + ")"
|
||||
color: model.index == popup.currentIndex ? timelineRoot.palette.highlightedText : timelineRoot.palette.placeholderText
|
||||
text: "(" + model.userid + ")"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "emoji"
|
||||
|
||||
RowLayout {
|
||||
id: del
|
||||
|
||||
anchors.centerIn: parent
|
||||
spacing: rowSpacing
|
||||
|
||||
Label {
|
||||
text: model.unicode
|
||||
color: model.index == popup.currentIndex ? timelineRoot.palette.highlightedText : timelineRoot.palette.text
|
||||
font: Settings.emojiFont
|
||||
text: model.unicode
|
||||
}
|
||||
|
||||
Label {
|
||||
text: model.shortName
|
||||
color: model.index == popup.currentIndex ? timelineRoot.palette.highlightedText : timelineRoot.palette.text
|
||||
text: model.shortName
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "customEmoji"
|
||||
|
||||
RowLayout {
|
||||
id: del
|
||||
|
||||
anchors.centerIn: parent
|
||||
spacing: rowSpacing
|
||||
|
||||
Avatar {
|
||||
height: popup.avatarHeight
|
||||
width: popup.avatarWidth
|
||||
crop: false
|
||||
displayName: model.shortcode
|
||||
height: popup.avatarHeight
|
||||
//userid: model.shortcode
|
||||
url: model.url.replace("mxc://", "image://MxcImage/")
|
||||
width: popup.avatarWidth
|
||||
|
||||
onClicked: popup.completionClicked(completer.completionAt(model.index))
|
||||
crop: false
|
||||
}
|
||||
|
||||
Label {
|
||||
text: model.shortcode
|
||||
color: model.index == popup.currentIndex ? timelineRoot.palette.highlightedText : timelineRoot.palette.text
|
||||
text: model.shortcode
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "(" + model.packname + ")"
|
||||
color: model.index == popup.currentIndex ? timelineRoot.palette.highlightedText : timelineRoot.palette.placeholderText
|
||||
text: "(" + model.packname + ")"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "room"
|
||||
|
||||
RowLayout {
|
||||
id: del
|
||||
|
||||
anchors.centerIn: centerRowContent ? parent : undefined
|
||||
spacing: rowSpacing
|
||||
|
||||
Avatar {
|
||||
height: popup.avatarHeight
|
||||
width: popup.avatarWidth
|
||||
displayName: model.roomName
|
||||
height: popup.avatarHeight
|
||||
roomid: model.roomid
|
||||
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||
width: popup.avatarWidth
|
||||
|
||||
onClicked: {
|
||||
popup.completionClicked(completer.completionAt(model.index));
|
||||
popup.completionSelected(model.roomid);
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
text: model.roomName
|
||||
font.pixelSize: popup.avatarHeight * 0.5
|
||||
color: model.index == popup.currentIndex ? timelineRoot.palette.highlightedText : timelineRoot.palette.text
|
||||
font.pixelSize: popup.avatarHeight * 0.5
|
||||
text: model.roomName
|
||||
textFormat: Text.RichText
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "roomAliases"
|
||||
|
||||
RowLayout {
|
||||
id: del
|
||||
|
||||
anchors.centerIn: parent
|
||||
spacing: rowSpacing
|
||||
|
||||
Avatar {
|
||||
height: popup.avatarHeight
|
||||
width: popup.avatarWidth
|
||||
displayName: model.roomName
|
||||
height: popup.avatarHeight
|
||||
roomid: model.roomid
|
||||
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||
width: popup.avatarWidth
|
||||
|
||||
onClicked: popup.completionClicked(completer.completionAt(model.index))
|
||||
}
|
||||
|
||||
Label {
|
||||
text: model.roomName
|
||||
color: model.index == popup.currentIndex ? timelineRoot.palette.highlightedText : timelineRoot.palette.text
|
||||
text: model.roomName
|
||||
textFormat: Text.RichText
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "(" + model.roomAlias + ")"
|
||||
color: model.index == popup.currentIndex ? timelineRoot.palette.highlightedText : timelineRoot.palette.placeholderText
|
||||
text: "(" + model.roomAlias + ")"
|
||||
textFormat: Text.RichText
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onContentYChanged: deadTimer.restart()
|
||||
|
||||
Timer {
|
||||
id: deadTimer
|
||||
interval: 50
|
||||
}
|
||||
}
|
||||
|
||||
onCompleterNameChanged: {
|
||||
if (completerName) {
|
||||
completer = TimelineManager.completerFor(completerName, completerName == "room" ? "" : room.roomId);
|
||||
completer.setSearchString("");
|
||||
} else {
|
||||
completer = undefined;
|
||||
}
|
||||
|
||||
currentIndex = -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
background: Rectangle {
|
||||
color: timelineRoot.palette.base
|
||||
border.color: timelineRoot.palette.mid
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.5
|
||||
import im.nheko
|
||||
|
@ -10,21 +8,25 @@ import im.nheko
|
|||
Label {
|
||||
id: root
|
||||
|
||||
property alias fullText: metrics.text
|
||||
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
|
||||
text: (textFormat == Text.PlainText) ? metrics.elidedText : TimelineManager.escapeEmoji(TimelineManager.htmlEscape(metrics.elidedText))
|
||||
maximumLineCount: 1
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 1
|
||||
text: (textFormat == Text.PlainText) ? metrics.elidedText : TimelineManager.escapeEmoji(TimelineManager.htmlEscape(metrics.elidedText))
|
||||
textFormat: Text.PlainText
|
||||
|
||||
TextMetrics {
|
||||
id: metrics
|
||||
|
||||
font.pointSize: root.font.pointSize
|
||||
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: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.1
|
||||
import QtQuick.Window 2.15
|
||||
|
@ -12,12 +10,9 @@ Image {
|
|||
id: stateImg
|
||||
|
||||
property bool encrypted: false
|
||||
property int trust: Crypto.Unverified
|
||||
|
||||
property string sourceUrl: {
|
||||
if (!encrypted)
|
||||
return "image://colorimage/:/icons/icons/ui/shield-filled-cross.svg?";
|
||||
|
||||
switch (trust) {
|
||||
case Crypto.Verified:
|
||||
return "image://colorimage/:/icons/icons/ui/shield-filled-checkmark.svg?";
|
||||
|
@ -29,11 +24,22 @@ Image {
|
|||
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
|
||||
sourceSize.height: height * Screen.devicePixelRatio
|
||||
sourceSize.width: width * Screen.devicePixelRatio
|
||||
source: {
|
||||
if (encrypted) {
|
||||
switch (trust) {
|
||||
|
@ -48,23 +54,11 @@ Image {
|
|||
return sourceUrl + Nheko.theme.error;
|
||||
}
|
||||
}
|
||||
ToolTip.visible: ma.hovered
|
||||
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.");
|
||||
}
|
||||
}
|
||||
sourceSize.height: height * Screen.devicePixelRatio
|
||||
sourceSize.width: width * Screen.devicePixelRatio
|
||||
width: 16
|
||||
|
||||
HoverHandler {
|
||||
id: ma
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import "./delegates/"
|
||||
import "delegates"
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.3
|
||||
import im.nheko
|
||||
|
@ -17,66 +15,65 @@ Popup {
|
|||
mid = mid_in;
|
||||
}
|
||||
|
||||
x: Math.round(parent.width / 2 - width / 2)
|
||||
y: Math.round(parent.height / 4)
|
||||
leftPadding: 10
|
||||
modal: true
|
||||
palette: timelineRoot.palette
|
||||
parent: Overlay.overlay
|
||||
width: timelineRoot.width * 0.8
|
||||
leftPadding: 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: {
|
||||
roomTextInput.forceActiveFocus();
|
||||
}
|
||||
|
||||
Column {
|
||||
id: forwardColumn
|
||||
|
||||
spacing: 5
|
||||
|
||||
Label {
|
||||
id: titleLabel
|
||||
|
||||
text: qsTr("Forward Message")
|
||||
font.bold: true
|
||||
bottomPadding: 10
|
||||
color: timelineRoot.palette.text
|
||||
font.bold: true
|
||||
text: qsTr("Forward Message")
|
||||
}
|
||||
|
||||
Reply {
|
||||
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 ?? ""
|
||||
body: modelData.body ?? ""
|
||||
formattedBody: modelData.formattedBody ?? ""
|
||||
encryptionError: modelData.encryptionError ?? ""
|
||||
eventId: modelData.eventId ?? ""
|
||||
filename: modelData.filename ?? ""
|
||||
filesize: modelData.filesize ?? ""
|
||||
formattedBody: modelData.formattedBody ?? ""
|
||||
isOnlyEmoji: modelData.isOnlyEmoji ?? false
|
||||
originalWidth: modelData.originalWidth ?? 0
|
||||
proportionalHeight: modelData.proportionalHeight ?? 1
|
||||
type: modelData.type ?? MtxEvent.UnknownMessage
|
||||
typeString: modelData.typeString ?? ""
|
||||
url: modelData.url ?? ""
|
||||
originalWidth: modelData.originalWidth ?? 0
|
||||
isOnlyEmoji: modelData.isOnlyEmoji ?? false
|
||||
userColor: TimelineManager.userColor(modelData.userId, timelineRoot.palette.window)
|
||||
userId: modelData.userId ?? ""
|
||||
userName: modelData.userName ?? ""
|
||||
encryptionError: modelData.encryptionError ?? ""
|
||||
width: parent.width
|
||||
}
|
||||
|
||||
MatrixTextField {
|
||||
id: roomTextInput
|
||||
|
||||
width: forwardMessagePopup.width - forwardMessagePopup.leftPadding * 2
|
||||
color: timelineRoot.palette.text
|
||||
onTextEdited: {
|
||||
completerPopup.completer.searchString = text;
|
||||
}
|
||||
width: forwardMessagePopup.width - forwardMessagePopup.leftPadding * 2
|
||||
|
||||
Keys.onPressed: {
|
||||
if (event.key == Qt.Key_Up || event.key == Qt.Key_Backtab) {
|
||||
event.accepted = true;
|
||||
|
@ -92,43 +89,31 @@ Popup {
|
|||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
onTextEdited: {
|
||||
completerPopup.completer.searchString = text;
|
||||
}
|
||||
}
|
||||
|
||||
Completer {
|
||||
id: completerPopup
|
||||
|
||||
width: forwardMessagePopup.width - forwardMessagePopup.leftPadding * 2
|
||||
completerName: "room"
|
||||
fullWidth: true
|
||||
centerRowContent: false
|
||||
avatarHeight: 24
|
||||
avatarWidth: 24
|
||||
bottomToTop: false
|
||||
centerRowContent: false
|
||||
completerName: "room"
|
||||
fullWidth: true
|
||||
width: forwardMessagePopup.width - forwardMessagePopup.leftPadding * 2
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onCompletionSelected(id) {
|
||||
room.forwardMessage(messageContextMenu.eventId, id);
|
||||
forwardMessagePopup.close();
|
||||
}
|
||||
|
||||
function onCountChanged() {
|
||||
if (completerPopup.count > 0 && (completerPopup.currentIndex < 0 || completerPopup.currentIndex >= completerPopup.count))
|
||||
completerPopup.currentIndex = 0;
|
||||
|
||||
}
|
||||
|
||||
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: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import "./ui"
|
||||
import "ui"
|
||||
import QtQuick 2.3
|
||||
import QtQuick.Controls 2.3
|
||||
import im.nheko
|
||||
|
@ -11,36 +9,32 @@ import im.nheko
|
|||
AbstractButton {
|
||||
id: button
|
||||
|
||||
property alias cursor: mouseArea.cursorShape
|
||||
property string image: undefined
|
||||
property color highlightColor: timelineRoot.palette.highlight
|
||||
property color buttonTextColor: timelineRoot.palette.placeholderText
|
||||
property bool changeColorOnHover: true
|
||||
property alias cursor: mouseArea.cursorShape
|
||||
property color highlightColor: timelineRoot.palette.highlight
|
||||
property string image: undefined
|
||||
property bool ripple: true
|
||||
|
||||
focusPolicy: Qt.NoFocus
|
||||
width: 16
|
||||
height: 16
|
||||
width: 16
|
||||
|
||||
Image {
|
||||
id: buttonImg
|
||||
|
||||
// Workaround, can't get icon.source working for now...
|
||||
anchors.fill: parent
|
||||
source: image != "" ? ("image://colorimage/" + image + "?" + ((button.hovered && changeColorOnHover) ? highlightColor : buttonTextColor)) : ""
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: image != "" ? ("image://colorimage/" + image + "?" + ((button.hovered && changeColorOnHover) ? highlightColor : buttonTextColor)) : ""
|
||||
}
|
||||
|
||||
NhekoCursorShape {
|
||||
id: mouseArea
|
||||
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
|
||||
Ripple {
|
||||
enabled: button.ripple
|
||||
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: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 2.3
|
||||
import im.nheko
|
||||
|
@ -12,27 +10,27 @@ TextEdit {
|
|||
|
||||
property alias cursorShape: cs.cursorShape
|
||||
|
||||
textFormat: TextEdit.RichText
|
||||
readOnly: true
|
||||
focus: false
|
||||
wrapMode: Text.Wrap
|
||||
selectByMouse: !Settings.mobileMode
|
||||
ToolTip.text: hoveredLink
|
||||
ToolTip.visible: hoveredLink || false
|
||||
// this always has to be enabled, otherwise you can't click links anymore!
|
||||
//enabled: selectByMouse
|
||||
color: timelineRoot.palette.text
|
||||
onLinkActivated: Nheko.openLink(link)
|
||||
ToolTip.visible: hoveredLink || false
|
||||
ToolTip.text: hoveredLink
|
||||
focus: false
|
||||
readOnly: true
|
||||
selectByMouse: !Settings.mobileMode
|
||||
textFormat: TextEdit.RichText
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
// Setting a tooltip delay makes the hover text empty .-.
|
||||
//ToolTip.delay: Nheko.tooltipDelay
|
||||
Component.onCompleted: {
|
||||
TimelineManager.fixImageRendering(r.textDocument, r);
|
||||
}
|
||||
onLinkActivated: Nheko.openLink(link)
|
||||
|
||||
NhekoCursorShape {
|
||||
id: cs
|
||||
anchors.fill: parent
|
||||
cursorShape: hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,74 +1,66 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
import QtQuick.Layouts 1.12
|
||||
import im.nheko
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
id: c
|
||||
|
||||
property color backgroundColor: timelineRoot.palette.base
|
||||
property alias color: labelC.color
|
||||
property alias textPadding: input.padding
|
||||
property alias text: input.text
|
||||
property alias echoMode: input.echoMode
|
||||
property alias font: input.font
|
||||
property alias label: labelC.text
|
||||
property alias placeholderText: input.placeholderText
|
||||
property alias font: input.font
|
||||
property alias echoMode: input.echoMode
|
||||
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 editingFinished
|
||||
|
||||
function forceActiveFocus() {
|
||||
input.forceActiveFocus();
|
||||
}
|
||||
signal textEdited
|
||||
|
||||
function clear() {
|
||||
input.clear();
|
||||
}
|
||||
function forceActiveFocus() {
|
||||
input.forceActiveFocus();
|
||||
}
|
||||
|
||||
ToolTip.delay: Nheko.tooltipDelay
|
||||
ToolTip.visible: hover.hovered
|
||||
|
||||
spacing: 0
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: labelC.contentHeight
|
||||
Layout.margins: input.padding
|
||||
Layout.bottomMargin: Nheko.paddingSmall
|
||||
visible: labelC.text
|
||||
onTextChanged: timer.restart()
|
||||
|
||||
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
|
||||
|
||||
Label {
|
||||
id: labelC
|
||||
|
||||
y: contentHeight + input.padding + Nheko.paddingSmall
|
||||
enabled: false
|
||||
|
||||
palette: timelineRoot.palette
|
||||
color: timelineRoot.palette.text
|
||||
enabled: false
|
||||
font.letterSpacing: input.font.pixelSize * 0.02
|
||||
font.pixelSize: input.font.pixelSize
|
||||
font.weight: Font.DemiBold
|
||||
font.letterSpacing: input.font.pixelSize * 0.02
|
||||
width: parent.width
|
||||
|
||||
palette: timelineRoot.palette
|
||||
state: labelC.text && (input.activeFocus == true || input.text) ? "focused" : ""
|
||||
width: parent.width
|
||||
y: contentHeight + input.padding + Nheko.paddingSmall
|
||||
|
||||
states: State {
|
||||
name: "focused"
|
||||
|
@ -77,76 +69,63 @@ ColumnLayout {
|
|||
target: labelC
|
||||
y: 0
|
||||
}
|
||||
|
||||
PropertyChanges {
|
||||
target: input
|
||||
opacity: 1
|
||||
target: input
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
transitions: Transition {
|
||||
from: ""
|
||||
to: "focused"
|
||||
reversible: true
|
||||
to: "focused"
|
||||
|
||||
NumberAnimation {
|
||||
target: labelC
|
||||
alwaysRunToEnd: true
|
||||
duration: 210
|
||||
easing.type: Easing.InCubic
|
||||
properties: "y"
|
||||
duration: 210
|
||||
easing.type: Easing.InCubic
|
||||
alwaysRunToEnd: true
|
||||
target: labelC
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
target: input
|
||||
properties: "opacity"
|
||||
alwaysRunToEnd: true
|
||||
duration: 210
|
||||
easing.type: Easing.InCubic
|
||||
alwaysRunToEnd: true
|
||||
}
|
||||
|
||||
properties: "opacity"
|
||||
target: input
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: input
|
||||
Layout.fillWidth: true
|
||||
|
||||
palette: timelineRoot.palette
|
||||
color: labelC.color
|
||||
opacity: labelC.text ? 0 : 1
|
||||
focus: true
|
||||
|
||||
onTextEdited: c.textEdited()
|
||||
onAccepted: c.accepted()
|
||||
onEditingFinished: c.editingFinished()
|
||||
opacity: labelC.text ? 0 : 1
|
||||
palette: timelineRoot.palette
|
||||
|
||||
background: Rectangle {
|
||||
id: backgroundRect
|
||||
|
||||
color: labelC.text ? "transparent" : backgroundColor
|
||||
}
|
||||
|
||||
onAccepted: c.accepted()
|
||||
onEditingFinished: c.editingFinished()
|
||||
onTextEdited: c.textEdited()
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: blueBar
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
color: timelineRoot.palette.highlight
|
||||
height: 1
|
||||
|
||||
Rectangle {
|
||||
id: blackBar
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
color: timelineRoot.palette.text
|
||||
height: parent.height * 2
|
||||
width: 0
|
||||
color: timelineRoot.palette.text
|
||||
|
||||
states: State {
|
||||
name: "focused"
|
||||
|
@ -156,29 +135,22 @@ ColumnLayout {
|
|||
target: blackBar
|
||||
width: blueBar.width
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
transitions: Transition {
|
||||
from: ""
|
||||
to: "focused"
|
||||
reversible: true
|
||||
|
||||
to: "focused"
|
||||
|
||||
NumberAnimation {
|
||||
target: blackBar
|
||||
properties: "width"
|
||||
alwaysRunToEnd: true
|
||||
duration: 310
|
||||
easing.type: Easing.InCubic
|
||||
alwaysRunToEnd: true
|
||||
properties: "width"
|
||||
target: blackBar
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
id: hover
|
||||
enabled: c.ToolTip.text
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import "./emoji"
|
||||
import "./voip"
|
||||
import "emoji"
|
||||
import "voip"
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.3
|
||||
import QtQuick.Layouts 1.2
|
||||
|
@ -14,47 +12,41 @@ import im.nheko
|
|||
Rectangle {
|
||||
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)
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumHeight: 40
|
||||
Layout.preferredHeight: row.implicitHeight
|
||||
color: timelineRoot.palette.window
|
||||
|
||||
Component {
|
||||
id: placeCallDialog
|
||||
|
||||
PlaceCall {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Component {
|
||||
id: screenShareDialog
|
||||
|
||||
ScreenShare {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: row
|
||||
|
||||
visible: room ? room.permissions.canSend(MtxEvent.TextMessage) : false
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
visible: room ? room.permissions.canSend(MtxEvent.TextMessage) : false
|
||||
|
||||
ImageButton {
|
||||
visible: CallManager.callsSupported && showAllButtons
|
||||
opacity: CallManager.haveCallInvite ? 0.3 : 1
|
||||
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
|
||||
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: {
|
||||
if (room) {
|
||||
if (CallManager.haveCallInvite) {
|
||||
|
@ -69,18 +61,18 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImageButton {
|
||||
visible: showAllButtons
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
hoverEnabled: true
|
||||
width: 22
|
||||
height: 22
|
||||
image: ":/icons/icons/ui/attach.svg"
|
||||
Layout.margins: 8
|
||||
onClicked: room.input.openFileSelection()
|
||||
ToolTip.visible: hovered
|
||||
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 {
|
||||
anchors.fill: parent
|
||||
|
@ -91,101 +83,57 @@ Rectangle {
|
|||
anchors.fill: parent
|
||||
running: parent.visible
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
id: textInput
|
||||
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumHeight: Window.height / 4
|
||||
Layout.minimumHeight: fontMetrics.lineSpacing
|
||||
Layout.preferredHeight: contentHeight
|
||||
Layout.fillWidth: true
|
||||
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
|
||||
contentWidth: availableWidth
|
||||
|
||||
TextArea {
|
||||
id: messageInput
|
||||
|
||||
property int completerTriggeredAt: 0
|
||||
property string lastChar
|
||||
|
||||
function insertCompletion(completion) {
|
||||
messageInput.remove(completerTriggeredAt, cursorPosition);
|
||||
messageInput.insert(cursorPosition, completion);
|
||||
}
|
||||
|
||||
function openCompleter(pos, type) {
|
||||
if (popup.opened) return;
|
||||
if (popup.opened)
|
||||
return;
|
||||
completerTriggeredAt = pos;
|
||||
completer.completerName = type;
|
||||
popup.open();
|
||||
completer.completer.setSearchString(messageInput.getText(completerTriggeredAt, cursorPosition) + messageInput.preeditText);
|
||||
}
|
||||
|
||||
function positionCursorAtEnd() {
|
||||
cursorPosition = messageInput.length;
|
||||
}
|
||||
|
||||
function positionCursorAtStart() {
|
||||
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...")
|
||||
placeholderTextColor: timelineRoot.palette.placeholderText
|
||||
color: timelineRoot.palette.text
|
||||
width: textInput.width
|
||||
verticalAlignment: TextEdit.AlignVCenter
|
||||
wrapMode: TextEdit.Wrap
|
||||
padding: 0
|
||||
selectByMouse: true
|
||||
topPadding: 8
|
||||
bottomPadding: 8
|
||||
leftPadding: inputBar.showAllButtons? 0 : 8
|
||||
focus: true
|
||||
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 ;
|
||||
verticalAlignment: TextEdit.AlignVCenter
|
||||
width: textInput.width
|
||||
wrapMode: TextEdit.Wrap
|
||||
|
||||
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: {
|
||||
if (event.matches(StandardKey.Paste)) {
|
||||
room.input.paste(false);
|
||||
|
@ -194,10 +142,8 @@ Rectangle {
|
|||
// close popup if user enters space after colon
|
||||
if (cursorPosition == completerTriggeredAt + 1)
|
||||
popup.close();
|
||||
|
||||
if (popup.opened && completer.count <= 0)
|
||||
popup.close();
|
||||
|
||||
} else if (event.modifiers == Qt.ControlModifier && event.key == Qt.Key_U) {
|
||||
messageInput.clear();
|
||||
} else if (event.modifiers == Qt.ControlModifier && event.key == Qt.Key_P) {
|
||||
|
@ -212,7 +158,8 @@ Rectangle {
|
|||
completer.completerName = "";
|
||||
popup.close();
|
||||
} else if (event.matches(StandardKey.InsertLineSeparator)) {
|
||||
if (popup.opened) popup.close();
|
||||
if (popup.opened)
|
||||
popup.close();
|
||||
} else if (event.matches(StandardKey.InsertParagraphSeparator)) {
|
||||
if (popup.opened) {
|
||||
var currentCompletion = completer.currentCompletion();
|
||||
|
@ -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 {
|
||||
function onRoomChanged() {
|
||||
messageInput.clear();
|
||||
if (room)
|
||||
messageInput.append(room.input.text);
|
||||
|
||||
completer.completerName = "";
|
||||
messageInput.forceActiveFocus();
|
||||
}
|
||||
|
||||
target: timelineView
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onCompletionClicked(completion) {
|
||||
messageInput.insertCompletion(completion);
|
||||
|
@ -323,49 +302,42 @@ Rectangle {
|
|||
|
||||
target: completer
|
||||
}
|
||||
|
||||
Popup {
|
||||
id: popup
|
||||
|
||||
background: null
|
||||
padding: 0
|
||||
x: messageInput.positionToRectangle(messageInput.completerTriggeredAt).x
|
||||
y: messageInput.positionToRectangle(messageInput.completerTriggeredAt).y - height
|
||||
|
||||
background: null
|
||||
padding: 0
|
||||
enter: Transition {
|
||||
NumberAnimation {
|
||||
duration: 100
|
||||
from: 0
|
||||
property: "opacity"
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
exit: Transition {
|
||||
NumberAnimation {
|
||||
duration: 100
|
||||
from: 1
|
||||
property: "opacity"
|
||||
to: 0
|
||||
}
|
||||
}
|
||||
|
||||
Completer {
|
||||
anchors.fill: parent
|
||||
id: completer
|
||||
anchors.fill: parent
|
||||
rowMargin: 2
|
||||
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 {
|
||||
function onInsertText(text) {
|
||||
messageInput.remove(messageInput.selectionStart, messageInput.selectionEnd);
|
||||
messageInput.insert(messageInput.cursorPosition, text);
|
||||
}
|
||||
|
||||
function onTextChanged(newText) {
|
||||
messageInput.text = newText;
|
||||
messageInput.cursorPosition = newText.length;
|
||||
|
@ -374,20 +346,17 @@ Rectangle {
|
|||
ignoreUnknownSignals: true
|
||||
target: room ? room.input : null
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onReplyChanged() {
|
||||
function onEditChanged() {
|
||||
messageInput.forceActiveFocus();
|
||||
}
|
||||
|
||||
function onEditChanged() {
|
||||
function onReplyChanged() {
|
||||
messageInput.forceActiveFocus();
|
||||
}
|
||||
|
||||
ignoreUnknownSignals: true
|
||||
target: room
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onFocusInput() {
|
||||
messageInput.forceActiveFocus();
|
||||
|
@ -395,31 +364,28 @@ Rectangle {
|
|||
|
||||
target: TimelineManager
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
acceptedButtons: Qt.MiddleButton
|
||||
// workaround for wrong cursor shape on some platforms
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.MiddleButton
|
||||
cursorShape: Qt.IBeamCursor
|
||||
|
||||
onClicked: room.input.paste(true)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ImageButton {
|
||||
id: stickerButton
|
||||
visible: showAllButtons
|
||||
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
|
||||
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.visible: hovered
|
||||
height: 22
|
||||
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();
|
||||
|
@ -427,51 +393,45 @@ Rectangle {
|
|||
|
||||
StickerPicker {
|
||||
id: stickerPopup
|
||||
|
||||
colors: timelineRoot.palette
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ImageButton {
|
||||
id: emojiButton
|
||||
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
|
||||
Layout.margins: 8
|
||||
hoverEnabled: true
|
||||
width: 22
|
||||
height: 22
|
||||
image: ":/icons/icons/ui/smile.svg"
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: qsTr("Emoji")
|
||||
ToolTip.visible: hovered
|
||||
height: 22
|
||||
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 {
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
|
||||
Layout.margins: 8
|
||||
hoverEnabled: true
|
||||
width: 22
|
||||
height: 22
|
||||
image: ":/icons/icons/ui/send.svg"
|
||||
Layout.rightMargin: 8
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: qsTr("Send")
|
||||
ToolTip.visible: hovered
|
||||
height: 22
|
||||
hoverEnabled: true
|
||||
image: ":/icons/icons/ui/send.svg"
|
||||
width: 22
|
||||
|
||||
onClicked: {
|
||||
room.input.send();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Text {
|
||||
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
|
||||
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: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.3
|
||||
import QtQuick.Layouts 1.2
|
||||
|
@ -10,38 +8,41 @@ import im.nheko
|
|||
|
||||
BusyIndicator {
|
||||
id: control
|
||||
|
||||
contentItem: Item {
|
||||
implicitWidth: 64
|
||||
implicitHeight: 64
|
||||
implicitWidth: 64
|
||||
|
||||
Item {
|
||||
id: item
|
||||
|
||||
height: Math.min(parent.height, parent.width)
|
||||
width: height
|
||||
opacity: control.running ? 1 : 0
|
||||
width: height
|
||||
|
||||
RotationAnimator {
|
||||
target: item
|
||||
running: control.visible && control.running
|
||||
from: 0
|
||||
to: 360
|
||||
loops: Animation.Infinite
|
||||
duration: 2000
|
||||
Behavior on opacity {
|
||||
OpacityAnimator {
|
||||
duration: 250
|
||||
}
|
||||
}
|
||||
|
||||
RotationAnimator {
|
||||
duration: 2000
|
||||
from: 0
|
||||
loops: Animation.Infinite
|
||||
running: control.visible && control.running
|
||||
target: item
|
||||
to: 360
|
||||
}
|
||||
Repeater {
|
||||
id: repeater
|
||||
|
||||
model: 6
|
||||
|
||||
Rectangle {
|
||||
implicitWidth: radius * 2
|
||||
implicitHeight: radius * 2
|
||||
radius: item.height / 6
|
||||
color: timelineRoot.palette.text
|
||||
implicitHeight: radius * 2
|
||||
implicitWidth: radius * 2
|
||||
opacity: (index + 2) / (repeater.count + 2)
|
||||
radius: item.height / 6
|
||||
|
||||
transform: [
|
||||
Translate {
|
||||
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: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.3
|
||||
import QtQuick.Layouts 1.2
|
||||
import im.nheko
|
||||
|
||||
Item {
|
||||
implicitHeight: warningRect.visible ? warningDisplay.implicitHeight : 0
|
||||
height: implicitHeight
|
||||
Layout.fillWidth: true
|
||||
height: implicitHeight
|
||||
implicitHeight: warningRect.visible ? warningDisplay.implicitHeight : 0
|
||||
|
||||
Rectangle {
|
||||
id: warningRect
|
||||
|
||||
visible: (room && room.permissions.canPingRoom() && room.input.containsAtRoom)
|
||||
color: timelineRoot.palette.base
|
||||
anchors.fill: parent
|
||||
color: timelineRoot.palette.base
|
||||
visible: (room && room.permissions.canPingRoom() && room.input.containsAtRoom)
|
||||
z: 3
|
||||
|
||||
Label {
|
||||
id: warningDisplay
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 10
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 10
|
||||
anchors.bottom: parent.bottom
|
||||
color: Nheko.theme.red
|
||||
text: qsTr("You are about to notify the whole room")
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
//import QtGraphicalEffects 1.0
|
||||
|
@ -11,8 +10,8 @@ import im.nheko
|
|||
Item {
|
||||
id: privacyScreen
|
||||
|
||||
property var timelineRoot
|
||||
property int screenTimeout
|
||||
property var timelineRoot
|
||||
|
||||
Connections {
|
||||
function onFocusChanged() {
|
||||
|
@ -22,29 +21,26 @@ Item {
|
|||
} else {
|
||||
if (timelineRoot.visible)
|
||||
screenSaverTimer.start();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
target: TimelineManager
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: screenSaverTimer
|
||||
|
||||
interval: screenTimeout * 1000
|
||||
running: !MainWindow.active
|
||||
|
||||
onTriggered: {
|
||||
screenSaver.state = "Visible";
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: screenSaver
|
||||
|
||||
state: "Invisible"
|
||||
anchors.fill: parent
|
||||
state: "Invisible"
|
||||
visible: false
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "Visible"
|
||||
|
@ -53,26 +49,22 @@ Item {
|
|||
target: screenSaver
|
||||
visible: true
|
||||
}
|
||||
|
||||
PropertyChanges {
|
||||
target: screenSaver
|
||||
opacity: 1
|
||||
target: screenSaver
|
||||
}
|
||||
|
||||
},
|
||||
State {
|
||||
name: "Invisible"
|
||||
|
||||
PropertyChanges {
|
||||
target: screenSaver
|
||||
opacity: 0
|
||||
target: screenSaver
|
||||
}
|
||||
|
||||
PropertyChanges {
|
||||
target: screenSaver
|
||||
visible: false
|
||||
}
|
||||
|
||||
}
|
||||
]
|
||||
transitions: [
|
||||
|
@ -82,20 +74,17 @@ Item {
|
|||
|
||||
SequentialAnimation {
|
||||
NumberAnimation {
|
||||
target: screenSaver
|
||||
property: "opacity"
|
||||
duration: 250
|
||||
easing.type: Easing.InQuad
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
property: "opacity"
|
||||
target: screenSaver
|
||||
property: "visible"
|
||||
}
|
||||
NumberAnimation {
|
||||
duration: 0
|
||||
property: "visible"
|
||||
target: screenSaver
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
Transition {
|
||||
from: "Invisible"
|
||||
|
@ -103,20 +92,17 @@ Item {
|
|||
|
||||
SequentialAnimation {
|
||||
NumberAnimation {
|
||||
target: screenSaver
|
||||
property: "visible"
|
||||
duration: 0
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
property: "visible"
|
||||
target: screenSaver
|
||||
property: "opacity"
|
||||
}
|
||||
NumberAnimation {
|
||||
duration: 500
|
||||
easing.type: Easing.InQuad
|
||||
property: "opacity"
|
||||
target: screenSaver
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
]
|
||||
|
||||
|
@ -127,7 +113,5 @@ Item {
|
|||
// source: timelineRoot
|
||||
// radius: 50
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
|
@ -12,19 +10,24 @@ Popup {
|
|||
id: quickSwitcher
|
||||
|
||||
property int textHeight: Math.round(Qt.application.font.pixelSize * 2.4)
|
||||
property int textMargin: Nheko.paddingSmall
|
||||
|
||||
background: null
|
||||
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)
|
||||
y: Math.round(parent.height / 4)
|
||||
modal: true
|
||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
||||
parent: Overlay.overlay
|
||||
palette: timelineRoot.palette
|
||||
|
||||
Overlay.modal: Rectangle {
|
||||
color: "#aa1E1E1E"
|
||||
}
|
||||
|
||||
onOpened: {
|
||||
roomTextInput.forceActiveFocus();
|
||||
}
|
||||
property int textMargin: Nheko.paddingSmall
|
||||
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
|
@ -32,13 +35,10 @@ Popup {
|
|||
|
||||
MatrixTextField {
|
||||
id: roomTextInput
|
||||
|
||||
width: parent.width
|
||||
font.pixelSize: Math.ceil(quickSwitcher.textHeight * 0.6)
|
||||
color: timelineRoot.palette.text
|
||||
onTextEdited: {
|
||||
completerPopup.completer.searchString = text;
|
||||
}
|
||||
font.pixelSize: Math.ceil(quickSwitcher.textHeight * 0.6)
|
||||
width: parent.width
|
||||
|
||||
Keys.onPressed: {
|
||||
if (event.key == Qt.Key_Up || event.key == Qt.Key_Backtab) {
|
||||
event.accepted = true;
|
||||
|
@ -54,41 +54,34 @@ Popup {
|
|||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
onTextEdited: {
|
||||
completerPopup.completer.searchString = text;
|
||||
}
|
||||
}
|
||||
|
||||
Completer {
|
||||
id: completerPopup
|
||||
|
||||
visible: roomTextInput.text.length > 0
|
||||
width: parent.width
|
||||
completerName: "room"
|
||||
bottomToTop: false
|
||||
fullWidth: true
|
||||
avatarHeight: quickSwitcher.textHeight
|
||||
avatarWidth: quickSwitcher.textHeight
|
||||
bottomToTop: false
|
||||
centerRowContent: false
|
||||
completerName: "room"
|
||||
fullWidth: true
|
||||
rowMargin: Math.round(quickSwitcher.textMargin / 2)
|
||||
rowSpacing: quickSwitcher.textMargin
|
||||
visible: roomTextInput.text.length > 0
|
||||
width: parent.width
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onCompletionSelected(id) {
|
||||
Rooms.setCurrentRoom(id);
|
||||
quickSwitcher.close();
|
||||
}
|
||||
|
||||
function onCountChanged() {
|
||||
if (completerPopup.count > 0 && (completerPopup.currentIndex < 0 || completerPopup.currentIndex >= completerPopup.count))
|
||||
completerPopup.currentIndex = 0;
|
||||
|
||||
}
|
||||
|
||||
target: completerPopup
|
||||
}
|
||||
|
||||
Overlay.modal: Rectangle {
|
||||
color: "#aa1E1E1E"
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.6
|
||||
import QtQuick.Controls 2.2
|
||||
import im.nheko
|
||||
|
@ -12,58 +10,54 @@ import im.nheko
|
|||
Flow {
|
||||
id: reactionFlow
|
||||
|
||||
property string eventId
|
||||
|
||||
// highlight colors for selfReactedEvent background
|
||||
property real highlightHue: timelineRoot.palette.highlight.hslHue
|
||||
property real highlightSat: timelineRoot.palette.highlight.hslSaturation
|
||||
property real highlightLight: timelineRoot.palette.highlight.hslLightness
|
||||
property string eventId
|
||||
property real highlightSat: timelineRoot.palette.highlight.hslSaturation
|
||||
property alias reactions: repeater.model
|
||||
|
||||
spacing: 4
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
|
||||
delegate: AbstractButton {
|
||||
id: reaction
|
||||
|
||||
hoverEnabled: true
|
||||
implicitWidth: contentItem.childrenRect.width + contentItem.leftPadding * 2
|
||||
implicitHeight: contentItem.childrenRect.height
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.delay: Nheko.tooltipDelay
|
||||
onClicked: {
|
||||
console.debug("Picked " + modelData.key + "in response to " + reactionFlow.eventId + ". selfReactedEvent: " + modelData.selfReactedEvent);
|
||||
room.input.reaction(reactionFlow.eventId, modelData.key);
|
||||
}
|
||||
Component.onCompleted: {
|
||||
ToolTip.text = Qt.binding(function() {
|
||||
if (textMetrics.elidedText === textMetrics.text) {
|
||||
return modelData.users;
|
||||
}
|
||||
return modelData.displayKey + "\n" + modelData.users;
|
||||
})
|
||||
}
|
||||
ToolTip.visible: hovered
|
||||
hoverEnabled: true
|
||||
implicitHeight: contentItem.childrenRect.height
|
||||
implicitWidth: contentItem.childrenRect.width + contentItem.leftPadding * 2
|
||||
|
||||
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 {
|
||||
anchors.centerIn: parent
|
||||
spacing: reactionText.implicitHeight / 4
|
||||
leftPadding: reactionText.implicitHeight / 2
|
||||
rightPadding: reactionText.implicitHeight / 2
|
||||
spacing: reactionText.implicitHeight / 4
|
||||
|
||||
TextMetrics {
|
||||
id: textMetrics
|
||||
|
||||
font.family: Settings.emojiFont
|
||||
elide: Text.ElideRight
|
||||
elideWidth: 150
|
||||
font.family: Settings.emojiFont
|
||||
text: modelData.displayKey
|
||||
}
|
||||
|
||||
Text {
|
||||
id: reactionText
|
||||
|
||||
anchors.baseline: reactionCounter.baseline
|
||||
color: reaction.hovered ? timelineRoot.palette.highlight : timelineRoot.palette.text
|
||||
font.family: Settings.emojiFont
|
||||
maximumLineCount: 1
|
||||
text: {
|
||||
// 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) {
|
||||
|
@ -73,42 +67,34 @@ Flow {
|
|||
}
|
||||
return textMetrics.elidedText;
|
||||
}
|
||||
font.family: Settings.emojiFont
|
||||
color: reaction.hovered ? timelineRoot.palette.highlight : timelineRoot.palette.text
|
||||
maximumLineCount: 1
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: divider
|
||||
|
||||
color: (reaction.hovered || modelData.selfReactedEvent !== '') ? timelineRoot.palette.highlight : timelineRoot.palette.text
|
||||
height: Math.floor(reactionCounter.implicitHeight * 1.4)
|
||||
width: 1
|
||||
color: (reaction.hovered || modelData.selfReactedEvent !== '') ? timelineRoot.palette.highlight : timelineRoot.palette.text
|
||||
}
|
||||
|
||||
Text {
|
||||
id: reactionCounter
|
||||
|
||||
anchors.verticalCenter: divider.verticalCenter
|
||||
text: modelData.count
|
||||
font: reaction.font
|
||||
color: reaction.hovered ? timelineRoot.palette.highlight : timelineRoot.palette.text
|
||||
font: reaction.font
|
||||
text: modelData.count
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
ToolTip.text = Qt.binding(function () {
|
||||
if (textMetrics.elidedText === textMetrics.text) {
|
||||
return modelData.users;
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
anchors.centerIn: parent
|
||||
implicitWidth: reaction.implicitWidth
|
||||
implicitHeight: reaction.implicitHeight
|
||||
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
|
||||
border.width: 1
|
||||
radius: reaction.height / 2
|
||||
return modelData.displayKey + "\n" + modelData.users;
|
||||
});
|
||||
}
|
||||
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: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import "./delegates/"
|
||||
import "delegates"
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.3
|
||||
import QtQuick.Layouts 1.2
|
||||
|
@ -11,76 +9,71 @@ import im.nheko
|
|||
|
||||
Rectangle {
|
||||
id: replyPopup
|
||||
|
||||
Layout.fillWidth: true
|
||||
visible: room && (room.reply || room.edit)
|
||||
color: timelineRoot.palette.window
|
||||
// Height of child, plus margins, plus border
|
||||
implicitHeight: (room && room.reply ? replyPreview.height : closeEditButton.height) + Nheko.paddingSmall
|
||||
color: timelineRoot.palette.window
|
||||
visible: room && (room.reply || room.edit)
|
||||
z: 3
|
||||
|
||||
Reply {
|
||||
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.leftMargin: replyPopup.width < 450 ? Nheko.paddingSmall : (CallManager.callsSupported ? 2 * (22 + 16) : 1 * (22 + 16))
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: replyPopup.width < 450 ? 2 * (22 + 16) : 3 * (22 + 16)
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: Nheko.paddingSmall
|
||||
userColor: TimelineManager.userColor(modelData.userId, timelineRoot.palette.window)
|
||||
blurhash: modelData.blurhash ?? ""
|
||||
body: modelData.body ?? ""
|
||||
formattedBody: modelData.formattedBody ?? ""
|
||||
encryptionError: modelData.encryptionError ?? 0
|
||||
eventId: modelData.eventId ?? ""
|
||||
filename: modelData.filename ?? ""
|
||||
filesize: modelData.filesize ?? ""
|
||||
formattedBody: modelData.formattedBody ?? ""
|
||||
isOnlyEmoji: modelData.isOnlyEmoji ?? false
|
||||
originalWidth: modelData.originalWidth ?? 0
|
||||
proportionalHeight: modelData.proportionalHeight ?? 1
|
||||
type: modelData.type ?? MtxEvent.UnknownMessage
|
||||
typeString: modelData.typeString ?? ""
|
||||
url: modelData.url ?? ""
|
||||
originalWidth: modelData.originalWidth ?? 0
|
||||
isOnlyEmoji: modelData.isOnlyEmoji ?? false
|
||||
userColor: TimelineManager.userColor(modelData.userId, timelineRoot.palette.window)
|
||||
userId: modelData.userId ?? ""
|
||||
userName: modelData.userName ?? ""
|
||||
encryptionError: modelData.encryptionError ?? 0
|
||||
visible: room && room.reply
|
||||
width: parent.width
|
||||
}
|
||||
|
||||
ImageButton {
|
||||
id: closeReplyButton
|
||||
|
||||
visible: room && room.reply
|
||||
ToolTip.text: qsTr("Close")
|
||||
ToolTip.visible: closeReplyButton.hovered
|
||||
anchors.margins: Nheko.paddingSmall
|
||||
anchors.right: replyPreview.right
|
||||
anchors.top: replyPreview.top
|
||||
anchors.margins: Nheko.paddingSmall
|
||||
hoverEnabled: true
|
||||
width: 16
|
||||
height: 16
|
||||
hoverEnabled: true
|
||||
image: ":/icons/icons/ui/dismiss.svg"
|
||||
ToolTip.visible: closeReplyButton.hovered
|
||||
ToolTip.text: qsTr("Close")
|
||||
visible: room && room.reply
|
||||
width: 16
|
||||
|
||||
onClicked: room.reply = undefined
|
||||
}
|
||||
|
||||
ImageButton {
|
||||
id: closeEditButton
|
||||
|
||||
visible: room && room.edit
|
||||
anchors.right: parent.right
|
||||
ToolTip.text: qsTr("Cancel Edit")
|
||||
ToolTip.visible: closeEditButton.hovered
|
||||
anchors.margins: 8
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
height: 22
|
||||
hoverEnabled: true
|
||||
image: ":/icons/icons/ui/dismiss_edit.svg"
|
||||
visible: room && room.edit
|
||||
width: 22
|
||||
height: 22
|
||||
ToolTip.visible: closeEditButton.hovered
|
||||
ToolTip.text: qsTr("Cancel Edit")
|
||||
|
||||
onClicked: room.edit = undefined
|
||||
}
|
||||
|
||||
}
|
||||
|
|
268
qml/Root.qml
268
qml/Root.qml
|
@ -1,15 +1,13 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import "./delegates"
|
||||
import "./device-verification"
|
||||
import "./dialogs"
|
||||
import "./emoji"
|
||||
import "./pages"
|
||||
import "./voip"
|
||||
import "./ui"
|
||||
import "delegates"
|
||||
import "device-verification"
|
||||
import "dialogs"
|
||||
import "emoji"
|
||||
import "pages"
|
||||
import "voip"
|
||||
import "ui"
|
||||
import Qt.labs.platform 1.1 as Platform
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
|
@ -19,6 +17,15 @@ import im.nheko
|
|||
|
||||
Pane {
|
||||
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
|
||||
padding: 0
|
||||
|
@ -33,180 +40,130 @@ Pane {
|
|||
// running: true
|
||||
// repeat: true
|
||||
//}
|
||||
|
||||
EmojiPicker {
|
||||
id: emojiPopup
|
||||
|
||||
colors: palette
|
||||
model: TimelineManager.completerFor("allemoji", "")
|
||||
}
|
||||
|
||||
Component {
|
||||
id: userProfileComponent
|
||||
|
||||
UserProfile {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Component {
|
||||
id: roomSettingsComponent
|
||||
|
||||
RoomSettings {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Component {
|
||||
id: roomMembersComponent
|
||||
|
||||
RoomMembers {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Component {
|
||||
id: mobileCallInviteDialog
|
||||
|
||||
CallInvite {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Component {
|
||||
id: quickSwitcherComponent
|
||||
|
||||
QuickSwitcher {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Component {
|
||||
id: deviceVerificationDialog
|
||||
|
||||
DeviceVerification {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Component {
|
||||
id: inviteDialog
|
||||
|
||||
InviteDialog {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Component {
|
||||
id: packSettingsComponent
|
||||
|
||||
ImagePackSettingsDialog {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Component {
|
||||
id: readReceiptsDialog
|
||||
|
||||
ReadReceipts {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Component {
|
||||
id: rawMessageDialog
|
||||
|
||||
RawMessageDialog {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Component {
|
||||
id: logoutDialog
|
||||
|
||||
LogoutDialog {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Component {
|
||||
id: joinRoomDialog
|
||||
|
||||
JoinRoomDialog {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Component {
|
||||
id: leaveRoomComponent
|
||||
|
||||
LeaveRoomDialog {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Component {
|
||||
id: imageOverlay
|
||||
|
||||
ImageOverlay {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Component {
|
||||
id: userSettingsPage
|
||||
|
||||
UserSettingsPage {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Shortcut {
|
||||
sequence: StandardKey.Quit
|
||||
|
||||
onActivated: Qt.quit()
|
||||
}
|
||||
|
||||
Shortcut {
|
||||
sequence: "Ctrl+K"
|
||||
|
||||
onActivated: {
|
||||
var quickSwitch = quickSwitcherComponent.createObject(timelineRoot);
|
||||
quickSwitch.open();
|
||||
destroyOnClosed(quickSwitch);
|
||||
}
|
||||
}
|
||||
|
||||
Shortcut {
|
||||
// Add alternative shortcut, because sometimes Alt+A is stolen by the TextEdit
|
||||
sequences: ["Alt+A", "Ctrl+Shift+A"]
|
||||
|
||||
onActivated: Rooms.nextRoomWithActivity()
|
||||
}
|
||||
|
||||
Shortcut {
|
||||
sequence: "Ctrl+Down"
|
||||
|
||||
onActivated: Rooms.nextRoom()
|
||||
}
|
||||
|
||||
Shortcut {
|
||||
sequence: "Ctrl+Up"
|
||||
|
||||
onActivated: Rooms.previousRoom()
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onOpenJoinRoomDialog() {
|
||||
var dialog = joinRoomDialog.createObject(timelineRoot);
|
||||
dialog.show();
|
||||
destroyOnClose(dialog);
|
||||
}
|
||||
function onOpenLogoutDialog() {
|
||||
var dialog = logoutDialog.createObject(timelineRoot);
|
||||
dialog.open();
|
||||
destroyOnClose(dialog);
|
||||
}
|
||||
|
||||
function onOpenJoinRoomDialog() {
|
||||
var dialog = joinRoomDialog.createObject(timelineRoot);
|
||||
dialog.show();
|
||||
destroyOnClose(dialog);
|
||||
}
|
||||
|
||||
target: Nheko
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onNewDeviceVerificationRequest(flow) {
|
||||
var dialog = deviceVerificationDialog.createObject(timelineRoot, {
|
||||
|
@ -218,51 +175,7 @@ Pane {
|
|||
|
||||
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 {
|
||||
function onOpenProfile(profile) {
|
||||
var userProfile = userProfileComponent.createObject(timelineRoot, {
|
||||
"profile": profile
|
||||
});
|
||||
userProfile.show();
|
||||
destroyOnClose(userProfile);
|
||||
}
|
||||
|
||||
function onShowImagePackSettings(room, packlist) {
|
||||
var packSet = packSettingsComponent.createObject(timelineRoot, {
|
||||
"room": room,
|
||||
"packlist": packlist
|
||||
});
|
||||
packSet.show();
|
||||
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,
|
||||
|
@ -272,7 +185,6 @@ Pane {
|
|||
dialog.show();
|
||||
destroyOnClose(dialog);
|
||||
}
|
||||
|
||||
function onOpenLeaveRoomDialog(roomid, reason) {
|
||||
var dialog = leaveRoomComponent.createObject(timelineRoot, {
|
||||
"roomId": roomid,
|
||||
|
@ -281,7 +193,28 @@ Pane {
|
|||
dialog.open();
|
||||
destroyOnClose(dialog);
|
||||
}
|
||||
|
||||
function onOpenProfile(profile) {
|
||||
var userProfile = userProfileComponent.createObject(timelineRoot, {
|
||||
"profile": profile
|
||||
});
|
||||
userProfile.show();
|
||||
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,
|
||||
|
@ -293,10 +226,17 @@ Pane {
|
|||
dialog.showFullScreen();
|
||||
destroyOnClose(dialog);
|
||||
}
|
||||
function onShowImagePackSettings(room, packlist) {
|
||||
var packSet = packSettingsComponent.createObject(timelineRoot, {
|
||||
"room": room,
|
||||
"packlist": packlist
|
||||
});
|
||||
packSet.show();
|
||||
destroyOnClose(packSet);
|
||||
}
|
||||
|
||||
target: TimelineManager
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onNewInviteState() {
|
||||
if (CallManager.haveCallInvite && Settings.mobileMode) {
|
||||
|
@ -308,144 +248,122 @@ Pane {
|
|||
|
||||
target: CallManager
|
||||
}
|
||||
|
||||
SelfVerificationCheck {
|
||||
}
|
||||
|
||||
InputDialog {
|
||||
id: uiaPassPrompt
|
||||
|
||||
echoMode: TextInput.Password
|
||||
title: UIA.title
|
||||
prompt: qsTr("Please enter your login password to continue:")
|
||||
onAccepted: (t) => {
|
||||
title: UIA.title
|
||||
|
||||
onAccepted: t => {
|
||||
return UIA.continuePassword(t);
|
||||
}
|
||||
}
|
||||
|
||||
InputDialog {
|
||||
id: uiaEmailPrompt
|
||||
|
||||
title: UIA.title
|
||||
prompt: qsTr("Please enter a valid email address to continue:")
|
||||
onAccepted: (t) => {
|
||||
title: UIA.title
|
||||
|
||||
onAccepted: t => {
|
||||
return UIA.continueEmail(t);
|
||||
}
|
||||
}
|
||||
|
||||
PhoneNumberInputDialog {
|
||||
id: uiaPhoneNumberPrompt
|
||||
|
||||
title: UIA.title
|
||||
prompt: qsTr("Please enter a valid phone number to continue:")
|
||||
title: UIA.title
|
||||
|
||||
onAccepted: (p, t) => {
|
||||
return UIA.continuePhoneNumber(p, t);
|
||||
}
|
||||
}
|
||||
|
||||
InputDialog {
|
||||
id: uiaTokenPrompt
|
||||
|
||||
title: UIA.title
|
||||
prompt: qsTr("Please enter the token, which has been sent to you:")
|
||||
onAccepted: (t) => {
|
||||
title: UIA.title
|
||||
|
||||
onAccepted: t => {
|
||||
return UIA.submit3pidToken(t);
|
||||
}
|
||||
}
|
||||
|
||||
Platform.MessageDialog {
|
||||
id: uiaErrorDialog
|
||||
|
||||
buttons: Platform.MessageDialog.Ok
|
||||
}
|
||||
|
||||
Platform.MessageDialog {
|
||||
id: uiaConfirmationLinkDialog
|
||||
|
||||
buttons: Platform.MessageDialog.Ok
|
||||
text: qsTr("Wait for the confirmation link to arrive, then continue.")
|
||||
|
||||
onAccepted: UIA.continue3pidReceived()
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onPassword() {
|
||||
console.log("UIA: password needed");
|
||||
uiaPassPrompt.show();
|
||||
}
|
||||
|
||||
function onEmail() {
|
||||
uiaEmailPrompt.show();
|
||||
}
|
||||
|
||||
function onPhoneNumber() {
|
||||
uiaPhoneNumberPrompt.show();
|
||||
}
|
||||
|
||||
function onPrompt3pidToken() {
|
||||
uiaTokenPrompt.show();
|
||||
}
|
||||
|
||||
function onConfirm3pidToken() {
|
||||
uiaConfirmationLinkDialog.open();
|
||||
}
|
||||
|
||||
function onEmail() {
|
||||
uiaEmailPrompt.show();
|
||||
}
|
||||
function onError(msg) {
|
||||
uiaErrorDialog.text = msg;
|
||||
uiaErrorDialog.open();
|
||||
}
|
||||
function onPassword() {
|
||||
console.log("UIA: password needed");
|
||||
uiaPassPrompt.show();
|
||||
}
|
||||
function onPhoneNumber() {
|
||||
uiaPhoneNumberPrompt.show();
|
||||
}
|
||||
function onPrompt3pidToken() {
|
||||
uiaTokenPrompt.show();
|
||||
}
|
||||
|
||||
target: UIA
|
||||
}
|
||||
|
||||
StackView {
|
||||
id: mainWindow
|
||||
|
||||
anchors.fill: parent
|
||||
initialItem: welcomePage
|
||||
}
|
||||
|
||||
Component {
|
||||
id: welcomePage
|
||||
|
||||
WelcomePage {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: chatPage
|
||||
|
||||
ChatPage {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: loginPage
|
||||
|
||||
LoginPage {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: registerPage
|
||||
|
||||
RegisterPage {
|
||||
}
|
||||
}
|
||||
|
||||
Snackbar { id: snackbar }
|
||||
|
||||
Snackbar {
|
||||
id: snackbar
|
||||
}
|
||||
Connections {
|
||||
function onSwitchToChatPage() {
|
||||
mainWindow.replace(null, chatPage);
|
||||
}
|
||||
function onSwitchToLoginPage(error) {
|
||||
mainWindow.replace(welcomePage, {}, loginPage, {"error": error}, StackView.PopTransition);
|
||||
}
|
||||
function onShowNotification(msg) {
|
||||
snackbar.showNotification(msg);
|
||||
console.log("New snack: " + msg);
|
||||
}
|
||||
target: MainWindow
|
||||
function onSwitchToChatPage() {
|
||||
mainWindow.replace(null, chatPage);
|
||||
}
|
||||
function onSwitchToLoginPage(error) {
|
||||
mainWindow.replace(welcomePage, {}, loginPage, {
|
||||
"error": error
|
||||
}, StackView.PopTransition);
|
||||
}
|
||||
|
||||
target: MainWindow
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import "./components/"
|
||||
import "components"
|
||||
import Qt.labs.platform 1.1 as P
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
|
@ -11,68 +9,61 @@ import QtQuick.Layouts 1.3
|
|||
import im.nheko
|
||||
|
||||
Item {
|
||||
visible: false
|
||||
enabled: false
|
||||
visible: false
|
||||
|
||||
Dialog {
|
||||
id: showRecoverKeyDialog
|
||||
|
||||
property string recoveryKey: ""
|
||||
|
||||
parent: Overlay.overlay
|
||||
anchors.centerIn: parent
|
||||
height: content.height + implicitFooterHeight + implicitHeaderHeight
|
||||
width: content.width
|
||||
padding: 0
|
||||
modal: true
|
||||
standardButtons: Dialog.Ok
|
||||
closePolicy: Popup.NoAutoClose
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
|
||||
spacing: 0
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
height: content.height + implicitFooterHeight + implicitHeaderHeight
|
||||
modal: true
|
||||
padding: 0
|
||||
parent: Overlay.overlay
|
||||
standardButtons: Dialog.Ok
|
||||
width: content.width
|
||||
|
||||
background: Rectangle {
|
||||
color: timelineRoot.palette.window
|
||||
border.color: Nheko.theme.separator
|
||||
border.width: 1
|
||||
color: timelineRoot.palette.window
|
||||
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 {
|
||||
id: successDialog
|
||||
|
||||
buttons: P.MessageDialog.Ok
|
||||
text: qsTr("Encryption setup successfully")
|
||||
}
|
||||
|
||||
P.MessageDialog {
|
||||
id: failureDialog
|
||||
|
||||
|
@ -81,199 +72,185 @@ Item {
|
|||
buttons: P.MessageDialog.Ok
|
||||
text: qsTr("Failed to setup encryption: %1").arg(errorMessage)
|
||||
}
|
||||
|
||||
MainWindowDialog {
|
||||
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)
|
||||
|
||||
GridLayout {
|
||||
id: grid
|
||||
|
||||
width: bootstrapCrosssigning.useableWidth
|
||||
columnSpacing: 0
|
||||
columns: 2
|
||||
rowSpacing: 0
|
||||
columnSpacing: 0
|
||||
width: bootstrapCrosssigning.useableWidth
|
||||
z: 1
|
||||
|
||||
Label {
|
||||
Layout.margins: Nheko.paddingMedium
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.columnSpan: 2
|
||||
Layout.margins: Nheko.paddingMedium
|
||||
color: timelineRoot.palette.text
|
||||
font.pointSize: fontMetrics.font.pointSize * 2
|
||||
text: qsTr("Setup Encryption")
|
||||
color: timelineRoot.palette.text
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.margins: Nheko.paddingMedium
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
Layout.columnSpan: 2
|
||||
Layout.margins: Nheko.paddingMedium
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.margins: Nheko.paddingMedium
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
Layout.columnSpan: 1
|
||||
Layout.margins: Nheko.paddingMedium
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.margins: Nheko.paddingMedium
|
||||
Layout.preferredHeight: storeSecretsOnline.height
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: Nheko.paddingMedium
|
||||
Layout.preferredHeight: storeSecretsOnline.height
|
||||
|
||||
ToggleButton {
|
||||
id: storeSecretsOnline
|
||||
|
||||
checked: true
|
||||
|
||||
onClicked: console.log("Store secrets toggled: " + checked)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.margins: Nheko.paddingMedium
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
Layout.columnSpan: 1
|
||||
Layout.rowSpan: 2
|
||||
Layout.margins: Nheko.paddingMedium
|
||||
Layout.maximumWidth: Math.floor(grid.width / 2) - Nheko.paddingMedium * 2
|
||||
visible: storeSecretsOnline.checked
|
||||
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.)"
|
||||
Layout.rowSpan: 2
|
||||
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
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.margins: Nheko.paddingMedium
|
||||
Layout.topMargin: Nheko.paddingLarge
|
||||
Layout.preferredHeight: storeSecretsOnline.height
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||
Layout.rowSpan: usePassword.checked ? 1 : 2
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: Nheko.paddingMedium
|
||||
Layout.preferredHeight: storeSecretsOnline.height
|
||||
Layout.rowSpan: usePassword.checked ? 1 : 2
|
||||
Layout.topMargin: Nheko.paddingLarge
|
||||
visible: storeSecretsOnline.checked
|
||||
|
||||
ToggleButton {
|
||||
id: usePassword
|
||||
|
||||
checked: false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MatrixTextField {
|
||||
id: passwordField
|
||||
|
||||
Layout.margins: Nheko.paddingMedium
|
||||
Layout.maximumWidth: Math.floor(grid.width / 2) - Nheko.paddingMedium * 2
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||
Layout.columnSpan: 1
|
||||
Layout.fillWidth: true
|
||||
visible: storeSecretsOnline.checked && usePassword.checked
|
||||
echoMode: TextInput.Password
|
||||
}
|
||||
|
||||
Label {
|
||||
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.columnSpan: 1
|
||||
Layout.margins: Nheko.paddingMedium
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.margins: Nheko.paddingMedium
|
||||
Layout.preferredHeight: storeSecretsOnline.height
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: Nheko.paddingMedium
|
||||
Layout.preferredHeight: storeSecretsOnline.height
|
||||
|
||||
ToggleButton {
|
||||
id: useOnlineKeyBackup
|
||||
|
||||
checked: true
|
||||
|
||||
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 {
|
||||
id: verifyMasterKey
|
||||
|
||||
standardButtons: Dialog.Cancel
|
||||
|
||||
GridLayout {
|
||||
id: masterGrid
|
||||
|
||||
width: verifyMasterKey.useableWidth
|
||||
columns: 1
|
||||
width: verifyMasterKey.useableWidth
|
||||
z: 1
|
||||
|
||||
Label {
|
||||
Layout.margins: Nheko.paddingMedium
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.margins: Nheko.paddingMedium
|
||||
color: timelineRoot.palette.text
|
||||
//Layout.columnSpan: 2
|
||||
font.pointSize: fontMetrics.font.pointSize * 2
|
||||
text: qsTr("Activate Encryption")
|
||||
color: timelineRoot.palette.text
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.margins: Nheko.paddingMedium
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
Layout.margins: Nheko.paddingMedium
|
||||
//Layout.columnSpan: 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
|
||||
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
|
||||
}
|
||||
|
||||
FlatButton {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: qsTr("verify")
|
||||
|
||||
onClicked: {
|
||||
SelfVerificationStatus.verifyMasterKey();
|
||||
verifyMasterKey.close();
|
||||
}
|
||||
}
|
||||
|
||||
FlatButton {
|
||||
visible: SelfVerificationStatus.hasSSSS
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: qsTr("enter passphrase")
|
||||
visible: SelfVerificationStatus.hasSSSS
|
||||
|
||||
onClicked: {
|
||||
SelfVerificationStatus.verifyMasterKeyWithPassphrase();
|
||||
verifyMasterKey.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onSetupCompleted() {
|
||||
successDialog.open();
|
||||
}
|
||||
function onSetupFailed(m) {
|
||||
failureDialog.errorMessage = m;
|
||||
failureDialog.open();
|
||||
}
|
||||
function onShowRecoveryKey(key) {
|
||||
showRecoverKeyDialog.recoveryKey = key;
|
||||
showRecoverKeyDialog.open();
|
||||
}
|
||||
function onStatusChanged() {
|
||||
console.log("STATUS CHANGED: " + SelfVerificationStatus.status);
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 2.1
|
||||
import im.nheko
|
||||
|
@ -10,15 +8,9 @@ import im.nheko
|
|||
ImageButton {
|
||||
id: indicator
|
||||
|
||||
required property int status
|
||||
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: {
|
||||
switch (status) {
|
||||
case MtxEvent.Failed:
|
||||
|
@ -33,11 +25,11 @@ ImageButton {
|
|||
return "";
|
||||
}
|
||||
}
|
||||
onClicked: {
|
||||
if (status == MtxEvent.Read)
|
||||
room.showReadReceipts(eventId);
|
||||
|
||||
}
|
||||
ToolTip.visible: hovered && status != MtxEvent.Empty
|
||||
changeColorOnHover: (status == MtxEvent.Read)
|
||||
cursor: (status == MtxEvent.Read) ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
height: 16
|
||||
hoverEnabled: true
|
||||
image: {
|
||||
switch (status) {
|
||||
case MtxEvent.Failed:
|
||||
|
@ -52,4 +44,10 @@ ImageButton {
|
|||
return "";
|
||||
}
|
||||
}
|
||||
width: 16
|
||||
|
||||
onClicked: {
|
||||
if (status == MtxEvent.Read)
|
||||
room.showReadReceipts(eventId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import "./delegates"
|
||||
import "./emoji"
|
||||
import "delegates"
|
||||
import "emoji"
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.3
|
||||
import QtQuick.Layouts 1.2
|
||||
|
@ -14,69 +12,42 @@ import im.nheko
|
|||
AbstractButton {
|
||||
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 body
|
||||
required property string formattedBody
|
||||
required property string callType
|
||||
required property int duration
|
||||
required property int encryptionError
|
||||
required property string eventId
|
||||
required property string filename
|
||||
required property string filesize
|
||||
required property string url
|
||||
required property string thumbnailUrl
|
||||
required property bool isOnlyEmoji
|
||||
required property bool isSender
|
||||
required property bool isEncrypted
|
||||
required property string formattedBody
|
||||
required property bool isEditable
|
||||
required property bool isEdited
|
||||
required property bool isEncrypted
|
||||
required property bool isOnlyEmoji
|
||||
required property bool isSender
|
||||
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 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 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
|
||||
|
||||
hoverEnabled: true
|
||||
|
||||
width: parent.width
|
||||
height: row.height + (reactionRow.height > 0 ? reactionRow.height - 2 : 0)
|
||||
hoverEnabled: true
|
||||
width: parent.width
|
||||
|
||||
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 {
|
||||
name: "dragging"
|
||||
when: draghandler.active
|
||||
|
@ -84,212 +55,234 @@ AbstractButton {
|
|||
transitions: Transition {
|
||||
from: "dragging"
|
||||
to: ""
|
||||
|
||||
PropertyAnimation {
|
||||
target: r
|
||||
properties: "x"
|
||||
easing.type: Easing.InOutQuad
|
||||
to: 0
|
||||
duration: 100
|
||||
easing.type: Easing.InOutQuad
|
||||
properties: "x"
|
||||
target: r
|
||||
to: 0
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
let link = contentItem.child.linkAt != undefined && contentItem.child.linkAt(pressX - row.x - msg.x, pressY - row.y - msg.y - contentItem.y);
|
||||
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 {
|
||||
id: row
|
||||
property bool bubbleOnRight : isSender && Settings.bubbles
|
||||
anchors.leftMargin: isStateEvent || Settings.smallAvatars? 0 : Nheko.avatarSize+8 // align bubble with section header
|
||||
anchors.left: isStateEvent? undefined : (bubbleOnRight? undefined : parent.left)
|
||||
anchors.right: isStateEvent? undefined: (bubbleOnRight? parent.right : undefined)
|
||||
anchors.horizontalCenter: isStateEvent? parent.horizontalCenter : undefined
|
||||
property int maxWidth: (parent.width-(Settings.smallAvatars || isStateEvent? 0 : Nheko.avatarSize+8))*(Settings.bubbles && !isStateEvent? 0.9 : 1)
|
||||
width: Settings.bubbles? Math.min(maxWidth,Math.max(reply.implicitWidth+8,contentItem.implicitWidth+metadata.width+20)) : maxWidth
|
||||
height: msg.height+msg.anchors.margins*2
|
||||
anchors.fill: parent
|
||||
color: (Settings.messageHoverHighlight && hovered) ? timelineRoot.palette.alternateBase : "transparent"
|
||||
|
||||
// this looks better without margins
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.RightButton
|
||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad
|
||||
gesturePolicy: TapHandler.ReleaseWithinBounds
|
||||
|
||||
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 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"
|
||||
height: msg.height + msg.anchors.margins * 2
|
||||
radius: 4
|
||||
width: Settings.bubbles ? Math.min(maxWidth, Math.max(reply.implicitWidth + 8, contentItem.implicitWidth + metadata.width + 20)) : maxWidth
|
||||
|
||||
GridLayout {
|
||||
anchors {
|
||||
left: parent.left
|
||||
top: parent.top
|
||||
right: parent.right
|
||||
margins: (Settings.bubbles && ! isStateEvent)? 4 : 2
|
||||
leftMargin: 4
|
||||
}
|
||||
id: msg
|
||||
rowSpacing: 0
|
||||
columnSpacing: 2
|
||||
columns: Settings.bubbles ? 1 : 2
|
||||
rowSpacing: 0
|
||||
rows: Settings.bubbles ? 3 : 2
|
||||
|
||||
anchors {
|
||||
left: parent.left
|
||||
leftMargin: 4
|
||||
margins: (Settings.bubbles && !isStateEvent) ? 4 : 2
|
||||
right: parent.right
|
||||
top: parent.top
|
||||
}
|
||||
|
||||
// fancy reply, if this is a 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
|
||||
|
||||
function fromModel(role) {
|
||||
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) ?? ""
|
||||
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) ?? ""
|
||||
filename: r.relatedEventCacheBuster, fromModel(Room.Filename) ?? ""
|
||||
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
|
||||
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
|
||||
typeString: r.relatedEventCacheBuster, fromModel(Room.TypeString) ?? ""
|
||||
url: r.relatedEventCacheBuster, fromModel(Room.Url) ?? ""
|
||||
originalWidth: r.relatedEventCacheBuster, fromModel(Room.OriginalWidth) ?? 0
|
||||
isOnlyEmoji: r.relatedEventCacheBuster, fromModel(Room.IsOnlyEmoji) ?? false
|
||||
isStateEvent: r.relatedEventCacheBuster, fromModel(Room.IsStateEvent) ?? false
|
||||
userColor: r.relatedEventCacheBuster, TimelineManager.userColor(userId, timelineRoot.palette.base)
|
||||
userId: r.relatedEventCacheBuster, fromModel(Room.UserId) ?? ""
|
||||
userName: r.relatedEventCacheBuster, fromModel(Room.UserName) ?? ""
|
||||
thumbnailUrl: r.relatedEventCacheBuster, fromModel(Room.ThumbnailUrl) ?? ""
|
||||
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
|
||||
visible: replyTo
|
||||
}
|
||||
|
||||
// actual message content
|
||||
MessageDelegate {
|
||||
Layout.row: 1
|
||||
id: contentItem
|
||||
Layout.column: 0
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: height
|
||||
id: contentItem
|
||||
|
||||
Layout.row: 1
|
||||
blurhash: r.blurhash
|
||||
body: r.body
|
||||
formattedBody: r.formattedBody
|
||||
callType: r.callType
|
||||
duration: r.duration
|
||||
encryptionError: r.encryptionError
|
||||
eventId: r.eventId
|
||||
filename: r.filename
|
||||
filesize: r.filesize
|
||||
formattedBody: r.formattedBody
|
||||
isOnlyEmoji: r.isOnlyEmoji
|
||||
isReply: false
|
||||
isStateEvent: r.isStateEvent
|
||||
metadataWidth: metadata.width
|
||||
originalWidth: r.originalWidth
|
||||
proportionalHeight: r.proportionalHeight
|
||||
relatedEventCacheBuster: r.relatedEventCacheBuster
|
||||
roomName: r.roomName
|
||||
roomTopic: r.roomTopic
|
||||
thumbnailUrl: r.thumbnailUrl
|
||||
type: r.type
|
||||
typeString: r.typeString ?? ""
|
||||
url: r.url
|
||||
thumbnailUrl: r.thumbnailUrl
|
||||
duration: r.duration
|
||||
originalWidth: r.originalWidth
|
||||
isOnlyEmoji: r.isOnlyEmoji
|
||||
isStateEvent: r.isStateEvent
|
||||
userId: r.userId
|
||||
userName: r.userName
|
||||
roomTopic: r.roomTopic
|
||||
roomName: r.roomName
|
||||
callType: r.callType
|
||||
encryptionError: r.encryptionError
|
||||
relatedEventCacheBuster: r.relatedEventCacheBuster
|
||||
isReply: false
|
||||
metadataWidth: metadata.width
|
||||
}
|
||||
|
||||
Row {
|
||||
id: metadata
|
||||
Layout.column: Settings.bubbles? 0 : 1
|
||||
Layout.row: Settings.bubbles? 2 : 0
|
||||
Layout.rowSpan: Settings.bubbles? 1 : 2
|
||||
Layout.bottomMargin: -2
|
||||
Layout.topMargin: (contentItem.fitsMetadata && Settings.bubbles)? -height-Layout.bottomMargin : 0
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignRight
|
||||
Layout.preferredWidth: implicitWidth
|
||||
visible: !isStateEvent
|
||||
spacing: 2
|
||||
|
||||
property double scaling: Settings.bubbles? 0.75 : 1
|
||||
|
||||
property int iconSize: Math.floor(fontMetrics.ascent * scaling)
|
||||
property double scaling: Settings.bubbles ? 0.75 : 1
|
||||
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignRight
|
||||
Layout.bottomMargin: -2
|
||||
Layout.column: Settings.bubbles ? 0 : 1
|
||||
Layout.preferredWidth: implicitWidth
|
||||
Layout.row: Settings.bubbles ? 2 : 0
|
||||
Layout.rowSpan: Settings.bubbles ? 1 : 2
|
||||
Layout.topMargin: (contentItem.fitsMetadata && Settings.bubbles) ? -height - Layout.bottomMargin : 0
|
||||
spacing: 2
|
||||
visible: !isStateEvent
|
||||
|
||||
StatusIndicator {
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||
height: parent.iconSize
|
||||
width: parent.iconSize
|
||||
status: r.status
|
||||
eventId: r.eventId
|
||||
anchors.verticalCenter: ts.verticalCenter
|
||||
}
|
||||
|
||||
Image {
|
||||
visible: isEdited || eventId == chat.model.edit
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||
eventId: r.eventId
|
||||
height: parent.iconSize
|
||||
status: r.status
|
||||
width: parent.iconSize
|
||||
sourceSize.width: parent.iconSize * Screen.devicePixelRatio
|
||||
sourceSize.height: parent.iconSize * Screen.devicePixelRatio
|
||||
source: "image://colorimage/:/icons/icons/ui/edit.svg?" + ((eventId == chat.model.edit) ? timelineRoot.palette.highlight : timelineRoot.palette.placeholderText)
|
||||
ToolTip.visible: editHovered.hovered
|
||||
}
|
||||
Image {
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||
ToolTip.delay: Nheko.tooltipDelay
|
||||
ToolTip.text: qsTr("Edited")
|
||||
ToolTip.visible: editHovered.hovered
|
||||
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 {
|
||||
id: editHovered
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
EncryptionIndicator {
|
||||
visible: room.isEncrypted
|
||||
encrypted: isEncrypted
|
||||
trust: trustlevel
|
||||
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
|
||||
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 {
|
||||
id: ts
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||
Layout.preferredWidth: implicitWidth
|
||||
text: timestamp.toLocaleTimeString(Locale.ShortFormat)
|
||||
color: timelineRoot.palette.inactive.text
|
||||
ToolTip.visible: ma.hovered
|
||||
ToolTip.delay: Nheko.tooltipDelay
|
||||
ToolTip.text: Qt.formatDateTime(timestamp, Qt.DefaultLocaleLongDate)
|
||||
ToolTip.visible: ma.hovered
|
||||
color: timelineRoot.palette.inactive.text
|
||||
font.pointSize: fontMetrics.font.pointSize * parent.scaling
|
||||
text: timestamp.toLocaleTimeString(Locale.ShortFormat)
|
||||
|
||||
HoverHandler {
|
||||
id: ma
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reactions {
|
||||
id: reactionRow
|
||||
eventId: r.eventId
|
||||
layoutDirection: row.bubbleOnRight ? Qt.RightToLeft : Qt.LeftToRight
|
||||
reactions: r.reactions
|
||||
width: row.maxWidth
|
||||
|
||||
anchors {
|
||||
top: row.bottom
|
||||
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
|
||||
top: row.bottom
|
||||
topMargin: -2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import "./components"
|
||||
import "./delegates"
|
||||
import "./device-verification"
|
||||
import "./emoji"
|
||||
import "./ui"
|
||||
import "./voip"
|
||||
import "components"
|
||||
import "delegates"
|
||||
import "device-verification"
|
||||
import "emoji"
|
||||
import "ui"
|
||||
import "voip"
|
||||
import Qt.labs.platform 1.1 as Platform
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.5
|
||||
|
@ -23,55 +21,50 @@ Item {
|
|||
property var room: null
|
||||
property var roomPreview: null
|
||||
property bool showBackButton: false
|
||||
|
||||
clip: true
|
||||
|
||||
Shortcut {
|
||||
sequence: StandardKey.Close
|
||||
|
||||
onActivated: Rooms.resetCurrentRoom()
|
||||
}
|
||||
|
||||
Label {
|
||||
visible: !room && !TimelineManager.isInitialSync && (!roomPreview || !roomPreview.roomid)
|
||||
anchors.centerIn: parent
|
||||
text: qsTr("No room open")
|
||||
font.pointSize: 24
|
||||
color: timelineRoot.palette.text
|
||||
font.pointSize: 24
|
||||
text: qsTr("No room open")
|
||||
visible: !room && !TimelineManager.isInitialSync && (!roomPreview || !roomPreview.roomid)
|
||||
}
|
||||
|
||||
Spinner {
|
||||
visible: TimelineManager.isInitialSync
|
||||
anchors.centerIn: parent
|
||||
foreground: timelineRoot.palette.mid
|
||||
running: TimelineManager.isInitialSync
|
||||
// height is somewhat arbitrary here... don't set width because width scales w/ height
|
||||
height: parent.height / 16
|
||||
running: TimelineManager.isInitialSync
|
||||
visible: TimelineManager.isInitialSync
|
||||
z: 3
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: timelineLayout
|
||||
|
||||
visible: room != null && !room.isSpace
|
||||
enabled: visible
|
||||
anchors.fill: parent
|
||||
enabled: visible
|
||||
spacing: 0
|
||||
visible: room != null && !room.isSpace
|
||||
|
||||
TopBar {
|
||||
showBackButton: timelineView.showBackButton
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
color: Nheko.theme.separator
|
||||
height: 1
|
||||
z: 3
|
||||
color: Nheko.theme.separator
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: msgView
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
color: timelineRoot.palette.base
|
||||
|
||||
ColumnLayout {
|
||||
|
@ -80,7 +73,6 @@ Item {
|
|||
|
||||
StackLayout {
|
||||
id: stackLayout
|
||||
|
||||
currentIndex: 0
|
||||
|
||||
Connections {
|
||||
|
@ -90,129 +82,107 @@ Item {
|
|||
|
||||
target: timelineView
|
||||
}
|
||||
|
||||
MessageView {
|
||||
implicitHeight: msgView.height - typingIndicator.height
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: msgView.height - typingIndicator.height
|
||||
}
|
||||
|
||||
Loader {
|
||||
source: CallManager.isOnCall && CallManager.callType != Voip.VOICE ? "voip/VideoCall.qml" : ""
|
||||
|
||||
onLoaded: TimelineManager.setVideoCallItem()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TypingIndicator {
|
||||
id: typingIndicator
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
CallInviteBar {
|
||||
id: callInviteBar
|
||||
|
||||
Layout.fillWidth: true
|
||||
z: 3
|
||||
}
|
||||
|
||||
ActiveCallBar {
|
||||
Layout.fillWidth: true
|
||||
z: 3
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
z: 3
|
||||
height: 1
|
||||
color: Nheko.theme.separator
|
||||
height: 1
|
||||
z: 3
|
||||
}
|
||||
|
||||
|
||||
UploadBox {
|
||||
}
|
||||
|
||||
NotificationWarning {
|
||||
}
|
||||
|
||||
ReplyPopup {
|
||||
}
|
||||
|
||||
MessageInput {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: preview
|
||||
|
||||
property string avatarUrl: room ? room.roomAvatarUrl : (roomPreview ? roomPreview.roomAvatarUrl : "")
|
||||
property string roomId: room ? room.roomId : (roomPreview ? roomPreview.roomid : "")
|
||||
property string roomName: room ? room.roomName : (roomPreview ? roomPreview.roomName : "")
|
||||
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.margins: Nheko.paddingLarge
|
||||
enabled: visible
|
||||
spacing: Nheko.paddingLarge
|
||||
visible: room != null && room.isSpace || roomPreview != null
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
Avatar {
|
||||
url: parent.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||
roomid: parent.roomId
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
displayName: parent.roomName
|
||||
height: 130
|
||||
width: 130
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
enabled: false
|
||||
height: 130
|
||||
roomid: parent.roomId
|
||||
url: parent.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||
width: 130
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: Nheko.paddingMedium
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
spacing: Nheko.paddingMedium
|
||||
|
||||
MatrixText {
|
||||
text: preview.roomName == "" ? qsTr("No preview available") : preview.roomName
|
||||
font.pixelSize: 24
|
||||
text: preview.roomName == "" ? qsTr("No preview available") : preview.roomName
|
||||
}
|
||||
|
||||
ImageButton {
|
||||
ToolTip.text: qsTr("Settings")
|
||||
ToolTip.visible: hovered
|
||||
hoverEnabled: true
|
||||
image: ":/icons/icons/ui/settings.svg"
|
||||
visible: !!room
|
||||
hoverEnabled: true
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: qsTr("Settings")
|
||||
|
||||
onClicked: TimelineManager.openRoomSettings(room.roomId)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
visible: !!room
|
||||
spacing: Nheko.paddingMedium
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
spacing: Nheko.paddingMedium
|
||||
visible: !!room
|
||||
|
||||
MatrixText {
|
||||
text: qsTr("%1 member(s)").arg(room ? room.roomMemberCount : 0)
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
text: qsTr("%1 member(s)").arg(room ? room.roomMemberCount : 0)
|
||||
}
|
||||
|
||||
ImageButton {
|
||||
image: ":/icons/icons/ui/people.svg"
|
||||
hoverEnabled: true
|
||||
ToolTip.visible: hovered
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
|
@ -220,78 +190,71 @@ Item {
|
|||
Layout.rightMargin: Nheko.paddingLarge
|
||||
|
||||
TextArea {
|
||||
text: TimelineManager.escapeEmoji(preview.roomTopic)
|
||||
wrapMode: TextEdit.WordWrap
|
||||
textFormat: TextEdit.RichText
|
||||
readOnly: true
|
||||
background: null
|
||||
selectByMouse: true
|
||||
color: timelineRoot.palette.text
|
||||
horizontalAlignment: TextEdit.AlignHCenter
|
||||
readOnly: true
|
||||
selectByMouse: true
|
||||
text: TimelineManager.escapeEmoji(preview.roomTopic)
|
||||
textFormat: TextEdit.RichText
|
||||
wrapMode: TextEdit.WordWrap
|
||||
|
||||
onLinkActivated: Nheko.openLink(link)
|
||||
|
||||
NhekoCursorShape {
|
||||
anchors.fill: parent
|
||||
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
FlatButton {
|
||||
visible: roomPreview && !roomPreview.isInvite
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: qsTr("join the conversation")
|
||||
visible: roomPreview && !roomPreview.isInvite
|
||||
|
||||
onClicked: Rooms.joinPreview(roomPreview.roomid)
|
||||
}
|
||||
|
||||
FlatButton {
|
||||
visible: roomPreview && roomPreview.isInvite
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: qsTr("accept invite")
|
||||
visible: roomPreview && roomPreview.isInvite
|
||||
|
||||
onClicked: Rooms.acceptInvite(roomPreview.roomid)
|
||||
}
|
||||
|
||||
FlatButton {
|
||||
visible: roomPreview && roomPreview.isInvite
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: qsTr("decline invite")
|
||||
visible: roomPreview && roomPreview.isInvite
|
||||
|
||||
onClicked: Rooms.declineInvite(roomPreview.roomid)
|
||||
}
|
||||
|
||||
Item {
|
||||
visible: room != null
|
||||
Layout.preferredHeight: Math.ceil(fontMetrics.lineSpacing * 2)
|
||||
visible: room != null
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ImageButton {
|
||||
id: backToRoomsButton
|
||||
|
||||
anchors.top: parent.top
|
||||
ToolTip.text: qsTr("Back to room list")
|
||||
ToolTip.visible: hovered
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Nheko.paddingMedium
|
||||
width: Nheko.avatarSize
|
||||
height: Nheko.avatarSize
|
||||
visible: (room == null || room.isSpace) && showBackButton
|
||||
anchors.top: parent.top
|
||||
enabled: visible
|
||||
height: Nheko.avatarSize
|
||||
image: ":/icons/icons/ui/angle-arrow-left.svg"
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: qsTr("Back to room list")
|
||||
visible: (room == null || room.isSpace) && showBackButton
|
||||
width: Nheko.avatarSize
|
||||
|
||||
onClicked: Rooms.resetCurrentRoom()
|
||||
}
|
||||
|
||||
NhekoDropArea {
|
||||
anchors.fill: parent
|
||||
roomid: room ? room.roomId : ""
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onOpenReadReceiptsDialog(rr) {
|
||||
var dialog = readReceiptsDialog.createObject(timelineRoot, {
|
||||
|
@ -301,7 +264,6 @@ Item {
|
|||
dialog.show();
|
||||
timelineRoot.destroyOnClose(dialog);
|
||||
}
|
||||
|
||||
function onShowRawMessageDialog(rawMessage) {
|
||||
var dialog = rawMessageDialog.createObject(timelineRoot, {
|
||||
"rawMessage": rawMessage
|
||||
|
@ -312,5 +274,4 @@ Item {
|
|||
|
||||
target: room
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
|
@ -10,36 +8,31 @@ import im.nheko
|
|||
|
||||
Switch {
|
||||
id: toggleButton
|
||||
|
||||
implicitWidth: indicatorItem.width
|
||||
|
||||
indicator: Item {
|
||||
id: indicatorItem
|
||||
|
||||
implicitWidth: 48
|
||||
implicitHeight: 24
|
||||
implicitWidth: 48
|
||||
y: parent.height / 2 - height / 2
|
||||
|
||||
Rectangle {
|
||||
border.color: "#cccccc"
|
||||
color: toggleButton.checked ? "skyblue" : "grey"
|
||||
height: 3 * parent.height / 4
|
||||
radius: height / 2
|
||||
width: parent.width - height
|
||||
x: radius
|
||||
y: parent.height / 2 - height / 2
|
||||
color: toggleButton.checked ? "skyblue" : "grey"
|
||||
border.color: "#cccccc"
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
x: toggleButton.checked ? parent.width - width : 0
|
||||
y: parent.height / 2 - height / 2
|
||||
width: parent.height
|
||||
border.color: "#ebebeb"
|
||||
color: toggleButton.enabled ? "whitesmoke" : "#cccccc"
|
||||
height: width
|
||||
radius: width / 2
|
||||
color: toggleButton.enabled ? "whitesmoke" : "#cccccc"
|
||||
border.color: "#ebebeb"
|
||||
width: parent.height
|
||||
x: toggleButton.checked ? parent.width - width : 0
|
||||
y: parent.height / 2 - height / 2
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
270
qml/TopBar.qml
270
qml/TopBar.qml
|
@ -1,161 +1,105 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import Qt.labs.platform 1.1 as Platform
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.2
|
||||
import QtQuick.Window 2.15
|
||||
import im.nheko
|
||||
|
||||
import "./delegates"
|
||||
import "delegates"
|
||||
|
||||
Pane {
|
||||
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 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 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
|
||||
implicitHeight: topBarC.height + Nheko.paddingMedium * 2
|
||||
padding: 0
|
||||
z: 3
|
||||
|
||||
padding: 0
|
||||
background: Rectangle {
|
||||
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 {
|
||||
GridLayout {
|
||||
id: topBarC
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: Nheko.paddingMedium
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
columnSpacing: Nheko.paddingSmall
|
||||
rowSpacing: Nheko.paddingSmall
|
||||
|
||||
ImageButton {
|
||||
id: backToRoomsButton
|
||||
|
||||
Layout.column: 0
|
||||
Layout.row: 0
|
||||
Layout.rowSpan: 2
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.column: 0
|
||||
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
|
||||
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
|
||||
visible: showBackButton
|
||||
image: ":/icons/icons/ui/angle-arrow-left.svg"
|
||||
ToolTip.visible: hovered
|
||||
Layout.row: 0
|
||||
Layout.rowSpan: 2
|
||||
ToolTip.text: qsTr("Back to room list")
|
||||
ToolTip.visible: hovered
|
||||
image: ":/icons/icons/ui/angle-arrow-left.svg"
|
||||
visible: showBackButton
|
||||
|
||||
onClicked: Rooms.resetCurrentRoom()
|
||||
}
|
||||
|
||||
Avatar {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.column: 1
|
||||
Layout.row: 0
|
||||
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
|
||||
enabled: false
|
||||
height: Nheko.avatarSize
|
||||
roomid: roomId
|
||||
url: avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||
userid: isDirect ? directChatOtherUserId : ""
|
||||
width: Nheko.avatarSize
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.column: 2
|
||||
Layout.fillWidth: true
|
||||
Layout.row: 0
|
||||
color: timelineRoot.palette.text
|
||||
font.pointSize: fontMetrics.font.pointSize * 1.1
|
||||
text: roomName
|
||||
maximumLineCount: 1
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: fontMetrics.font.pointSize * 1.1
|
||||
maximumLineCount: 1
|
||||
text: roomName
|
||||
textFormat: Text.RichText
|
||||
}
|
||||
|
||||
MatrixText {
|
||||
id: roomTopicC
|
||||
Layout.fillWidth: true
|
||||
Layout.column: 2
|
||||
Layout.row: 1
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumHeight: fontMetrics.lineSpacing * 2 // show 2 lines
|
||||
selectByMouse: false
|
||||
enabled: false
|
||||
Layout.row: 1
|
||||
clip: true
|
||||
enabled: false
|
||||
selectByMouse: false
|
||||
text: roomTopic
|
||||
}
|
||||
|
||||
AbstractButton {
|
||||
Layout.column: 3
|
||||
Layout.row: 0
|
||||
Layout.rowSpan: 2
|
||||
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
|
||||
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
|
||||
|
||||
contentItem: EncryptionIndicator {
|
||||
sourceSize.height: parent.Layout.preferredHeight * Screen.devicePixelRatio
|
||||
sourceSize.width: parent.Layout.preferredWidth * Screen.devicePixelRatio
|
||||
visible: isEncrypted
|
||||
encrypted: isEncrypted
|
||||
trust: trustlevel
|
||||
enabled: false
|
||||
}
|
||||
|
||||
background: null
|
||||
|
||||
Layout.row: 0
|
||||
Layout.rowSpan: 2
|
||||
ToolTip.delay: Nheko.tooltipDelay
|
||||
ToolTip.text: {
|
||||
if (!isEncrypted)
|
||||
return qsTr("This room is not encrypted!");
|
||||
|
||||
switch (trustlevel) {
|
||||
case Crypto.Verified:
|
||||
return qsTr("This room contains only verified devices.");
|
||||
|
@ -166,25 +110,35 @@ Pane {
|
|||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
ImageButton {
|
||||
id: pinButton
|
||||
|
||||
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.column: 4
|
||||
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
|
||||
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
|
||||
image: pinsShown ? ":/icons/icons/ui/pin.svg" : ":/icons/icons/ui/pin-off.svg"
|
||||
ToolTip.visible: hovered
|
||||
Layout.row: 0
|
||||
Layout.rowSpan: 2
|
||||
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: {
|
||||
var ps = Settings.hiddenPins;
|
||||
if (pinsShown) {
|
||||
|
@ -197,156 +151,168 @@ Pane {
|
|||
}
|
||||
Settings.hiddenPins = ps;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ImageButton {
|
||||
id: roomOptionsButton
|
||||
|
||||
visible: !!room
|
||||
Layout.column: 5
|
||||
Layout.row: 0
|
||||
Layout.rowSpan: 2
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.column: 5
|
||||
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
|
||||
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
|
||||
image: ":/icons/icons/ui/options.svg"
|
||||
ToolTip.visible: hovered
|
||||
Layout.row: 0
|
||||
Layout.rowSpan: 2
|
||||
ToolTip.text: qsTr("Room options")
|
||||
ToolTip.visible: hovered
|
||||
image: ":/icons/icons/ui/options.svg"
|
||||
visible: !!room
|
||||
|
||||
onClicked: roomOptionsMenu.open(roomOptionsButton)
|
||||
|
||||
Platform.Menu {
|
||||
id: roomOptionsMenu
|
||||
|
||||
Platform.MenuItem {
|
||||
visible: room ? room.permissions.canInvite() : false
|
||||
text: qsTr("Invite users")
|
||||
visible: room ? room.permissions.canInvite() : false
|
||||
|
||||
onTriggered: TimelineManager.openInviteUsers(roomId)
|
||||
}
|
||||
|
||||
Platform.MenuItem {
|
||||
text: qsTr("Members")
|
||||
|
||||
onTriggered: TimelineManager.openRoomMembers(room)
|
||||
}
|
||||
|
||||
Platform.MenuItem {
|
||||
text: qsTr("Leave room")
|
||||
|
||||
onTriggered: TimelineManager.openLeaveRoomDialog(roomId)
|
||||
}
|
||||
|
||||
Platform.MenuItem {
|
||||
text: qsTr("Settings")
|
||||
|
||||
onTriggered: TimelineManager.openRoomSettings(roomId)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
id: pinnedMessages
|
||||
|
||||
Layout.row: 2
|
||||
Layout.column: 2
|
||||
Layout.columnSpan: 3
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: Math.min(contentHeight, Nheko.avatarSize * 4)
|
||||
|
||||
visible: !!room && room.pinnedMessages.length > 0 && !Settings.hiddenPins.includes(roomId)
|
||||
clip: true
|
||||
|
||||
palette: timelineRoot.palette
|
||||
Layout.row: 2
|
||||
ScrollBar.horizontal.visible: false
|
||||
clip: true
|
||||
palette: timelineRoot.palette
|
||||
visible: !!room && room.pinnedMessages.length > 0 && !Settings.hiddenPins.includes(roomId)
|
||||
|
||||
ListView {
|
||||
|
||||
spacing: Nheko.paddingSmall
|
||||
model: room ? room.pinnedMessages : undefined
|
||||
spacing: Nheko.paddingSmall
|
||||
|
||||
delegate: RowLayout {
|
||||
required property string modelData
|
||||
|
||||
width: ListView.view.width
|
||||
height: implicitHeight
|
||||
width: ListView.view.width
|
||||
|
||||
Reply {
|
||||
property var e: room ? room.getDump(modelData, "") : {}
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: height
|
||||
|
||||
userColor: TimelineManager.userColor(e.userId, timelineRoot.palette.window)
|
||||
blurhash: e.blurhash ?? ""
|
||||
body: e.body ?? ""
|
||||
formattedBody: e.formattedBody ?? ""
|
||||
encryptionError: e.encryptionError ?? ""
|
||||
eventId: e.eventId ?? ""
|
||||
filename: e.filename ?? ""
|
||||
filesize: e.filesize ?? ""
|
||||
formattedBody: e.formattedBody ?? ""
|
||||
isOnlyEmoji: e.isOnlyEmoji ?? false
|
||||
originalWidth: e.originalWidth ?? 0
|
||||
proportionalHeight: e.proportionalHeight ?? 1
|
||||
type: e.type ?? MtxEvent.UnknownMessage
|
||||
typeString: e.typeString ?? ""
|
||||
url: e.url ?? ""
|
||||
originalWidth: e.originalWidth ?? 0
|
||||
isOnlyEmoji: e.isOnlyEmoji ?? false
|
||||
userColor: TimelineManager.userColor(e.userId, timelineRoot.palette.window)
|
||||
userId: e.userId ?? ""
|
||||
userName: e.userName ?? ""
|
||||
encryptionError: e.encryptionError ?? ""
|
||||
}
|
||||
|
||||
ImageButton {
|
||||
id: deletePinButton
|
||||
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
||||
Layout.preferredHeight: 16
|
||||
Layout.preferredWidth: 16
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
||||
visible: room.permissions.canChange(MtxEvent.PinnedEvents)
|
||||
|
||||
ToolTip.text: qsTr("Unpin")
|
||||
ToolTip.visible: hovered
|
||||
hoverEnabled: true
|
||||
image: ":/icons/icons/ui/dismiss.svg"
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: qsTr("Unpin")
|
||||
visible: room.permissions.canChange(MtxEvent.PinnedEvents)
|
||||
|
||||
onClicked: room.unpin(modelData)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
id: widgets
|
||||
|
||||
Layout.row: 3
|
||||
Layout.column: 2
|
||||
Layout.columnSpan: 3
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: Math.min(contentHeight, Nheko.avatarSize * 1.5)
|
||||
|
||||
visible: !!room && room.widgetLinks.length > 0 && !Settings.hiddenWidgets.includes(roomId)
|
||||
clip: true
|
||||
|
||||
palette: timelineRoot.palette
|
||||
Layout.row: 3
|
||||
ScrollBar.horizontal.visible: false
|
||||
clip: true
|
||||
palette: timelineRoot.palette
|
||||
visible: !!room && room.widgetLinks.length > 0 && !Settings.hiddenWidgets.includes(roomId)
|
||||
|
||||
ListView {
|
||||
|
||||
spacing: Nheko.paddingSmall
|
||||
model: room ? room.widgetLinks : undefined
|
||||
spacing: Nheko.paddingSmall
|
||||
|
||||
delegate: MatrixText {
|
||||
required property var modelData
|
||||
|
||||
color: timelineRoot.palette.text
|
||||
text: modelData
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NhekoCursorShape {
|
||||
anchors.fill: parent
|
||||
anchors.bottomMargin: (pinnedMessages.visible ? pinnedMessages.height : 0) + (widgets.visible ? widgets.height : 0)
|
||||
anchors.fill: parent
|
||||
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: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.3
|
||||
import QtQuick.Layouts 1.2
|
||||
import im.nheko
|
||||
|
||||
Item {
|
||||
implicitHeight: Math.max(fontMetrics.height * 1.2, typingDisplay.height)
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: Math.max(fontMetrics.height * 1.2, typingDisplay.height)
|
||||
|
||||
Rectangle {
|
||||
id: typingRect
|
||||
|
||||
visible: (room && room.typingUsers.length > 0)
|
||||
color: timelineRoot.palette.base
|
||||
anchors.fill: parent
|
||||
color: timelineRoot.palette.base
|
||||
visible: (room && room.typingUsers.length > 0)
|
||||
z: 3
|
||||
|
||||
Label {
|
||||
id: typingDisplay
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 10
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 10
|
||||
anchors.bottom: parent.bottom
|
||||
color: timelineRoot.palette.text
|
||||
text: room ? room.formatTypingUsers(room.typingUsers, timelineRoot.palette.base) : ""
|
||||
textFormat: Text.RichText
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import "./components"
|
||||
import "./ui"
|
||||
|
||||
import "components"
|
||||
import "ui"
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.5
|
||||
import QtQuick.Layouts 1.3
|
||||
|
@ -12,31 +9,30 @@ import im.nheko
|
|||
|
||||
Page {
|
||||
id: uploadPopup
|
||||
visible: room && room.input.uploads.length > 0
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 200
|
||||
clip: true
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
padding: Nheko.paddingMedium
|
||||
visible: room && room.input.uploads.length > 0
|
||||
|
||||
background: Rectangle {
|
||||
color: timelineRoot.palette.base
|
||||
}
|
||||
contentItem: ListView {
|
||||
id: uploadsList
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
model: room ? room.input.uploads : undefined
|
||||
orientation: ListView.Horizontal
|
||||
spacing: Nheko.paddingMedium
|
||||
width: Math.min(contentWidth, parent.availableWidth)
|
||||
|
||||
ScrollBar.horizontal: ScrollBar {
|
||||
id: scr
|
||||
}
|
||||
|
||||
orientation: ListView.Horizontal
|
||||
width: Math.min(contentWidth, parent.availableWidth)
|
||||
model: room ? room.input.uploads : undefined
|
||||
spacing: Nheko.paddingMedium
|
||||
|
||||
delegate: Pane {
|
||||
padding: Nheko.paddingSmall
|
||||
height: uploadPopup.availableHeight - buttons.height - (scr.visible ? scr.height : 0)
|
||||
padding: Nheko.paddingSmall
|
||||
width: uploadPopup.availableHeight - buttons.height
|
||||
|
||||
background: Rectangle {
|
||||
|
@ -45,45 +41,45 @@ Page {
|
|||
}
|
||||
contentItem: ColumnLayout {
|
||||
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.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.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 {
|
||||
Layout.fillWidth: true
|
||||
text: modelData.filename
|
||||
|
||||
onTextEdited: modelData.filename = text
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer: DialogButtonBox {
|
||||
id: buttons
|
||||
|
||||
standardButtons: DialogButtonBox.Cancel
|
||||
Button {
|
||||
text: qsTr("Upload %n file(s)", "", (room ? room.input.uploads.length : 0))
|
||||
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
||||
}
|
||||
|
||||
onAccepted: room.input.acceptUploads()
|
||||
onRejected: room.input.declineUploads()
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: timelineRoot.palette.base
|
||||
Button {
|
||||
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: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.5
|
||||
import QtQuick.Layouts 1.12
|
||||
|
@ -12,16 +10,80 @@ Container {
|
|||
//Component.onCompleted: {
|
||||
// parent.width = Qt.binding(function() { return calculatedWidth; })
|
||||
//}
|
||||
|
||||
id: container
|
||||
|
||||
property Component handle: Rectangle {
|
||||
anchors.right: parent.right
|
||||
color: Nheko.theme.separator
|
||||
height: container.height
|
||||
width: visible ? 1 : 0
|
||||
z: 3
|
||||
}
|
||||
property Component handleToucharea: Item {
|
||||
id: splitter
|
||||
|
||||
property int calculatedWidth: {
|
||||
if (!visible)
|
||||
return 0;
|
||||
else if (container.singlePageMode)
|
||||
return container.width;
|
||||
else
|
||||
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
|
||||
height: container.height
|
||||
width: 1
|
||||
x: parent.preferredWidth
|
||||
z: 3
|
||||
|
||||
NhekoCursorShape {
|
||||
cursorShape: Qt.SizeHorCursor
|
||||
height: parent.height
|
||||
width: container.splitterGrabMargin * 2
|
||||
x: -container.splitterGrabMargin
|
||||
}
|
||||
DragHandler {
|
||||
id: dragHandler
|
||||
enabled: !container.singlePageMode
|
||||
grabPermissions: PointerHandler.CanTakeOverFromAnything | PointerHandler.ApprovesTakeOverByHandlersOfSameType
|
||||
margin: container.splitterGrabMargin
|
||||
xAxis.enabled: true
|
||||
xAxis.maximum: splitter.maximumWidth
|
||||
xAxis.minimum: splitter.minimumWidth - 1
|
||||
yAxis.enabled: false
|
||||
|
||||
onActiveChanged: {
|
||||
if (!active) {
|
||||
splitter.x = splitter.calculatedWidth;
|
||||
splitter.parent.preferredWidth = splitter.calculatedWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
HoverHandler {
|
||||
enabled: !container.singlePageMode
|
||||
margin: container.splitterGrabMargin
|
||||
}
|
||||
}
|
||||
property alias pageIndex: view.currentIndex
|
||||
property bool singlePageMode: width < 800
|
||||
property int splitterGrabMargin: Nheko.paddingSmall
|
||||
property alias pageIndex: view.currentIndex
|
||||
property Component handle
|
||||
property Component handleToucharea
|
||||
|
||||
onSinglePageModeChanged: if (!singlePageMode) pageIndex = 0
|
||||
contentItem: ListView {
|
||||
id: view
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
currentIndex: container.singlePageMode ? container.pageIndex : 0
|
||||
highlightMoveDuration: container.singlePageMode ? 200 : 0
|
||||
highlightRangeMode: ListView.StrictlyEnforceRange
|
||||
interactive: singlePageMode
|
||||
model: container.contentModel
|
||||
orientation: ListView.Horizontal
|
||||
snapMode: ListView.SnapOneItem
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
for (var i = 0; i < count - 1; i++) {
|
||||
|
@ -42,7 +104,6 @@ Container {
|
|||
for (var i = 0; i < count - 1; i++) {
|
||||
if (contentChildren[i].width)
|
||||
w = w - contentChildren[i].width;
|
||||
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
@ -57,81 +118,6 @@ Container {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
handle: Rectangle {
|
||||
z: 3
|
||||
color: Nheko.theme.separator
|
||||
height: container.height
|
||||
width: visible ? 1 : 0
|
||||
anchors.right: parent.right
|
||||
}
|
||||
|
||||
handleToucharea: Item {
|
||||
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: {
|
||||
if (!visible)
|
||||
return 0;
|
||||
else if (container.singlePageMode)
|
||||
return container.width;
|
||||
else
|
||||
return (collapsible && x < minimumWidth) ? collapsedWidth : x;
|
||||
}
|
||||
|
||||
enabled: !container.singlePageMode
|
||||
height: container.height
|
||||
width: 1
|
||||
x: parent.preferredWidth
|
||||
z: 3
|
||||
|
||||
NhekoCursorShape {
|
||||
height: parent.height
|
||||
width: container.splitterGrabMargin * 2
|
||||
x: -container.splitterGrabMargin
|
||||
cursorShape: Qt.SizeHorCursor
|
||||
}
|
||||
|
||||
DragHandler {
|
||||
id: dragHandler
|
||||
|
||||
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
|
||||
onActiveChanged: {
|
||||
if (!active) {
|
||||
splitter.x = splitter.calculatedWidth;
|
||||
splitter.parent.preferredWidth = splitter.calculatedWidth;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
enabled: !container.singlePageMode
|
||||
margin: container.splitterGrabMargin
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
contentItem: ListView {
|
||||
id: view
|
||||
|
||||
model: container.contentModel
|
||||
snapMode: ListView.SnapOneItem
|
||||
orientation: ListView.Horizontal
|
||||
highlightRangeMode: ListView.StrictlyEnforceRange
|
||||
interactive: singlePageMode
|
||||
highlightMoveDuration: container.singlePageMode ? 200 : 0
|
||||
currentIndex: container.singlePageMode ? container.pageIndex : 0
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
}
|
||||
|
||||
onSinglePageModeChanged: if (!singlePageMode)
|
||||
pageIndex = 0
|
||||
}
|
||||
|
|
|
@ -1,20 +1,18 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.5
|
||||
import QtQuick.Layouts 1.12
|
||||
|
||||
Item {
|
||||
property int minimumWidth: 100
|
||||
property int maximumWidth: 400
|
||||
property bool collapsed: width < minimumWidth
|
||||
property int collapsedWidth: 40
|
||||
property bool collapsible: true
|
||||
property bool collapsed: width < minimumWidth
|
||||
property int splitterWidth: 1
|
||||
property int maximumWidth: 400
|
||||
property int minimumWidth: 100
|
||||
property int preferredWidth: 100
|
||||
property int splitterWidth: 1
|
||||
|
||||
Component.onCompleted: {
|
||||
children[0].width = Qt.binding(() => {
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import ".."
|
||||
import "../"
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
|
@ -12,86 +10,81 @@ import im.nheko
|
|||
Rectangle {
|
||||
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)
|
||||
required property string avatarUrl
|
||||
required property string title
|
||||
required property string subtitle
|
||||
required property int index
|
||||
required property int selectedIndex
|
||||
property color background: timelineRoot.palette.window
|
||||
property color bubbleBackground: timelineRoot.palette.highlight
|
||||
property color bubbleText: timelineRoot.palette.highlightedText
|
||||
property bool crop: true
|
||||
property color importantText: timelineRoot.palette.text
|
||||
required property int index
|
||||
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
|
||||
|
||||
color: background
|
||||
height: avatarSize + 2 * Nheko.paddingMedium
|
||||
width: ListView.view.width
|
||||
state: "normal"
|
||||
width: ListView.view.width
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "highlight"
|
||||
when: hovered.hovered && !(index == selectedIndex)
|
||||
|
||||
PropertyChanges {
|
||||
target: tile
|
||||
background: timelineRoot.palette.dark
|
||||
importantText: timelineRoot.palette.brightText
|
||||
unimportantText: timelineRoot.palette.brightText
|
||||
bubbleBackground: timelineRoot.palette.highlight
|
||||
bubbleText: timelineRoot.palette.highlightedText
|
||||
importantText: timelineRoot.palette.brightText
|
||||
target: tile
|
||||
unimportantText: timelineRoot.palette.brightText
|
||||
}
|
||||
|
||||
},
|
||||
State {
|
||||
name: "selected"
|
||||
when: index == selectedIndex
|
||||
|
||||
PropertyChanges {
|
||||
target: tile
|
||||
background: timelineRoot.palette.highlight
|
||||
importantText: timelineRoot.palette.highlightedText
|
||||
unimportantText: timelineRoot.palette.highlightedText
|
||||
bubbleBackground: timelineRoot.palette.highlightedText
|
||||
bubbleText: timelineRoot.palette.highlight
|
||||
importantText: timelineRoot.palette.highlightedText
|
||||
target: tile
|
||||
unimportantText: timelineRoot.palette.highlightedText
|
||||
}
|
||||
|
||||
}
|
||||
]
|
||||
|
||||
HoverHandler {
|
||||
id: hovered
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: Nheko.paddingMedium
|
||||
anchors.fill: parent
|
||||
anchors.margins: Nheko.paddingMedium
|
||||
spacing: Nheko.paddingMedium
|
||||
|
||||
Avatar {
|
||||
id: avatar
|
||||
|
||||
enabled: false
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
height: avatarSize
|
||||
width: avatarSize
|
||||
url: tile.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||
displayName: title
|
||||
crop: tile.crop
|
||||
displayName: title
|
||||
enabled: false
|
||||
height: avatarSize
|
||||
url: tile.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||
width: avatarSize
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: textContent
|
||||
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumWidth: 100
|
||||
width: parent.width - avatar.width
|
||||
Layout.preferredWidth: parent.width - avatar.width
|
||||
spacing: Nheko.paddingSmall
|
||||
width: parent.width - avatar.width
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
@ -104,33 +97,25 @@ Rectangle {
|
|||
fullText: title
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
ElidedLabel {
|
||||
color: tile.unimportantText
|
||||
font.pixelSize: fontMetrics.font.pixelSize * 0.9
|
||||
elideWidth: textContent.width - Nheko.paddingSmall
|
||||
font.pixelSize: fontMetrics.font.pixelSize * 0.9
|
||||
fullText: subtitle
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
//import QtGraphicalEffects 1.12
|
||||
|
@ -13,11 +12,18 @@ import im.nheko
|
|||
Button {
|
||||
id: control
|
||||
|
||||
property string iconImage: ""
|
||||
|
||||
hoverEnabled: true
|
||||
implicitHeight: Math.ceil(control.contentItem.implicitHeight * 1.70)
|
||||
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 {
|
||||
// anchors.fill: control.background
|
||||
|
@ -29,38 +35,29 @@ Button {
|
|||
// color: "#80000000"
|
||||
// source: control.background
|
||||
//}
|
||||
|
||||
contentItem: RowLayout {
|
||||
spacing: 0
|
||||
anchors.centerIn: parent
|
||||
spacing: 0
|
||||
|
||||
Image {
|
||||
Layout.leftMargin: Nheko.paddingMedium
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
|
||||
Layout.leftMargin: Nheko.paddingMedium
|
||||
Layout.preferredHeight: fontMetrics.font.pixelSize * 1.5
|
||||
Layout.preferredWidth: fontMetrics.font.pixelSize * 1.5
|
||||
visible: !!iconImage
|
||||
source: iconImage
|
||||
visible: !!iconImage
|
||||
}
|
||||
|
||||
Text {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: control.text
|
||||
//font.capitalization: Font.AllUppercase
|
||||
color: timelineRoot.palette.light
|
||||
elide: Text.ElideRight
|
||||
//font: control.font
|
||||
font.capitalization: Font.AllUppercase
|
||||
font.pointSize: Math.ceil(fontMetrics.font.pointSize * 1.5)
|
||||
//font.capitalization: Font.AllUppercase
|
||||
color: timelineRoot.palette.light
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: control.text
|
||||
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: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import Qt.labs.platform 1.1 as P
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
|
@ -13,30 +11,28 @@ Dialog {
|
|||
default property alias inner: scroll.data
|
||||
property int useableWidth: scroll.width - scroll.ScrollBar.vertical.width
|
||||
|
||||
parent: Overlay.overlay
|
||||
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
|
||||
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: [
|
||||
ScrollView {
|
||||
id: scroll
|
||||
|
||||
clip: true
|
||||
anchors.fill: parent
|
||||
ScrollBar.horizontal.visible: false
|
||||
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: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import "../ui"
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
|
@ -11,37 +9,32 @@ import im.nheko // for cursor shape
|
|||
AbstractButton {
|
||||
id: button
|
||||
|
||||
property color buttonTextColor: timelineRoot.palette.placeholderText
|
||||
property alias cursor: mouseArea.cursorShape
|
||||
property color highlightColor: timelineRoot.palette.highlight
|
||||
property color buttonTextColor: timelineRoot.palette.placeholderText
|
||||
|
||||
focusPolicy: Qt.NoFocus
|
||||
width: buttonText.implicitWidth
|
||||
height: buttonText.implicitHeight
|
||||
implicitWidth: buttonText.implicitWidth
|
||||
implicitHeight: buttonText.implicitHeight
|
||||
implicitWidth: buttonText.implicitWidth
|
||||
width: buttonText.implicitWidth
|
||||
|
||||
Label {
|
||||
id: buttonText
|
||||
|
||||
anchors.centerIn: parent
|
||||
padding: 0
|
||||
text: button.text
|
||||
color: button.hovered ? highlightColor : buttonTextColor
|
||||
font: button.font
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
padding: 0
|
||||
text: button.text
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
NhekoCursorShape {
|
||||
id: mouseArea
|
||||
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
|
||||
Ripple {
|
||||
color: Qt.rgba(buttonTextColor.r, buttonTextColor.g, buttonTextColor.b, 0.5)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import ".."
|
||||
import "../"
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
|
@ -15,32 +13,31 @@ Rectangle {
|
|||
required property int encryptionError
|
||||
required property string eventId
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
RowLayout {
|
||||
id: contents
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Nheko.paddingMedium
|
||||
spacing: Nheko.paddingMedium
|
||||
|
||||
Image {
|
||||
source: "image://colorimage/:/icons/icons/ui/shield-filled-cross.svg?" + Nheko.theme.error
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
width: 24
|
||||
height: width
|
||||
source: "image://colorimage/:/icons/icons/ui/shield-filled-cross.svg?" + Nheko.theme.error
|
||||
width: 24
|
||||
}
|
||||
|
||||
Column {
|
||||
spacing: Nheko.paddingSmall
|
||||
Layout.fillWidth: true
|
||||
spacing: Nheko.paddingSmall
|
||||
|
||||
MatrixText {
|
||||
id: encryptedText
|
||||
color: timelineRoot.palette.text
|
||||
text: {
|
||||
switch (encryptionError) {
|
||||
case Olm.MissingSession:
|
||||
|
@ -59,19 +56,15 @@ Rectangle {
|
|||
return qsTr("Unknown decryption error");
|
||||
}
|
||||
}
|
||||
color: timelineRoot.palette.text
|
||||
width: parent.width
|
||||
}
|
||||
|
||||
Button {
|
||||
palette: timelineRoot.palette
|
||||
visible: encryptionError == Olm.MissingSession || encryptionError == Olm.MissingSessionIndex
|
||||
text: qsTr("Request key")
|
||||
visible: encryptionError == Olm.MissingSession || encryptionError == Olm.MissingSessionIndex
|
||||
|
||||
onClicked: room.requestKeyForEvent(eventId)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Layouts 1.2
|
||||
import im.nheko
|
||||
|
@ -11,86 +9,71 @@ Item {
|
|||
required property string eventId
|
||||
required property string filename
|
||||
required property string filesize
|
||||
property bool fitsMetadata: true
|
||||
property int metadataWidth
|
||||
|
||||
height: row.height + (Settings.bubbles ? 16 : 24)
|
||||
width: parent.width
|
||||
implicitWidth: row.implicitWidth + metadataWidth
|
||||
property int metadataWidth
|
||||
property bool fitsMetadata: true
|
||||
width: parent.width
|
||||
|
||||
RowLayout {
|
||||
id: row
|
||||
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - (Settings.bubbles? 16 : 24)
|
||||
spacing: 15
|
||||
width: parent.width - (Settings.bubbles ? 16 : 24)
|
||||
|
||||
Rectangle {
|
||||
id: button
|
||||
|
||||
color: timelineRoot.palette.light
|
||||
radius: 22
|
||||
height: 44
|
||||
radius: 22
|
||||
width: 44
|
||||
|
||||
Image {
|
||||
id: img
|
||||
|
||||
anchors.centerIn: parent
|
||||
fillMode: Image.Pad
|
||||
height: 40
|
||||
width: 40
|
||||
source: "qrc:/icons/icons/ui/download.svg"
|
||||
sourceSize.height: 40
|
||||
sourceSize.width: 40
|
||||
|
||||
anchors.centerIn: parent
|
||||
source: "qrc:/icons/icons/ui/download.svg"
|
||||
fillMode: Image.Pad
|
||||
width: 40
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
onSingleTapped: room.saveMedia(eventId)
|
||||
gesturePolicy: TapHandler.ReleaseWithinBounds
|
||||
}
|
||||
|
||||
onSingleTapped: room.saveMedia(eventId)
|
||||
}
|
||||
NhekoCursorShape {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: col
|
||||
|
||||
Text {
|
||||
id: filename_
|
||||
|
||||
Layout.fillWidth: true
|
||||
color: timelineRoot.palette.text
|
||||
elide: Text.ElideRight
|
||||
text: filename
|
||||
textFormat: Text.PlainText
|
||||
elide: Text.ElideRight
|
||||
color: timelineRoot.palette.text
|
||||
}
|
||||
|
||||
Text {
|
||||
id: filesize_
|
||||
|
||||
Layout.fillWidth: true
|
||||
color: timelineRoot.palette.text
|
||||
elide: Text.ElideRight
|
||||
text: filesize
|
||||
textFormat: Text.PlainText
|
||||
elide: Text.ElideRight
|
||||
color: timelineRoot.palette.text
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color: timelineRoot.palette.alternateBase
|
||||
z: -1
|
||||
radius: 10
|
||||
anchors.fill: parent
|
||||
color: timelineRoot.palette.alternateBase
|
||||
radius: 10
|
||||
visible: !Settings.bubbles // the bubble in a bubble looks odd
|
||||
z: -1
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,102 +1,85 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Window 2.15
|
||||
import QtQuick.Controls 2.3
|
||||
import im.nheko
|
||||
|
||||
AbstractButton {
|
||||
required property int type
|
||||
required property int originalWidth
|
||||
required property double proportionalHeight
|
||||
required property string url
|
||||
required property string blurhash
|
||||
required property string body
|
||||
required property string filename
|
||||
required property bool isReply
|
||||
required property string eventId
|
||||
property double divisor: isReply ? 5 : 3
|
||||
|
||||
required property string eventId
|
||||
required property string filename
|
||||
property bool fitsMetadata: (parent.width - width) > metadataWidth + 4
|
||||
required property bool isReply
|
||||
property int metadataWidth
|
||||
required property int originalWidth
|
||||
required property double proportionalHeight
|
||||
property int tempWidth: originalWidth < 1 ? 400 : originalWidth
|
||||
required property int type
|
||||
required property string url
|
||||
|
||||
implicitWidth: Math.round(tempWidth*Math.min((timelineView.height/divisor)/(tempWidth*proportionalHeight), 1))
|
||||
width: Math.min(parent?.width ?? implicitWidth,implicitWidth)
|
||||
height: width * proportionalHeight
|
||||
hoverEnabled: true
|
||||
implicitWidth: Math.round(tempWidth * Math.min((timelineView.height / divisor) / (tempWidth * proportionalHeight), 1))
|
||||
width: Math.min(parent?.width ?? implicitWidth, implicitWidth)
|
||||
|
||||
property int metadataWidth
|
||||
property bool fitsMetadata: (parent.width - width) > metadataWidth+4
|
||||
onClicked: Settings.openImageExternal ? room.openMedia(eventId) : TimelineManager.openImageOverlay(room, url, eventId)
|
||||
|
||||
Image {
|
||||
id: blurhash_
|
||||
|
||||
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
|
||||
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.width: parent.width * Screen.devicePixelRatio
|
||||
visible: img.status != Image.Ready
|
||||
}
|
||||
|
||||
Image {
|
||||
id: img
|
||||
|
||||
visible: !mxcimage.loaded
|
||||
anchors.fill: parent
|
||||
source: url.replace("mxc://", "image://MxcImage/") + "?scale"
|
||||
asynchronous: true
|
||||
fillMode: Image.PreserveAspectFit
|
||||
smooth: true
|
||||
mipmap: true
|
||||
|
||||
sourceSize.width: Math.min(Screen.desktopAvailableWidth, originalWidth < 1 ? Screen.desktopAvailableWidth : originalWidth) * Screen.devicePixelRatio
|
||||
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
|
||||
visible: !mxcimage.loaded
|
||||
}
|
||||
|
||||
MxcAnimatedImage {
|
||||
id: mxcimage
|
||||
|
||||
visible: loaded
|
||||
anchors.fill: parent
|
||||
roomm: room
|
||||
play: !Settings.animateImagesOnHover || parent.hovered
|
||||
eventId: parent.eventId
|
||||
play: !Settings.animateImagesOnHover || parent.hovered
|
||||
roomm: room
|
||||
visible: loaded
|
||||
}
|
||||
|
||||
onClicked :Settings.openImageExternal ? room.openMedia(eventId) : TimelineManager.openImageOverlay(room, url, eventId);
|
||||
|
||||
Item {
|
||||
id: overlay
|
||||
|
||||
anchors.fill: parent
|
||||
visible: parent.hovered
|
||||
|
||||
Rectangle {
|
||||
id: container
|
||||
|
||||
width: parent.width
|
||||
implicitHeight: imgcaption.implicitHeight
|
||||
anchors.bottom: overlay.bottom
|
||||
color: timelineRoot.palette.window
|
||||
implicitHeight: imgcaption.implicitHeight
|
||||
opacity: 0.75
|
||||
width: parent.width
|
||||
}
|
||||
|
||||
Text {
|
||||
id: imgcaption
|
||||
|
||||
anchors.fill: container
|
||||
color: timelineRoot.palette.text
|
||||
elide: Text.ElideMiddle
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
// See this MSC: https://github.com/matrix-org/matrix-doc/pull/2530
|
||||
text: filename ? filename : body
|
||||
color: timelineRoot.palette.text
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.6
|
||||
import QtQuick.Controls 2.1
|
||||
import QtQuick.Layouts 1.2
|
||||
|
@ -12,35 +10,35 @@ import im.nheko
|
|||
Item {
|
||||
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 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 filename
|
||||
required property string filesize
|
||||
required property string url
|
||||
required property string thumbnailUrl
|
||||
property bool fitsMetadata: (chooser.child && chooser.child.fitsMetadata) ? chooser.child.fitsMetadata : false
|
||||
required property string formattedBody
|
||||
required property bool isOnlyEmoji
|
||||
required property bool isReply
|
||||
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 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
|
||||
implicitWidth: (chooser.child && chooser.child.implicitWidth) ? chooser.child.implicitWidth : 0
|
||||
|
||||
DelegateChooser {
|
||||
id: chooser
|
||||
|
@ -48,97 +46,84 @@ Item {
|
|||
//role: "type" //< not supported in our custom implementation, have to use roleValue
|
||||
roleValue: type
|
||||
//anchors.fill: parent
|
||||
|
||||
width: parent.width ? parent.width : 0 // this should get rid of "cannot read property 'width' of null"
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.UnknownMessage
|
||||
|
||||
Placeholder {
|
||||
typeString: d.typeString
|
||||
text: "Unretrieved event"
|
||||
typeString: d.typeString
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.TextMessage
|
||||
|
||||
TextMessage {
|
||||
formatted: d.formattedBody
|
||||
body: d.body
|
||||
formatted: d.formattedBody
|
||||
isOnlyEmoji: d.isOnlyEmoji
|
||||
isReply: d.isReply
|
||||
metadataWidth: d.metadataWidth
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.NoticeMessage
|
||||
|
||||
NoticeMessage {
|
||||
formatted: d.formattedBody
|
||||
body: d.body
|
||||
formatted: d.formattedBody
|
||||
isOnlyEmoji: d.isOnlyEmoji
|
||||
isReply: d.isReply
|
||||
isStateEvent: d.isStateEvent
|
||||
metadataWidth: d.metadataWidth
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.EmoteMessage
|
||||
|
||||
NoticeMessage {
|
||||
formatted: TimelineManager.escapeEmoji(d.userName) + " " + d.formattedBody
|
||||
color: TimelineManager.userColor(d.userId, timelineRoot.palette.base)
|
||||
body: d.body
|
||||
color: TimelineManager.userColor(d.userId, timelineRoot.palette.base)
|
||||
formatted: TimelineManager.escapeEmoji(d.userName) + " " + d.formattedBody
|
||||
isOnlyEmoji: d.isOnlyEmoji
|
||||
isReply: d.isReply
|
||||
isStateEvent: d.isStateEvent
|
||||
metadataWidth: d.metadataWidth
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.ImageMessage
|
||||
|
||||
ImageMessage {
|
||||
type: d.type
|
||||
originalWidth: d.originalWidth
|
||||
proportionalHeight: d.proportionalHeight
|
||||
url: d.url
|
||||
blurhash: d.blurhash
|
||||
body: d.body
|
||||
eventId: d.eventId
|
||||
filename: d.filename
|
||||
isReply: d.isReply
|
||||
eventId: d.eventId
|
||||
metadataWidth: d.metadataWidth
|
||||
originalWidth: d.originalWidth
|
||||
proportionalHeight: d.proportionalHeight
|
||||
type: d.type
|
||||
url: d.url
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.Sticker
|
||||
|
||||
ImageMessage {
|
||||
type: d.type
|
||||
originalWidth: d.originalWidth
|
||||
proportionalHeight: d.proportionalHeight
|
||||
url: d.url
|
||||
blurhash: d.blurhash
|
||||
body: d.body
|
||||
eventId: d.eventId
|
||||
filename: d.filename
|
||||
isReply: d.isReply
|
||||
eventId: d.eventId
|
||||
metadataWidth: d.metadataWidth
|
||||
originalWidth: d.originalWidth
|
||||
proportionalHeight: d.proportionalHeight
|
||||
type: d.type
|
||||
url: d.url
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.FileMessage
|
||||
|
||||
|
@ -148,45 +133,39 @@ Item {
|
|||
filesize: d.filesize
|
||||
metadataWidth: d.metadataWidth
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.VideoMessage
|
||||
|
||||
PlayableMediaMessage {
|
||||
proportionalHeight: d.proportionalHeight
|
||||
type: d.type
|
||||
originalWidth: d.originalWidth
|
||||
thumbnailUrl: d.thumbnailUrl
|
||||
eventId: d.eventId
|
||||
url: d.url
|
||||
body: d.body
|
||||
filesize: d.filesize
|
||||
duration: d.duration
|
||||
eventId: d.eventId
|
||||
filesize: d.filesize
|
||||
metadataWidth: d.metadataWidth
|
||||
originalWidth: d.originalWidth
|
||||
proportionalHeight: d.proportionalHeight
|
||||
thumbnailUrl: d.thumbnailUrl
|
||||
type: d.type
|
||||
url: d.url
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.AudioMessage
|
||||
|
||||
PlayableMediaMessage {
|
||||
proportionalHeight: d.proportionalHeight
|
||||
type: d.type
|
||||
originalWidth: d.originalWidth
|
||||
thumbnailUrl: d.thumbnailUrl
|
||||
eventId: d.eventId
|
||||
url: d.url
|
||||
body: d.body
|
||||
filesize: d.filesize
|
||||
duration: d.duration
|
||||
eventId: d.eventId
|
||||
filesize: d.filesize
|
||||
metadataWidth: d.metadataWidth
|
||||
originalWidth: d.originalWidth
|
||||
proportionalHeight: d.proportionalHeight
|
||||
thumbnailUrl: d.thumbnailUrl
|
||||
type: d.type
|
||||
url: d.url
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.Redacted
|
||||
|
||||
|
@ -194,27 +173,22 @@ Item {
|
|||
metadataWidth: d.metadataWidth
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.Redaction
|
||||
|
||||
Pill {
|
||||
text: qsTr("%1 removed a message").arg(d.userName)
|
||||
isStateEvent: d.isStateEvent
|
||||
text: qsTr("%1 removed a message").arg(d.userName)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.Encryption
|
||||
|
||||
Pill {
|
||||
text: qsTr("%1 enabled encryption").arg(d.userName)
|
||||
isStateEvent: d.isStateEvent
|
||||
text: qsTr("%1 enabled encryption").arg(d.userName)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.Encrypted
|
||||
|
||||
|
@ -222,122 +196,100 @@ Item {
|
|||
encryptionError: d.encryptionError
|
||||
eventId: d.eventId
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.Name
|
||||
|
||||
NoticeMessage {
|
||||
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
|
||||
isReply: d.isReply
|
||||
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 {
|
||||
roleValue: MtxEvent.Topic
|
||||
|
||||
NoticeMessage {
|
||||
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
|
||||
isReply: d.isReply
|
||||
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 {
|
||||
roleValue: MtxEvent.Avatar
|
||||
|
||||
NoticeMessage {
|
||||
body: formatted
|
||||
formatted: qsTr("%1 changed the room avatar").arg(d.userName)
|
||||
isOnlyEmoji: false
|
||||
isReply: d.isReply
|
||||
isStateEvent: d.isStateEvent
|
||||
formatted: qsTr("%1 changed the room avatar").arg(d.userName)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.PinnedEvents
|
||||
|
||||
NoticeMessage {
|
||||
body: formatted
|
||||
formatted: qsTr("%1 changed the pinned messages.").arg(d.userName)
|
||||
isOnlyEmoji: false
|
||||
isReply: d.isReply
|
||||
isStateEvent: d.isStateEvent
|
||||
formatted: qsTr("%1 changed the pinned messages.").arg(d.userName)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.ImagePackInRoom
|
||||
|
||||
NoticeMessage {
|
||||
body: formatted
|
||||
formatted: d.relatedEventCacheBuster, room.formatImagePackEvent(d.eventId)
|
||||
isOnlyEmoji: false
|
||||
isReply: d.isReply
|
||||
isStateEvent: d.isStateEvent
|
||||
formatted: d.relatedEventCacheBuster, room.formatImagePackEvent(d.eventId)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.CanonicalAlias
|
||||
|
||||
NoticeMessage {
|
||||
body: formatted
|
||||
formatted: qsTr("%1 changed the addresses for this room.").arg(d.userName)
|
||||
isOnlyEmoji: false
|
||||
isReply: d.isReply
|
||||
isStateEvent: d.isStateEvent
|
||||
formatted: qsTr("%1 changed the addresses for this room.").arg(d.userName)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.SpaceParent
|
||||
|
||||
NoticeMessage {
|
||||
body: formatted
|
||||
formatted: qsTr("%1 changed the parent spaces for this room.").arg(d.userName)
|
||||
isOnlyEmoji: false
|
||||
isReply: d.isReply
|
||||
isStateEvent: d.isStateEvent
|
||||
formatted: qsTr("%1 changed the parent spaces for this room.").arg(d.userName)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.RoomCreate
|
||||
|
||||
NoticeMessage {
|
||||
body: formatted
|
||||
formatted: qsTr("%1 created and configured room: %2").arg(d.userName).arg(room.roomId)
|
||||
isOnlyEmoji: false
|
||||
isReply: d.isReply
|
||||
isStateEvent: d.isStateEvent
|
||||
formatted: qsTr("%1 created and configured room: %2").arg(d.userName).arg(room.roomId)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.CallInvite
|
||||
|
||||
NoticeMessage {
|
||||
body: formatted
|
||||
isOnlyEmoji: false
|
||||
isReply: d.isReply
|
||||
isStateEvent: d.isStateEvent
|
||||
formatted: {
|
||||
switch (d.callType) {
|
||||
case "voice":
|
||||
|
@ -348,101 +300,88 @@ Item {
|
|||
return qsTr("%1 placed a call.").arg(d.userName);
|
||||
}
|
||||
}
|
||||
isOnlyEmoji: false
|
||||
isReply: d.isReply
|
||||
isStateEvent: d.isStateEvent
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.CallAnswer
|
||||
|
||||
NoticeMessage {
|
||||
body: formatted
|
||||
formatted: qsTr("%1 answered the call.").arg(d.userName)
|
||||
isOnlyEmoji: false
|
||||
isReply: d.isReply
|
||||
isStateEvent: d.isStateEvent
|
||||
formatted: qsTr("%1 answered the call.").arg(d.userName)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.CallHangUp
|
||||
|
||||
NoticeMessage {
|
||||
body: formatted
|
||||
formatted: qsTr("%1 ended the call.").arg(d.userName)
|
||||
isOnlyEmoji: false
|
||||
isReply: d.isReply
|
||||
isStateEvent: d.isStateEvent
|
||||
formatted: qsTr("%1 ended the call.").arg(d.userName)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.CallCandidates
|
||||
|
||||
NoticeMessage {
|
||||
body: formatted
|
||||
formatted: qsTr("%1 is negotiating the call...").arg(d.userName)
|
||||
isOnlyEmoji: false
|
||||
isReply: d.isReply
|
||||
isStateEvent: d.isStateEvent
|
||||
formatted: qsTr("%1 is negotiating the call...").arg(d.userName)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.PowerLevels
|
||||
|
||||
NoticeMessage {
|
||||
body: formatted
|
||||
formatted: d.relatedEventCacheBuster, room.formatPowerLevelEvent(d.eventId)
|
||||
isOnlyEmoji: false
|
||||
isReply: d.isReply
|
||||
isStateEvent: d.isStateEvent
|
||||
formatted: d.relatedEventCacheBuster, room.formatPowerLevelEvent(d.eventId)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.RoomJoinRules
|
||||
|
||||
NoticeMessage {
|
||||
body: formatted
|
||||
formatted: d.relatedEventCacheBuster, room.formatJoinRuleEvent(d.eventId)
|
||||
isOnlyEmoji: false
|
||||
isReply: d.isReply
|
||||
isStateEvent: d.isStateEvent
|
||||
formatted: d.relatedEventCacheBuster, room.formatJoinRuleEvent(d.eventId)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.RoomHistoryVisibility
|
||||
|
||||
NoticeMessage {
|
||||
body: formatted
|
||||
formatted: d.relatedEventCacheBuster, room.formatHistoryVisibilityEvent(d.eventId)
|
||||
isOnlyEmoji: false
|
||||
isReply: d.isReply
|
||||
isStateEvent: d.isStateEvent
|
||||
formatted: d.relatedEventCacheBuster, room.formatHistoryVisibilityEvent(d.eventId)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.RoomGuestAccess
|
||||
|
||||
NoticeMessage {
|
||||
body: formatted
|
||||
formatted: d.relatedEventCacheBuster, room.formatGuestAccessEvent(d.eventId)
|
||||
isOnlyEmoji: false
|
||||
isReply: d.isReply
|
||||
isStateEvent: d.isStateEvent
|
||||
formatted: d.relatedEventCacheBuster, room.formatGuestAccessEvent(d.eventId)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.Member
|
||||
|
||||
|
@ -450,149 +389,125 @@ Item {
|
|||
width: parent?.width
|
||||
|
||||
NoticeMessage {
|
||||
Layout.fillWidth: true
|
||||
body: formatted
|
||||
formatted: d.relatedEventCacheBuster, room.formatMemberEvent(d.eventId)
|
||||
isOnlyEmoji: false
|
||||
isReply: d.isReply
|
||||
isStateEvent: d.isStateEvent
|
||||
Layout.fillWidth: true
|
||||
formatted: d.relatedEventCacheBuster, room.formatMemberEvent(d.eventId)
|
||||
}
|
||||
|
||||
Button {
|
||||
visible: d.relatedEventCacheBuster, room.showAcceptKnockButton(d.eventId)
|
||||
palette: timelineRoot.palette
|
||||
text: qsTr("Allow them in")
|
||||
visible: d.relatedEventCacheBuster, room.showAcceptKnockButton(d.eventId)
|
||||
|
||||
onClicked: room.acceptKnock(eventId)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.KeyVerificationRequest
|
||||
|
||||
NoticeMessage {
|
||||
body: formatted
|
||||
formatted: "KeyVerificationRequest"
|
||||
isOnlyEmoji: false
|
||||
isReply: d.isReply
|
||||
isStateEvent: d.isStateEvent
|
||||
formatted: "KeyVerificationRequest"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.KeyVerificationStart
|
||||
|
||||
NoticeMessage {
|
||||
body: formatted
|
||||
formatted: "KeyVerificationStart"
|
||||
isOnlyEmoji: false
|
||||
isReply: d.isReply
|
||||
isStateEvent: d.isStateEvent
|
||||
formatted: "KeyVerificationStart"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.KeyVerificationReady
|
||||
|
||||
NoticeMessage {
|
||||
body: formatted
|
||||
formatted: "KeyVerificationReady"
|
||||
isOnlyEmoji: false
|
||||
isReply: d.isReply
|
||||
isStateEvent: d.isStateEvent
|
||||
formatted: "KeyVerificationReady"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.KeyVerificationCancel
|
||||
|
||||
NoticeMessage {
|
||||
body: formatted
|
||||
formatted: "KeyVerificationCancel"
|
||||
isOnlyEmoji: false
|
||||
isReply: d.isReply
|
||||
isStateEvent: d.isStateEvent
|
||||
formatted: "KeyVerificationCancel"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.KeyVerificationKey
|
||||
|
||||
NoticeMessage {
|
||||
body: formatted
|
||||
formatted: "KeyVerificationKey"
|
||||
isOnlyEmoji: false
|
||||
isReply: d.isReply
|
||||
isStateEvent: d.isStateEvent
|
||||
formatted: "KeyVerificationKey"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.KeyVerificationMac
|
||||
|
||||
NoticeMessage {
|
||||
body: formatted
|
||||
isOnlyEmoji: false
|
||||
isReply: d.isReply
|
||||
isStateEvent: d.isStateEvent
|
||||
formatted: "KeyVerificationMac"
|
||||
isOnlyEmoji: false
|
||||
isReply: d.isReply
|
||||
isStateEvent: d.isStateEvent
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.KeyVerificationDone
|
||||
|
||||
NoticeMessage {
|
||||
body: formatted
|
||||
formatted: "KeyVerificationDone"
|
||||
isOnlyEmoji: false
|
||||
isReply: d.isReply
|
||||
isStateEvent: d.isStateEvent
|
||||
formatted: "KeyVerificationDone"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.KeyVerificationDone
|
||||
|
||||
NoticeMessage {
|
||||
body: formatted
|
||||
formatted: "KeyVerificationDone"
|
||||
isOnlyEmoji: false
|
||||
isReply: d.isReply
|
||||
isStateEvent: d.isStateEvent
|
||||
formatted: "KeyVerificationDone"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.KeyVerificationAccept
|
||||
|
||||
NoticeMessage {
|
||||
body: formatted
|
||||
formatted: "KeyVerificationAccept"
|
||||
isOnlyEmoji: false
|
||||
isReply: d.isReply
|
||||
isStateEvent: d.isStateEvent
|
||||
formatted: "KeyVerificationAccept"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
Placeholder {
|
||||
typeString: d.typeString
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.5
|
||||
import im.nheko
|
||||
|
||||
|
||||
TextMessage {
|
||||
property bool isStateEvent
|
||||
font.italic: true
|
||||
|
||||
color: timelineRoot.palette.placeholderText
|
||||
font.italic: true
|
||||
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: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 2.1
|
||||
import im.nheko
|
||||
|
||||
Label {
|
||||
property bool isStateEvent
|
||||
|
||||
color: timelineRoot.palette.text
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
height: Math.round(fontMetrics.height * 1.4)
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
width: contentWidth * 1.2
|
||||
|
||||
background: Rectangle {
|
||||
radius: parent.height / 2
|
||||
color: timelineRoot.palette.alternateBase
|
||||
radius: parent.height / 2
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import ".."
|
||||
import "../"
|
||||
import im.nheko
|
||||
|
||||
MatrixText {
|
||||
required property string typeString
|
||||
|
||||
text: qsTr("unimplemented event: ") + typeString
|
||||
// width: parent.width
|
||||
color: timelineRoot.palette.inactive.text
|
||||
text: qsTr("unimplemented event: ") + typeString
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import "../"
|
||||
import "../ui/media"
|
||||
import QtMultimedia
|
||||
|
@ -14,99 +12,88 @@ import im.nheko
|
|||
Item {
|
||||
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 filesize
|
||||
property double divisor: isReply ? 4 : 2
|
||||
required property int duration
|
||||
required property string eventId
|
||||
required property string filesize
|
||||
property bool fitsMetadata: (parent.width - fileInfoLabel.width) > metadataWidth + 4
|
||||
property int metadataWidth
|
||||
required property int originalWidth
|
||||
required property double proportionalHeight
|
||||
property int tempWidth: originalWidth < 1 ? 400 : originalWidth
|
||||
implicitWidth: type == MtxEvent.VideoMessage ? Math.round(tempWidth*Math.min((timelineView.height/divisor)/(tempWidth*proportionalHeight), 1)) : 500
|
||||
width: Math.min(parent.width, implicitWidth)
|
||||
required property string thumbnailUrl
|
||||
required property int type
|
||||
required property string url
|
||||
|
||||
height: (type == MtxEvent.VideoMessage ? width * proportionalHeight : 80) + fileInfoLabel.height
|
||||
implicitHeight: height
|
||||
|
||||
property int metadataWidth
|
||||
property bool fitsMetadata: (parent.width - fileInfoLabel.width) > metadataWidth+4
|
||||
implicitWidth: type == MtxEvent.VideoMessage ? Math.round(tempWidth * Math.min((timelineView.height / divisor) / (tempWidth * proportionalHeight), 1)) : 500
|
||||
width: Math.min(parent.width, implicitWidth)
|
||||
|
||||
MxcMedia {
|
||||
id: mxcmedia
|
||||
|
||||
roomm: room
|
||||
videoOutput: videoOutput
|
||||
|
||||
audioOutput: AudioOutput {
|
||||
muted: mediaControls.muted
|
||||
volume: mediaControls.desiredVolume
|
||||
}
|
||||
videoOutput: videoOutput
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: videoContainer
|
||||
|
||||
color: type == MtxEvent.VideoMessage ? timelineRoot.palette.window : "transparent"
|
||||
width: parent.width
|
||||
height: parent.height - fileInfoLabel.height
|
||||
width: parent.width
|
||||
|
||||
TapHandler {
|
||||
onTapped: Settings.openVideoExternal ? room.openMedia(eventId) : mediaControls.showControls()
|
||||
}
|
||||
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
source: thumbnailUrl ? thumbnailUrl.replace("mxc://", "image://MxcImage/") + "?scale" : ""
|
||||
asynchronous: true
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: thumbnailUrl ? thumbnailUrl.replace("mxc://", "image://MxcImage/") + "?scale" : ""
|
||||
|
||||
VideoOutput {
|
||||
id: videoOutput
|
||||
|
||||
visible: type == MtxEvent.VideoMessage
|
||||
clip: true
|
||||
anchors.fill: parent
|
||||
clip: true
|
||||
fillMode: VideoOutput.PreserveAspectFit
|
||||
//flushMode: VideoOutput.FirstFrame
|
||||
orientation: mxcmedia.orientation
|
||||
visible: type == MtxEvent.VideoMessage
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MediaControls {
|
||||
id: mediaControls
|
||||
|
||||
anchors.bottom: fileInfoLabel.top
|
||||
anchors.left: content.left
|
||||
anchors.right: content.right
|
||||
anchors.bottom: fileInfoLabel.top
|
||||
playingVideo: type == MtxEvent.VideoMessage
|
||||
positionValue: mxcmedia.position
|
||||
duration: mediaLoaded ? mxcmedia.duration : content.duration
|
||||
mediaLoaded: mxcmedia.loaded
|
||||
mediaState: mxcmedia.state
|
||||
onPositionChanged: mxcmedia.position = position
|
||||
onPlayPauseActivated: mxcmedia.state == MediaPlayer.PlayingState ? mxcmedia.pause() : mxcmedia.play()
|
||||
playingVideo: type == MtxEvent.VideoMessage
|
||||
positionValue: mxcmedia.position
|
||||
|
||||
onLoadActivated: mxcmedia.eventId = eventId
|
||||
onPlayPauseActivated: mxcmedia.state == MediaPlayer.PlayingState ? mxcmedia.pause() : mxcmedia.play()
|
||||
onPositionChanged: mxcmedia.position = position
|
||||
}
|
||||
|
||||
// information about file name and file size
|
||||
Label {
|
||||
id: fileInfoLabel
|
||||
|
||||
anchors.bottom: content.bottom
|
||||
color: timelineRoot.palette.text
|
||||
elide: Text.ElideRight
|
||||
text: body + " [" + filesize + "]"
|
||||
textFormat: Text.RichText
|
||||
elide: Text.ElideRight
|
||||
color: timelineRoot.palette.text
|
||||
|
||||
background: Rectangle {
|
||||
color: timelineRoot.palette.base
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,49 +1,49 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import im.nheko
|
||||
|
||||
Rectangle {
|
||||
property bool fitsMetadata: parent.width - redactedLayout.width > metadataWidth + 4
|
||||
property int metadataWidth
|
||||
|
||||
color: timelineRoot.palette.alternateBase
|
||||
height: redactedLayout.implicitHeight + Nheko.paddingSmall
|
||||
implicitWidth: redactedLayout.implicitWidth + 2 * Nheko.paddingMedium
|
||||
width: Math.min(parent.width,implicitWidth+1)
|
||||
radius: fontMetrics.lineSpacing / 2 + 2 * Nheko.paddingSmall
|
||||
color: timelineRoot.palette.alternateBase
|
||||
property int metadataWidth
|
||||
property bool fitsMetadata: parent.width - redactedLayout.width > metadataWidth + 4
|
||||
width: Math.min(parent.width, implicitWidth + 1)
|
||||
|
||||
RowLayout {
|
||||
id: redactedLayout
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - 2 * Nheko.paddingMedium
|
||||
spacing: Nheko.paddingSmall
|
||||
width: parent.width - 2 * Nheko.paddingMedium
|
||||
|
||||
Image {
|
||||
id: trashImg
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
Layout.preferredWidth: fontMetrics.font.pixelSize
|
||||
Layout.preferredHeight: fontMetrics.font.pixelSize
|
||||
Layout.preferredWidth: fontMetrics.font.pixelSize
|
||||
source: "image://colorimage/:/icons/icons/ui/delete.svg?" + timelineRoot.palette.text
|
||||
}
|
||||
Label {
|
||||
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.visible: hh.hovered
|
||||
color: timelineRoot.palette.text
|
||||
text: redactedPair["first"]
|
||||
wrapMode: Label.WordWrap
|
||||
|
||||
HoverHandler {
|
||||
id: hh
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import Qt.labs.platform 1.1 as Platform
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.3
|
||||
|
@ -14,129 +12,124 @@ import "../"
|
|||
AbstractButton {
|
||||
id: r
|
||||
|
||||
property color userColor: "red"
|
||||
property double proportionalHeight
|
||||
property int type
|
||||
property string typeString
|
||||
property int originalWidth
|
||||
property string blurhash
|
||||
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 int duration
|
||||
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 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
|
||||
implicitHeight: replyContainer.height
|
||||
implicitWidth: visible ? colorLine.width + Math.max(replyContainer.implicitWidth, userName_.fullTextWidth) : 0 // visible? seems to be causing issues
|
||||
|
||||
NhekoCursorShape {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: colorLine
|
||||
|
||||
anchors.top: replyContainer.top
|
||||
anchors.bottom: replyContainer.bottom
|
||||
width: 4
|
||||
color: TimelineManager.userColor(userId, timelineRoot.palette.base)
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
let link = reply.child.linkAt != undefined && reply.child.linkAt(pressX - colorLine.width, pressY - userName_.implicitHeight);
|
||||
if (link) {
|
||||
Nheko.openLink(link)
|
||||
Nheko.openLink(link);
|
||||
} else {
|
||||
room.showEvent(r.eventId)
|
||||
room.showEvent(r.eventId);
|
||||
}
|
||||
}
|
||||
onPressAndHold: replyContextMenu.show(reply.child.copyText, reply.child.linkAt(pressX - colorLine.width, pressY - userName_.implicitHeight), r.eventId)
|
||||
|
||||
NhekoCursorShape {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
Rectangle {
|
||||
id: colorLine
|
||||
anchors.bottom: replyContainer.bottom
|
||||
anchors.top: replyContainer.top
|
||||
color: TimelineManager.userColor(userId, timelineRoot.palette.base)
|
||||
width: 4
|
||||
}
|
||||
ColumnLayout {
|
||||
id: replyContainer
|
||||
|
||||
anchors.left: colorLine.right
|
||||
width: parent.width - 4
|
||||
spacing: 0
|
||||
width: parent.width - 4
|
||||
|
||||
TapHandler {
|
||||
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
|
||||
}
|
||||
gesturePolicy: TapHandler.ReleaseWithinBounds
|
||||
|
||||
onSingleTapped: replyContextMenu.show(reply.child.copyText, reply.child.linkAt(eventPoint.position.x, eventPoint.position.y - userName_.implicitHeight), r.eventId)
|
||||
}
|
||||
AbstractButton {
|
||||
Layout.leftMargin: 4
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 4
|
||||
|
||||
contentItem: ElidedLabel {
|
||||
id: userName_
|
||||
fullText: userName
|
||||
color: r.userColor
|
||||
elideWidth: width
|
||||
fullText: userName
|
||||
textFormat: Text.RichText
|
||||
width: parent.width
|
||||
elideWidth: width
|
||||
}
|
||||
onClicked: room.openUserProfile(userId)
|
||||
}
|
||||
|
||||
onClicked: room.openUserProfile(userId)
|
||||
}
|
||||
MessageDelegate {
|
||||
id: reply
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 4
|
||||
Layout.preferredHeight: height
|
||||
id: reply
|
||||
blurhash: r.blurhash
|
||||
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
|
||||
filename: r.filename
|
||||
filesize: r.filesize
|
||||
formattedBody: r.formattedBody
|
||||
isOnlyEmoji: r.isOnlyEmoji
|
||||
isReply: true
|
||||
isStateEvent: r.isStateEvent
|
||||
originalWidth: r.originalWidth
|
||||
proportionalHeight: r.proportionalHeight
|
||||
relatedEventCacheBuster: r.relatedEventCacheBuster
|
||||
roomName: r.roomName
|
||||
roomTopic: r.roomTopic
|
||||
thumbnailUrl: r.thumbnailUrl
|
||||
type: r.type
|
||||
typeString: r.typeString ?? ""
|
||||
url: r.url
|
||||
thumbnailUrl: r.thumbnailUrl
|
||||
duration: r.duration
|
||||
originalWidth: r.originalWidth
|
||||
isOnlyEmoji: r.isOnlyEmoji
|
||||
isStateEvent: r.isStateEvent
|
||||
userId: r.userId
|
||||
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 {
|
||||
id: backgroundItem
|
||||
|
||||
z: -1
|
||||
anchors.fill: replyContainer
|
||||
property color userColor: TimelineManager.userColor(userId, 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: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import ".."
|
||||
import "../"
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import im.nheko
|
||||
|
||||
MatrixText {
|
||||
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 isReply
|
||||
required property string formatted
|
||||
property string copyText: selectedText ? getText(selectionStart, selectionEnd) : body
|
||||
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
|
||||
text: "
|
||||
|
@ -37,16 +41,10 @@ MatrixText {
|
|||
</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>")
|
||||
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 {
|
||||
enabled: isReply
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: isReply
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.10
|
||||
import QtQuick.Controls 2.3
|
||||
import QtQuick.Window 2.13
|
||||
|
@ -13,74 +11,56 @@ ApplicationWindow {
|
|||
|
||||
property var flow
|
||||
|
||||
onClosing: VerificationManager.removeVerificationFlow(flow)
|
||||
title: stack.currentItem.title_
|
||||
color: timelineRoot.palette.window
|
||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||
minimumHeight: stack.implicitHeight
|
||||
modality: Qt.NonModal
|
||||
palette: timelineRoot.palette
|
||||
color: timelineRoot.palette.window
|
||||
minimumHeight: stack.implicitHeight
|
||||
title: stack.currentItem.title_
|
||||
width: stack.implicitWidth
|
||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||
|
||||
onClosing: VerificationManager.removeVerificationFlow(flow)
|
||||
|
||||
StackView {
|
||||
id: stack
|
||||
|
||||
anchors.fill: parent
|
||||
initialItem: newVerificationRequest
|
||||
implicitWidth: currentItem.implicitWidth
|
||||
implicitHeight: currentItem.implicitHeight
|
||||
implicitWidth: currentItem.implicitWidth
|
||||
initialItem: newVerificationRequest
|
||||
}
|
||||
|
||||
Component {
|
||||
id: newVerificationRequest
|
||||
|
||||
NewVerificationRequest {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Component {
|
||||
id: waiting
|
||||
|
||||
Waiting {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Component {
|
||||
id: success
|
||||
|
||||
Success {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Component {
|
||||
id: failed
|
||||
|
||||
Failed {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Component {
|
||||
id: digitVerification
|
||||
|
||||
DigitVerification {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Component {
|
||||
id: emojiVerification
|
||||
|
||||
EmojiVerification {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Item {
|
||||
state: flow.state
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "PromptStartVerification"
|
||||
|
@ -88,7 +68,6 @@ ApplicationWindow {
|
|||
StateChangeScript {
|
||||
script: stack.replace(newVerificationRequest)
|
||||
}
|
||||
|
||||
},
|
||||
State {
|
||||
name: "CompareEmoji"
|
||||
|
@ -96,7 +75,6 @@ ApplicationWindow {
|
|||
StateChangeScript {
|
||||
script: stack.replace(emojiVerification)
|
||||
}
|
||||
|
||||
},
|
||||
State {
|
||||
name: "CompareNumber"
|
||||
|
@ -104,7 +82,6 @@ ApplicationWindow {
|
|||
StateChangeScript {
|
||||
script: stack.replace(digitVerification)
|
||||
}
|
||||
|
||||
},
|
||||
State {
|
||||
name: "WaitingForKeys"
|
||||
|
@ -112,7 +89,6 @@ ApplicationWindow {
|
|||
StateChangeScript {
|
||||
script: stack.replace(waiting)
|
||||
}
|
||||
|
||||
},
|
||||
State {
|
||||
name: "WaitingForOtherToAccept"
|
||||
|
@ -120,7 +96,6 @@ ApplicationWindow {
|
|||
StateChangeScript {
|
||||
script: stack.replace(waiting)
|
||||
}
|
||||
|
||||
},
|
||||
State {
|
||||
name: "WaitingForMac"
|
||||
|
@ -128,7 +103,6 @@ ApplicationWindow {
|
|||
StateChangeScript {
|
||||
script: stack.replace(waiting)
|
||||
}
|
||||
|
||||
},
|
||||
State {
|
||||
name: "Success"
|
||||
|
@ -136,7 +110,6 @@ ApplicationWindow {
|
|||
StateChangeScript {
|
||||
script: stack.replace(success)
|
||||
}
|
||||
|
||||
},
|
||||
State {
|
||||
name: "Failed"
|
||||
|
@ -144,9 +117,7 @@ ApplicationWindow {
|
|||
StateChangeScript {
|
||||
script: stack.replace(failed)
|
||||
}
|
||||
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.3
|
||||
import QtQuick.Controls 2.3
|
||||
import QtQuick.Layouts 1.10
|
||||
|
@ -10,6 +8,7 @@ import im.nheko
|
|||
|
||||
Pane {
|
||||
property string title: qsTr("Verification Code")
|
||||
|
||||
background: Rectangle {
|
||||
color: timelineRoot.palette.window
|
||||
}
|
||||
|
@ -19,61 +18,57 @@ Pane {
|
|||
spacing: 16
|
||||
|
||||
Label {
|
||||
Layout.preferredWidth: 400
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.Wrap
|
||||
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!")
|
||||
Layout.preferredWidth: 400
|
||||
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
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true; }
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
Label {
|
||||
color: timelineRoot.palette.text
|
||||
font.pixelSize: Qt.application.font.pixelSize * 2
|
||||
text: flow.sasList[0]
|
||||
color: timelineRoot.palette.text
|
||||
}
|
||||
|
||||
Label {
|
||||
color: timelineRoot.palette.text
|
||||
font.pixelSize: Qt.application.font.pixelSize * 2
|
||||
text: flow.sasList[1]
|
||||
color: timelineRoot.palette.text
|
||||
}
|
||||
|
||||
Label {
|
||||
color: timelineRoot.palette.text
|
||||
font.pixelSize: Qt.application.font.pixelSize * 2
|
||||
text: flow.sasList[2]
|
||||
color: timelineRoot.palette.text
|
||||
}
|
||||
|
||||
}
|
||||
Item { Layout.fillHeight: true; }
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
RowLayout {
|
||||
Button {
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
text: qsTr("They do not match!")
|
||||
|
||||
onClicked: {
|
||||
flow.cancel();
|
||||
dialog.close();
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
text: qsTr("They match!")
|
||||
|
||||
onClicked: flow.next()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.3
|
||||
import QtQuick.Controls 2.3
|
||||
import QtQuick.Layouts 1.10
|
||||
|
||||
Rectangle {
|
||||
color: "red"
|
||||
height: Qt.application.font.pixelSize * 4
|
||||
implicitHeight: Qt.application.font.pixelSize * 4
|
||||
implicitWidth: col.width
|
||||
height: Qt.application.font.pixelSize * 4
|
||||
width: col.width
|
||||
|
||||
ColumnLayout {
|
||||
|
@ -22,17 +20,14 @@ Rectangle {
|
|||
anchors.bottom: parent.bottom
|
||||
|
||||
Label {
|
||||
height: font.pixelSize * 2
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: col.emoji.emoji
|
||||
font.pixelSize: Qt.application.font.pixelSize * 2
|
||||
height: font.pixelSize * 2
|
||||
text: col.emoji.emoji
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
||||
text: col.emoji.description
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.3
|
||||
import QtQuick.Controls 2.3
|
||||
import QtQuick.Layouts 1.10
|
||||
|
@ -10,6 +8,7 @@ import im.nheko
|
|||
|
||||
Pane {
|
||||
property string title: qsTr("Verification Code")
|
||||
|
||||
background: Rectangle {
|
||||
color: timelineRoot.palette.window
|
||||
}
|
||||
|
@ -19,15 +18,16 @@ Pane {
|
|||
spacing: 16
|
||||
|
||||
Label {
|
||||
Layout.preferredWidth: 400
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.Wrap
|
||||
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!")
|
||||
Layout.preferredWidth: 400
|
||||
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
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true; }
|
||||
RowLayout {
|
||||
id: emojis
|
||||
|
||||
|
@ -357,7 +357,6 @@ Pane {
|
|||
|
||||
Repeater {
|
||||
id: repeater
|
||||
|
||||
model: 7
|
||||
|
||||
delegate: Rectangle {
|
||||
|
@ -376,49 +375,42 @@ Pane {
|
|||
Label {
|
||||
//height: font.pixelSize * 2
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: col.emoji.emoji
|
||||
font.pixelSize: Qt.application.font.pixelSize * 2
|
||||
font.family: Settings.emojiFont
|
||||
color: timelineRoot.palette.text
|
||||
font.family: Settings.emojiFont
|
||||
font.pixelSize: Qt.application.font.pixelSize * 2
|
||||
text: col.emoji.emoji
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
||||
text: col.emoji.description
|
||||
color: timelineRoot.palette.text
|
||||
text: col.emoji.description
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Item { Layout.fillHeight: true; }
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
RowLayout {
|
||||
Button {
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
text: qsTr("They do not match!")
|
||||
|
||||
onClicked: {
|
||||
flow.cancel();
|
||||
dialog.close();
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
text: qsTr("They match!")
|
||||
|
||||
onClicked: flow.next()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.3
|
||||
import QtQuick.Controls 2.3
|
||||
import QtQuick.Layouts 1.10
|
||||
|
@ -10,6 +8,7 @@ import im.nheko
|
|||
|
||||
Pane {
|
||||
property string title: qsTr("Verification failed")
|
||||
|
||||
background: Rectangle {
|
||||
color: timelineRoot.palette.window
|
||||
}
|
||||
|
@ -20,10 +19,9 @@ Pane {
|
|||
|
||||
Text {
|
||||
id: content
|
||||
|
||||
Layout.preferredWidth: 400
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.Wrap
|
||||
Layout.preferredWidth: 400
|
||||
color: timelineRoot.palette.text
|
||||
text: {
|
||||
switch (flow.error) {
|
||||
case DeviceVerificationFlow.UnknownMethod:
|
||||
|
@ -42,25 +40,22 @@ Pane {
|
|||
return qsTr("Unknown verification error.");
|
||||
}
|
||||
}
|
||||
color: timelineRoot.palette.text
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true; }
|
||||
|
||||
RowLayout {
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
text: qsTr("Close")
|
||||
|
||||
onClicked: dialog.close()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.3
|
||||
import QtQuick.Controls 2.3
|
||||
import QtQuick.Layouts 1.10
|
||||
|
@ -10,6 +8,7 @@ import im.nheko
|
|||
|
||||
Pane {
|
||||
property string title: flow.sender ? qsTr("Send Verification Request") : qsTr("Received Verification Request")
|
||||
|
||||
background: Rectangle {
|
||||
color: timelineRoot.palette.window
|
||||
}
|
||||
|
@ -19,11 +18,10 @@ Pane {
|
|||
spacing: 16
|
||||
|
||||
Label {
|
||||
// Self verification
|
||||
|
||||
Layout.preferredWidth: 400
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.Wrap
|
||||
// Self verification
|
||||
Layout.preferredWidth: 400
|
||||
color: timelineRoot.palette.text
|
||||
text: {
|
||||
if (flow.sender) {
|
||||
if (flow.isSelfVerification)
|
||||
|
@ -42,34 +40,31 @@ Pane {
|
|||
return qsTr("Your device (%1) has requested to be verified.").arg(flow.deviceId);
|
||||
}
|
||||
}
|
||||
color: timelineRoot.palette.text
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true; }
|
||||
|
||||
RowLayout {
|
||||
Button {
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
text: flow.sender ? qsTr("Cancel") : qsTr("Deny")
|
||||
|
||||
onClicked: {
|
||||
flow.cancel();
|
||||
dialog.close();
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
text: flow.sender ? qsTr("Start verification") : qsTr("Accept")
|
||||
|
||||
onClicked: flow.next()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.3
|
||||
import QtQuick.Controls 2.3
|
||||
import QtQuick.Layouts 1.10
|
||||
|
@ -10,6 +8,7 @@ import im.nheko
|
|||
|
||||
Pane {
|
||||
property string title: qsTr("Successful Verification")
|
||||
|
||||
background: Rectangle {
|
||||
color: timelineRoot.palette.window
|
||||
}
|
||||
|
@ -20,30 +19,26 @@ Pane {
|
|||
|
||||
Label {
|
||||
id: content
|
||||
|
||||
Layout.preferredWidth: 400
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.Wrap
|
||||
text: qsTr("Verification successful! Both sides verified their devices!")
|
||||
Layout.preferredWidth: 400
|
||||
color: timelineRoot.palette.text
|
||||
text: qsTr("Verification successful! Both sides verified their devices!")
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true; }
|
||||
|
||||
RowLayout {
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
text: qsTr("Close")
|
||||
|
||||
onClicked: dialog.close()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import "../ui"
|
||||
import QtQuick 2.3
|
||||
import QtQuick.Controls 2.3
|
||||
|
@ -11,6 +9,7 @@ import im.nheko
|
|||
|
||||
Pane {
|
||||
property string title: qsTr("Waiting for other party…")
|
||||
|
||||
background: Rectangle {
|
||||
color: timelineRoot.palette.window
|
||||
}
|
||||
|
@ -21,10 +20,9 @@ Pane {
|
|||
|
||||
Label {
|
||||
id: content
|
||||
|
||||
Layout.preferredWidth: 400
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.Wrap
|
||||
Layout.preferredWidth: 400
|
||||
color: timelineRoot.palette.text
|
||||
text: {
|
||||
switch (flow.state) {
|
||||
case "WaitingForOtherToAccept":
|
||||
|
@ -35,33 +33,32 @@ Pane {
|
|||
return qsTr("Waiting for other side to complete the verification process.");
|
||||
}
|
||||
}
|
||||
color: timelineRoot.palette.text
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true; }
|
||||
Spinner {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
foreground: timelineRoot.palette.mid
|
||||
}
|
||||
Item { Layout.fillHeight: true; }
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
RowLayout {
|
||||
Button {
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
text: qsTr("Cancel")
|
||||
|
||||
onClicked: {
|
||||
flow.cancel();
|
||||
dialog.close();
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import ".."
|
||||
import "../"
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Window 2.13
|
||||
import QtQuick.Layouts 1.3
|
||||
|
@ -13,13 +11,31 @@ import im.nheko
|
|||
|
||||
ApplicationWindow {
|
||||
id: createDirectRoot
|
||||
title: qsTr("Create Direct Chat")
|
||||
property var profile
|
||||
|
||||
property bool otherUserHasE2ee: profile ? profile.deviceList.rowCount() > 0 : true
|
||||
property var profile
|
||||
|
||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||
minimumHeight: layout.implicitHeight + footer.implicitHeight + Nheko.paddingLarge * 2
|
||||
minimumWidth: Math.max(footer.implicitWidth, layout.implicitWidth)
|
||||
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: {
|
||||
userID.forceActiveFocus();
|
||||
|
@ -27,9 +43,9 @@ ApplicationWindow {
|
|||
|
||||
Shortcut {
|
||||
sequence: StandardKey.Cancel
|
||||
|
||||
onActivated: createDirectRoot.close()
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
anchors.fill: parent
|
||||
|
@ -38,43 +54,44 @@ ApplicationWindow {
|
|||
|
||||
GridLayout {
|
||||
Layout.fillWidth: true
|
||||
rows: 2
|
||||
columnSpacing: Nheko.paddingMedium
|
||||
columns: 2
|
||||
rowSpacing: Nheko.paddingSmall
|
||||
columnSpacing: Nheko.paddingMedium
|
||||
rows: 2
|
||||
|
||||
Avatar {
|
||||
Layout.rowSpan: 2
|
||||
Layout.preferredWidth: Nheko.avatarSize
|
||||
Layout.preferredHeight: Nheko.avatarSize
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
userid: profile? profile.userid : ""
|
||||
url: profile? profile.avatarUrl.replace("mxc://", "image://MxcImage/") : null
|
||||
Layout.preferredHeight: Nheko.avatarSize
|
||||
Layout.preferredWidth: Nheko.avatarSize
|
||||
Layout.rowSpan: 2
|
||||
displayName: profile ? profile.displayName : ""
|
||||
enabled: false
|
||||
url: profile ? profile.avatarUrl.replace("mxc://", "image://MxcImage/") : null
|
||||
userid: profile ? profile.userid : ""
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: profile? profile.displayName : ""
|
||||
color: TimelineManager.userColor(userID.text, timelineRoot.palette.window)
|
||||
font.pointSize: fontMetrics.font.pointSize
|
||||
text: profile ? profile.displayName : ""
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: userID.text
|
||||
color: timelineRoot.palette.placeholderText
|
||||
font.pointSize: fontMetrics.font.pointSize * 0.9
|
||||
text: userID.text
|
||||
}
|
||||
}
|
||||
|
||||
MatrixTextField {
|
||||
id: userID
|
||||
|
||||
property bool isValidMxid: text.match("@.+?:.{3,}")
|
||||
|
||||
Layout.fillWidth: true
|
||||
focus: true
|
||||
label: qsTr("User to invite")
|
||||
placeholderText: qsTr("@user:server.tld")
|
||||
|
||||
onTextChanged: {
|
||||
if (isValidMxid) {
|
||||
profile = TimelineManager.getGlobalUserProfile(text);
|
||||
|
@ -82,35 +99,23 @@ ApplicationWindow {
|
|||
profile = null;
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
text: qsTr("Encryption")
|
||||
Layout.fillWidth: true
|
||||
color: timelineRoot.palette.text
|
||||
text: qsTr("Encryption")
|
||||
}
|
||||
ToggleButton {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
id: encryption
|
||||
Layout.alignment: Qt.AlignRight
|
||||
checked: otherUserHasE2ee
|
||||
}
|
||||
}
|
||||
|
||||
Item {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()
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import ".."
|
||||
import "../"
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Window 2.13
|
||||
import QtQuick.Layouts 1.3
|
||||
|
@ -12,11 +10,32 @@ import im.nheko
|
|||
|
||||
ApplicationWindow {
|
||||
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
|
||||
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: {
|
||||
newRoomName.forceActiveFocus();
|
||||
|
@ -24,6 +43,7 @@ ApplicationWindow {
|
|||
|
||||
Shortcut {
|
||||
sequence: StandardKey.Cancel
|
||||
|
||||
onActivated: createRoomRoot.close()
|
||||
}
|
||||
GridLayout {
|
||||
|
@ -37,7 +57,6 @@ ApplicationWindow {
|
|||
id: newRoomName
|
||||
Layout.columnSpan: 2
|
||||
Layout.fillWidth: true
|
||||
|
||||
focus: true
|
||||
label: qsTr("Name")
|
||||
placeholderText: qsTr("No name")
|
||||
|
@ -46,23 +65,21 @@ ApplicationWindow {
|
|||
id: newRoomTopic
|
||||
Layout.columnSpan: 2
|
||||
Layout.fillWidth: true
|
||||
|
||||
focus: true
|
||||
label: qsTr("Topic")
|
||||
placeholderText: qsTr("No topic")
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.preferredHeight: newRoomName.height / 2
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.columnSpan: 2
|
||||
Layout.fillWidth: true
|
||||
|
||||
Label {
|
||||
Layout.preferredWidth: implicitWidth
|
||||
text: qsTr("#")
|
||||
color: timelineRoot.palette.text
|
||||
text: qsTr("#")
|
||||
}
|
||||
MatrixTextField {
|
||||
id: newRoomAlias
|
||||
|
@ -70,88 +87,73 @@ ApplicationWindow {
|
|||
placeholderText: qsTr("Alias")
|
||||
}
|
||||
Label {
|
||||
Layout.preferredWidth: implicitWidth
|
||||
property string userName: userInfoGrid.profile.userid
|
||||
text: userName.substring(userName.indexOf(":"))
|
||||
|
||||
Layout.preferredWidth: implicitWidth
|
||||
color: timelineRoot.palette.text
|
||||
text: userName.substring(userName.indexOf(":"))
|
||||
}
|
||||
}
|
||||
Label {
|
||||
Layout.preferredWidth: implicitWidth
|
||||
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
|
||||
text: qsTr("Public")
|
||||
|
||||
HoverHandler {
|
||||
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 {
|
||||
id: isPublic
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.preferredWidth: implicitWidth
|
||||
id: isPublic
|
||||
checked: false
|
||||
}
|
||||
Label {
|
||||
Layout.preferredWidth: implicitWidth
|
||||
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
|
||||
text: qsTr("Trusted")
|
||||
|
||||
HoverHandler {
|
||||
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 {
|
||||
id: isTrusted
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.preferredWidth: implicitWidth
|
||||
id: isTrusted
|
||||
checked: false
|
||||
enabled: !isPublic.checked
|
||||
}
|
||||
Label {
|
||||
Layout.preferredWidth: implicitWidth
|
||||
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
|
||||
text: qsTr("Encryption")
|
||||
|
||||
HoverHandler {
|
||||
id: encryptionHover
|
||||
}
|
||||
ToolTip.visible: encryptionHover.hovered
|
||||
ToolTip.text: qsTr("Caution: Encryption cannot be disabled")
|
||||
ToolTip.delay: Nheko.tooltipDelay
|
||||
}
|
||||
ToggleButton {
|
||||
id: isEncrypted
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.preferredWidth: implicitWidth
|
||||
id: isEncrypted
|
||||
checked: false
|
||||
}
|
||||
|
||||
Item {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();
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import ".."
|
||||
import "../"
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.5
|
||||
import QtQuick.Layouts 1.3
|
||||
|
@ -11,117 +9,108 @@ import im.nheko
|
|||
ApplicationWindow {
|
||||
id: hiddenEventsDialog
|
||||
|
||||
property string roomid: ""
|
||||
property string roomName: ""
|
||||
property var onAccepted: undefined
|
||||
property string roomName: ""
|
||||
property string roomid: ""
|
||||
|
||||
modality: Qt.NonModal
|
||||
flags: Qt.Dialog | Qt.WindowTitleHint
|
||||
minimumWidth: 250
|
||||
minimumHeight: 220
|
||||
|
||||
HiddenEvents {
|
||||
id: hiddenEvents
|
||||
|
||||
roomid: hiddenEventsDialog.roomid
|
||||
}
|
||||
|
||||
minimumWidth: 250
|
||||
modality: Qt.NonModal
|
||||
title: {
|
||||
if (roomid) {
|
||||
return qsTr("Hidden events for %1").arg(roomName);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
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 {
|
||||
id: dbb
|
||||
|
||||
standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
|
||||
|
||||
onAccepted: {
|
||||
hiddenEvents.save();
|
||||
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-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Window 2.15
|
||||
|
||||
import ".."
|
||||
|
||||
import "../"
|
||||
import im.nheko
|
||||
|
||||
Window {
|
||||
id: imageOverlay
|
||||
|
||||
required property string url
|
||||
required property string eventId
|
||||
required property Room room
|
||||
required property int originalWidth
|
||||
required property double proportionalHeight
|
||||
|
||||
flags: Qt.FramelessWindowHint
|
||||
required property Room room
|
||||
required property string url
|
||||
|
||||
//visibility: Window.FullScreen
|
||||
color: Qt.rgba(0.2, 0.2, 0.2, 0.66)
|
||||
flags: Qt.FramelessWindowHint
|
||||
|
||||
Shortcut {
|
||||
sequence: StandardKey.Cancel
|
||||
|
||||
onActivated: imageOverlay.close()
|
||||
}
|
||||
|
||||
|
||||
|
||||
Item {
|
||||
id: imgContainer
|
||||
|
||||
property int imgSrcWidth: (originalWidth && originalWidth > 200) ? originalWidth : Screen.width
|
||||
property int imgSrcHeight: proportionalHeight ? imgSrcWidth * proportionalHeight : Screen.height
|
||||
property int imgSrcWidth: (originalWidth && originalWidth > 200) ? originalWidth : Screen.width
|
||||
|
||||
height: Math.min(parent.height, imgSrcHeight)
|
||||
width: Math.min(parent.width, imgSrcWidth)
|
||||
|
||||
x: (parent.width - width)
|
||||
y: (parent.height - height)
|
||||
|
||||
onScaleChanged: {
|
||||
if (scale > 10)
|
||||
scale = 10;
|
||||
if (scale < 0.1)
|
||||
scale = 0.1;
|
||||
}
|
||||
|
||||
Image {
|
||||
id: img
|
||||
|
||||
visible: !mxcimage.loaded
|
||||
property bool loaded: status == Image.Ready
|
||||
|
||||
anchors.fill: parent
|
||||
source: url.replace("mxc://", "image://MxcImage/")
|
||||
asynchronous: true
|
||||
fillMode: Image.PreserveAspectFit
|
||||
smooth: true
|
||||
mipmap: true
|
||||
property bool loaded: status == Image.Ready
|
||||
smooth: true
|
||||
source: url.replace("mxc://", "image://MxcImage/")
|
||||
visible: !mxcimage.loaded
|
||||
}
|
||||
|
||||
MxcAnimatedImage {
|
||||
id: mxcimage
|
||||
|
||||
visible: loaded
|
||||
anchors.fill: parent
|
||||
roomm: imageOverlay.room
|
||||
play: !Settings.animateImagesOnHover || mouseArea.hovered
|
||||
eventId: imageOverlay.eventId
|
||||
}
|
||||
|
||||
onScaleChanged: {
|
||||
if (scale > 10) scale = 10;
|
||||
if (scale < 0.1) scale = 0.1
|
||||
play: !Settings.animateImagesOnHover || mouseArea.hovered
|
||||
roomm: imageOverlay.room
|
||||
visible: loaded
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
|
||||
|
||||
PinchHandler {
|
||||
target: imgContainer
|
||||
maximumScale: 10
|
||||
minimumScale: 0.1
|
||||
target: imgContainer
|
||||
}
|
||||
|
||||
WheelHandler {
|
||||
property: "scale"
|
||||
target: imgContainer
|
||||
}
|
||||
|
||||
DragHandler {
|
||||
target: imgContainer
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
id: mouseArea
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Row {
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.margins: Nheko.paddingLarge
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
spacing: Nheko.paddingMedium
|
||||
|
||||
ImageButton {
|
||||
height: 48
|
||||
width: 48
|
||||
hoverEnabled: true
|
||||
image: ":/icons/icons/ui/download.svg"
|
||||
width: 48
|
||||
|
||||
//ToolTip.visible: hovered
|
||||
//ToolTip.delay: Nheko.tooltipDelay
|
||||
//ToolTip.text: qsTr("Download")
|
||||
|
@ -122,14 +108,14 @@ Window {
|
|||
}
|
||||
ImageButton {
|
||||
height: 48
|
||||
width: 48
|
||||
hoverEnabled: true
|
||||
image: ":/icons/icons/ui/dismiss.svg"
|
||||
width: 48
|
||||
|
||||
//ToolTip.visible: hovered
|
||||
//ToolTip.delay: Nheko.tooltipDelay
|
||||
//ToolTip.text: qsTr("Close")
|
||||
onClicked: imageOverlay.close()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import ".."
|
||||
import "../"
|
||||
import "../components"
|
||||
import Qt.labs.platform 1.1
|
||||
import QtQuick 2.12
|
||||
|
@ -15,316 +13,23 @@ ApplicationWindow {
|
|||
id: win
|
||||
|
||||
property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 2.3)
|
||||
property SingleImagePackModel imagePack
|
||||
property int currentImageIndex: -1
|
||||
property SingleImagePackModel imagePack
|
||||
readonly property int stickerDim: 128
|
||||
readonly property int stickerDimPad: 128 + Nheko.paddingSmall
|
||||
|
||||
title: qsTr("Editing image pack")
|
||||
height: 600
|
||||
width: 600
|
||||
palette: timelineRoot.palette
|
||||
color: timelineRoot.palette.base
|
||||
modality: Qt.WindowModal
|
||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||
|
||||
AdaptiveLayout {
|
||||
id: adaptiveView
|
||||
|
||||
anchors.fill: parent
|
||||
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 {
|
||||
height: 600
|
||||
modality: Qt.WindowModal
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
title: qsTr("Editing image pack")
|
||||
width: 600
|
||||
|
||||
footer: DialogButtonBox {
|
||||
id: buttons
|
||||
|
||||
standardButtons: DialogButtonBox.Save | DialogButtonBox.Cancel
|
||||
|
||||
onAccepted: {
|
||||
imagePack.save();
|
||||
win.close();
|
||||
|
@ -332,4 +37,269 @@ ApplicationWindow {
|
|||
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: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import ".."
|
||||
import "../"
|
||||
import "../components"
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
|
@ -13,96 +11,70 @@ import im.nheko
|
|||
ApplicationWindow {
|
||||
id: win
|
||||
|
||||
property Room room
|
||||
property ImagePackListModel packlist
|
||||
property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 2.3)
|
||||
property SingleImagePackModel currentPack: packlist.packAt(currentPackIndex)
|
||||
property int currentPackIndex: 0
|
||||
property ImagePackListModel packlist
|
||||
property Room room
|
||||
readonly property int stickerDim: 128
|
||||
readonly property int stickerDimPad: 128 + Nheko.paddingSmall
|
||||
|
||||
title: qsTr("Image pack settings")
|
||||
height: 600
|
||||
width: 800
|
||||
palette: timelineRoot.palette
|
||||
color: timelineRoot.palette.base
|
||||
modality: Qt.NonModal
|
||||
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 {
|
||||
id: packEditor
|
||||
|
||||
ImagePackEditorDialog {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
AdaptiveLayout {
|
||||
id: adaptiveView
|
||||
|
||||
anchors.fill: parent
|
||||
singlePageMode: false
|
||||
pageIndex: 0
|
||||
singlePageMode: false
|
||||
|
||||
AdaptiveLayoutElement {
|
||||
id: packlistC
|
||||
|
||||
visible: Settings.groupView
|
||||
minimumWidth: 200
|
||||
collapsedWidth: 200
|
||||
preferredWidth: 300
|
||||
maximumWidth: 300
|
||||
minimumWidth: 200
|
||||
preferredWidth: 300
|
||||
visible: Settings.groupView
|
||||
|
||||
ListView {
|
||||
model: packlist
|
||||
clip: true
|
||||
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
}
|
||||
model: packlist
|
||||
|
||||
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 displayName
|
||||
required property bool fromAccountData
|
||||
required property bool fromCurrentRoom
|
||||
property color importantText: timelineRoot.palette.text
|
||||
required property string statekey
|
||||
property color unimportantText: timelineRoot.palette.placeholderText
|
||||
|
||||
title: displayName
|
||||
roomid: statekey
|
||||
selectedIndex: currentPackIndex
|
||||
subtitle: {
|
||||
if (fromAccountData)
|
||||
return qsTr("Private pack");
|
||||
|
@ -111,31 +83,55 @@ ApplicationWindow {
|
|||
else
|
||||
return qsTr("Globally enabled pack");
|
||||
}
|
||||
selectedIndex: currentPackIndex
|
||||
roomid: statekey
|
||||
title: displayName
|
||||
|
||||
TapHandler {
|
||||
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 {
|
||||
id: packinfoC
|
||||
|
||||
Rectangle {
|
||||
color: timelineRoot.palette.window
|
||||
|
||||
ColumnLayout {
|
||||
id: packinfo
|
||||
|
||||
property string packName: currentPack ? currentPack.packname : ""
|
||||
property string attribution: currentPack ? currentPack.attribution : ""
|
||||
property string avatarUrl: currentPack ? currentPack.avatarUrl : ""
|
||||
property string packName: currentPack ? currentPack.packname : ""
|
||||
property string statekey: currentPack ? currentPack.statekey : ""
|
||||
|
||||
anchors.fill: parent
|
||||
|
@ -143,54 +139,50 @@ ApplicationWindow {
|
|||
spacing: Nheko.paddingLarge
|
||||
|
||||
Avatar {
|
||||
url: packinfo.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||
displayName: packinfo.packName
|
||||
roomid: packinfo.statekey
|
||||
height: 100
|
||||
width: 100
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
displayName: packinfo.packName
|
||||
enabled: false
|
||||
height: 100
|
||||
roomid: packinfo.statekey
|
||||
url: packinfo.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||
width: 100
|
||||
}
|
||||
|
||||
MatrixText {
|
||||
text: packinfo.packName
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.preferredWidth: packinfoC.width - Nheko.paddingLarge * 2
|
||||
font.pixelSize: Math.ceil(fontMetrics.pixelSize * 1.1)
|
||||
horizontalAlignment: TextEdit.AlignHCenter
|
||||
text: packinfo.packName
|
||||
}
|
||||
MatrixText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.preferredWidth: packinfoC.width - Nheko.paddingLarge * 2
|
||||
}
|
||||
|
||||
MatrixText {
|
||||
horizontalAlignment: TextEdit.AlignHCenter
|
||||
text: packinfo.attribution
|
||||
wrapMode: TextEdit.Wrap
|
||||
horizontalAlignment: TextEdit.AlignHCenter
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.preferredWidth: packinfoC.width - Nheko.paddingLarge * 2
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
visible: currentPack && currentPack.roomid != ""
|
||||
columns: 2
|
||||
rowSpacing: Nheko.paddingMedium
|
||||
visible: currentPack && currentPack.roomid != ""
|
||||
|
||||
MatrixText {
|
||||
text: qsTr("Enable globally")
|
||||
}
|
||||
|
||||
ToggleButton {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
ToolTip.text: qsTr("Enables this pack to be used in all rooms")
|
||||
checked: currentPack ? currentPack.isGloballyEnabled : false
|
||||
|
||||
onCheckedChanged: currentPack.isGloballyEnabled = checked
|
||||
Layout.alignment: Qt.AlignRight
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: qsTr("Edit")
|
||||
enabled: currentPack.canEdit
|
||||
text: qsTr("Edit")
|
||||
|
||||
onClicked: {
|
||||
var dialog = packEditor.createObject(timelineRoot, {
|
||||
"imagePack": currentPack
|
||||
|
@ -199,61 +191,40 @@ ApplicationWindow {
|
|||
timelineRoot.destroyOnClose(dialog);
|
||||
}
|
||||
}
|
||||
|
||||
GridView {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
model: currentPack
|
||||
cellWidth: stickerDimPad
|
||||
cellHeight: stickerDimPad
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
cacheBuffer: 500
|
||||
cellHeight: stickerDimPad
|
||||
cellWidth: stickerDimPad
|
||||
clip: true
|
||||
currentIndex: -1 // prevent sorting from stealing focus
|
||||
cacheBuffer: 500
|
||||
|
||||
model: currentPack
|
||||
|
||||
// Individual emoji
|
||||
delegate: AbstractButton {
|
||||
width: stickerDim
|
||||
height: stickerDim
|
||||
hoverEnabled: true
|
||||
ToolTip.text: ":" + model.shortCode + ": - " + model.body
|
||||
ToolTip.visible: hovered
|
||||
|
||||
contentItem: Image {
|
||||
height: stickerDim
|
||||
hoverEnabled: true
|
||||
width: stickerDim
|
||||
source: model.url.replace("mxc://", "image://MxcImage/") + "?scale"
|
||||
fillMode: Image.PreserveAspectFit
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
anchors.fill: parent
|
||||
color: hovered ? timelineRoot.palette.highlight : 'transparent'
|
||||
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: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import ".."
|
||||
import "../"
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.5
|
||||
import QtQuick.Layouts 1.3
|
||||
|
@ -12,53 +10,26 @@ import im.nheko
|
|||
ApplicationWindow {
|
||||
id: inputDialog
|
||||
|
||||
property alias prompt: promptLabel.text
|
||||
property alias echoMode: statusInput.echoMode
|
||||
property var onAccepted: undefined
|
||||
|
||||
modality: Qt.NonModal
|
||||
flags: Qt.Dialog
|
||||
width: 350
|
||||
height: fontMetrics.lineSpacing * 7
|
||||
property alias prompt: promptLabel.text
|
||||
|
||||
function forceActiveFocus() {
|
||||
statusInput.forceActiveFocus();
|
||||
}
|
||||
|
||||
Shortcut {
|
||||
sequence: StandardKey.Cancel
|
||||
onActivated: dbb.rejected()
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
flags: Qt.Dialog
|
||||
height: fontMetrics.lineSpacing * 7
|
||||
modality: Qt.NonModal
|
||||
width: 350
|
||||
|
||||
footer: DialogButtonBox {
|
||||
id: dbb
|
||||
|
||||
standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
|
||||
|
||||
onAccepted: {
|
||||
if (inputDialog.onAccepted)
|
||||
inputDialog.onAccepted(statusInput.text);
|
||||
|
||||
inputDialog.close();
|
||||
}
|
||||
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: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import ".."
|
||||
import "../"
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
import QtQuick.Layouts 1.12
|
||||
|
@ -12,9 +10,9 @@ import im.nheko
|
|||
ApplicationWindow {
|
||||
id: inviteDialogRoot
|
||||
|
||||
property string roomId
|
||||
property string plainRoomName
|
||||
property InviteesModel invitees
|
||||
property string plainRoomName
|
||||
property string roomId
|
||||
|
||||
function addInvite() {
|
||||
if (inviteeEntry.isValidMxid) {
|
||||
|
@ -22,43 +20,57 @@ ApplicationWindow {
|
|||
inviteeEntry.clear();
|
||||
}
|
||||
}
|
||||
|
||||
function cleanUpAndClose() {
|
||||
if (inviteeEntry.isValidMxid)
|
||||
addInvite();
|
||||
|
||||
invitees.accept();
|
||||
close();
|
||||
}
|
||||
|
||||
title: qsTr("Invite users to %1").arg(plainRoomName)
|
||||
height: 380
|
||||
width: 340
|
||||
palette: timelineRoot.palette
|
||||
color: timelineRoot.palette.window
|
||||
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 {
|
||||
sequence: "Ctrl+Enter"
|
||||
|
||||
onActivated: cleanUpAndClose()
|
||||
}
|
||||
|
||||
Shortcut {
|
||||
sequence: StandardKey.Cancel
|
||||
|
||||
onActivated: inviteDialogRoot.close()
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Nheko.paddingMedium
|
||||
spacing: Nheko.paddingMedium
|
||||
|
||||
Label {
|
||||
text: qsTr("User ID to invite")
|
||||
Layout.fillWidth: true
|
||||
color: timelineRoot.palette.text
|
||||
text: qsTr("User ID to invite")
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: Nheko.paddingMedium
|
||||
|
||||
|
@ -67,120 +79,88 @@ ApplicationWindow {
|
|||
|
||||
property bool isValidMxid: text.match("@.+?:.{3,}")
|
||||
|
||||
Layout.fillWidth: true
|
||||
backgroundColor: timelineRoot.palette.window
|
||||
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()
|
||||
Keys.onShortcutOverride: event.accepted = ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && (event.modifiers & Qt.ControlModifier))
|
||||
Keys.onPressed: {
|
||||
if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && (event.modifiers === Qt.ControlModifier))
|
||||
cleanUpAndClose();
|
||||
|
||||
}
|
||||
Keys.onShortcutOverride: event.accepted = ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && (event.modifiers & Qt.ControlModifier))
|
||||
onAccepted: {
|
||||
if (isValidMxid)
|
||||
addInvite();
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: qsTr("Add")
|
||||
enabled: inviteeEntry.isValidMxid
|
||||
text: qsTr("Add")
|
||||
|
||||
onClicked: addInvite()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: inviteesList
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
model: invitees
|
||||
|
||||
delegate: ItemDelegate {
|
||||
id: del
|
||||
|
||||
height: layout.implicitHeight + Nheko.paddingSmall * 2
|
||||
hoverEnabled: true
|
||||
width: ListView.view.width
|
||||
height: layout.implicitHeight + Nheko.paddingSmall * 2
|
||||
onClicked: TimelineManager.openGlobalUserProfile(model.mxid)
|
||||
|
||||
background: Rectangle {
|
||||
color: del.hovered ? timelineRoot.palette.dark : inviteDialogRoot.color
|
||||
}
|
||||
|
||||
onClicked: TimelineManager.openGlobalUserProfile(model.mxid)
|
||||
|
||||
RowLayout {
|
||||
id: layout
|
||||
|
||||
spacing: Nheko.paddingMedium
|
||||
anchors.centerIn: parent
|
||||
spacing: Nheko.paddingMedium
|
||||
width: del.width - Nheko.paddingSmall * 2
|
||||
|
||||
Avatar {
|
||||
width: Nheko.avatarSize
|
||||
height: Nheko.avatarSize
|
||||
userid: model.mxid
|
||||
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||
displayName: model.displayName
|
||||
enabled: false
|
||||
height: Nheko.avatarSize
|
||||
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||
userid: model.mxid
|
||||
width: Nheko.avatarSize
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: Nheko.paddingSmall
|
||||
|
||||
Label {
|
||||
text: model.displayName
|
||||
color: TimelineManager.userColor(model ? model.mxid : "", del.background.color)
|
||||
font.pointSize: fontMetrics.font.pointSize
|
||||
text: model.displayName
|
||||
}
|
||||
|
||||
Label {
|
||||
text: model.mxid
|
||||
color: del.hovered ? timelineRoot.palette.brightText : timelineRoot.palette.placeholderText
|
||||
font.pointSize: fontMetrics.font.pointSize * 0.9
|
||||
text: model.mxid
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
ImageButton {
|
||||
image: ":/icons/icons/ui/dismiss.svg"
|
||||
|
||||
onClicked: invitees.removeUser(model.mxid)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
NhekoCursorShape {
|
||||
anchors.fill: parent
|
||||
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: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import ".."
|
||||
import "../"
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.5
|
||||
import QtQuick.Layouts 1.3
|
||||
|
@ -11,50 +9,18 @@ import im.nheko
|
|||
|
||||
ApplicationWindow {
|
||||
id: joinRoomRoot
|
||||
|
||||
title: qsTr("Join room")
|
||||
modality: Qt.WindowModal
|
||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||
palette: timelineRoot.palette
|
||||
color: timelineRoot.palette.window
|
||||
width: 350
|
||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||
height: fontMetrics.lineSpacing * 7
|
||||
|
||||
Shortcut {
|
||||
sequence: StandardKey.Cancel
|
||||
onActivated: dbb.rejected()
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
modality: Qt.WindowModal
|
||||
palette: timelineRoot.palette
|
||||
title: qsTr("Join room")
|
||||
width: 350
|
||||
|
||||
footer: DialogButtonBox {
|
||||
id: dbb
|
||||
|
||||
standardButtons: DialogButtonBox.Cancel
|
||||
|
||||
onAccepted: {
|
||||
Nheko.joinRoom(input.text);
|
||||
joinRoomRoot.close();
|
||||
|
@ -64,11 +30,36 @@ ApplicationWindow {
|
|||
}
|
||||
|
||||
Button {
|
||||
text: "Join"
|
||||
enabled: input.text.match("#.+?:.{3,}")
|
||||
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: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import Qt.labs.platform 1.1 as P
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
|
@ -11,12 +9,13 @@ import im.nheko
|
|||
P.MessageDialog {
|
||||
id: leaveRoomRoot
|
||||
|
||||
required property string roomId
|
||||
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
|
||||
modality: Qt.ApplicationModal
|
||||
text: qsTr("Are you sure you want to leave?")
|
||||
title: qsTr("Leave room")
|
||||
|
||||
onAccepted: Rooms.leave(roomId, reason)
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import Qt.labs.platform 1.1 as P
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
|
@ -10,11 +8,11 @@ import im.nheko
|
|||
|
||||
P.MessageDialog {
|
||||
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
|
||||
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()
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,8 +1,6 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import im.nheko
|
||||
|
@ -12,44 +10,40 @@ ApplicationWindow {
|
|||
|
||||
property alias rawMessage: rawMessageView.text
|
||||
|
||||
height: 420
|
||||
width: 420
|
||||
palette: timelineRoot.palette
|
||||
color: timelineRoot.palette.window
|
||||
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 {
|
||||
sequence: StandardKey.Cancel
|
||||
|
||||
onActivated: rawMessageRoot.close()
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
anchors.margins: Nheko.paddingMedium
|
||||
anchors.fill: parent
|
||||
palette: timelineRoot.palette
|
||||
anchors.margins: Nheko.paddingMedium
|
||||
padding: Nheko.paddingMedium
|
||||
palette: timelineRoot.palette
|
||||
|
||||
TextArea {
|
||||
id: rawMessageView
|
||||
|
||||
font: Nheko.monospaceFont()
|
||||
anchors.fill: parent
|
||||
color: timelineRoot.palette.text
|
||||
font: Nheko.monospaceFont()
|
||||
readOnly: true
|
||||
textFormat: Text.PlainText
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
background: Rectangle {
|
||||
color: timelineRoot.palette.base
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
footer: DialogButtonBox {
|
||||
standardButtons: DialogButtonBox.Ok
|
||||
onAccepted: rawMessageRoot.close()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import ".."
|
||||
import "../"
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
|
@ -15,19 +13,25 @@ ApplicationWindow {
|
|||
property ReadReceiptsProxy readReceipts
|
||||
property Room room
|
||||
|
||||
color: timelineRoot.palette.window
|
||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||
height: 380
|
||||
width: 340
|
||||
minimumHeight: 380
|
||||
minimumWidth: headerTitle.width + 2 * Nheko.paddingMedium
|
||||
palette: timelineRoot.palette
|
||||
color: timelineRoot.palette.window
|
||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||
width: 340
|
||||
|
||||
footer: DialogButtonBox {
|
||||
standardButtons: DialogButtonBox.Ok
|
||||
|
||||
onAccepted: readReceiptsRoot.close()
|
||||
}
|
||||
|
||||
Shortcut {
|
||||
sequence: StandardKey.Cancel
|
||||
|
||||
onActivated: readReceiptsRoot.close()
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Nheko.paddingMedium
|
||||
|
@ -35,97 +39,78 @@ ApplicationWindow {
|
|||
|
||||
Label {
|
||||
id: headerTitle
|
||||
|
||||
color: timelineRoot.palette.text
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
text: qsTr("Read receipts")
|
||||
color: timelineRoot.palette.text
|
||||
font.pointSize: fontMetrics.font.pointSize * 1.5
|
||||
text: qsTr("Read receipts")
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
palette: timelineRoot.palette
|
||||
padding: Nheko.paddingMedium
|
||||
ScrollBar.horizontal.visible: false
|
||||
Layout.fillHeight: true
|
||||
Layout.minimumHeight: 200
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumHeight: 200
|
||||
ScrollBar.horizontal.visible: false
|
||||
padding: Nheko.paddingMedium
|
||||
palette: timelineRoot.palette
|
||||
|
||||
ListView {
|
||||
id: readReceiptsList
|
||||
|
||||
clip: true
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
clip: true
|
||||
model: readReceipts
|
||||
|
||||
delegate: ItemDelegate {
|
||||
id: del
|
||||
|
||||
onClicked: room.openUserProfile(model.mxid)
|
||||
padding: Nheko.paddingMedium
|
||||
width: ListView.view.width
|
||||
ToolTip.text: model.mxid
|
||||
ToolTip.visible: hovered
|
||||
height: receiptLayout.implicitHeight + Nheko.paddingSmall * 2
|
||||
hoverEnabled: true
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: model.mxid
|
||||
padding: Nheko.paddingMedium
|
||||
width: ListView.view.width
|
||||
|
||||
background: Rectangle {
|
||||
color: del.hovered ? timelineRoot.palette.dark : readReceiptsRoot.color
|
||||
}
|
||||
|
||||
onClicked: room.openUserProfile(model.mxid)
|
||||
|
||||
RowLayout {
|
||||
id: receiptLayout
|
||||
|
||||
spacing: Nheko.paddingMedium
|
||||
anchors.fill: parent
|
||||
anchors.margins: Nheko.paddingSmall
|
||||
spacing: Nheko.paddingMedium
|
||||
|
||||
Avatar {
|
||||
width: Nheko.avatarSize
|
||||
height: Nheko.avatarSize
|
||||
userid: model.mxid
|
||||
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||
displayName: model.displayName
|
||||
enabled: false
|
||||
height: Nheko.avatarSize
|
||||
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||
userid: model.mxid
|
||||
width: Nheko.avatarSize
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: Nheko.paddingSmall
|
||||
|
||||
Label {
|
||||
text: model.displayName
|
||||
color: TimelineManager.userColor(model ? model.mxid : "", timelineRoot.palette.window)
|
||||
font.pointSize: fontMetrics.font.pointSize
|
||||
text: model.displayName
|
||||
}
|
||||
|
||||
Label {
|
||||
text: model.timestamp
|
||||
color: timelineRoot.palette.placeholderText
|
||||
font.pointSize: fontMetrics.font.pointSize * 0.9
|
||||
text: model.timestamp
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
NhekoCursorShape {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
footer: DialogButtonBox {
|
||||
standardButtons: DialogButtonBox.Ok
|
||||
onAccepted: readReceiptsRoot.close()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import ".."
|
||||
import "../"
|
||||
import "../ui"
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.3
|
||||
|
@ -13,181 +11,157 @@ import im.nheko
|
|||
ApplicationWindow {
|
||||
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
|
||||
modality: Qt.WindowModal
|
||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||
height: 420
|
||||
minimumHeight: 340
|
||||
minimumWidth: 340
|
||||
modality: Qt.WindowModal
|
||||
palette: timelineRoot.palette
|
||||
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 {
|
||||
sequence: StandardKey.Cancel
|
||||
|
||||
onActivated: roomDirectoryWindow.close()
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: roomDirView
|
||||
|
||||
anchors.fill: parent
|
||||
model: publicRooms
|
||||
|
||||
delegate: Rectangle {
|
||||
id: roomDirDelegate
|
||||
|
||||
property int avatarSize: fontMetrics.height * 3.2
|
||||
property color background: timelineRoot.palette.window
|
||||
property color importantText: timelineRoot.palette.text
|
||||
property color unimportantText: timelineRoot.palette.placeholderText
|
||||
property int avatarSize: fontMetrics.height * 3.2
|
||||
|
||||
color: background
|
||||
height: avatarSize + Nheko.paddingLarge
|
||||
width: ListView.view.width
|
||||
|
||||
RowLayout {
|
||||
spacing: Nheko.paddingMedium
|
||||
anchors.fill: parent
|
||||
anchors.margins: Nheko.paddingMedium
|
||||
implicitHeight: textContent.implicitHeight
|
||||
spacing: Nheko.paddingMedium
|
||||
|
||||
Avatar {
|
||||
id: roomAvatar
|
||||
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.rightMargin: Nheko.paddingMedium
|
||||
width: avatarSize
|
||||
height: avatarSize
|
||||
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||
roomid: model.roomid
|
||||
displayName: model.name
|
||||
height: avatarSize
|
||||
roomid: model.roomid
|
||||
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||
width: avatarSize
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
id: textContent
|
||||
rows: 2
|
||||
columns: 2
|
||||
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
width: parent.width - avatar.width
|
||||
Layout.preferredWidth: parent.width - avatar.width
|
||||
columns: 2
|
||||
rows: 2
|
||||
width: parent.width - avatar.width
|
||||
|
||||
ElidedLabel {
|
||||
Layout.row: 0
|
||||
Layout.column: 0
|
||||
Layout.fillWidth: true
|
||||
Layout.row: 0
|
||||
color: roomDirDelegate.importantText
|
||||
elideWidth: width
|
||||
fullText: model.name
|
||||
}
|
||||
|
||||
Label {
|
||||
id: roomTopic
|
||||
|
||||
color: roomDirDelegate.unimportantText
|
||||
Layout.row: 1
|
||||
Layout.column: 0
|
||||
font.pointSize: fontMetrics.font.pointSize*0.9
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 2
|
||||
Layout.fillWidth: true
|
||||
Layout.row: 1
|
||||
color: roomDirDelegate.unimportantText
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: fontMetrics.font.pointSize * 0.9
|
||||
maximumLineCount: 2
|
||||
text: model.topic
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.row: 0
|
||||
Layout.column: 1
|
||||
id: roomCount
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.column: 1
|
||||
Layout.row: 0
|
||||
color: roomDirDelegate.unimportantText
|
||||
font.pointSize: fontMetrics.font.pointSize * 0.9
|
||||
text: model.numMembers.toString()
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.row: 1
|
||||
Layout.column: 1
|
||||
id: joinRoomButton
|
||||
Layout.column: 1
|
||||
Layout.row: 1
|
||||
enabled: model.canJoin
|
||||
text: "Join"
|
||||
|
||||
onClicked: publicRooms.joinRoom(model.index)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
footer: Item {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: parent.width
|
||||
visible: !publicRooms.reachedEndOfPagination && publicRooms.loadingMoreRooms
|
||||
anchors.margins: Nheko.paddingLarge
|
||||
// hacky but works
|
||||
height: loadingSpinner.height + 2 * Nheko.paddingLarge
|
||||
anchors.margins: Nheko.paddingLarge
|
||||
visible: !publicRooms.reachedEndOfPagination && publicRooms.loadingMoreRooms
|
||||
width: parent.width
|
||||
|
||||
Spinner {
|
||||
id: loadingSpinner
|
||||
|
||||
anchors.centerIn: parent
|
||||
anchors.margins: Nheko.paddingLarge
|
||||
running: visible
|
||||
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: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import ".."
|
||||
import "../"
|
||||
import "../ui"
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
|
@ -17,19 +15,25 @@ ApplicationWindow {
|
|||
property MemberList members
|
||||
property Room room
|
||||
|
||||
title: qsTr("Members of %1").arg(members.roomName)
|
||||
height: 650
|
||||
width: 420
|
||||
minimumHeight: 420
|
||||
palette: timelineRoot.palette
|
||||
color: timelineRoot.palette.window
|
||||
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 {
|
||||
sequence: StandardKey.Cancel
|
||||
|
||||
onActivated: roomMembersRoot.close()
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Nheko.paddingMedium
|
||||
|
@ -37,143 +41,135 @@ ApplicationWindow {
|
|||
|
||||
Avatar {
|
||||
id: roomAvatar
|
||||
|
||||
width: 130
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
displayName: members.roomName
|
||||
height: width
|
||||
roomid: members.roomId
|
||||
displayName: members.roomName
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
url: members.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||
width: 130
|
||||
|
||||
onClicked: TimelineManager.openRoomSettings(members.roomId)
|
||||
}
|
||||
|
||||
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
|
||||
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 {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
image: ":/icons/icons/ui/add-square-button.svg"
|
||||
hoverEnabled: true
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: qsTr("Invite more people")
|
||||
ToolTip.visible: hovered
|
||||
hoverEnabled: true
|
||||
image: ":/icons/icons/ui/add-square-button.svg"
|
||||
|
||||
onClicked: TimelineManager.openInviteUsers(members.roomId)
|
||||
}
|
||||
|
||||
MatrixTextField {
|
||||
id: searchBar
|
||||
|
||||
Layout.fillWidth: true
|
||||
placeholderText: qsTr("Search...")
|
||||
onTextChanged: members.setFilterString(text)
|
||||
|
||||
Component.onCompleted: forceActiveFocus()
|
||||
onTextChanged: members.setFilterString(text)
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: Nheko.paddingMedium
|
||||
|
||||
Label {
|
||||
text: qsTr("Sort by: ")
|
||||
color: Nheko.colors.text
|
||||
text: qsTr("Sort by: ")
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
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") }
|
||||
}
|
||||
Layout.fillWidth: true
|
||||
textRole: "text"
|
||||
valueRole: "data"
|
||||
onCurrentValueChanged: members.sortBy(currentValue)
|
||||
Layout.fillWidth: true
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
ScrollView {
|
||||
palette: timelineRoot.palette
|
||||
padding: Nheko.paddingMedium
|
||||
ScrollBar.horizontal.visible: false
|
||||
Layout.fillHeight: true
|
||||
Layout.minimumHeight: 200
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumHeight: 200
|
||||
ScrollBar.horizontal.visible: false
|
||||
padding: Nheko.paddingMedium
|
||||
palette: timelineRoot.palette
|
||||
|
||||
ListView {
|
||||
id: memberList
|
||||
|
||||
clip: true
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
clip: true
|
||||
model: members
|
||||
|
||||
|
||||
delegate: ItemDelegate {
|
||||
id: del
|
||||
|
||||
onClicked: Rooms.currentRoom.openUserProfile(model.mxid)
|
||||
padding: Nheko.paddingMedium
|
||||
width: ListView.view.width
|
||||
height: memberLayout.implicitHeight + Nheko.paddingSmall * 2
|
||||
hoverEnabled: true
|
||||
padding: Nheko.paddingMedium
|
||||
width: ListView.view.width
|
||||
|
||||
background: Rectangle {
|
||||
color: del.hovered ? timelineRoot.palette.dark : roomMembersRoot.color
|
||||
}
|
||||
|
||||
onClicked: Rooms.currentRoom.openUserProfile(model.mxid)
|
||||
|
||||
RowLayout {
|
||||
id: memberLayout
|
||||
|
||||
spacing: Nheko.paddingMedium
|
||||
anchors.centerIn: parent
|
||||
spacing: Nheko.paddingMedium
|
||||
width: parent.width - Nheko.paddingSmall * 2
|
||||
|
||||
Avatar {
|
||||
id: avatar
|
||||
|
||||
width: Nheko.avatarSize
|
||||
height: Nheko.avatarSize
|
||||
userid: model.mxid
|
||||
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||
displayName: model.displayName
|
||||
enabled: false
|
||||
height: Nheko.avatarSize
|
||||
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||
userid: model.mxid
|
||||
width: Nheko.avatarSize
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: Nheko.paddingSmall
|
||||
|
||||
ElidedLabel {
|
||||
fullText: model.displayName
|
||||
color: TimelineManager.userColor(model ? model.mxid : "", del.background.color)
|
||||
elideWidth: del.width - Nheko.paddingMedium * 2 - avatar.width - encryptInd.width
|
||||
font.pixelSize: fontMetrics.font.pixelSize
|
||||
elideWidth: del.width - Nheko.paddingMedium * 2 - avatar.width - encryptInd.width
|
||||
fullText: model.displayName
|
||||
}
|
||||
|
||||
ElidedLabel {
|
||||
fullText: model.mxid
|
||||
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
|
||||
font.pixelSize: Math.ceil(fontMetrics.font.pixelSize * 0.9)
|
||||
fullText: model.mxid
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
EncryptionIndicator {
|
||||
id: encryptInd
|
||||
|
||||
Layout.alignment: Qt.AlignRight
|
||||
visible: room.isEncrypted
|
||||
encrypted: room.isEncrypted
|
||||
trust: encrypted ? model.trustlevel : Crypto.Unverified
|
||||
ToolTip.text: {
|
||||
if (!encrypted)
|
||||
return qsTr("This room is not encrypted!");
|
||||
|
||||
switch (trust) {
|
||||
case Crypto.Verified:
|
||||
return qsTr("This user is verified.");
|
||||
|
@ -183,42 +179,30 @@ ApplicationWindow {
|
|||
return qsTr("This user has unverified devices!");
|
||||
}
|
||||
}
|
||||
encrypted: room.isEncrypted
|
||||
trust: encrypted ? model.trustlevel : Crypto.Unverified
|
||||
visible: room.isEncrypted
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
NhekoCursorShape {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
footer: Item {
|
||||
width: parent.width
|
||||
visible: (members.numUsersLoaded < members.memberCount) && members.loadingMoreMembers
|
||||
anchors.margins: Nheko.paddingMedium
|
||||
// use the default height if it's visible, otherwise no height at all
|
||||
height: membersLoadingSpinner.implicitHeight
|
||||
anchors.margins: Nheko.paddingMedium
|
||||
visible: (members.numUsersLoaded < members.memberCount) && members.loadingMoreMembers
|
||||
width: parent.width
|
||||
|
||||
Spinner {
|
||||
id: membersLoadingSpinner
|
||||
|
||||
anchors.centerIn: parent
|
||||
implicitHeight: parent.visible ? 35 : 0
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
footer: DialogButtonBox {
|
||||
standardButtons: DialogButtonBox.Ok
|
||||
onAccepted: roomMembersRoot.close()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import ".."
|
||||
import "../"
|
||||
import "../ui"
|
||||
import Qt.labs.platform 1.1 as Platform
|
||||
import QtQuick 2.15
|
||||
|
@ -18,112 +16,109 @@ ApplicationWindow {
|
|||
|
||||
property var roomSettings
|
||||
|
||||
minimumWidth: 340
|
||||
minimumHeight: 450
|
||||
width: 450
|
||||
height: 680
|
||||
palette: timelineRoot.palette
|
||||
color: timelineRoot.palette.window
|
||||
modality: Qt.NonModal
|
||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||
height: 680
|
||||
minimumHeight: 450
|
||||
minimumWidth: 340
|
||||
modality: Qt.NonModal
|
||||
palette: timelineRoot.palette
|
||||
title: qsTr("Room Settings")
|
||||
width: 450
|
||||
|
||||
footer: DialogButtonBox {
|
||||
standardButtons: DialogButtonBox.Ok
|
||||
|
||||
onAccepted: close()
|
||||
}
|
||||
|
||||
Shortcut {
|
||||
sequence: StandardKey.Cancel
|
||||
|
||||
onActivated: roomSettingsDialog.close()
|
||||
}
|
||||
|
||||
Flickable {
|
||||
id: flickable
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
anchors.fill: parent
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
clip: true
|
||||
flickableDirection: Flickable.VerticalFlick
|
||||
contentWidth: roomSettingsDialog.width
|
||||
contentHeight: contentLayout1.height
|
||||
contentWidth: roomSettingsDialog.width
|
||||
flickableDirection: Flickable.VerticalFlick
|
||||
|
||||
ColumnLayout {
|
||||
id: contentLayout1
|
||||
width: parent.width
|
||||
spacing: Nheko.paddingMedium
|
||||
width: parent.width
|
||||
|
||||
Avatar {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: Nheko.paddingMedium
|
||||
url: roomSettings.roomAvatarUrl.replace("mxc://", "image://MxcImage/")
|
||||
roomid: roomSettings.roomId
|
||||
displayName: roomSettings.roomName
|
||||
height: 130
|
||||
roomid: roomSettings.roomId
|
||||
url: roomSettings.roomAvatarUrl.replace("mxc://", "image://MxcImage/")
|
||||
width: 130
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
onClicked: {
|
||||
if (roomSettings.canChangeAvatar)
|
||||
roomSettings.updateAvatar();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Spinner {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
visible: roomSettings.isLoading
|
||||
foreground: timelineRoot.palette.mid
|
||||
running: roomSettings.isLoading
|
||||
visible: roomSettings.isLoading
|
||||
}
|
||||
|
||||
Text {
|
||||
id: errorText
|
||||
|
||||
color: "red"
|
||||
visible: opacity > 0
|
||||
opacity: 0
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
wrapMode: Text.Wrap // somehow still doesn't wrap
|
||||
Layout.fillWidth: true
|
||||
color: "red"
|
||||
opacity: 0
|
||||
visible: opacity > 0
|
||||
wrapMode: Text.Wrap // somehow still doesn't wrap
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
id: hideErrorAnimation
|
||||
|
||||
running: false
|
||||
|
||||
PauseAnimation {
|
||||
duration: 4000
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
target: errorText
|
||||
property: 'opacity'
|
||||
to: 0
|
||||
duration: 1000
|
||||
property: 'opacity'
|
||||
target: errorText
|
||||
to: 0
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: roomSettings
|
||||
function onDisplayError(errorMessage) {
|
||||
errorText.text = errorMessage;
|
||||
errorText.opacity = 1;
|
||||
hideErrorAnimation.restart();
|
||||
}
|
||||
}
|
||||
|
||||
target: roomSettings
|
||||
}
|
||||
TextEdit {
|
||||
id: roomName
|
||||
|
||||
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.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
|
||||
wrapMode: TextEdit.Wrap
|
||||
readOnly: !isNameEditingAllowed
|
||||
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: {
|
||||
if (event.matches(StandardKey.InsertLineSeparator) || event.matches(StandardKey.InsertParagraphSeparator)) {
|
||||
roomSettings.changeName(roomName.text);
|
||||
|
@ -131,18 +126,20 @@ ApplicationWindow {
|
|||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
Keys.onShortcutOverride: event.key === Qt.Key_Enter
|
||||
|
||||
ImageButton {
|
||||
id: nameChangeButton
|
||||
visible: roomSettings.canChangeName
|
||||
anchors.leftMargin: Nheko.paddingSmall
|
||||
ToolTip.delay: Nheko.tooltipDelay
|
||||
ToolTip.text: qsTr("Change name of this room")
|
||||
ToolTip.visible: hovered
|
||||
anchors.left: roomName.right
|
||||
anchors.leftMargin: Nheko.paddingSmall
|
||||
anchors.verticalCenter: roomName.verticalCenter
|
||||
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"
|
||||
visible: roomSettings.canChangeName
|
||||
|
||||
onClicked: {
|
||||
if (roomName.isNameEditingAllowed) {
|
||||
roomSettings.changeName(roomName.text);
|
||||
|
@ -154,65 +151,60 @@ ApplicationWindow {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Label {
|
||||
text: qsTr("%n member(s)", "", roomSettings.memberCount)
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
color: timelineRoot.palette.text
|
||||
text: qsTr("%n member(s)", "", roomSettings.memberCount)
|
||||
|
||||
TapHandler {
|
||||
onSingleTapped: TimelineManager.openRoomMembers(Rooms.getRoomById(roomSettings.roomId))
|
||||
}
|
||||
|
||||
NhekoCursorShape {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TextArea {
|
||||
id: roomTopic
|
||||
|
||||
property bool cut: implicitHeight > 100
|
||||
property bool isTopicEditingAllowed: false
|
||||
property bool showMore: false
|
||||
clip: true
|
||||
Layout.maximumHeight: showMore? Number.POSITIVE_INFINITY : 100
|
||||
Layout.preferredHeight: implicitHeight
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Nheko.paddingLarge
|
||||
Layout.maximumHeight: showMore ? Number.POSITIVE_INFINITY : 100
|
||||
Layout.preferredHeight: implicitHeight
|
||||
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
|
||||
selectByMouse: !Settings.mobileMode
|
||||
clip: true
|
||||
color: timelineRoot.palette.text
|
||||
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)
|
||||
|
||||
NhekoCursorShape {
|
||||
anchors.fill: parent
|
||||
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ImageButton {
|
||||
id: topicChangeButton
|
||||
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.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"
|
||||
visible: roomSettings.canChangeTopic
|
||||
|
||||
onClicked: {
|
||||
if (roomTopic.isTopicEditingAllowed) {
|
||||
roomSettings.changeTopic(roomTopic.text);
|
||||
|
@ -225,82 +217,78 @@ ApplicationWindow {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
id: showMorePlaceholder
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.preferredHeight: showMoreButton.height
|
||||
Layout.preferredWidth: showMoreButton.width
|
||||
visible: roomTopic.cut
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: Nheko.paddingMedium
|
||||
columns: 2
|
||||
rowSpacing: Nheko.paddingMedium
|
||||
Layout.margins: Nheko.paddingMedium
|
||||
Layout.fillWidth: true
|
||||
|
||||
Label {
|
||||
text: qsTr("SETTINGS")
|
||||
font.bold: true
|
||||
color: timelineRoot.palette.text
|
||||
font.bold: true
|
||||
text: qsTr("SETTINGS")
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Label {
|
||||
text: qsTr("Notifications")
|
||||
Layout.fillWidth: true
|
||||
color: timelineRoot.palette.text
|
||||
text: qsTr("Notifications")
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
model: [qsTr("Muted"), qsTr("Mentions only"), qsTr("All messages")]
|
||||
Layout.fillWidth: true
|
||||
currentIndex: roomSettings.notifications
|
||||
model: [qsTr("Muted"), qsTr("Mentions only"), qsTr("All messages")]
|
||||
|
||||
onActivated: {
|
||||
roomSettings.changeNotifications(index);
|
||||
}
|
||||
Layout.fillWidth: true
|
||||
WheelHandler{} // suppress scrolling changing values
|
||||
}
|
||||
|
||||
WheelHandler {
|
||||
} // suppress scrolling changing values
|
||||
}
|
||||
Label {
|
||||
text: qsTr("Room access")
|
||||
Layout.fillWidth: true
|
||||
color: timelineRoot.palette.text
|
||||
text: qsTr("Room access")
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
Layout.fillWidth: true
|
||||
currentIndex: roomSettings.accessJoinRules
|
||||
enabled: roomSettings.canChangeJoinRules
|
||||
model: {
|
||||
let opts = [qsTr("Anyone and guests"), qsTr("Anyone"), qsTr("Invited users")];
|
||||
if (roomSettings.supportsKnocking)
|
||||
opts.push(qsTr("By knocking"));
|
||||
|
||||
if (roomSettings.supportsRestricted)
|
||||
opts.push(qsTr("Restricted by membership in other rooms"));
|
||||
|
||||
return opts;
|
||||
}
|
||||
currentIndex: roomSettings.accessJoinRules
|
||||
|
||||
onActivated: {
|
||||
roomSettings.changeAccessRules(index);
|
||||
}
|
||||
Layout.fillWidth: true
|
||||
WheelHandler{} // suppress scrolling changing values
|
||||
}
|
||||
|
||||
WheelHandler {
|
||||
} // suppress scrolling changing values
|
||||
}
|
||||
Label {
|
||||
text: qsTr("Encryption")
|
||||
color: timelineRoot.palette.text
|
||||
text: qsTr("Encryption")
|
||||
}
|
||||
|
||||
ToggleButton {
|
||||
id: encryptionToggle
|
||||
|
||||
Layout.alignment: Qt.AlignRight
|
||||
checked: roomSettings.isEncryptionEnabled
|
||||
|
||||
onCheckedChanged: {
|
||||
if (roomSettings.isEncryptionEnabled) {
|
||||
checked = true;
|
||||
|
@ -308,140 +296,128 @@ ApplicationWindow {
|
|||
}
|
||||
confirmEncryptionDialog.open();
|
||||
}
|
||||
Layout.alignment: Qt.AlignRight
|
||||
}
|
||||
|
||||
Platform.MessageDialog {
|
||||
id: confirmEncryptionDialog
|
||||
|
||||
title: qsTr("End-to-End Encryption")
|
||||
buttons: Platform.MessageDialog.Ok | Platform.MessageDialog.Cancel
|
||||
modality: Qt.NonModal
|
||||
text: qsTr("Encryption is currently experimental and things might break unexpectedly. <br>
|
||||
Please take note that it can't be disabled afterwards.")
|
||||
modality: Qt.NonModal
|
||||
title: qsTr("End-to-End Encryption")
|
||||
|
||||
onAccepted: {
|
||||
if (roomSettings.isEncryptionEnabled)
|
||||
return;
|
||||
|
||||
roomSettings.enableEncryption();
|
||||
}
|
||||
onRejected: {
|
||||
encryptionToggle.checked = false;
|
||||
}
|
||||
buttons: Platform.MessageDialog.Ok | Platform.MessageDialog.Cancel
|
||||
}
|
||||
|
||||
Label {
|
||||
color: timelineRoot.palette.text
|
||||
text: qsTr("Sticker & Emote Settings")
|
||||
color: timelineRoot.palette.text
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
ToolTip.text: qsTr("Change what packs are enabled, remove packs or create new ones")
|
||||
text: qsTr("Change")
|
||||
|
||||
onClicked: TimelineManager.openImagePackSettings(roomSettings.roomId)
|
||||
}
|
||||
Label {
|
||||
text: qsTr("Hidden events")
|
||||
color: timelineRoot.palette.text
|
||||
text: qsTr("Hidden events")
|
||||
}
|
||||
|
||||
HiddenEventsDialog {
|
||||
id: hiddenEventsDialog
|
||||
roomid: roomSettings.roomId
|
||||
roomName: roomSettings.roomName
|
||||
roomid: roomSettings.roomId
|
||||
}
|
||||
|
||||
Button {
|
||||
text: qsTr("Configure")
|
||||
ToolTip.text: qsTr("Select events to hide in this room")
|
||||
onClicked: hiddenEventsDialog.show()
|
||||
Layout.alignment: Qt.AlignRight
|
||||
}
|
||||
ToolTip.text: qsTr("Select events to hide in this room")
|
||||
text: qsTr("Configure")
|
||||
|
||||
onClicked: hiddenEventsDialog.show()
|
||||
}
|
||||
Item {
|
||||
// for adding extra space between sections
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Item {
|
||||
// for adding extra space between sections
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Label {
|
||||
text: qsTr("INFO")
|
||||
color: timelineRoot.palette.text
|
||||
font.bold: true
|
||||
color: timelineRoot.palette.text
|
||||
text: qsTr("INFO")
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Label {
|
||||
text: qsTr("Internal ID")
|
||||
color: timelineRoot.palette.text
|
||||
text: qsTr("Internal ID")
|
||||
}
|
||||
|
||||
AbstractButton { // AbstractButton does not allow setting text color
|
||||
AbstractButton {
|
||||
// AbstractButton does not allow setting text color
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.fillWidth: true
|
||||
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
|
||||
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.visible: toolTipTimer.running
|
||||
}
|
||||
TextEdit{ // label does not allow selection
|
||||
id: textEdit
|
||||
visible: false
|
||||
color: timelineRoot.palette.text
|
||||
font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 0.8)
|
||||
horizontalAlignment: Text.AlignRight
|
||||
text: roomSettings.roomId
|
||||
width: parent.width
|
||||
wrapMode: Text.WrapAnywhere
|
||||
}
|
||||
onClicked: {
|
||||
textEdit.selectAll()
|
||||
textEdit.copy()
|
||||
toolTipTimer.start()
|
||||
TextEdit {
|
||||
// label does not allow selection
|
||||
id: textEdit
|
||||
text: roomSettings.roomId
|
||||
visible: false
|
||||
}
|
||||
Timer {
|
||||
id: toolTipTimer
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
text: qsTr("Room Version")
|
||||
color: timelineRoot.palette.text
|
||||
text: qsTr("Room Version")
|
||||
}
|
||||
|
||||
Label {
|
||||
text: roomSettings.roomVersion
|
||||
font.pixelSize: fontMetrics.font.pixelSize
|
||||
Layout.alignment: Qt.AlignRight
|
||||
color: timelineRoot.palette.text
|
||||
font.pixelSize: fontMetrics.font.pixelSize
|
||||
text: roomSettings.roomVersion
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
Button {
|
||||
id: showMoreButton
|
||||
anchors.horizontalCenter: flickable.horizontalCenter
|
||||
y: Math.min(showMorePlaceholder.y+contentLayout1.y-flickable.contentY,flickable.height-height)
|
||||
visible: roomTopic.cut
|
||||
text: roomTopic.showMore ? qsTr("show less") : qsTr("show more")
|
||||
onClicked: {roomTopic.showMore = !roomTopic.showMore
|
||||
console.log(flickable.visibleArea)
|
||||
visible: roomTopic.cut
|
||||
y: Math.min(showMorePlaceholder.y + contentLayout1.y - flickable.contentY, flickable.height - height)
|
||||
|
||||
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: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import ".."
|
||||
import "../"
|
||||
import "../device-verification"
|
||||
import "../ui"
|
||||
import QtQuick 2.15
|
||||
|
@ -18,101 +16,232 @@ ApplicationWindow {
|
|||
|
||||
property var profile
|
||||
|
||||
height: 650
|
||||
width: 420
|
||||
minimumWidth: 150
|
||||
minimumHeight: 150
|
||||
palette: timelineRoot.palette
|
||||
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
|
||||
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 {
|
||||
sequence: StandardKey.Cancel
|
||||
|
||||
onActivated: userProfileDialog.close()
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: devicelist
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
clip: true
|
||||
spacing: 8
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
model: profile.deviceList
|
||||
anchors.fill: parent
|
||||
anchors.margins: 10
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
clip: true
|
||||
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 {
|
||||
id: contentL
|
||||
|
||||
width: devicelist.width
|
||||
spacing: 10
|
||||
width: devicelist.width
|
||||
|
||||
Avatar {
|
||||
id: displayAvatar
|
||||
|
||||
url: profile.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||
height: 130
|
||||
width: 130
|
||||
displayName: profile.displayName
|
||||
userid: profile.userid
|
||||
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, "")
|
||||
|
||||
ImageButton {
|
||||
hoverEnabled: true
|
||||
ToolTip.visible: hovered
|
||||
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.top: displayAvatar.top
|
||||
anchors.leftMargin: Nheko.paddingMedium
|
||||
anchors.top: displayAvatar.top
|
||||
anchors.topMargin: Nheko.paddingMedium
|
||||
visible: profile.isSelf
|
||||
hoverEnabled: true
|
||||
image: ":/icons/icons/ui/edit.svg"
|
||||
visible: profile.isSelf
|
||||
|
||||
onClicked: profile.changeAvatar()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Spinner {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
foreground: timelineRoot.palette.mid
|
||||
running: profile.isLoading
|
||||
visible: profile.isLoading
|
||||
foreground: timelineRoot.palette.mid
|
||||
}
|
||||
|
||||
Text {
|
||||
id: errorText
|
||||
|
||||
color: "red"
|
||||
visible: opacity > 0
|
||||
opacity: 0
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
color: "red"
|
||||
opacity: 0
|
||||
visible: opacity > 0
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
id: hideErrorAnimation
|
||||
|
||||
running: false
|
||||
|
||||
PauseAnimation {
|
||||
duration: 4000
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
target: errorText
|
||||
property: 'opacity'
|
||||
to: 0
|
||||
duration: 1000
|
||||
property: 'opacity'
|
||||
target: errorText
|
||||
to: 0
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onDisplayError(errorMessage) {
|
||||
errorText.text = errorMessage;
|
||||
|
@ -122,22 +251,22 @@ ApplicationWindow {
|
|||
|
||||
target: profile
|
||||
}
|
||||
|
||||
TextInput {
|
||||
id: displayUsername
|
||||
|
||||
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.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
|
||||
wrapMode: TextInput.Wrap
|
||||
readOnly: !isUsernameEditingAllowed
|
||||
selectByMouse: true
|
||||
text: profile.displayName
|
||||
wrapMode: TextInput.Wrap
|
||||
|
||||
onAccepted: {
|
||||
profile.changeUsername(displayUsername.text);
|
||||
displayUsername.isUsernameEditingAllowed = false;
|
||||
|
@ -145,14 +274,15 @@ ApplicationWindow {
|
|||
|
||||
ImageButton {
|
||||
id: usernameChangeButton
|
||||
visible: profile.isSelf
|
||||
anchors.leftMargin: Nheko.paddingSmall
|
||||
ToolTip.text: profile.isGlobalUserProfile ? qsTr("Change display name globally.") : qsTr("Change display name. Will only apply to this room.")
|
||||
ToolTip.visible: hovered
|
||||
anchors.left: displayUsername.right
|
||||
anchors.leftMargin: Nheko.paddingSmall
|
||||
anchors.verticalCenter: displayUsername.verticalCenter
|
||||
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"
|
||||
visible: profile.isSelf
|
||||
|
||||
onClicked: {
|
||||
if (displayUsername.isUsernameEditingAllowed) {
|
||||
profile.changeUsername(displayUsername.text);
|
||||
|
@ -164,63 +294,54 @@ ApplicationWindow {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MatrixText {
|
||||
text: profile.userid
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: profile.userid
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
visible: !profile.isGlobalUserProfile
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
spacing: Nheko.paddingSmall
|
||||
visible: !profile.isGlobalUserProfile
|
||||
|
||||
MatrixText {
|
||||
id: displayRoomname
|
||||
|
||||
text: qsTr("Room: %1").arg(profile.room ? profile.room.roomName : "")
|
||||
Layout.maximumWidth: parent.parent.width - (parent.spacing * 3) - 16
|
||||
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
|
||||
Layout.maximumWidth: parent.parent.width - (parent.spacing * 3) - 16
|
||||
horizontalAlignment: TextEdit.AlignHCenter
|
||||
text: qsTr("Room: %1").arg(profile.room ? profile.room.roomName : "")
|
||||
|
||||
HoverHandler {
|
||||
id: ma
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ImageButton {
|
||||
image: ":/icons/icons/ui/world.svg"
|
||||
hoverEnabled: true
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: qsTr("Open the global profile for this user.")
|
||||
ToolTip.visible: hovered
|
||||
hoverEnabled: true
|
||||
image: ":/icons/icons/ui/world.svg"
|
||||
|
||||
onClicked: profile.openGlobalProfile()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Button {
|
||||
id: verifyUserButton
|
||||
|
||||
text: qsTr("Verify")
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
enabled: profile.userVerified != Crypto.Verified
|
||||
text: qsTr("Verify")
|
||||
visible: profile.userVerified != Crypto.Verified && !profile.isSelf && profile.userVerificationEnabled
|
||||
|
||||
onClicked: profile.verify()
|
||||
}
|
||||
|
||||
EncryptionIndicator {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.preferredHeight: 16
|
||||
Layout.preferredWidth: 16
|
||||
ToolTip.visible: false
|
||||
encrypted: profile.userVerificationEnabled
|
||||
trust: profile.userVerified
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
ToolTip.visible: false
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
// ImageButton{
|
||||
// image:":/icons/icons/ui/volume-off-indicator.svg"
|
||||
|
@ -234,202 +355,45 @@ ApplicationWindow {
|
|||
// profile.ignoreUser()
|
||||
// }
|
||||
// }
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.bottomMargin: 10
|
||||
spacing: Nheko.paddingSmall
|
||||
|
||||
ImageButton {
|
||||
image: ":/icons/icons/ui/chat.svg"
|
||||
hoverEnabled: true
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: qsTr("Start a private chat.")
|
||||
ToolTip.visible: hovered
|
||||
hoverEnabled: true
|
||||
image: ":/icons/icons/ui/chat.svg"
|
||||
|
||||
onClicked: profile.startChat()
|
||||
}
|
||||
|
||||
ImageButton {
|
||||
image: ":/icons/icons/ui/round-remove-button.svg"
|
||||
hoverEnabled: true
|
||||
ToolTip.visible: hovered
|
||||
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()
|
||||
}
|
||||
|
||||
onClicked: profile.kickUser()
|
||||
}
|
||||
ImageButton {
|
||||
image: ":/icons/icons/ui/ban.svg"
|
||||
hoverEnabled: true
|
||||
ToolTip.visible: hovered
|
||||
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
|
||||
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.visible: hovered
|
||||
hoverEnabled: true
|
||||
image: ":/icons/icons/ui/refresh.svg"
|
||||
|
||||
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: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import "../"
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.3
|
||||
|
@ -15,12 +13,12 @@ Menu {
|
|||
|
||||
property var callback
|
||||
property var colors
|
||||
property alias model: gridView.model
|
||||
property var textArea
|
||||
property string emojiCategory: "people"
|
||||
property real highlightHue: timelineRoot.palette.highlight.hslHue
|
||||
property real highlightSat: timelineRoot.palette.highlight.hslSaturation
|
||||
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) {
|
||||
console.debug("Showing emojiPicker");
|
||||
|
@ -28,13 +26,13 @@ Menu {
|
|||
popup(showAt ? showAt : null);
|
||||
}
|
||||
|
||||
margins: 0
|
||||
bottomPadding: 1
|
||||
leftPadding: 1
|
||||
rightPadding: 1
|
||||
modal: true
|
||||
focus: true
|
||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
||||
focus: true
|
||||
leftPadding: 1
|
||||
margins: 0
|
||||
modal: true
|
||||
rightPadding: 1
|
||||
//height: columnView.implicitHeight + 4
|
||||
//width: columnView.implicitWidth
|
||||
width: 7 * 52 + 20
|
||||
|
@ -46,28 +44,27 @@ Menu {
|
|||
|
||||
ColumnLayout {
|
||||
id: columnView
|
||||
|
||||
spacing: 0
|
||||
anchors.leftMargin: 3
|
||||
anchors.rightMargin: 3
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 3
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 3
|
||||
anchors.topMargin: 2
|
||||
spacing: 0
|
||||
|
||||
// Search field
|
||||
TextField {
|
||||
id: emojiSearch
|
||||
|
||||
Layout.topMargin: 3
|
||||
Layout.preferredWidth: 7 * 52 + 20 - 6
|
||||
palette: timelineRoot.palette
|
||||
Layout.topMargin: 3
|
||||
background: null
|
||||
placeholderTextColor: timelineRoot.palette.placeholderText
|
||||
color: timelineRoot.palette.text
|
||||
palette: timelineRoot.palette
|
||||
placeholderText: qsTr("Search")
|
||||
selectByMouse: true
|
||||
placeholderTextColor: timelineRoot.palette.placeholderText
|
||||
rightPadding: clearSearch.width
|
||||
selectByMouse: true
|
||||
|
||||
onTextChanged: searchTimer.restart()
|
||||
onVisibleChanged: {
|
||||
if (visible)
|
||||
|
@ -78,74 +75,71 @@ Menu {
|
|||
|
||||
Timer {
|
||||
id: searchTimer
|
||||
|
||||
interval: 350 // tweak as needed?
|
||||
|
||||
onTriggered: {
|
||||
emojiPopup.model.searchString = emojiSearch.text;
|
||||
emojiPopup.model.category = Emoji.Category.Search;
|
||||
}
|
||||
}
|
||||
|
||||
ToolButton {
|
||||
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
|
||||
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 {
|
||||
verticalCenter: parent.verticalCenter
|
||||
right: parent.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
// clear the default hover effects.
|
||||
|
||||
Image {
|
||||
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)
|
||||
width: height
|
||||
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
right: parent.right
|
||||
margins: Nheko.paddingSmall
|
||||
right: parent.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// emoji grid
|
||||
GridView {
|
||||
id: gridView
|
||||
|
||||
Layout.leftMargin: 4
|
||||
Layout.preferredHeight: cellHeight * 5
|
||||
Layout.preferredWidth: 7 * 52 + 20
|
||||
Layout.leftMargin: 4
|
||||
cellWidth: 52
|
||||
cellHeight: 52
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
cacheBuffer: 500
|
||||
cellHeight: 52
|
||||
cellWidth: 52
|
||||
clip: true
|
||||
currentIndex: -1 // prevent sorting from stealing focus
|
||||
cacheBuffer: 500
|
||||
|
||||
ScrollBar.vertical: ScrollBar {
|
||||
id: emojiScroll
|
||||
}
|
||||
|
||||
// Individual emoji
|
||||
delegate: AbstractButton {
|
||||
width: 48
|
||||
height: 48
|
||||
hoverEnabled: true
|
||||
ToolTip.text: model.shortName
|
||||
ToolTip.visible: hovered
|
||||
// TODO: maybe add favorites at some point?
|
||||
onClicked: {
|
||||
console.debug("Picked " + model.unicode);
|
||||
emojiPopup.close();
|
||||
callback(model.unicode);
|
||||
height: 48
|
||||
hoverEnabled: true
|
||||
width: 48
|
||||
|
||||
background: Rectangle {
|
||||
anchors.fill: parent
|
||||
color: hovered ? timelineRoot.palette.highlight : 'transparent'
|
||||
radius: 5
|
||||
}
|
||||
|
||||
// give the emoji a little oomf
|
||||
|
@ -159,97 +153,73 @@ Menu {
|
|||
// color: "#80000000"
|
||||
// source: parent.contentItem
|
||||
// }
|
||||
|
||||
contentItem: Text {
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
color: timelineRoot.palette.text
|
||||
font.family: Settings.emojiFont
|
||||
font.pixelSize: 36
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: model.unicode.replace('\ufe0f', '')
|
||||
color: timelineRoot.palette.text
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
anchors.fill: parent
|
||||
color: hovered ? timelineRoot.palette.highlight : 'transparent'
|
||||
radius: 5
|
||||
// TODO: maybe add favorites at some point?
|
||||
onClicked: {
|
||||
console.debug("Picked " + model.unicode);
|
||||
emojiPopup.close();
|
||||
callback(model.unicode);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ScrollBar.vertical: ScrollBar {
|
||||
id: emojiScroll
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Separator
|
||||
Rectangle {
|
||||
visible: emojiSearch.text === ''
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 1
|
||||
color: emojiPopup.Nheko.theme.separator
|
||||
visible: emojiSearch.text === ''
|
||||
}
|
||||
|
||||
// Category picker row
|
||||
RowLayout {
|
||||
visible: emojiSearch.text === ''
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
||||
Layout.bottomMargin: 0
|
||||
Layout.preferredHeight: 42
|
||||
implicitHeight: 42
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
||||
visible: emojiSearch.text === ''
|
||||
|
||||
// Display the normal categories
|
||||
Repeater {
|
||||
|
||||
model: [
|
||||
// TODO: Would like to get 'simple' icons for the categories
|
||||
{
|
||||
image: ":/icons/icons/emoji-categories/people.svg",
|
||||
category: Emoji.Category.People
|
||||
},
|
||||
|
||||
{
|
||||
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",
|
||||
category: Emoji.Category.Activity
|
||||
},
|
||||
|
||||
{
|
||||
image: ":/icons/icons/emoji-categories/travel.svg",
|
||||
category: Emoji.Category.Travel
|
||||
},
|
||||
|
||||
{
|
||||
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
|
||||
}
|
||||
|
||||
]
|
||||
"image": ":/icons/icons/emoji-categories/people.svg",
|
||||
"category": Emoji.Category.People
|
||||
}, {
|
||||
"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",
|
||||
"category": Emoji.Category.Activity
|
||||
}, {
|
||||
"image": ":/icons/icons/emoji-categories/travel.svg",
|
||||
"category": Emoji.Category.Travel
|
||||
}, {
|
||||
"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 {
|
||||
Layout.preferredWidth: 36
|
||||
Layout.preferredHeight: 36
|
||||
hoverEnabled: true
|
||||
Layout.preferredWidth: 36
|
||||
ToolTip.text: {
|
||||
switch (modelData.category) {
|
||||
case Emoji.Category.People:
|
||||
|
@ -271,6 +241,27 @@ Menu {
|
|||
}
|
||||
}
|
||||
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: {
|
||||
//emojiPopup.model.category = model.category;
|
||||
gridView.positionViewAtIndex(emojiPopup.model.sourceModel.categoryToIndex(modelData.category), GridView.Beginning);
|
||||
|
@ -278,40 +269,14 @@ Menu {
|
|||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
|
||||
anchors.fill: parent
|
||||
onPressed: mouse.accepted = false
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
|
||||
contentItem: Image {
|
||||
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)
|
||||
onPressed: mouse.accepted = false
|
||||
}
|
||||
|
||||
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: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import "../"
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.3
|
||||
|
@ -14,15 +12,15 @@ Menu {
|
|||
|
||||
property var callback
|
||||
property var colors
|
||||
property string roomid
|
||||
property alias model: gridView.model
|
||||
property var textArea
|
||||
property real highlightHue: timelineRoot.palette.highlight.hslHue
|
||||
property real highlightSat: timelineRoot.palette.highlight.hslSaturation
|
||||
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 stickerDimPad: 128 + Nheko.paddingSmall
|
||||
readonly property int stickersPerRow: 3
|
||||
property var textArea
|
||||
|
||||
function show(showAt, roomid_, callback) {
|
||||
console.debug("Showing sticker picker");
|
||||
|
@ -31,13 +29,13 @@ Menu {
|
|||
popup(showAt ? showAt : null);
|
||||
}
|
||||
|
||||
margins: 0
|
||||
bottomPadding: 1
|
||||
leftPadding: 1
|
||||
rightPadding: 1
|
||||
modal: true
|
||||
focus: true
|
||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
||||
focus: true
|
||||
leftPadding: 1
|
||||
margins: 0
|
||||
modal: true
|
||||
rightPadding: 1
|
||||
width: stickersPerRow * stickerDimPad + 20
|
||||
|
||||
Rectangle {
|
||||
|
@ -47,28 +45,27 @@ Menu {
|
|||
|
||||
ColumnLayout {
|
||||
id: columnView
|
||||
|
||||
spacing: 0
|
||||
anchors.leftMargin: 3
|
||||
anchors.rightMargin: 3
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 3
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 3
|
||||
anchors.topMargin: 2
|
||||
spacing: 0
|
||||
|
||||
// Search field
|
||||
TextField {
|
||||
id: emojiSearch
|
||||
|
||||
Layout.topMargin: 3
|
||||
Layout.preferredWidth: stickersPerRow * stickerDimPad + 20 - 6
|
||||
palette: timelineRoot.palette
|
||||
Layout.topMargin: 3
|
||||
background: null
|
||||
placeholderTextColor: timelineRoot.palette.placeholderText
|
||||
color: timelineRoot.palette.text
|
||||
palette: timelineRoot.palette
|
||||
placeholderText: qsTr("Search")
|
||||
selectByMouse: true
|
||||
placeholderTextColor: timelineRoot.palette.placeholderText
|
||||
rightPadding: clearSearch.width
|
||||
selectByMouse: true
|
||||
|
||||
onTextChanged: searchTimer.restart()
|
||||
onVisibleChanged: {
|
||||
if (visible)
|
||||
|
@ -79,97 +76,85 @@ Menu {
|
|||
|
||||
Timer {
|
||||
id: searchTimer
|
||||
|
||||
interval: 350 // tweak as needed?
|
||||
|
||||
onTriggered: stickerPopup.model.searchString = emojiSearch.text
|
||||
}
|
||||
|
||||
ToolButton {
|
||||
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
|
||||
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 {
|
||||
verticalCenter: parent.verticalCenter
|
||||
right: parent.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
// clear the default hover effects.
|
||||
|
||||
Image {
|
||||
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)
|
||||
width: height
|
||||
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
right: parent.right
|
||||
margins: Nheko.paddingSmall
|
||||
right: parent.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// emoji grid
|
||||
GridView {
|
||||
id: gridView
|
||||
|
||||
model: roomid ? TimelineManager.completerFor("stickers", roomid) : null
|
||||
Layout.leftMargin: 4
|
||||
Layout.preferredHeight: cellHeight * 3.5
|
||||
Layout.preferredWidth: stickersPerRow * stickerDimPad + 20
|
||||
Layout.leftMargin: 4
|
||||
cellWidth: stickerDimPad
|
||||
cellHeight: stickerDimPad
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
cacheBuffer: 500
|
||||
cellHeight: stickerDimPad
|
||||
cellWidth: stickerDimPad
|
||||
clip: true
|
||||
currentIndex: -1 // prevent sorting from stealing focus
|
||||
cacheBuffer: 500
|
||||
model: roomid ? TimelineManager.completerFor("stickers", roomid) : null
|
||||
|
||||
ScrollBar.vertical: ScrollBar {
|
||||
id: emojiScroll
|
||||
}
|
||||
|
||||
// Individual emoji
|
||||
delegate: AbstractButton {
|
||||
width: stickerDim
|
||||
height: stickerDim
|
||||
hoverEnabled: true
|
||||
ToolTip.text: ":" + model.shortcode + ": - " + model.body
|
||||
ToolTip.visible: hovered
|
||||
// TODO: maybe add favorites at some point?
|
||||
onClicked: {
|
||||
console.debug("Picked " + model.shortcode);
|
||||
stickerPopup.close();
|
||||
callback(model.originalRow);
|
||||
}
|
||||
|
||||
contentItem: Image {
|
||||
height: stickerDim
|
||||
hoverEnabled: true
|
||||
width: stickerDim
|
||||
source: model.url.replace("mxc://", "image://MxcImage/") + "?scale"
|
||||
fillMode: Image.PreserveAspectFit
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
anchors.fill: parent
|
||||
color: hovered ? timelineRoot.palette.highlight : 'transparent'
|
||||
radius: 5
|
||||
}
|
||||
|
||||
contentItem: Image {
|
||||
fillMode: Image.PreserveAspectFit
|
||||
height: stickerDim
|
||||
source: model.url.replace("mxc://", "image://MxcImage/") + "?scale"
|
||||
width: stickerDim
|
||||
}
|
||||
|
||||
ScrollBar.vertical: ScrollBar {
|
||||
id: emojiScroll
|
||||
// TODO: maybe add favorites at some point?
|
||||
onClicked: {
|
||||
console.debug("Picked " + model.shortcode);
|
||||
stickerPopup.close();
|
||||
callback(model.originalRow);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,189 +1,172 @@
|
|||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.2
|
||||
import QtQuick.Window 2.15
|
||||
import im.nheko
|
||||
import "../components/"
|
||||
import "../ui/"
|
||||
import "../components"
|
||||
import "../ui"
|
||||
import "../"
|
||||
|
||||
Item {
|
||||
id: loginPage
|
||||
property int maxExpansion: 400
|
||||
|
||||
property string error: login.error
|
||||
property int maxExpansion: 400
|
||||
|
||||
Login {
|
||||
id: login
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
id: scroll
|
||||
|
||||
clip: false
|
||||
palette: timelineRoot.palette
|
||||
ScrollBar.horizontal.visible: false
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Nheko.paddingLarge
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
height: Math.min(loginPage.height, col.implicitHeight)
|
||||
anchors.margins: Nheko.paddingLarge
|
||||
|
||||
clip: false
|
||||
contentWidth: availableWidth
|
||||
height: Math.min(loginPage.height, col.implicitHeight)
|
||||
palette: timelineRoot.palette
|
||||
|
||||
ColumnLayout {
|
||||
id: col
|
||||
|
||||
spacing: Nheko.paddingMedium
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: Nheko.paddingMedium
|
||||
width: Math.min(loginPage.maxExpansion, scroll.width - Nheko.paddingLarge * 2)
|
||||
|
||||
Image {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
source: "qrc:/logos/login.png"
|
||||
height: 128
|
||||
source: "qrc:/logos/login.png"
|
||||
width: 128
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Nheko.paddingLarge
|
||||
|
||||
Layout.fillWidth: true
|
||||
MatrixTextField {
|
||||
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")
|
||||
placeholderText: qsTr("e.g @joe:matrix.org")
|
||||
|
||||
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 {
|
||||
height: matrixIdLabel.height/2
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
|
||||
visible: running
|
||||
running: login.lookingUpHs
|
||||
foreground: timelineRoot.palette.mid
|
||||
height: matrixIdLabel.height / 2
|
||||
running: login.lookingUpHs
|
||||
visible: running
|
||||
}
|
||||
}
|
||||
|
||||
MatrixText {
|
||||
textFormat: Text.PlainText
|
||||
color: Nheko.theme.error
|
||||
text: login.mxidError
|
||||
textFormat: Text.PlainText
|
||||
visible: text
|
||||
}
|
||||
|
||||
MatrixTextField {
|
||||
id: passwordLabel
|
||||
Layout.fillWidth: true
|
||||
label: qsTr("Password")
|
||||
echoMode: TextInput.Password
|
||||
ToolTip.text: qsTr("Your password.")
|
||||
visible: login.passwordSupported
|
||||
Keys.forwardTo: [pwBtn, ssoRepeater]
|
||||
Layout.fillWidth: true
|
||||
ToolTip.text: qsTr("Your password.")
|
||||
echoMode: TextInput.Password
|
||||
label: qsTr("Password")
|
||||
visible: login.passwordSupported
|
||||
}
|
||||
|
||||
MatrixTextField {
|
||||
id: deviceNameLabel
|
||||
Keys.forwardTo: [pwBtn, ssoRepeater]
|
||||
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")
|
||||
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 {
|
||||
id: hsLabel
|
||||
enabled: visible
|
||||
visible: login.homeserverNeeded
|
||||
|
||||
Keys.forwardTo: [pwBtn, ssoRepeater]
|
||||
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")
|
||||
placeholderText: qsTr("server.my:8787")
|
||||
text: login.homeserver
|
||||
onEditingFinished: login.homeserver = text
|
||||
ToolTip.text: qsTr("The address that can be used to contact you homeservers client API.\nExample: https://server.my:8787")
|
||||
Keys.forwardTo: [pwBtn, ssoRepeater]
|
||||
}
|
||||
visible: login.homeserverNeeded
|
||||
|
||||
onEditingFinished: login.homeserver = text
|
||||
}
|
||||
Item {
|
||||
height: Nheko.avatarSize
|
||||
Layout.fillWidth: true
|
||||
height: Nheko.avatarSize
|
||||
|
||||
Spinner {
|
||||
height: parent.height
|
||||
anchors.centerIn: parent
|
||||
|
||||
visible: running
|
||||
running: login.loggingIn
|
||||
foreground: timelineRoot.palette.mid
|
||||
height: parent.height
|
||||
running: login.loggingIn
|
||||
visible: running
|
||||
}
|
||||
}
|
||||
|
||||
MatrixText {
|
||||
textFormat: Text.PlainText
|
||||
color: Nheko.theme.error
|
||||
text: loginPage.error
|
||||
textFormat: Text.PlainText
|
||||
visible: text
|
||||
}
|
||||
|
||||
FlatButton {
|
||||
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() {
|
||||
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.onReturnPressed: pwBtn.pwLogin()
|
||||
Keys.enabled: pwBtn.enabled && login.passwordSupported
|
||||
onClicked: pwBtn.pwLogin()
|
||||
}
|
||||
|
||||
Repeater {
|
||||
id: ssoRepeater
|
||||
|
||||
model: login.identityProviders
|
||||
|
||||
delegate: FlatButton {
|
||||
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() {
|
||||
login.onLoginButtonClicked(Login.SSO, matrixIdLabel.text, modelData.id, deviceNameLabel.text)
|
||||
}
|
||||
onClicked: ssoBtn.ssoLogin()
|
||||
Keys.onEnterPressed: ssoBtn.ssoLogin()
|
||||
Keys.onReturnPressed: ssoBtn.ssoLogin()
|
||||
Keys.enabled: ssoBtn.enabled && !login.passwordSupported
|
||||
}
|
||||
}
|
||||
}
|
||||
login.onLoginButtonClicked(Login.SSO, matrixIdLabel.text, modelData.id, deviceNameLabel.text);
|
||||
}
|
||||
|
||||
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.onReturnPressed: ssoBtn.ssoLogin()
|
||||
onClicked: ssoBtn.ssoLogin()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ImageButton {
|
||||
anchors.top: parent.top
|
||||
ToolTip.text: qsTr("Back")
|
||||
ToolTip.visible: hovered
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Nheko.paddingMedium
|
||||
width: Nheko.avatarSize
|
||||
anchors.top: parent.top
|
||||
height: Nheko.avatarSize
|
||||
image: ":/icons/icons/ui/angle-arrow-left.svg"
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: qsTr("Back")
|
||||
width: Nheko.avatarSize
|
||||
|
||||
onClicked: mainWindow.pop()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,215 +1,192 @@
|
|||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.2
|
||||
import QtQuick.Window 2.15
|
||||
import im.nheko
|
||||
import "../components/"
|
||||
import "../ui/"
|
||||
import "../components"
|
||||
import "../ui"
|
||||
import "../"
|
||||
|
||||
Item {
|
||||
id: registrationPage
|
||||
property int maxExpansion: 400
|
||||
|
||||
property string error: regis.error
|
||||
property int maxExpansion: 400
|
||||
|
||||
Registration {
|
||||
id: regis
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
id: scroll
|
||||
|
||||
clip: false
|
||||
palette: timelineRoot.palette
|
||||
ScrollBar.horizontal.visible: false
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Nheko.paddingLarge
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
height: Math.min(registrationPage.height, col.implicitHeight)
|
||||
anchors.margins: Nheko.paddingLarge
|
||||
|
||||
clip: false
|
||||
contentWidth: availableWidth
|
||||
height: Math.min(registrationPage.height, col.implicitHeight)
|
||||
palette: timelineRoot.palette
|
||||
|
||||
ColumnLayout {
|
||||
id: col
|
||||
|
||||
spacing: Nheko.paddingMedium
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: Nheko.paddingMedium
|
||||
width: Math.min(registrationPage.maxExpansion, scroll.width - Nheko.paddingLarge * 2)
|
||||
|
||||
Image {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
source: "qrc:/logos/login.png"
|
||||
height: 128
|
||||
source: "qrc:/logos/login.png"
|
||||
width: 128
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Nheko.paddingLarge
|
||||
|
||||
Layout.fillWidth: true
|
||||
MatrixTextField {
|
||||
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")
|
||||
placeholderText: qsTr("your.server")
|
||||
|
||||
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 {
|
||||
height: hsLabel.height/2
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
|
||||
visible: running
|
||||
running: regis.lookingUpHs
|
||||
foreground: timelineRoot.palette.mid
|
||||
height: hsLabel.height / 2
|
||||
running: regis.lookingUpHs
|
||||
visible: running
|
||||
}
|
||||
}
|
||||
|
||||
MatrixText {
|
||||
textFormat: Text.PlainText
|
||||
color: Nheko.theme.error
|
||||
text: regis.hsError
|
||||
textFormat: Text.PlainText
|
||||
visible: text
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Nheko.paddingLarge
|
||||
|
||||
visible: regis.supported
|
||||
|
||||
Layout.fillWidth: true
|
||||
MatrixTextField {
|
||||
id: usernameLabel
|
||||
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 /.")
|
||||
label: qsTr("Username")
|
||||
|
||||
onEditingFinished: regis.checkUsername(text)
|
||||
}
|
||||
Spinner {
|
||||
height: usernameLabel.height/2
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
|
||||
visible: running
|
||||
running: regis.lookingUpUsername
|
||||
foreground: timelineRoot.palette.mid
|
||||
height: usernameLabel.height / 2
|
||||
running: regis.lookingUpUsername
|
||||
visible: running
|
||||
}
|
||||
|
||||
Image {
|
||||
width: usernameLabel.height/2
|
||||
height: width
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
Layout.preferredHeight: usernameLabel.height / 2
|
||||
Layout.preferredWidth: usernameLabel.height / 2
|
||||
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)
|
||||
visible: regis.usernameAvailable || regis.usernameUnavailable
|
||||
ToolTip.visible: ma.hovered
|
||||
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.width: width * Screen.devicePixelRatio
|
||||
visible: regis.usernameAvailable || regis.usernameUnavailable
|
||||
width: usernameLabel.height / 2
|
||||
|
||||
HoverHandler {
|
||||
id: ma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MatrixText {
|
||||
textFormat: Text.PlainText
|
||||
color: Nheko.theme.error
|
||||
text: regis.usernameError
|
||||
textFormat: Text.PlainText
|
||||
visible: text
|
||||
}
|
||||
|
||||
|
||||
MatrixTextField {
|
||||
visible: regis.supported
|
||||
id: passwordLabel
|
||||
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.")
|
||||
}
|
||||
|
||||
MatrixTextField {
|
||||
echoMode: TextInput.Password
|
||||
label: qsTr("Password")
|
||||
visible: regis.supported
|
||||
}
|
||||
MatrixTextField {
|
||||
id: passwordConfirmationLabel
|
||||
Layout.fillWidth: true
|
||||
label: qsTr("Password confirmation")
|
||||
echoMode: TextInput.Password
|
||||
}
|
||||
|
||||
MatrixText {
|
||||
label: qsTr("Password confirmation")
|
||||
visible: regis.supported
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
MatrixText {
|
||||
color: Nheko.theme.error
|
||||
text: passwordLabel.text != passwordConfirmationLabel.text ? qsTr("Your passwords do not match!") : ""
|
||||
}
|
||||
|
||||
MatrixTextField {
|
||||
textFormat: Text.PlainText
|
||||
visible: regis.supported
|
||||
}
|
||||
MatrixTextField {
|
||||
id: deviceNameLabel
|
||||
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")
|
||||
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 {
|
||||
height: Nheko.avatarSize
|
||||
Layout.fillWidth: true
|
||||
height: Nheko.avatarSize
|
||||
|
||||
Spinner {
|
||||
height: parent.height
|
||||
anchors.centerIn: parent
|
||||
|
||||
visible: running
|
||||
running: regis.registering
|
||||
foreground: timelineRoot.palette.mid
|
||||
height: parent.height
|
||||
running: regis.registering
|
||||
visible: running
|
||||
}
|
||||
}
|
||||
|
||||
MatrixText {
|
||||
textFormat: Text.PlainText
|
||||
color: Nheko.theme.error
|
||||
text: registrationPage.error
|
||||
textFormat: Text.PlainText
|
||||
visible: text
|
||||
}
|
||||
|
||||
FlatButton {
|
||||
id: regisBtn
|
||||
visible: regis.supported
|
||||
enabled: usernameLabel.text && passwordLabel.text && passwordLabel.text == passwordConfirmationLabel.text
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: qsTr("REGISTER")
|
||||
function register() {
|
||||
regis.startRegistration(usernameLabel.text, passwordLabel.text, deviceNameLabel.text)
|
||||
}
|
||||
onClicked: regisBtn.register()
|
||||
Keys.onEnterPressed: regisBtn.register()
|
||||
Keys.onReturnPressed: regisBtn.register()
|
||||
Keys.enabled: regisBtn.enabled && regis.supported
|
||||
}
|
||||
}
|
||||
regis.startRegistration(usernameLabel.text, passwordLabel.text, deviceNameLabel.text);
|
||||
}
|
||||
|
||||
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.onReturnPressed: regisBtn.register()
|
||||
onClicked: regisBtn.register()
|
||||
}
|
||||
}
|
||||
}
|
||||
ImageButton {
|
||||
anchors.top: parent.top
|
||||
ToolTip.text: qsTr("Back")
|
||||
ToolTip.visible: hovered
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Nheko.paddingMedium
|
||||
width: Nheko.avatarSize
|
||||
anchors.top: parent.top
|
||||
height: Nheko.avatarSize
|
||||
image: ":/icons/icons/ui/angle-arrow-left.svg"
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: qsTr("Back")
|
||||
width: Nheko.avatarSize
|
||||
|
||||
onClicked: mainWindow.pop()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import ".."
|
||||
import "../"
|
||||
import "../ui"
|
||||
import Qt.labs.platform 1.1 as Platform
|
||||
import QtQuick 2.15
|
||||
|
@ -18,92 +16,94 @@ Rectangle {
|
|||
|
||||
property int collapsePoint: 800
|
||||
property bool collapsed: width < collapsePoint
|
||||
|
||||
color: timelineRoot.palette.window
|
||||
|
||||
ScrollView {
|
||||
id: scroll
|
||||
|
||||
palette: timelineRoot.palette
|
||||
ScrollBar.horizontal.visible: false
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: (collapsed ? backButton.height : 0) + Nheko.paddingLarge
|
||||
leftPadding: collapsed? Nheko.paddingMedium : Nheko.paddingLarge
|
||||
bottomPadding: Nheko.paddingLarge
|
||||
contentWidth: availableWidth
|
||||
leftPadding: collapsed ? Nheko.paddingMedium : Nheko.paddingLarge
|
||||
palette: timelineRoot.palette
|
||||
|
||||
ColumnLayout {
|
||||
id: grid
|
||||
|
||||
spacing: Nheko.paddingMedium
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: userSettingsDialog.collapsed ? 0 : (userSettingsDialog.width - userSettingsDialog.collapsePoint) * 0.4 + Nheko.paddingLarge
|
||||
anchors.rightMargin: anchors.leftMargin
|
||||
spacing: Nheko.paddingMedium
|
||||
|
||||
Repeater {
|
||||
model: UserSettingsModel
|
||||
Layout.fillWidth: true
|
||||
model: UserSettingsModel
|
||||
|
||||
delegate: GridLayout {
|
||||
id: r
|
||||
|
||||
required property var model
|
||||
|
||||
columns: collapsed ? 1 : 2
|
||||
rows: collapsed ? 2 : 1
|
||||
required property var model
|
||||
id: r
|
||||
|
||||
Label {
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
Layout.fillWidth: true
|
||||
color: timelineRoot.palette.text
|
||||
text: model.name
|
||||
//Layout.column: 0
|
||||
Layout.columnSpan: (model.type == UserSettingsModel.SectionTitle && !userSettingsDialog.collapsed) ? 2 : 1
|
||||
Layout.fillWidth: true
|
||||
//Layout.row: model.index
|
||||
//Layout.minimumWidth: implicitWidth
|
||||
Layout.leftMargin: model.type == UserSettingsModel.SectionTitle ? 0 : Nheko.paddingMedium
|
||||
Layout.topMargin: model.type == UserSettingsModel.SectionTitle ? Nheko.paddingLarge : 0
|
||||
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
|
||||
text: model.name
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
HoverHandler {
|
||||
id: hovered
|
||||
enabled: model.description ?? false
|
||||
}
|
||||
ToolTip.visible: hovered.hovered && model.description
|
||||
ToolTip.text: model.description ?? ""
|
||||
ToolTip.delay: Nheko.tooltipDelay
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
DelegateChooser {
|
||||
id: chooser
|
||||
|
||||
roleValue: model.type
|
||||
Layout.alignment: Qt.AlignRight
|
||||
|
||||
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.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
|
||||
roleValue: model.type
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: UserSettingsModel.Toggle
|
||||
|
||||
ToggleButton {
|
||||
checked: model.value
|
||||
onCheckedChanged: model.value = checked
|
||||
enabled: model.enabled
|
||||
|
||||
onCheckedChanged: model.value = checked
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: UserSettingsModel.Options
|
||||
|
||||
ComboBox {
|
||||
anchors.right: parent.right
|
||||
width: Math.min(parent.width, implicitWidth)
|
||||
model: r.model.values
|
||||
currentIndex: r.model.value
|
||||
model: r.model.values
|
||||
width: Math.min(parent.width, implicitWidth)
|
||||
|
||||
onCurrentIndexChanged: r.model.value = currentIndex
|
||||
|
||||
WheelHandler{} // suppress scrolling changing values
|
||||
WheelHandler {
|
||||
} // suppress scrolling changing values
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
|
@ -111,15 +111,17 @@ Rectangle {
|
|||
|
||||
SpinBox {
|
||||
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
|
||||
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 {
|
||||
|
@ -128,56 +130,58 @@ Rectangle {
|
|||
SpinBox {
|
||||
id: spinbox
|
||||
|
||||
readonly property double div: 100
|
||||
readonly property int decimals: 2
|
||||
readonly property double div: 100
|
||||
property real realValue: value / div
|
||||
|
||||
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
|
||||
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 {
|
||||
bottom: Math.min(spinbox.from / spinbox.div, spinbox.to / spinbox.div)
|
||||
top: Math.max(spinbox.from / spinbox.div, spinbox.to / spinbox.div)
|
||||
}
|
||||
|
||||
textFromValue: function(value, locale) {
|
||||
return Number(value / spinbox.div).toLocaleString(locale, 'f', spinbox.decimals)
|
||||
}
|
||||
onValueChanged: model.value = value / div
|
||||
|
||||
valueFromText: function(text, locale) {
|
||||
return Number.fromLocaleString(locale, text) * spinbox.div
|
||||
}
|
||||
|
||||
WheelHandler{} // suppress scrolling changing values
|
||||
WheelHandler {
|
||||
} // suppress scrolling changing values
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: UserSettingsModel.ReadOnlyText
|
||||
|
||||
TextEdit {
|
||||
color: timelineRoot.palette.text
|
||||
text: model.value
|
||||
readOnly: true
|
||||
selectByMouse: !Settings.mobileMode
|
||||
text: model.value
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: UserSettingsModel.SectionTitle
|
||||
|
||||
Item {
|
||||
width: grid.width
|
||||
height: fontMetrics.lineSpacing
|
||||
width: grid.width
|
||||
|
||||
Rectangle {
|
||||
anchors.topMargin: Nheko.paddingSmall
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: Nheko.paddingSmall
|
||||
color: timelineRoot.palette.placeholderText
|
||||
height: 1
|
||||
}
|
||||
|
@ -185,6 +189,7 @@ Rectangle {
|
|||
}
|
||||
DelegateChoice {
|
||||
roleValue: UserSettingsModel.KeyStatus
|
||||
|
||||
Text {
|
||||
color: model.good ? "green" : Nheko.theme.error
|
||||
text: model.value ? qsTr("CACHED") : qsTr("NOT CACHED")
|
||||
|
@ -192,26 +197,32 @@ Rectangle {
|
|||
}
|
||||
DelegateChoice {
|
||||
roleValue: UserSettingsModel.SessionKeyImportExport
|
||||
|
||||
RowLayout {
|
||||
Button {
|
||||
text: qsTr("IMPORT")
|
||||
|
||||
onClicked: UserSettingsModel.importSessionKeys()
|
||||
}
|
||||
Button {
|
||||
text: qsTr("EXPORT")
|
||||
|
||||
onClicked: UserSettingsModel.exportSessionKeys()
|
||||
}
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: UserSettingsModel.XSignKeysRequestDownload
|
||||
|
||||
RowLayout {
|
||||
Button {
|
||||
text: qsTr("DOWNLOAD")
|
||||
|
||||
onClicked: UserSettingsModel.downloadCrossSigningSecrets()
|
||||
}
|
||||
Button {
|
||||
text: qsTr("REQUEST")
|
||||
|
||||
onClicked: UserSettingsModel.requestCrossSigningSecrets()
|
||||
}
|
||||
}
|
||||
|
@ -226,19 +237,17 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImageButton {
|
||||
id: backButton
|
||||
anchors.top: parent.top
|
||||
ToolTip.text: qsTr("Back")
|
||||
ToolTip.visible: hovered
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Nheko.paddingMedium
|
||||
width: Nheko.avatarSize
|
||||
anchors.top: parent.top
|
||||
height: Nheko.avatarSize
|
||||
image: ":/icons/icons/ui/angle-arrow-left.svg"
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: qsTr("Back")
|
||||
width: Nheko.avatarSize
|
||||
|
||||
onClicked: mainWindow.pop()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,64 +1,61 @@
|
|||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.2
|
||||
import QtQuick.Window 2.15
|
||||
import im.nheko
|
||||
import "../components/"
|
||||
import "../components"
|
||||
|
||||
ColumnLayout {
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
Image {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
source: "qrc:/logos/splash.png"
|
||||
height: 256
|
||||
source: "qrc:/logos/splash.png"
|
||||
width: 256
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.margins: Nheko.paddingLarge
|
||||
Layout.bottomMargin: 0
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.bottomMargin: 0
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Welcome to nheko! The desktop client for the Matrix protocol.")
|
||||
Layout.margins: Nheko.paddingLarge
|
||||
color: timelineRoot.palette.text
|
||||
font.pointSize: fontMetrics.font.pointSize * 2
|
||||
wrapMode: Text.Wrap
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: qsTr("Welcome to nheko! The desktop client for the Matrix protocol.")
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
Label {
|
||||
Layout.margins: Nheko.paddingLarge
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Enjoy your stay!")
|
||||
Layout.margins: Nheko.paddingLarge
|
||||
color: timelineRoot.palette.text
|
||||
font.pointSize: fontMetrics.font.pointSize * 1.5
|
||||
wrapMode: Text.Wrap
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: qsTr("Enjoy your stay!")
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
FlatButton {
|
||||
Layout.margins: Nheko.paddingLarge
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.margins: Nheko.paddingLarge
|
||||
text: qsTr("REGISTER")
|
||||
|
||||
onClicked: {
|
||||
mainWindow.push(registerPage);
|
||||
}
|
||||
}
|
||||
FlatButton {
|
||||
Layout.margins: Nheko.paddingLarge
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.margins: Nheko.paddingLarge
|
||||
text: qsTr("LOGIN")
|
||||
|
||||
onClicked: {
|
||||
mainWindow.push(loginPage);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import im.nheko
|
||||
|
@ -10,42 +8,39 @@ import im.nheko
|
|||
Slider {
|
||||
id: control
|
||||
|
||||
property color progressColor: timelineRoot.palette.highlight
|
||||
property bool alwaysShowSlider: true
|
||||
property color progressColor: timelineRoot.palette.highlight
|
||||
property int sliderRadius: 16
|
||||
|
||||
value: 0
|
||||
implicitHeight: sliderRadius
|
||||
padding: 0
|
||||
value: 0
|
||||
|
||||
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
|
||||
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 {
|
||||
width: control.visualPosition * parent.width
|
||||
height: parent.height
|
||||
color: control.progressColor
|
||||
height: parent.height
|
||||
radius: 2
|
||||
width: control.visualPosition * parent.width
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
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: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
|
||||
|
@ -11,28 +9,21 @@ Item {
|
|||
|
||||
property color color: "#22000000"
|
||||
property real maxRadius: Math.max(width, height)
|
||||
readonly property real opacityAnimationDuration: 300
|
||||
readonly property real radiusAnimationRate: 0.05
|
||||
readonly property real radiusTailAnimationRate: 0.5
|
||||
readonly property real opacityAnimationDuration: 300
|
||||
property var rippleTarget: parent
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
PointHandler {
|
||||
id: ph
|
||||
|
||||
onGrabChanged: {
|
||||
circle.centerX = point.position.x
|
||||
circle.centerY = point.position.y
|
||||
}
|
||||
|
||||
target: Rectangle {
|
||||
id: backgroundLayer
|
||||
parent: rippleTarget
|
||||
|
||||
anchors.fill: parent
|
||||
color: "transparent"
|
||||
clip: true
|
||||
color: "transparent"
|
||||
parent: rippleTarget
|
||||
|
||||
Rectangle {
|
||||
id: circle
|
||||
|
@ -40,15 +31,14 @@ Item {
|
|||
property real centerX
|
||||
property real centerY
|
||||
|
||||
color: ripple.color
|
||||
height: radius * 2
|
||||
radius: 0
|
||||
state: ph.active ? "ACTIVE" : "NORMAL"
|
||||
width: radius * 2
|
||||
x: centerX - radius
|
||||
y: centerY - radius
|
||||
|
||||
height: radius*2
|
||||
width: radius*2
|
||||
radius: 0
|
||||
color: ripple.color
|
||||
|
||||
state: ph.active ? "ACTIVE" : "NORMAL"
|
||||
states: [
|
||||
State {
|
||||
name: "NORMAL"
|
||||
|
@ -65,26 +55,29 @@ Item {
|
|||
SequentialAnimation {
|
||||
//PropertyAction { target: circle; property: "centerX"; value: ph.point.position.x }
|
||||
//PropertyAction { target: circle; property: "centerY"; value: ph.point.position.y }
|
||||
PropertyAction { target: circle; property: "visible"; value: true }
|
||||
PropertyAction { target: circle; property: "opacity"; value: 1 }
|
||||
|
||||
PropertyAction {
|
||||
property: "visible"
|
||||
target: circle
|
||||
value: true
|
||||
}
|
||||
PropertyAction {
|
||||
property: "opacity"
|
||||
target: circle
|
||||
value: 1
|
||||
}
|
||||
NumberAnimation {
|
||||
id: radius_animation
|
||||
|
||||
target: circle
|
||||
properties: "radius"
|
||||
from: 0
|
||||
to: ripple.maxRadius
|
||||
duration: ripple.maxRadius / ripple.radiusAnimationRate
|
||||
from: 0
|
||||
properties: "radius"
|
||||
target: circle
|
||||
to: ripple.maxRadius
|
||||
|
||||
easing {
|
||||
type: Easing.OutQuad
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
Transition {
|
||||
from: "ACTIVE"
|
||||
|
@ -94,38 +87,41 @@ Item {
|
|||
ParallelAnimation {
|
||||
NumberAnimation {
|
||||
id: radius_tail_animation
|
||||
|
||||
target: circle
|
||||
properties: "radius"
|
||||
to: ripple.maxRadius
|
||||
duration: ripple.maxRadius / ripple.radiusTailAnimationRate
|
||||
properties: "radius"
|
||||
target: circle
|
||||
to: ripple.maxRadius
|
||||
|
||||
easing {
|
||||
type: Easing.Linear
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
id: opacity_animation
|
||||
|
||||
target: circle
|
||||
properties: "opacity"
|
||||
to: 0
|
||||
duration: ripple.opacityAnimationDuration
|
||||
properties: "opacity"
|
||||
target: circle
|
||||
to: 0
|
||||
|
||||
easing {
|
||||
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-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import im.nheko
|
||||
|
@ -9,8 +7,8 @@ import im.nheko
|
|||
Popup {
|
||||
id: snackbar
|
||||
|
||||
property var messages: []
|
||||
property string currentMessage: ""
|
||||
property var messages: []
|
||||
|
||||
function showNotification(msg) {
|
||||
messages.push(msg);
|
||||
|
@ -21,10 +19,58 @@ Popup {
|
|||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: dismissTimer
|
||||
interval: 10000
|
||||
onTriggered: snackbar.close()
|
||||
opacity: 0
|
||||
padding: Nheko.paddingLarge
|
||||
parent: Overlay.overlay
|
||||
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: {
|
||||
|
@ -38,61 +84,10 @@ Popup {
|
|||
}
|
||||
}
|
||||
|
||||
parent: Overlay.overlay
|
||||
opacity: 0
|
||||
y: -100
|
||||
x: (parent.width - width)/2
|
||||
padding: Nheko.paddingLarge
|
||||
Timer {
|
||||
id: dismissTimer
|
||||
interval: 10000
|
||||
|
||||
contentItem: Label {
|
||||
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
|
||||
onTriggered: snackbar.close()
|
||||
}
|
||||
}
|
||||
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: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import "./animations"
|
||||
import "animations"
|
||||
//import QtGraphicalEffects 1.12
|
||||
import QtQuick 2.12
|
||||
|
||||
Item {
|
||||
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 var colors: ["#c0def5", "#87aade", "white"]
|
||||
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 pauseDuration: barCount * 150
|
||||
property bool running: true
|
||||
property int spacing: 0
|
||||
|
||||
height: 40
|
||||
width: barCount * (height * 0.375)
|
||||
|
||||
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 {
|
||||
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 {
|
||||
|
@ -152,5 +125,4 @@ Item {
|
|||
// }
|
||||
|
||||
//}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,32 +1,26 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.12
|
||||
|
||||
SequentialAnimation {
|
||||
property alias target: numberAnimation.target
|
||||
property alias glowDuration: numberAnimation.duration
|
||||
property int pauseDuration: 150
|
||||
property double offset: 0
|
||||
property int pauseDuration: 150
|
||||
property alias target: numberAnimation.target
|
||||
|
||||
loops: Animation.Infinite
|
||||
|
||||
PauseAnimation {
|
||||
duration: pauseDuration * offset
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
id: numberAnimation
|
||||
|
||||
property: "opacity"
|
||||
from: 0
|
||||
property: "opacity"
|
||||
to: 1
|
||||
}
|
||||
|
||||
PauseAnimation {
|
||||
duration: pauseDuration * (1 - offset)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import "../"
|
||||
import "../../"
|
||||
import QtMultimedia 5.15
|
||||
|
@ -15,27 +13,22 @@ Rectangle {
|
|||
id: control
|
||||
|
||||
property alias desiredVolume: volumeSlider.desiredVolume
|
||||
property var duration
|
||||
property bool mediaLoaded: false
|
||||
property var mediaState
|
||||
property bool muted: false
|
||||
property bool playingVideo: false
|
||||
property var mediaState
|
||||
property bool mediaLoaded: false
|
||||
property var duration
|
||||
property var positionValue: 0
|
||||
property var position
|
||||
property var positionValue: 0
|
||||
property bool shouldShowControls: !playingVideo || playerMouseArea.shouldShowControls || volumeSlider.state == "shown"
|
||||
|
||||
signal playPauseActivated()
|
||||
signal loadActivated()
|
||||
|
||||
function showControls() {
|
||||
controlHideTimer.restart();
|
||||
}
|
||||
signal loadActivated
|
||||
signal playPauseActivated
|
||||
|
||||
function durationToString(duration) {
|
||||
function maybeZeroPrepend(time) {
|
||||
return (time < 10) ? "0" + time.toString() : time.toString();
|
||||
}
|
||||
|
||||
var totalSeconds = Math.floor(duration / 1000);
|
||||
var seconds = totalSeconds % 60;
|
||||
var minutes = (Math.floor(totalSeconds / 60)) % 60;
|
||||
|
@ -46,16 +39,25 @@ Rectangle {
|
|||
var hh = hours.toString();
|
||||
if (hours < 1)
|
||||
return mm + ":" + ss;
|
||||
|
||||
return hh + ":" + mm + ":" + ss;
|
||||
}
|
||||
function showControls() {
|
||||
controlHideTimer.restart();
|
||||
}
|
||||
|
||||
color: {
|
||||
var wc = timelineRoot.palette.alternateBase;
|
||||
return Qt.rgba(wc.r, wc.g, wc.b, 0.5);
|
||||
}
|
||||
opacity: control.shouldShowControls ? 1 : 0
|
||||
height: controlLayout.implicitHeight
|
||||
opacity: control.shouldShowControls ? 1 : 0
|
||||
|
||||
// Fade controls in/out
|
||||
Behavior on opacity {
|
||||
OpacityAnimator {
|
||||
duration: 100
|
||||
}
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
id: playerMouseArea
|
||||
|
@ -64,41 +66,38 @@ Rectangle {
|
|||
|
||||
onHoveredChanged: showControls()
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: controlLayout
|
||||
|
||||
enabled: control.shouldShowControls
|
||||
spacing: 0
|
||||
anchors.bottom: control.bottom
|
||||
anchors.left: control.left
|
||||
anchors.right: control.right
|
||||
enabled: control.shouldShowControls
|
||||
spacing: 0
|
||||
|
||||
NhekoSlider {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Nheko.paddingSmall
|
||||
Layout.rightMargin: Nheko.paddingSmall
|
||||
alwaysShowSlider: false
|
||||
enabled: control.mediaLoaded
|
||||
value: control.positionValue
|
||||
onMoved: control.position = value
|
||||
from: 0
|
||||
to: control.duration
|
||||
alwaysShowSlider: false
|
||||
}
|
||||
value: control.positionValue
|
||||
|
||||
onMoved: control.position = value
|
||||
}
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: Nheko.paddingSmall
|
||||
spacing: Nheko.paddingSmall
|
||||
Layout.fillWidth: true
|
||||
|
||||
// Cache/Play/pause button
|
||||
ImageButton {
|
||||
id: playbackStateImage
|
||||
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
buttonTextColor: timelineRoot.palette.text
|
||||
Layout.preferredHeight: 24
|
||||
Layout.preferredWidth: 24
|
||||
buttonTextColor: timelineRoot.palette.text
|
||||
image: {
|
||||
if (control.mediaLoaded) {
|
||||
if (control.mediaState == MediaPlayer.PlayingState)
|
||||
|
@ -109,38 +108,48 @@ Rectangle {
|
|||
return ":/icons/icons/ui/download.svg";
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: control.mediaLoaded ? control.playPauseActivated() : control.loadActivated()
|
||||
}
|
||||
|
||||
ImageButton {
|
||||
id: volumeButton
|
||||
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
buttonTextColor: timelineRoot.palette.text
|
||||
Layout.preferredHeight: 24
|
||||
Layout.preferredWidth: 24
|
||||
buttonTextColor: timelineRoot.palette.text
|
||||
image: {
|
||||
if (control.muted || control.desiredVolume <= 0)
|
||||
return ":/icons/icons/ui/volume-off-indicator.svg";
|
||||
else
|
||||
return ":/icons/icons/ui/volume-up.svg";
|
||||
}
|
||||
|
||||
onClicked: control.muted = !control.muted
|
||||
}
|
||||
|
||||
NhekoSlider {
|
||||
id: volumeSlider
|
||||
|
||||
property real desiredVolume: QtMultimedia.convertVolume(volumeSlider.value, QtMultimedia.LogarithmicVolumeScale, QtMultimedia.LinearVolumeScale)
|
||||
|
||||
state: ""
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
Layout.preferredWidth: 0
|
||||
opacity: 0
|
||||
orientation: Qt.Horizontal
|
||||
state: ""
|
||||
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: [
|
||||
Transition {
|
||||
|
@ -151,20 +160,16 @@ Rectangle {
|
|||
PauseAnimation {
|
||||
duration: 50
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
duration: 100
|
||||
properties: "opacity"
|
||||
easing.type: Easing.InQuad
|
||||
properties: "opacity"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
properties: "Layout.preferredWidth"
|
||||
duration: 150
|
||||
properties: "Layout.preferredWidth"
|
||||
}
|
||||
|
||||
},
|
||||
Transition {
|
||||
from: "shown"
|
||||
|
@ -174,72 +179,40 @@ Rectangle {
|
|||
PauseAnimation {
|
||||
duration: 100
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
NumberAnimation {
|
||||
duration: 100
|
||||
properties: "opacity"
|
||||
easing.type: Easing.InQuad
|
||||
properties: "opacity"
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
properties: "Layout.preferredWidth"
|
||||
duration: 150
|
||||
properties: "Layout.preferredWidth"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
]
|
||||
|
||||
states: State {
|
||||
name: "shown"
|
||||
when: Settings.mobileMode || volumeButton.hovered || volumeSlider.hovered || volumeSlider.pressed
|
||||
|
||||
PropertyChanges {
|
||||
target: volumeSlider
|
||||
Layout.preferredWidth: 100
|
||||
onDesiredVolumeChanged: {
|
||||
control.muted = !(desiredVolume > 0);
|
||||
}
|
||||
|
||||
PropertyChanges {
|
||||
target: volumeSlider
|
||||
opacity: 1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
text: (!control.mediaLoaded ? "-- " : durationToString(control.positionValue)) + " / " + durationToString(control.duration)
|
||||
color: timelineRoot.palette.text
|
||||
text: (!control.mediaLoaded ? "-- " : durationToString(control.positionValue)) + " / " + durationToString(control.duration)
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// For hiding controls on stationary cursor
|
||||
Timer {
|
||||
id: controlHideTimer
|
||||
|
||||
interval: 1500 //ms
|
||||
repeat: false
|
||||
}
|
||||
|
||||
// Fade controls in/out
|
||||
Behavior on opacity {
|
||||
OpacityAnimator {
|
||||
duration: 100
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import "../"
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.3
|
||||
|
@ -10,51 +8,46 @@ import QtQuick.Layouts 1.2
|
|||
import im.nheko
|
||||
|
||||
Rectangle {
|
||||
visible: CallManager.isOnCall
|
||||
color: callInviteBar.color
|
||||
implicitHeight: visible ? rowLayout.height + 8 : 0
|
||||
visible: CallManager.isOnCall
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
|
||||
onClicked: {
|
||||
if (CallManager.callType != Voip.VOICE)
|
||||
stackLayout.currentIndex = stackLayout.currentIndex ? 0 : 1;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: rowLayout
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 8
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 8
|
||||
|
||||
Avatar {
|
||||
width: Nheko.avatarSize
|
||||
displayName: CallManager.callPartyDisplayName
|
||||
height: Nheko.avatarSize
|
||||
url: CallManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/")
|
||||
userid: CallManager.callParty
|
||||
displayName: CallManager.callPartyDisplayName
|
||||
width: Nheko.avatarSize
|
||||
|
||||
onClicked: TimelineManager.openImageOverlay(room, room.avatarUrl(userid), room.data.eventId)
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.leftMargin: 8
|
||||
color: "#000000"
|
||||
font.pointSize: fontMetrics.font.pointSize * 1.1
|
||||
text: CallManager.callPartyDisplayName
|
||||
color: "#000000"
|
||||
}
|
||||
|
||||
Image {
|
||||
id: callTypeIcon
|
||||
|
||||
Layout.leftMargin: 4
|
||||
Layout.preferredWidth: 24
|
||||
Layout.preferredHeight: 24
|
||||
Layout.preferredWidth: 24
|
||||
}
|
||||
|
||||
Item {
|
||||
states: [
|
||||
State {
|
||||
|
@ -62,41 +55,35 @@ Rectangle {
|
|||
when: CallManager.callType == Voip.VOICE
|
||||
|
||||
PropertyChanges {
|
||||
target: callTypeIcon
|
||||
source: "qrc:/icons/icons/ui/place-call.svg"
|
||||
target: callTypeIcon
|
||||
}
|
||||
|
||||
},
|
||||
State {
|
||||
name: "VIDEO"
|
||||
when: CallManager.callType == Voip.VIDEO
|
||||
|
||||
PropertyChanges {
|
||||
target: callTypeIcon
|
||||
source: "qrc:/icons/icons/ui/video.svg"
|
||||
target: callTypeIcon
|
||||
}
|
||||
|
||||
},
|
||||
State {
|
||||
name: "SCREEN"
|
||||
when: CallManager.callType == Voip.SCREEN
|
||||
|
||||
PropertyChanges {
|
||||
target: callTypeIcon
|
||||
source: "qrc:/icons/icons/ui/screen-share.svg"
|
||||
target: callTypeIcon
|
||||
}
|
||||
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Label {
|
||||
id: callStateLabel
|
||||
|
||||
font.pointSize: fontMetrics.font.pointSize * 1.1
|
||||
color: "#000000"
|
||||
font.pointSize: fontMetrics.font.pointSize * 1.1
|
||||
}
|
||||
|
||||
Item {
|
||||
states: [
|
||||
State {
|
||||
|
@ -107,7 +94,6 @@ Rectangle {
|
|||
target: callStateLabel
|
||||
text: qsTr("Calling...")
|
||||
}
|
||||
|
||||
},
|
||||
State {
|
||||
name: "CONNECTING"
|
||||
|
@ -117,7 +103,6 @@ Rectangle {
|
|||
target: callStateLabel
|
||||
text: qsTr("Connecting...")
|
||||
}
|
||||
|
||||
},
|
||||
State {
|
||||
name: "ANSWERSENT"
|
||||
|
@ -127,7 +112,6 @@ Rectangle {
|
|||
target: callStateLabel
|
||||
text: qsTr("Connecting...")
|
||||
}
|
||||
|
||||
},
|
||||
State {
|
||||
name: "CONNECTED"
|
||||
|
@ -137,17 +121,14 @@ Rectangle {
|
|||
target: callStateLabel
|
||||
text: "00:00"
|
||||
}
|
||||
|
||||
PropertyChanges {
|
||||
target: callTimer
|
||||
startTime: Math.floor((new Date()).getTime() / 1000)
|
||||
target: callTimer
|
||||
}
|
||||
|
||||
PropertyChanges {
|
||||
target: stackLayout
|
||||
currentIndex: CallManager.callType != Voip.VOICE ? 1 : 0
|
||||
target: stackLayout
|
||||
}
|
||||
|
||||
},
|
||||
State {
|
||||
name: "DISCONNECTED"
|
||||
|
@ -157,16 +138,13 @@ Rectangle {
|
|||
target: callStateLabel
|
||||
text: ""
|
||||
}
|
||||
|
||||
PropertyChanges {
|
||||
target: stackLayout
|
||||
currentIndex: 0
|
||||
target: stackLayout
|
||||
}
|
||||
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: callTimer
|
||||
|
||||
|
@ -177,8 +155,9 @@ Rectangle {
|
|||
}
|
||||
|
||||
interval: 1000
|
||||
running: CallManager.callState == Voip.CONNECTED
|
||||
repeat: true
|
||||
running: CallManager.callState == Voip.CONNECTED
|
||||
|
||||
onTriggered: {
|
||||
var d = new Date();
|
||||
let seconds = Math.floor(d.getTime() / 1000 - startTime);
|
||||
|
@ -188,44 +167,40 @@ Rectangle {
|
|||
callStateLabel.text = (h ? (pad(h) + ":") : "") + pad(m) + ":" + pad(s);
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
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"
|
||||
font.pointSize: fontMetrics.font.pointSize * 1.1
|
||||
text: qsTr("You are screen sharing")
|
||||
visible: CallManager.callType == Voip.SCREEN && CallManager.callState == Voip.CONNECTED
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
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
|
||||
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()
|
||||
}
|
||||
|
||||
ImageButton {
|
||||
Layout.leftMargin: 8
|
||||
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.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()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.3
|
||||
import QtQuick.Layouts 1.2
|
||||
|
@ -11,79 +9,68 @@ import im.nheko
|
|||
Popup {
|
||||
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
|
||||
// see https://doc.qt.io/qt-5/qml-qtquick-controls2-popup.html#anchors.centerIn-prop
|
||||
Component.onCompleted: {
|
||||
if (anchors)
|
||||
anchors.centerIn = parent;
|
||||
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 16
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 8
|
||||
Layout.topMargin: 8
|
||||
Layout.leftMargin: 8
|
||||
Layout.rightMargin: 8
|
||||
Layout.topMargin: 8
|
||||
spacing: 8
|
||||
|
||||
RowLayout {
|
||||
Image {
|
||||
Layout.preferredWidth: 22
|
||||
Layout.preferredHeight: 22
|
||||
Layout.preferredWidth: 22
|
||||
source: "image://colorimage/:/icons/icons/ui/microphone-unmute.svg?" + timelineRoot.palette.windowText
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: micCombo
|
||||
|
||||
Layout.fillWidth: true
|
||||
model: CallManager.mics
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
visible: CallManager.callType == Voip.VIDEO && CallManager.cameras.length > 0
|
||||
|
||||
Image {
|
||||
Layout.preferredWidth: 22
|
||||
Layout.preferredHeight: 22
|
||||
Layout.preferredWidth: 22
|
||||
source: "image://colorimage/:/icons/icons/ui/video-call.svg?" + timelineRoot.palette.windowText
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: cameraCombo
|
||||
|
||||
Layout.fillWidth: true
|
||||
model: CallManager.cameras
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DialogButtonBox {
|
||||
Layout.leftMargin: 128
|
||||
standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
|
||||
|
||||
onAccepted: {
|
||||
Settings.microphone = micCombo.currentText;
|
||||
if (cameraCombo.visible)
|
||||
Settings.camera = cameraCombo.currentText;
|
||||
|
||||
close();
|
||||
}
|
||||
onRejected: {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: timelineRoot.palette.window
|
||||
border.color: timelineRoot.palette.windowText
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import "../"
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.3
|
||||
|
@ -11,54 +9,51 @@ import im.nheko
|
|||
|
||||
Popup {
|
||||
id: callInv
|
||||
|
||||
closePolicy: Popup.NoAutoClose
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
palette: timelineRoot.palette
|
||||
width: parent.width
|
||||
|
||||
background: Rectangle {
|
||||
border.color: timelineRoot.palette.windowText
|
||||
color: timelineRoot.palette.window
|
||||
}
|
||||
|
||||
Component {
|
||||
id: deviceError
|
||||
|
||||
DeviceError {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onNewInviteState() {
|
||||
if (!CallManager.haveCallInvite)
|
||||
close();
|
||||
|
||||
}
|
||||
|
||||
target: CallManager
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
|
||||
Label {
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
Layout.topMargin: callInv.parent.height / 25
|
||||
Layout.fillWidth: true
|
||||
text: CallManager.callPartyDisplayName
|
||||
font.pointSize: fontMetrics.font.pointSize * 2
|
||||
Layout.topMargin: callInv.parent.height / 25
|
||||
color: timelineRoot.palette.windowText
|
||||
font.pointSize: fontMetrics.font.pointSize * 2
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: CallManager.callPartyDisplayName
|
||||
}
|
||||
|
||||
Avatar {
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
Layout.preferredHeight: callInv.height / 5
|
||||
Layout.preferredWidth: callInv.height / 5
|
||||
displayName: CallManager.callPartyDisplayName
|
||||
url: CallManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/")
|
||||
userid: CallManager.callParty
|
||||
displayName: CallManager.callPartyDisplayName
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
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"
|
||||
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
Layout.preferredWidth: callInv.height / 10
|
||||
Layout.preferredHeight: callInv.height / 10
|
||||
Layout.preferredWidth: callInv.height / 10
|
||||
source: "image://colorimage/" + image + "?" + timelineRoot.palette.windowText
|
||||
}
|
||||
|
||||
Label {
|
||||
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
|
||||
font.pointSize: fontMetrics.font.pointSize * 2
|
||||
text: CallManager.callType == Voip.VIDEO ? qsTr("Video Call") : qsTr("Voice Call")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: deviceCombos
|
||||
|
||||
|
@ -93,41 +85,32 @@ Popup {
|
|||
Layout.alignment: Qt.AlignCenter
|
||||
|
||||
Image {
|
||||
Layout.preferredWidth: deviceCombos.imageSize
|
||||
Layout.preferredHeight: deviceCombos.imageSize
|
||||
Layout.preferredWidth: deviceCombos.imageSize
|
||||
source: "image://colorimage/:/icons/icons/ui/microphone-unmute.svg?" + timelineRoot.palette.windowText
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: micCombo
|
||||
|
||||
Layout.fillWidth: true
|
||||
model: CallManager.mics
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
visible: CallManager.callType == Voip.VIDEO && CallManager.cameras.length > 0
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
visible: CallManager.callType == Voip.VIDEO && CallManager.cameras.length > 0
|
||||
|
||||
Image {
|
||||
Layout.preferredWidth: deviceCombos.imageSize
|
||||
Layout.preferredHeight: deviceCombos.imageSize
|
||||
Layout.preferredWidth: deviceCombos.imageSize
|
||||
source: "image://colorimage/:/icons/icons/ui/video.svg?" + timelineRoot.palette.windowText
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: cameraCombo
|
||||
|
||||
Layout.fillWidth: true
|
||||
model: CallManager.cameras
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: buttonLayout
|
||||
|
||||
|
@ -150,60 +133,48 @@ Popup {
|
|||
spacing: callInv.height / 6
|
||||
|
||||
RoundButton {
|
||||
implicitWidth: buttonLayout.buttonSize
|
||||
implicitHeight: buttonLayout.buttonSize
|
||||
onClicked: {
|
||||
CallManager.hangUp();
|
||||
close();
|
||||
}
|
||||
implicitWidth: buttonLayout.buttonSize
|
||||
|
||||
background: Rectangle {
|
||||
radius: buttonLayout.buttonSize / 2
|
||||
color: "#ff0000"
|
||||
radius: buttonLayout.buttonSize / 2
|
||||
}
|
||||
|
||||
contentItem: Image {
|
||||
source: "image://colorimage/:/icons/icons/ui/end-call.svg?#ffffff"
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
CallManager.hangUp();
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
RoundButton {
|
||||
id: acceptButton
|
||||
|
||||
property string image: CallManager.callType == Voip.VIDEO ? ":/icons/icons/ui/video.svg" : ":/icons/icons/ui/place-call.svg"
|
||||
|
||||
implicitWidth: 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: {
|
||||
if (buttonLayout.validateMic()) {
|
||||
Settings.microphone = micCombo.currentText;
|
||||
if (cameraCombo.visible)
|
||||
Settings.camera = cameraCombo.currentText;
|
||||
|
||||
CallManager.acceptInvite();
|
||||
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: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import "../"
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.3
|
||||
|
@ -10,88 +8,78 @@ import QtQuick.Layouts 1.2
|
|||
import im.nheko
|
||||
|
||||
Rectangle {
|
||||
visible: CallManager.haveCallInvite && !Settings.mobileMode
|
||||
color: "#2ECC71"
|
||||
implicitHeight: visible ? rowLayout.height + 8 : 0
|
||||
visible: CallManager.haveCallInvite && !Settings.mobileMode
|
||||
|
||||
Component {
|
||||
id: devicesDialog
|
||||
|
||||
CallDevices {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Component {
|
||||
id: deviceError
|
||||
|
||||
DeviceError {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: rowLayout
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 8
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 8
|
||||
|
||||
Avatar {
|
||||
width: Nheko.avatarSize
|
||||
displayName: CallManager.callPartyDisplayName
|
||||
height: Nheko.avatarSize
|
||||
url: CallManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/")
|
||||
userid: CallManager.callParty
|
||||
displayName: CallManager.callPartyDisplayName
|
||||
width: Nheko.avatarSize
|
||||
|
||||
onClicked: TimelineManager.openImageOverlay(room, room.avatarUrl(userid), room.data.eventId)
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.leftMargin: 8
|
||||
color: "#000000"
|
||||
font.pointSize: fontMetrics.font.pointSize * 1.1
|
||||
text: CallManager.callPartyDisplayName
|
||||
color: "#000000"
|
||||
}
|
||||
|
||||
Image {
|
||||
Layout.leftMargin: 4
|
||||
Layout.preferredWidth: 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"
|
||||
}
|
||||
|
||||
Label {
|
||||
color: "#000000"
|
||||
font.pointSize: fontMetrics.font.pointSize * 1.1
|
||||
text: CallManager.callType == Voip.VIDEO ? qsTr("Video Call") : qsTr("Voice Call")
|
||||
color: "#000000"
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
ImageButton {
|
||||
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.visible: hovered
|
||||
buttonTextColor: "#000000"
|
||||
height: 20
|
||||
hoverEnabled: true
|
||||
image: ":/icons/icons/ui/settings.svg"
|
||||
width: 20
|
||||
|
||||
onClicked: {
|
||||
var dialog = devicesDialog.createObject(timelineRoot);
|
||||
dialog.open();
|
||||
timelineRoot.destroyOnClose(dialog);
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.rightMargin: 4
|
||||
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
|
||||
text: qsTr("Accept")
|
||||
|
||||
onClicked: {
|
||||
if (CallManager.mics.length == 0) {
|
||||
var dialog = deviceError.createObject(timelineRoot, {
|
||||
|
@ -122,17 +110,15 @@ Rectangle {
|
|||
CallManager.acceptInvite();
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.rightMargin: 16
|
||||
icon.source: "qrc:/icons/icons/ui/end-call.svg"
|
||||
text: qsTr("Decline")
|
||||
palette: timelineRoot.palette
|
||||
text: qsTr("Decline")
|
||||
|
||||
onClicked: {
|
||||
CallManager.hangUp();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.3
|
||||
import QtQuick.Layouts 1.2
|
||||
|
@ -13,31 +11,28 @@ Popup {
|
|||
property var image
|
||||
|
||||
modal: true
|
||||
|
||||
background: Rectangle {
|
||||
border.color: timelineRoot.palette.windowText
|
||||
color: timelineRoot.palette.window
|
||||
}
|
||||
|
||||
// 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
|
||||
Component.onCompleted: {
|
||||
if (anchors)
|
||||
anchors.centerIn = parent;
|
||||
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Image {
|
||||
Layout.preferredWidth: 16
|
||||
Layout.preferredHeight: 16
|
||||
Layout.preferredWidth: 16
|
||||
source: "image://colorimage/" + image + "?" + timelineRoot.palette.windowText
|
||||
}
|
||||
|
||||
Label {
|
||||
text: errorString
|
||||
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: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import "../"
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.3
|
||||
|
@ -11,46 +9,43 @@ import im.nheko
|
|||
|
||||
Popup {
|
||||
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
|
||||
// see https://doc.qt.io/qt-5/qml-qtquick-controls2-popup.html#anchors.centerIn-prop
|
||||
Component.onCompleted: {
|
||||
if (anchors)
|
||||
anchors.centerIn = parent;
|
||||
|
||||
}
|
||||
palette: timelineRoot.palette
|
||||
|
||||
Component {
|
||||
id: deviceError
|
||||
|
||||
DeviceError {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: columnLayout
|
||||
|
||||
spacing: 16
|
||||
|
||||
RowLayout {
|
||||
Layout.topMargin: 8
|
||||
Layout.leftMargin: 8
|
||||
Layout.topMargin: 8
|
||||
|
||||
Label {
|
||||
text: qsTr("Place a call to %1?").arg(room.roomName)
|
||||
color: timelineRoot.palette.windowText
|
||||
text: qsTr("Place a call to %1?").arg(room.roomName)
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: buttonLayout
|
||||
|
||||
function validateMic() {
|
||||
if (CallManager.mics.length == 0) {
|
||||
var dialog = deviceError.createObject(timelineRoot, {
|
||||
|
@ -69,17 +64,18 @@ Popup {
|
|||
|
||||
Avatar {
|
||||
Layout.rightMargin: cameraCombo.visible ? 16 : 64
|
||||
width: Nheko.avatarSize
|
||||
height: Nheko.avatarSize
|
||||
url: room.roomAvatarUrl.replace("mxc://", "image://MxcImage/")
|
||||
displayName: room.roomName
|
||||
height: Nheko.avatarSize
|
||||
roomid: room.roomId
|
||||
url: room.roomAvatarUrl.replace("mxc://", "image://MxcImage/")
|
||||
width: Nheko.avatarSize
|
||||
|
||||
onClicked: TimelineManager.openImageOverlay(room, room.avatarUrl(userid), room.data.eventId)
|
||||
}
|
||||
|
||||
Button {
|
||||
text: qsTr("Voice")
|
||||
icon.source: "qrc:/icons/icons/ui/place-call.svg"
|
||||
text: qsTr("Voice")
|
||||
|
||||
onClicked: {
|
||||
if (buttonLayout.validateMic()) {
|
||||
Settings.microphone = micCombo.currentText;
|
||||
|
@ -88,11 +84,11 @@ Popup {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
visible: CallManager.cameras.length > 0
|
||||
text: qsTr("Video")
|
||||
icon.source: "qrc:/icons/icons/ui/video.svg"
|
||||
text: qsTr("Video")
|
||||
visible: CallManager.cameras.length > 0
|
||||
|
||||
onClicked: {
|
||||
if (buttonLayout.validateMic()) {
|
||||
Settings.microphone = micCombo.currentText;
|
||||
|
@ -102,16 +98,15 @@ Popup {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
visible: CallManager.screenShareSupported
|
||||
text: qsTr("Screen")
|
||||
icon.source: "qrc:/icons/icons/ui/screen-share.svg"
|
||||
text: qsTr("Screen")
|
||||
visible: CallManager.screenShareSupported
|
||||
|
||||
onClicked: {
|
||||
if (buttonLayout.validateMic()) {
|
||||
Settings.microphone = micCombo.currentText;
|
||||
Settings.camera = cameraCombo.currentText;
|
||||
|
||||
var dialog = screenShareDialog.createObject(timelineRoot);
|
||||
dialog.open();
|
||||
timelineRoot.destroyOnClose(dialog);
|
||||
|
@ -119,67 +114,50 @@ Popup {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: qsTr("Cancel")
|
||||
|
||||
onClicked: {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 8
|
||||
|
||||
RowLayout {
|
||||
Layout.bottomMargin: cameraCombo.visible ? 0 : 8
|
||||
Layout.leftMargin: 8
|
||||
Layout.rightMargin: 8
|
||||
Layout.bottomMargin: cameraCombo.visible ? 0 : 8
|
||||
|
||||
Image {
|
||||
Layout.preferredWidth: 22
|
||||
Layout.preferredHeight: 22
|
||||
Layout.preferredWidth: 22
|
||||
source: "image://colorimage/:/icons/icons/ui/microphone-unmute.svg?" + timelineRoot.palette.windowText
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: micCombo
|
||||
|
||||
Layout.fillWidth: true
|
||||
model: CallManager.mics
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
visible: CallManager.cameras.length > 0
|
||||
Layout.bottomMargin: 8
|
||||
Layout.leftMargin: 8
|
||||
Layout.rightMargin: 8
|
||||
Layout.bottomMargin: 8
|
||||
visible: CallManager.cameras.length > 0
|
||||
|
||||
Image {
|
||||
Layout.preferredWidth: 22
|
||||
Layout.preferredHeight: 22
|
||||
Layout.preferredWidth: 22
|
||||
source: "image://colorimage/:/icons/icons/ui/video.svg?" + timelineRoot.palette.windowText
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: cameraCombo
|
||||
|
||||
Layout.fillWidth: true
|
||||
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: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import "../"
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.3
|
||||
|
@ -10,154 +8,129 @@ import QtQuick.Layouts 1.2
|
|||
import im.nheko
|
||||
|
||||
Popup {
|
||||
anchors.centerIn: parent
|
||||
modal: true
|
||||
palette: timelineRoot.palette
|
||||
|
||||
anchors.centerIn: parent;
|
||||
background: Rectangle {
|
||||
border.color: timelineRoot.palette.windowText
|
||||
color: timelineRoot.palette.window
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
frameRateCombo.currentIndex = frameRateCombo.find(Settings.screenShareFrameRate);
|
||||
}
|
||||
palette: timelineRoot.palette
|
||||
|
||||
ColumnLayout {
|
||||
Label {
|
||||
Layout.topMargin: 16
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
Layout.bottomMargin: 16
|
||||
Layout.leftMargin: 8
|
||||
Layout.rightMargin: 8
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
text: qsTr("Share desktop with %1?").arg(room.roomName)
|
||||
Layout.topMargin: 16
|
||||
color: timelineRoot.palette.windowText
|
||||
text: qsTr("Share desktop with %1?").arg(room.roomName)
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.bottomMargin: 8
|
||||
Layout.leftMargin: 8
|
||||
Layout.rightMargin: 8
|
||||
Layout.bottomMargin: 8
|
||||
|
||||
Label {
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
text: qsTr("Window:")
|
||||
color: timelineRoot.palette.windowText
|
||||
text: qsTr("Window:")
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: windowCombo
|
||||
|
||||
Layout.fillWidth: true
|
||||
model: CallManager.windowList()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.bottomMargin: 8
|
||||
Layout.leftMargin: 8
|
||||
Layout.rightMargin: 8
|
||||
Layout.bottomMargin: 8
|
||||
|
||||
Label {
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
text: qsTr("Frame rate:")
|
||||
color: timelineRoot.palette.windowText
|
||||
text: qsTr("Frame rate:")
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: frameRateCombo
|
||||
|
||||
Layout.fillWidth: true
|
||||
model: ["25", "20", "15", "10", "5", "2", "1"]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
Layout.margins: 8
|
||||
columns: 2
|
||||
rowSpacing: 10
|
||||
Layout.margins: 8
|
||||
|
||||
MatrixText {
|
||||
text: qsTr("Include your camera picture-in-picture")
|
||||
}
|
||||
|
||||
ToggleButton {
|
||||
id: pipCheckBox
|
||||
|
||||
enabled: CallManager.cameras.length > 0
|
||||
checked: CallManager.cameras.length > 0 && Settings.screenSharePiP
|
||||
Layout.alignment: Qt.AlignRight
|
||||
checked: CallManager.cameras.length > 0 && Settings.screenSharePiP
|
||||
enabled: CallManager.cameras.length > 0
|
||||
}
|
||||
|
||||
MatrixText {
|
||||
text: qsTr("Request remote camera")
|
||||
ToolTip.text: qsTr("View your callee's camera like a regular video call")
|
||||
ToolTip.visible: hovered
|
||||
text: qsTr("Request remote camera")
|
||||
}
|
||||
|
||||
ToggleButton {
|
||||
id: remoteVideoCheckBox
|
||||
|
||||
Layout.alignment: Qt.AlignRight
|
||||
checked: Settings.screenShareRemoteVideo
|
||||
ToolTip.text: qsTr("View your callee's camera like a regular video call")
|
||||
ToolTip.visible: hovered
|
||||
checked: Settings.screenShareRemoteVideo
|
||||
}
|
||||
|
||||
MatrixText {
|
||||
text: qsTr("Hide mouse cursor")
|
||||
}
|
||||
|
||||
ToggleButton {
|
||||
id: hideCursorCheckBox
|
||||
|
||||
Layout.alignment: Qt.AlignRight
|
||||
checked: Settings.screenShareHideCursor
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.margins: 8
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Button {
|
||||
text: qsTr("Share")
|
||||
icon.source: "qrc:/icons/icons/ui/screen-share.svg"
|
||||
text: qsTr("Share")
|
||||
|
||||
onClicked: {
|
||||
Settings.screenShareFrameRate = frameRateCombo.currentText;
|
||||
Settings.screenSharePiP = pipCheckBox.checked;
|
||||
Settings.screenShareRemoteVideo = remoteVideoCheckBox.checked;
|
||||
Settings.screenShareHideCursor = hideCursorCheckBox.checked;
|
||||
|
||||
CallManager.sendInvite(room.roomId, Voip.SCREEN, windowCombo.currentIndex);
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: qsTr("Preview")
|
||||
|
||||
onClicked: {
|
||||
CallManager.previewWindow(windowCombo.currentIndex);
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: qsTr("Cancel")
|
||||
|
||||
onClicked: {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: timelineRoot.palette.window
|
||||
border.color: timelineRoot.palette.windowText
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.9
|
||||
import org.freedesktop.gstreamer.GLVideoItem 1.0
|
||||
|
||||
|
|
Loading…
Reference in a new issue