Lint qml with qml-format

This commit is contained in:
Nicolas Werner 2020-10-08 21:11:21 +02:00
parent 517a126a44
commit 1a029112d9
31 changed files with 2863 additions and 2179 deletions

View file

@ -1,111 +1,113 @@
import QtQuick 2.9 import QtQuick 2.9
import QtQuick.Controls 2.3 import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2 import QtQuick.Layouts 1.2
import im.nheko 1.0 import im.nheko 1.0
Rectangle { Rectangle {
id: activeCallBar id: activeCallBar
visible: TimelineManager.callState != WebRTCState.DISCONNECTED
color: "#2ECC71"
implicitHeight: rowLayout.height + 8
RowLayout { visible: TimelineManager.callState != WebRTCState.DISCONNECTED
id: rowLayout color: "#2ECC71"
anchors.left: parent.left implicitHeight: rowLayout.height + 8
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: 8
Avatar { RowLayout {
width: avatarSize id: rowLayout
height: avatarSize
url: TimelineManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/") anchors.left: parent.left
displayName: TimelineManager.callPartyName anchors.right: parent.right
} anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: 8
Label { Avatar {
font.pointSize: fontMetrics.font.pointSize * 1.1 width: avatarSize
text: " " + TimelineManager.callPartyName + " " height: avatarSize
} url: TimelineManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/")
displayName: TimelineManager.callPartyName
}
Image { Label {
Layout.preferredWidth: 24 font.pointSize: fontMetrics.font.pointSize * 1.1
Layout.preferredHeight: 24 text: " " + TimelineManager.callPartyName + " "
source: "qrc:/icons/icons/ui/place-call.png" }
}
Label { Image {
id: callStateLabel Layout.preferredWidth: 24
font.pointSize: fontMetrics.font.pointSize * 1.1 Layout.preferredHeight: 24
} source: "qrc:/icons/icons/ui/place-call.png"
}
Connections { Label {
target: TimelineManager id: callStateLabel
function onCallStateChanged(state) {
switch (state) {
case WebRTCState.INITIATING:
callStateLabel.text = qsTr("Initiating...")
break;
case WebRTCState.OFFERSENT:
callStateLabel.text = qsTr("Calling...")
break;
case WebRTCState.CONNECTING:
callStateLabel.text = qsTr("Connecting...")
break;
case WebRTCState.CONNECTED:
callStateLabel.text = "00:00"
var d = new Date()
callTimer.startTime = Math.floor(d.getTime() / 1000)
break;
case WebRTCState.DISCONNECTED:
callStateLabel.text = ""
}
}
}
Timer { font.pointSize: fontMetrics.font.pointSize * 1.1
id: callTimer }
property int startTime
interval: 1000
running: TimelineManager.callState == WebRTCState.CONNECTED
repeat: true
onTriggered: {
var d = new Date()
let seconds = Math.floor(d.getTime() / 1000 - startTime)
let s = Math.floor(seconds % 60)
let m = Math.floor(seconds / 60) % 60
let h = Math.floor(seconds / 3600)
callStateLabel.text = (h ? (pad(h) + ":") : "") + pad(m) + ":" + pad(s)
}
function pad(n) { Connections {
return (n < 10) ? ("0" + n) : n function onCallStateChanged(state) {
} switch (state) {
} case WebRTCState.INITIATING:
callStateLabel.text = qsTr("Initiating...");
break;
case WebRTCState.OFFERSENT:
callStateLabel.text = qsTr("Calling...");
break;
case WebRTCState.CONNECTING:
callStateLabel.text = qsTr("Connecting...");
break;
case WebRTCState.CONNECTED:
callStateLabel.text = "00:00";
var d = new Date();
callTimer.startTime = Math.floor(d.getTime() / 1000);
break;
case WebRTCState.DISCONNECTED:
callStateLabel.text = "";
}
}
Item { target: TimelineManager
Layout.fillWidth: true }
}
ImageButton { Timer {
width: 24 id: callTimer
height: 24
buttonTextColor: "#000000"
image: TimelineManager.isMicMuted ?
":/icons/icons/ui/microphone-unmute.png" :
":/icons/icons/ui/microphone-mute.png"
hoverEnabled: true property int startTime
ToolTip.visible: hovered
ToolTip.text: TimelineManager.isMicMuted ? qsTr("Unmute Mic") : qsTr("Mute Mic")
onClicked: TimelineManager.toggleMicMute() function pad(n) {
} return (n < 10) ? ("0" + n) : n;
}
interval: 1000
running: TimelineManager.callState == WebRTCState.CONNECTED
repeat: true
onTriggered: {
var d = new Date();
let seconds = Math.floor(d.getTime() / 1000 - startTime);
let s = Math.floor(seconds % 60);
let m = Math.floor(seconds / 60) % 60;
let h = Math.floor(seconds / 3600);
callStateLabel.text = (h ? (pad(h) + ":") : "") + pad(m) + ":" + pad(s);
}
}
Item {
Layout.fillWidth: true
}
ImageButton {
width: 24
height: 24
buttonTextColor: "#000000"
image: TimelineManager.isMicMuted ? ":/icons/icons/ui/microphone-unmute.png" : ":/icons/icons/ui/microphone-mute.png"
hoverEnabled: true
ToolTip.visible: hovered
ToolTip.text: TimelineManager.isMicMuted ? qsTr("Unmute Mic") : qsTr("Mute Mic")
onClicked: TimelineManager.toggleMicMute()
}
Item {
implicitWidth: 16
}
}
Item {
implicitWidth: 16
}
}
} }

View file

@ -1,69 +1,75 @@
import QtGraphicalEffects 1.0
import QtQuick 2.6 import QtQuick 2.6
import QtQuick.Controls 2.3 import QtQuick.Controls 2.3
import QtGraphicalEffects 1.0
import im.nheko 1.0 import im.nheko 1.0
Rectangle { Rectangle {
id: avatar id: avatar
width: 48
height: 48
radius: Settings.avatarCircles ? height/2 : 3
property alias url: img.source property alias url: img.source
property string userid property string userid
property string displayName property string displayName
Label { width: 48
anchors.fill: parent height: 48
text: TimelineManager.escapeEmoji(displayName ? String.fromCodePoint(displayName.codePointAt(0)) : "") radius: Settings.avatarCircles ? height / 2 : 3
textFormat: Text.RichText color: colors.base
font.pixelSize: avatar.height/2
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
visible: img.status != Image.Ready
color: colors.text
}
Image { Label {
id: img anchors.fill: parent
anchors.fill: parent text: TimelineManager.escapeEmoji(displayName ? String.fromCodePoint(displayName.codePointAt(0)) : "")
asynchronous: true textFormat: Text.RichText
fillMode: Image.PreserveAspectCrop font.pixelSize: avatar.height / 2
mipmap: true verticalAlignment: Text.AlignVCenter
smooth: false horizontalAlignment: Text.AlignHCenter
visible: img.status != Image.Ready
color: colors.text
}
sourceSize.width: avatar.width Image {
sourceSize.height: avatar.height id: img
layer.enabled: true anchors.fill: parent
layer.effect: OpacityMask { asynchronous: true
maskSource: Rectangle { fillMode: Image.PreserveAspectCrop
anchors.fill: parent mipmap: true
width: avatar.width smooth: false
height: avatar.height sourceSize.width: avatar.width
radius: Settings.avatarCircles ? height/2 : 3 sourceSize.height: avatar.height
} layer.enabled: true
}
} layer.effect: OpacityMask {
Rectangle { maskSource: Rectangle {
anchors.bottom: avatar.bottom anchors.fill: parent
anchors.right: avatar.right width: avatar.width
height: avatar.height
radius: Settings.avatarCircles ? height / 2 : 3
}
visible: !!userid }
height: avatar.height / 6 }
width: height
radius: Settings.avatarCircles ? height / 2 : height / 4 Rectangle {
color: switch (TimelineManager.userPresence(userid)) { anchors.bottom: avatar.bottom
case "online": return "#00cc66" anchors.right: avatar.right
case "unavailable": return "#ff9933" visible: !!userid
case "offline": // return "#a82353" don't show anything if offline, since it is confusing, if presence is disabled height: avatar.height / 6
default: "transparent" width: height
} radius: Settings.avatarCircles ? height / 2 : height / 4
} color: {
switch (TimelineManager.userPresence(userid)) {
case "online":
return "#00cc66";
case "unavailable":
return "#ff9933";
case "offline":
default:
// return "#a82353" don't show anything if offline, since it is confusing, if presence is disabled
"transparent";
}
}
}
color: colors.base
} }

View file

@ -3,39 +3,42 @@ import QtQuick.Controls 2.1
import im.nheko 1.0 import im.nheko 1.0
Rectangle { Rectangle {
property bool encrypted: false id: indicator
id: indicator
color: "transparent"
width: 16
height: 16
ToolTip.visible: ma.containsMouse && indicator.visible property bool encrypted: false
ToolTip.text: getEncryptionTooltip()
MouseArea{ function getEncryptionImage() {
id: ma if (encrypted)
anchors.fill: parent return "image://colorimage/:/icons/icons/ui/lock.png?" + colors.buttonText;
hoverEnabled: true else
} return "image://colorimage/:/icons/icons/ui/unlock.png?#dd3d3d";
}
Image { function getEncryptionTooltip() {
id: stateImg if (encrypted)
anchors.fill: parent return qsTr("Encrypted");
source: getEncryptionImage() else
} return qsTr("This message is not encrypted!");
}
function getEncryptionImage() { color: "transparent"
if (encrypted) width: 16
return "image://colorimage/:/icons/icons/ui/lock.png?"+colors.buttonText height: 16
else ToolTip.visible: ma.containsMouse && indicator.visible
return "image://colorimage/:/icons/icons/ui/unlock.png?#dd3d3d" ToolTip.text: getEncryptionTooltip()
}
MouseArea {
id: ma
anchors.fill: parent
hoverEnabled: true
}
Image {
id: stateImg
anchors.fill: parent
source: getEncryptionImage()
}
function getEncryptionTooltip() {
if (encrypted)
return qsTr("Encrypted")
else
return qsTr("This message is not encrypted!")
}
} }

View file

@ -2,25 +2,29 @@ import QtQuick 2.3
import QtQuick.Controls 2.3 import QtQuick.Controls 2.3
AbstractButton { AbstractButton {
property string image: undefined id: button
property color highlightColor: colors.highlight
property color buttonTextColor: colors.buttonText
width: 16
height: 16
id: button
Image { property string image: undefined
id: buttonImg property color highlightColor: colors.highlight
// Workaround, can't get icon.source working for now... property color buttonTextColor: colors.buttonText
anchors.fill: parent
source: "image://colorimage/" + image + "?" + (button.hovered ? highlightColor : buttonTextColor) width: 16
} height: 16
Image {
id: buttonImg
// Workaround, can't get icon.source working for now...
anchors.fill: parent
source: "image://colorimage/" + image + "?" + (button.hovered ? highlightColor : buttonTextColor)
}
MouseArea {
id: mouseArea
anchors.fill: parent
onPressed: mouse.accepted = false
cursorShape: Qt.PointingHandCursor
}
MouseArea
{
id: mouseArea
anchors.fill: parent
onPressed: mouse.accepted = false
cursorShape: Qt.PointingHandCursor
}
} }

View file

