mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-22 19:08:58 +03:00
Lint qml with qml-format
This commit is contained in:
parent
517a126a44
commit
1a029112d9
31 changed files with 2863 additions and 2179 deletions
|
@ -1,111 +1,113 @@
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
|
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: activeCallBar
|
id: activeCallBar
|
||||||
visible: TimelineManager.callState != WebRTCState.DISCONNECTED
|
|
||||||
color: "#2ECC71"
|
|
||||||
implicitHeight: rowLayout.height + 8
|
|
||||||
|
|
||||||
RowLayout {
|
visible: TimelineManager.callState != WebRTCState.DISCONNECTED
|
||||||
id: rowLayout
|
color: "#2ECC71"
|
||||||
anchors.left: parent.left
|
implicitHeight: rowLayout.height + 8
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
anchors.leftMargin: 8
|
|
||||||
|
|
||||||
Avatar {
|
RowLayout {
|
||||||
width: avatarSize
|
id: rowLayout
|
||||||
height: avatarSize
|
|
||||||
|
|
||||||
url: TimelineManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/")
|
anchors.left: parent.left
|
||||||
displayName: TimelineManager.callPartyName
|
anchors.right: parent.right
|
||||||
}
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.leftMargin: 8
|
||||||
|
|
||||||
Label {
|
Avatar {
|
||||||
font.pointSize: fontMetrics.font.pointSize * 1.1
|
width: avatarSize
|
||||||
text: " " + TimelineManager.callPartyName + " "
|
height: avatarSize
|
||||||
}
|
url: TimelineManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
|
displayName: TimelineManager.callPartyName
|
||||||
|
}
|
||||||
|
|
||||||
Image {
|
Label {
|
||||||
Layout.preferredWidth: 24
|
font.pointSize: fontMetrics.font.pointSize * 1.1
|
||||||
Layout.preferredHeight: 24
|
text: " " + TimelineManager.callPartyName + " "
|
||||||
source: "qrc:/icons/icons/ui/place-call.png"
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
Image {
|
||||||
id: callStateLabel
|
Layout.preferredWidth: 24
|
||||||
font.pointSize: fontMetrics.font.pointSize * 1.1
|
Layout.preferredHeight: 24
|
||||||
}
|
source: "qrc:/icons/icons/ui/place-call.png"
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Label {
|
||||||
target: TimelineManager
|
id: callStateLabel
|
||||||
function onCallStateChanged(state) {
|
|
||||||
switch (state) {
|
|
||||||
case WebRTCState.INITIATING:
|
|
||||||
callStateLabel.text = qsTr("Initiating...")
|
|
||||||
break;
|
|
||||||
case WebRTCState.OFFERSENT:
|
|
||||||
callStateLabel.text = qsTr("Calling...")
|
|
||||||
break;
|
|
||||||
case WebRTCState.CONNECTING:
|
|
||||||
callStateLabel.text = qsTr("Connecting...")
|
|
||||||
break;
|
|
||||||
case WebRTCState.CONNECTED:
|
|
||||||
callStateLabel.text = "00:00"
|
|
||||||
var d = new Date()
|
|
||||||
callTimer.startTime = Math.floor(d.getTime() / 1000)
|
|
||||||
break;
|
|
||||||
case WebRTCState.DISCONNECTED:
|
|
||||||
callStateLabel.text = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
font.pointSize: fontMetrics.font.pointSize * 1.1
|
||||||
id: callTimer
|
}
|
||||||
property int startTime
|
|
||||||
interval: 1000
|
|
||||||
running: TimelineManager.callState == WebRTCState.CONNECTED
|
|
||||||
repeat: true
|
|
||||||
onTriggered: {
|
|
||||||
var d = new Date()
|
|
||||||
let seconds = Math.floor(d.getTime() / 1000 - startTime)
|
|
||||||
let s = Math.floor(seconds % 60)
|
|
||||||
let m = Math.floor(seconds / 60) % 60
|
|
||||||
let h = Math.floor(seconds / 3600)
|
|
||||||
callStateLabel.text = (h ? (pad(h) + ":") : "") + pad(m) + ":" + pad(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
function pad(n) {
|
Connections {
|
||||||
return (n < 10) ? ("0" + n) : n
|
function onCallStateChanged(state) {
|
||||||
}
|
switch (state) {
|
||||||
}
|
case WebRTCState.INITIATING:
|
||||||
|
callStateLabel.text = qsTr("Initiating...");
|
||||||
|
break;
|
||||||
|
case WebRTCState.OFFERSENT:
|
||||||
|
callStateLabel.text = qsTr("Calling...");
|
||||||
|
break;
|
||||||
|
case WebRTCState.CONNECTING:
|
||||||
|
callStateLabel.text = qsTr("Connecting...");
|
||||||
|
break;
|
||||||
|
case WebRTCState.CONNECTED:
|
||||||
|
callStateLabel.text = "00:00";
|
||||||
|
var d = new Date();
|
||||||
|
callTimer.startTime = Math.floor(d.getTime() / 1000);
|
||||||
|
break;
|
||||||
|
case WebRTCState.DISCONNECTED:
|
||||||
|
callStateLabel.text = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Item {
|
target: TimelineManager
|
||||||
Layout.fillWidth: true
|
}
|
||||||
}
|
|
||||||
|
|
||||||
ImageButton {
|
Timer {
|
||||||
width: 24
|
id: callTimer
|
||||||
height: 24
|
|
||||||
buttonTextColor: "#000000"
|
|
||||||
image: TimelineManager.isMicMuted ?
|
|
||||||
":/icons/icons/ui/microphone-unmute.png" :
|
|
||||||
":/icons/icons/ui/microphone-mute.png"
|
|
||||||
|
|
||||||
hoverEnabled: true
|
property int startTime
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: TimelineManager.isMicMuted ? qsTr("Unmute Mic") : qsTr("Mute Mic")
|
|
||||||
|
|
||||||
onClicked: TimelineManager.toggleMicMute()
|
function pad(n) {
|
||||||
}
|
return (n < 10) ? ("0" + n) : n;
|
||||||
|
}
|
||||||
|
|
||||||
|
interval: 1000
|
||||||
|
running: TimelineManager.callState == WebRTCState.CONNECTED
|
||||||
|
repeat: true
|
||||||
|
onTriggered: {
|
||||||
|
var d = new Date();
|
||||||
|
let seconds = Math.floor(d.getTime() / 1000 - startTime);
|
||||||
|
let s = Math.floor(seconds % 60);
|
||||||
|
let m = Math.floor(seconds / 60) % 60;
|
||||||
|
let h = Math.floor(seconds / 3600);
|
||||||
|
callStateLabel.text = (h ? (pad(h) + ":") : "") + pad(m) + ":" + pad(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageButton {
|
||||||
|
width: 24
|
||||||
|
height: 24
|
||||||
|
buttonTextColor: "#000000"
|
||||||
|
image: TimelineManager.isMicMuted ? ":/icons/icons/ui/microphone-unmute.png" : ":/icons/icons/ui/microphone-mute.png"
|
||||||
|
hoverEnabled: true
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
ToolTip.text: TimelineManager.isMicMuted ? qsTr("Unmute Mic") : qsTr("Mute Mic")
|
||||||
|
onClicked: TimelineManager.toggleMicMute()
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
implicitWidth: 16
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Item {
|
|
||||||
implicitWidth: 16
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,69 +1,75 @@
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
import QtQuick 2.6
|
import QtQuick 2.6
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import QtGraphicalEffects 1.0
|
|
||||||
|
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: avatar
|
id: avatar
|
||||||
width: 48
|
|
||||||
height: 48
|
|
||||||
radius: Settings.avatarCircles ? height/2 : 3
|
|
||||||
|
|
||||||
property alias url: img.source
|
property alias url: img.source
|
||||||
property string userid
|
property string userid
|
||||||
property string displayName
|
property string displayName
|
||||||
|
|
||||||
Label {
|
width: 48
|
||||||
anchors.fill: parent
|
height: 48
|
||||||
text: TimelineManager.escapeEmoji(displayName ? String.fromCodePoint(displayName.codePointAt(0)) : "")
|
radius: Settings.avatarCircles ? height / 2 : 3
|
||||||
textFormat: Text.RichText
|
color: colors.base
|
||||||
font.pixelSize: avatar.height/2
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
visible: img.status != Image.Ready
|
|
||||||
color: colors.text
|
|
||||||
}
|
|
||||||
|
|
||||||
Image {
|
Label {
|
||||||
id: img
|
anchors.fill: parent
|
||||||
anchors.fill: parent
|
text: TimelineManager.escapeEmoji(displayName ? String.fromCodePoint(displayName.codePointAt(0)) : "")
|
||||||
asynchronous: true
|
textFormat: Text.RichText
|
||||||
fillMode: Image.PreserveAspectCrop
|
font.pixelSize: avatar.height / 2
|
||||||
mipmap: true
|
verticalAlignment: Text.AlignVCenter
|
||||||
smooth: false
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
visible: img.status != Image.Ready
|
||||||
|
color: colors.text
|
||||||
|
}
|
||||||
|
|
||||||
sourceSize.width: avatar.width
|
Image {
|
||||||
sourceSize.height: avatar.height
|
id: img
|
||||||
|
|
||||||
layer.enabled: true
|
anchors.fill: parent
|
||||||
layer.effect: OpacityMask {
|
asynchronous: true
|
||||||
maskSource: Rectangle {
|
fillMode: Image.PreserveAspectCrop
|
||||||
anchors.fill: parent
|
mipmap: true
|
||||||
width: avatar.width
|
smooth: false
|
||||||
height: avatar.height
|
sourceSize.width: avatar.width
|
||||||
radius: Settings.avatarCircles ? height/2 : 3
|
sourceSize.height: avatar.height
|
||||||
}
|
layer.enabled: true
|
||||||
}
|
|
||||||
|
|
||||||
}
|
layer.effect: OpacityMask {
|
||||||
|
|
||||||
Rectangle {
|
maskSource: Rectangle {
|
||||||
anchors.bottom: avatar.bottom
|
anchors.fill: parent
|
||||||
anchors.right: avatar.right
|
width: avatar.width
|
||||||
|
height: avatar.height
|
||||||
|
radius: Settings.avatarCircles ? height / 2 : 3
|
||||||
|
}
|
||||||
|
|
||||||
visible: !!userid
|
}
|
||||||
|
|
||||||
height: avatar.height / 6
|
}
|
||||||
width: height
|
|
||||||
radius: Settings.avatarCircles ? height / 2 : height / 4
|
Rectangle {
|
||||||
color: switch (TimelineManager.userPresence(userid)) {
|
anchors.bottom: avatar.bottom
|
||||||
case "online": return "#00cc66"
|
anchors.right: avatar.right
|
||||||
case "unavailable": return "#ff9933"
|
visible: !!userid
|
||||||
case "offline": // return "#a82353" don't show anything if offline, since it is confusing, if presence is disabled
|
height: avatar.height / 6
|
||||||
default: "transparent"
|
width: height
|
||||||
}
|
radius: Settings.avatarCircles ? height / 2 : height / 4
|
||||||
}
|
color: {
|
||||||
|
switch (TimelineManager.userPresence(userid)) {
|
||||||
|
case "online":
|
||||||
|
return "#00cc66";
|
||||||
|
case "unavailable":
|
||||||
|
return "#ff9933";
|
||||||
|
case "offline":
|
||||||
|
default:
|
||||||
|
// return "#a82353" don't show anything if offline, since it is confusing, if presence is disabled
|
||||||
|
"transparent";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
color: colors.base
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,39 +3,42 @@ import QtQuick.Controls 2.1
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
property bool encrypted: false
|
id: indicator
|
||||||
id: indicator
|
|
||||||
color: "transparent"
|
|
||||||
width: 16
|
|
||||||
height: 16
|
|
||||||
|
|
||||||
ToolTip.visible: ma.containsMouse && indicator.visible
|
property bool encrypted: false
|
||||||
ToolTip.text: getEncryptionTooltip()
|
|
||||||
|
|
||||||
MouseArea{
|
function getEncryptionImage() {
|
||||||
id: ma
|
if (encrypted)
|
||||||
anchors.fill: parent
|
return "image://colorimage/:/icons/icons/ui/lock.png?" + colors.buttonText;
|
||||||
hoverEnabled: true
|
else
|
||||||
}
|
return "image://colorimage/:/icons/icons/ui/unlock.png?#dd3d3d";
|
||||||
|
}
|
||||||
|
|
||||||
Image {
|
function getEncryptionTooltip() {
|
||||||
id: stateImg
|
if (encrypted)
|
||||||
anchors.fill: parent
|
return qsTr("Encrypted");
|
||||||
source: getEncryptionImage()
|
else
|
||||||
}
|
return qsTr("This message is not encrypted!");
|
||||||
|
}
|
||||||
|
|
||||||
function getEncryptionImage() {
|
color: "transparent"
|
||||||
if (encrypted)
|
width: 16
|
||||||
return "image://colorimage/:/icons/icons/ui/lock.png?"+colors.buttonText
|
height: 16
|
||||||
else
|
ToolTip.visible: ma.containsMouse && indicator.visible
|
||||||
return "image://colorimage/:/icons/icons/ui/unlock.png?#dd3d3d"
|
ToolTip.text: getEncryptionTooltip()
|
||||||
}
|
|
||||||
|
MouseArea {
|
||||||
|
id: ma
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: stateImg
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
source: getEncryptionImage()
|
||||||
|
}
|
||||||
|
|
||||||
function getEncryptionTooltip() {
|
|
||||||
if (encrypted)
|
|
||||||
return qsTr("Encrypted")
|
|
||||||
else
|
|
||||||
return qsTr("This message is not encrypted!")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,25 +2,29 @@ import QtQuick 2.3
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
|
|
||||||
AbstractButton {
|
AbstractButton {
|
||||||
property string image: undefined
|
id: button
|
||||||
property color highlightColor: colors.highlight
|
|
||||||
property color buttonTextColor: colors.buttonText
|
|
||||||
width: 16
|
|
||||||
height: 16
|
|
||||||
id: button
|
|
||||||
|
|
||||||
Image {
|
property string image: undefined
|
||||||
id: buttonImg
|
property color highlightColor: colors.highlight
|
||||||
// Workaround, can't get icon.source working for now...
|
property color buttonTextColor: colors.buttonText
|
||||||
anchors.fill: parent
|
|
||||||
source: "image://colorimage/" + image + "?" + (button.hovered ? highlightColor : buttonTextColor)
|
width: 16
|
||||||
}
|
height: 16
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: buttonImg
|
||||||
|
|
||||||
|
// Workaround, can't get icon.source working for now...
|
||||||
|
anchors.fill: parent
|
||||||
|
source: "image://colorimage/" + image + "?" + (button.hovered ? highlightColor : buttonTextColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
onPressed: mouse.accepted = false
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
}
|
||||||
|
|
||||||
MouseArea
|
|
||||||
{
|
|
||||||
id: mouseArea
|
|
||||||
anchors.fill: parent
|
|
||||||
onPressed: mouse.accepted = false
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,35 +1,37 @@
|
||||||
import QtQuick 2.5
|
import QtQuick 2.5
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
|
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
|
||||||
TextEdit {
|
TextEdit {
|
||||||
textFormat: TextEdit.RichText
|
textFormat: TextEdit.RichText
|
||||||
readOnly: true
|
readOnly: true
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
selectByMouse: true
|
selectByMouse: true
|
||||||
activeFocusOnPress: false
|
activeFocusOnPress: false
|
||||||
color: colors.text
|
color: colors.text
|
||||||
|
onLinkActivated: {
|
||||||
|
if (/^https:\/\/matrix.to\/#\/(@.*)$/.test(link)) {
|
||||||
|
chat.model.openUserProfile(/^https:\/\/matrix.to\/#\/(@.*)$/.exec(link)[1]);
|
||||||
|
} else if (/^https:\/\/matrix.to\/#\/(![^\/]*)$/.test(link)) {
|
||||||
|
TimelineManager.setHistoryView(/^https:\/\/matrix.to\/#\/(!.*)$/.exec(link)[1]);
|
||||||
|
} else if (/^https:\/\/matrix.to\/#\/(![^\/]*)\/(\$.*)$/.test(link)) {
|
||||||
|
var match = /^https:\/\/matrix.to\/#\/(![^\/]*)\/(\$.*)$/.exec(link);
|
||||||
|
TimelineManager.setHistoryView(match[1]);
|
||||||
|
chat.positionViewAtIndex(chat.model.idToIndex(match[2]), ListView.Contain);
|
||||||
|
} else {
|
||||||
|
TimelineManager.openLink(link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ToolTip.visible: hoveredLink
|
||||||
|
ToolTip.text: hoveredLink
|
||||||
|
|
||||||
onLinkActivated: {
|
MouseArea {
|
||||||
if (/^https:\/\/matrix.to\/#\/(@.*)$/.test(link)) chat.model.openUserProfile(/^https:\/\/matrix.to\/#\/(@.*)$/.exec(link)[1])
|
id: ma
|
||||||
else if (/^https:\/\/matrix.to\/#\/(![^\/]*)$/.test(link)) TimelineManager.setHistoryView(/^https:\/\/matrix.to\/#\/(!.*)$/.exec(link)[1])
|
|
||||||
else if (/^https:\/\/matrix.to\/#\/(![^\/]*)\/(\$.*)$/.test(link)) {
|
anchors.fill: parent
|
||||||
var match = /^https:\/\/matrix.to\/#\/(![^\/]*)\/(\$.*)$/.exec(link)
|
propagateComposedEvents: true
|
||||||
TimelineManager.setHistoryView(match[1])
|
acceptedButtons: Qt.NoButton
|
||||||
chat.positionViewAtIndex(chat.model.idToIndex(match[2]), ListView.Contain)
|
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
}
|
}
|
||||||
else TimelineManager.openLink(link)
|
|
||||||
}
|
|
||||||
MouseArea
|
|
||||||
{
|
|
||||||
id: ma
|
|
||||||
anchors.fill: parent
|
|
||||||
propagateComposedEvents: true
|
|
||||||
acceptedButtons: Qt.NoButton
|
|
||||||
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
|
||||||
}
|
|
||||||
|
|
||||||
ToolTip.visible: hoveredLink
|
|
||||||
ToolTip.text: hoveredLink
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,94 +1,95 @@
|
||||||
import QtQuick 2.6
|
import QtQuick 2.6
|
||||||
import QtQuick.Controls 2.2
|
import QtQuick.Controls 2.2
|
||||||
|
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
|
||||||
// This class is for showing Reactions in the timeline row, not for
|
// This class is for showing Reactions in the timeline row, not for
|
||||||
// adding new reactions via the emoji picker
|
// adding new reactions via the emoji picker
|
||||||
Flow {
|
Flow {
|
||||||
id: reactionFlow
|
id: reactionFlow
|
||||||
|
|
||||||
// highlight colors for selfReactedEvent background
|
// highlight colors for selfReactedEvent background
|
||||||
property real highlightHue: colors.highlight.hslHue
|
property real highlightHue: colors.highlight.hslHue
|
||||||
property real highlightSat: colors.highlight.hslSaturation
|
property real highlightSat: colors.highlight.hslSaturation
|
||||||
property real highlightLight: colors.highlight.hslLightness
|
property real highlightLight: colors.highlight.hslLightness
|
||||||
|
property string eventId
|
||||||
|
property alias reactions: repeater.model
|
||||||
|
|
||||||
property string eventId
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
spacing: 4
|
||||||
|
|
||||||
anchors.left: parent.left
|
Repeater {
|
||||||
anchors.right: parent.right
|
id: repeater
|
||||||
spacing: 4
|
|
||||||
|
|
||||||
property alias reactions: repeater.model
|
delegate: AbstractButton {
|
||||||
|
id: reaction
|
||||||
|
|
||||||
Repeater {
|
hoverEnabled: true
|
||||||
id: repeater
|
implicitWidth: contentItem.childrenRect.width + contentItem.leftPadding * 2
|
||||||
|
implicitHeight: contentItem.childrenRect.height
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
ToolTip.text: modelData.users
|
||||||
|
onClicked: {
|
||||||
|
console.debug("Picked " + modelData.key + "in response to " + reactionFlow.eventId + ". selfReactedEvent: " + modelData.selfReactedEvent);
|
||||||
|
TimelineManager.queueReactionMessage(reactionFlow.eventId, modelData.key);
|
||||||
|
}
|
||||||
|
|
||||||
delegate: AbstractButton {
|
contentItem: Row {
|
||||||
id: reaction
|
anchors.centerIn: parent
|
||||||
hoverEnabled: true
|
spacing: reactionText.implicitHeight / 4
|
||||||
implicitWidth: contentItem.childrenRect.width + contentItem.leftPadding*2
|
leftPadding: reactionText.implicitHeight / 2
|
||||||
implicitHeight: contentItem.childrenRect.height
|
rightPadding: reactionText.implicitHeight / 2
|
||||||
|
|
||||||
ToolTip.visible: hovered
|
TextMetrics {
|
||||||
ToolTip.text: modelData.users
|
id: textMetrics
|
||||||
|
|
||||||
onClicked: {
|
font.family: Settings.emojiFont
|
||||||
console.debug("Picked " + modelData.key + "in response to " + reactionFlow.eventId + ". selfReactedEvent: " + modelData.selfReactedEvent)
|
elide: Text.ElideRight
|
||||||
TimelineManager.queueReactionMessage(reactionFlow.eventId, modelData.key)
|
elideWidth: 150
|
||||||
}
|
text: modelData.key
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: reactionText
|
||||||
|
|
||||||
contentItem: Row {
|
anchors.baseline: reactionCounter.baseline
|
||||||
anchors.centerIn: parent
|
text: textMetrics.elidedText + (textMetrics.elidedText == modelData.key ? "" : "…")
|
||||||
spacing: reactionText.implicitHeight/4
|
font.family: Settings.emojiFont
|
||||||
leftPadding: reactionText.implicitHeight / 2
|
color: reaction.hovered ? colors.highlight : colors.text
|
||||||
rightPadding: reactionText.implicitHeight / 2
|
maximumLineCount: 1
|
||||||
|
}
|
||||||
|
|
||||||
TextMetrics {
|
Rectangle {
|
||||||
id: textMetrics
|
id: divider
|
||||||
font.family: Settings.emojiFont
|
|
||||||
elide: Text.ElideRight
|
|
||||||
elideWidth: 150
|
|
||||||
text: modelData.key
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
height: Math.floor(reactionCounter.implicitHeight * 1.4)
|
||||||
anchors.baseline: reactionCounter.baseline
|
width: 1
|
||||||
id: reactionText
|
color: (reaction.hovered || modelData.selfReactedEvent !== '') ? colors.highlight : colors.text
|
||||||
text: textMetrics.elidedText + (textMetrics.elidedText == modelData.key ? "" : "…")
|
}
|
||||||
font.family: Settings.emojiFont
|
|
||||||
color: reaction.hovered ? colors.highlight : colors.text
|
|
||||||
maximumLineCount: 1
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
Text {
|
||||||
id: divider
|
id: reactionCounter
|
||||||
height: Math.floor(reactionCounter.implicitHeight * 1.4)
|
|
||||||
width: 1
|
|
||||||
color: (reaction.hovered || modelData.selfReactedEvent !== '') ? colors.highlight : colors.text
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
anchors.verticalCenter: divider.verticalCenter
|
||||||
anchors.verticalCenter: divider.verticalCenter
|
text: modelData.count
|
||||||
id: reactionCounter
|
font: reaction.font
|
||||||
text: modelData.count
|
color: reaction.hovered ? colors.highlight : colors.text
|
||||||
font: reaction.font
|
}
|
||||||
color: reaction.hovered ? colors.highlight : colors.text
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
background: Rectangle {
|
}
|
||||||
anchors.centerIn: parent
|
|
||||||
|
background: Rectangle {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
implicitWidth: reaction.implicitWidth
|
||||||
|
implicitHeight: reaction.implicitHeight
|
||||||
|
border.color: (reaction.hovered || modelData.selfReactedEvent !== '') ? colors.highlight : colors.text
|
||||||
|
color: modelData.selfReactedEvent !== '' ? Qt.hsla(highlightHue, highlightSat, highlightLight, 0.2) : colors.base
|
||||||
|
border.width: 1
|
||||||
|
radius: reaction.height / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
implicitWidth: reaction.implicitWidth
|
|
||||||
implicitHeight: reaction.implicitHeight
|
|
||||||
border.color: (reaction.hovered || modelData.selfReactedEvent !== '') ? colors.highlight : colors.text
|
|
||||||
color: modelData.selfReactedEvent !== '' ? Qt.hsla(highlightHue, highlightSat, highlightLight, 0.20) : colors.base
|
|
||||||
border.width: 1
|
|
||||||
radius: reaction.height / 2.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,10 +16,6 @@
|
||||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import QtQuick 2.9
|
|
||||||
import QtQuick.Controls 2.3
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Shamelessly stolen from:
|
* Shamelessly stolen from:
|
||||||
* https://cgit.kde.org/kube.git/tree/framework/qml/ScrollHelper.qml
|
* https://cgit.kde.org/kube.git/tree/framework/qml/ScrollHelper.qml
|
||||||
|
@ -31,81 +27,82 @@ import QtQuick.Controls 2.3
|
||||||
* ScrollView.qml in qtquickcontrols
|
* ScrollView.qml in qtquickcontrols
|
||||||
* qquickwheelarea.cpp in qtquickcontrols
|
* qquickwheelarea.cpp in qtquickcontrols
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.9
|
||||||
|
import QtQuick.Controls 2.3
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
|
// console.warn("Delta: ", wheel.pixelDelta.y);
|
||||||
|
// console.warn("Old position: ", flickable.contentY);
|
||||||
|
// console.warn("New position: ", newPos);
|
||||||
|
|
||||||
id: root
|
id: root
|
||||||
propagateComposedEvents: true
|
|
||||||
|
|
||||||
property Flickable flickable
|
property Flickable flickable
|
||||||
property alias enabled: root.enabled
|
property alias enabled: root.enabled
|
||||||
|
|
||||||
//Place the mouse area under the flickable
|
|
||||||
z: -1
|
|
||||||
onFlickableChanged: {
|
|
||||||
if (enabled) {
|
|
||||||
flickable.maximumFlickVelocity = 100000
|
|
||||||
flickable.boundsBehavior = Flickable.StopAtBounds
|
|
||||||
root.parent = flickable
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
acceptedButtons: Qt.NoButton
|
|
||||||
|
|
||||||
function calculateNewPosition(flickableItem, wheel) {
|
function calculateNewPosition(flickableItem, wheel) {
|
||||||
//Nothing to scroll
|
//Nothing to scroll
|
||||||
if (flickableItem.contentHeight < flickableItem.height) {
|
if (flickableItem.contentHeight < flickableItem.height)
|
||||||
return flickableItem.contentY;
|
return flickableItem.contentY;
|
||||||
}
|
|
||||||
//Ignore 0 events (happens at least with Christians trackpad)
|
//Ignore 0 events (happens at least with Christians trackpad)
|
||||||
if (wheel.pixelDelta.y == 0 && wheel.angleDelta.y == 0) {
|
if (wheel.pixelDelta.y == 0 && wheel.angleDelta.y == 0)
|
||||||
return flickableItem.contentY;
|
return flickableItem.contentY;
|
||||||
}
|
|
||||||
//pixelDelta seems to be the same as angleDelta/8
|
//pixelDelta seems to be the same as angleDelta/8
|
||||||
var pixelDelta = 0
|
var pixelDelta = 0;
|
||||||
//The pixelDelta is a smaller number if both are provided, so pixelDelta can be 0 while angleDelta is still something. So we check the angleDelta
|
//The pixelDelta is a smaller number if both are provided, so pixelDelta can be 0 while angleDelta is still something. So we check the angleDelta
|
||||||
if (wheel.angleDelta.y) {
|
if (wheel.angleDelta.y) {
|
||||||
var wheelScrollLines = 3 //Default value of QApplication wheelScrollLines property
|
var wheelScrollLines = 3; //Default value of QApplication wheelScrollLines property
|
||||||
var pixelPerLine = 20 //Default value in Qt, originally comes from QTextEdit
|
var pixelPerLine = 20; //Default value in Qt, originally comes from QTextEdit
|
||||||
var ticks = (wheel.angleDelta.y / 8) / 15.0 //Divide by 8 gives us pixels typically come in 15pixel steps.
|
var ticks = (wheel.angleDelta.y / 8) / 15; //Divide by 8 gives us pixels typically come in 15pixel steps.
|
||||||
pixelDelta = ticks * pixelPerLine * wheelScrollLines
|
pixelDelta = ticks * pixelPerLine * wheelScrollLines;
|
||||||
} else {
|
} else {
|
||||||
pixelDelta = wheel.pixelDelta.y
|
pixelDelta = wheel.pixelDelta.y;
|
||||||
}
|
}
|
||||||
|
pixelDelta = Math.round(pixelDelta);
|
||||||
pixelDelta = Math.round(pixelDelta)
|
if (!pixelDelta)
|
||||||
|
|
||||||
if (!pixelDelta) {
|
|
||||||
return flickableItem.contentY;
|
return flickableItem.contentY;
|
||||||
}
|
|
||||||
|
|
||||||
var minYExtent = flickableItem.originY + flickableItem.topMargin;
|
var minYExtent = flickableItem.originY + flickableItem.topMargin;
|
||||||
var maxYExtent = (flickableItem.contentHeight + flickableItem.bottomMargin + flickableItem.originY) - flickableItem.height;
|
var maxYExtent = (flickableItem.contentHeight + flickableItem.bottomMargin + flickableItem.originY) - flickableItem.height;
|
||||||
|
if (typeof (flickableItem.headerItem) !== "undefined" && flickableItem.headerItem)
|
||||||
if (typeof(flickableItem.headerItem) !== "undefined" && flickableItem.headerItem) {
|
minYExtent += flickableItem.headerItem.height;
|
||||||
minYExtent += flickableItem.headerItem.height
|
|
||||||
}
|
|
||||||
|
|
||||||
//Avoid overscrolling
|
//Avoid overscrolling
|
||||||
return Math.max(minYExtent, Math.min(maxYExtent, flickableItem.contentY - pixelDelta));
|
return Math.max(minYExtent, Math.min(maxYExtent, flickableItem.contentY - pixelDelta));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
propagateComposedEvents: true
|
||||||
|
//Place the mouse area under the flickable
|
||||||
|
z: -1
|
||||||
|
onFlickableChanged: {
|
||||||
|
if (enabled) {
|
||||||
|
flickable.maximumFlickVelocity = 100000;
|
||||||
|
flickable.boundsBehavior = Flickable.StopAtBounds;
|
||||||
|
root.parent = flickable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
acceptedButtons: Qt.NoButton
|
||||||
onWheel: {
|
onWheel: {
|
||||||
var newPos = calculateNewPosition(flickable, wheel);
|
var newPos = calculateNewPosition(flickable, wheel);
|
||||||
// console.warn("Delta: ", wheel.pixelDelta.y);
|
|
||||||
// console.warn("Old position: ", flickable.contentY);
|
|
||||||
// console.warn("New position: ", newPos);
|
|
||||||
|
|
||||||
// Show the scrollbars
|
// Show the scrollbars
|
||||||
flickable.flick(0, 0);
|
flickable.flick(0, 0);
|
||||||
flickable.contentY = newPos;
|
flickable.contentY = newPos;
|
||||||
cancelFlickStateTimer.start()
|
cancelFlickStateTimer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: cancelFlickStateTimer
|
id: cancelFlickStateTimer
|
||||||
|
|
||||||
//How long the scrollbar will remain visible
|
//How long the scrollbar will remain visible
|
||||||
interval: 500
|
interval: 500
|
||||||
// Hide the scrollbars
|
// Hide the scrollbars
|
||||||
onTriggered: { flickable.cancelFlick(); flickable.movementEnded(); }
|
onTriggered: {
|
||||||
|
flickable.cancelFlick();
|
||||||
|
flickable.movementEnded();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,37 +3,55 @@ import QtQuick.Controls 2.1
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: indicator
|
id: indicator
|
||||||
property int state: 0
|
|
||||||
color: "transparent"
|
|
||||||
width: 16
|
|
||||||
height: 16
|
|
||||||
|
|
||||||
ToolTip.visible: ma.containsMouse && state != MtxEvent.Empty
|
property int state: 0
|
||||||
ToolTip.text: switch (state) {
|
|
||||||
case MtxEvent.Failed: return qsTr("Failed")
|
color: "transparent"
|
||||||
case MtxEvent.Sent: return qsTr("Sent")
|
width: 16
|
||||||
case MtxEvent.Received: return qsTr("Received")
|
height: 16
|
||||||
case MtxEvent.Read: return qsTr("Read")
|
ToolTip.visible: ma.containsMouse && state != MtxEvent.Empty
|
||||||
default: return ""
|
ToolTip.text: {
|
||||||
}
|
switch (state) {
|
||||||
MouseArea{
|
case MtxEvent.Failed:
|
||||||
id: ma
|
return qsTr("Failed");
|
||||||
anchors.fill: parent
|
case MtxEvent.Sent:
|
||||||
hoverEnabled: true
|
return qsTr("Sent");
|
||||||
}
|
case MtxEvent.Received:
|
||||||
|
return qsTr("Received");
|
||||||
|
case MtxEvent.Read:
|
||||||
|
return qsTr("Read");
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: ma
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: stateImg
|
||||||
|
|
||||||
|
// Workaround, can't get icon.source working for now...
|
||||||
|
anchors.fill: parent
|
||||||
|
source: {
|
||||||
|
switch (indicator.state) {
|
||||||
|
case MtxEvent.Failed:
|
||||||
|
return "image://colorimage/:/icons/icons/ui/remove-symbol.png?" + colors.buttonText;
|
||||||
|
case MtxEvent.Sent:
|
||||||
|
return "image://colorimage/:/icons/icons/ui/clock.png?" + colors.buttonText;
|
||||||
|
case MtxEvent.Received:
|
||||||
|
return "image://colorimage/:/icons/icons/ui/checkmark.png?" + colors.buttonText;
|
||||||
|
case MtxEvent.Read:
|
||||||
|
return "image://colorimage/:/icons/icons/ui/double-tick-indicator.png?" + colors.buttonText;
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Image {
|
|
||||||
id: stateImg
|
|
||||||
// Workaround, can't get icon.source working for now...
|
|
||||||
anchors.fill: parent
|
|
||||||
source: switch (indicator.state) {
|
|
||||||
case MtxEvent.Failed: return "image://colorimage/:/icons/icons/ui/remove-symbol.png?" + colors.buttonText
|
|
||||||
case MtxEvent.Sent: return "image://colorimage/:/icons/icons/ui/clock.png?" + colors.buttonText
|
|
||||||
case MtxEvent.Received: return "image://colorimage/:/icons/icons/ui/checkmark.png?" + colors.buttonText
|
|
||||||
case MtxEvent.Read: return "image://colorimage/:/icons/icons/ui/double-tick-indicator.png?" + colors.buttonText
|
|
||||||
default: return ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,146 +1,148 @@
|
||||||
|
import "./delegates"
|
||||||
|
import "./emoji"
|
||||||
import QtQuick 2.6
|
import QtQuick 2.6
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
import QtQuick.Window 2.2
|
import QtQuick.Window 2.2
|
||||||
|
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
|
||||||
import "./delegates"
|
|
||||||
import "./emoji"
|
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
height: row.height
|
height: row.height
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
propagateComposedEvents: true
|
propagateComposedEvents: true
|
||||||
preventStealing: true
|
preventStealing: true
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
|
acceptedButtons: Qt.AllButtons
|
||||||
|
onClicked: {
|
||||||
|
if (mouse.button === Qt.RightButton)
|
||||||
|
messageContextMenu.show(model.id, model.type, model.isEncrypted, row);
|
||||||
|
|
||||||
acceptedButtons: Qt.AllButtons
|
}
|
||||||
onClicked: {
|
onPressAndHold: {
|
||||||
if (mouse.button === Qt.RightButton)
|
messageContextMenu.show(model.id, model.type, model.isEncrypted, row, mapToItem(timelineRoot, mouse.x, mouse.y));
|
||||||
messageContextMenu.show(model.id, model.type, model.isEncrypted, row)
|
}
|
||||||
}
|
}
|
||||||
onPressAndHold: {
|
|
||||||
messageContextMenu.show(model.id, model.type, model.isEncrypted, row, mapToItem(timelineRoot, mouse.x, mouse.y))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Rectangle {
|
|
||||||
color: (Settings.messageHoverHighlight && parent.containsMouse) ? colors.base : "transparent"
|
|
||||||
anchors.fill: row
|
|
||||||
}
|
|
||||||
RowLayout {
|
|
||||||
id: row
|
|
||||||
|
|
||||||
anchors.leftMargin: avatarSize + 16
|
Rectangle {
|
||||||
anchors.left: parent.left
|
color: (Settings.messageHoverHighlight && parent.containsMouse) ? colors.base : "transparent"
|
||||||
anchors.right: parent.right
|
anchors.fill: row
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: row
|
||||||
|
|
||||||
Column {
|
anchors.leftMargin: avatarSize + 16
|
||||||
Layout.fillWidth: true
|
anchors.left: parent.left
|
||||||
Layout.alignment: Qt.AlignTop
|
anchors.right: parent.right
|
||||||
spacing: 4
|
|
||||||
|
|
||||||
// fancy reply, if this is a reply
|
Column {
|
||||||
Reply {
|
Layout.fillWidth: true
|
||||||
visible: model.replyTo
|
Layout.alignment: Qt.AlignTop
|
||||||
modelData: chat.model.getDump(model.replyTo,model.id)
|
spacing: 4
|
||||||
userColor: TimelineManager.userColor(modelData.userId, colors.window)
|
|
||||||
}
|
|
||||||
|
|
||||||
// actual message content
|
// fancy reply, if this is a reply
|
||||||
MessageDelegate {
|
Reply {
|
||||||
id: contentItem
|
visible: model.replyTo
|
||||||
|
modelData: chat.model.getDump(model.replyTo, model.id)
|
||||||
|
userColor: TimelineManager.userColor(modelData.userId, colors.window)
|
||||||
|
}
|
||||||
|
|
||||||
width: parent.width
|
// actual message content
|
||||||
|
MessageDelegate {
|
||||||
|
id: contentItem
|
||||||
|
|
||||||
modelData: model
|
width: parent.width
|
||||||
}
|
modelData: model
|
||||||
|
}
|
||||||
|
|
||||||
Reactions {
|
Reactions {
|
||||||
id: reactionRow
|
id: reactionRow
|
||||||
reactions: model.reactions
|
|
||||||
eventId: model.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusIndicator {
|
reactions: model.reactions
|
||||||
state: model.state
|
eventId: model.id
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
}
|
||||||
Layout.preferredHeight: 16
|
|
||||||
width: 16
|
|
||||||
}
|
|
||||||
|
|
||||||
EncryptionIndicator {
|
}
|
||||||
visible: model.isRoomEncrypted
|
|
||||||
encrypted: model.isEncrypted
|
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
|
||||||
Layout.preferredHeight: 16
|
|
||||||
width: 16
|
|
||||||
}
|
|
||||||
EmojiButton {
|
|
||||||
visible: Settings.buttonsInTimeline
|
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
|
||||||
Layout.preferredHeight: 16
|
|
||||||
width: 16
|
|
||||||
id: reactButton
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("React")
|
|
||||||
emojiPicker: emojiPopup
|
|
||||||
event_id: model.id
|
|
||||||
}
|
|
||||||
ImageButton {
|
|
||||||
visible: Settings.buttonsInTimeline
|
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
|
||||||
Layout.preferredHeight: 16
|
|
||||||
width: 16
|
|
||||||
id: replyButton
|
|
||||||
hoverEnabled: true
|
|
||||||
|
|
||||||
|
StatusIndicator {
|
||||||
|
state: model.state
|
||||||
|
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||||
|
Layout.preferredHeight: 16
|
||||||
|
width: 16
|
||||||
|
}
|
||||||
|
|
||||||
image: ":/icons/icons/ui/mail-reply.png"
|
EncryptionIndicator {
|
||||||
|
visible: model.isRoomEncrypted
|
||||||
|
encrypted: model.isEncrypted
|
||||||
|
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||||
|
Layout.preferredHeight: 16
|
||||||
|
width: 16
|
||||||
|
}
|
||||||
|
|
||||||
ToolTip.visible: hovered
|
EmojiButton {
|
||||||
ToolTip.text: qsTr("Reply")
|
id: reactButton
|
||||||
|
|
||||||
onClicked: chat.model.replyAction(model.id)
|
visible: Settings.buttonsInTimeline
|
||||||
}
|
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||||
ImageButton {
|
Layout.preferredHeight: 16
|
||||||
visible: Settings.buttonsInTimeline
|
width: 16
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
hoverEnabled: true
|
||||||
Layout.preferredHeight: 16
|
ToolTip.visible: hovered
|
||||||
width: 16
|
ToolTip.text: qsTr("React")
|
||||||
id: optionsButton
|
emojiPicker: emojiPopup
|
||||||
hoverEnabled: true
|
event_id: model.id
|
||||||
|
}
|
||||||
|
|
||||||
image: ":/icons/icons/ui/vertical-ellipsis.png"
|
ImageButton {
|
||||||
|
id: replyButton
|
||||||
|
|
||||||
ToolTip.visible: hovered
|
visible: Settings.buttonsInTimeline
|
||||||
ToolTip.text: qsTr("Options")
|
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||||
|
Layout.preferredHeight: 16
|
||||||
|
width: 16
|
||||||
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/mail-reply.png"
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
ToolTip.text: qsTr("Reply")
|
||||||
|
onClicked: chat.model.replyAction(model.id)
|
||||||
|
}
|
||||||
|
|
||||||
onClicked: messageContextMenu.show(model.id, model.type, model.isEncrypted, optionsButton)
|
ImageButton {
|
||||||
}
|
id: optionsButton
|
||||||
|
|
||||||
Label {
|
visible: Settings.buttonsInTimeline
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||||
text: model.timestamp.toLocaleTimeString("HH:mm")
|
Layout.preferredHeight: 16
|
||||||
width: Math.max(implicitWidth, text.length*fontMetrics.maximumCharacterWidth)
|
width: 16
|
||||||
color: inactiveColors.text
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/vertical-ellipsis.png"
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
ToolTip.text: qsTr("Options")
|
||||||
|
onClicked: messageContextMenu.show(model.id, model.type, model.isEncrypted, optionsButton)
|
||||||
|
}
|
||||||
|
|
||||||
MouseArea{
|
Label {
|
||||||
id: ma
|
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||||
anchors.fill: parent
|
text: model.timestamp.toLocaleTimeString("HH:mm")
|
||||||
hoverEnabled: true
|
width: Math.max(implicitWidth, text.length * fontMetrics.maximumCharacterWidth)
|
||||||
propagateComposedEvents: true
|
color: inactiveColors.text
|
||||||
}
|
ToolTip.visible: ma.containsMouse
|
||||||
|
ToolTip.text: Qt.formatDateTime(model.timestamp, Qt.DefaultLocaleLongDate)
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: ma
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
propagateComposedEvents: true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
ToolTip.visible: ma.containsMouse
|
|
||||||
ToolTip.text: Qt.formatDateTime(model.timestamp, Qt.DefaultLocaleLongDate)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,172 +1,175 @@
|
||||||
|
import "./device-verification"
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
import QtQuick.Window 2.3
|
import QtQuick.Window 2.3
|
||||||
|
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
|
||||||
import "./device-verification"
|
ApplicationWindow {
|
||||||
|
id: userProfileDialog
|
||||||
|
|
||||||
ApplicationWindow{
|
property var profile
|
||||||
property var profile
|
|
||||||
|
|
||||||
id: userProfileDialog
|
height: 650
|
||||||
height: 650
|
width: 420
|
||||||
width: 420
|
minimumHeight: 420
|
||||||
minimumHeight: 420
|
palette: colors
|
||||||
|
|
||||||
palette: colors
|
Component {
|
||||||
|
id: deviceVerificationDialog
|
||||||
|
|
||||||
Component {
|
DeviceVerification {
|
||||||
id: deviceVerificationDialog
|
}
|
||||||
DeviceVerification {}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout{
|
}
|
||||||
id: contentL
|
|
||||||
|
|
||||||
anchors.fill: parent
|
ColumnLayout {
|
||||||
anchors.margins: 10
|
id: contentL
|
||||||
|
|
||||||
spacing: 10
|
anchors.fill: parent
|
||||||
|
anchors.margins: 10
|
||||||
|
spacing: 10
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
url: profile.avatarUrl.replace("mxc://", "image://MxcImage/")
|
url: profile.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
height: 130
|
height: 130
|
||||||
width: 130
|
width: 130
|
||||||
displayName: profile.displayName
|
displayName: profile.displayName
|
||||||
userid: profile.userid
|
userid: profile.userid
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: profile.displayName
|
text: profile.displayName
|
||||||
fontSizeMode: Text.HorizontalFit
|
fontSizeMode: Text.HorizontalFit
|
||||||
font.pixelSize: 20
|
font.pixelSize: 20
|
||||||
color: TimelineManager.userColor(profile.userid, colors.window)
|
color: TimelineManager.userColor(profile.userid, colors.window)
|
||||||
font.bold: true
|
font.bold: true
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
text: profile.userid
|
text: profile.userid
|
||||||
font.pixelSize: 15
|
font.pixelSize: 15
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
id: verifyUserButton
|
id: verifyUserButton
|
||||||
text: qsTr("Verify")
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
enabled: !profile.isUserVerified
|
|
||||||
visible: !profile.isUserVerified
|
|
||||||
|
|
||||||
onClicked: profile.verify()
|
text: qsTr("Verify")
|
||||||
}
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
enabled: !profile.isUserVerified
|
||||||
|
visible: !profile.isUserVerified
|
||||||
|
onClicked: profile.verify()
|
||||||
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
spacing: 8
|
spacing: 8
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
image:":/icons/icons/ui/do-not-disturb-rounded-sign.png"
|
image: ":/icons/icons/ui/do-not-disturb-rounded-sign.png"
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
ToolTip.text: qsTr("Ban the user")
|
ToolTip.text: qsTr("Ban the user")
|
||||||
onClicked: profile.banUser()
|
onClicked: profile.banUser()
|
||||||
}
|
}
|
||||||
// ImageButton{
|
// ImageButton{
|
||||||
// image:":/icons/icons/ui/volume-off-indicator.png"
|
|
||||||
// Layout.margins: {
|
|
||||||
// left: 5
|
|
||||||
// right: 5
|
|
||||||
// }
|
|
||||||
// ToolTip.visible: hovered
|
|
||||||
// ToolTip.text: qsTr("Ignore messages from this user")
|
|
||||||
// onClicked : {
|
|
||||||
// profile.ignoreUser()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
ImageButton{
|
|
||||||
image:":/icons/icons/ui/black-bubble-speech.png"
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Start a private chat")
|
|
||||||
onClicked: profile.startChat()
|
|
||||||
}
|
|
||||||
ImageButton{
|
|
||||||
image:":/icons/icons/ui/round-remove-button.png"
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Kick the user")
|
|
||||||
onClicked: profile.kickUser()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ListView{
|
// image:":/icons/icons/ui/volume-off-indicator.png"
|
||||||
id: devicelist
|
// Layout.margins: {
|
||||||
|
// left: 5
|
||||||
|
// right: 5
|
||||||
|
// }
|
||||||
|
// ToolTip.visible: hovered
|
||||||
|
// ToolTip.text: qsTr("Ignore messages from this user")
|
||||||
|
// onClicked : {
|
||||||
|
// profile.ignoreUser()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
ImageButton {
|
||||||
|
image: ":/icons/icons/ui/black-bubble-speech.png"
|
||||||
|
hoverEnabled: true
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
ToolTip.text: qsTr("Start a private chat")
|
||||||
|
onClicked: profile.startChat()
|
||||||
|
}
|
||||||
|
|
||||||
Layout.fillHeight: true
|
ImageButton {
|
||||||
Layout.minimumHeight: 200
|
image: ":/icons/icons/ui/round-remove-button.png"
|
||||||
Layout.fillWidth: true
|
hoverEnabled: true
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
ToolTip.text: qsTr("Kick the user")
|
||||||
|
onClicked: profile.kickUser()
|
||||||
|
}
|
||||||
|
|
||||||
clip: true
|
}
|
||||||
spacing: 8
|
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
|
||||||
|
|
||||||
model: profile.deviceList
|
ListView {
|
||||||
|
id: devicelist
|
||||||
|
|
||||||
delegate: RowLayout{
|
Layout.fillHeight: true
|
||||||
width: devicelist.width
|
Layout.minimumHeight: 200
|
||||||
spacing: 4
|
Layout.fillWidth: true
|
||||||
|
clip: true
|
||||||
|
spacing: 8
|
||||||
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
model: profile.deviceList
|
||||||
|
|
||||||
ColumnLayout{
|
delegate: RowLayout {
|
||||||
spacing: 0
|
width: devicelist.width
|
||||||
Text{
|
spacing: 4
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
|
||||||
|
|
||||||
elide: Text.ElideRight
|
ColumnLayout {
|
||||||
font.bold: true
|
spacing: 0
|
||||||
color: colors.text
|
|
||||||
text: model.deviceId
|
|
||||||
}
|
|
||||||
Text{
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
|
|
||||||
elide: Text.ElideRight
|
Text {
|
||||||
color: colors.text
|
Layout.fillWidth: true
|
||||||
text: model.deviceName
|
Layout.alignment: Qt.AlignLeft
|
||||||
}
|
elide: Text.ElideRight
|
||||||
}
|
font.bold: true
|
||||||
|
color: colors.text
|
||||||
|
text: model.deviceId
|
||||||
|
}
|
||||||
|
|
||||||
Image{
|
Text {
|
||||||
Layout.preferredHeight: 16
|
Layout.fillWidth: true
|
||||||
Layout.preferredWidth: 16
|
Layout.alignment: Qt.AlignRight
|
||||||
|
elide: Text.ElideRight
|
||||||
|
color: colors.text
|
||||||
|
text: model.deviceName
|
||||||
|
}
|
||||||
|
|
||||||
source: ((model.verificationStatus == VerificationStatus.VERIFIED)?"image://colorimage/:/icons/icons/ui/lock.png?green":
|
}
|
||||||
((model.verificationStatus == VerificationStatus.UNVERIFIED)?"image://colorimage/:/icons/icons/ui/unlock.png?yellow":
|
|
||||||
"image://colorimage/:/icons/icons/ui/unlock.png?red"))
|
|
||||||
}
|
|
||||||
Button{
|
|
||||||
id: verifyButton
|
|
||||||
text: (model.verificationStatus != VerificationStatus.VERIFIED)?"Verify":"Unverify"
|
|
||||||
onClicked: {
|
|
||||||
if(model.verificationStatus == VerificationStatus.VERIFIED){
|
|
||||||
profile.unverify(model.deviceId)
|
|
||||||
}else{
|
|
||||||
profile.verify(model.deviceId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
footer: DialogButtonBox {
|
Image {
|
||||||
standardButtons: DialogButtonBox.Ok
|
Layout.preferredHeight: 16
|
||||||
|
Layout.preferredWidth: 16
|
||||||
|
source: ((model.verificationStatus == VerificationStatus.VERIFIED) ? "image://colorimage/:/icons/icons/ui/lock.png?green" : ((model.verificationStatus == VerificationStatus.UNVERIFIED) ? "image://colorimage/:/icons/icons/ui/unlock.png?yellow" : "image://colorimage/:/icons/icons/ui/unlock.png?red"))
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: verifyButton
|
||||||
|
|
||||||
|
text: (model.verificationStatus != VerificationStatus.VERIFIED) ? "Verify" : "Unverify"
|
||||||
|
onClicked: {
|
||||||
|
if (model.verificationStatus == VerificationStatus.VERIFIED)
|
||||||
|
profile.unverify(model.deviceId);
|
||||||
|
else
|
||||||
|
profile.verify(model.deviceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
footer: DialogButtonBox {
|
||||||
|
standardButtons: DialogButtonBox.Ok
|
||||||
|
onAccepted: userProfileDialog.close()
|
||||||
|
}
|
||||||
|
|
||||||
onAccepted: userProfileDialog.close()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,68 +1,75 @@
|
||||||
import QtQuick 2.6
|
import QtQuick 2.6
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
|
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
height: row.height + 24
|
height: row.height + 24
|
||||||
width: parent ? parent.width : undefined
|
width: parent ? parent.width : undefined
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: row
|
id: row
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
width: parent.width - 24
|
width: parent.width - 24
|
||||||
|
spacing: 15
|
||||||
|
|
||||||
spacing: 15
|
Rectangle {
|
||||||
|
id: button
|
||||||
|
|
||||||
Rectangle {
|
color: colors.light
|
||||||
id: button
|
radius: 22
|
||||||
color: colors.light
|
height: 44
|
||||||
radius: 22
|
width: 44
|
||||||
height: 44
|
|
||||||
width: 44
|
|
||||||
Image {
|
|
||||||
id: img
|
|
||||||
anchors.centerIn: parent
|
|
||||||
|
|
||||||
source: "qrc:/icons/icons/ui/arrow-pointing-down.png"
|
Image {
|
||||||
fillMode: Image.Pad
|
id: img
|
||||||
|
|
||||||
}
|
anchors.centerIn: parent
|
||||||
MouseArea {
|
source: "qrc:/icons/icons/ui/arrow-pointing-down.png"
|
||||||
anchors.fill: parent
|
fillMode: Image.Pad
|
||||||
onClicked: TimelineManager.timeline.saveMedia(model.data.id)
|
}
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ColumnLayout {
|
|
||||||
id: col
|
|
||||||
|
|
||||||
Text {
|
MouseArea {
|
||||||
id: filename
|
anchors.fill: parent
|
||||||
Layout.fillWidth: true
|
onClicked: TimelineManager.timeline.saveMedia(model.data.id)
|
||||||
text: model.data.filename
|
cursorShape: Qt.PointingHandCursor
|
||||||
textFormat: Text.PlainText
|
}
|
||||||
elide: Text.ElideRight
|
|
||||||
color: colors.text
|
|
||||||
}
|
|
||||||
Text {
|
|
||||||
id: filesize
|
|
||||||
Layout.fillWidth: true
|
|
||||||
text: model.data.filesize
|
|
||||||
textFormat: Text.PlainText
|
|
||||||
elide: Text.ElideRight
|
|
||||||
color: colors.text
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
}
|
||||||
color: colors.dark
|
|
||||||
z: -1
|
ColumnLayout {
|
||||||
radius: 10
|
id: col
|
||||||
height: row.height + 24
|
|
||||||
width: 44 + 24 + 24 + Math.max(Math.min(filesize.width, filesize.implicitWidth), Math.min(filename.width, filename.implicitWidth))
|
Text {
|
||||||
}
|
id: filename
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: model.data.filename
|
||||||
|
textFormat: Text.PlainText
|
||||||
|
elide: Text.ElideRight
|
||||||
|
color: colors.text
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: filesize
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: model.data.filesize
|
||||||
|
textFormat: Text.PlainText
|
||||||
|
elide: Text.ElideRight
|
||||||
|
color: colors.text
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: colors.dark
|
||||||
|
z: -1
|
||||||
|
radius: 10
|
||||||
|
height: row.height + 24
|
||||||
|
width: 44 + 24 + 24 + Math.max(Math.min(filesize.width, filesize.implicitWidth), Math.min(filename.width, filename.implicitWidth))
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,42 +1,41 @@
|
||||||
import QtQuick 2.6
|
import QtQuick 2.6
|
||||||
|
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
property double tempWidth: Math.min(parent ? parent.width : undefined, model.data.width < 1 ? parent.width : model.data.width)
|
property double tempWidth: Math.min(parent ? parent.width : undefined, model.data.width < 1 ? parent.width : model.data.width)
|
||||||
property double tempHeight: tempWidth * model.data.proportionalHeight
|
property double tempHeight: tempWidth * model.data.proportionalHeight
|
||||||
|
property double divisor: model.isReply ? 4 : 2
|
||||||
|
property bool tooHigh: tempHeight > timelineRoot.height / divisor
|
||||||
|
|
||||||
property double divisor: model.isReply ? 4 : 2
|
height: Math.round(tooHigh ? timelineRoot.height / divisor : tempHeight)
|
||||||
property bool tooHigh: tempHeight > timelineRoot.height / divisor
|
width: Math.round(tooHigh ? (timelineRoot.height / divisor) / model.data.proportionalHeight : tempWidth)
|
||||||
|
|
||||||
height: Math.round(tooHigh ? timelineRoot.height / divisor : tempHeight)
|
Image {
|
||||||
width: Math.round(tooHigh ? (timelineRoot.height / divisor) / model.data.proportionalHeight : tempWidth)
|
id: blurhash
|
||||||
|
|
||||||
Image {
|
anchors.fill: parent
|
||||||
id: blurhash
|
visible: img.status != Image.Ready
|
||||||
anchors.fill: parent
|
source: model.data.blurhash ? ("image://blurhash/" + model.data.blurhash) : ("image://colorimage/:/icons/icons/ui/do-not-disturb-rounded-sign@2x.png?" + colors.buttonText)
|
||||||
visible: img.status != Image.Ready
|
asynchronous: true
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
sourceSize.width: parent.width
|
||||||
|
sourceSize.height: parent.height
|
||||||
|
}
|
||||||
|
|
||||||
source: model.data.blurhash ? ("image://blurhash/" + model.data.blurhash) : ("image://colorimage/:/icons/icons/ui/do-not-disturb-rounded-sign@2x.png?"+colors.buttonText)
|
Image {
|
||||||
asynchronous: true
|
id: img
|
||||||
fillMode: Image.PreserveAspectFit
|
|
||||||
|
|
||||||
sourceSize.width: parent.width
|
anchors.fill: parent
|
||||||
sourceSize.height: parent.height
|
source: model.data.url.replace("mxc://", "image://MxcImage/")
|
||||||
}
|
asynchronous: true
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
|
||||||
Image {
|
MouseArea {
|
||||||
id: img
|
enabled: model.data.type == MtxEvent.ImageMessage && img.status == Image.Ready
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
onClicked: TimelineManager.openImageOverlay(model.data.url, model.data.id)
|
||||||
|
}
|
||||||
|
|
||||||
source: model.data.url.replace("mxc://", "image://MxcImage/")
|
}
|
||||||
asynchronous: true
|
|
||||||
fillMode: Image.PreserveAspectFit
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
enabled: model.data.type == MtxEvent.ImageMessage && img.status == Image.Ready
|
|
||||||
anchors.fill: parent
|
|
||||||
onClicked: TimelineManager.openImageOverlay(model.data.url, model.data.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,215 +2,334 @@ import QtQuick 2.6
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
// Workaround to have an assignable global property
|
property alias modelData: model.data
|
||||||
Item {
|
property alias isReply: model.isReply
|
||||||
id: model
|
property real implicitWidth: (chooser.child && chooser.child.implicitWidth) ? chooser.child.implicitWidth : width
|
||||||
property var data;
|
|
||||||
property bool isReply: false
|
|
||||||
}
|
|
||||||
|
|
||||||
property alias modelData: model.data
|
|
||||||
property alias isReply: model.isReply
|
|
||||||
|
|
||||||
height: chooser.childrenRect.height
|
height: chooser.childrenRect.height
|
||||||
property real implicitWidth: (chooser.child && chooser.child.implicitWidth) ? chooser.child.implicitWidth : width
|
|
||||||
|
|
||||||
DelegateChooser {
|
// Workaround to have an assignable global property
|
||||||
id: chooser
|
Item {
|
||||||
//role: "type" //< not supported in our custom implementation, have to use roleValue
|
id: model
|
||||||
roleValue: model.data.type
|
|
||||||
anchors.fill: parent
|
property var data
|
||||||
|
property bool isReply: false
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChooser {
|
||||||
|
id: chooser
|
||||||
|
|
||||||
|
//role: "type" //< not supported in our custom implementation, have to use roleValue
|
||||||
|
roleValue: model.data.type
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: MtxEvent.UnknownMessage
|
||||||
|
|
||||||
|
Placeholder {
|
||||||
|
text: "Unretrieved event"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: MtxEvent.TextMessage
|
||||||
|
|
||||||
|
TextMessage {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: MtxEvent.NoticeMessage
|
||||||
|
|
||||||
|
NoticeMessage {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: MtxEvent.EmoteMessage
|
||||||
|
|
||||||
|
NoticeMessage {
|
||||||
|
formatted: TimelineManager.escapeEmoji(modelData.userName) + " " + model.data.formattedBody
|
||||||
|
color: TimelineManager.userColor(modelData.userId, colors.window)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: MtxEvent.ImageMessage
|
||||||
|
|
||||||
|
ImageMessage {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: MtxEvent.Sticker
|
||||||
|
|
||||||
|
ImageMessage {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: MtxEvent.FileMessage
|
||||||
|
|
||||||
|
FileMessage {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: MtxEvent.VideoMessage
|
||||||
|
|
||||||
|
PlayableMediaMessage {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: MtxEvent.AudioMessage
|
||||||
|
|
||||||
|
PlayableMediaMessage {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: MtxEvent.Redacted
|
||||||
|
|
||||||
|
Pill {
|
||||||
|
text: qsTr("redacted")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: MtxEvent.Redaction
|
||||||
|
|
||||||
|
Pill {
|
||||||
|
text: qsTr("redacted")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: MtxEvent.Encryption
|
||||||
|
|
||||||
|
Pill {
|
||||||
|
text: qsTr("Encryption enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: MtxEvent.Name
|
||||||
|
|
||||||
|
NoticeMessage {
|
||||||
|
text: model.data.roomName ? qsTr("room name changed to: %1").arg(model.data.roomName) : qsTr("removed room name")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: MtxEvent.Topic
|
||||||
|
|
||||||
|
NoticeMessage {
|
||||||
|
text: model.data.roomTopic ? qsTr("topic changed to: %1").arg(model.data.roomTopic) : qsTr("removed topic")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: MtxEvent.RoomCreate
|
||||||
|
|
||||||
|
NoticeMessage {
|
||||||
|
text: qsTr("%1 created and configured room: %2").arg(model.data.userName).arg(model.data.roomId)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: MtxEvent.CallInvite
|
||||||
|
|
||||||
|
NoticeMessage {
|
||||||
|
text: {
|
||||||
|
switch (model.data.callType) {
|
||||||
|
case "voice":
|
||||||
|
return qsTr("%1 placed a voice call.").arg(model.data.userName);
|
||||||
|
case "video":
|
||||||
|
return qsTr("%1 placed a video call.").arg(model.data.userName);
|
||||||
|
default:
|
||||||
|
return qsTr("%1 placed a call.").arg(model.data.userName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: MtxEvent.CallAnswer
|
||||||
|
|
||||||
|
NoticeMessage {
|
||||||
|
text: qsTr("%1 answered the call.").arg(model.data.userName)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: MtxEvent.CallHangUp
|
||||||
|
|
||||||
|
NoticeMessage {
|
||||||
|
text: qsTr("%1 ended the call.").arg(model.data.userName)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: MtxEvent.CallCandidates
|
||||||
|
|
||||||
|
NoticeMessage {
|
||||||
|
text: qsTr("Negotiating call...")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
// TODO: make a more complex formatter for the power levels.
|
||||||
|
roleValue: MtxEvent.PowerLevels
|
||||||
|
|
||||||
|
NoticeMessage {
|
||||||
|
text: TimelineManager.timeline.formatPowerLevelEvent(model.data.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: MtxEvent.RoomJoinRules
|
||||||
|
|
||||||
|
NoticeMessage {
|
||||||
|
text: TimelineManager.timeline.formatJoinRuleEvent(model.data.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: MtxEvent.RoomHistoryVisibility
|
||||||
|
|
||||||
|
NoticeMessage {
|
||||||
|
text: TimelineManager.timeline.formatHistoryVisibilityEvent(model.data.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: MtxEvent.RoomGuestAccess
|
||||||
|
|
||||||
|
NoticeMessage {
|
||||||
|
text: TimelineManager.timeline.formatGuestAccessEvent(model.data.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: MtxEvent.Member
|
||||||
|
|
||||||
|
NoticeMessage {
|
||||||
|
text: TimelineManager.timeline.formatMemberEvent(model.data.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: MtxEvent.KeyVerificationRequest
|
||||||
|
|
||||||
|
NoticeMessage {
|
||||||
|
text: "KeyVerificationRequest"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: MtxEvent.KeyVerificationStart
|
||||||
|
|
||||||
|
NoticeMessage {
|
||||||
|
text: "KeyVerificationStart"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: MtxEvent.KeyVerificationReady
|
||||||
|
|
||||||
|
NoticeMessage {
|
||||||
|
text: "KeyVerificationReady"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: MtxEvent.KeyVerificationCancel
|
||||||
|
|
||||||
|
NoticeMessage {
|
||||||
|
text: "KeyVerificationCancel"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: MtxEvent.KeyVerificationKey
|
||||||
|
|
||||||
|
NoticeMessage {
|
||||||
|
text: "KeyVerificationKey"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: MtxEvent.KeyVerificationMac
|
||||||
|
|
||||||
|
NoticeMessage {
|
||||||
|
text: "KeyVerificationMac"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: MtxEvent.KeyVerificationDone
|
||||||
|
|
||||||
|
NoticeMessage {
|
||||||
|
text: "KeyVerificationDone"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: MtxEvent.KeyVerificationDone
|
||||||
|
|
||||||
|
NoticeMessage {
|
||||||
|
text: "KeyVerificationDone"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: MtxEvent.KeyVerificationAccept
|
||||||
|
|
||||||
|
NoticeMessage {
|
||||||
|
text: "KeyVerificationAccept"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
Placeholder {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: MtxEvent.UnknownMessage
|
|
||||||
Placeholder { text: "Unretrieved event" }
|
|
||||||
}
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: MtxEvent.TextMessage
|
|
||||||
TextMessage {}
|
|
||||||
}
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: MtxEvent.NoticeMessage
|
|
||||||
NoticeMessage {}
|
|
||||||
}
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: MtxEvent.EmoteMessage
|
|
||||||
NoticeMessage {
|
|
||||||
formatted: TimelineManager.escapeEmoji(modelData.userName) + " " + model.data.formattedBody
|
|
||||||
color: TimelineManager.userColor(modelData.userId, colors.window)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: MtxEvent.ImageMessage
|
|
||||||
ImageMessage {}
|
|
||||||
}
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: MtxEvent.Sticker
|
|
||||||
ImageMessage {}
|
|
||||||
}
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: MtxEvent.FileMessage
|
|
||||||
FileMessage {}
|
|
||||||
}
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: MtxEvent.VideoMessage
|
|
||||||
PlayableMediaMessage {}
|
|
||||||
}
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: MtxEvent.AudioMessage
|
|
||||||
PlayableMediaMessage {}
|
|
||||||
}
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: MtxEvent.Redacted
|
|
||||||
Pill {
|
|
||||||
text: qsTr("redacted")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: MtxEvent.Redaction
|
|
||||||
Pill {
|
|
||||||
text: qsTr("redacted")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: MtxEvent.Encryption
|
|
||||||
Pill {
|
|
||||||
text: qsTr("Encryption enabled")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: MtxEvent.Name
|
|
||||||
NoticeMessage {
|
|
||||||
text: model.data.roomName ? qsTr("room name changed to: %1").arg(model.data.roomName) : qsTr("removed room name")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: MtxEvent.Topic
|
|
||||||
NoticeMessage {
|
|
||||||
text: model.data.roomTopic ? qsTr("topic changed to: %1").arg(model.data.roomTopic) : qsTr("removed topic")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: MtxEvent.RoomCreate
|
|
||||||
NoticeMessage {
|
|
||||||
text: qsTr("%1 created and configured room: %2").arg(model.data.userName).arg(model.data.roomId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: MtxEvent.CallInvite
|
|
||||||
NoticeMessage {
|
|
||||||
text: switch(model.data.callType) {
|
|
||||||
case "voice": return qsTr("%1 placed a voice call.").arg(model.data.userName)
|
|
||||||
case "video": return qsTr("%1 placed a video call.").arg(model.data.userName)
|
|
||||||
default: return qsTr("%1 placed a call.").arg(model.data.userName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: MtxEvent.CallAnswer
|
|
||||||
NoticeMessage {
|
|
||||||
text: qsTr("%1 answered the call.").arg(model.data.userName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: MtxEvent.CallHangUp
|
|
||||||
NoticeMessage {
|
|
||||||
text: qsTr("%1 ended the call.").arg(model.data.userName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: MtxEvent.CallCandidates
|
|
||||||
NoticeMessage {
|
|
||||||
text: qsTr("Negotiating call...")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DelegateChoice {
|
|
||||||
// TODO: make a more complex formatter for the power levels.
|
|
||||||
roleValue: MtxEvent.PowerLevels
|
|
||||||
NoticeMessage {
|
|
||||||
text: TimelineManager.timeline.formatPowerLevelEvent(model.data.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: MtxEvent.RoomJoinRules
|
|
||||||
NoticeMessage {
|
|
||||||
text: TimelineManager.timeline.formatJoinRuleEvent(model.data.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: MtxEvent.RoomHistoryVisibility
|
|
||||||
NoticeMessage {
|
|
||||||
text: TimelineManager.timeline.formatHistoryVisibilityEvent(model.data.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: MtxEvent.RoomGuestAccess
|
|
||||||
NoticeMessage {
|
|
||||||
text: TimelineManager.timeline.formatGuestAccessEvent(model.data.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: MtxEvent.Member
|
|
||||||
NoticeMessage {
|
|
||||||
text: TimelineManager.timeline.formatMemberEvent(model.data.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: MtxEvent.KeyVerificationRequest
|
|
||||||
NoticeMessage {
|
|
||||||
text: "KeyVerificationRequest";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: MtxEvent.KeyVerificationStart
|
|
||||||
NoticeMessage {
|
|
||||||
text: "KeyVerificationStart";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: MtxEvent.KeyVerificationReady
|
|
||||||
NoticeMessage {
|
|
||||||
text: "KeyVerificationReady";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: MtxEvent.KeyVerificationCancel
|
|
||||||
NoticeMessage {
|
|
||||||
text: "KeyVerificationCancel";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: MtxEvent.KeyVerificationKey
|
|
||||||
NoticeMessage {
|
|
||||||
text: "KeyVerificationKey";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: MtxEvent.KeyVerificationMac
|
|
||||||
NoticeMessage {
|
|
||||||
text: "KeyVerificationMac";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: MtxEvent.KeyVerificationDone
|
|
||||||
NoticeMessage {
|
|
||||||
text: "KeyVerificationDone";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: MtxEvent.KeyVerificationDone
|
|
||||||
NoticeMessage {
|
|
||||||
text: "KeyVerificationDone";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: MtxEvent.KeyVerificationAccept
|
|
||||||
NoticeMessage {
|
|
||||||
text: "KeyVerificationAccept";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DelegateChoice {
|
|
||||||
Placeholder {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
TextMessage {
|
TextMessage {
|
||||||
font.italic: true
|
font.italic: true
|
||||||
color: colors.buttonText
|
color: colors.buttonText
|
||||||
height: isReply ? Math.min(chat.height / 8, implicitHeight) : undefined
|
height: isReply ? Math.min(chat.height / 8, implicitHeight) : undefined
|
||||||
clip: true
|
clip: true
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,14 @@ import QtQuick 2.5
|
||||||
import QtQuick.Controls 2.1
|
import QtQuick.Controls 2.1
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
color: colors.brightText
|
color: colors.brightText
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
height: contentHeight * 1.2
|
||||||
|
width: contentWidth * 1.2
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
radius: parent.height / 2
|
||||||
|
color: colors.dark
|
||||||
|
}
|
||||||
|
|
||||||
height: contentHeight * 1.2
|
|
||||||
width: contentWidth * 1.2
|
|
||||||
background: Rectangle {
|
|
||||||
radius: parent.height / 2
|
|
||||||
color: colors.dark
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import ".."
|
import ".."
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
text: qsTr("unimplemented event: ") + model.data.typeString
|
text: qsTr("unimplemented event: ") + model.data.typeString
|
||||||
width: parent ? parent.width : undefined
|
width: parent ? parent.width : undefined
|
||||||
color: inactiveColors.text
|
color: inactiveColors.text
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,173 +1,216 @@
|
||||||
import QtQuick 2.6
|
|
||||||
import QtQuick.Layouts 1.2
|
|
||||||
import QtQuick.Controls 2.1
|
|
||||||
import QtMultimedia 5.6
|
import QtMultimedia 5.6
|
||||||
|
import QtQuick 2.6
|
||||||
|
import QtQuick.Controls 2.1
|
||||||
|
import QtQuick.Layouts 1.2
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: bg
|
id: bg
|
||||||
radius: 10
|
|
||||||
color: colors.dark
|
|
||||||
height: Math.round(content.height + 24)
|
|
||||||
width: parent ? parent.width : undefined
|
|
||||||
|
|
||||||
Column {
|
radius: 10
|
||||||
id: content
|
color: colors.dark
|
||||||
width: parent.width - 24
|
height: Math.round(content.height + 24)
|
||||||
anchors.centerIn: parent
|
width: parent ? parent.width : undefined
|
||||||
|
|
||||||
Rectangle {
|
Column {
|
||||||
id: videoContainer
|
id: content
|
||||||
visible: model.data.type == MtxEvent.VideoMessage
|
|
||||||
property double tempWidth: Math.min(parent ? parent.width : undefined, model.data.width < 1 ? 400 : model.data.width)
|
|
||||||
property double tempHeight: tempWidth * model.data.proportionalHeight
|
|
||||||
|
|
||||||
property double divisor: model.isReply ? 4 : 2
|
width: parent.width - 24
|
||||||
property bool tooHigh: tempHeight > timelineRoot.height / divisor
|
anchors.centerIn: parent
|
||||||
|
|
||||||
height: tooHigh ? timelineRoot.height / divisor : tempHeight
|
Rectangle {
|
||||||
width: tooHigh ? (timelineRoot.height / divisor) / model.data.proportionalHeight : tempWidth
|
id: videoContainer
|
||||||
Image {
|
|
||||||
anchors.fill: parent
|
|
||||||
source: model.data.thumbnailUrl.replace("mxc://", "image://MxcImage/")
|
|
||||||
asynchronous: true
|
|
||||||
fillMode: Image.PreserveAspectFit
|
|
||||||
|
|
||||||
VideoOutput {
|
property double tempWidth: Math.min(parent ? parent.width : undefined, model.data.width < 1 ? 400 : model.data.width)
|
||||||
anchors.fill: parent
|
property double tempHeight: tempWidth * model.data.proportionalHeight
|
||||||
fillMode: VideoOutput.PreserveAspectFit
|
property double divisor: model.isReply ? 4 : 2
|
||||||
source: media
|
property bool tooHigh: tempHeight > timelineRoot.height / divisor
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
visible: model.data.type == MtxEvent.VideoMessage
|
||||||
width: parent.width
|
height: tooHigh ? timelineRoot.height / divisor : tempHeight
|
||||||
Text {
|
width: tooHigh ? (timelineRoot.height / divisor) / model.data.proportionalHeight : tempWidth
|
||||||
id: positionText
|
|
||||||
text: "--:--:--"
|
|
||||||
color: colors.text
|
|
||||||
}
|
|
||||||
Slider {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
id: progress
|
|
||||||
value: media.position
|
|
||||||
from: 0
|
|
||||||
to: media.duration
|
|
||||||
|
|
||||||
onMoved: media.seek(value)
|
Image {
|
||||||
//indeterminate: true
|
anchors.fill: parent
|
||||||
function updatePositionTexts() {
|
source: model.data.thumbnailUrl.replace("mxc://", "image://MxcImage/")
|
||||||
function formatTime(date) {
|
asynchronous: true
|
||||||
var hh = date.getUTCHours();
|
fillMode: Image.PreserveAspectFit
|
||||||
var mm = date.getUTCMinutes();
|
|
||||||
var ss = date.getSeconds();
|
|
||||||
if (hh < 10) {hh = "0"+hh;}
|
|
||||||
if (mm < 10) {mm = "0"+mm;}
|
|
||||||
if (ss < 10) {ss = "0"+ss;}
|
|
||||||
return hh+":"+mm+":"+ss;
|
|
||||||
}
|
|
||||||
positionText.text = formatTime(new Date(media.position))
|
|
||||||
durationText.text = formatTime(new Date(media.duration))
|
|
||||||
}
|
|
||||||
onValueChanged: updatePositionTexts()
|
|
||||||
|
|
||||||
palette: colors
|
VideoOutput {
|
||||||
}
|
anchors.fill: parent
|
||||||
Text {
|
fillMode: VideoOutput.PreserveAspectFit
|
||||||
id: durationText
|
source: media
|
||||||
text: "--:--:--"
|
}
|
||||||
color: colors.text
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
}
|
||||||
width: parent.width
|
|
||||||
|
|
||||||
spacing: 15
|
}
|
||||||
|
|
||||||
Rectangle {
|
RowLayout {
|
||||||
id: button
|
width: parent.width
|
||||||
color: colors.window
|
|
||||||
radius: 22
|
|
||||||
height: 44
|
|
||||||
width: 44
|
|
||||||
Image {
|
|
||||||
id: img
|
|
||||||
anchors.centerIn: parent
|
|
||||||
z: 3
|
|
||||||
|
|
||||||
source: "image://colorimage/:/icons/icons/ui/arrow-pointing-down.png?"+colors.text
|
Text {
|
||||||
fillMode: Image.Pad
|
id: positionText
|
||||||
|
|
||||||
}
|
text: "--:--:--"
|
||||||
MouseArea {
|
color: colors.text
|
||||||
anchors.fill: parent
|
}
|
||||||
onClicked: {
|
|
||||||
switch (button.state) {
|
|
||||||
case "": TimelineManager.timeline.cacheMedia(model.data.id); break;
|
|
||||||
case "stopped":
|
|
||||||
media.play(); console.log("play");
|
|
||||||
button.state = "playing"
|
|
||||||
break
|
|
||||||
case "playing":
|
|
||||||
media.pause(); console.log("pause");
|
|
||||||
button.state = "stopped"
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
}
|
|
||||||
MediaPlayer {
|
|
||||||
id: media
|
|
||||||
onError: console.log(errorString)
|
|
||||||
onStatusChanged: if(status == MediaPlayer.Loaded) progress.updatePositionTexts()
|
|
||||||
onStopped: button.state = "stopped"
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
Slider {
|
||||||
target: TimelineManager.timeline
|
id: progress
|
||||||
onMediaCached: {
|
|
||||||
if (mxcUrl == model.data.url) {
|
|
||||||
media.source = "file://" + cacheUrl
|
|
||||||
button.state = "stopped"
|
|
||||||
console.log("media loaded: " + mxcUrl + " at " + cacheUrl)
|
|
||||||
}
|
|
||||||
console.log("media cached: " + mxcUrl + " at " + cacheUrl)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
states: [
|
//indeterminate: true
|
||||||
State {
|
function updatePositionTexts() {
|
||||||
name: "stopped"
|
function formatTime(date) {
|
||||||
PropertyChanges { target: img; source: "image://colorimage/:/icons/icons/ui/play-sign.png?"+colors.text }
|
var hh = date.getUTCHours();
|
||||||
},
|
var mm = date.getUTCMinutes();
|
||||||
State {
|
var ss = date.getSeconds();
|
||||||
name: "playing"
|
if (hh < 10)
|
||||||
PropertyChanges { target: img; source: "image://colorimage/:/icons/icons/ui/pause-symbol.png?"+colors.text }
|
hh = "0" + hh;
|
||||||
}
|
|
||||||
]
|
if (mm < 10)
|
||||||
}
|
mm = "0" + mm;
|
||||||
ColumnLayout {
|
|
||||||
id: col
|
if (ss < 10)
|
||||||
|
ss = "0" + ss;
|
||||||
|
|
||||||
|
return hh + ":" + mm + ":" + ss;
|
||||||
|
}
|
||||||
|
|
||||||
|
positionText.text = formatTime(new Date(media.position));
|
||||||
|
durationText.text = formatTime(new Date(media.duration));
|
||||||
|
}
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
value: media.position
|
||||||
|
from: 0
|
||||||
|
to: media.duration
|
||||||
|
onMoved: media.seek(value)
|
||||||
|
onValueChanged: updatePositionTexts()
|
||||||
|
palette: colors
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: durationText
|
||||||
|
|
||||||
|
text: "--:--:--"
|
||||||
|
color: colors.text
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
width: parent.width
|
||||||
|
spacing: 15
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: button
|
||||||
|
|
||||||
|
color: colors.window
|
||||||
|
radius: 22
|
||||||
|
height: 44
|
||||||
|
width: 44
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "stopped"
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: img
|
||||||
|
source: "image://colorimage/:/icons/icons/ui/play-sign.png?" + colors.text
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "playing"
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: img
|
||||||
|
source: "image://colorimage/:/icons/icons/ui/pause-symbol.png?" + colors.text
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: img
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
z: 3
|
||||||
|
source: "image://colorimage/:/icons/icons/ui/arrow-pointing-down.png?" + colors.text
|
||||||
|
fillMode: Image.Pad
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
switch (button.state) {
|
||||||
|
case "":
|
||||||
|
TimelineManager.timeline.cacheMedia(model.data.id);
|
||||||
|
break;
|
||||||
|
case "stopped":
|
||||||
|
media.play();
|
||||||
|
console.log("play");
|
||||||
|
button.state = "playing";
|
||||||
|
break;
|
||||||
|
case "playing":
|
||||||
|
media.pause();
|
||||||
|
console.log("pause");
|
||||||
|
button.state = "stopped";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaPlayer {
|
||||||
|
id: media
|
||||||
|
|
||||||
|
onError: console.log(errorString)
|
||||||
|
onStatusChanged: {
|
||||||
|
if (status == MediaPlayer.Loaded)
|
||||||
|
progress.updatePositionTexts();
|
||||||
|
|
||||||
|
}
|
||||||
|
onStopped: button.state = "stopped"
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: TimelineManager.timeline
|
||||||
|
onMediaCached: {
|
||||||
|
if (mxcUrl == model.data.url) {
|
||||||
|
media.source = "file://" + cacheUrl;
|
||||||
|
button.state = "stopped";
|
||||||
|
console.log("media loaded: " + mxcUrl + " at " + cacheUrl);
|
||||||
|
}
|
||||||
|
console.log("media cached: " + mxcUrl + " at " + cacheUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: col
|
||||||
|
|
||||||
|
Text {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: model.data.body
|
||||||
|
textFormat: Text.PlainText
|
||||||
|
elide: Text.ElideRight
|
||||||
|
color: colors.text
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: model.data.filesize
|
||||||
|
textFormat: Text.PlainText
|
||||||
|
elide: Text.ElideRight
|
||||||
|
color: colors.text
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Text {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
text: model.data.body
|
|
||||||
textFormat: Text.PlainText
|
|
||||||
elide: Text.ElideRight
|
|
||||||
color: colors.text
|
|
||||||
}
|
|
||||||
Text {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
text: model.data.filesize
|
|
||||||
textFormat: Text.PlainText
|
|
||||||
elide: Text.ElideRight
|
|
||||||
color: colors.text
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,66 +2,71 @@ import QtQuick 2.6
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
import QtQuick.Window 2.2
|
import QtQuick.Window 2.2
|
||||||
|
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: replyComponent
|
id: replyComponent
|
||||||
|
|
||||||
property alias modelData: reply.modelData
|
property alias modelData: reply.modelData
|
||||||
property color userColor: "red"
|
property color userColor: "red"
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: replyContainer.height
|
height: replyContainer.height
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
preventStealing: true
|
preventStealing: true
|
||||||
onClicked: chat.positionViewAtIndex(chat.model.idToIndex(modelData.id), ListView.Contain)
|
onClicked: chat.positionViewAtIndex(chat.model.idToIndex(modelData.id), ListView.Contain)
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: colorLine
|
id: colorLine
|
||||||
|
|
||||||
anchors.top: replyContainer.top
|
anchors.top: replyContainer.top
|
||||||
anchors.bottom: replyContainer.bottom
|
anchors.bottom: replyContainer.bottom
|
||||||
width: 4
|
width: 4
|
||||||
|
color: TimelineManager.userColor(reply.modelData.userId, colors.window)
|
||||||
|
}
|
||||||
|
|
||||||
color: TimelineManager.userColor(reply.modelData.userId, colors.window)
|
Column {
|
||||||
}
|
id: replyContainer
|
||||||
|
|
||||||
Column {
|
anchors.left: colorLine.right
|
||||||
id: replyContainer
|
anchors.leftMargin: 4
|
||||||
anchors.left: colorLine.right
|
width: parent.width - 8
|
||||||
anchors.leftMargin: 4
|
|
||||||
width: parent.width - 8
|
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: userName
|
id: userName
|
||||||
text: TimelineManager.escapeEmoji(reply.modelData.userName)
|
|
||||||
color: replyComponent.userColor
|
|
||||||
textFormat: Text.RichText
|
|
||||||
|
|
||||||
MouseArea {
|
text: TimelineManager.escapeEmoji(reply.modelData.userName)
|
||||||
anchors.fill: parent
|
color: replyComponent.userColor
|
||||||
onClicked: chat.model.openUserProfile(reply.modelData.userId)
|
textFormat: Text.RichText
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MessageDelegate {
|
MouseArea {
|
||||||
id: reply
|
anchors.fill: parent
|
||||||
width: parent.width
|
onClicked: chat.model.openUserProfile(reply.modelData.userId)
|
||||||
isReply: true
|
cursorShape: Qt.PointingHandCursor
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageDelegate {
|
||||||
|
id: reply
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
isReply: true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: backgroundItem
|
||||||
|
|
||||||
|
z: -1
|
||||||
|
height: replyContainer.height
|
||||||
|
width: Math.min(Math.max(reply.implicitWidth, userName.implicitWidth) + 8 + 4, parent.width)
|
||||||
|
color: Qt.rgba(userColor.r, userColor.g, userColor.b, 0.2)
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: backgroundItem
|
|
||||||
z: -1
|
|
||||||
height: replyContainer.height
|
|
||||||
width: Math.min(Math.max(reply.implicitWidth, userName.implicitWidth) + 8 + 4, parent.width)
|
|
||||||
color: Qt.rgba(userColor.r, userColor.g, userColor.b, 0.2)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import ".."
|
import ".."
|
||||||
|
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
property string formatted: model.data.formattedBody
|
property string formatted: model.data.formattedBody
|
||||||
text: "<style type=\"text/css\">a { color:"+colors.link+";}</style>" + formatted.replace("<pre>", "<pre style='white-space: pre-wrap'>")
|
|
||||||
width: parent ? parent.width : undefined
|
text: "<style type=\"text/css\">a { color:" + colors.link + ";}</style>" + formatted.replace("<pre>", "<pre style='white-space: pre-wrap'>")
|
||||||
height: isReply ? Math.round(Math.min(timelineRoot.height / 8, implicitHeight)) : undefined
|
width: parent ? parent.width : undefined
|
||||||
clip: true
|
height: isReply ? Math.round(Math.min(timelineRoot.height / 8, implicitHeight)) : undefined
|
||||||
font.pointSize: (Settings.enlargeEmojiOnlyMessages && model.data.isOnlyEmoji > 0 && model.data.isOnlyEmoji < 4) ? Settings.fontSize * 3 : Settings.fontSize
|
clip: true
|
||||||
|
font.pointSize: (Settings.enlargeEmojiOnlyMessages && model.data.isOnlyEmoji > 0 && model.data.isOnlyEmoji < 4) ? Settings.fontSize * 3 : Settings.fontSize
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,39 +1,46 @@
|
||||||
import QtQuick 2.3
|
import QtQuick 2.3
|
||||||
import QtQuick.Controls 2.10
|
import QtQuick.Controls 2.10
|
||||||
import QtQuick.Layouts 1.10
|
import QtQuick.Layouts 1.10
|
||||||
|
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
|
||||||
Pane {
|
Pane {
|
||||||
property string title: qsTr("Awaiting Confirmation")
|
property string title: qsTr("Awaiting Confirmation")
|
||||||
ColumnLayout {
|
|
||||||
spacing: 16
|
ColumnLayout {
|
||||||
Label {
|
spacing: 16
|
||||||
Layout.maximumWidth: 400
|
|
||||||
Layout.fillHeight: true
|
Label {
|
||||||
Layout.fillWidth: true
|
id: content
|
||||||
wrapMode: Text.Wrap
|
|
||||||
id: content
|
Layout.maximumWidth: 400
|
||||||
text: qsTr("Waiting for other side to complete verification.")
|
Layout.fillHeight: true
|
||||||
color:colors.text
|
Layout.fillWidth: true
|
||||||
verticalAlignment: Text.AlignVCenter
|
wrapMode: Text.Wrap
|
||||||
}
|
text: qsTr("Waiting for other side to complete verification.")
|
||||||
BusyIndicator {
|
color: colors.text
|
||||||
Layout.alignment: Qt.AlignHCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
}
|
}
|
||||||
RowLayout {
|
|
||||||
Button {
|
BusyIndicator {
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignHCenter
|
||||||
text: qsTr("Cancel")
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Button {
|
||||||
|
Layout.alignment: Qt.AlignLeft
|
||||||
|
text: qsTr("Cancel")
|
||||||
|
onClicked: {
|
||||||
|
flow.cancel();
|
||||||
|
dialog.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
flow.cancel();
|
|
||||||
dialog.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Item {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,97 +1,144 @@
|
||||||
import QtQuick 2.10
|
import QtQuick 2.10
|
||||||
import QtQuick.Controls 2.10
|
import QtQuick.Controls 2.10
|
||||||
import QtQuick.Window 2.10
|
import QtQuick.Window 2.10
|
||||||
|
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
property var flow
|
id: dialog
|
||||||
|
|
||||||
onClosing: TimelineManager.removeVerificationFlow(flow)
|
property var flow
|
||||||
|
|
||||||
title: stack.currentItem.title
|
onClosing: TimelineManager.removeVerificationFlow(flow)
|
||||||
id: dialog
|
title: stack.currentItem.title
|
||||||
|
flags: Qt.Dialog
|
||||||
|
palette: colors
|
||||||
|
height: stack.implicitHeight
|
||||||
|
width: stack.implicitWidth
|
||||||
|
|
||||||
flags: Qt.Dialog
|
StackView {
|
||||||
|
id: stack
|
||||||
|
|
||||||
palette: colors
|
initialItem: newVerificationRequest
|
||||||
|
implicitWidth: currentItem.implicitWidth
|
||||||
|
implicitHeight: currentItem.implicitHeight
|
||||||
|
}
|
||||||
|
|
||||||
height: stack.implicitHeight
|
Component {
|
||||||
width: stack.implicitWidth
|
id: newVerificationRequest
|
||||||
|
|
||||||
StackView {
|
NewVerificationRequest {
|
||||||
id: stack
|
}
|
||||||
initialItem: newVerificationRequest
|
|
||||||
implicitWidth: currentItem.implicitWidth
|
|
||||||
implicitHeight: currentItem.implicitHeight
|
|
||||||
}
|
|
||||||
|
|
||||||
Component{
|
}
|
||||||
id: newVerificationRequest
|
|
||||||
NewVerificationRequest {}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: waiting
|
id: waiting
|
||||||
Waiting {}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
Waiting {
|
||||||
id: success
|
}
|
||||||
Success {}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
}
|
||||||
id: failed
|
|
||||||
Failed {}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: digitVerification
|
id: success
|
||||||
DigitVerification {}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
Success {
|
||||||
id: emojiVerification
|
}
|
||||||
EmojiVerification {}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
}
|
||||||
state: flow.state
|
|
||||||
|
Component {
|
||||||
|
id: failed
|
||||||
|
|
||||||
|
Failed {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: digitVerification
|
||||||
|
|
||||||
|
DigitVerification {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: emojiVerification
|
||||||
|
|
||||||
|
EmojiVerification {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
state: flow.state
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "PromptStartVerification"
|
||||||
|
|
||||||
|
StateChangeScript {
|
||||||
|
script: stack.replace(newVerificationRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "CompareEmoji"
|
||||||
|
|
||||||
|
StateChangeScript {
|
||||||
|
script: stack.replace(emojiVerification)
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "CompareNumber"
|
||||||
|
|
||||||
|
StateChangeScript {
|
||||||
|
script: stack.replace(digitVerification)
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "WaitingForKeys"
|
||||||
|
|
||||||
|
StateChangeScript {
|
||||||
|
script: stack.replace(waiting)
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "WaitingForOtherToAccept"
|
||||||
|
|
||||||
|
StateChangeScript {
|
||||||
|
script: stack.replace(waiting)
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "WaitingForMac"
|
||||||
|
|
||||||
|
StateChangeScript {
|
||||||
|
script: stack.replace(waiting)
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "Success"
|
||||||
|
|
||||||
|
StateChangeScript {
|
||||||
|
script: stack.replace(success)
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "Failed"
|
||||||
|
|
||||||
|
StateChangeScript {
|
||||||
|
script: stack.replace(failed)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
states: [
|
|
||||||
State {
|
|
||||||
name: "PromptStartVerification"
|
|
||||||
StateChangeScript { script: stack.replace(newVerificationRequest) }
|
|
||||||
},
|
|
||||||
State {
|
|
||||||
name: "CompareEmoji"
|
|
||||||
StateChangeScript { script: stack.replace(emojiVerification) }
|
|
||||||
},
|
|
||||||
State {
|
|
||||||
name: "CompareNumber"
|
|
||||||
StateChangeScript { script: stack.replace(digitVerification) }
|
|
||||||
},
|
|
||||||
State {
|
|
||||||
name: "WaitingForKeys"
|
|
||||||
StateChangeScript { script: stack.replace(waiting) }
|
|
||||||
},
|
|
||||||
State {
|
|
||||||
name: "WaitingForOtherToAccept"
|
|
||||||
StateChangeScript { script: stack.replace(waiting) }
|
|
||||||
},
|
|
||||||
State {
|
|
||||||
name: "WaitingForMac"
|
|
||||||
StateChangeScript { script: stack.replace(waiting) }
|
|
||||||
},
|
|
||||||
State {
|
|
||||||
name: "Success"
|
|
||||||
StateChangeScript { script: stack.replace(success) }
|
|
||||||
},
|
|
||||||
State {
|
|
||||||
name: "Failed"
|
|
||||||
StateChangeScript { script: stack.replace(failed); }
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,60 +1,69 @@
|
||||||
import QtQuick 2.3
|
import QtQuick 2.3
|
||||||
import QtQuick.Controls 2.10
|
import QtQuick.Controls 2.10
|
||||||
import QtQuick.Layouts 1.10
|
import QtQuick.Layouts 1.10
|
||||||
|
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
|
||||||
Pane {
|
Pane {
|
||||||
property string title: qsTr("Verification Code")
|
property string title: qsTr("Verification Code")
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
spacing: 16
|
spacing: 16
|
||||||
Label {
|
|
||||||
Layout.maximumWidth: 400
|
|
||||||
Layout.fillHeight: true
|
|
||||||
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!")
|
|
||||||
color:colors.text
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
RowLayout {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Label {
|
|
||||||
font.pixelSize: Qt.application.font.pixelSize * 2
|
|
||||||
text: flow.sasList[0]
|
|
||||||
color:colors.text
|
|
||||||
}
|
|
||||||
Label {
|
|
||||||
font.pixelSize: Qt.application.font.pixelSize * 2
|
|
||||||
text: flow.sasList[1]
|
|
||||||
color:colors.text
|
|
||||||
}
|
|
||||||
Label {
|
|
||||||
font.pixelSize: Qt.application.font.pixelSize * 2
|
|
||||||
text: flow.sasList[2]
|
|
||||||
color:colors.text
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RowLayout {
|
|
||||||
Button {
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
|
||||||
text: qsTr("They do not match!")
|
|
||||||
|
|
||||||
onClicked: {
|
Label {
|
||||||
flow.cancel();
|
Layout.maximumWidth: 400
|
||||||
dialog.close();
|
Layout.fillHeight: true
|
||||||
}
|
Layout.fillWidth: true
|
||||||
}
|
wrapMode: Text.Wrap
|
||||||
Item {
|
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.fillWidth: true
|
color: colors.text
|
||||||
}
|
verticalAlignment: Text.AlignVCenter
|
||||||
Button {
|
}
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
text: qsTr("They match!")
|
RowLayout {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
||||||
|
Label {
|
||||||
|
font.pixelSize: Qt.application.font.pixelSize * 2
|
||||||
|
text: flow.sasList[0]
|
||||||
|
color: colors.text
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
font.pixelSize: Qt.application.font.pixelSize * 2
|
||||||
|
text: flow.sasList[1]
|
||||||
|
color: colors.text
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
font.pixelSize: Qt.application.font.pixelSize * 2
|
||||||
|
text: flow.sasList[2]
|
||||||
|
color: colors.text
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
onClicked: flow.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,24 +3,31 @@ import QtQuick.Controls 2.10
|
||||||
import QtQuick.Layouts 1.10
|
import QtQuick.Layouts 1.10
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
color: "red"
|
color: "red"
|
||||||
implicitHeight: Qt.application.font.pixelSize * 4
|
implicitHeight: Qt.application.font.pixelSize * 4
|
||||||
implicitWidth: col.width
|
implicitWidth: col.width
|
||||||
height: Qt.application.font.pixelSize * 4
|
height: Qt.application.font.pixelSize * 4
|
||||||
width: col.width
|
width: col.width
|
||||||
ColumnLayout {
|
|
||||||
id: col
|
ColumnLayout {
|
||||||
anchors.bottom: parent.bottom
|
id: col
|
||||||
property var emoji: emojis.mapping[Math.floor(Math.random()*64)]
|
|
||||||
Label {
|
property var emoji: emojis.mapping[Math.floor(Math.random() * 64)]
|
||||||
height: font.pixelSize * 2
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
anchors.bottom: parent.bottom
|
||||||
text: col.emoji.emoji
|
|
||||||
font.pixelSize: Qt.application.font.pixelSize * 2
|
Label {
|
||||||
}
|
height: font.pixelSize * 2
|
||||||
Label {
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
text: col.emoji.emoji
|
||||||
text: col.emoji.description
|
font.pixelSize: Qt.application.font.pixelSize * 2
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
Label {
|
||||||
|
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
||||||
|
text: col.emoji.description
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,140 +1,414 @@
|
||||||
import QtQuick 2.3
|
import QtQuick 2.3
|
||||||
import QtQuick.Controls 2.10
|
import QtQuick.Controls 2.10
|
||||||
import QtQuick.Layouts 1.10
|
import QtQuick.Layouts 1.10
|
||||||
|
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
|
||||||
Pane {
|
Pane {
|
||||||
property string title: qsTr("Verification Code")
|
property string title: qsTr("Verification Code")
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
spacing: 16
|
spacing: 16
|
||||||
Label {
|
|
||||||
Layout.maximumWidth: 400
|
|
||||||
Layout.fillHeight: true
|
|
||||||
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!")
|
|
||||||
color:colors.text
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
RowLayout {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
id: emojis
|
|
||||||
property var mapping: [
|
|
||||||
{"number": 0, "emoji": "🐶", "description": "Dog", "unicode": "U+1F436"},
|
|
||||||
{"number": 1, "emoji": "🐱", "description": "Cat", "unicode": "U+1F431"},
|
|
||||||
{"number": 2, "emoji": "🦁", "description": "Lion", "unicode": "U+1F981"},
|
|
||||||
{"number": 3, "emoji": "🐎", "description": "Horse", "unicode": "U+1F40E"},
|
|
||||||
{"number": 4, "emoji": "🦄", "description": "Unicorn", "unicode": "U+1F984"},
|
|
||||||
{"number": 5, "emoji": "🐷", "description": "Pig", "unicode": "U+1F437"},
|
|
||||||
{"number": 6, "emoji": "🐘", "description": "Elephant", "unicode": "U+1F418"},
|
|
||||||
{"number": 7, "emoji": "🐰", "description": "Rabbit", "unicode": "U+1F430"},
|
|
||||||
{"number": 8, "emoji": "🐼", "description": "Panda", "unicode": "U+1F43C"},
|
|
||||||
{"number": 9, "emoji": "🐓", "description": "Rooster", "unicode": "U+1F413"},
|
|
||||||
{"number": 10, "emoji": "🐧", "description": "Penguin", "unicode": "U+1F427"},
|
|
||||||
{"number": 11, "emoji": "🐢", "description": "Turtle", "unicode": "U+1F422"},
|
|
||||||
{"number": 12, "emoji": "🐟", "description": "Fish", "unicode": "U+1F41F"},
|
|
||||||
{"number": 13, "emoji": "🐙", "description": "Octopus", "unicode": "U+1F419"},
|
|
||||||
{"number": 14, "emoji": "🦋", "description": "Butterfly", "unicode": "U+1F98B"},
|
|
||||||
{"number": 15, "emoji": "🌷", "description": "Flower", "unicode": "U+1F337"},
|
|
||||||
{"number": 16, "emoji": "🌳", "description": "Tree", "unicode": "U+1F333"},
|
|
||||||
{"number": 17, "emoji": "🌵", "description": "Cactus", "unicode": "U+1F335"},
|
|
||||||
{"number": 18, "emoji": "🍄", "description": "Mushroom", "unicode": "U+1F344"},
|
|
||||||
{"number": 19, "emoji": "🌏", "description": "Globe", "unicode": "U+1F30F"},
|
|
||||||
{"number": 20, "emoji": "🌙", "description": "Moon", "unicode": "U+1F319"},
|
|
||||||
{"number": 21, "emoji": "☁️", "description": "Cloud", "unicode": "U+2601U+FE0F"},
|
|
||||||
{"number": 22, "emoji": "🔥", "description": "Fire", "unicode": "U+1F525"},
|
|
||||||
{"number": 23, "emoji": "🍌", "description": "Banana", "unicode": "U+1F34C"},
|
|
||||||
{"number": 24, "emoji": "🍎", "description": "Apple", "unicode": "U+1F34E"},
|
|
||||||
{"number": 25, "emoji": "🍓", "description": "Strawberry", "unicode": "U+1F353"},
|
|
||||||
{"number": 26, "emoji": "🌽", "description": "Corn", "unicode": "U+1F33D"},
|
|
||||||
{"number": 27, "emoji": "🍕", "description": "Pizza", "unicode": "U+1F355"},
|
|
||||||
{"number": 28, "emoji": "🎂", "description": "Cake", "unicode": "U+1F382"},
|
|
||||||
{"number": 29, "emoji": "❤️", "description": "Heart", "unicode": "U+2764U+FE0F"},
|
|
||||||
{"number": 30, "emoji": "😀", "description": "Smiley", "unicode": "U+1F600"},
|
|
||||||
{"number": 31, "emoji": "🤖", "description": "Robot", "unicode": "U+1F916"},
|
|
||||||
{"number": 32, "emoji": "🎩", "description": "Hat", "unicode": "U+1F3A9"},
|
|
||||||
{"number": 33, "emoji": "👓", "description": "Glasses", "unicode": "U+1F453"},
|
|
||||||
{"number": 34, "emoji": "🔧", "description": "Spanner", "unicode": "U+1F527"},
|
|
||||||
{"number": 35, "emoji": "🎅", "description": "Santa", "unicode": "U+1F385"},
|
|
||||||
{"number": 36, "emoji": "👍", "description": "Thumbs Up", "unicode": "U+1F44D"},
|
|
||||||
{"number": 37, "emoji": "☂️", "description": "Umbrella", "unicode": "U+2602U+FE0F"},
|
|
||||||
{"number": 38, "emoji": "⌛", "description": "Hourglass", "unicode": "U+231B"},
|
|
||||||
{"number": 39, "emoji": "⏰", "description": "Clock", "unicode": "U+23F0"},
|
|
||||||
{"number": 40, "emoji": "🎁", "description": "Gift", "unicode": "U+1F381"},
|
|
||||||
{"number": 41, "emoji": "💡", "description": "Light Bulb", "unicode": "U+1F4A1"},
|
|
||||||
{"number": 42, "emoji": "📕", "description": "Book", "unicode": "U+1F4D5"},
|
|
||||||
{"number": 43, "emoji": "✏️", "description": "Pencil", "unicode": "U+270FU+FE0F"},
|
|
||||||
{"number": 44, "emoji": "📎", "description": "Paperclip", "unicode": "U+1F4CE"},
|
|
||||||
{"number": 45, "emoji": "✂️", "description": "Scissors", "unicode": "U+2702U+FE0F"},
|
|
||||||
{"number": 46, "emoji": "🔒", "description": "Lock", "unicode": "U+1F512"},
|
|
||||||
{"number": 47, "emoji": "🔑", "description": "Key", "unicode": "U+1F511"},
|
|
||||||
{"number": 48, "emoji": "🔨", "description": "Hammer", "unicode": "U+1F528"},
|
|
||||||
{"number": 49, "emoji": "☎️", "description": "Telephone", "unicode": "U+260EU+FE0F"},
|
|
||||||
{"number": 50, "emoji": "🏁", "description": "Flag", "unicode": "U+1F3C1"},
|
|
||||||
{"number": 51, "emoji": "🚂", "description": "Train", "unicode": "U+1F682"},
|
|
||||||
{"number": 52, "emoji": "🚲", "description": "Bicycle", "unicode": "U+1F6B2"},
|
|
||||||
{"number": 53, "emoji": "✈️", "description": "Aeroplane", "unicode": "U+2708U+FE0F"},
|
|
||||||
{"number": 54, "emoji": "🚀", "description": "Rocket", "unicode": "U+1F680"},
|
|
||||||
{"number": 55, "emoji": "🏆", "description": "Trophy", "unicode": "U+1F3C6"},
|
|
||||||
{"number": 56, "emoji": "⚽", "description": "Ball", "unicode": "U+26BD"},
|
|
||||||
{"number": 57, "emoji": "🎸", "description": "Guitar", "unicode": "U+1F3B8"},
|
|
||||||
{"number": 58, "emoji": "🎺", "description": "Trumpet", "unicode": "U+1F3BA"},
|
|
||||||
{"number": 59, "emoji": "🔔", "description": "Bell", "unicode": "U+1F514"},
|
|
||||||
{"number": 60, "emoji": "⚓", "description": "Anchor", "unicode": "U+2693"},
|
|
||||||
{"number": 61, "emoji": "🎧", "description": "Headphones", "unicode": "U+1F3A7"},
|
|
||||||
{"number": 62, "emoji": "📁", "description": "Folder", "unicode": "U+1F4C1"},
|
|
||||||
{"number": 63, "emoji": "📌", "description": "Pin", "unicode": "U+1F4CC"}
|
|
||||||
]
|
|
||||||
Repeater {
|
|
||||||
id: repeater
|
|
||||||
model: 7
|
|
||||||
delegate: Rectangle {
|
|
||||||
color: "transparent"
|
|
||||||
implicitHeight: Qt.application.font.pixelSize * 8
|
|
||||||
implicitWidth: col.width
|
|
||||||
ColumnLayout {
|
|
||||||
id: col
|
|
||||||
Layout.fillWidth: true
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
property var emoji: emojis.mapping[flow.sasList[index]]
|
|
||||||
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:colors.text
|
|
||||||
}
|
|
||||||
Label {
|
|
||||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
|
||||||
text: col.emoji.description
|
|
||||||
color:colors.text
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RowLayout {
|
|
||||||
Button {
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
|
||||||
text: qsTr("They do not match!")
|
|
||||||
|
|
||||||
onClicked: {
|
Label {
|
||||||
flow.cancel();
|
Layout.maximumWidth: 400
|
||||||
dialog.close();
|
Layout.fillHeight: true
|
||||||
}
|
Layout.fillWidth: true
|
||||||
}
|
wrapMode: Text.Wrap
|
||||||
Item {
|
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.fillWidth: true
|
color: colors.text
|
||||||
}
|
verticalAlignment: Text.AlignVCenter
|
||||||
Button {
|
}
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
text: qsTr("They match!")
|
RowLayout {
|
||||||
|
id: emojis
|
||||||
|
|
||||||
|
property var mapping: [{
|
||||||
|
"number": 0,
|
||||||
|
"emoji": "🐶",
|
||||||
|
"description": "Dog",
|
||||||
|
"unicode": "U+1F436"
|
||||||
|
}, {
|
||||||
|
"number": 1,
|
||||||
|
"emoji": "🐱",
|
||||||
|
"description": "Cat",
|
||||||
|
"unicode": "U+1F431"
|
||||||
|
}, {
|
||||||
|
"number": 2,
|
||||||
|
"emoji": "🦁",
|
||||||
|
"description": "Lion",
|
||||||
|
"unicode": "U+1F981"
|
||||||
|
}, {
|
||||||
|
"number": 3,
|
||||||
|
"emoji": "🐎",
|
||||||
|
"description": "Horse",
|
||||||
|
"unicode": "U+1F40E"
|
||||||
|
}, {
|
||||||
|
"number": 4,
|
||||||
|
"emoji": "🦄",
|
||||||
|
"description": "Unicorn",
|
||||||
|
"unicode": "U+1F984"
|
||||||
|
}, {
|
||||||
|
"number": 5,
|
||||||
|
"emoji": "🐷",
|
||||||
|
"description": "Pig",
|
||||||
|
"unicode": "U+1F437"
|
||||||
|
}, {
|
||||||
|
"number": 6,
|
||||||
|
"emoji": "🐘",
|
||||||
|
"description": "Elephant",
|
||||||
|
"unicode": "U+1F418"
|
||||||
|
}, {
|
||||||
|
"number": 7,
|
||||||
|
"emoji": "🐰",
|
||||||
|
"description": "Rabbit",
|
||||||
|
"unicode": "U+1F430"
|
||||||
|
}, {
|
||||||
|
"number": 8,
|
||||||
|
"emoji": "🐼",
|
||||||
|
"description": "Panda",
|
||||||
|
"unicode": "U+1F43C"
|
||||||
|
}, {
|
||||||
|
"number": 9,
|
||||||
|
"emoji": "🐓",
|
||||||
|
"description": "Rooster",
|
||||||
|
"unicode": "U+1F413"
|
||||||
|
}, {
|
||||||
|
"number": 10,
|
||||||
|
"emoji": "🐧",
|
||||||
|
"description": "Penguin",
|
||||||
|
"unicode": "U+1F427"
|
||||||
|
}, {
|
||||||
|
"number": 11,
|
||||||
|
"emoji": "🐢",
|
||||||
|
"description": "Turtle",
|
||||||
|
"unicode": "U+1F422"
|
||||||
|
}, {
|
||||||
|
"number": 12,
|
||||||
|
"emoji": "🐟",
|
||||||
|
"description": "Fish",
|
||||||
|
"unicode": "U+1F41F"
|
||||||
|
}, {
|
||||||
|
"number": 13,
|
||||||
|
"emoji": "🐙",
|
||||||
|
"description": "Octopus",
|
||||||
|
"unicode": "U+1F419"
|
||||||
|
}, {
|
||||||
|
"number": 14,
|
||||||
|
"emoji": "🦋",
|
||||||
|
"description": "Butterfly",
|
||||||
|
"unicode": "U+1F98B"
|
||||||
|
}, {
|
||||||
|
"number": 15,
|
||||||
|
"emoji": "🌷",
|
||||||
|
"description": "Flower",
|
||||||
|
"unicode": "U+1F337"
|
||||||
|
}, {
|
||||||
|
"number": 16,
|
||||||
|
"emoji": "🌳",
|
||||||
|
"description": "Tree",
|
||||||
|
"unicode": "U+1F333"
|
||||||
|
}, {
|
||||||
|
"number": 17,
|
||||||
|
"emoji": "🌵",
|
||||||
|
"description": "Cactus",
|
||||||
|
"unicode": "U+1F335"
|
||||||
|
}, {
|
||||||
|
"number": 18,
|
||||||
|
"emoji": "🍄",
|
||||||
|
"description": "Mushroom",
|
||||||
|
"unicode": "U+1F344"
|
||||||
|
}, {
|
||||||
|
"number": 19,
|
||||||
|
"emoji": "🌏",
|
||||||
|
"description": "Globe",
|
||||||
|
"unicode": "U+1F30F"
|
||||||
|
}, {
|
||||||
|
"number": 20,
|
||||||
|
"emoji": "🌙",
|
||||||
|
"description": "Moon",
|
||||||
|
"unicode": "U+1F319"
|
||||||
|
}, {
|
||||||
|
"number": 21,
|
||||||
|
"emoji": "☁️",
|
||||||
|
"description": "Cloud",
|
||||||
|
"unicode": "U+2601U+FE0F"
|
||||||
|
}, {
|
||||||
|
"number": 22,
|
||||||
|
"emoji": "🔥",
|
||||||
|
"description": "Fire",
|
||||||
|
"unicode": "U+1F525"
|
||||||
|
}, {
|
||||||
|
"number": 23,
|
||||||
|
"emoji": "🍌",
|
||||||
|
"description": "Banana",
|
||||||
|
"unicode": "U+1F34C"
|
||||||
|
}, {
|
||||||
|
"number": 24,
|
||||||
|
"emoji": "🍎",
|
||||||
|
"description": "Apple",
|
||||||
|
"unicode": "U+1F34E"
|
||||||
|
}, {
|
||||||
|
"number": 25,
|
||||||
|
"emoji": "🍓",
|
||||||
|
"description": "Strawberry",
|
||||||
|
"unicode": "U+1F353"
|
||||||
|
}, {
|
||||||
|
"number": 26,
|
||||||
|
"emoji": "🌽",
|
||||||
|
"description": "Corn",
|
||||||
|
"unicode": "U+1F33D"
|
||||||
|
}, {
|
||||||
|
"number": 27,
|
||||||
|
"emoji": "🍕",
|
||||||
|
"description": "Pizza",
|
||||||
|
"unicode": "U+1F355"
|
||||||
|
}, {
|
||||||
|
"number": 28,
|
||||||
|
"emoji": "🎂",
|
||||||
|
"description": "Cake",
|
||||||
|
"unicode": "U+1F382"
|
||||||
|
}, {
|
||||||
|
"number": 29,
|
||||||
|
"emoji": "❤️",
|
||||||
|
"description": "Heart",
|
||||||
|
"unicode": "U+2764U+FE0F"
|
||||||
|
}, {
|
||||||
|
"number": 30,
|
||||||
|
"emoji": "😀",
|
||||||
|
"description": "Smiley",
|
||||||
|
"unicode": "U+1F600"
|
||||||
|
}, {
|
||||||
|
"number": 31,
|
||||||
|
"emoji": "🤖",
|
||||||
|
"description": "Robot",
|
||||||
|
"unicode": "U+1F916"
|
||||||
|
}, {
|
||||||
|
"number": 32,
|
||||||
|
"emoji": "🎩",
|
||||||
|
"description": "Hat",
|
||||||
|
"unicode": "U+1F3A9"
|
||||||
|
}, {
|
||||||
|
"number": 33,
|
||||||
|
"emoji": "👓",
|
||||||
|
"description": "Glasses",
|
||||||
|
"unicode": "U+1F453"
|
||||||
|
}, {
|
||||||
|
"number": 34,
|
||||||
|
"emoji": "🔧",
|
||||||
|
"description": "Spanner",
|
||||||
|
"unicode": "U+1F527"
|
||||||
|
}, {
|
||||||
|
"number": 35,
|
||||||
|
"emoji": "🎅",
|
||||||
|
"description": "Santa",
|
||||||
|
"unicode": "U+1F385"
|
||||||
|
}, {
|
||||||
|
"number": 36,
|
||||||
|
"emoji": "👍",
|
||||||
|
"description": "Thumbs Up",
|
||||||
|
"unicode": "U+1F44D"
|
||||||
|
}, {
|
||||||
|
"number": 37,
|
||||||
|
"emoji": "☂️",
|
||||||
|
"description": "Umbrella",
|
||||||
|
"unicode": "U+2602U+FE0F"
|
||||||
|
}, {
|
||||||
|
"number": 38,
|
||||||
|
"emoji": "⌛",
|
||||||
|
"description": "Hourglass",
|
||||||
|
"unicode": "U+231B"
|
||||||
|
}, {
|
||||||
|
"number": 39,
|
||||||
|
"emoji": "⏰",
|
||||||
|
"description": "Clock",
|
||||||
|
"unicode": "U+23F0"
|
||||||
|
}, {
|
||||||
|
"number": 40,
|
||||||
|
"emoji": "🎁",
|
||||||
|
"description": "Gift",
|
||||||
|
"unicode": "U+1F381"
|
||||||
|
}, {
|
||||||
|
"number": 41,
|
||||||
|
"emoji": "💡",
|
||||||
|
"description": "Light Bulb",
|
||||||
|
"unicode": "U+1F4A1"
|
||||||
|
}, {
|
||||||
|
"number": 42,
|
||||||
|
"emoji": "📕",
|
||||||
|
"description": "Book",
|
||||||
|
"unicode": "U+1F4D5"
|
||||||
|
}, {
|
||||||
|
"number": 43,
|
||||||
|
"emoji": "✏️",
|
||||||
|
"description": "Pencil",
|
||||||
|
"unicode": "U+270FU+FE0F"
|
||||||
|
}, {
|
||||||
|
"number": 44,
|
||||||
|
"emoji": "📎",
|
||||||
|
"description": "Paperclip",
|
||||||
|
"unicode": "U+1F4CE"
|
||||||
|
}, {
|
||||||
|
"number": 45,
|
||||||
|
"emoji": "✂️",
|
||||||
|
"description": "Scissors",
|
||||||
|
"unicode": "U+2702U+FE0F"
|
||||||
|
}, {
|
||||||
|
"number": 46,
|
||||||
|
"emoji": "🔒",
|
||||||
|
"description": "Lock",
|
||||||
|
"unicode": "U+1F512"
|
||||||
|
}, {
|
||||||
|
"number": 47,
|
||||||
|
"emoji": "🔑",
|
||||||
|
"description": "Key",
|
||||||
|
"unicode": "U+1F511"
|
||||||
|
}, {
|
||||||
|
"number": 48,
|
||||||
|
"emoji": "🔨",
|
||||||
|
"description": "Hammer",
|
||||||
|
"unicode": "U+1F528"
|
||||||
|
}, {
|
||||||
|
"number": 49,
|
||||||
|
"emoji": "☎️",
|
||||||
|
"description": "Telephone",
|
||||||
|
"unicode": "U+260EU+FE0F"
|
||||||
|
}, {
|
||||||
|
"number": 50,
|
||||||
|
"emoji": "🏁",
|
||||||
|
"description": "Flag",
|
||||||
|
"unicode": "U+1F3C1"
|
||||||
|
}, {
|
||||||
|
"number": 51,
|
||||||
|
"emoji": "🚂",
|
||||||
|
"description": "Train",
|
||||||
|
"unicode": "U+1F682"
|
||||||
|
}, {
|
||||||
|
"number": 52,
|
||||||
|
"emoji": "🚲",
|
||||||
|
"description": "Bicycle",
|
||||||
|
"unicode": "U+1F6B2"
|
||||||
|
}, {
|
||||||
|
"number": 53,
|
||||||
|
"emoji": "✈️",
|
||||||
|
"description": "Aeroplane",
|
||||||
|
"unicode": "U+2708U+FE0F"
|
||||||
|
}, {
|
||||||
|
"number": 54,
|
||||||
|
"emoji": "🚀",
|
||||||
|
"description": "Rocket",
|
||||||
|
"unicode": "U+1F680"
|
||||||
|
}, {
|
||||||
|
"number": 55,
|
||||||
|
"emoji": "🏆",
|
||||||
|
"description": "Trophy",
|
||||||
|
"unicode": "U+1F3C6"
|
||||||
|
}, {
|
||||||
|
"number": 56,
|
||||||
|
"emoji": "⚽",
|
||||||
|
"description": "Ball",
|
||||||
|
"unicode": "U+26BD"
|
||||||
|
}, {
|
||||||
|
"number": 57,
|
||||||
|
"emoji": "🎸",
|
||||||
|
"description": "Guitar",
|
||||||
|
"unicode": "U+1F3B8"
|
||||||
|
}, {
|
||||||
|
"number": 58,
|
||||||
|
"emoji": "🎺",
|
||||||
|
"description": "Trumpet",
|
||||||
|
"unicode": "U+1F3BA"
|
||||||
|
}, {
|
||||||
|
"number": 59,
|
||||||
|
"emoji": "🔔",
|
||||||
|
"description": "Bell",
|
||||||
|
"unicode": "U+1F514"
|
||||||
|
}, {
|
||||||
|
"number": 60,
|
||||||
|
"emoji": "⚓",
|
||||||
|
"description": "Anchor",
|
||||||
|
"unicode": "U+2693"
|
||||||
|
}, {
|
||||||
|
"number": 61,
|
||||||
|
"emoji": "🎧",
|
||||||
|
"description": "Headphones",
|
||||||
|
"unicode": "U+1F3A7"
|
||||||
|
}, {
|
||||||
|
"number": 62,
|
||||||
|
"emoji": "📁",
|
||||||
|
"description": "Folder",
|
||||||
|
"unicode": "U+1F4C1"
|
||||||
|
}, {
|
||||||
|
"number": 63,
|
||||||
|
"emoji": "📌",
|
||||||
|
"description": "Pin",
|
||||||
|
"unicode": "U+1F4CC"
|
||||||
|
}]
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: repeater
|
||||||
|
|
||||||
|
model: 7
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
color: "transparent"
|
||||||
|
implicitHeight: Qt.application.font.pixelSize * 8
|
||||||
|
implicitWidth: col.width
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: col
|
||||||
|
|
||||||
|
property var emoji: emojis.mapping[flow.sasList[index]]
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
|
||||||
|
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: colors.text
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
||||||
|
text: col.emoji.description
|
||||||
|
color: colors.text
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
onClicked: flow.next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,44 +1,56 @@
|
||||||
import QtQuick 2.3
|
import QtQuick 2.3
|
||||||
import QtQuick.Controls 2.10
|
import QtQuick.Controls 2.10
|
||||||
import QtQuick.Layouts 1.10
|
import QtQuick.Layouts 1.10
|
||||||
|
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
|
||||||
Pane {
|
Pane {
|
||||||
property string title: qsTr("Verification failed")
|
property string title: qsTr("Verification failed")
|
||||||
ColumnLayout {
|
|
||||||
spacing: 16
|
|
||||||
Text {
|
|
||||||
id: content
|
|
||||||
|
|
||||||
Layout.maximumWidth: 400
|
ColumnLayout {
|
||||||
Layout.fillHeight: true
|
spacing: 16
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
wrapMode: Text.Wrap
|
Text {
|
||||||
text: switch (flow.error) {
|
id: content
|
||||||
case DeviceVerificationFlow.UnknownMethod: return qsTr("Other client does not support our verification protocol.")
|
|
||||||
case DeviceVerificationFlow.MismatchedCommitment:
|
Layout.maximumWidth: 400
|
||||||
case DeviceVerificationFlow.MismatchedSAS:
|
Layout.fillHeight: true
|
||||||
case DeviceVerificationFlow.KeyMismatch: return qsTr("Key mismatch detected!")
|
Layout.fillWidth: true
|
||||||
case DeviceVerificationFlow.Timeout: return qsTr("Device verification timed out.")
|
wrapMode: Text.Wrap
|
||||||
case DeviceVerificationFlow.User: return qsTr("Other party canceled the verification.")
|
text: {
|
||||||
case DeviceVerificationFlow.OutOfOrder: return qsTr("Device verification timed out.")
|
switch (flow.error) {
|
||||||
default: return "Unknown verification error.";
|
case DeviceVerificationFlow.UnknownMethod:
|
||||||
}
|
return qsTr("Other client does not support our verification protocol.");
|
||||||
color:colors.text
|
case DeviceVerificationFlow.MismatchedCommitment:
|
||||||
verticalAlignment: Text.AlignVCenter
|
case DeviceVerificationFlow.MismatchedSAS:
|
||||||
}
|
case DeviceVerificationFlow.KeyMismatch:
|
||||||
RowLayout {
|
return qsTr("Key mismatch detected!");
|
||||||
Item {
|
case DeviceVerificationFlow.Timeout:
|
||||||
Layout.fillWidth: true
|
return qsTr("Device verification timed out.");
|
||||||
}
|
case DeviceVerificationFlow.User:
|
||||||
Button {
|
return qsTr("Other party canceled the verification.");
|
||||||
Layout.alignment: Qt.AlignRight
|
case DeviceVerificationFlow.OutOfOrder:
|
||||||
text: qsTr("Close")
|
return qsTr("Device verification timed out.");
|
||||||
|
default:
|
||||||
|
return "Unknown verification error.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
color: colors.text
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
|
text: qsTr("Close")
|
||||||
|
onClicked: dialog.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
onClicked: dialog.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,44 +1,46 @@
|
||||||
import QtQuick 2.3
|
import QtQuick 2.3
|
||||||
import QtQuick.Controls 2.10
|
import QtQuick.Controls 2.10
|
||||||
import QtQuick.Layouts 1.10
|
import QtQuick.Layouts 1.10
|
||||||
|
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
|
||||||
Pane {
|
Pane {
|
||||||
property string title: flow.sender ? qsTr("Send Device Verification Request") : qsTr("Recieved Device Verification Request")
|
property string title: flow.sender ? qsTr("Send Device Verification Request") : qsTr("Recieved Device Verification Request")
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
spacing: 16
|
spacing: 16
|
||||||
Label {
|
|
||||||
Layout.maximumWidth: 400
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.fillWidth: true
|
|
||||||
wrapMode: Text.Wrap
|
|
||||||
text: flow.sender ?
|
|
||||||
qsTr("To ensure that no malicious user can eavesdrop on your encrypted communications, you can verify this device.")
|
|
||||||
: qsTr("The device was requested to be verified")
|
|
||||||
color:colors.text
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
RowLayout {
|
|
||||||
Button {
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
|
||||||
text: flow.sender ? qsTr("Cancel") : qsTr("Deny")
|
|
||||||
|
|
||||||
onClicked: {
|
Label {
|
||||||
flow.cancel();
|
Layout.maximumWidth: 400
|
||||||
dialog.close();
|
Layout.fillHeight: true
|
||||||
}
|
Layout.fillWidth: true
|
||||||
}
|
wrapMode: Text.Wrap
|
||||||
Item {
|
text: flow.sender ? qsTr("To ensure that no malicious user can eavesdrop on your encrypted communications, you can verify this device.") : qsTr("The device was requested to be verified")
|
||||||
Layout.fillWidth: true
|
color: colors.text
|
||||||
}
|
verticalAlignment: Text.AlignVCenter
|
||||||
Button {
|
}
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
text: flow.sender ? qsTr("Start verification") : qsTr("Accept")
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
onClicked: flow.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,29 +3,36 @@ import QtQuick.Controls 2.10
|
||||||
import QtQuick.Layouts 1.10
|
import QtQuick.Layouts 1.10
|
||||||
|
|
||||||
Pane {
|
Pane {
|
||||||
property string title: qsTr("Successful Verification")
|
property string title: qsTr("Successful Verification")
|
||||||
ColumnLayout {
|
|
||||||
spacing: 16
|
ColumnLayout {
|
||||||
Label {
|
spacing: 16
|
||||||
Layout.maximumWidth: 400
|
|
||||||
Layout.fillHeight: true
|
Label {
|
||||||
Layout.fillWidth: true
|
id: content
|
||||||
wrapMode: Text.Wrap
|
|
||||||
id: content
|
Layout.maximumWidth: 400
|
||||||
text: qsTr("Verification successful! Both sides verified their devices!")
|
Layout.fillHeight: true
|
||||||
color:colors.text
|
Layout.fillWidth: true
|
||||||
verticalAlignment: Text.AlignVCenter
|
wrapMode: Text.Wrap
|
||||||
}
|
text: qsTr("Verification successful! Both sides verified their devices!")
|
||||||
RowLayout {
|
color: colors.text
|
||||||
Item {
|
verticalAlignment: Text.AlignVCenter
|
||||||
Layout.fillWidth: true
|
}
|
||||||
}
|
|
||||||
Button {
|
RowLayout {
|
||||||
Layout.alignment: Qt.AlignRight
|
Item {
|
||||||
text: qsTr("Close")
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
|
text: qsTr("Close")
|
||||||
|
onClicked: dialog.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
onClicked: dialog.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,45 +1,56 @@
|
||||||
import QtQuick 2.3
|
import QtQuick 2.3
|
||||||
import QtQuick.Controls 2.10
|
import QtQuick.Controls 2.10
|
||||||
import QtQuick.Layouts 1.10
|
import QtQuick.Layouts 1.10
|
||||||
|
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
|
||||||
Pane {
|
Pane {
|
||||||
property string title: qsTr("Waiting for other party")
|
property string title: qsTr("Waiting for other party")
|
||||||
ColumnLayout {
|
|
||||||
spacing: 16
|
|
||||||
Label {
|
|
||||||
Layout.maximumWidth: 400
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.fillWidth: true
|
|
||||||
wrapMode: Text.Wrap
|
|
||||||
id: content
|
|
||||||
text: switch (flow.state) {
|
|
||||||
case "WaitingForOtherToAccept": return qsTr("Waiting for other side to accept the verification request.")
|
|
||||||
case "WaitingForKeys": return qsTr("Waiting for other side to continue the verification request.")
|
|
||||||
case "WaitingForMac": return qsTr("Waiting for other side to complete the verification request.")
|
|
||||||
}
|
|
||||||
|
|
||||||
color: colors.text
|
ColumnLayout {
|
||||||
verticalAlignment: Text.AlignVCenter
|
spacing: 16
|
||||||
}
|
|
||||||
BusyIndicator {
|
Label {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
id: content
|
||||||
palette: colors
|
|
||||||
}
|
Layout.maximumWidth: 400
|
||||||
RowLayout {
|
Layout.fillHeight: true
|
||||||
Button {
|
Layout.fillWidth: true
|
||||||
Layout.alignment: Qt.AlignLeft
|
wrapMode: Text.Wrap
|
||||||
text: qsTr("Cancel")
|
text: {
|
||||||
|
switch (flow.state) {
|
||||||
|
case "WaitingForOtherToAccept":
|
||||||
|
return qsTr("Waiting for other side to accept the verification request.");
|
||||||
|
case "WaitingForKeys":
|
||||||
|
return qsTr("Waiting for other side to continue the verification request.");
|
||||||
|
case "WaitingForMac":
|
||||||
|
return qsTr("Waiting for other side to complete the verification request.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
color: colors.text
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
BusyIndicator {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
palette: colors
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Button {
|
||||||
|
Layout.alignment: Qt.AlignLeft
|
||||||
|
text: qsTr("Cancel")
|
||||||
|
onClicked: {
|
||||||
|
flow.cancel();
|
||||||
|
dialog.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
flow.cancel();
|
|
||||||
dialog.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Item {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,16 @@
|
||||||
|
import "../"
|
||||||
import QtQuick 2.10
|
import QtQuick 2.10
|
||||||
import QtQuick.Controls 2.1
|
import QtQuick.Controls 2.1
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
import im.nheko.EmojiModel 1.0
|
import im.nheko.EmojiModel 1.0
|
||||||
|
|
||||||
import "../"
|
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
|
id: emojiButton
|
||||||
|
|
||||||
property var colors: currentActivePalette
|
property var colors: currentActivePalette
|
||||||
property var emojiPicker
|
property var emojiPicker
|
||||||
property string event_id
|
property string event_id
|
||||||
|
|
||||||
image: ":/icons/icons/ui/smile.png"
|
image: ":/icons/icons/ui/smile.png"
|
||||||
id: emojiButton
|
|
||||||
onClicked: emojiPicker.visible ? emojiPicker.close() : emojiPicker.show(emojiButton, event_id)
|
onClicked: emojiPicker.visible ? emojiPicker.close() : emojiPicker.show(emojiButton, event_id)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,13 @@
|
||||||
|
import "../"
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts 1.3
|
||||||
import QtGraphicalEffects 1.0
|
|
||||||
|
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
import im.nheko.EmojiModel 1.0
|
import im.nheko.EmojiModel 1.0
|
||||||
|
|
||||||
import "../"
|
|
||||||
|
|
||||||
Popup {
|
Popup {
|
||||||
|
id: emojiPopup
|
||||||
function show(showAt, event_id) {
|
|
||||||
console.debug("Showing emojiPicker for " + event_id)
|
|
||||||
if (showAt){
|
|
||||||
parent = showAt
|
|
||||||
x = Math.round((showAt.width - width) / 2)
|
|
||||||
y = showAt.height
|
|
||||||
}
|
|
||||||
emojiPopup.event_id = event_id
|
|
||||||
open()
|
|
||||||
}
|
|
||||||
|
|
||||||
property string event_id
|
property string event_id
|
||||||
property var colors
|
property var colors
|
||||||
|
@ -30,19 +18,28 @@ Popup {
|
||||||
property real highlightSat: colors.highlight.hslSaturation
|
property real highlightSat: colors.highlight.hslSaturation
|
||||||
property real highlightLight: colors.highlight.hslLightness
|
property real highlightLight: colors.highlight.hslLightness
|
||||||
|
|
||||||
id: emojiPopup
|
function show(showAt, event_id) {
|
||||||
|
console.debug("Showing emojiPicker for " + event_id);
|
||||||
|
if (showAt) {
|
||||||
|
parent = showAt;
|
||||||
|
x = Math.round((showAt.width - width) / 2);
|
||||||
|
y = showAt.height;
|
||||||
|
}
|
||||||
|
emojiPopup.event_id = event_id;
|
||||||
|
open();
|
||||||
|
}
|
||||||
|
|
||||||
margins: 0
|
margins: 0
|
||||||
bottomPadding: 1
|
bottomPadding: 1
|
||||||
leftPadding: 1
|
leftPadding: 1
|
||||||
rightPadding: 1
|
rightPadding: 1
|
||||||
|
|
||||||
modal: true
|
modal: true
|
||||||
focus: true
|
focus: true
|
||||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: columnView
|
id: columnView
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
spacing: 0
|
spacing: 0
|
||||||
Layout.bottomMargin: 0
|
Layout.bottomMargin: 0
|
||||||
|
@ -58,23 +55,41 @@ Popup {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.leftMargin: 4
|
Layout.leftMargin: 4
|
||||||
|
|
||||||
cellWidth: 52
|
cellWidth: 52
|
||||||
cellHeight: 52
|
cellHeight: 52
|
||||||
|
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
// Individual emoji
|
// Individual emoji
|
||||||
delegate: AbstractButton {
|
delegate: AbstractButton {
|
||||||
width: 48
|
width: 48
|
||||||
height: 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 + "in response to " + emojiPopup.event_id);
|
||||||
|
emojiPopup.close();
|
||||||
|
TimelineManager.queueReactionMessage(emojiPopup.event_id, model.unicode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// give the emoji a little oomf
|
||||||
|
DropShadow {
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
horizontalOffset: 3
|
||||||
|
verticalOffset: 3
|
||||||
|
radius: 8
|
||||||
|
samples: 17
|
||||||
|
color: "#80000000"
|
||||||
|
source: parent.contentItem
|
||||||
|
}
|
||||||
|
|
||||||
contentItem: Text {
|
contentItem: Text {
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
font.family: Settings.emojiFont
|
font.family: Settings.emojiFont
|
||||||
|
|
||||||
font.pixelSize: 36
|
font.pixelSize: 36
|
||||||
text: model.unicode
|
text: model.unicode
|
||||||
}
|
}
|
||||||
|
@ -85,76 +100,66 @@ Popup {
|
||||||
radius: 5
|
radius: 5
|
||||||
}
|
}
|
||||||
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.text: model.shortName
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
|
|
||||||
// give the emoji a little oomf
|
|
||||||
DropShadow {
|
|
||||||
width: parent.width;
|
|
||||||
height: parent.height;
|
|
||||||
horizontalOffset: 3
|
|
||||||
verticalOffset: 3
|
|
||||||
radius: 8.0
|
|
||||||
samples: 17
|
|
||||||
color: "#80000000"
|
|
||||||
source: parent.contentItem
|
|
||||||
}
|
|
||||||
// TODO: maybe add favorites at some point?
|
|
||||||
onClicked: {
|
|
||||||
console.debug("Picked " + model.unicode + "in response to " + emojiPopup.event_id)
|
|
||||||
emojiPopup.close()
|
|
||||||
TimelineManager.queueReactionMessage(emojiPopup.event_id, model.unicode)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search field
|
// Search field
|
||||||
header: TextField {
|
header: TextField {
|
||||||
id: emojiSearch
|
id: emojiSearch
|
||||||
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.rightMargin: emojiScroll.width + 4
|
anchors.rightMargin: emojiScroll.width + 4
|
||||||
placeholderText: qsTr("Search")
|
placeholderText: qsTr("Search")
|
||||||
selectByMouse: true
|
selectByMouse: true
|
||||||
rightPadding: clearSearch.width
|
rightPadding: clearSearch.width
|
||||||
|
onTextChanged: searchTimer.restart()
|
||||||
|
onVisibleChanged: {
|
||||||
|
if (visible)
|
||||||
|
forceActiveFocus();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: searchTimer
|
id: searchTimer
|
||||||
|
|
||||||
interval: 350 // tweak as needed?
|
interval: 350 // tweak as needed?
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
emojiPopup.model.filter = emojiSearch.text
|
emojiPopup.model.filter = emojiSearch.text;
|
||||||
emojiPopup.model.category = EmojiCategory.Search
|
emojiPopup.model.category = EmojiCategory.Search;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ToolButton {
|
ToolButton {
|
||||||
id: clearSearch
|
id: clearSearch
|
||||||
|
|
||||||
|
visible: emojiSearch.text !== ''
|
||||||
|
icon.source: "image://colorimage/:/icons/icons/ui/round-remove-button.png?" + (clearSearch.hovered ? colors.highlight : colors.buttonText)
|
||||||
|
focusPolicy: Qt.NoFocus
|
||||||
|
onClicked: emojiSearch.clear()
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
verticalCenter: parent.verticalCenter
|
verticalCenter: parent.verticalCenter
|
||||||
right: parent.right
|
right: parent.right
|
||||||
}
|
}
|
||||||
// clear the default hover effects.
|
// clear the default hover effects.
|
||||||
background: Item {}
|
|
||||||
visible: emojiSearch.text !== ''
|
background: Item {
|
||||||
icon.source: "image://colorimage/:/icons/icons/ui/round-remove-button.png?" + (clearSearch.hovered ? colors.highlight : colors.buttonText)
|
}
|
||||||
focusPolicy: Qt.NoFocus
|
|
||||||
onClicked: emojiSearch.clear()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onTextChanged: searchTimer.restart()
|
|
||||||
onVisibleChanged: if (visible) forceActiveFocus()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollBar.vertical: ScrollBar {
|
ScrollBar.vertical: ScrollBar {
|
||||||
id: emojiScroll
|
id: emojiScroll
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Separator
|
// Separator
|
||||||
Rectangle {
|
Rectangle {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: 1
|
Layout.preferredHeight: 1
|
||||||
|
|
||||||
color: emojiPopup.colors.dark
|
color: emojiPopup.colors.dark
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,23 +169,90 @@ Popup {
|
||||||
Layout.preferredHeight: 42
|
Layout.preferredHeight: 42
|
||||||
implicitHeight: 42
|
implicitHeight: 42
|
||||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
||||||
|
|
||||||
// Display the normal categories
|
// Display the normal categories
|
||||||
Repeater {
|
Repeater {
|
||||||
|
|
||||||
model: ListModel {
|
model: ListModel {
|
||||||
// TODO: Would like to get 'simple' icons for the categories
|
// TODO: Would like to get 'simple' icons for the categories
|
||||||
ListElement { image: ":/icons/icons/emoji-categories/people.png"; category: EmojiCategory.People }
|
ListElement {
|
||||||
ListElement { image: ":/icons/icons/emoji-categories/nature.png"; category: EmojiCategory.Nature }
|
image: ":/icons/icons/emoji-categories/people.png"
|
||||||
ListElement { image: ":/icons/icons/emoji-categories/foods.png"; category: EmojiCategory.Food }
|
category: EmojiCategory.People
|
||||||
ListElement { image: ":/icons/icons/emoji-categories/activity.png"; category: EmojiCategory.Activity }
|
}
|
||||||
ListElement { image: ":/icons/icons/emoji-categories/travel.png"; category: EmojiCategory.Travel }
|
|
||||||
ListElement { image: ":/icons/icons/emoji-categories/objects.png"; category: EmojiCategory.Objects }
|
ListElement {
|
||||||
ListElement { image: ":/icons/icons/emoji-categories/symbols.png"; category: EmojiCategory.Symbols }
|
image: ":/icons/icons/emoji-categories/nature.png"
|
||||||
ListElement { image: ":/icons/icons/emoji-categories/flags.png"; category: EmojiCategory.Flags }
|
category: EmojiCategory.Nature
|
||||||
|
}
|
||||||
|
|
||||||
|
ListElement {
|
||||||
|
image: ":/icons/icons/emoji-categories/foods.png"
|
||||||
|
category: EmojiCategory.Food
|
||||||
|
}
|
||||||
|
|
||||||
|
ListElement {
|
||||||
|
image: ":/icons/icons/emoji-categories/activity.png"
|
||||||
|
category: EmojiCategory.Activity
|
||||||
|
}
|
||||||
|
|
||||||
|
ListElement {
|
||||||
|
image: ":/icons/icons/emoji-categories/travel.png"
|
||||||
|
category: EmojiCategory.Travel
|
||||||
|
}
|
||||||
|
|
||||||
|
ListElement {
|
||||||
|
image: ":/icons/icons/emoji-categories/objects.png"
|
||||||
|
category: EmojiCategory.Objects
|
||||||
|
}
|
||||||
|
|
||||||
|
ListElement {
|
||||||
|
image: ":/icons/icons/emoji-categories/symbols.png"
|
||||||
|
category: EmojiCategory.Symbols
|
||||||
|
}
|
||||||
|
|
||||||
|
ListElement {
|
||||||
|
image: ":/icons/icons/emoji-categories/flags.png"
|
||||||
|
category: EmojiCategory.Flags
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: AbstractButton {
|
delegate: AbstractButton {
|
||||||
Layout.preferredWidth: 36
|
Layout.preferredWidth: 36
|
||||||
Layout.preferredHeight: 36
|
Layout.preferredHeight: 36
|
||||||
|
hoverEnabled: true
|
||||||
|
ToolTip.text: {
|
||||||
|
switch (model.category) {
|
||||||
|
case EmojiCategory.People:
|
||||||
|
return qsTr('People');
|
||||||
|
case EmojiCategory.Nature:
|
||||||
|
return qsTr('Nature');
|
||||||
|
case EmojiCategory.Food:
|
||||||
|
return qsTr('Food');
|
||||||
|
case EmojiCategory.Activity:
|
||||||
|
return qsTr('Activity');
|
||||||
|
case EmojiCategory.Travel:
|
||||||
|
return qsTr('Travel');
|
||||||
|
case EmojiCategory.Objects:
|
||||||
|
return qsTr('Objects');
|
||||||
|
case EmojiCategory.Symbols:
|
||||||
|
return qsTr('Symbols');
|
||||||
|
case EmojiCategory.Flags:
|
||||||
|
return qsTr('Flags');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
onClicked: {
|
||||||
|
emojiPopup.model.category = model.category;
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
onPressed: mouse.accepted = false
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
}
|
||||||
|
|
||||||
contentItem: Image {
|
contentItem: Image {
|
||||||
horizontalAlignment: Image.AlignHCenter
|
horizontalAlignment: Image.AlignHCenter
|
||||||
|
@ -191,49 +263,15 @@ Popup {
|
||||||
source: "image://colorimage/" + model.image + "?" + (hovered ? colors.highlight : colors.buttonText)
|
source: "image://colorimage/" + model.image + "?" + (hovered ? colors.highlight : colors.buttonText)
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea
|
|
||||||
{
|
|
||||||
id: mouseArea
|
|
||||||
anchors.fill: parent
|
|
||||||
onPressed: mouse.accepted = false
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
}
|
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
color: emojiPopup.model.category === model.category ? Qt.hsla(highlightHue, highlightSat, highlightLight, 0.2) : 'transparent'
|
||||||
color: emojiPopup.model.category === model.category ? Qt.hsla(highlightHue, highlightSat, highlightLight, 0.20) : 'transparent'
|
|
||||||
radius: 5
|
radius: 5
|
||||||
border.color: emojiPopup.model.category === model.category ? colors.highlight : 'transparent'
|
border.color: emojiPopup.model.category === model.category ? colors.highlight : 'transparent'
|
||||||
}
|
}
|
||||||
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.text: {
|
|
||||||
switch (model.category) {
|
|
||||||
case EmojiCategory.People:
|
|
||||||
return qsTr('People');
|
|
||||||
case EmojiCategory.Nature:
|
|
||||||
return qsTr('Nature');
|
|
||||||
case EmojiCategory.Food:
|
|
||||||
return qsTr('Food');
|
|
||||||
case EmojiCategory.Activity:
|
|
||||||
return qsTr('Activity');
|
|
||||||
case EmojiCategory.Travel:
|
|
||||||
return qsTr('Travel');
|
|
||||||
case EmojiCategory.Objects:
|
|
||||||
return qsTr('Objects');
|
|
||||||
case EmojiCategory.Symbols:
|
|
||||||
return qsTr('Symbols');
|
|
||||||
case EmojiCategory.Flags:
|
|
||||||
return qsTr('Flags');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
emojiPopup.model.category = model.category
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Separator
|
// Separator
|
||||||
|
@ -242,30 +280,37 @@ Popup {
|
||||||
Layout.preferredWidth: 1
|
Layout.preferredWidth: 1
|
||||||
implicitWidth: 1
|
implicitWidth: 1
|
||||||
height: parent.height
|
height: parent.height
|
||||||
|
|
||||||
color: emojiPopup.colors.dark
|
color: emojiPopup.colors.dark
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search Button is special
|
// Search Button is special
|
||||||
AbstractButton {
|
AbstractButton {
|
||||||
id: searchBtn
|
id: searchBtn
|
||||||
|
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
Layout.bottomMargin: 0
|
Layout.bottomMargin: 0
|
||||||
|
|
||||||
ToolTip.text: qsTr("Search")
|
ToolTip.text: qsTr("Search")
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
onClicked: {
|
onClicked: {
|
||||||
// clear any filters
|
// clear any filters
|
||||||
emojiPopup.model.category = EmojiCategory.Search
|
emojiPopup.model.category = EmojiCategory.Search;
|
||||||
gridView.positionViewAtBeginning()
|
gridView.positionViewAtBeginning();
|
||||||
emojiSearch.forceActiveFocus()
|
emojiSearch.forceActiveFocus();
|
||||||
}
|
}
|
||||||
Layout.preferredWidth: 36
|
Layout.preferredWidth: 36
|
||||||
Layout.preferredHeight: 36
|
Layout.preferredHeight: 36
|
||||||
implicitWidth: 36
|
implicitWidth: 36
|
||||||
implicitHeight: 36
|
implicitHeight: 36
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
onPressed: mouse.accepted = false
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
}
|
||||||
|
|
||||||
contentItem: Image {
|
contentItem: Image {
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
horizontalAlignment: Image.AlignHCenter
|
horizontalAlignment: Image.AlignHCenter
|
||||||
|
@ -277,14 +322,10 @@ Popup {
|
||||||
source: "image://colorimage/:/icons/icons/ui/search.png?" + (parent.hovered ? colors.highlight : colors.buttonText)
|
source: "image://colorimage/:/icons/icons/ui/search.png?" + (parent.hovered ? colors.highlight : colors.buttonText)
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea
|
|
||||||
{
|
|
||||||
id: mouseArea
|
|
||||||
anchors.fill: parent
|
|
||||||
onPressed: mouse.accepted = false
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue