diff --git a/resources/qml/ActiveCallBar.qml b/resources/qml/ActiveCallBar.qml
index 61484625..9344738e 100644
--- a/resources/qml/ActiveCallBar.qml
+++ b/resources/qml/ActiveCallBar.qml
@@ -1,111 +1,113 @@
import QtQuick 2.9
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2
-
import im.nheko 1.0
Rectangle {
- id: activeCallBar
- visible: TimelineManager.callState != WebRTCState.DISCONNECTED
- color: "#2ECC71"
- implicitHeight: rowLayout.height + 8
-
- RowLayout {
- id: rowLayout
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.verticalCenter: parent.verticalCenter
- anchors.leftMargin: 8
-
- Avatar {
- width: avatarSize
- height: avatarSize
-
- url: TimelineManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/")
- displayName: TimelineManager.callPartyName
- }
-
- Label {
- font.pointSize: fontMetrics.font.pointSize * 1.1
- text: " " + TimelineManager.callPartyName + " "
- }
-
- Image {
- Layout.preferredWidth: 24
- Layout.preferredHeight: 24
- source: "qrc:/icons/icons/ui/place-call.png"
- }
-
- Label {
- id: callStateLabel
- font.pointSize: fontMetrics.font.pointSize * 1.1
- }
-
- Connections {
- target: TimelineManager
- 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 {
- 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) {
- return (n < 10) ? ("0" + n) : n
- }
- }
-
- 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
- }
- }
+ id: activeCallBar
+
+ visible: TimelineManager.callState != WebRTCState.DISCONNECTED
+ color: "#2ECC71"
+ implicitHeight: rowLayout.height + 8
+
+ RowLayout {
+ id: rowLayout
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.leftMargin: 8
+
+ Avatar {
+ width: avatarSize
+ height: avatarSize
+ url: TimelineManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/")
+ displayName: TimelineManager.callPartyName
+ }
+
+ Label {
+ font.pointSize: fontMetrics.font.pointSize * 1.1
+ text: " " + TimelineManager.callPartyName + " "
+ }
+
+ Image {
+ Layout.preferredWidth: 24
+ Layout.preferredHeight: 24
+ source: "qrc:/icons/icons/ui/place-call.png"
+ }
+
+ Label {
+ id: callStateLabel
+
+ font.pointSize: fontMetrics.font.pointSize * 1.1
+ }
+
+ Connections {
+ 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 = "";
+ }
+ }
+
+ target: TimelineManager
+ }
+
+ Timer {
+ id: callTimer
+
+ property int startTime
+
+ 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
+ }
+
+ }
+
}
diff --git a/resources/qml/Avatar.qml b/resources/qml/Avatar.qml
index df3dd08e..a247bffe 100644
--- a/resources/qml/Avatar.qml
+++ b/resources/qml/Avatar.qml
@@ -1,69 +1,75 @@
+import QtGraphicalEffects 1.0
import QtQuick 2.6
import QtQuick.Controls 2.3
-import QtGraphicalEffects 1.0
-
import im.nheko 1.0
Rectangle {
- id: avatar
- width: 48
- height: 48
- radius: Settings.avatarCircles ? height/2 : 3
+ id: avatar
+
+ property alias url: img.source
+ property string userid
+ property string displayName
- property alias url: img.source
- property string userid
- property string displayName
+ width: 48
+ height: 48
+ radius: Settings.avatarCircles ? height / 2 : 3
+ color: colors.base
- Label {
- anchors.fill: parent
- text: TimelineManager.escapeEmoji(displayName ? String.fromCodePoint(displayName.codePointAt(0)) : "")
- textFormat: Text.RichText
- font.pixelSize: avatar.height/2
- verticalAlignment: Text.AlignVCenter
- horizontalAlignment: Text.AlignHCenter
- visible: img.status != Image.Ready
- color: colors.text
- }
+ Label {
+ anchors.fill: parent
+ text: TimelineManager.escapeEmoji(displayName ? String.fromCodePoint(displayName.codePointAt(0)) : "")
+ textFormat: Text.RichText
+ font.pixelSize: avatar.height / 2
+ verticalAlignment: Text.AlignVCenter
+ horizontalAlignment: Text.AlignHCenter
+ visible: img.status != Image.Ready
+ color: colors.text
+ }
- Image {
- id: img
- anchors.fill: parent
- asynchronous: true
- fillMode: Image.PreserveAspectCrop
- mipmap: true
- smooth: false
+ Image {
+ id: img
- sourceSize.width: avatar.width
- sourceSize.height: avatar.height
+ anchors.fill: parent
+ asynchronous: true
+ fillMode: Image.PreserveAspectCrop
+ mipmap: true
+ smooth: false
+ sourceSize.width: avatar.width
+ sourceSize.height: avatar.height
+ layer.enabled: true
- layer.enabled: true
- layer.effect: OpacityMask {
- maskSource: Rectangle {
- anchors.fill: parent
- width: avatar.width
- height: avatar.height
- radius: Settings.avatarCircles ? height/2 : 3
- }
- }
+ layer.effect: OpacityMask {
- }
+ maskSource: Rectangle {
+ anchors.fill: parent
+ width: avatar.width
+ height: avatar.height
+ radius: Settings.avatarCircles ? height / 2 : 3
+ }
- Rectangle {
- anchors.bottom: avatar.bottom
- anchors.right: avatar.right
+ }
- visible: !!userid
+ }
- height: avatar.height / 6
- width: height
- radius: Settings.avatarCircles ? height / 2 : height / 4
- color: switch (TimelineManager.userPresence(userid)) {
- case "online": return "#00cc66"
- case "unavailable": return "#ff9933"
- case "offline": // return "#a82353" don't show anything if offline, since it is confusing, if presence is disabled
- default: "transparent"
- }
- }
+ Rectangle {
+ anchors.bottom: avatar.bottom
+ anchors.right: avatar.right
+ visible: !!userid
+ height: avatar.height / 6
+ 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
}
diff --git a/resources/qml/EncryptionIndicator.qml b/resources/qml/EncryptionIndicator.qml
index 428c2fae..46ca62c5 100644
--- a/resources/qml/EncryptionIndicator.qml
+++ b/resources/qml/EncryptionIndicator.qml
@@ -3,39 +3,42 @@ import QtQuick.Controls 2.1
import im.nheko 1.0
Rectangle {
- property bool encrypted: false
- id: indicator
- color: "transparent"
- width: 16
- height: 16
-
- ToolTip.visible: ma.containsMouse && indicator.visible
- ToolTip.text: getEncryptionTooltip()
-
- MouseArea{
- id: ma
- anchors.fill: parent
- hoverEnabled: true
- }
-
- Image {
- id: stateImg
- anchors.fill: parent
- source: getEncryptionImage()
- }
-
- function getEncryptionImage() {
- if (encrypted)
- return "image://colorimage/:/icons/icons/ui/lock.png?"+colors.buttonText
- else
- return "image://colorimage/:/icons/icons/ui/unlock.png?#dd3d3d"
- }
-
- function getEncryptionTooltip() {
- if (encrypted)
- return qsTr("Encrypted")
- else
- return qsTr("This message is not encrypted!")
- }
-}
+ id: indicator
+
+ property bool encrypted: false
+
+ function getEncryptionImage() {
+ if (encrypted)
+ return "image://colorimage/:/icons/icons/ui/lock.png?" + colors.buttonText;
+ else
+ return "image://colorimage/:/icons/icons/ui/unlock.png?#dd3d3d";
+ }
+
+ function getEncryptionTooltip() {
+ if (encrypted)
+ return qsTr("Encrypted");
+ else
+ return qsTr("This message is not encrypted!");
+ }
+
+ color: "transparent"
+ width: 16
+ height: 16
+ ToolTip.visible: ma.containsMouse && indicator.visible
+ ToolTip.text: getEncryptionTooltip()
+ MouseArea {
+ id: ma
+
+ anchors.fill: parent
+ hoverEnabled: true
+ }
+
+ Image {
+ id: stateImg
+
+ anchors.fill: parent
+ source: getEncryptionImage()
+ }
+
+}
diff --git a/resources/qml/ImageButton.qml b/resources/qml/ImageButton.qml
index 54399ae7..49ddf671 100644
--- a/resources/qml/ImageButton.qml
+++ b/resources/qml/ImageButton.qml
@@ -2,25 +2,29 @@ import QtQuick 2.3
import QtQuick.Controls 2.3
AbstractButton {
- property string image: undefined
- property color highlightColor: colors.highlight
- property color buttonTextColor: colors.buttonText
- width: 16
- height: 16
- id: button
-
- 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
- }
+ id: button
+
+ property string image: undefined
+ property color highlightColor: colors.highlight
+ property color buttonTextColor: colors.buttonText
+
+ 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
+ }
+
}
diff --git a/resources/qml/MatrixText.qml b/resources/qml/MatrixText.qml
index 29214168..6c96a539 100644
--- a/resources/qml/MatrixText.qml
+++ b/resources/qml/MatrixText.qml
@@ -1,35 +1,37 @@
import QtQuick 2.5
import QtQuick.Controls 2.3
-
import im.nheko 1.0
TextEdit {
- textFormat: TextEdit.RichText
- readOnly: true
- wrapMode: Text.Wrap
- selectByMouse: true
- activeFocusOnPress: false
- color: colors.text
+ textFormat: TextEdit.RichText
+ readOnly: true
+ wrapMode: Text.Wrap
+ selectByMouse: true
+ activeFocusOnPress: false
+ 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
+
+ MouseArea {
+ id: ma
- 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)
- }
- MouseArea
- {
- id: ma
- anchors.fill: parent
- propagateComposedEvents: true
- acceptedButtons: Qt.NoButton
- cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
- }
+ anchors.fill: parent
+ propagateComposedEvents: true
+ acceptedButtons: Qt.NoButton
+ cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
+ }
- ToolTip.visible: hoveredLink
- ToolTip.text: hoveredLink
}
diff --git a/resources/qml/Reactions.qml b/resources/qml/Reactions.qml
index ec46f7e0..6487f512 100644
--- a/resources/qml/Reactions.qml
+++ b/resources/qml/Reactions.qml
@@ -1,94 +1,95 @@
import QtQuick 2.6
import QtQuick.Controls 2.2
-
import im.nheko 1.0
// This class is for showing Reactions in the timeline row, not for
// adding new reactions via the emoji picker
Flow {
- id: reactionFlow
-
- // highlight colors for selfReactedEvent background
- property real highlightHue: colors.highlight.hslHue
- property real highlightSat: colors.highlight.hslSaturation
- property real highlightLight: colors.highlight.hslLightness
-
- property string eventId
-
- anchors.left: parent.left
- anchors.right: parent.right
- spacing: 4
-
- property alias reactions: repeater.model
-
- Repeater {
- id: repeater
-
- delegate: AbstractButton {
- id: reaction
- hoverEnabled: true
- 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)
- }
-
-
- contentItem: Row {
- anchors.centerIn: parent
- spacing: reactionText.implicitHeight/4
- leftPadding: reactionText.implicitHeight / 2
- rightPadding: reactionText.implicitHeight / 2
-
- TextMetrics {
- id: textMetrics
- font.family: Settings.emojiFont
- elide: Text.ElideRight
- elideWidth: 150
- text: modelData.key
- }
-
- Text {
- anchors.baseline: reactionCounter.baseline
- id: reactionText
- text: textMetrics.elidedText + (textMetrics.elidedText == modelData.key ? "" : "…")
- font.family: Settings.emojiFont
- color: reaction.hovered ? colors.highlight : colors.text
- maximumLineCount: 1
- }
-
- Rectangle {
- id: divider
- height: Math.floor(reactionCounter.implicitHeight * 1.4)
- width: 1
- color: (reaction.hovered || modelData.selfReactedEvent !== '') ? colors.highlight : colors.text
- }
-
- Text {
- anchors.verticalCenter: divider.verticalCenter
- id: reactionCounter
- text: modelData.count
- font: reaction.font
- color: reaction.hovered ? colors.highlight : colors.text
- }
- }
-
- 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.20) : colors.base
- border.width: 1
- radius: reaction.height / 2.0
- }
- }
- }
-}
+ id: reactionFlow
+
+ // highlight colors for selfReactedEvent background
+ property real highlightHue: colors.highlight.hslHue
+ property real highlightSat: colors.highlight.hslSaturation
+ property real highlightLight: colors.highlight.hslLightness
+ property string eventId
+ property alias reactions: repeater.model
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+ spacing: 4
+
+ Repeater {
+ id: repeater
+
+ delegate: AbstractButton {
+ id: reaction
+
+ hoverEnabled: true
+ implicitWidth: contentItem.childrenRect.width + contentItem.leftPadding * 2
+ implicitHeight: contentItem.childrenRect.height
+ ToolTip.visible: hovered
+ ToolTip.text: modelData.users
+ onClicked: {
+ console.debug("Picked " + modelData.key + "in response to " + reactionFlow.eventId + ". selfReactedEvent: " + modelData.selfReactedEvent);
+ TimelineManager.queueReactionMessage(reactionFlow.eventId, modelData.key);
+ }
+
+ contentItem: Row {
+ anchors.centerIn: parent
+ spacing: reactionText.implicitHeight / 4
+ leftPadding: reactionText.implicitHeight / 2
+ rightPadding: reactionText.implicitHeight / 2
+
+ TextMetrics {
+ id: textMetrics
+
+ font.family: Settings.emojiFont
+ elide: Text.ElideRight
+ elideWidth: 150
+ text: modelData.key
+ }
+
+ Text {
+ id: reactionText
+
+ anchors.baseline: reactionCounter.baseline
+ text: textMetrics.elidedText + (textMetrics.elidedText == modelData.key ? "" : "…")
+ font.family: Settings.emojiFont
+ color: reaction.hovered ? colors.highlight : colors.text
+ maximumLineCount: 1
+ }
+
+ Rectangle {
+ id: divider
+
+ height: Math.floor(reactionCounter.implicitHeight * 1.4)
+ width: 1
+ color: (reaction.hovered || modelData.selfReactedEvent !== '') ? colors.highlight : colors.text
+ }
+
+ Text {
+ id: reactionCounter
+
+ anchors.verticalCenter: divider.verticalCenter
+ text: modelData.count
+ font: reaction.font
+ color: reaction.hovered ? colors.highlight : colors.text
+ }
+
+ }
+
+ 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
+ }
+
+ }
+
+ }
+}
diff --git a/resources/qml/ScrollHelper.qml b/resources/qml/ScrollHelper.qml
index ba7c2648..7dc31464 100644
--- a/resources/qml/ScrollHelper.qml
+++ b/resources/qml/ScrollHelper.qml
@@ -16,10 +16,6 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-
-import QtQuick 2.9
-import QtQuick.Controls 2.3
-
/*
* Shamelessly stolen from:
* https://cgit.kde.org/kube.git/tree/framework/qml/ScrollHelper.qml
@@ -31,81 +27,82 @@ import QtQuick.Controls 2.3
* ScrollView.qml in qtquickcontrols
* qquickwheelarea.cpp in qtquickcontrols
*/
+
+import QtQuick 2.9
+import QtQuick.Controls 2.3
+
MouseArea {
+ // console.warn("Delta: ", wheel.pixelDelta.y);
+ // console.warn("Old position: ", flickable.contentY);
+ // console.warn("New position: ", newPos);
+
id: root
- propagateComposedEvents: true
property Flickable flickable
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) {
//Nothing to scroll
- if (flickableItem.contentHeight < flickableItem.height) {
+ if (flickableItem.contentHeight < flickableItem.height)
return flickableItem.contentY;
- }
+
//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;
- }
+
//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
if (wheel.angleDelta.y) {
- var wheelScrollLines = 3 //Default value of QApplication wheelScrollLines property
- 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.
- pixelDelta = ticks * pixelPerLine * wheelScrollLines
+ var wheelScrollLines = 3; //Default value of QApplication wheelScrollLines property
+ var pixelPerLine = 20; //Default value in Qt, originally comes from QTextEdit
+ var ticks = (wheel.angleDelta.y / 8) / 15; //Divide by 8 gives us pixels typically come in 15pixel steps.
+ pixelDelta = ticks * pixelPerLine * wheelScrollLines;
} else {
- pixelDelta = wheel.pixelDelta.y
+ pixelDelta = wheel.pixelDelta.y;
}
-
- pixelDelta = Math.round(pixelDelta)
-
- if (!pixelDelta) {
+ pixelDelta = Math.round(pixelDelta);
+ if (!pixelDelta)
return flickableItem.contentY;
- }
var minYExtent = flickableItem.originY + flickableItem.topMargin;
var maxYExtent = (flickableItem.contentHeight + flickableItem.bottomMargin + flickableItem.originY) - flickableItem.height;
-
- if (typeof(flickableItem.headerItem) !== "undefined" && flickableItem.headerItem) {
- minYExtent += flickableItem.headerItem.height
- }
+ if (typeof (flickableItem.headerItem) !== "undefined" && flickableItem.headerItem)
+ minYExtent += flickableItem.headerItem.height;
//Avoid overscrolling
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: {
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
flickable.flick(0, 0);
flickable.contentY = newPos;
- cancelFlickStateTimer.start()
+ cancelFlickStateTimer.start();
}
-
Timer {
id: cancelFlickStateTimer
+
//How long the scrollbar will remain visible
interval: 500
// Hide the scrollbars
- onTriggered: { flickable.cancelFlick(); flickable.movementEnded(); }
+ onTriggered: {
+ flickable.cancelFlick();
+ flickable.movementEnded();
+ }
}
+
}
diff --git a/resources/qml/StatusIndicator.qml b/resources/qml/StatusIndicator.qml
index ec82ed49..0b18b888 100644
--- a/resources/qml/StatusIndicator.qml
+++ b/resources/qml/StatusIndicator.qml
@@ -3,37 +3,55 @@ import QtQuick.Controls 2.1
import im.nheko 1.0
Rectangle {
- id: indicator
- property int state: 0
- color: "transparent"
- width: 16
- height: 16
-
- ToolTip.visible: ma.containsMouse && state != MtxEvent.Empty
- ToolTip.text: switch (state) {
- case MtxEvent.Failed: return qsTr("Failed")
- case MtxEvent.Sent: 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 ""
- }
- }
-}
+ id: indicator
+
+ property int state: 0
+
+ color: "transparent"
+ width: 16
+ height: 16
+ ToolTip.visible: ma.containsMouse && state != MtxEvent.Empty
+ ToolTip.text: {
+ switch (state) {
+ case MtxEvent.Failed:
+ return qsTr("Failed");
+ case MtxEvent.Sent:
+ 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 "";
+ }
+ }
+ }
+
+}
diff --git a/resources/qml/TimelineRow.qml b/resources/qml/TimelineRow.qml
index c026d828..38597673 100644
--- a/resources/qml/TimelineRow.qml
+++ b/resources/qml/TimelineRow.qml
@@ -1,146 +1,148 @@
+import "./delegates"
+import "./emoji"
import QtQuick 2.6
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2
import QtQuick.Window 2.2
-
import im.nheko 1.0
-import "./delegates"
-import "./emoji"
-
Item {
- anchors.left: parent.left
- anchors.right: parent.right
- height: row.height
-
- MouseArea {
- anchors.fill: parent
- propagateComposedEvents: true
- preventStealing: true
- hoverEnabled: true
-
- acceptedButtons: Qt.AllButtons
- onClicked: {
- if (mouse.button === Qt.RightButton)
- 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
- anchors.left: parent.left
- anchors.right: parent.right
-
-
- Column {
- Layout.fillWidth: true
- Layout.alignment: Qt.AlignTop
- spacing: 4
-
- // fancy reply, if this is a reply
- Reply {
- visible: model.replyTo
- modelData: chat.model.getDump(model.replyTo,model.id)
- userColor: TimelineManager.userColor(modelData.userId, colors.window)
- }
-
- // actual message content
- MessageDelegate {
- id: contentItem
-
- width: parent.width
-
- modelData: model
- }
-
- Reactions {
- id: reactionRow
- reactions: model.reactions
- eventId: model.id
- }
- }
-
- StatusIndicator {
- state: model.state
- 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
-
-
- image: ":/icons/icons/ui/mail-reply.png"
-
- ToolTip.visible: hovered
- ToolTip.text: qsTr("Reply")
-
- onClicked: chat.model.replyAction(model.id)
- }
- ImageButton {
- visible: Settings.buttonsInTimeline
- Layout.alignment: Qt.AlignRight | Qt.AlignTop
- Layout.preferredHeight: 16
- width: 16
- id: optionsButton
- 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)
- }
-
- Label {
- Layout.alignment: Qt.AlignRight | Qt.AlignTop
- text: model.timestamp.toLocaleTimeString("HH:mm")
- width: Math.max(implicitWidth, text.length*fontMetrics.maximumCharacterWidth)
- color: inactiveColors.text
-
- MouseArea{
- id: ma
- anchors.fill: parent
- hoverEnabled: true
- propagateComposedEvents: true
- }
-
- ToolTip.visible: ma.containsMouse
- ToolTip.text: Qt.formatDateTime(model.timestamp, Qt.DefaultLocaleLongDate)
- }
- }
+ anchors.left: parent.left
+ anchors.right: parent.right
+ height: row.height
+
+ MouseArea {
+ anchors.fill: parent
+ propagateComposedEvents: true
+ preventStealing: true
+ hoverEnabled: true
+ acceptedButtons: Qt.AllButtons
+ onClicked: {
+ if (mouse.button === Qt.RightButton)
+ 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
+ anchors.left: parent.left
+ anchors.right: parent.right
+
+ Column {
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignTop
+ spacing: 4
+
+ // fancy reply, if this is a reply
+ Reply {
+ visible: model.replyTo
+ modelData: chat.model.getDump(model.replyTo, model.id)
+ userColor: TimelineManager.userColor(modelData.userId, colors.window)
+ }
+
+ // actual message content
+ MessageDelegate {
+ id: contentItem
+
+ width: parent.width
+ modelData: model
+ }
+
+ Reactions {
+ id: reactionRow
+
+ reactions: model.reactions
+ eventId: model.id
+ }
+
+ }
+
+ StatusIndicator {
+ state: model.state
+ 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 {
+ id: reactButton
+
+ visible: Settings.buttonsInTimeline
+ Layout.alignment: Qt.AlignRight | Qt.AlignTop
+ Layout.preferredHeight: 16
+ width: 16
+ hoverEnabled: true
+ ToolTip.visible: hovered
+ ToolTip.text: qsTr("React")
+ emojiPicker: emojiPopup
+ event_id: model.id
+ }
+
+ ImageButton {
+ id: replyButton
+
+ visible: Settings.buttonsInTimeline
+ 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)
+ }
+
+ ImageButton {
+ id: optionsButton
+
+ visible: Settings.buttonsInTimeline
+ Layout.alignment: Qt.AlignRight | Qt.AlignTop
+ Layout.preferredHeight: 16
+ width: 16
+ 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)
+ }
+
+ Label {
+ Layout.alignment: Qt.AlignRight | Qt.AlignTop
+ text: model.timestamp.toLocaleTimeString("HH:mm")
+ width: Math.max(implicitWidth, text.length * fontMetrics.maximumCharacterWidth)
+ 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
+ }
+
+ }
+
+ }
+
}
diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml
index 5c9ca348..ab0148e9 100644
--- a/resources/qml/TimelineView.qml
+++ b/resources/qml/TimelineView.qml
@@ -1,526 +1,582 @@
+import "./delegates"
+import "./device-verification"
+import "./emoji"
+import QtGraphicalEffects 1.0
import QtQuick 2.9
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2
-import QtGraphicalEffects 1.0
import QtQuick.Window 2.2
-
import im.nheko 1.0
import im.nheko.EmojiModel 1.0
-import "./delegates"
-import "./emoji"
-import "./device-verification"
-
Page {
- id: timelineRoot
-
- property var colors: currentActivePalette
- property var systemInactive: SystemPalette { colorGroup: SystemPalette.Disabled }
- property var inactiveColors: currentInactivePalette ? currentInactivePalette : systemInactive
- property int avatarSize: 40
- property real highlightHue: colors.highlight.hslHue
- property real highlightSat: colors.highlight.hslSaturation
- property real highlightLight: colors.highlight.hslLightness
-
- palette: colors
-
- FontMetrics {
- id: fontMetrics
- }
-
- EmojiPicker {
- id: emojiPopup
- width: 7 * 52 + 20
- height: 6 * 52
- colors: palette
- model: EmojiProxyModel {
- category: EmojiCategory.People
- sourceModel: EmojiModel {}
- }
- }
-
- Menu {
- id: messageContextMenu
- modal: true
-
- function show(eventId_, eventType_, isEncrypted_, showAt_, position) {
- eventId = eventId_
- eventType = eventType_
- isEncrypted = isEncrypted_
-
- if (position)
- popup(position, showAt_)
- else
- popup(showAt_)
- }
-
- property string eventId
- property int eventType
- property bool isEncrypted
-
- MenuItem {
- text: qsTr("React")
- onClicked: emojiPopup.show(messageContextMenu.parent, messageContextMenu.eventId)
- }
- MenuItem {
- text: qsTr("Reply")
- onClicked: chat.model.replyAction(messageContextMenu.eventId)
- }
- MenuItem {
- text: qsTr("Read receipts")
- onTriggered: chat.model.readReceiptsAction(messageContextMenu.eventId)
- }
- MenuItem {
- text: qsTr("Mark as read")
- }
- MenuItem {
- text: qsTr("View raw message")
- onTriggered: chat.model.viewRawMessage(messageContextMenu.eventId)
- }
- MenuItem {
- visible: messageContextMenu.isEncrypted
- height: visible ? implicitHeight : 0
- text: qsTr("View decrypted raw message")
- onTriggered: chat.model.viewDecryptedRawMessage(messageContextMenu.eventId)
- }
- MenuItem {
- text: qsTr("Redact message")
- onTriggered: chat.model.redactEvent(messageContextMenu.eventId)
- }
- MenuItem {
- visible: messageContextMenu.eventType == MtxEvent.ImageMessage || messageContextMenu.eventType == MtxEvent.VideoMessage || messageContextMenu.eventType == MtxEvent.AudioMessage || messageContextMenu.eventType == MtxEvent.FileMessage || messageContextMenu.eventType == MtxEvent.Sticker
- height: visible ? implicitHeight : 0
- text: qsTr("Save as")
- onTriggered: TimelineManager.timeline.saveMedia(messageContextMenu.eventId)
- }
- }
-
- Rectangle {
- anchors.fill: parent
- color: colors.window
-
- Component {
- id: deviceVerificationDialog
- DeviceVerification {}
- }
- Connections {
- target: TimelineManager
- function onNewDeviceVerificationRequest(flow,transactionId,userId,deviceId,isRequest) {
- var dialog = deviceVerificationDialog.createObject(timelineRoot, {flow: flow});
- dialog.show();
- }
- }
- Connections {
- target: TimelineManager.timeline
- function onOpenProfile(profile) {
- var userProfile = userProfileComponent.createObject(timelineRoot,{profile: profile});
- userProfile.show();
- }
- }
-
- Label {
- visible: !TimelineManager.timeline && !TimelineManager.isInitialSync
- anchors.centerIn: parent
- text: qsTr("No room open")
- font.pointSize: 24
- color: colors.text
- }
-
- BusyIndicator {
- visible: running
- anchors.centerIn: parent
- running: TimelineManager.isInitialSync
- height: 200
- width: 200
- z: 3
- }
-
- ColumnLayout {
- anchors.fill: parent
- Rectangle {
- id: topBar
-
- Layout.fillWidth: true
- implicitHeight: topLayout.height + 16
- z: 3
-
- color: colors.base
-
- MouseArea {
- anchors.fill: parent
- onClicked: TimelineManager.openRoomSettings();
- }
-
- GridLayout {
- id: topLayout
-
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.margins: 8
- anchors.verticalCenter: parent.verticalCenter
-
- //Layout.margins: 8
-
- ImageButton {
- id: backToRoomsButton
-
- Layout.column: 0
- Layout.row: 0
- Layout.rowSpan: 2
- Layout.alignment: Qt.AlignVCenter
-
- visible: TimelineManager.isNarrowView
-
- image: ":/icons/icons/ui/angle-pointing-to-left.png"
-
- ToolTip.visible: hovered
- ToolTip.text: qsTr("Back to room list")
-
- onClicked: TimelineManager.backToRooms()
- }
-
- Avatar {
- Layout.column: 1
- Layout.row: 0
- Layout.rowSpan: 2
- Layout.alignment: Qt.AlignVCenter
-
- width: avatarSize
- height: avatarSize
-
- url: chat.model ? chat.model.roomAvatarUrl.replace("mxc://", "image://MxcImage/") : ""
- displayName: chat.model ? chat.model.roomName : qsTr("No room selected")
-
- MouseArea {
- anchors.fill: parent
- onClicked: TimelineManager.openRoomSettings();
- }
- }
-
- Label {
- Layout.fillWidth: true
- Layout.column: 2
- Layout.row: 0
- color: colors.text
-
- font.pointSize: fontMetrics.font.pointSize * 1.1
-
- text: chat.model ? chat.model.roomName : qsTr("No room selected")
-
- MouseArea {
- anchors.fill: parent
- onClicked: TimelineManager.openRoomSettings();
- }
- }
- MatrixText {
- Layout.fillWidth: true
- Layout.column: 2
- Layout.row: 1
- Layout.maximumHeight: fontMetrics.lineSpacing * 2 // show 2 lines
- clip: true
-
- text: chat.model ? chat.model.roomTopic : ""
- }
-
- ImageButton {
- id: roomOptionsButton
-
- Layout.column: 3
- Layout.row: 0
- Layout.rowSpan: 2
- Layout.alignment: Qt.AlignVCenter
-
- image: ":/icons/icons/ui/vertical-ellipsis.png"
-
- ToolTip.visible: hovered
- ToolTip.text: qsTr("Room options")
-
- onClicked: roomOptionsMenu.popup(roomOptionsButton)
-
- Menu {
- id: roomOptionsMenu
- MenuItem {
- text: qsTr("Invite users")
- onTriggered: TimelineManager.openInviteUsersDialog();
- }
- MenuItem {
- text: qsTr("Members")
- onTriggered: TimelineManager.openMemberListDialog();
- }
- MenuItem {
- text: qsTr("Leave room")
- onTriggered: TimelineManager.openLeaveRoomDialog();
- }
- MenuItem {
- text: qsTr("Settings")
- onTriggered: TimelineManager.openRoomSettings();
- }
- }
- }
- }
- }
-
- ListView {
- id: chat
-
- visible: TimelineManager.timeline != null
-
- cacheBuffer: 400
-
- Layout.fillWidth: true
- Layout.fillHeight: true
-
- model: TimelineManager.timeline
-
- boundsBehavior: Flickable.StopAtBounds
-
- ScrollHelper {
- flickable: parent
- anchors.fill: parent
- }
-
- pixelAligned: true
-
- Shortcut {
- sequence: StandardKey.MoveToPreviousPage
- onActivated: { chat.contentY = chat.contentY - chat.height / 2; chat.returnToBounds(); }
- }
- Shortcut {
- sequence: StandardKey.MoveToNextPage
- onActivated: { chat.contentY = chat.contentY + chat.height / 2; chat.returnToBounds(); }
- }
- Shortcut {
- sequence: StandardKey.Cancel
- onActivated: chat.model.reply = undefined
- }
- Shortcut {
- sequence: "Alt+Up"
- onActivated: chat.model.reply = chat.model.indexToId(chat.model.reply? chat.model.idToIndex(chat.model.reply) + 1 : 0)
- }
- Shortcut {
- sequence: "Alt+Down"
- onActivated: {
- var idx = chat.model.reply? chat.model.idToIndex(chat.model.reply) - 1 : -1
- chat.model.reply = idx >= 0 ? chat.model.indexToId(idx) : undefined
- }
- }
-
- ScrollBar.vertical: ScrollBar {
- id: scrollbar
- }
-
- spacing: 4
- verticalLayoutDirection: ListView.BottomToTop
-
- onCountChanged: if (atYEnd) model.currentIndex = 0 // Mark last event as read, since we are at the bottom
-
- property int delegateMaxWidth: (Settings.timelineMaxWidth > 100 && (parent.width - Settings.timelineMaxWidth) > scrollbar.width*2) ? Settings.timelineMaxWidth : (parent.width - scrollbar.width*2)
-
- delegate: Item {
- // This would normally be previousSection, but our model's order is inverted.
- property bool sectionBoundary: (ListView.nextSection != "" && ListView.nextSection !== ListView.section) || model.index === chat.count - 1
-
- id: wrapper
- property Item section
- anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined
- width: chat.delegateMaxWidth
- height: section ? section.height + timelinerow.height : timelinerow.height
-
- TimelineRow {
- id: timelinerow
- y: section ? section.y + section.height : 0
- }
-
- onSectionBoundaryChanged: {
- if (sectionBoundary) {
- var properties = {
- 'modelData': model.dump,
- 'section': ListView.section,
- 'nextSection': ListView.nextSection
- }
- section = sectionHeader.createObject(wrapper, properties)
- } else {
- section.destroy()
- section = null
- }
- }
-
- Connections {
- target: chat
- function onMovementEnded() {
- if (y + height + 2 * chat.spacing > chat.contentY + chat.height && y < chat.contentY + chat.height)
- chat.model.currentIndex = index;
- }
- }
- }
-
- Component{
- id: userProfileComponent
- UserProfile{}
- }
-
- section {
- property: "section"
- }
- Component {
- id: sectionHeader
- Column {
- property var modelData
- property string section
- property string nextSection
-
- topPadding: 4
- bottomPadding: 4
- spacing: 8
-
- visible: !!modelData
-
- width: parent.width
- height: (section.includes(" ") ? dateBubble.height + 8 + userName.height : userName.height) + 8
-
- Label {
- id: dateBubble
- anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined
- visible: section.includes(" ")
- text: chat.model.formatDateSeparator(modelData.timestamp)
- color: colors.text
-
- height: fontMetrics.height * 1.4
- width: contentWidth * 1.2
-
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignVCenter
- background: Rectangle {
- radius: parent.height / 2
- color: colors.base
- }
- }
-
- Row {
- height: userName.height
- spacing: 8
-
- Avatar {
- width: avatarSize
- height: avatarSize
- url: chat.model.avatarUrl(modelData.userId).replace("mxc://", "image://MxcImage/")
- displayName: modelData.userName
- userid: modelData.userId
-
- MouseArea {
- anchors.fill: parent
- onClicked: chat.model.openUserProfile(modelData.userId)
- cursorShape: Qt.PointingHandCursor
- propagateComposedEvents: true
- }
- }
-
- Label {
- id: userName
- text: TimelineManager.escapeEmoji(modelData.userName)
- color: TimelineManager.userColor(modelData.userId, colors.window)
- textFormat: Text.RichText
-
- MouseArea {
- anchors.fill: parent
- Layout.alignment: Qt.AlignHCenter
- onClicked: chat.model.openUserProfile(modelData.userId)
- cursorShape: Qt.PointingHandCursor
- propagateComposedEvents: true
- }
- }
- }
- }
- }
-
- footer: BusyIndicator {
- anchors.horizontalCenter: parent.horizontalCenter
- running: chat.model && chat.model.paginationInProgress
- height: 50
- width: 50
- z: 3
- }
- }
-
- Item {
- id: chatFooter
-
- implicitHeight: Math.max(fontMetrics.height * 1.2, footerContent.height)
- Layout.fillWidth: true
- z: 3
-
- Column {
- id: footerContent
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.bottom: parent.bottom
- Rectangle {
- id: typingRect
+ id: timelineRoot
+
+ property var colors: currentActivePalette
+ property var systemInactive
+ property var inactiveColors: currentInactivePalette ? currentInactivePalette : systemInactive
+ property int avatarSize: 40
+ property real highlightHue: colors.highlight.hslHue
+ property real highlightSat: colors.highlight.hslSaturation
+ property real highlightLight: colors.highlight.hslLightness
+
+ palette: colors
+
+ FontMetrics {
+ id: fontMetrics
+ }
+
+ EmojiPicker {
+ id: emojiPopup
+
+ width: 7 * 52 + 20
+ height: 6 * 52
+ colors: palette
+
+ model: EmojiProxyModel {
+ category: EmojiCategory.People
+
+ sourceModel: EmojiModel {
+ }
+
+ }
+
+ }
+
+ Menu {
+ id: messageContextMenu
+
+ property string eventId
+ property int eventType
+ property bool isEncrypted
+
+ function show(eventId_, eventType_, isEncrypted_, showAt_, position) {
+ eventId = eventId_;
+ eventType = eventType_;
+ isEncrypted = isEncrypted_;
+ if (position)
+ popup(position, showAt_);
+ else
+ popup(showAt_);
+ }
+
+ modal: true
+
+ MenuItem {
+ text: qsTr("React")
+ onClicked: emojiPopup.show(messageContextMenu.parent, messageContextMenu.eventId)
+ }
+
+ MenuItem {
+ text: qsTr("Reply")
+ onClicked: chat.model.replyAction(messageContextMenu.eventId)
+ }
+
+ MenuItem {
+ text: qsTr("Read receipts")
+ onTriggered: chat.model.readReceiptsAction(messageContextMenu.eventId)
+ }
+
+ MenuItem {
+ text: qsTr("Mark as read")
+ }
+
+ MenuItem {
+ text: qsTr("View raw message")
+ onTriggered: chat.model.viewRawMessage(messageContextMenu.eventId)
+ }
+
+ MenuItem {
+ visible: messageContextMenu.isEncrypted
+ height: visible ? implicitHeight : 0
+ text: qsTr("View decrypted raw message")
+ onTriggered: chat.model.viewDecryptedRawMessage(messageContextMenu.eventId)
+ }
+
+ MenuItem {
+ text: qsTr("Redact message")
+ onTriggered: chat.model.redactEvent(messageContextMenu.eventId)
+ }
+
+ MenuItem {
+ visible: messageContextMenu.eventType == MtxEvent.ImageMessage || messageContextMenu.eventType == MtxEvent.VideoMessage || messageContextMenu.eventType == MtxEvent.AudioMessage || messageContextMenu.eventType == MtxEvent.FileMessage || messageContextMenu.eventType == MtxEvent.Sticker
+ height: visible ? implicitHeight : 0
+ text: qsTr("Save as")
+ onTriggered: TimelineManager.timeline.saveMedia(messageContextMenu.eventId)
+ }
+
+ }
+
+ Rectangle {
+ anchors.fill: parent
+ color: colors.window
+
+ Component {
+ id: deviceVerificationDialog
+
+ DeviceVerification {
+ }
+
+ }
+
+ Connections {
+ function onNewDeviceVerificationRequest(flow, transactionId, userId, deviceId, isRequest) {
+ var dialog = deviceVerificationDialog.createObject(timelineRoot, {
+ "flow": flow
+ });
+ dialog.show();
+ }
+
+ target: TimelineManager
+ }
+
+ Connections {
+ function onOpenProfile(profile) {
+ var userProfile = userProfileComponent.createObject(timelineRoot, {
+ "profile": profile
+ });
+ userProfile.show();
+ }
+
+ target: TimelineManager.timeline
+ }
+
+ Label {
+ visible: !TimelineManager.timeline && !TimelineManager.isInitialSync
+ anchors.centerIn: parent
+ text: qsTr("No room open")
+ font.pointSize: 24
+ color: colors.text
+ }
+
+ BusyIndicator {
+ visible: running
+ anchors.centerIn: parent
+ running: TimelineManager.isInitialSync
+ height: 200
+ width: 200
+ z: 3
+ }
+
+ ColumnLayout {
+ anchors.fill: parent
+
+ Rectangle {
+ id: topBar
+
+ Layout.fillWidth: true
+ implicitHeight: topLayout.height + 16
+ z: 3
+ color: colors.base
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: TimelineManager.openRoomSettings()
+ }
+
+ GridLayout {
+ //Layout.margins: 8
+
+ id: topLayout
+
anchors.left: parent.left
anchors.right: parent.right
- color: (chat.model && chat.model.typingUsers.length > 0) ? colors.window : "transparent"
- height: typingDisplay.height
+ anchors.margins: 8
+ anchors.verticalCenter: parent.verticalCenter
+
+ ImageButton {
+ id: backToRoomsButton
+
+ Layout.column: 0
+ Layout.row: 0
+ Layout.rowSpan: 2
+ Layout.alignment: Qt.AlignVCenter
+ visible: TimelineManager.isNarrowView
+ image: ":/icons/icons/ui/angle-pointing-to-left.png"
+ ToolTip.visible: hovered
+ ToolTip.text: qsTr("Back to room list")
+ onClicked: TimelineManager.backToRooms()
+ }
+
+ Avatar {
+ Layout.column: 1
+ Layout.row: 0
+ Layout.rowSpan: 2
+ Layout.alignment: Qt.AlignVCenter
+ width: avatarSize
+ height: avatarSize
+ url: chat.model ? chat.model.roomAvatarUrl.replace("mxc://", "image://MxcImage/") : ""
+ displayName: chat.model ? chat.model.roomName : qsTr("No room selected")
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: TimelineManager.openRoomSettings()
+ }
+
+ }
+
Label {
- id: typingDisplay
+ Layout.fillWidth: true
+ Layout.column: 2
+ Layout.row: 0
+ color: colors.text
+ font.pointSize: fontMetrics.font.pointSize * 1.1
+ text: chat.model ? chat.model.roomName : qsTr("No room selected")
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: TimelineManager.openRoomSettings()
+ }
+
+ }
+
+ MatrixText {
+ Layout.fillWidth: true
+ Layout.column: 2
+ Layout.row: 1
+ Layout.maximumHeight: fontMetrics.lineSpacing * 2 // show 2 lines
+ clip: true
+ text: chat.model ? chat.model.roomTopic : ""
+ }
+
+ ImageButton {
+ id: roomOptionsButton
+
+ Layout.column: 3
+ Layout.row: 0
+ Layout.rowSpan: 2
+ Layout.alignment: Qt.AlignVCenter
+ image: ":/icons/icons/ui/vertical-ellipsis.png"
+ ToolTip.visible: hovered
+ ToolTip.text: qsTr("Room options")
+ onClicked: roomOptionsMenu.popup(roomOptionsButton)
+
+ Menu {
+ id: roomOptionsMenu
+
+ MenuItem {
+ text: qsTr("Invite users")
+ onTriggered: TimelineManager.openInviteUsersDialog()
+ }
+
+ MenuItem {
+ text: qsTr("Members")
+ onTriggered: TimelineManager.openMemberListDialog()
+ }
+
+ MenuItem {
+ text: qsTr("Leave room")
+ onTriggered: TimelineManager.openLeaveRoomDialog()
+ }
+
+ MenuItem {
+ text: qsTr("Settings")
+ onTriggered: TimelineManager.openRoomSettings()
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ ListView {
+ id: chat
+
+ property int delegateMaxWidth: (Settings.timelineMaxWidth > 100 && (parent.width - Settings.timelineMaxWidth) > scrollbar.width * 2) ? Settings.timelineMaxWidth : (parent.width - scrollbar.width * 2)
+
+ visible: TimelineManager.timeline != null
+ cacheBuffer: 400
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ model: TimelineManager.timeline
+ boundsBehavior: Flickable.StopAtBounds
+ pixelAligned: true
+ spacing: 4
+ verticalLayoutDirection: ListView.BottomToTop
+ onCountChanged: {
+ if (atYEnd)
+ model.currentIndex = 0;
+
+ } // Mark last event as read, since we are at the bottom
+
+ ScrollHelper {
+ flickable: parent
+ anchors.fill: parent
+ }
+
+ Shortcut {
+ sequence: StandardKey.MoveToPreviousPage
+ onActivated: {
+ chat.contentY = chat.contentY - chat.height / 2;
+ chat.returnToBounds();
+ }
+ }
+
+ Shortcut {
+ sequence: StandardKey.MoveToNextPage
+ onActivated: {
+ chat.contentY = chat.contentY + chat.height / 2;
+ chat.returnToBounds();
+ }
+ }
+
+ Shortcut {
+ sequence: StandardKey.Cancel
+ onActivated: chat.model.reply = undefined
+ }
+
+ Shortcut {
+ sequence: "Alt+Up"
+ onActivated: chat.model.reply = chat.model.indexToId(chat.model.reply ? chat.model.idToIndex(chat.model.reply) + 1 : 0)
+ }
+
+ Shortcut {
+ sequence: "Alt+Down"
+ onActivated: {
+ var idx = chat.model.reply ? chat.model.idToIndex(chat.model.reply) - 1 : -1;
+ chat.model.reply = idx >= 0 ? chat.model.indexToId(idx) : undefined;
+ }
+ }
+
+ Component {
+ id: userProfileComponent
+
+ UserProfile {
+ }
+
+ }
+
+ section {
+ property: "section"
+ }
+
+ Component {
+ id: sectionHeader
+
+ Column {
+ property var modelData
+ property string section
+ property string nextSection
+
+ topPadding: 4
+ bottomPadding: 4
+ spacing: 8
+ visible: !!modelData
+ width: parent.width
+ height: (section.includes(" ") ? dateBubble.height + 8 + userName.height : userName.height) + 8
+
+ Label {
+ id: dateBubble
+
+ anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined
+ visible: section.includes(" ")
+ text: chat.model.formatDateSeparator(modelData.timestamp)
+ color: colors.text
+ height: fontMetrics.height * 1.4
+ width: contentWidth * 1.2
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+
+ background: Rectangle {
+ radius: parent.height / 2
+ color: colors.base
+ }
+
+ }
+
+ Row {
+ height: userName.height
+ spacing: 8
+
+ Avatar {
+ width: avatarSize
+ height: avatarSize
+ url: chat.model.avatarUrl(modelData.userId).replace("mxc://", "image://MxcImage/")
+ displayName: modelData.userName
+ userid: modelData.userId
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: chat.model.openUserProfile(modelData.userId)
+ cursorShape: Qt.PointingHandCursor
+ propagateComposedEvents: true
+ }
+
+ }
+
+ Label {
+ id: userName
+
+ text: TimelineManager.escapeEmoji(modelData.userName)
+ color: TimelineManager.userColor(modelData.userId, colors.window)
+ textFormat: Text.RichText
+
+ MouseArea {
+ anchors.fill: parent
+ Layout.alignment: Qt.AlignHCenter
+ onClicked: chat.model.openUserProfile(modelData.userId)
+ cursorShape: Qt.PointingHandCursor
+ propagateComposedEvents: true
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ ScrollBar.vertical: ScrollBar {
+ id: scrollbar
+ }
+
+ delegate: Item {
+ id: wrapper
+
+ // This would normally be previousSection, but our model's order is inverted.
+ property bool sectionBoundary: (ListView.nextSection != "" && ListView.nextSection !== ListView.section) || model.index === chat.count - 1
+ property Item section
+
+ anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined
+ width: chat.delegateMaxWidth
+ height: section ? section.height + timelinerow.height : timelinerow.height
+ onSectionBoundaryChanged: {
+ if (sectionBoundary) {
+ var properties = {
+ "modelData": model.dump,
+ "section": ListView.section,
+ "nextSection": ListView.nextSection
+ };
+ section = sectionHeader.createObject(wrapper, properties);
+ } else {
+ section.destroy();
+ section = null;
+ }
+ }
+
+ TimelineRow {
+ id: timelinerow
+
+ y: section ? section.y + section.height : 0
+ }
+
+ Connections {
+ function onMovementEnded() {
+ if (y + height + 2 * chat.spacing > chat.contentY + chat.height && y < chat.contentY + chat.height)
+ chat.model.currentIndex = index;
+
+ }
+
+ target: chat
+ }
+
+ }
+
+ footer: BusyIndicator {
+ anchors.horizontalCenter: parent.horizontalCenter
+ running: chat.model && chat.model.paginationInProgress
+ height: 50
+ width: 50
+ z: 3
+ }
+
+ }
+
+ Item {
+ id: chatFooter
+
+ implicitHeight: Math.max(fontMetrics.height * 1.2, footerContent.height)
+ Layout.fillWidth: true
+ z: 3
+
+ Column {
+ id: footerContent
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+
+ Rectangle {
+ id: typingRect
+
anchors.left: parent.left
- anchors.leftMargin: 10
anchors.right: parent.right
- anchors.rightMargin: 10
- color: colors.text
- text: chat.model ? chat.model.formatTypingUsers(chat.model.typingUsers, colors.window) : ""
- textFormat: Text.RichText
+ color: (chat.model && chat.model.typingUsers.length > 0) ? colors.window : "transparent"
+ height: typingDisplay.height
+
+ Label {
+ id: typingDisplay
+
+ anchors.left: parent.left
+ anchors.leftMargin: 10
+ anchors.right: parent.right
+ anchors.rightMargin: 10
+ color: colors.text
+ text: chat.model ? chat.model.formatTypingUsers(chat.model.typingUsers, colors.window) : ""
+ textFormat: Text.RichText
+ }
+
+ }
+
+ Rectangle {
+ id: replyPopup
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+ visible: chat.model && chat.model.reply
+ // Height of child, plus margins, plus border
+ height: replyPreview.height + 10
+ color: colors.base
+
+ Reply {
+ id: replyPreview
+
+ anchors.left: parent.left
+ anchors.leftMargin: 10
+ anchors.right: closeReplyButton.left
+ anchors.rightMargin: 20
+ anchors.bottom: parent.bottom
+ modelData: chat.model ? chat.model.getDump(chat.model.reply, chat.model.id) : {
+ }
+ userColor: TimelineManager.userColor(modelData.userId, colors.window)
+ }
+
+ ImageButton {
+ id: closeReplyButton
+
+ anchors.right: parent.right
+ anchors.rightMargin: 15
+ anchors.top: replyPreview.top
+ hoverEnabled: true
+ width: 16
+ height: 16
+ image: ":/icons/icons/ui/remove-symbol.png"
+ ToolTip.visible: closeReplyButton.hovered
+ ToolTip.text: qsTr("Close")
+ onClicked: chat.model.reply = undefined
+ }
+
}
+
}
- Rectangle {
- anchors.left: parent.left
- anchors.right: parent.right
-
- id: replyPopup
-
- visible: chat.model && chat.model.reply
- // Height of child, plus margins, plus border
- height: replyPreview.height + 10
- color: colors.base
-
-
- Reply {
- id: replyPreview
-
- anchors.left: parent.left
- anchors.leftMargin: 10
- anchors.right: closeReplyButton.left
- anchors.rightMargin: 20
- anchors.bottom: parent.bottom
-
- modelData: chat.model ? chat.model.getDump(chat.model.reply, chat.model.id) : {}
- userColor: TimelineManager.userColor(modelData.userId, colors.window)
- }
-
- ImageButton {
- id: closeReplyButton
-
- anchors.right: parent.right
- anchors.rightMargin: 15
- anchors.top: replyPreview.top
- hoverEnabled: true
- width: 16
- height: 16
-
- image: ":/icons/icons/ui/remove-symbol.png"
- ToolTip.visible: closeReplyButton.hovered
- ToolTip.text: qsTr("Close")
-
- onClicked: chat.model.reply = undefined
- }
- }
- }
- }
-
- ActiveCallBar {
- Layout.fillWidth: true
- z: 3
- }
- }
- }
+ }
+
+ ActiveCallBar {
+ Layout.fillWidth: true
+ z: 3
+ }
+
+ }
+
+ }
+
+ systemInactive: SystemPalette {
+ colorGroup: SystemPalette.Disabled
+ }
+
}
diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml
index 562dd4f9..2e6758ae 100644
--- a/resources/qml/UserProfile.qml
+++ b/resources/qml/UserProfile.qml
@@ -1,172 +1,175 @@
+import "./device-verification"
import QtQuick 2.9
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2
import QtQuick.Window 2.3
-
import im.nheko 1.0
-import "./device-verification"
+ApplicationWindow {
+ id: userProfileDialog
+
+ property var profile
+
+ height: 650
+ width: 420
+ minimumHeight: 420
+ palette: colors
+
+ Component {
+ id: deviceVerificationDialog
+
+ DeviceVerification {
+ }
+
+ }
+
+ ColumnLayout {
+ id: contentL
+
+ anchors.fill: parent
+ anchors.margins: 10
+ spacing: 10
+
+ Avatar {
+ url: profile.avatarUrl.replace("mxc://", "image://MxcImage/")
+ height: 130
+ width: 130
+ displayName: profile.displayName
+ userid: profile.userid
+ Layout.alignment: Qt.AlignHCenter
+ }
+
+ Label {
+ text: profile.displayName
+ fontSizeMode: Text.HorizontalFit
+ font.pixelSize: 20
+ color: TimelineManager.userColor(profile.userid, colors.window)
+ font.bold: true
+ Layout.alignment: Qt.AlignHCenter
+ }
+
+ MatrixText {
+ text: profile.userid
+ font.pixelSize: 15
+ Layout.alignment: Qt.AlignHCenter
+ }
+
+ Button {
+ id: verifyUserButton
+
+ text: qsTr("Verify")
+ Layout.alignment: Qt.AlignHCenter
+ enabled: !profile.isUserVerified
+ visible: !profile.isUserVerified
+ onClicked: profile.verify()
+ }
+
+ RowLayout {
+ Layout.alignment: Qt.AlignHCenter
+ spacing: 8
+
+ ImageButton {
+ image: ":/icons/icons/ui/do-not-disturb-rounded-sign.png"
+ hoverEnabled: true
+ ToolTip.visible: hovered
+ ToolTip.text: qsTr("Ban the user")
+ onClicked: profile.banUser()
+ }
+ // 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 {
+ id: devicelist
+
+ Layout.fillHeight: true
+ Layout.minimumHeight: 200
+ Layout.fillWidth: true
+ clip: true
+ spacing: 8
+ boundsBehavior: Flickable.StopAtBounds
+ model: profile.deviceList
+
+ delegate: RowLayout {
+ width: devicelist.width
+ spacing: 4
+
+ ColumnLayout {
+ spacing: 0
+
+ Text {
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignLeft
+ elide: Text.ElideRight
+ font.bold: true
+ color: colors.text
+ text: model.deviceId
+ }
+
+ Text {
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignRight
+ elide: Text.ElideRight
+ color: colors.text
+ text: model.deviceName
+ }
+
+ }
+
+ Image {
+ 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()
+ }
-ApplicationWindow{
- property var profile
-
- id: userProfileDialog
- height: 650
- width: 420
- minimumHeight: 420
-
- palette: colors
-
- Component {
- id: deviceVerificationDialog
- DeviceVerification {}
- }
-
- ColumnLayout{
- id: contentL
-
- anchors.fill: parent
- anchors.margins: 10
-
- spacing: 10
-
- Avatar {
- url: profile.avatarUrl.replace("mxc://", "image://MxcImage/")
- height: 130
- width: 130
- displayName: profile.displayName
- userid: profile.userid
- Layout.alignment: Qt.AlignHCenter
- }
-
- Label {
- text: profile.displayName
- fontSizeMode: Text.HorizontalFit
- font.pixelSize: 20
- color: TimelineManager.userColor(profile.userid, colors.window)
- font.bold: true
- Layout.alignment: Qt.AlignHCenter
- }
-
- MatrixText {
- text: profile.userid
- font.pixelSize: 15
- Layout.alignment: Qt.AlignHCenter
- }
-
- Button {
- id: verifyUserButton
- text: qsTr("Verify")
- Layout.alignment: Qt.AlignHCenter
- enabled: !profile.isUserVerified
- visible: !profile.isUserVerified
-
- onClicked: profile.verify()
- }
-
- RowLayout {
- Layout.alignment: Qt.AlignHCenter
- spacing: 8
-
- ImageButton {
- image:":/icons/icons/ui/do-not-disturb-rounded-sign.png"
- hoverEnabled: true
- ToolTip.visible: hovered
- ToolTip.text: qsTr("Ban the user")
- onClicked: profile.banUser()
- }
- // 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{
- id: devicelist
-
- Layout.fillHeight: true
- Layout.minimumHeight: 200
- Layout.fillWidth: true
-
- clip: true
- spacing: 8
- boundsBehavior: Flickable.StopAtBounds
-
- model: profile.deviceList
-
- delegate: RowLayout{
- width: devicelist.width
- spacing: 4
-
- ColumnLayout{
- spacing: 0
- Text{
- Layout.fillWidth: true
- Layout.alignment: Qt.AlignLeft
-
- elide: Text.ElideRight
- font.bold: true
- color: colors.text
- text: model.deviceId
- }
- Text{
- Layout.fillWidth: true
- Layout.alignment: Qt.AlignRight
-
- elide: Text.ElideRight
- color: colors.text
- text: model.deviceName
- }
- }
-
- Image{
- 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()
- }
}
diff --git a/resources/qml/delegates/FileMessage.qml b/resources/qml/delegates/FileMessage.qml
index 7a2588f3..c6f213ee 100644
--- a/resources/qml/delegates/FileMessage.qml
+++ b/resources/qml/delegates/FileMessage.qml
@@ -1,68 +1,75 @@
import QtQuick 2.6
import QtQuick.Layouts 1.2
-
import im.nheko 1.0
Item {
- height: row.height + 24
- width: parent ? parent.width : undefined
-
- RowLayout {
- id: row
-
- anchors.centerIn: parent
- width: parent.width - 24
-
- spacing: 15
-
- Rectangle {
- id: button
- color: colors.light
- radius: 22
- height: 44
- width: 44
- Image {
- id: img
- anchors.centerIn: parent
-
- source: "qrc:/icons/icons/ui/arrow-pointing-down.png"
- fillMode: Image.Pad
-
- }
- MouseArea {
- anchors.fill: parent
- onClicked: TimelineManager.timeline.saveMedia(model.data.id)
- cursorShape: Qt.PointingHandCursor
- }
- }
- ColumnLayout {
- id: col
-
- 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))
- }
+ height: row.height + 24
+ width: parent ? parent.width : undefined
+
+ RowLayout {
+ id: row
+
+ anchors.centerIn: parent
+ width: parent.width - 24
+ spacing: 15
+
+ Rectangle {
+ id: button
+
+ color: colors.light
+ radius: 22
+ height: 44
+ width: 44
+
+ Image {
+ id: img
+
+ anchors.centerIn: parent
+ source: "qrc:/icons/icons/ui/arrow-pointing-down.png"
+ fillMode: Image.Pad
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: TimelineManager.timeline.saveMedia(model.data.id)
+ cursorShape: Qt.PointingHandCursor
+ }
+
+ }
+
+ ColumnLayout {
+ id: col
+
+ 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))
+ }
}
diff --git a/resources/qml/delegates/ImageMessage.qml b/resources/qml/delegates/ImageMessage.qml
index b5c51c2c..e2c78fbe 100644
--- a/resources/qml/delegates/ImageMessage.qml
+++ b/resources/qml/delegates/ImageMessage.qml
@@ -1,42 +1,41 @@
import QtQuick 2.6
-
import im.nheko 1.0
Item {
- 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 divisor: model.isReply ? 4 : 2
- property bool tooHigh: tempHeight > timelineRoot.height / divisor
-
- height: Math.round(tooHigh ? timelineRoot.height / divisor : tempHeight)
- width: Math.round(tooHigh ? (timelineRoot.height / divisor) / model.data.proportionalHeight : tempWidth)
-
- Image {
- id: blurhash
- anchors.fill: parent
- visible: img.status != Image.Ready
-
- source: model.data.blurhash ? ("image://blurhash/" + model.data.blurhash) : ("image://colorimage/:/icons/icons/ui/do-not-disturb-rounded-sign@2x.png?"+colors.buttonText)
- asynchronous: true
- fillMode: Image.PreserveAspectFit
-
- sourceSize.width: parent.width
- sourceSize.height: parent.height
- }
-
- Image {
- id: img
- anchors.fill: parent
-
- 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)
- }
- }
+ 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 divisor: model.isReply ? 4 : 2
+ property bool tooHigh: tempHeight > timelineRoot.height / divisor
+
+ height: Math.round(tooHigh ? timelineRoot.height / divisor : tempHeight)
+ width: Math.round(tooHigh ? (timelineRoot.height / divisor) / model.data.proportionalHeight : tempWidth)
+
+ Image {
+ id: blurhash
+
+ anchors.fill: parent
+ visible: img.status != Image.Ready
+ source: model.data.blurhash ? ("image://blurhash/" + model.data.blurhash) : ("image://colorimage/:/icons/icons/ui/do-not-disturb-rounded-sign@2x.png?" + colors.buttonText)
+ asynchronous: true
+ fillMode: Image.PreserveAspectFit
+ sourceSize.width: parent.width
+ sourceSize.height: parent.height
+ }
+
+ Image {
+ id: img
+
+ anchors.fill: parent
+ 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)
+ }
+
+ }
+
}
diff --git a/resources/qml/delegates/MessageDelegate.qml b/resources/qml/delegates/MessageDelegate.qml
index a5cb2ae7..cb5d8a95 100644
--- a/resources/qml/delegates/MessageDelegate.qml
+++ b/resources/qml/delegates/MessageDelegate.qml
@@ -2,215 +2,334 @@ import QtQuick 2.6
import im.nheko 1.0
Item {
- // Workaround to have an assignable global property
- Item {
- id: model
- property var data;
- property bool isReply: false
- }
-
- property alias modelData: model.data
- property alias isReply: model.isReply
-
- height: chooser.childrenRect.height
- property real implicitWidth: (chooser.child && chooser.child.implicitWidth) ? chooser.child.implicitWidth : width
-
- 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 {}
- }
- }
+ property alias modelData: model.data
+ property alias isReply: model.isReply
+ property real implicitWidth: (chooser.child && chooser.child.implicitWidth) ? chooser.child.implicitWidth : width
+
+ height: chooser.childrenRect.height
+
+ // Workaround to have an assignable global property
+ Item {
+ id: model
+
+ 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 {
+ }
+
+ }
+
+ }
+
}
diff --git a/resources/qml/delegates/NoticeMessage.qml b/resources/qml/delegates/NoticeMessage.qml
index be348329..d9a7a3e7 100644
--- a/resources/qml/delegates/NoticeMessage.qml
+++ b/resources/qml/delegates/NoticeMessage.qml
@@ -1,6 +1,6 @@
TextMessage {
- font.italic: true
- color: colors.buttonText
- height: isReply ? Math.min(chat.height / 8, implicitHeight) : undefined
- clip: true
+ font.italic: true
+ color: colors.buttonText
+ height: isReply ? Math.min(chat.height / 8, implicitHeight) : undefined
+ clip: true
}
diff --git a/resources/qml/delegates/Pill.qml b/resources/qml/delegates/Pill.qml
index 27985b58..4acf2bef 100644
--- a/resources/qml/delegates/Pill.qml
+++ b/resources/qml/delegates/Pill.qml
@@ -2,13 +2,14 @@ import QtQuick 2.5
import QtQuick.Controls 2.1
Label {
- color: colors.brightText
- horizontalAlignment: Text.AlignHCenter
+ color: colors.brightText
+ 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
- }
}
diff --git a/resources/qml/delegates/Placeholder.qml b/resources/qml/delegates/Placeholder.qml
index 26de2067..db023d8a 100644
--- a/resources/qml/delegates/Placeholder.qml
+++ b/resources/qml/delegates/Placeholder.qml
@@ -1,7 +1,7 @@
import ".."
MatrixText {
- text: qsTr("unimplemented event: ") + model.data.typeString
- width: parent ? parent.width : undefined
- color: inactiveColors.text
+ text: qsTr("unimplemented event: ") + model.data.typeString
+ width: parent ? parent.width : undefined
+ color: inactiveColors.text
}
diff --git a/resources/qml/delegates/PlayableMediaMessage.qml b/resources/qml/delegates/PlayableMediaMessage.qml
index 893325b6..9ad115c7 100644
--- a/resources/qml/delegates/PlayableMediaMessage.qml
+++ b/resources/qml/delegates/PlayableMediaMessage.qml
@@ -1,173 +1,216 @@
+import QtMultimedia 5.6
import QtQuick 2.6
-import QtQuick.Layouts 1.2
import QtQuick.Controls 2.1
-import QtMultimedia 5.6
-
+import QtQuick.Layouts 1.2
import im.nheko 1.0
Rectangle {
- id: bg
- radius: 10
- color: colors.dark
- height: Math.round(content.height + 24)
- width: parent ? parent.width : undefined
-
- Column {
- id: content
- width: parent.width - 24
- anchors.centerIn: parent
-
- Rectangle {
- id: videoContainer
- 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
- property bool tooHigh: tempHeight > timelineRoot.height / divisor
-
- height: tooHigh ? timelineRoot.height / divisor : tempHeight
- width: tooHigh ? (timelineRoot.height / divisor) / model.data.proportionalHeight : tempWidth
- Image {
- anchors.fill: parent
- source: model.data.thumbnailUrl.replace("mxc://", "image://MxcImage/")
- asynchronous: true
- fillMode: Image.PreserveAspectFit
-
- VideoOutput {
- anchors.fill: parent
- fillMode: VideoOutput.PreserveAspectFit
- source: media
- }
- }
- }
-
- RowLayout {
- width: parent.width
- Text {
- id: positionText
- text: "--:--:--"
- color: colors.text
- }
- Slider {
- Layout.fillWidth: true
- id: progress
- value: media.position
- from: 0
- to: media.duration
-
- onMoved: media.seek(value)
- //indeterminate: true
- function updatePositionTexts() {
- function formatTime(date) {
- var hh = date.getUTCHours();
- 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
- }
- 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
- 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)
- }
- }
-
- 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 }
- }
- ]
- }
- 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
- }
- }
- }
- }
-}
+ id: bg
+ radius: 10
+ color: colors.dark
+ height: Math.round(content.height + 24)
+ width: parent ? parent.width : undefined
+
+ Column {
+ id: content
+
+ width: parent.width - 24
+ anchors.centerIn: parent
+
+ Rectangle {
+ id: videoContainer
+
+ 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
+ property bool tooHigh: tempHeight > timelineRoot.height / divisor
+
+ visible: model.data.type == MtxEvent.VideoMessage
+ height: tooHigh ? timelineRoot.height / divisor : tempHeight
+ width: tooHigh ? (timelineRoot.height / divisor) / model.data.proportionalHeight : tempWidth
+
+ Image {
+ anchors.fill: parent
+ source: model.data.thumbnailUrl.replace("mxc://", "image://MxcImage/")
+ asynchronous: true
+ fillMode: Image.PreserveAspectFit
+
+ VideoOutput {
+ anchors.fill: parent
+ fillMode: VideoOutput.PreserveAspectFit
+ source: media
+ }
+
+ }
+
+ }
+
+ RowLayout {
+ width: parent.width
+
+ Text {
+ id: positionText
+
+ text: "--:--:--"
+ color: colors.text
+ }
+
+ Slider {
+ id: progress
+
+ //indeterminate: true
+ function updatePositionTexts() {
+ function formatTime(date) {
+ var hh = date.getUTCHours();
+ 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));
+ }
+
+ 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
+ }
+
+ }
+
+ }
+
+ }
+
+}
diff --git a/resources/qml/delegates/Reply.qml b/resources/qml/delegates/Reply.qml
index 43fc2814..1471dbdf 100644
--- a/resources/qml/delegates/Reply.qml
+++ b/resources/qml/delegates/Reply.qml
@@ -2,66 +2,71 @@ import QtQuick 2.6
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2
import QtQuick.Window 2.2
-
import im.nheko 1.0
Item {
- id: replyComponent
-
- property alias modelData: reply.modelData
- property color userColor: "red"
-
- width: parent.width
- height: replyContainer.height
-
- MouseArea {
- anchors.fill: parent
- preventStealing: true
- onClicked: chat.positionViewAtIndex(chat.model.idToIndex(modelData.id), ListView.Contain)
- cursorShape: Qt.PointingHandCursor
- }
-
- Rectangle {
- id: colorLine
-
- anchors.top: replyContainer.top
- anchors.bottom: replyContainer.bottom
- width: 4
-
- color: TimelineManager.userColor(reply.modelData.userId, colors.window)
- }
-
- Column {
- id: replyContainer
- anchors.left: colorLine.right
- anchors.leftMargin: 4
- width: parent.width - 8
-
- Text {
- id: userName
- text: TimelineManager.escapeEmoji(reply.modelData.userName)
- color: replyComponent.userColor
- textFormat: Text.RichText
-
- MouseArea {
- anchors.fill: parent
- onClicked: chat.model.openUserProfile(reply.modelData.userId)
- 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)
- }
+ id: replyComponent
+
+ property alias modelData: reply.modelData
+ property color userColor: "red"
+
+ width: parent.width
+ height: replyContainer.height
+
+ MouseArea {
+ anchors.fill: parent
+ preventStealing: true
+ onClicked: chat.positionViewAtIndex(chat.model.idToIndex(modelData.id), ListView.Contain)
+ cursorShape: Qt.PointingHandCursor
+ }
+
+ Rectangle {
+ id: colorLine
+
+ anchors.top: replyContainer.top
+ anchors.bottom: replyContainer.bottom
+ width: 4
+ color: TimelineManager.userColor(reply.modelData.userId, colors.window)
+ }
+
+ Column {
+ id: replyContainer
+
+ anchors.left: colorLine.right
+ anchors.leftMargin: 4
+ width: parent.width - 8
+
+ Text {
+ id: userName
+
+ text: TimelineManager.escapeEmoji(reply.modelData.userName)
+ color: replyComponent.userColor
+ textFormat: Text.RichText
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: chat.model.openUserProfile(reply.modelData.userId)
+ 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)
+ }
+
}
diff --git a/resources/qml/delegates/TextMessage.qml b/resources/qml/delegates/TextMessage.qml
index 99ff9329..69f2f0e3 100644
--- a/resources/qml/delegates/TextMessage.qml
+++ b/resources/qml/delegates/TextMessage.qml
@@ -1,12 +1,12 @@
import ".."
-
import im.nheko 1.0
MatrixText {
- 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
- height: isReply ? Math.round(Math.min(timelineRoot.height / 8, implicitHeight)) : undefined
- clip: true
- font.pointSize: (Settings.enlargeEmojiOnlyMessages && model.data.isOnlyEmoji > 0 && model.data.isOnlyEmoji < 4) ? Settings.fontSize * 3 : Settings.fontSize
+ 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
+ height: isReply ? Math.round(Math.min(timelineRoot.height / 8, implicitHeight)) : undefined
+ clip: true
+ font.pointSize: (Settings.enlargeEmojiOnlyMessages && model.data.isOnlyEmoji > 0 && model.data.isOnlyEmoji < 4) ? Settings.fontSize * 3 : Settings.fontSize
}
diff --git a/resources/qml/device-verification/AwaitingVerificationConfirmation.qml b/resources/qml/device-verification/AwaitingVerificationConfirmation.qml
index cd8ccfd9..42bd68df 100644
--- a/resources/qml/device-verification/AwaitingVerificationConfirmation.qml
+++ b/resources/qml/device-verification/AwaitingVerificationConfirmation.qml
@@ -1,39 +1,46 @@
import QtQuick 2.3
import QtQuick.Controls 2.10
import QtQuick.Layouts 1.10
-
import im.nheko 1.0
Pane {
- property string title: qsTr("Awaiting Confirmation")
- ColumnLayout {
- spacing: 16
- Label {
- Layout.maximumWidth: 400
- Layout.fillHeight: true
- Layout.fillWidth: true
- wrapMode: Text.Wrap
- id: content
- text: qsTr("Waiting for other side to complete verification.")
- color:colors.text
- verticalAlignment: Text.AlignVCenter
- }
- BusyIndicator {
- Layout.alignment: Qt.AlignHCenter
- }
- RowLayout {
- Button {
- Layout.alignment: Qt.AlignLeft
- text: qsTr("Cancel")
-
- onClicked: {
- flow.cancel();
- dialog.close();
- }
- }
- Item {
- Layout.fillWidth: true
- }
- }
- }
+ property string title: qsTr("Awaiting Confirmation")
+
+ ColumnLayout {
+ spacing: 16
+
+ Label {
+ id: content
+
+ Layout.maximumWidth: 400
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ wrapMode: Text.Wrap
+ text: qsTr("Waiting for other side to complete verification.")
+ color: colors.text
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ BusyIndicator {
+ Layout.alignment: Qt.AlignHCenter
+ }
+
+ RowLayout {
+ Button {
+ Layout.alignment: Qt.AlignLeft
+ text: qsTr("Cancel")
+ onClicked: {
+ flow.cancel();
+ dialog.close();
+ }
+ }
+
+ Item {
+ Layout.fillWidth: true
+ }
+
+ }
+
+ }
+
}
diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml
index 2e8f7504..5009cc3a 100644
--- a/resources/qml/device-verification/DeviceVerification.qml
+++ b/resources/qml/device-verification/DeviceVerification.qml
@@ -1,97 +1,144 @@
import QtQuick 2.10
import QtQuick.Controls 2.10
import QtQuick.Window 2.10
-
import im.nheko 1.0
ApplicationWindow {
- property var flow
-
- onClosing: TimelineManager.removeVerificationFlow(flow)
-
- title: stack.currentItem.title
- id: dialog
-
- flags: Qt.Dialog
-
- palette: colors
-
- height: stack.implicitHeight
- width: stack.implicitWidth
-
- StackView {
- id: stack
- initialItem: newVerificationRequest
- implicitWidth: currentItem.implicitWidth
- implicitHeight: currentItem.implicitHeight
- }
-
- Component{
- id: newVerificationRequest
- NewVerificationRequest {}
- }
-
- Component {
- id: waiting
- Waiting {}
- }
-
- Component {
- id: success
- Success {}
- }
-
- Component {
- id: failed
- Failed {}
- }
-
- Component {
- id: digitVerification
- DigitVerification {}
- }
-
- Component {
- id: emojiVerification
- EmojiVerification {}
- }
-
- Item {
- state: flow.state
-
- states: [
- State {
- name: "PromptStartVerification"
- 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); }
- }
- ]
-}
+ id: dialog
+
+ property var flow
+
+ onClosing: TimelineManager.removeVerificationFlow(flow)
+ title: stack.currentItem.title
+ flags: Qt.Dialog
+ palette: colors
+ height: stack.implicitHeight
+ width: stack.implicitWidth
+
+ StackView {
+ id: stack
+
+ initialItem: newVerificationRequest
+ implicitWidth: currentItem.implicitWidth
+ implicitHeight: currentItem.implicitHeight
+ }
+
+ Component {
+ id: newVerificationRequest
+
+ NewVerificationRequest {
+ }
+
+ }
+
+ Component {
+ id: waiting
+
+ Waiting {
+ }
+
+ }
+
+ Component {
+ id: success
+
+ Success {
+ }
+
+ }
+
+ Component {
+ id: failed
+
+ Failed {
+ }
+
+ }
+
+ Component {
+ id: digitVerification
+
+ DigitVerification {
+ }
+
+ }
+
+ Component {
+ id: emojiVerification
+
+ EmojiVerification {
+ }
+
+ }
+
+ Item {
+ state: flow.state
+ states: [
+ State {
+ name: "PromptStartVerification"
+
+ 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)
+ }
+
+ }
+ ]
+ }
+
}
diff --git a/resources/qml/device-verification/DigitVerification.qml b/resources/qml/device-verification/DigitVerification.qml
index ff878a50..11c32d26 100644
--- a/resources/qml/device-verification/DigitVerification.qml
+++ b/resources/qml/device-verification/DigitVerification.qml
@@ -1,60 +1,69 @@
import QtQuick 2.3
import QtQuick.Controls 2.10
import QtQuick.Layouts 1.10
-
import im.nheko 1.0
Pane {
- property string title: qsTr("Verification Code")
-
- ColumnLayout {
- 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: {
- flow.cancel();
- dialog.close();
- }
- }
- Item {
- Layout.fillWidth: true
- }
- Button {
- Layout.alignment: Qt.AlignRight
- text: qsTr("They match!")
-
- onClicked: flow.next();
- }
- }
- }
+ property string title: qsTr("Verification Code")
+
+ ColumnLayout {
+ 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: {
+ flow.cancel();
+ dialog.close();
+ }
+ }
+
+ Item {
+ Layout.fillWidth: true
+ }
+
+ Button {
+ Layout.alignment: Qt.AlignRight
+ text: qsTr("They match!")
+ onClicked: flow.next()
+ }
+
+ }
+
+ }
+
}
diff --git a/resources/qml/device-verification/EmojiElement.qml b/resources/qml/device-verification/EmojiElement.qml
index 7e364594..73ad1c9b 100644
--- a/resources/qml/device-verification/EmojiElement.qml
+++ b/resources/qml/device-verification/EmojiElement.qml
@@ -3,24 +3,31 @@ import QtQuick.Controls 2.10
import QtQuick.Layouts 1.10
Rectangle {
- color: "red"
- implicitHeight: Qt.application.font.pixelSize * 4
- implicitWidth: col.width
- height: Qt.application.font.pixelSize * 4
- width: col.width
- ColumnLayout {
- id: col
- anchors.bottom: parent.bottom
- property var emoji: emojis.mapping[Math.floor(Math.random()*64)]
- Label {
- height: font.pixelSize * 2
- Layout.alignment: Qt.AlignHCenter
- text: col.emoji.emoji
- font.pixelSize: Qt.application.font.pixelSize * 2
- }
- Label {
- Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
- text: col.emoji.description
- }
- }
+ color: "red"
+ implicitHeight: Qt.application.font.pixelSize * 4
+ implicitWidth: col.width
+ height: Qt.application.font.pixelSize * 4
+ width: col.width
+
+ ColumnLayout {
+ id: col
+
+ property var emoji: emojis.mapping[Math.floor(Math.random() * 64)]
+
+ anchors.bottom: parent.bottom
+
+ Label {
+ height: font.pixelSize * 2
+ Layout.alignment: Qt.AlignHCenter
+ text: col.emoji.emoji
+ font.pixelSize: Qt.application.font.pixelSize * 2
+ }
+
+ Label {
+ Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
+ text: col.emoji.description
+ }
+
+ }
+
}
diff --git a/resources/qml/device-verification/EmojiVerification.qml b/resources/qml/device-verification/EmojiVerification.qml
index ed7727aa..6ac340e4 100644
--- a/resources/qml/device-verification/EmojiVerification.qml
+++ b/resources/qml/device-verification/EmojiVerification.qml
@@ -1,140 +1,414 @@
import QtQuick 2.3
import QtQuick.Controls 2.10
import QtQuick.Layouts 1.10
-
import im.nheko 1.0
Pane {
- property string title: qsTr("Verification Code")
-
- ColumnLayout {
- 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: {
- flow.cancel();
- dialog.close();
- }
- }
- Item {
- Layout.fillWidth: true
- }
- Button {
- Layout.alignment: Qt.AlignRight
- text: qsTr("They match!")
-
- onClicked: flow.next()
- }
- }
- }
+ property string title: qsTr("Verification Code")
+
+ ColumnLayout {
+ 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 {
+ 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()
+ }
+
+ }
+
+ }
+
}
diff --git a/resources/qml/device-verification/Failed.qml b/resources/qml/device-verification/Failed.qml
index fcff7893..5f9a2859 100644
--- a/resources/qml/device-verification/Failed.qml
+++ b/resources/qml/device-verification/Failed.qml
@@ -1,44 +1,56 @@
import QtQuick 2.3
import QtQuick.Controls 2.10
import QtQuick.Layouts 1.10
-
import im.nheko 1.0
Pane {
- property string title: qsTr("Verification failed")
- ColumnLayout {
- spacing: 16
- Text {
- id: content
-
- Layout.maximumWidth: 400
- Layout.fillHeight: true
- Layout.fillWidth: true
-
- wrapMode: Text.Wrap
- text: switch (flow.error) {
- case DeviceVerificationFlow.UnknownMethod: return qsTr("Other client does not support our verification protocol.")
- case DeviceVerificationFlow.MismatchedCommitment:
- case DeviceVerificationFlow.MismatchedSAS:
- case DeviceVerificationFlow.KeyMismatch: return qsTr("Key mismatch detected!")
- case DeviceVerificationFlow.Timeout: return qsTr("Device verification timed out.")
- case DeviceVerificationFlow.User: return qsTr("Other party canceled the verification.")
- case DeviceVerificationFlow.OutOfOrder: 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()
- }
- }
- }
+ property string title: qsTr("Verification failed")
+
+ ColumnLayout {
+ spacing: 16
+
+ Text {
+ id: content
+
+ Layout.maximumWidth: 400
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ wrapMode: Text.Wrap
+ text: {
+ switch (flow.error) {
+ case DeviceVerificationFlow.UnknownMethod:
+ return qsTr("Other client does not support our verification protocol.");
+ case DeviceVerificationFlow.MismatchedCommitment:
+ case DeviceVerificationFlow.MismatchedSAS:
+ case DeviceVerificationFlow.KeyMismatch:
+ return qsTr("Key mismatch detected!");
+ case DeviceVerificationFlow.Timeout:
+ return qsTr("Device verification timed out.");
+ case DeviceVerificationFlow.User:
+ return qsTr("Other party canceled the verification.");
+ case DeviceVerificationFlow.OutOfOrder:
+ 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()
+ }
+
+ }
+
+ }
+
}
diff --git a/resources/qml/device-verification/NewVerificationRequest.qml b/resources/qml/device-verification/NewVerificationRequest.qml
index bd25bb90..d411ed47 100644
--- a/resources/qml/device-verification/NewVerificationRequest.qml
+++ b/resources/qml/device-verification/NewVerificationRequest.qml
@@ -1,44 +1,46 @@
import QtQuick 2.3
import QtQuick.Controls 2.10
import QtQuick.Layouts 1.10
-
import im.nheko 1.0
Pane {
- property string title: flow.sender ? qsTr("Send Device Verification Request") : qsTr("Recieved Device Verification Request")
-
- ColumnLayout {
- 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: {
- flow.cancel();
- dialog.close();
- }
- }
- Item {
- Layout.fillWidth: true
- }
- Button {
- Layout.alignment: Qt.AlignRight
- text: flow.sender ? qsTr("Start verification") : qsTr("Accept")
-
- onClicked: flow.next();
- }
- }
- }
+ property string title: flow.sender ? qsTr("Send Device Verification Request") : qsTr("Recieved Device Verification Request")
+
+ ColumnLayout {
+ 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: {
+ flow.cancel();
+ dialog.close();
+ }
+ }
+
+ Item {
+ Layout.fillWidth: true
+ }
+
+ Button {
+ Layout.alignment: Qt.AlignRight
+ text: flow.sender ? qsTr("Start verification") : qsTr("Accept")
+ onClicked: flow.next()
+ }
+
+ }
+
+ }
+
}
diff --git a/resources/qml/device-verification/Success.qml b/resources/qml/device-verification/Success.qml
index b17b293c..175f7524 100644
--- a/resources/qml/device-verification/Success.qml
+++ b/resources/qml/device-verification/Success.qml
@@ -3,29 +3,36 @@ import QtQuick.Controls 2.10
import QtQuick.Layouts 1.10
Pane {
- property string title: qsTr("Successful Verification")
- ColumnLayout {
- spacing: 16
- Label {
- Layout.maximumWidth: 400
- Layout.fillHeight: true
- Layout.fillWidth: true
- wrapMode: Text.Wrap
- id: content
- text: qsTr("Verification successful! Both sides verified their devices!")
- color:colors.text
- verticalAlignment: Text.AlignVCenter
- }
- RowLayout {
- Item {
- Layout.fillWidth: true
- }
- Button {
- Layout.alignment: Qt.AlignRight
- text: qsTr("Close")
-
- onClicked: dialog.close();
- }
- }
- }
+ property string title: qsTr("Successful Verification")
+
+ ColumnLayout {
+ spacing: 16
+
+ Label {
+ id: content
+
+ Layout.maximumWidth: 400
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ wrapMode: Text.Wrap
+ text: qsTr("Verification successful! Both sides verified their devices!")
+ color: colors.text
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ RowLayout {
+ Item {
+ Layout.fillWidth: true
+ }
+
+ Button {
+ Layout.alignment: Qt.AlignRight
+ text: qsTr("Close")
+ onClicked: dialog.close()
+ }
+
+ }
+
+ }
+
}
diff --git a/resources/qml/device-verification/Waiting.qml b/resources/qml/device-verification/Waiting.qml
index 38abf767..0c4ae405 100644
--- a/resources/qml/device-verification/Waiting.qml
+++ b/resources/qml/device-verification/Waiting.qml
@@ -1,45 +1,56 @@
import QtQuick 2.3
import QtQuick.Controls 2.10
import QtQuick.Layouts 1.10
-
import im.nheko 1.0
Pane {
- 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
- 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
- }
- }
- }
+ property string title: qsTr("Waiting for other party")
+
+ ColumnLayout {
+ spacing: 16
+
+ Label {
+ id: content
+
+ Layout.maximumWidth: 400
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ wrapMode: Text.Wrap
+ 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
+ }
+
+ }
+
+ }
+
}
diff --git a/resources/qml/emoji/EmojiButton.qml b/resources/qml/emoji/EmojiButton.qml
index c5eee4e4..1fcfc0c5 100644
--- a/resources/qml/emoji/EmojiButton.qml
+++ b/resources/qml/emoji/EmojiButton.qml
@@ -1,17 +1,16 @@
+import "../"
import QtQuick 2.10
import QtQuick.Controls 2.1
import im.nheko 1.0
import im.nheko.EmojiModel 1.0
-import "../"
-
ImageButton {
+ id: emojiButton
+
property var colors: currentActivePalette
property var emojiPicker
property string event_id
image: ":/icons/icons/ui/smile.png"
- id: emojiButton
onClicked: emojiPicker.visible ? emojiPicker.close() : emojiPicker.show(emojiButton, event_id)
-
}
diff --git a/resources/qml/emoji/EmojiPicker.qml b/resources/qml/emoji/EmojiPicker.qml
index cbb77beb..3a5ee57a 100644
--- a/resources/qml/emoji/EmojiPicker.qml
+++ b/resources/qml/emoji/EmojiPicker.qml
@@ -1,25 +1,13 @@
+import "../"
+import QtGraphicalEffects 1.0
import QtQuick 2.9
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.3
-import QtGraphicalEffects 1.0
-
import im.nheko 1.0
import im.nheko.EmojiModel 1.0
-import "../"
-
Popup {
-
- 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()
- }
+ id: emojiPopup
property string event_id
property var colors
@@ -30,19 +18,28 @@ Popup {
property real highlightSat: colors.highlight.hslSaturation
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
bottomPadding: 1
leftPadding: 1
rightPadding: 1
-
modal: true
focus: true
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
ColumnLayout {
id: columnView
+
anchors.fill: parent
spacing: 0
Layout.bottomMargin: 0
@@ -58,23 +55,41 @@ Popup {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.leftMargin: 4
-
cellWidth: 52
cellHeight: 52
-
boundsBehavior: Flickable.StopAtBounds
-
clip: true
// Individual emoji
delegate: AbstractButton {
width: 48
height: 48
+ hoverEnabled: true
+ ToolTip.text: model.shortName
+ ToolTip.visible: hovered
+ // TODO: maybe add favorites at some point?
+ onClicked: {
+ console.debug("Picked " + model.unicode + "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 {
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.family: Settings.emojiFont
-
font.pixelSize: 36
text: model.unicode
}
@@ -85,76 +100,66 @@ Popup {
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
header: TextField {
id: emojiSearch
+
anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: emojiScroll.width + 4
placeholderText: qsTr("Search")
selectByMouse: true
rightPadding: clearSearch.width
+ onTextChanged: searchTimer.restart()
+ onVisibleChanged: {
+ if (visible)
+ forceActiveFocus();
+
+ }
Timer {
id: searchTimer
+
interval: 350 // tweak as needed?
onTriggered: {
- emojiPopup.model.filter = emojiSearch.text
- emojiPopup.model.category = EmojiCategory.Search
+ emojiPopup.model.filter = emojiSearch.text;
+ emojiPopup.model.category = EmojiCategory.Search;
}
}
ToolButton {
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 {
verticalCenter: parent.verticalCenter
right: parent.right
}
// clear the default hover effects.
- background: Item {}
- 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()
+
+ background: Item {
+ }
+
}
- onTextChanged: searchTimer.restart()
- onVisibleChanged: if (visible) forceActiveFocus()
}
- ScrollBar.vertical: ScrollBar {
- id: emojiScroll
- }
+ ScrollBar.vertical: ScrollBar {
+ id: emojiScroll
+ }
+
}
// Separator
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
-
color: emojiPopup.colors.dark
}
@@ -164,23 +169,90 @@ Popup {
Layout.preferredHeight: 42
implicitHeight: 42
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
+
// Display the normal categories
Repeater {
+
model: ListModel {
// TODO: Would like to get 'simple' icons for the categories
- ListElement { image: ":/icons/icons/emoji-categories/people.png"; category: EmojiCategory.People }
- ListElement { image: ":/icons/icons/emoji-categories/nature.png"; 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 }
+ ListElement {
+ image: ":/icons/icons/emoji-categories/people.png"
+ category: EmojiCategory.People
+ }
+
+ ListElement {
+ image: ":/icons/icons/emoji-categories/nature.png"
+ 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 {
Layout.preferredWidth: 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 {
horizontalAlignment: Image.AlignHCenter
@@ -191,49 +263,15 @@ Popup {
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 {
anchors.fill: parent
-
- color: emojiPopup.model.category === model.category ? Qt.hsla(highlightHue, highlightSat, highlightLight, 0.20) : 'transparent'
+ color: emojiPopup.model.category === model.category ? Qt.hsla(highlightHue, highlightSat, highlightLight, 0.2) : 'transparent'
radius: 5
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
@@ -242,30 +280,37 @@ Popup {
Layout.preferredWidth: 1
implicitWidth: 1
height: parent.height
-
color: emojiPopup.colors.dark
}
// Search Button is special
AbstractButton {
id: searchBtn
+
hoverEnabled: true
Layout.alignment: Qt.AlignRight
Layout.bottomMargin: 0
-
ToolTip.text: qsTr("Search")
ToolTip.visible: hovered
onClicked: {
// clear any filters
- emojiPopup.model.category = EmojiCategory.Search
- gridView.positionViewAtBeginning()
- emojiSearch.forceActiveFocus()
+ emojiPopup.model.category = EmojiCategory.Search;
+ gridView.positionViewAtBeginning();
+ emojiSearch.forceActiveFocus();
}
Layout.preferredWidth: 36
Layout.preferredHeight: 36
implicitWidth: 36
implicitHeight: 36
+ MouseArea {
+ id: mouseArea
+
+ anchors.fill: parent
+ onPressed: mouse.accepted = false
+ cursorShape: Qt.PointingHandCursor
+ }
+
contentItem: Image {
anchors.right: parent.right
horizontalAlignment: Image.AlignHCenter
@@ -277,14 +322,10 @@ Popup {
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
- }
}
+
}
+
}
+
}
|