@ -1,35 +1,37 @@
import QtQuick 2.5 import QtQuick 2.5
import QtQuick.Controls 2.3 import QtQuick.Controls 2.3
import im.nheko 1.0 import im.nheko 1.0
TextEdit { TextEdit {
textFormat: TextEdit.RichText textFormat: TextEdit.RichText
readOnly: true readOnly: true
wrapMode: Text.Wrap wrapMode: Text.Wrap
selectByMouse: true selectByMouse: true
activeFocusOnPress: false activeFocusOnPress: false
color: colors.text color: colors.text
onLinkActivated: {
if (/^https:\/\/matrix.to\/#\/(@.*)$/.test(link)) {
chat.model.openUserProfile(/^https:\/\/matrix.to\/#\/(@.*)$/.exec(link)[1]);
} else if (/^https:\/\/matrix.to\/#\/(![^\/]*)$/.test(link)) {
TimelineManager.setHistoryView(/^https:\/\/matrix.to\/#\/(!.*)$/.exec(link)[1]);
} else if (/^https:\/\/matrix.to\/#\/(![^\/]*)\/(\$.*)$/.test(link)) {
var match = /^https:\/\/matrix.to\/#\/(![^\/]*)\/(\$.*)$/.exec(link);
TimelineManager.setHistoryView(match[1]);
chat.positionViewAtIndex(chat.model.idToIndex(match[2]), ListView.Contain);
} else {
TimelineManager.openLink(link);
}
}
ToolTip.visible: hoveredLink
ToolTip.text: hoveredLink
onLinkActivated: { MouseArea {
if (/^https:\/\/matrix.to\/#\/(@.*)$/.test(link)) chat.model.openUserProfile(/^https:\/\/matrix.to\/#\/(@.*)$/.exec(link)[1]) id: ma
else if (/^https:\/\/matrix.to\/#\/(![^\/]*)$/.test(link)) TimelineManager.setHistoryView(/^https:\/\/matrix.to\/#\/(!.*)$/.exec(link)[1])
else if (/^https:\/\/matrix.to\/#\/(![^\/]*)\/(\$.*)$/.test(link)) { anchors.fill: parent
var match = /^https:\/\/matrix.to\/#\/(![^\/]*)\/(\$.*)$/.exec(link) propagateComposedEvents: true
TimelineManager.setHistoryView(match[1]) acceptedButtons: Qt.NoButton
chat.positionViewAtIndex(chat.model.idToIndex(match[2]), ListView.Contain) cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
} }
else TimelineManager.openLink(link)
}
MouseArea
{
id: ma
anchors.fill: parent
propagateComposedEvents: true
acceptedButtons: Qt.NoButton
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
}
ToolTip.visible: hoveredLink
ToolTip.text: hoveredLink
} }

View file

@ -1,94 +1,95 @@
import QtQuick 2.6 import QtQuick 2.6
import QtQuick.Controls 2.2 import QtQuick.Controls 2.2
import im.nheko 1.0 import im.nheko 1.0
// This class is for showing Reactions in the timeline row, not for // This class is for showing Reactions in the timeline row, not for
// adding new reactions via the emoji picker // adding new reactions via the emoji picker
Flow { Flow {
id: reactionFlow id: reactionFlow
// highlight colors for selfReactedEvent background // highlight colors for selfReactedEvent background
property real highlightHue: colors.highlight.hslHue property real highlightHue: colors.highlight.hslHue
property real highlightSat: colors.highlight.hslSaturation property real highlightSat: colors.highlight.hslSaturation
property real highlightLight: colors.highlight.hslLightness property real highlightLight: colors.highlight.hslLightness
property string eventId
property alias reactions: repeater.model
property string eventId anchors.left: parent.left
anchors.right: parent.right
spacing: 4
anchors.left: parent.left Repeater {
anchors.right: parent.right id: repeater
spacing: 4
property alias reactions: repeater.model delegate: AbstractButton {
id: reaction
Repeater { hoverEnabled: true
id: repeater implicitWidth: contentItem.childrenRect.width + contentItem.leftPadding * 2
implicitHeight: contentItem.childrenRect.height
ToolTip.visible: hovered
ToolTip.text: modelData.users
onClicked: {
console.debug("Picked " + modelData.key + "in response to " + reactionFlow.eventId + ". selfReactedEvent: " + modelData.selfReactedEvent);
TimelineManager.queueReactionMessage(reactionFlow.eventId, modelData.key);
}
delegate: AbstractButton { contentItem: Row {
id: reaction anchors.centerIn: parent
hoverEnabled: true spacing: reactionText.implicitHeight / 4
implicitWidth: contentItem.childrenRect.width + contentItem.leftPadding*2 leftPadding: reactionText.implicitHeight / 2
implicitHeight: contentItem.childrenRect.height rightPadding: reactionText.implicitHeight / 2
ToolTip.visible: hovered TextMetrics {
ToolTip.text: modelData.users id: textMetrics
onClicked: { font.family: Settings.emojiFont
console.debug("Picked " + modelData.key + "in response to " + reactionFlow.eventId + ". selfReactedEvent: " + modelData.selfReactedEvent) elide: Text.ElideRight
TimelineManager.queueReactionMessage(reactionFlow.eventId, modelData.key) elideWidth: 150
} text: modelData.key
}
Text {
id: reactionText
contentItem: Row { anchors.baseline: reactionCounter.baseline
anchors.centerIn: parent text: textMetrics.elidedText + (textMetrics.elidedText == modelData.key ? "" : "…")
spacing: reactionText.implicitHeight/4 font.family: Settings.emojiFont
leftPadding: reactionText.implicitHeight / 2 color: reaction.hovered ? colors.highlight : colors.text
rightPadding: reactionText.implicitHeight / 2 maximumLineCount: 1
}
TextMetrics { Rectangle {
id: textMetrics id: divider
font.family: Settings.emojiFont
elide: Text.ElideRight
elideWidth: 150
text: modelData.key
}
Text { height: Math.floor(reactionCounter.implicitHeight * 1.4)
anchors.baseline: reactionCounter.baseline width: 1
id: reactionText color: (reaction.hovered || modelData.selfReactedEvent !== '') ? colors.highlight : colors.text
text: textMetrics.elidedText + (textMetrics.elidedText == modelData.key ? "" : "…") }
font.family: Settings.emojiFont
color: reaction.hovered ? colors.highlight : colors.text
maximumLineCount: 1
}
Rectangle { Text {
id: divider id: reactionCounter
height: Math.floor(reactionCounter.implicitHeight * 1.4)
width: 1
color: (reaction.hovered || modelData.selfReactedEvent !== '') ? colors.highlight : colors.text
}
Text { anchors.verticalCenter: divider.verticalCenter
anchors.verticalCenter: divider.verticalCenter text: modelData.count
id: reactionCounter font: reaction.font
text: modelData.count color: reaction.hovered ? colors.highlight : colors.text
font: reaction.font }
color: reaction.hovered ? colors.highlight : colors.text
}
}
background: Rectangle { }
anchors.centerIn: parent
background: Rectangle {
anchors.centerIn: parent
implicitWidth: reaction.implicitWidth
implicitHeight: reaction.implicitHeight
border.color: (reaction.hovered || modelData.selfReactedEvent !== '') ? colors.highlight : colors.text
color: modelData.selfReactedEvent !== '' ? Qt.hsla(highlightHue, highlightSat, highlightLight, 0.2) : colors.base
border.width: 1
radius: reaction.height / 2
}
}
}
implicitWidth: reaction.implicitWidth
implicitHeight: reaction.implicitHeight
border.color: (reaction.hovered || modelData.selfReactedEvent !== '') ? colors.highlight : colors.text
color: modelData.selfReactedEvent !== '' ? Qt.hsla(highlightHue, highlightSat, highlightLight, 0.20) : colors.base
border.width: 1
radius: reaction.height / 2.0
}
}
}
} }

View file

@ -16,10 +16,6 @@
* with this program; if not, write to the Free Software Foundation, Inc., * with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import QtQuick 2.9
import QtQuick.Controls 2.3
/* /*
* Shamelessly stolen from: * Shamelessly stolen from:
* https://cgit.kde.org/kube.git/tree/framework/qml/ScrollHelper.qml * https://cgit.kde.org/kube.git/tree/framework/qml/ScrollHelper.qml
@ -31,81 +27,82 @@ import QtQuick.Controls 2.3
* ScrollView.qml in qtquickcontrols * ScrollView.qml in qtquickcontrols
* qquickwheelarea.cpp in qtquickcontrols * qquickwheelarea.cpp in qtquickcontrols
*/ */
import QtQuick 2.9
import QtQuick.Controls 2.3
MouseArea { MouseArea {
// console.warn("Delta: ", wheel.pixelDelta.y);
// console.warn("Old position: ", flickable.contentY);
// console.warn("New position: ", newPos);
id: root id: root
propagateComposedEvents: true
property Flickable flickable property Flickable flickable
property alias enabled: root.enabled property alias enabled: root.enabled
//Place the mouse area under the flickable
z: -1
onFlickableChanged: {
if (enabled) {
flickable.maximumFlickVelocity = 100000
flickable.boundsBehavior = Flickable.StopAtBounds
root.parent = flickable
}
}
acceptedButtons: Qt.NoButton
function calculateNewPosition(flickableItem, wheel) { function calculateNewPosition(flickableItem, wheel) {
//Nothing to scroll //Nothing to scroll
if (flickableItem.contentHeight < flickableItem.height) { if (flickableItem.contentHeight < flickableItem.height)
return flickableItem.contentY; return flickableItem.contentY;
}
//Ignore 0 events (happens at least with Christians trackpad) //Ignore 0 events (happens at least with Christians trackpad)
if (wheel.pixelDelta.y == 0 && wheel.angleDelta.y == 0) { if (wheel.pixelDelta.y == 0 && wheel.angleDelta.y == 0)
return flickableItem.contentY; return flickableItem.contentY;
}
//pixelDelta seems to be the same as angleDelta/8 //pixelDelta seems to be the same as angleDelta/8
var pixelDelta = 0 var pixelDelta = 0;
//The pixelDelta is a smaller number if both are provided, so pixelDelta can be 0 while angleDelta is still something. So we check the angleDelta //The pixelDelta is a smaller number if both are provided, so pixelDelta can be 0 while angleDelta is still something. So we check the angleDelta
if (wheel.angleDelta.y) { if (wheel.angleDelta.y) {
var wheelScrollLines = 3 //Default value of QApplication wheelScrollLines property var wheelScrollLines = 3; //Default value of QApplication wheelScrollLines property
var pixelPerLine = 20 //Default value in Qt, originally comes from QTextEdit var pixelPerLine = 20; //Default value in Qt, originally comes from QTextEdit
var ticks = (wheel.angleDelta.y / 8) / 15.0 //Divide by 8 gives us pixels typically come in 15pixel steps. var ticks = (wheel.angleDelta.y / 8) / 15; //Divide by 8 gives us pixels typically come in 15pixel steps.
pixelDelta = ticks * pixelPerLine * wheelScrollLines pixelDelta = ticks * pixelPerLine * wheelScrollLines;
} else { } else {
pixelDelta = wheel.pixelDelta.y pixelDelta = wheel.pixelDelta.y;
} }
pixelDelta = Math.round(pixelDelta);
pixelDelta = Math.round(pixelDelta) if (!pixelDelta)
if (!pixelDelta) {
return flickableItem.contentY; return flickableItem.contentY;
}
var minYExtent = flickableItem.originY + flickableItem.topMargin; var minYExtent = flickableItem.originY + flickableItem.topMargin;
var maxYExtent = (flickableItem.contentHeight + flickableItem.bottomMargin + flickableItem.originY) - flickableItem.height; var maxYExtent = (flickableItem.contentHeight + flickableItem.bottomMargin + flickableItem.originY) - flickableItem.height;
if (typeof (flickableItem.headerItem) !== "undefined" && flickableItem.headerItem)
if (typeof(flickableItem.headerItem) !== "undefined" && flickableItem.headerItem) { minYExtent += flickableItem.headerItem.height;
minYExtent += flickableItem.headerItem.height
}
//Avoid overscrolling //Avoid overscrolling
return Math.max(minYExtent, Math.min(maxYExtent, flickableItem.contentY - pixelDelta)); return Math.max(minYExtent, Math.min(maxYExtent, flickableItem.contentY - pixelDelta));
} }
propagateComposedEvents: true
//Place the mouse area under the flickable
z: -1
onFlickableChanged: {
if (enabled) {
flickable.maximumFlickVelocity = 100000;
flickable.boundsBehavior = Flickable.StopAtBounds;
root.parent = flickable;
}
}
acceptedButtons: Qt.NoButton
onWheel: { onWheel: {
var newPos = calculateNewPosition(flickable, wheel); var newPos = calculateNewPosition(flickable, wheel);
// console.warn("Delta: ", wheel.pixelDelta.y);
// console.warn("Old position: ", flickable.contentY);
// console.warn("New position: ", newPos);
// Show the scrollbars // Show the scrollbars
flickable.flick(0, 0); flickable.flick(0, 0);
flickable.contentY = newPos; flickable.contentY = newPos;
cancelFlickStateTimer.start() cancelFlickStateTimer.start();
} }
Timer { Timer {
id: cancelFlickStateTimer id: cancelFlickStateTimer
//How long the scrollbar will remain visible //How long the scrollbar will remain visible
interval: 500 interval: 500
// Hide the scrollbars // Hide the scrollbars
onTriggered: { flickable.cancelFlick(); flickable.movementEnded(); } onTriggered: {
flickable.cancelFlick();
flickable.movementEnded();
}
} }
} }

View file

@ -3,37 +3,55 @@ import QtQuick.Controls 2.1
import im.nheko 1.0 import im.nheko 1.0
Rectangle { Rectangle {
id: indicator id: indicator
property int state: 0
color: "transparent"
width: 16
height: 16
ToolTip.visible: ma.containsMouse && state != MtxEvent.Empty property int state: 0
ToolTip.text: switch (state) {
case MtxEvent.Failed: return qsTr("Failed") color: "transparent"
case MtxEvent.Sent: return qsTr("Sent") width: 16
case MtxEvent.Received: return qsTr("Received") height: 16
case MtxEvent.Read: return qsTr("Read") ToolTip.visible: ma.containsMouse && state != MtxEvent.Empty
default: return "" ToolTip.text: {
} switch (state) {
MouseArea{ case MtxEvent.Failed:
id: ma return qsTr("Failed");
anchors.fill: parent case MtxEvent.Sent:
hoverEnabled: true return qsTr("Sent");
} case MtxEvent.Received:
return qsTr("Received");
case MtxEvent.Read:
return qsTr("Read");
default:
return "";
}
}
MouseArea {
id: ma
anchors.fill: parent
hoverEnabled: true
}
Image {
id: stateImg
// Workaround, can't get icon.source working for now...
anchors.fill: parent
source: {
switch (indicator.state) {
case MtxEvent.Failed:
return "image://colorimage/:/icons/icons/ui/remove-symbol.png?" + colors.buttonText;
case MtxEvent.Sent:
return "image://colorimage/:/icons/icons/ui/clock.png?" + colors.buttonText;
case MtxEvent.Received:
return "image://colorimage/:/icons/icons/ui/checkmark.png?" + colors.buttonText;
case MtxEvent.Read:
return "image://colorimage/:/icons/icons/ui/double-tick-indicator.png?" + colors.buttonText;
default:
return "";
}
}
}
Image {
id: stateImg
// Workaround, can't get icon.source working for now...
anchors.fill: parent
source: switch (indicator.state) {
case MtxEvent.Failed: return "image://colorimage/:/icons/icons/ui/remove-symbol.png?" + colors.buttonText
case MtxEvent.Sent: return "image://colorimage/:/icons/icons/ui/clock.png?" + colors.buttonText
case MtxEvent.Received: return "image://colorimage/:/icons/icons/ui/checkmark.png?" + colors.buttonText
case MtxEvent.Read: return "image://colorimage/:/icons/icons/ui/double-tick-indicator.png?" + colors.buttonText
default: return ""
}
}
} }

View file

@ -1,146 +1,148 @@
import "./delegates"
import "./emoji"
import QtQuick 2.6 import QtQuick 2.6
import QtQuick.Controls 2.3 import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2 import QtQuick.Layouts 1.2
import QtQuick.Window 2.2 import QtQuick.Window 2.2
import im.nheko 1.0 import im.nheko 1.0
import "./delegates"
import "./emoji"
Item { Item {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
height: row.height height: row.height
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
propagateComposedEvents: true propagateComposedEvents: true
preventStealing: true preventStealing: true
hoverEnabled: true hoverEnabled: true
acceptedButtons: Qt.AllButtons
onClicked: {
if (mouse.button === Qt.RightButton)
messageContextMenu.show(model.id, model.type, model.isEncrypted, row);
acceptedButtons: Qt.AllButtons }
onClicked: { onPressAndHold: {
if (mouse.button === Qt.RightButton) messageContextMenu.show(model.id, model.type, model.isEncrypted, row, mapToItem(timelineRoot, mouse.x, mouse.y));
messageContextMenu.show(model.id, model.type, model.isEncrypted, row) }
} }
onPressAndHold: {
messageContextMenu.show(model.id, model.type, model.isEncrypted, row, mapToItem(timelineRoot, mouse.x, mouse.y))
}
}
Rectangle {
color: (Settings.messageHoverHighlight && parent.containsMouse) ? colors.base : "transparent"
anchors.fill: row
}
RowLayout {
id: row
anchors.leftMargin: avatarSize + 16 Rectangle {
anchors.left: parent.left color: (Settings.messageHoverHighlight && parent.containsMouse) ? colors.base : "transparent"
anchors.right: parent.right anchors.fill: row
}
RowLayout {
id: row
Column { anchors.leftMargin: avatarSize + 16
Layout.fillWidth: true anchors.left: parent.left
Layout.alignment: Qt.AlignTop anchors.right: parent.right
spacing: 4
// fancy reply, if this is a reply Column {
Reply { Layout.fillWidth: true
visible: model.replyTo Layout.alignment: Qt.AlignTop
modelData: chat.model.getDump(model.replyTo,model.id) spacing: 4
userColor: TimelineManager.userColor(modelData.userId, colors.window)
}
// actual message content // fancy reply, if this is a reply
MessageDelegate { Reply {
id: contentItem visible: model.replyTo
modelData: chat.model.getDump(model.replyTo, model.id)
userColor: TimelineManager.userColor(modelData.userId, colors.window)
}
width: parent.width // actual message content
MessageDelegate {
id: contentItem
modelData: model width: parent.width
} modelData: model
}
Reactions { Reactions {
id: reactionRow id: reactionRow
reactions: model.reactions
eventId: model.id
}
}
StatusIndicator { reactions: model.reactions
state: model.state eventId: model.id
Layout.alignment: Qt.AlignRight | Qt.AlignTop }
Layout.preferredHeight: 16
width: 16
}
EncryptionIndicator { }
visible: model.isRoomEncrypted
encrypted: model.isEncrypted
Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.preferredHeight: 16
width: 16
}
EmojiButton {
visible: Settings.buttonsInTimeline
Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.preferredHeight: 16
width: 16
id: reactButton
hoverEnabled: true
ToolTip.visible: hovered
ToolTip.text: qsTr("React")
emojiPicker: emojiPopup
event_id: model.id
}
ImageButton {
visible: Settings.buttonsInTimeline
Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.preferredHeight: 16
width: 16
id: replyButton
hoverEnabled: true
StatusIndicator {
state: model.state
Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.preferredHeight: 16
width: 16
}
image: ":/icons/icons/ui/mail-reply.png" EncryptionIndicator {
visible: model.isRoomEncrypted
encrypted: model.isEncrypted
Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.preferredHeight: 16
width: 16
}
ToolTip.visible: hovered EmojiButton {
ToolTip.text: qsTr("Reply") id: reactButton
onClicked: chat.model.replyAction(model.id) visible: Settings.buttonsInTimeline
} Layout.alignment: Qt.AlignRight | Qt.AlignTop
ImageButton { Layout.preferredHeight: 16
visible: Settings.buttonsInTimeline width: 16
Layout.alignment: Qt.AlignRight | Qt.AlignTop hoverEnabled: true
Layout.preferredHeight: 16 ToolTip.visible: hovered
width: 16 ToolTip.text: qsTr("React")
id: optionsButton emojiPicker: emojiPopup
hoverEnabled: true event_id: model.id
}
image: ":/icons/icons/ui/vertical-ellipsis.png" ImageButton {
id: replyButton
ToolTip.visible: hovered visible: Settings.buttonsInTimeline
ToolTip.text: qsTr("Options") Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.preferredHeight: 16
width: 16
hoverEnabled: true
image: ":/icons/icons/ui/mail-reply.png"
ToolTip.visible: hovered
ToolTip.text: qsTr("Reply")
onClicked: chat.model.replyAction(model.id)
}
onClicked: messageContextMenu.show(model.id, model.type, model.isEncrypted, optionsButton) ImageButton {
} id: optionsButton
Label { visible: Settings.buttonsInTimeline
Layout.alignment: Qt.AlignRight | Qt.AlignTop Layout.alignment: Qt.AlignRight | Qt.AlignTop
text: model.timestamp.toLocaleTimeString("HH:mm") Layout.preferredHeight: 16
width: Math.max(implicitWidth, text.length*fontMetrics.maximumCharacterWidth) width: 16
color: inactiveColors.text hoverEnabled: true
image: ":/icons/icons/ui/vertical-ellipsis.png"
ToolTip.visible: hovered
ToolTip.text: qsTr("Options")
onClicked: messageContextMenu.show(model.id, model.type, model.isEncrypted, optionsButton)
}
MouseArea{ Label {
id: ma Layout.alignment: Qt.AlignRight | Qt.AlignTop
anchors.fill: parent text: model.timestamp.toLocaleTimeString("HH:mm")
hoverEnabled: true width: Math.max(implicitWidth, text.length * fontMetrics.maximumCharacterWidth)
propagateComposedEvents: true color: inactiveColors.text
} ToolTip.visible: ma.containsMouse
ToolTip.text: Qt.formatDateTime(model.timestamp, Qt.DefaultLocaleLongDate)
MouseArea {
id: ma
anchors.fill: parent
hoverEnabled: true
propagateComposedEvents: true
}
}
}
ToolTip.visible: ma.containsMouse
ToolTip.text: Qt.formatDateTime(model.timestamp, Qt.DefaultLocaleLongDate)
}
}
} }

File diff suppressed because it is too large Load diff

View file

@ -1,172 +1,175 @@
import "./device-verification"
import QtQuick 2.9 import QtQuick 2.9
import QtQuick.Controls 2.3 import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2 import QtQuick.Layouts 1.2
import QtQuick.Window 2.3 import QtQuick.Window 2.3
import im.nheko 1.0 import im.nheko 1.0
import "./device-verification" ApplicationWindow {
id: userProfileDialog
ApplicationWindow{ property var profile
property var profile
id: userProfileDialog height: 650
height: 650 width: 420
width: 420 minimumHeight: 420
minimumHeight: 420 palette: colors
palette: colors Component {
id: deviceVerificationDialog
Component { DeviceVerification {
id: deviceVerificationDialog }
DeviceVerification {}
}
ColumnLayout{ }
id: contentL
anchors.fill: parent ColumnLayout {
anchors.margins: 10 id: contentL
spacing: 10 anchors.fill: parent
anchors.margins: 10
spacing: 10
Avatar { Avatar {
url: profile.avatarUrl.replace("mxc://", "image://MxcImage/") url: profile.avatarUrl.replace("mxc://", "image://MxcImage/")
height: 130 height: 130
width: 130 width: 130
displayName: profile.displayName displayName: profile.displayName
userid: profile.userid userid: profile.userid
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
} }
Label { Label {
text: profile.displayName text: profile.displayName
fontSizeMode: Text.HorizontalFit fontSizeMode: Text.HorizontalFit
font.pixelSize: 20 font.pixelSize: 20
color: TimelineManager.userColor(profile.userid, colors.window) color: TimelineManager.userColor(profile.userid, colors.window)
font.bold: true font.bold: true
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
} }
MatrixText { MatrixText {
text: profile.userid text: profile.userid
font.pixelSize: 15 font.pixelSize: 15
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
} }
Button { Button {
id: verifyUserButton id: verifyUserButton
text: qsTr("Verify")
Layout.alignment: Qt.AlignHCenter
enabled: !profile.isUserVerified
visible: !profile.isUserVerified
onClicked: profile.verify() text: qsTr("Verify")
} Layout.alignment: Qt.AlignHCenter
enabled: !profile.isUserVerified
visible: !profile.isUserVerified
onClicked: profile.verify()
}
RowLayout { RowLayout {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
spacing: 8 spacing: 8
ImageButton { ImageButton {
image:":/icons/icons/ui/do-not-disturb-rounded-sign.png" image: ":/icons/icons/ui/do-not-disturb-rounded-sign.png"
hoverEnabled: true hoverEnabled: true
ToolTip.visible: hovered ToolTip.visible: hovered
ToolTip.text: qsTr("Ban the user") ToolTip.text: qsTr("Ban the user")
onClicked: profile.banUser() onClicked: profile.banUser()
} }
// ImageButton{ // ImageButton{
// image:":/icons/icons/ui/volume-off-indicator.png"
// Layout.margins: {
// left: 5
// right: 5
// }
// ToolTip.visible: hovered
// ToolTip.text: qsTr("Ignore messages from this user")
// onClicked : {
// profile.ignoreUser()
// }
// }
ImageButton{
image:":/icons/icons/ui/black-bubble-speech.png"
hoverEnabled: true
ToolTip.visible: hovered
ToolTip.text: qsTr("Start a private chat")
onClicked: profile.startChat()
}
ImageButton{
image:":/icons/icons/ui/round-remove-button.png"
hoverEnabled: true
ToolTip.visible: hovered
ToolTip.text: qsTr("Kick the user")
onClicked: profile.kickUser()
}
}
ListView{ // image:":/icons/icons/ui/volume-off-indicator.png"
id: devicelist // Layout.margins: {
// left: 5
// right: 5
// }
// ToolTip.visible: hovered
// ToolTip.text: qsTr("Ignore messages from this user")
// onClicked : {
// profile.ignoreUser()
// }
// }
ImageButton {
image: ":/icons/icons/ui/black-bubble-speech.png"
hoverEnabled: true
ToolTip.visible: hovered
ToolTip.text: qsTr("Start a private chat")
onClicked: profile.startChat()
}
Layout.fillHeight: true ImageButton {
Layout.minimumHeight: 200 image: ":/icons/icons/ui/round-remove-button.png"
Layout.fillWidth: true hoverEnabled: true
ToolTip.visible: hovered
ToolTip.text: qsTr("Kick the user")
onClicked: profile.kickUser()
}
clip: true }
spacing: 8
boundsBehavior: Flickable.StopAtBounds
model: profile.deviceList ListView {
id: devicelist
delegate: RowLayout{ Layout.fillHeight: true
width: devicelist.width Layout.minimumHeight: 200
spacing: 4 Layout.fillWidth: true
clip: true
spacing: 8
boundsBehavior: Flickable.StopAtBounds
model: profile.deviceList
ColumnLayout{ delegate: RowLayout {
spacing: 0 width: devicelist.width
Text{ spacing: 4
Layout.fillWidth: true
Layout.alignment: Qt.AlignLeft
elide: Text.ElideRight ColumnLayout {
font.bold: true spacing: 0
color: colors.text
text: model.deviceId
}
Text{
Layout.fillWidth: true
Layout.alignment: Qt.AlignRight
elide: Text.ElideRight Text {
color: colors.text Layout.fillWidth: true
text: model.deviceName Layout.alignment: Qt.AlignLeft
} elide: Text.ElideRight
} font.bold: true
color: colors.text
text: model.deviceId
}
Image{ Text {
Layout.preferredHeight: 16 Layout.fillWidth: true
Layout.preferredWidth: 16 Layout.alignment: Qt.AlignRight
elide: Text.ElideRight
color: colors.text
text: model.deviceName
}
source: ((model.verificationStatus == VerificationStatus.VERIFIED)?"image://colorimage/:/icons/icons/ui/lock.png?green": }
((model.verificationStatus == VerificationStatus.UNVERIFIED)?"image://colorimage/:/icons/icons/ui/unlock.png?yellow":
"image://colorimage/:/icons/icons/ui/unlock.png?red"))
}
Button{
id: verifyButton
text: (model.verificationStatus != VerificationStatus.VERIFIED)?"Verify":"Unverify"
onClicked: {
if(model.verificationStatus == VerificationStatus.VERIFIED){
profile.unverify(model.deviceId)
}else{
profile.verify(model.deviceId);
}
}
}
}
}
}
footer: DialogButtonBox { Image {
standardButtons: DialogButtonBox.Ok Layout.preferredHeight: 16
Layout.preferredWidth: 16
source: ((model.verificationStatus == VerificationStatus.VERIFIED) ? "image://colorimage/:/icons/icons/ui/lock.png?green" : ((model.verificationStatus == VerificationStatus.UNVERIFIED) ? "image://colorimage/:/icons/icons/ui/unlock.png?yellow" : "image://colorimage/:/icons/icons/ui/unlock.png?red"))
}
Button {
id: verifyButton
text: (model.verificationStatus != VerificationStatus.VERIFIED) ? "Verify" : "Unverify"
onClicked: {
if (model.verificationStatus == VerificationStatus.VERIFIED)
profile.unverify(model.deviceId);
else
profile.verify(model.deviceId);
}
}
}
}
}
footer: DialogButtonBox {
standardButtons: DialogButtonBox.Ok
onAccepted: userProfileDialog.close()
}
onAccepted: userProfileDialog.close()
}
} }

View file

@ -1,68 +1,75 @@
import QtQuick 2.6 import QtQuick 2.6
import QtQuick.Layouts 1.2 import QtQuick.Layouts 1.2
import im.nheko 1.0 import im.nheko 1.0
Item { Item {
height: row.height + 24 height: row.height + 24
width: parent ? parent.width : undefined width: parent ? parent.width : undefined
RowLayout { RowLayout {
id: row id: row
anchors.centerIn: parent anchors.centerIn: parent
width: parent.width - 24 width: parent.width - 24
spacing: 15
spacing: 15 Rectangle {
id: button
Rectangle { color: colors.light
id: button radius: 22
color: colors.light height: 44
radius: 22 width: 44
height: 44
width: 44
Image {
id: img
anchors.centerIn: parent
source: "qrc:/icons/icons/ui/arrow-pointing-down.png" Image {
fillMode: Image.Pad id: img
} anchors.centerIn: parent
MouseArea { source: "qrc:/icons/icons/ui/arrow-pointing-down.png"
anchors.fill: parent fillMode: Image.Pad
onClicked: TimelineManager.timeline.saveMedia(model.data.id) }
cursorShape: Qt.PointingHandCursor
}
}
ColumnLayout {
id: col
Text { MouseArea {
id: filename anchors.fill: parent
Layout.fillWidth: true onClicked: TimelineManager.timeline.saveMedia(model.data.id)
text: model.data.filename cursorShape: Qt.PointingHandCursor
textFormat: Text.PlainText }
elide: Text.ElideRight
color: colors.text
}
Text {
id: filesize
Layout.fillWidth: true
text: model.data.filesize
textFormat: Text.PlainText
elide: Text.ElideRight
color: colors.text
}
}
}
Rectangle { }
color: colors.dark
z: -1 ColumnLayout {
radius: 10 id: col
height: row.height + 24
width: 44 + 24 + 24 + Math.max(Math.min(filesize.width, filesize.implicitWidth), Math.min(filename.width, filename.implicitWidth)) Text {
} id: filename
Layout.fillWidth: true
text: model.data.filename
textFormat: Text.PlainText
elide: Text.ElideRight
color: colors.text
}
Text {
id: filesize
Layout.fillWidth: true
text: model.data.filesize
textFormat: Text.PlainText
elide: Text.ElideRight
color: colors.text
}
}
}
Rectangle {
color: colors.dark
z: -1
radius: 10
height: row.height + 24
width: 44 + 24 + 24 + Math.max(Math.min(filesize.width, filesize.implicitWidth), Math.min(filename.width, filename.implicitWidth))
}
} }

View file

@ -1,42 +1,41 @@
import QtQuick 2.6 import QtQuick 2.6
import im.nheko 1.0 import im.nheko 1.0
Item { Item {
property double tempWidth: Math.min(parent ? parent.width : undefined, model.data.width < 1 ? parent.width : model.data.width) property double tempWidth: Math.min(parent ? parent.width : undefined, model.data.width < 1 ? parent.width : model.data.width)
property double tempHeight: tempWidth * model.data.proportionalHeight property double tempHeight: tempWidth * model.data.proportionalHeight
property double divisor: model.isReply ? 4 : 2
property bool tooHigh: tempHeight > timelineRoot.height / divisor
property double divisor: model.isReply ? 4 : 2 height: Math.round(tooHigh ? timelineRoot.height / divisor : tempHeight)
property bool tooHigh: tempHeight > timelineRoot.height / divisor width: Math.round(tooHigh ? (timelineRoot.height / divisor) / model.data.proportionalHeight : tempWidth)
height: Math.round(tooHigh ? timelineRoot.height / divisor : tempHeight) Image {
width: Math.round(tooHigh ? (timelineRoot.height / divisor) / model.data.proportionalHeight : tempWidth) id: blurhash
Image { anchors.fill: parent
id: blurhash visible: img.status != Image.Ready
anchors.fill: parent source: model.data.blurhash ? ("image://blurhash/" + model.data.blurhash) : ("image://colorimage/:/icons/icons/ui/do-not-disturb-rounded-sign@2x.png?" + colors.buttonText)
visible: img.status != Image.Ready asynchronous: true
fillMode: Image.PreserveAspectFit
sourceSize.width: parent.width
sourceSize.height: parent.height
}
source: model.data.blurhash ? ("image://blurhash/" + model.data.blurhash) : ("image://colorimage/:/icons/icons/ui/do-not-disturb-rounded-sign@2x.png?"+colors.buttonText) Image {
asynchronous: true id: img
fillMode: Image.PreserveAspectFit
sourceSize.width: parent.width anchors.fill: parent
sourceSize.height: parent.height source: model.data.url.replace("mxc://", "image://MxcImage/")
} asynchronous: true
fillMode: Image.PreserveAspectFit
Image { MouseArea {
id: img enabled: model.data.type == MtxEvent.ImageMessage && img.status == Image.Ready
anchors.fill: parent anchors.fill: parent
onClicked: TimelineManager.openImageOverlay(model.data.url, model.data.id)
}
source: model.data.url.replace("mxc://", "image://MxcImage/") }
asynchronous: true
fillMode: Image.PreserveAspectFit
MouseArea {
enabled: model.data.type == MtxEvent.ImageMessage && img.status == Image.Ready
anchors.fill: parent
onClicked: TimelineManager.openImageOverlay(model.data.url, model.data.id)
}
}
} }

View file

@ -2,215 +2,334 @@ import QtQuick 2.6
import im.nheko 1.0 import im.nheko 1.0
Item { Item {
// Workaround to have an assignable global property property alias modelData: model.data
Item { property alias isReply: model.isReply
id: model property real implicitWidth: (chooser.child && chooser.child.implicitWidth) ? chooser.child.implicitWidth : width
property var data;
property bool isReply: false
}
property alias modelData: model.data
property alias isReply: model.isReply
height: chooser.childrenRect.height height: chooser.childrenRect.height
property real implicitWidth: (chooser.child && chooser.child.implicitWidth) ? chooser.child.implicitWidth : width
DelegateChooser { // Workaround to have an assignable global property
id: chooser Item {
//role: "type" //< not supported in our custom implementation, have to use roleValue id: model
roleValue: model.data.type
anchors.fill: parent property var data
property bool isReply: false
}
DelegateChooser {
id: chooser
//role: "type" //< not supported in our custom implementation, have to use roleValue
roleValue: model.data.type
anchors.fill: parent
DelegateChoice {
roleValue: MtxEvent.UnknownMessage
Placeholder {
text: "Unretrieved event"
}
}
DelegateChoice {
roleValue: MtxEvent.TextMessage
TextMessage {
}
}
DelegateChoice {
roleValue: MtxEvent.NoticeMessage
NoticeMessage {
}
}
DelegateChoice {
roleValue: MtxEvent.EmoteMessage
NoticeMessage {
formatted: TimelineManager.escapeEmoji(modelData.userName) + " " + model.data.formattedBody
color: TimelineManager.userColor(modelData.userId, colors.window)
}
}
DelegateChoice {
roleValue: MtxEvent.ImageMessage
ImageMessage {
}
}
DelegateChoice {
roleValue: MtxEvent.Sticker
ImageMessage {
}
}
DelegateChoice {
roleValue: MtxEvent.FileMessage
FileMessage {
}
}
DelegateChoice {
roleValue: MtxEvent.VideoMessage
PlayableMediaMessage {
}
}
DelegateChoice {
roleValue: MtxEvent.AudioMessage
PlayableMediaMessage {
}
}
DelegateChoice {
roleValue: MtxEvent.Redacted
Pill {
text: qsTr("redacted")
}
}
DelegateChoice {
roleValue: MtxEvent.Redaction
Pill {
text: qsTr("redacted")
}
}
DelegateChoice {
roleValue: MtxEvent.Encryption
Pill {
text: qsTr("Encryption enabled")
}
}
DelegateChoice {
roleValue: MtxEvent.Name
NoticeMessage {
text: model.data.roomName ? qsTr("room name changed to: %1").arg(model.data.roomName) : qsTr("removed room name")
}
}
DelegateChoice {
roleValue: MtxEvent.Topic
NoticeMessage {
text: model.data.roomTopic ? qsTr("topic changed to: %1").arg(model.data.roomTopic) : qsTr("removed topic")
}
}
DelegateChoice {
roleValue: MtxEvent.RoomCreate
NoticeMessage {
text: qsTr("%1 created and configured room: %2").arg(model.data.userName).arg(model.data.roomId)
}
}
DelegateChoice {
roleValue: MtxEvent.CallInvite
NoticeMessage {
text: {
switch (model.data.callType) {
case "voice":
return qsTr("%1 placed a voice call.").arg(model.data.userName);
case "video":
return qsTr("%1 placed a video call.").arg(model.data.userName);
default:
return qsTr("%1 placed a call.").arg(model.data.userName);
}
}
}
}
DelegateChoice {
roleValue: MtxEvent.CallAnswer
NoticeMessage {
text: qsTr("%1 answered the call.").arg(model.data.userName)
}
}
DelegateChoice {
roleValue: MtxEvent.CallHangUp
NoticeMessage {
text: qsTr("%1 ended the call.").arg(model.data.userName)
}
}
DelegateChoice {
roleValue: MtxEvent.CallCandidates
NoticeMessage {
text: qsTr("Negotiating call...")
}
}
DelegateChoice {
// TODO: make a more complex formatter for the power levels.
roleValue: MtxEvent.PowerLevels
NoticeMessage {
text: TimelineManager.timeline.formatPowerLevelEvent(model.data.id)
}
}
DelegateChoice {
roleValue: MtxEvent.RoomJoinRules
NoticeMessage {
text: TimelineManager.timeline.formatJoinRuleEvent(model.data.id)
}
}
DelegateChoice {
roleValue: MtxEvent.RoomHistoryVisibility
NoticeMessage {
text: TimelineManager.timeline.formatHistoryVisibilityEvent(model.data.id)
}
}
DelegateChoice {
roleValue: MtxEvent.RoomGuestAccess
NoticeMessage {
text: TimelineManager.timeline.formatGuestAccessEvent(model.data.id)
}
}
DelegateChoice {
roleValue: MtxEvent.Member
NoticeMessage {
text: TimelineManager.timeline.formatMemberEvent(model.data.id)
}
}
DelegateChoice {
roleValue: MtxEvent.KeyVerificationRequest
NoticeMessage {
text: "KeyVerificationRequest"
}
}
DelegateChoice {
roleValue: MtxEvent.KeyVerificationStart
NoticeMessage {
text: "KeyVerificationStart"
}
}
DelegateChoice {
roleValue: MtxEvent.KeyVerificationReady
NoticeMessage {
text: "KeyVerificationReady"
}
}
DelegateChoice {
roleValue: MtxEvent.KeyVerificationCancel
NoticeMessage {
text: "KeyVerificationCancel"
}
}
DelegateChoice {
roleValue: MtxEvent.KeyVerificationKey
NoticeMessage {
text: "KeyVerificationKey"
}
}
DelegateChoice {
roleValue: MtxEvent.KeyVerificationMac
NoticeMessage {
text: "KeyVerificationMac"
}
}
DelegateChoice {
roleValue: MtxEvent.KeyVerificationDone
NoticeMessage {
text: "KeyVerificationDone"
}
}
DelegateChoice {
roleValue: MtxEvent.KeyVerificationDone
NoticeMessage {
text: "KeyVerificationDone"
}
}
DelegateChoice {
roleValue: MtxEvent.KeyVerificationAccept
NoticeMessage {
text: "KeyVerificationAccept"
}
}
DelegateChoice {
Placeholder {
}
}
}
DelegateChoice {
roleValue: MtxEvent.UnknownMessage
Placeholder { text: "Unretrieved event" }
}
DelegateChoice {
roleValue: MtxEvent.TextMessage
TextMessage {}
}
DelegateChoice {
roleValue: MtxEvent.NoticeMessage
NoticeMessage {}
}
DelegateChoice {
roleValue: MtxEvent.EmoteMessage
NoticeMessage {
formatted: TimelineManager.escapeEmoji(modelData.userName) + " " + model.data.formattedBody
color: TimelineManager.userColor(modelData.userId, colors.window)
}
}
DelegateChoice {
roleValue: MtxEvent.ImageMessage
ImageMessage {}
}
DelegateChoice {
roleValue: MtxEvent.Sticker
ImageMessage {}
}
DelegateChoice {
roleValue: MtxEvent.FileMessage
FileMessage {}
}
DelegateChoice {
roleValue: MtxEvent.VideoMessage
PlayableMediaMessage {}
}
DelegateChoice {
roleValue: MtxEvent.AudioMessage
PlayableMediaMessage {}
}
DelegateChoice {
roleValue: MtxEvent.Redacted
Pill {
text: qsTr("redacted")
}
}
DelegateChoice {
roleValue: MtxEvent.Redaction
Pill {
text: qsTr("redacted")
}
}
DelegateChoice {
roleValue: MtxEvent.Encryption
Pill {
text: qsTr("Encryption enabled")
}
}
DelegateChoice {
roleValue: MtxEvent.Name
NoticeMessage {
text: model.data.roomName ? qsTr("room name changed to: %1").arg(model.data.roomName) : qsTr("removed room name")
}
}
DelegateChoice {
roleValue: MtxEvent.Topic
NoticeMessage {
text: model.data.roomTopic ? qsTr("topic changed to: %1").arg(model.data.roomTopic) : qsTr("removed topic")
}
}
DelegateChoice {
roleValue: MtxEvent.RoomCreate
NoticeMessage {
text: qsTr("%1 created and configured room: %2").arg(model.data.userName).arg(model.data.roomId)
}
}
DelegateChoice {
roleValue: MtxEvent.CallInvite
NoticeMessage {
text: switch(model.data.callType) {
case "voice": return qsTr("%1 placed a voice call.").arg(model.data.userName)
case "video": return qsTr("%1 placed a video call.").arg(model.data.userName)
default: return qsTr("%1 placed a call.").arg(model.data.userName)
}
}
}
DelegateChoice {
roleValue: MtxEvent.CallAnswer
NoticeMessage {
text: qsTr("%1 answered the call.").arg(model.data.userName)
}
}
DelegateChoice {
roleValue: MtxEvent.CallHangUp
NoticeMessage {
text: qsTr("%1 ended the call.").arg(model.data.userName)
}
}
DelegateChoice {
roleValue: MtxEvent.CallCandidates
NoticeMessage {
text: qsTr("Negotiating call...")
}
}
DelegateChoice {
// TODO: make a more complex formatter for the power levels.
roleValue: MtxEvent.PowerLevels
NoticeMessage {
text: TimelineManager.timeline.formatPowerLevelEvent(model.data.id)
}
}
DelegateChoice {
roleValue: MtxEvent.RoomJoinRules
NoticeMessage {
text: TimelineManager.timeline.formatJoinRuleEvent(model.data.id)
}
}
DelegateChoice {
roleValue: MtxEvent.RoomHistoryVisibility
NoticeMessage {
text: TimelineManager.timeline.formatHistoryVisibilityEvent(model.data.id)
}
}
DelegateChoice {
roleValue: MtxEvent.RoomGuestAccess
NoticeMessage {
text: TimelineManager.timeline.formatGuestAccessEvent(model.data.id)
}
}
DelegateChoice {
roleValue: MtxEvent.Member
NoticeMessage {
text: TimelineManager.timeline.formatMemberEvent(model.data.id);
}
}
DelegateChoice {
roleValue: MtxEvent.KeyVerificationRequest
NoticeMessage {
text: "KeyVerificationRequest";
}
}
DelegateChoice {
roleValue: MtxEvent.KeyVerificationStart
NoticeMessage {
text: "KeyVerificationStart";
}
}
DelegateChoice {
roleValue: MtxEvent.KeyVerificationReady
NoticeMessage {
text: "KeyVerificationReady";
}
}
DelegateChoice {
roleValue: MtxEvent.KeyVerificationCancel
NoticeMessage {
text: "KeyVerificationCancel";
}
}
DelegateChoice {
roleValue: MtxEvent.KeyVerificationKey
NoticeMessage {
text: "KeyVerificationKey";
}
}
DelegateChoice {
roleValue: MtxEvent.KeyVerificationMac
NoticeMessage {
text: "KeyVerificationMac";
}
}
DelegateChoice {
roleValue: MtxEvent.KeyVerificationDone
NoticeMessage {
text: "KeyVerificationDone";
}
}
DelegateChoice {
roleValue: MtxEvent.KeyVerificationDone
NoticeMessage {
text: "KeyVerificationDone";
}
}
DelegateChoice {
roleValue: MtxEvent.KeyVerificationAccept
NoticeMessage {
text: "KeyVerificationAccept";
}
}
DelegateChoice {
Placeholder {}
}
}
} }

View file

@ -1,6 +1,6 @@
TextMessage { TextMessage {
font.italic: true font.italic: true
color: colors.buttonText color: colors.buttonText
height: isReply ? Math.min(chat.height / 8, implicitHeight) : undefined height: isReply ? Math.min(chat.height / 8, implicitHeight) : undefined
clip: true clip: true
} }

View file

@ -2,13 +2,14 @@ import QtQuick 2.5
import QtQuick.Controls 2.1 import QtQuick.Controls 2.1
Label { Label {
color: colors.brightText color: colors.brightText
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
height: contentHeight * 1.2
width: contentWidth * 1.2
background: Rectangle {
radius: parent.height / 2
color: colors.dark
}
height: contentHeight * 1.2
width: contentWidth * 1.2
background: Rectangle {
radius: parent.height / 2
color: colors.dark
}
} }

View file

@ -1,7 +1,7 @@
import ".." import ".."
MatrixText { MatrixText {
text: qsTr("unimplemented event: ") + model.data.typeString text: qsTr("unimplemented event: ") + model.data.typeString
width: parent ? parent.width : undefined width: parent ? parent.width : undefined
color: inactiveColors.text color: inactiveColors.text
} }

View file

@ -1,173 +1,216 @@
import QtQuick 2.6
import QtQuick.Layouts 1.2
import QtQuick.Controls 2.1
import QtMultimedia 5.6 import QtMultimedia 5.6
import QtQuick 2.6
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.2
import im.nheko 1.0 import im.nheko 1.0
Rectangle { Rectangle {
id: bg id: bg
radius: 10
color: colors.dark
height: Math.round(content.height + 24)
width: parent ? parent.width : undefined
Column { radius: 10
id: content color: colors.dark
width: parent.width - 24 height: Math.round(content.height + 24)
anchors.centerIn: parent width: parent ? parent.width : undefined
Rectangle { Column {
id: videoContainer id: content
visible: model.data.type == MtxEvent.VideoMessage
property double tempWidth: Math.min(parent ? parent.width : undefined, model.data.width < 1 ? 400 : model.data.width)
property double tempHeight: tempWidth * model.data.proportionalHeight
property double divisor: model.isReply ? 4 : 2 width: parent.width - 24
property bool tooHigh: tempHeight > timelineRoot.height / divisor anchors.centerIn: parent
height: tooHigh ? timelineRoot.height / divisor : tempHeight Rectangle {
width: tooHigh ? (timelineRoot.height / divisor) / model.data.proportionalHeight : tempWidth id: videoContainer
Image {
anchors.fill: parent
source: model.data.thumbnailUrl.replace("mxc://", "image://MxcImage/")
asynchronous: true
fillMode: Image.PreserveAspectFit
VideoOutput { property double tempWidth: Math.min(parent ? parent.width : undefined, model.data.width < 1 ? 400 : model.data.width)
anchors.fill: parent property double tempHeight: tempWidth * model.data.proportionalHeight
fillMode: VideoOutput.PreserveAspectFit property double divisor: model.isReply ? 4 : 2
source: media property bool tooHigh: tempHeight > timelineRoot.height / divisor
}
}
}
RowLayout { visible: model.data.type == MtxEvent.VideoMessage
width: parent.width height: tooHigh ? timelineRoot.height / divisor : tempHeight
Text { width: tooHigh ? (timelineRoot.height / divisor) / model.data.proportionalHeight : tempWidth
id: positionText
text: "--:--:--"
color: colors.text
}
Slider {
Layout.fillWidth: true
id: progress
value: media.position
from: 0
to: media.duration
onMoved: media.seek(value) Image {
//indeterminate: true anchors.fill: parent
function updatePositionTexts() { source: model.data.thumbnailUrl.replace("mxc://", "image://MxcImage/")
function formatTime(date) { asynchronous: true
var hh = date.getUTCHours(); fillMode: Image.PreserveAspectFit
var mm = date.getUTCMinutes();
var ss = date.getSeconds();
if (hh < 10) {hh = "0"+hh;}
if (mm < 10) {mm = "0"+mm;}
if (ss < 10) {ss = "0"+ss;}
return hh+":"+mm+":"+ss;
}
positionText.text = formatTime(new Date(media.position))
durationText.text = formatTime(new Date(media.duration))
}
onValueChanged: updatePositionTexts()
palette: colors VideoOutput {
} anchors.fill: parent
Text { fillMode: VideoOutput.PreserveAspectFit
id: durationText source: media
text: "--:--:--" }
color: colors.text
}
}
RowLayout { }
width: parent.width
spacing: 15 }
Rectangle { RowLayout {
id: button width: parent.width
color: colors.window
radius: 22
height: 44
width: 44
Image {
id: img
anchors.centerIn: parent
z: 3
source: "image://colorimage/:/icons/icons/ui/arrow-pointing-down.png?"+colors.text Text {
fillMode: Image.Pad id: positionText
} text: "--:--:--"
MouseArea { color: colors.text
anchors.fill: parent }
onClicked: {
switch (button.state) {
case "": TimelineManager.timeline.cacheMedia(model.data.id); break;
case "stopped":
media.play(); console.log("play");
button.state = "playing"
break
case "playing":
media.pause(); console.log("pause");
button.state = "stopped"
break
}
}
cursorShape: Qt.PointingHandCursor
}
MediaPlayer {
id: media
onError: console.log(errorString)
onStatusChanged: if(status == MediaPlayer.Loaded) progress.updatePositionTexts()
onStopped: button.state = "stopped"
}
Connections { Slider {
target: TimelineManager.timeline id: progress
onMediaCached: {
if (mxcUrl == model.data.url) {
media.source = "file://" + cacheUrl
button.state = "stopped"
console.log("media loaded: " + mxcUrl + " at " + cacheUrl)
}
console.log("media cached: " + mxcUrl + " at " + cacheUrl)
}
}
states: [ //indeterminate: true
State { function updatePositionTexts() {
name: "stopped" function formatTime(date) {
PropertyChanges { target: img; source: "image://colorimage/:/icons/icons/ui/play-sign.png?"+colors.text } var hh = date.getUTCHours();
}, var mm = date.getUTCMinutes();
State { var ss = date.getSeconds();
name: "playing" if (hh < 10)
PropertyChanges { target: img; source: "image://colorimage/:/icons/icons/ui/pause-symbol.png?"+colors.text } hh = "0" + hh;
}
] if (mm < 10)
} mm = "0" + mm;
ColumnLayout {
id: col if (ss < 10)
ss = "0" + ss;
return hh + ":" + mm + ":" + ss;
}
positionText.text = formatTime(new Date(media.position));
durationText.text = formatTime(new Date(media.duration));
}
Layout.fillWidth: true
value: media.position
from: 0
to: media.duration
onMoved: media.seek(value)
onValueChanged: updatePositionTexts()
palette: colors
}
Text {
id: durationText
text: "--:--:--"
color: colors.text
}
}
RowLayout {
width: parent.width
spacing: 15
Rectangle {
id: button
color: colors.window
radius: 22
height: 44
width: 44
states: [
State {
name: "stopped"
PropertyChanges {
target: img
source: "image://colorimage/:/icons/icons/ui/play-sign.png?" + colors.text
}
},
State {
name: "playing"
PropertyChanges {
target: img
source: "image://colorimage/:/icons/icons/ui/pause-symbol.png?" + colors.text
}
}
]
Image {
id: img
anchors.centerIn: parent
z: 3
source: "image://colorimage/:/icons/icons/ui/arrow-pointing-down.png?" + colors.text
fillMode: Image.Pad
}
MouseArea {
anchors.fill: parent
onClicked: {
switch (button.state) {
case "":
TimelineManager.timeline.cacheMedia(model.data.id);
break;
case "stopped":
media.play();
console.log("play");
button.state = "playing";
break;
case "playing":
media.pause();
console.log("pause");
button.state = "stopped";
break;
}
}
cursorShape: Qt.PointingHandCursor
}
MediaPlayer {
id: media
onError: console.log(errorString)
onStatusChanged: {
if (status == MediaPlayer.Loaded)
progress.updatePositionTexts();
}
onStopped: button.state = "stopped"
}
Connections {
target: TimelineManager.timeline
onMediaCached: {
if (mxcUrl == model.data.url) {
media.source = "file://" + cacheUrl;
button.state = "stopped";
console.log("media loaded: " + mxcUrl + " at " + cacheUrl);
}
console.log("media cached: " + mxcUrl + " at " + cacheUrl);
}
}
}
ColumnLayout {
id: col
Text {
Layout.fillWidth: true
text: model.data.body
textFormat: Text.PlainText
elide: Text.ElideRight
color: colors.text
}
Text {
Layout.fillWidth: true
text: model.data.filesize
textFormat: Text.PlainText
elide: Text.ElideRight
color: colors.text
}
}
}
}
Text {
Layout.fillWidth: true
text: model.data.body
textFormat: Text.PlainText
elide: Text.ElideRight
color: colors.text
}
Text {
Layout.fillWidth: true
text: model.data.filesize
textFormat: Text.PlainText
elide: Text.ElideRight
color: colors.text
}
}
}
}
} }

View file

@ -2,66 +2,71 @@ import QtQuick 2.6
import QtQuick.Controls 2.3 import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2 import QtQuick.Layouts 1.2
import QtQuick.Window 2.2 import QtQuick.Window 2.2
import im.nheko 1.0 import im.nheko 1.0
Item { Item {
id: replyComponent id: replyComponent
property alias modelData: reply.modelData property alias modelData: reply.modelData
property color userColor: "red" property color userColor: "red"
width: parent.width width: parent.width
height: replyContainer.height height: replyContainer.height
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
preventStealing: true preventStealing: true
onClicked: chat.positionViewAtIndex(chat.model.idToIndex(modelData.id), ListView.Contain) onClicked: chat.positionViewAtIndex(chat.model.idToIndex(modelData.id), ListView.Contain)
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
} }
Rectangle { Rectangle {
id: colorLine id: colorLine
anchors.top: replyContainer.top anchors.top: replyContainer.top
anchors.bottom: replyContainer.bottom anchors.bottom: replyContainer.bottom
width: 4 width: 4
color: TimelineManager.userColor(reply.modelData.userId, colors.window)
}
color: TimelineManager.userColor(reply.modelData.userId, colors.window) Column {
} id: replyContainer
Column { anchors.left: colorLine.right
id: replyContainer anchors.leftMargin: 4
anchors.left: colorLine.right width: parent.width - 8
anchors.leftMargin: 4
width: parent.width - 8
Text { Text {
id: userName id: userName
text: TimelineManager.escapeEmoji(reply.modelData.userName)
color: replyComponent.userColor
textFormat: Text.RichText
MouseArea { text: TimelineManager.escapeEmoji(reply.modelData.userName)
anchors.fill: parent color: replyComponent.userColor
onClicked: chat.model.openUserProfile(reply.modelData.userId) textFormat: Text.RichText
cursorShape: Qt.PointingHandCursor
}
}
MessageDelegate { MouseArea {
id: reply anchors.fill: parent
width: parent.width onClicked: chat.model.openUserProfile(reply.modelData.userId)
isReply: true cursorShape: Qt.PointingHandCursor
} }
}
}
MessageDelegate {
id: reply
width: parent.width
isReply: true
}
}
Rectangle {
id: backgroundItem
z: -1
height: replyContainer.height
width: Math.min(Math.max(reply.implicitWidth, userName.implicitWidth) + 8 + 4, parent.width)
color: Qt.rgba(userColor.r, userColor.g, userColor.b, 0.2)
}
Rectangle {
id: backgroundItem
z: -1
height: replyContainer.height
width: Math.min(Math.max(reply.implicitWidth, userName.implicitWidth) + 8 + 4, parent.width)
color: Qt.rgba(userColor.r, userColor.g, userColor.b, 0.2)
}
} }

View file

@ -1,12 +1,12 @@
import ".." import ".."
import im.nheko 1.0 import im.nheko 1.0
MatrixText { MatrixText {
property string formatted: model.data.formattedBody property string formatted: model.data.formattedBody
text: "<style type=\"text/css\">a { color:"+colors.link+";}</style>" + formatted.replace("<pre>", "<pre style='white-space: pre-wrap'>")
width: parent ? parent.width : undefined text: "<style type=\"text/css\">a { color:" + colors.link + ";}</style>" + formatted.replace("<pre>", "<pre style='white-space: pre-wrap'>")
height: isReply ? Math.round(Math.min(timelineRoot.height / 8, implicitHeight)) : undefined width: parent ? parent.width : undefined
clip: true height: isReply ? Math.round(Math.min(timelineRoot.height / 8, implicitHeight)) : undefined
font.pointSize: (Settings.enlargeEmojiOnlyMessages && model.data.isOnlyEmoji > 0 && model.data.isOnlyEmoji < 4) ? Settings.fontSize * 3 : Settings.fontSize clip: true
font.pointSize: (Settings.enlargeEmojiOnlyMessages && model.data.isOnlyEmoji > 0 && model.data.isOnlyEmoji < 4) ? Settings.fontSize * 3 : Settings.fontSize
} }

View file

@ -1,39 +1,46 @@
import QtQuick 2.3 import QtQuick 2.3
import QtQuick.Controls 2.10 import QtQuick.Controls 2.10
import QtQuick.Layouts 1.10 import QtQuick.Layouts 1.10
import im.nheko 1.0 import im.nheko 1.0
Pane { Pane {
property string title: qsTr("Awaiting Confirmation") property string title: qsTr("Awaiting Confirmation")
ColumnLayout {
spacing: 16 ColumnLayout {
Label { spacing: 16
Layout.maximumWidth: 400
Layout.fillHeight: true Label {
Layout.fillWidth: true id: content
wrapMode: Text.Wrap
id: content Layout.maximumWidth: 400
text: qsTr("Waiting for other side to complete verification.") Layout.fillHeight: true
color:colors.text Layout.fillWidth: true
verticalAlignment: Text.AlignVCenter wrapMode: Text.Wrap
} text: qsTr("Waiting for other side to complete verification.")
BusyIndicator { color: colors.text
Layout.alignment: Qt.AlignHCenter verticalAlignment: Text.AlignVCenter
} }
RowLayout {
Button { BusyIndicator {
Layout.alignment: Qt.AlignLeft Layout.alignment: Qt.AlignHCenter
text: qsTr("Cancel") }
RowLayout {
Button {
Layout.alignment: Qt.AlignLeft
text: qsTr("Cancel")
onClicked: {
flow.cancel();
dialog.close();
}
}
Item {
Layout.fillWidth: true
}
}
}
onClicked: {
flow.cancel();
dialog.close();
}
}
Item {
Layout.fillWidth: true
}
}
}
} }

View file

@ -1,97 +1,144 @@
import QtQuick 2.10 import QtQuick 2.10
import QtQuick.Controls 2.10 import QtQuick.Controls 2.10
import QtQuick.Window 2.10 import QtQuick.Window 2.10
import im.nheko 1.0 import im.nheko 1.0
ApplicationWindow { ApplicationWindow {
property var flow id: dialog
onClosing: TimelineManager.removeVerificationFlow(flow) property var flow
title: stack.currentItem.title onClosing: TimelineManager.removeVerificationFlow(flow)
id: dialog title: stack.currentItem.title
flags: Qt.Dialog
palette: colors
height: stack.implicitHeight
width: stack.implicitWidth
flags: Qt.Dialog StackView {
id: stack
palette: colors initialItem: newVerificationRequest
implicitWidth: currentItem.implicitWidth
implicitHeight: currentItem.implicitHeight
}
height: stack.implicitHeight Component {
width: stack.implicitWidth id: newVerificationRequest
StackView { NewVerificationRequest {
id: stack }
initialItem: newVerificationRequest
implicitWidth: currentItem.implicitWidth
implicitHeight: currentItem.implicitHeight
}
Component{ }
id: newVerificationRequest
NewVerificationRequest {}
}
Component { Component {
id: waiting id: waiting
Waiting {}
}
Component { Waiting {
id: success }
Success {}
}
Component { }
id: failed
Failed {}
}
Component { Component {
id: digitVerification id: success
DigitVerification {}
}
Component { Success {
id: emojiVerification }
EmojiVerification {}
}
Item { }
state: flow.state
Component {
id: failed
Failed {
}
}
Component {
id: digitVerification
DigitVerification {
}
}
Component {
id: emojiVerification
EmojiVerification {
}
}
Item {
state: flow.state
states: [
State {
name: "PromptStartVerification"
StateChangeScript {
script: stack.replace(newVerificationRequest)
}
},
State {
name: "CompareEmoji"
StateChangeScript {
script: stack.replace(emojiVerification)
}
},
State {
name: "CompareNumber"
StateChangeScript {
script: stack.replace(digitVerification)
}
},
State {
name: "WaitingForKeys"
StateChangeScript {
script: stack.replace(waiting)
}
},
State {
name: "WaitingForOtherToAccept"
StateChangeScript {
script: stack.replace(waiting)
}
},
State {
name: "WaitingForMac"
StateChangeScript {
script: stack.replace(waiting)
}
},
State {
name: "Success"
StateChangeScript {
script: stack.replace(success)
}
},
State {
name: "Failed"
StateChangeScript {
script: stack.replace(failed)
}
}
]
}
states: [
State {
name: "PromptStartVerification"
StateChangeScript { script: stack.replace(newVerificationRequest) }
},
State {
name: "CompareEmoji"
StateChangeScript { script: stack.replace(emojiVerification) }
},
State {
name: "CompareNumber"
StateChangeScript { script: stack.replace(digitVerification) }
},
State {
name: "WaitingForKeys"
StateChangeScript { script: stack.replace(waiting) }
},
State {
name: "WaitingForOtherToAccept"
StateChangeScript { script: stack.replace(waiting) }
},
State {
name: "WaitingForMac"
StateChangeScript { script: stack.replace(waiting) }
},
State {
name: "Success"
StateChangeScript { script: stack.replace(success) }
},
State {
name: "Failed"
StateChangeScript { script: stack.replace(failed); }
}
]
}
} }

View file

@ -1,60 +1,69 @@
import QtQuick 2.3 import QtQuick 2.3
import QtQuick.Controls 2.10 import QtQuick.Controls 2.10
import QtQuick.Layouts 1.10 import QtQuick.Layouts 1.10
import im.nheko 1.0 import im.nheko 1.0
Pane { Pane {
property string title: qsTr("Verification Code") property string title: qsTr("Verification Code")
ColumnLayout { ColumnLayout {
spacing: 16 spacing: 16
Label {
Layout.maximumWidth: 400
Layout.fillHeight: true
Layout.fillWidth: true
wrapMode: Text.Wrap
text: qsTr("Please verify the following digits. You should see the same numbers on both sides. If they differ, please press 'They do not match!' to abort verification!")
color:colors.text
verticalAlignment: Text.AlignVCenter
}
RowLayout {
Layout.alignment: Qt.AlignHCenter
Label {
font.pixelSize: Qt.application.font.pixelSize * 2
text: flow.sasList[0]
color:colors.text
}
Label {
font.pixelSize: Qt.application.font.pixelSize * 2
text: flow.sasList[1]
color:colors.text
}
Label {
font.pixelSize: Qt.application.font.pixelSize * 2
text: flow.sasList[2]
color:colors.text
}
}
RowLayout {
Button {
Layout.alignment: Qt.AlignLeft
text: qsTr("They do not match!")
onClicked: { Label {
flow.cancel(); Layout.maximumWidth: 400
dialog.close(); Layout.fillHeight: true
} Layout.fillWidth: true
} wrapMode: Text.Wrap
Item { text: qsTr("Please verify the following digits. You should see the same numbers on both sides. If they differ, please press 'They do not match!' to abort verification!")
Layout.fillWidth: true color: colors.text
} verticalAlignment: Text.AlignVCenter
Button { }
Layout.alignment: Qt.AlignRight
text: qsTr("They match!") RowLayout {
Layout.alignment: Qt.AlignHCenter
Label {
font.pixelSize: Qt.application.font.pixelSize * 2
text: flow.sasList[0]
color: colors.text
}
Label {
font.pixelSize: Qt.application.font.pixelSize * 2
text: flow.sasList[1]
color: colors.text
}
Label {
font.pixelSize: Qt.application.font.pixelSize * 2
text: flow.sasList[2]
color: colors.text
}
}
RowLayout {
Button {
Layout.alignment: Qt.AlignLeft
text: qsTr("They do not match!")
onClicked: {
flow.cancel();
dialog.close();
}
}
Item {
Layout.fillWidth: true
}
Button {
Layout.alignment: Qt.AlignRight
text: qsTr("They match!")
onClicked: flow.next()
}
}
}
onClicked: flow.next();
}
}
}
} }

View file

@ -3,24 +3,31 @@ import QtQuick.Controls 2.10
import QtQuick.Layouts 1.10 import QtQuick.Layouts 1.10
Rectangle { Rectangle {
color: "red" color: "red"
implicitHeight: Qt.application.font.pixelSize * 4 implicitHeight: Qt.application.font.pixelSize * 4
implicitWidth: col.width implicitWidth: col.width
height: Qt.application.font.pixelSize * 4 height: Qt.application.font.pixelSize * 4
width: col.width width: col.width
ColumnLayout {
id: col ColumnLayout {
anchors.bottom: parent.bottom id: col
property var emoji: emojis.mapping[Math.floor(Math.random()*64)]
Label { property var emoji: emojis.mapping[Math.floor(Math.random() * 64)]
height: font.pixelSize * 2
Layout.alignment: Qt.AlignHCenter anchors.bottom: parent.bottom
text: col.emoji.emoji
font.pixelSize: Qt.application.font.pixelSize * 2 Label {
} height: font.pixelSize * 2
Label { Layout.alignment: Qt.AlignHCenter
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom text: col.emoji.emoji
text: col.emoji.description font.pixelSize: Qt.application.font.pixelSize * 2
} }
}
Label {
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
text: col.emoji.description
}
}
} }

View file

@ -1,140 +1,414 @@
import QtQuick 2.3 import QtQuick 2.3
import QtQuick.Controls 2.10 import QtQuick.Controls 2.10
import QtQuick.Layouts 1.10 import QtQuick.Layouts 1.10
import im.nheko 1.0 import im.nheko 1.0
Pane { Pane {
property string title: qsTr("Verification Code") property string title: qsTr("Verification Code")
ColumnLayout { ColumnLayout {
spacing: 16 spacing: 16
Label {
Layout.maximumWidth: 400
Layout.fillHeight: true
Layout.fillWidth: true
wrapMode: Text.Wrap
text: qsTr("Please verify the following emoji. You should see the same emoji on both sides. If they differ, please press 'They do not match!' to abort verification!")
color:colors.text
verticalAlignment: Text.AlignVCenter
}
RowLayout {
Layout.alignment: Qt.AlignHCenter
id: emojis
property var mapping: [
{"number": 0, "emoji": "🐶", "description": "Dog", "unicode": "U+1F436"},
{"number": 1, "emoji": "🐱", "description": "Cat", "unicode": "U+1F431"},
{"number": 2, "emoji": "🦁", "description": "Lion", "unicode": "U+1F981"},
{"number": 3, "emoji": "🐎", "description": "Horse", "unicode": "U+1F40E"},
{"number": 4, "emoji": "🦄", "description": "Unicorn", "unicode": "U+1F984"},
{"number": 5, "emoji": "🐷", "description": "Pig", "unicode": "U+1F437"},
{"number": 6, "emoji": "🐘", "description": "Elephant", "unicode": "U+1F418"},
{"number": 7, "emoji": "🐰", "description": "Rabbit", "unicode": "U+1F430"},
{"number": 8, "emoji": "🐼", "description": "Panda", "unicode": "U+1F43C"},
{"number": 9, "emoji": "🐓", "description": "Rooster", "unicode": "U+1F413"},
{"number": 10, "emoji": "🐧", "description": "Penguin", "unicode": "U+1F427"},
{"number": 11, "emoji": "🐢", "description": "Turtle", "unicode": "U+1F422"},
{"number": 12, "emoji": "🐟", "description": "Fish", "unicode": "U+1F41F"},
{"number": 13, "emoji": "🐙", "description": "Octopus", "unicode": "U+1F419"},
{"number": 14, "emoji": "🦋", "description": "Butterfly", "unicode": "U+1F98B"},
{"number": 15, "emoji": "🌷", "description": "Flower", "unicode": "U+1F337"},
{"number": 16, "emoji": "🌳", "description": "Tree", "unicode": "U+1F333"},
{"number": 17, "emoji": "🌵", "description": "Cactus", "unicode": "U+1F335"},
{"number": 18, "emoji": "🍄", "description": "Mushroom", "unicode": "U+1F344"},
{"number": 19, "emoji": "🌏", "description": "Globe", "unicode": "U+1F30F"},
{"number": 20, "emoji": "🌙", "description": "Moon", "unicode": "U+1F319"},
{"number": 21, "emoji": "☁️", "description": "Cloud", "unicode": "U+2601U+FE0F"},
{"number": 22, "emoji": "🔥", "description": "Fire", "unicode": "U+1F525"},
{"number": 23, "emoji": "🍌", "description": "Banana", "unicode": "U+1F34C"},
{"number": 24, "emoji": "🍎", "description": "Apple", "unicode": "U+1F34E"},
{"number": 25, "emoji": "🍓", "description": "Strawberry", "unicode": "U+1F353"},
{"number": 26, "emoji": "🌽", "description": "Corn", "unicode": "U+1F33D"},
{"number": 27, "emoji": "🍕", "description": "Pizza", "unicode": "U+1F355"},
{"number": 28, "emoji": "🎂", "description": "Cake", "unicode": "U+1F382"},
{"number": 29, "emoji": "❤️", "description": "Heart", "unicode": "U+2764U+FE0F"},
{"number": 30, "emoji": "😀", "description": "Smiley", "unicode": "U+1F600"},
{"number": 31, "emoji": "🤖", "description": "Robot", "unicode": "U+1F916"},
{"number": 32, "emoji": "🎩", "description": "Hat", "unicode": "U+1F3A9"},
{"number": 33, "emoji": "👓", "description": "Glasses", "unicode": "U+1F453"},
{"number": 34, "emoji": "🔧", "description": "Spanner", "unicode": "U+1F527"},
{"number": 35, "emoji": "🎅", "description": "Santa", "unicode": "U+1F385"},
{"number": 36, "emoji": "👍", "description": "Thumbs Up", "unicode": "U+1F44D"},
{"number": 37, "emoji": "☂️", "description": "Umbrella", "unicode": "U+2602U+FE0F"},
{"number": 38, "emoji": "⌛", "description": "Hourglass", "unicode": "U+231B"},
{"number": 39, "emoji": "⏰", "description": "Clock", "unicode": "U+23F0"},
{"number": 40, "emoji": "🎁", "description": "Gift", "unicode": "U+1F381"},
{"number": 41, "emoji": "💡", "description": "Light Bulb", "unicode": "U+1F4A1"},
{"number": 42, "emoji": "📕", "description": "Book", "unicode": "U+1F4D5"},
{"number": 43, "emoji": "✏️", "description": "Pencil", "unicode": "U+270FU+FE0F"},
{"number": 44, "emoji": "📎", "description": "Paperclip", "unicode": "U+1F4CE"},
{"number": 45, "emoji": "✂️", "description": "Scissors", "unicode": "U+2702U+FE0F"},
{"number": 46, "emoji": "🔒", "description": "Lock", "unicode": "U+1F512"},
{"number": 47, "emoji": "🔑", "description": "Key", "unicode": "U+1F511"},
{"number": 48, "emoji": "🔨", "description": "Hammer", "unicode": "U+1F528"},
{"number": 49, "emoji": "☎️", "description": "Telephone", "unicode": "U+260EU+FE0F"},
{"number": 50, "emoji": "🏁", "description": "Flag", "unicode": "U+1F3C1"},
{"number": 51, "emoji": "🚂", "description": "Train", "unicode": "U+1F682"},
{"number": 52, "emoji": "🚲", "description": "Bicycle", "unicode": "U+1F6B2"},
{"number": 53, "emoji": "✈️", "description": "Aeroplane", "unicode": "U+2708U+FE0F"},
{"number": 54, "emoji": "🚀", "description": "Rocket", "unicode": "U+1F680"},
{"number": 55, "emoji": "🏆", "description": "Trophy", "unicode": "U+1F3C6"},
{"number": 56, "emoji": "⚽", "description": "Ball", "unicode": "U+26BD"},
{"number": 57, "emoji": "🎸", "description": "Guitar", "unicode": "U+1F3B8"},
{"number": 58, "emoji": "🎺", "description": "Trumpet", "unicode": "U+1F3BA"},
{"number": 59, "emoji": "🔔", "description": "Bell", "unicode": "U+1F514"},
{"number": 60, "emoji": "⚓", "description": "Anchor", "unicode": "U+2693"},
{"number": 61, "emoji": "🎧", "description": "Headphones", "unicode": "U+1F3A7"},
{"number": 62, "emoji": "📁", "description": "Folder", "unicode": "U+1F4C1"},
{"number": 63, "emoji": "📌", "description": "Pin", "unicode": "U+1F4CC"}
]
Repeater {
id: repeater
model: 7
delegate: Rectangle {
color: "transparent"
implicitHeight: Qt.application.font.pixelSize * 8
implicitWidth: col.width
ColumnLayout {
id: col
Layout.fillWidth: true
anchors.bottom: parent.bottom
property var emoji: emojis.mapping[flow.sasList[index]]
Label {
//height: font.pixelSize * 2
Layout.alignment: Qt.AlignHCenter
text: col.emoji.emoji
font.pixelSize: Qt.application.font.pixelSize * 2
font.family: Settings.emojiFont
color:colors.text
}
Label {
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
text: col.emoji.description
color:colors.text
}
}
}
}
}
RowLayout {
Button {
Layout.alignment: Qt.AlignLeft
text: qsTr("They do not match!")
onClicked: { Label {
flow.cancel(); Layout.maximumWidth: 400
dialog.close(); Layout.fillHeight: true
} Layout.fillWidth: true
} wrapMode: Text.Wrap
Item { text: qsTr("Please verify the following emoji. You should see the same emoji on both sides. If they differ, please press 'They do not match!' to abort verification!")
Layout.fillWidth: true color: colors.text
} verticalAlignment: Text.AlignVCenter
Button { }
Layout.alignment: Qt.AlignRight
text: qsTr("They match!") RowLayout {
id: emojis
property var mapping: [{
"number": 0,
"emoji": "🐶",
"description": "Dog",
"unicode": "U+1F436"
}, {
"number": 1,
"emoji": "🐱",
"description": "Cat",
"unicode": "U+1F431"
}, {
"number": 2,
"emoji": "🦁",
"description": "Lion",
"unicode": "U+1F981"
}, {
"number": 3,
"emoji": "🐎",
"description": "Horse",
"unicode": "U+1F40E"
}, {
"number": 4,
"emoji": "🦄",
"description": "Unicorn",
"unicode": "U+1F984"
}, {
"number": 5,
"emoji": "🐷",
"description": "Pig",
"unicode": "U+1F437"
}, {
"number": 6,
"emoji": "🐘",
"description": "Elephant",
"unicode": "U+1F418"
}, {
"number": 7,
"emoji": "🐰",
"description": "Rabbit",
"unicode": "U+1F430"
}, {
"number": 8,
"emoji": "🐼",
"description": "Panda",
"unicode": "U+1F43C"
}, {
"number": 9,
"emoji": "🐓",
"description": "Rooster",
"unicode": "U+1F413"
}, {
"number": 10,
"emoji": "🐧",
"description": "Penguin",
"unicode": "U+1F427"
}, {
"number": 11,
"emoji": "🐢",
"description": "Turtle",
"unicode": "U+1F422"
}, {
"number": 12,
"emoji": "🐟",
"description": "Fish",
"unicode": "U+1F41F"
}, {
"number": 13,
"emoji": "🐙",
"description": "Octopus",
"unicode": "U+1F419"
}, {
"number": 14,
"emoji": "🦋",
"description": "Butterfly",
"unicode": "U+1F98B"
}, {
"number": 15,
"emoji": "🌷",
"description": "Flower",
"unicode": "U+1F337"
}, {
"number": 16,
"emoji": "🌳",
"description": "Tree",
"unicode": "U+1F333"
}, {
"number": 17,
"emoji": "🌵",
"description": "Cactus",
"unicode": "U+1F335"
}, {
"number": 18,
"emoji": "🍄",
"description": "Mushroom",
"unicode": "U+1F344"
}, {
"number": 19,
"emoji": "🌏",
"description": "Globe",
"unicode": "U+1F30F"
}, {
"number": 20,
"emoji": "🌙",
"description": "Moon",
"unicode": "U+1F319"
}, {
"number": 21,
"emoji": "☁️",
"description": "Cloud",
"unicode": "U+2601U+FE0F"
}, {
"number": 22,
"emoji": "🔥",
"description": "Fire",
"unicode": "U+1F525"
}, {
"number": 23,
"emoji": "🍌",
"description": "Banana",
"unicode": "U+1F34C"
}, {
"number": 24,
"emoji": "🍎",
"description": "Apple",
"unicode": "U+1F34E"
}, {
"number": 25,
"emoji": "🍓",
"description": "Strawberry",
"unicode": "U+1F353"
}, {
"number": 26,
"emoji": "🌽",
"description": "Corn",
"unicode": "U+1F33D"
}, {
"number": 27,
"emoji": "🍕",
"description": "Pizza",
"unicode": "U+1F355"
}, {
"number": 28,
"emoji": "🎂",
"description": "Cake",
"unicode": "U+1F382"
}, {
"number": 29,
"emoji": "❤️",
"description": "Heart",
"unicode": "U+2764U+FE0F"
}, {
"number": 30,
"emoji": "😀",
"description": "Smiley",
"unicode": "U+1F600"
}, {
"number": 31,
"emoji": "🤖",
"description": "Robot",
"unicode": "U+1F916"
}, {
"number": 32,
"emoji": "🎩",
"description": "Hat",
"unicode": "U+1F3A9"
}, {
"number": 33,
"emoji": "👓",
"description": "Glasses",
"unicode": "U+1F453"
}, {
"number": 34,
"emoji": "🔧",
"description": "Spanner",
"unicode": "U+1F527"
}, {
"number": 35,
"emoji": "🎅",
"description": "Santa",
"unicode": "U+1F385"
}, {
"number": 36,
"emoji": "👍",
"description": "Thumbs Up",
"unicode": "U+1F44D"
}, {
"number": 37,
"emoji": "☂️",
"description": "Umbrella",
"unicode": "U+2602U+FE0F"
}, {
"number": 38,
"emoji": "⌛",
"description": "Hourglass",
"unicode": "U+231B"
}, {
"number": 39,
"emoji": "⏰",
"description": "Clock",
"unicode": "U+23F0"
}, {
"number": 40,
"emoji": "🎁",
"description": "Gift",
"unicode": "U+1F381"
}, {
"number": 41,
"emoji": "💡",
"description": "Light Bulb",
"unicode": "U+1F4A1"
}, {
"number": 42,
"emoji": "📕",
"description": "Book",
"unicode": "U+1F4D5"
}, {
"number": 43,
"emoji": "✏️",
"description": "Pencil",
"unicode": "U+270FU+FE0F"
}, {
"number": 44,
"emoji": "📎",
"description": "Paperclip",
"unicode": "U+1F4CE"
}, {
"number": 45,
"emoji": "✂️",
"description": "Scissors",
"unicode": "U+2702U+FE0F"
}, {
"number": 46,
"emoji": "🔒",
"description": "Lock",
"unicode": "U+1F512"
}, {
"number": 47,
"emoji": "🔑",
"description": "Key",
"unicode": "U+1F511"
}, {
"number": 48,
"emoji": "🔨",
"description": "Hammer",
"unicode": "U+1F528"
}, {
"number": 49,
"emoji": "☎️",
"description": "Telephone",
"unicode": "U+260EU+FE0F"
}, {
"number": 50,
"emoji": "🏁",
"description": "Flag",
"unicode": "U+1F3C1"
}, {
"number": 51,
"emoji": "🚂",
"description": "Train",
"unicode": "U+1F682"
}, {
"number": 52,
"emoji": "🚲",
"description": "Bicycle",
"unicode": "U+1F6B2"
}, {
"number": 53,
"emoji": "✈️",
"description": "Aeroplane",
"unicode": "U+2708U+FE0F"
}, {
"number": 54,
"emoji": "🚀",
"description": "Rocket",
"unicode": "U+1F680"
}, {
"number": 55,
"emoji": "🏆",
"description": "Trophy",
"unicode": "U+1F3C6"
}, {
"number": 56,
"emoji": "⚽",
"description": "Ball",
"unicode": "U+26BD"
}, {
"number": 57,
"emoji": "🎸",
"description": "Guitar",
"unicode": "U+1F3B8"
}, {
"number": 58,
"emoji": "🎺",
"description": "Trumpet",
"unicode": "U+1F3BA"
}, {
"number": 59,
"emoji": "🔔",
"description": "Bell",
"unicode": "U+1F514"
}, {
"number": 60,
"emoji": "⚓",
"description": "Anchor",
"unicode": "U+2693"
}, {
"number": 61,
"emoji": "🎧",
"description": "Headphones",
"unicode": "U+1F3A7"
}, {
"number": 62,
"emoji": "📁",
"description": "Folder",
"unicode": "U+1F4C1"
}, {
"number": 63,
"emoji": "📌",
"description": "Pin",
"unicode": "U+1F4CC"
}]
Layout.alignment: Qt.AlignHCenter
Repeater {
id: repeater
model: 7
delegate: Rectangle {
color: "transparent"
implicitHeight: Qt.application.font.pixelSize * 8
implicitWidth: col.width
ColumnLayout {
id: col
property var emoji: emojis.mapping[flow.sasList[index]]
Layout.fillWidth: true
anchors.bottom: parent.bottom
Label {
//height: font.pixelSize * 2
Layout.alignment: Qt.AlignHCenter
text: col.emoji.emoji
font.pixelSize: Qt.application.font.pixelSize * 2
font.family: Settings.emojiFont
color: colors.text
}
Label {
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
text: col.emoji.description
color: colors.text
}
}
}
}
}
RowLayout {
Button {
Layout.alignment: Qt.AlignLeft
text: qsTr("They do not match!")
onClicked: {
flow.cancel();
dialog.close();
}
}
Item {
Layout.fillWidth: true
}
Button {
Layout.alignment: Qt.AlignRight
text: qsTr("They match!")
onClicked: flow.next()
}
}
}
onClicked: flow.next()
}
}
}
} }

View file

@ -1,44 +1,56 @@
import QtQuick 2.3 import QtQuick 2.3
import QtQuick.Controls 2.10 import QtQuick.Controls 2.10
import QtQuick.Layouts 1.10 import QtQuick.Layouts 1.10
import im.nheko 1.0 import im.nheko 1.0
Pane { Pane {
property string title: qsTr("Verification failed") property string title: qsTr("Verification failed")
ColumnLayout {
spacing: 16
Text {
id: content
Layout.maximumWidth: 400 ColumnLayout {
Layout.fillHeight: true spacing: 16
Layout.fillWidth: true
wrapMode: Text.Wrap Text {
text: switch (flow.error) { id: content
case DeviceVerificationFlow.UnknownMethod: return qsTr("Other client does not support our verification protocol.")
case DeviceVerificationFlow.MismatchedCommitment: Layout.maximumWidth: 400
case DeviceVerificationFlow.MismatchedSAS: Layout.fillHeight: true
case DeviceVerificationFlow.KeyMismatch: return qsTr("Key mismatch detected!") Layout.fillWidth: true
case DeviceVerificationFlow.Timeout: return qsTr("Device verification timed out.") wrapMode: Text.Wrap
case DeviceVerificationFlow.User: return qsTr("Other party canceled the verification.") text: {
case DeviceVerificationFlow.OutOfOrder: return qsTr("Device verification timed out.") switch (flow.error) {
default: return "Unknown verification error."; case DeviceVerificationFlow.UnknownMethod:
} return qsTr("Other client does not support our verification protocol.");
color:colors.text case DeviceVerificationFlow.MismatchedCommitment:
verticalAlignment: Text.AlignVCenter case DeviceVerificationFlow.MismatchedSAS:
} case DeviceVerificationFlow.KeyMismatch:
RowLayout { return qsTr("Key mismatch detected!");
Item { case DeviceVerificationFlow.Timeout:
Layout.fillWidth: true return qsTr("Device verification timed out.");
} case DeviceVerificationFlow.User:
Button { return qsTr("Other party canceled the verification.");
Layout.alignment: Qt.AlignRight case DeviceVerificationFlow.OutOfOrder:
text: qsTr("Close") return qsTr("Device verification timed out.");
default:
return "Unknown verification error.";
}
}
color: colors.text
verticalAlignment: Text.AlignVCenter
}
RowLayout {
Item {
Layout.fillWidth: true
}
Button {
Layout.alignment: Qt.AlignRight
text: qsTr("Close")
onClicked: dialog.close()
}
}
}
onClicked: dialog.close()
}
}
}
} }

View file

@ -1,44 +1,46 @@
import QtQuick 2.3 import QtQuick 2.3
import QtQuick.Controls 2.10 import QtQuick.Controls 2.10
import QtQuick.Layouts 1.10 import QtQuick.Layouts 1.10
import im.nheko 1.0 import im.nheko 1.0
Pane { Pane {
property string title: flow.sender ? qsTr("Send Device Verification Request") : qsTr("Recieved Device Verification Request") property string title: flow.sender ? qsTr("Send Device Verification Request") : qsTr("Recieved Device Verification Request")
ColumnLayout { ColumnLayout {
spacing: 16 spacing: 16
Label {
Layout.maximumWidth: 400
Layout.fillHeight: true
Layout.fillWidth: true
wrapMode: Text.Wrap
text: flow.sender ?
qsTr("To ensure that no malicious user can eavesdrop on your encrypted communications, you can verify this device.")
: qsTr("The device was requested to be verified")
color:colors.text
verticalAlignment: Text.AlignVCenter
}
RowLayout {
Button {
Layout.alignment: Qt.AlignLeft
text: flow.sender ? qsTr("Cancel") : qsTr("Deny")
onClicked: { Label {
flow.cancel(); Layout.maximumWidth: 400
dialog.close(); Layout.fillHeight: true
} Layout.fillWidth: true
} wrapMode: Text.Wrap
Item { text: flow.sender ? qsTr("To ensure that no malicious user can eavesdrop on your encrypted communications, you can verify this device.") : qsTr("The device was requested to be verified")
Layout.fillWidth: true color: colors.text
} verticalAlignment: Text.AlignVCenter
Button { }
Layout.alignment: Qt.AlignRight
text: flow.sender ? qsTr("Start verification") : qsTr("Accept") RowLayout {
Button {
Layout.alignment: Qt.AlignLeft
text: flow.sender ? qsTr("Cancel") : qsTr("Deny")
onClicked: {
flow.cancel();
dialog.close();
}
}
Item {
Layout.fillWidth: true
}
Button {
Layout.alignment: Qt.AlignRight
text: flow.sender ? qsTr("Start verification") : qsTr("Accept")
onClicked: flow.next()
}
}
}
onClicked: flow.next();
}
}
}
} }

View file

@ -3,29 +3,36 @@ import QtQuick.Controls 2.10
import QtQuick.Layouts 1.10 import QtQuick.Layouts 1.10
Pane { Pane {
property string title: qsTr("Successful Verification") property string title: qsTr("Successful Verification")
ColumnLayout {
spacing: 16 ColumnLayout {
Label { spacing: 16
Layout.maximumWidth: 400
Layout.fillHeight: true Label {
Layout.fillWidth: true id: content
wrapMode: Text.Wrap
id: content Layout.maximumWidth: 400
text: qsTr("Verification successful! Both sides verified their devices!") Layout.fillHeight: true
color:colors.text Layout.fillWidth: true
verticalAlignment: Text.AlignVCenter wrapMode: Text.Wrap
} text: qsTr("Verification successful! Both sides verified their devices!")
RowLayout { color: colors.text
Item { verticalAlignment: Text.AlignVCenter
Layout.fillWidth: true }
}
Button { RowLayout {
Layout.alignment: Qt.AlignRight Item {
text: qsTr("Close") Layout.fillWidth: true
}
Button {
Layout.alignment: Qt.AlignRight
text: qsTr("Close")
onClicked: dialog.close()
}
}
}
onClicked: dialog.close();
}
}
}
} }

View file

@ -1,45 +1,56 @@
import QtQuick 2.3 import QtQuick 2.3
import QtQuick.Controls 2.10 import QtQuick.Controls 2.10
import QtQuick.Layouts 1.10 import QtQuick.Layouts 1.10
import im.nheko 1.0 import im.nheko 1.0
Pane { Pane {
property string title: qsTr("Waiting for other party") property string title: qsTr("Waiting for other party")
ColumnLayout {
spacing: 16
Label {
Layout.maximumWidth: 400
Layout.fillHeight: true
Layout.fillWidth: true
wrapMode: Text.Wrap
id: content
text: switch (flow.state) {
case "WaitingForOtherToAccept": return qsTr("Waiting for other side to accept the verification request.")
case "WaitingForKeys": return qsTr("Waiting for other side to continue the verification request.")
case "WaitingForMac": return qsTr("Waiting for other side to complete the verification request.")
}
color: colors.text ColumnLayout {
verticalAlignment: Text.AlignVCenter spacing: 16
}
BusyIndicator { Label {
Layout.alignment: Qt.AlignHCenter id: content
palette: colors
} Layout.maximumWidth: 400
RowLayout { Layout.fillHeight: true
Button { Layout.fillWidth: true
Layout.alignment: Qt.AlignLeft wrapMode: Text.Wrap
text: qsTr("Cancel") text: {
switch (flow.state) {
case "WaitingForOtherToAccept":
return qsTr("Waiting for other side to accept the verification request.");
case "WaitingForKeys":
return qsTr("Waiting for other side to continue the verification request.");
case "WaitingForMac":
return qsTr("Waiting for other side to complete the verification request.");
}
}
color: colors.text
verticalAlignment: Text.AlignVCenter
}
BusyIndicator {
Layout.alignment: Qt.AlignHCenter
palette: colors
}
RowLayout {
Button {
Layout.alignment: Qt.AlignLeft
text: qsTr("Cancel")
onClicked: {
flow.cancel();
dialog.close();
}
}
Item {
Layout.fillWidth: true
}
}
}
onClicked: {
flow.cancel();
dialog.close();
}
}
Item {
Layout.fillWidth: true
}
}
}
} }

View file

@ -1,17 +1,16 @@
import "../"
import QtQuick 2.10 import QtQuick 2.10
import QtQuick.Controls 2.1 import QtQuick.Controls 2.1
import im.nheko 1.0 import im.nheko 1.0
import im.nheko.EmojiModel 1.0 import im.nheko.EmojiModel 1.0
import "../"
ImageButton { ImageButton {
id: emojiButton
property var colors: currentActivePalette property var colors: currentActivePalette
property var emojiPicker property var emojiPicker
property string event_id property string event_id
image: ":/icons/icons/ui/smile.png" image: ":/icons/icons/ui/smile.png"
id: emojiButton
onClicked: emojiPicker.visible ? emojiPicker.close() : emojiPicker.show(emojiButton, event_id) onClicked: emojiPicker.visible ? emojiPicker.close() : emojiPicker.show(emojiButton, event_id)
} }

View file

@ -1,25 +1,13 @@
import "../"
import QtGraphicalEffects 1.0
import QtQuick 2.9 import QtQuick 2.9
import QtQuick.Controls 2.3 import QtQuick.Controls 2.3
import QtQuick.Layouts 1.3 import QtQuick.Layouts 1.3
import QtGraphicalEffects 1.0
import im.nheko 1.0 import im.nheko 1.0
import im.nheko.EmojiModel 1.0 import im.nheko.EmojiModel 1.0
import "../"
Popup { Popup {
id: emojiPopup
function show(showAt, event_id) {
console.debug("Showing emojiPicker for " + event_id)
if (showAt){
parent = showAt
x = Math.round((showAt.width - width) / 2)
y = showAt.height
}
emojiPopup.event_id = event_id
open()
}
property string event_id property string event_id
property var colors property var colors
@ -30,19 +18,28 @@ Popup {
property real highlightSat: colors.highlight.hslSaturation property real highlightSat: colors.highlight.hslSaturation
property real highlightLight: colors.highlight.hslLightness property real highlightLight: colors.highlight.hslLightness
id: emojiPopup function show(showAt, event_id) {
console.debug("Showing emojiPicker for " + event_id);
if (showAt) {
parent = showAt;
x = Math.round((showAt.width - width) / 2);
y = showAt.height;
}
emojiPopup.event_id = event_id;
open();
}
margins: 0 margins: 0
bottomPadding: 1 bottomPadding: 1
leftPadding: 1 leftPadding: 1
rightPadding: 1 rightPadding: 1
modal: true modal: true
focus: true focus: true
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
ColumnLayout { ColumnLayout {
id: columnView id: columnView
anchors.fill: parent anchors.fill: parent
spacing: 0 spacing: 0
Layout.bottomMargin: 0 Layout.bottomMargin: 0
@ -58,23 +55,41 @@ Popup {
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
Layout.leftMargin: 4 Layout.leftMargin: 4
cellWidth: 52 cellWidth: 52
cellHeight: 52 cellHeight: 52
boundsBehavior: Flickable.StopAtBounds boundsBehavior: Flickable.StopAtBounds
clip: true clip: true
// Individual emoji // Individual emoji
delegate: AbstractButton { delegate: AbstractButton {
width: 48 width: 48
height: 48 height: 48
hoverEnabled: true
ToolTip.text: model.shortName
ToolTip.visible: hovered
// TODO: maybe add favorites at some point?
onClicked: {
console.debug("Picked " + model.unicode + "in response to " + emojiPopup.event_id);
emojiPopup.close();
TimelineManager.queueReactionMessage(emojiPopup.event_id, model.unicode);
}
// give the emoji a little oomf
DropShadow {
width: parent.width
height: parent.height
horizontalOffset: 3
verticalOffset: 3
radius: 8
samples: 17
color: "#80000000"
source: parent.contentItem
}
contentItem: Text { contentItem: Text {
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
font.family: Settings.emojiFont font.family: Settings.emojiFont
font.pixelSize: 36 font.pixelSize: 36
text: model.unicode text: model.unicode
} }
@ -85,76 +100,66 @@ Popup {
radius: 5 radius: 5
} }
hoverEnabled: true
ToolTip.text: model.shortName
ToolTip.visible: hovered
// give the emoji a little oomf
DropShadow {
width: parent.width;
height: parent.height;
horizontalOffset: 3
verticalOffset: 3
radius: 8.0
samples: 17
color: "#80000000"
source: parent.contentItem
}
// TODO: maybe add favorites at some point?
onClicked: {
console.debug("Picked " + model.unicode + "in response to " + emojiPopup.event_id)
emojiPopup.close()
TimelineManager.queueReactionMessage(emojiPopup.event_id, model.unicode)
}
} }
// Search field // Search field
header: TextField { header: TextField {
id: emojiSearch id: emojiSearch
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: emojiScroll.width + 4 anchors.rightMargin: emojiScroll.width + 4
placeholderText: qsTr("Search") placeholderText: qsTr("Search")
selectByMouse: true selectByMouse: true
rightPadding: clearSearch.width rightPadding: clearSearch.width
onTextChanged: searchTimer.restart()
onVisibleChanged: {
if (visible)
forceActiveFocus();
}
Timer { Timer {
id: searchTimer id: searchTimer
interval: 350 // tweak as needed? interval: 350 // tweak as needed?
onTriggered: { onTriggered: {
emojiPopup.model.filter = emojiSearch.text emojiPopup.model.filter = emojiSearch.text;
emojiPopup.model.category = EmojiCategory.Search emojiPopup.model.category = EmojiCategory.Search;
} }
} }
ToolButton { ToolButton {
id: clearSearch id: clearSearch
visible: emojiSearch.text !== ''
icon.source: "image://colorimage/:/icons/icons/ui/round-remove-button.png?" + (clearSearch.hovered ? colors.highlight : colors.buttonText)
focusPolicy: Qt.NoFocus
onClicked: emojiSearch.clear()
anchors { anchors {
verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter
right: parent.right right: parent.right
} }
// clear the default hover effects. // clear the default hover effects.
background: Item {}
visible: emojiSearch.text !== '' background: Item {
icon.source: "image://colorimage/:/icons/icons/ui/round-remove-button.png?" + (clearSearch.hovered ? colors.highlight : colors.buttonText) }
focusPolicy: Qt.NoFocus
onClicked: emojiSearch.clear()
} }
onTextChanged: searchTimer.restart()
onVisibleChanged: if (visible) forceActiveFocus()
} }
ScrollBar.vertical: ScrollBar { ScrollBar.vertical: ScrollBar {
id: emojiScroll id: emojiScroll
} }
} }
// Separator // Separator
Rectangle { Rectangle {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 1 Layout.preferredHeight: 1
color: emojiPopup.colors.dark color: emojiPopup.colors.dark
} }
@ -164,23 +169,90 @@ Popup {
Layout.preferredHeight: 42 Layout.preferredHeight: 42
implicitHeight: 42 implicitHeight: 42
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
// Display the normal categories // Display the normal categories
Repeater { Repeater {
model: ListModel { model: ListModel {
// TODO: Would like to get 'simple' icons for the categories // TODO: Would like to get 'simple' icons for the categories
ListElement { image: ":/icons/icons/emoji-categories/people.png"; category: EmojiCategory.People } ListElement {
ListElement { image: ":/icons/icons/emoji-categories/nature.png"; category: EmojiCategory.Nature } image: ":/icons/icons/emoji-categories/people.png"
ListElement { image: ":/icons/icons/emoji-categories/foods.png"; category: EmojiCategory.Food } category: EmojiCategory.People
ListElement { image: ":/icons/icons/emoji-categories/activity.png"; category: EmojiCategory.Activity } }
ListElement { image: ":/icons/icons/emoji-categories/travel.png"; category: EmojiCategory.Travel }
ListElement { image: ":/icons/icons/emoji-categories/objects.png"; category: EmojiCategory.Objects } ListElement {
ListElement { image: ":/icons/icons/emoji-categories/symbols.png"; category: EmojiCategory.Symbols } image: ":/icons/icons/emoji-categories/nature.png"
ListElement { image: ":/icons/icons/emoji-categories/flags.png"; category: EmojiCategory.Flags } category: EmojiCategory.Nature
}
ListElement {
image: ":/icons/icons/emoji-categories/foods.png"
category: EmojiCategory.Food
}
ListElement {
image: ":/icons/icons/emoji-categories/activity.png"
category: EmojiCategory.Activity
}
ListElement {
image: ":/icons/icons/emoji-categories/travel.png"
category: EmojiCategory.Travel
}
ListElement {
image: ":/icons/icons/emoji-categories/objects.png"
category: EmojiCategory.Objects
}
ListElement {
image: ":/icons/icons/emoji-categories/symbols.png"
category: EmojiCategory.Symbols
}
ListElement {
image: ":/icons/icons/emoji-categories/flags.png"
category: EmojiCategory.Flags
}
} }
delegate: AbstractButton { delegate: AbstractButton {
Layout.preferredWidth: 36 Layout.preferredWidth: 36
Layout.preferredHeight: 36 Layout.preferredHeight: 36
hoverEnabled: true
ToolTip.text: {
switch (model.category) {
case EmojiCategory.People:
return qsTr('People');
case EmojiCategory.Nature:
return qsTr('Nature');
case EmojiCategory.Food:
return qsTr('Food');
case EmojiCategory.Activity:
return qsTr('Activity');
case EmojiCategory.Travel:
return qsTr('Travel');
case EmojiCategory.Objects:
return qsTr('Objects');
case EmojiCategory.Symbols:
return qsTr('Symbols');
case EmojiCategory.Flags:
return qsTr('Flags');
}
}
ToolTip.visible: hovered
onClicked: {
emojiPopup.model.category = model.category;
}
MouseArea {
id: mouseArea
anchors.fill: parent
onPressed: mouse.accepted = false
cursorShape: Qt.PointingHandCursor
}
contentItem: Image { contentItem: Image {
horizontalAlignment: Image.AlignHCenter horizontalAlignment: Image.AlignHCenter
@ -191,49 +263,15 @@ Popup {
source: "image://colorimage/" + model.image + "?" + (hovered ? colors.highlight : colors.buttonText) source: "image://colorimage/" + model.image + "?" + (hovered ? colors.highlight : colors.buttonText)
} }
MouseArea
{
id: mouseArea
anchors.fill: parent
onPressed: mouse.accepted = false
cursorShape: Qt.PointingHandCursor
}
background: Rectangle { background: Rectangle {
anchors.fill: parent anchors.fill: parent
color: emojiPopup.model.category === model.category ? Qt.hsla(highlightHue, highlightSat, highlightLight, 0.2) : 'transparent'
color: emojiPopup.model.category === model.category ? Qt.hsla(highlightHue, highlightSat, highlightLight, 0.20) : 'transparent'
radius: 5 radius: 5
border.color: emojiPopup.model.category === model.category ? colors.highlight : 'transparent' border.color: emojiPopup.model.category === model.category ? colors.highlight : 'transparent'
} }
hoverEnabled: true
ToolTip.text: {
switch (model.category) {
case EmojiCategory.People:
return qsTr('People');
case EmojiCategory.Nature:
return qsTr('Nature');
case EmojiCategory.Food:
return qsTr('Food');
case EmojiCategory.Activity:
return qsTr('Activity');
case EmojiCategory.Travel:
return qsTr('Travel');
case EmojiCategory.Objects:
return qsTr('Objects');
case EmojiCategory.Symbols:
return qsTr('Symbols');
case EmojiCategory.Flags:
return qsTr('Flags');
}
}
ToolTip.visible: hovered
onClicked: {
emojiPopup.model.category = model.category
}
} }
} }
// Separator // Separator
@ -242,30 +280,37 @@ Popup {
Layout.preferredWidth: 1 Layout.preferredWidth: 1
implicitWidth: 1 implicitWidth: 1
height: parent.height height: parent.height
color: emojiPopup.colors.dark color: emojiPopup.colors.dark
} }
// Search Button is special // Search Button is special
AbstractButton { AbstractButton {
id: searchBtn id: searchBtn
hoverEnabled: true hoverEnabled: true
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
Layout.bottomMargin: 0 Layout.bottomMargin: 0
ToolTip.text: qsTr("Search") ToolTip.text: qsTr("Search")
ToolTip.visible: hovered ToolTip.visible: hovered
onClicked: { onClicked: {
// clear any filters // clear any filters
emojiPopup.model.category = EmojiCategory.Search emojiPopup.model.category = EmojiCategory.Search;
gridView.positionViewAtBeginning() gridView.positionViewAtBeginning();
emojiSearch.forceActiveFocus() emojiSearch.forceActiveFocus();
} }
Layout.preferredWidth: 36 Layout.preferredWidth: 36
Layout.preferredHeight: 36 Layout.preferredHeight: 36
implicitWidth: 36 implicitWidth: 36
implicitHeight: 36 implicitHeight: 36
MouseArea {
id: mouseArea
anchors.fill: parent
onPressed: mouse.accepted = false
cursorShape: Qt.PointingHandCursor
}
contentItem: Image { contentItem: Image {
anchors.right: parent.right anchors.right: parent.right
horizontalAlignment: Image.AlignHCenter horizontalAlignment: Image.AlignHCenter
@ -277,14 +322,10 @@ Popup {
source: "image://colorimage/:/icons/icons/ui/search.png?" + (parent.hovered ? colors.highlight : colors.buttonText) source: "image://colorimage/:/icons/icons/ui/search.png?" + (parent.hovered ? colors.highlight : colors.buttonText)
} }
MouseArea
{
id: mouseArea
anchors.fill: parent
onPressed: mouse.accepted = false
cursorShape: Qt.PointingHandCursor
}
} }
} }
} }
} }