From 8677349c4a0c55c824a1e848e5e7c64e087b5efc Mon Sep 17 00:00:00 2001 From: pmagnier-slimani Date: Wed, 18 Jun 2025 10:21:52 -0400 Subject: [PATCH] accessibility: fix chatview Add keyboard and screen-reader navigation for the chat view. Change-Id: I11a5dc1ee3b0d6303f4598f10008ecc6979bb777 --- src/app/MainApplicationWindow.qml | 2 + .../commoncomponents/CallMessageDelegate.qml | 57 +++++++++------- .../ContactMessageDelegate.qml | 38 ++++++++--- .../DataTransferMessageDelegate.qml | 66 +++++++++++-------- .../GeneratedMessageDelegate.qml | 24 +++++-- src/app/commoncomponents/SBSMessageBase.qml | 23 +++++-- .../commoncomponents/TextMessageDelegate.qml | 46 ++++++++----- .../mainview/components/MessageListView.qml | 33 +++++++++- src/app/net/jami/Constants/JamiStrings.qml | 4 ++ 9 files changed, 205 insertions(+), 88 deletions(-) diff --git a/src/app/MainApplicationWindow.qml b/src/app/MainApplicationWindow.qml index 1ed6d108..6ce1d761 100644 --- a/src/app/MainApplicationWindow.qml +++ b/src/app/MainApplicationWindow.qml @@ -53,6 +53,8 @@ ApplicationWindow { focusOverlay.margin = 0; focusOverlay.parent = activeFocusItem; } + } else { + focusOverlay.parent = null; } } else { focusOverlay.parent = null; diff --git a/src/app/commoncomponents/CallMessageDelegate.qml b/src/app/commoncomponents/CallMessageDelegate.qml index b654ae9e..57d33c19 100644 --- a/src/app/commoncomponents/CallMessageDelegate.qml +++ b/src/app/commoncomponents/CallMessageDelegate.qml @@ -23,12 +23,12 @@ import net.jami.Adapters 1.1 import net.jami.Constants 1.1 SBSMessageBase { - id: root + id: rootDelegate property var confId: ConfId property var currentCallId: CurrentCall.id component JoinCallButton: MaterialButton { - visible: root.isActive && root.currentCallId !== root.confId + visible: rootDelegate.isActive && rootDelegate.currentCallId !== rootDelegate.confId toolTipText: JamiStrings.joinCall color: JamiTheme.blackColor background.opacity: hovered ? 0.2 : 0.1 @@ -40,6 +40,20 @@ SBSMessageBase { textRightPadding: 9 } + Accessible.role: Accessible.StaticText + Accessible.name: { + let name = isOutgoing ? JamiStrings.inReplyToYou : UtilsAdapter.getBestNameForUri(CurrentAccount.id, Author); + return name + ": " + callLabel.text + " " + formattedDay; + } + Accessible.description: { + let status = ""; + if (bubble.isEdited) + status += JamiStrings.edited + " "; + return status + (readers.length > 0 ? JamiStrings.readBy + " " + readers.map(function (uri) { + return UtilsAdapter.getBestNameForUri(CurrentAccount.id, uri); + }).join(", ") : ""); + } + property bool isRemoteImage isOutgoing: Author === CurrentAccount.uri @@ -48,17 +62,17 @@ SBSMessageBase { formattedTime: MessagesAdapter.getFormattedTime(Timestamp) bubble.border.color: CurrentConversation.color - bubble.border.width: root.isActive ? 1.5 : 0 + bubble.border.width: rootDelegate.isActive ? 1.5 : 0 bubble.color: JamiTheme.messageInBgColor bubble.opacity: 1 Connections { target: CurrentConversation - enabled: root.isActive + enabled: rootDelegate.isActive function onActiveCallsChanged() { - root.isActive = LRCInstance.indexOfActiveCall(root.confId, ActionUri, DeviceId) !== -1; - if (root.isActive) { + rootDelegate.isActive = LRCInstance.indexOfActiveCall(rootDelegate.confId, ActionUri, DeviceId) !== -1; + if (rootDelegate.isActive) { bubble.mask.border.color = CurrentConversation.color; bubble.mask.border.width = 1.5; bubble.mask.z = -2; @@ -66,8 +80,8 @@ SBSMessageBase { } } - property bool isActive: LRCInstance.indexOfActiveCall(root.confId, ActionUri, DeviceId) !== -1 - visible: isActive || root.confId === "" || Duration > 0 + property bool isActive: LRCInstance.indexOfActiveCall(rootDelegate.confId, ActionUri, DeviceId) !== -1 + visible: isActive || rootDelegate.confId === "" || Duration > 0 property var baseColor: JamiTheme.messageInBgColor @@ -76,7 +90,7 @@ SBSMessageBase { id: msg anchors.right: isOutgoing ? parent.right : undefined spacing: 10 - visible: root.visible + visible: rootDelegate.visible Image { id: statusIcon @@ -84,10 +98,10 @@ SBSMessageBase { width: 10 height: 10 verticalAlignment: Qt.AlignVCenter - visible: !root.isActive + visible: !rootDelegate.isActive source: { - if (root.isOutgoing) { + if (rootDelegate.isOutgoing) { if (Duration > 0) return "qrc:/icons/outgoing-call.svg"; else @@ -104,12 +118,11 @@ SBSMessageBase { effect: ColorOverlay { color: { if (Duration > 0) - return UtilsAdapter.luma(root.baseColor) ? JamiTheme.chatviewTextColorLight : JamiTheme.chatviewTextColorDark - return JamiTheme.redColor + return UtilsAdapter.luma(rootDelegate.baseColor) ? JamiTheme.chatviewTextColorLight : JamiTheme.chatviewTextColorDark; + return JamiTheme.redColor; } } } - } Text { @@ -120,11 +133,11 @@ SBSMessageBase { bottomPadding: 8 Layout.fillWidth: true - Layout.rightMargin: root.isActive && root.currentCallId !== root.confId ? 0 : root.timeWidth + 16 - Layout.leftMargin: root.isActive ? 10 : -5 /* spacing is 10 and we want 5px with icon */ + Layout.rightMargin: rootDelegate.isActive && rootDelegate.currentCallId !== rootDelegate.confId ? 0 : rootDelegate.timeWidth + 16 + Layout.leftMargin: rootDelegate.isActive ? 10 : -5 /* spacing is 10 and we want 5px with icon */ text: { - if (root.isActive) + if (rootDelegate.isActive) return JamiStrings.startedACall; return Body; } @@ -136,7 +149,7 @@ SBSMessageBase { renderType: Text.NativeRendering textFormat: Text.MarkdownText - color: UtilsAdapter.luma(root.baseColor) ? JamiTheme.chatviewTextColorLight : JamiTheme.chatviewTextColorDark + color: UtilsAdapter.luma(rootDelegate.baseColor) ? JamiTheme.chatviewTextColorLight : JamiTheme.chatviewTextColorDark } JoinCallButton { @@ -146,7 +159,7 @@ SBSMessageBase { Layout.bottomMargin: 4 text: JamiStrings.joinWithAudio - onClicked: MessagesAdapter.joinCall(ActionUri, DeviceId, root.confId, true) + onClicked: MessagesAdapter.joinCall(ActionUri, DeviceId, rootDelegate.confId, true) } JoinCallButton { @@ -156,20 +169,20 @@ SBSMessageBase { Layout.topMargin: 4 Layout.bottomMargin: 4 - onClicked: MessagesAdapter.joinCall(ActionUri, DeviceId, root.confId) + onClicked: MessagesAdapter.joinCall(ActionUri, DeviceId, rootDelegate.confId) Layout.rightMargin: 4 } } ] opacity: 0 - Behavior on opacity { + Behavior on opacity { NumberAnimation { duration: 100 } } Component.onCompleted: { - bubble.timestampItem.visible = !root.isActive || root.currentCallId === root.confId; + bubble.timestampItem.visible = !rootDelegate.isActive || rootDelegate.currentCallId === rootDelegate.confId; opacity = 1; } } diff --git a/src/app/commoncomponents/ContactMessageDelegate.qml b/src/app/commoncomponents/ContactMessageDelegate.qml index b42c834f..c14770ca 100644 --- a/src/app/commoncomponents/ContactMessageDelegate.qml +++ b/src/app/commoncomponents/ContactMessageDelegate.qml @@ -21,8 +21,8 @@ import net.jami.Models 1.1 import net.jami.Adapters 1.1 import net.jami.Constants 1.1 -Column { - id: root +Control { + id: rootDelegate property bool showTime: false property bool showDay: false @@ -36,21 +36,41 @@ Column { height: timestampItem.height + textLabel.height spacing: 0 - Item { + Accessible.name: { + let name = UtilsAdapter.getBestNameForUri(CurrentAccount.id, Author); + return name + ": " + Body + " " + formattedTime + " " + formattedDay; + } + Accessible.description: { + let status = ""; + if (IsLastSent) + status += JamiStrings.sent + " "; + return status; + } + + background: Rectangle { + id: focusIndicator + visible: rootDelegate.activeFocus + border.color: JamiTheme.tintedBlue + border.width: 2 + radius: 10 + color: "transparent" + z: 1 + } + + contentItem: Item { anchors.horizontalCenter: parent.horizontalCenter height: timestampItem.height + textLabel.height TimestampInfo { id: timestampItem - showDay: root.showDay - showTime: root.showTime - formattedTime: root.formattedTime - formattedDay: root.formattedDay + showDay: rootDelegate.showDay + showTime: rootDelegate.showTime + formattedTime: rootDelegate.formattedTime + formattedDay: rootDelegate.formattedDay anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top - } Label { @@ -67,7 +87,7 @@ Column { } } opacity: 0 - Behavior on opacity { + Behavior on opacity { NumberAnimation { duration: 100 } diff --git a/src/app/commoncomponents/DataTransferMessageDelegate.qml b/src/app/commoncomponents/DataTransferMessageDelegate.qml index 24aa11d9..f0641f58 100644 --- a/src/app/commoncomponents/DataTransferMessageDelegate.qml +++ b/src/app/commoncomponents/DataTransferMessageDelegate.qml @@ -24,20 +24,32 @@ import net.jami.Constants 1.1 import net.jami.Adapters 1.1 Loader { - id: root + id: rootDelegate property var mediaInfo property bool showTime property bool showDay property int timestamp: Timestamp - property string formattedTime: MessagesAdapter.getFormattedTime(root.timestamp) - property string formattedDay: MessagesAdapter.getFormattedDay(root.timestamp) + property string formattedTime: MessagesAdapter.getFormattedTime(rootDelegate.timestamp) + property string formattedDay: MessagesAdapter.getFormattedDay(rootDelegate.timestamp) property int seq: MsgSeq.single property string author: Author property string body: Body property var tid: TID property int transferStatus: TransferStatus + + Accessible.name: { + let name = UtilsAdapter.getBestNameForUri(CurrentAccount.id, Author); + return JamiStrings.dataTransfer + name + ": " + JamiStrings.status + TransferStatus + Body + " " + formattedTime + " " + formattedDay; + } + Accessible.description: { + let status = ""; + if (IsLastSent) + status += JamiStrings.sent + " "; + return status; + } + onTidChanged: { if (tid === "") { sourceComponent = deletedMsgComp; @@ -48,7 +60,7 @@ Loader { sourceComponent = deletedMsgComp; return; } else if (transferStatus === Interaction.TransferStatus.TRANSFER_FINISHED) { - mediaInfo = MessagesAdapter.getMediaInfo(root.body); + mediaInfo = MessagesAdapter.getMediaInfo(rootDelegate.body); if (Object.keys(mediaInfo).length !== 0 && WITH_WEBENGINE) { sourceComponent = localMediaMsgComp; return; @@ -74,13 +86,13 @@ Loader { id: deletedItem isOutgoing: Author === CurrentAccount.uri - showTime: root.showTime - seq: root.seq + showTime: rootDelegate.showTime + seq: rootDelegate.seq author: Author readers: Readers - timestamp: root.timestamp - formattedTime: root.formattedTime - formattedDay: root.formattedTime + timestamp: rootDelegate.timestamp + formattedTime: rootDelegate.formattedTime + formattedDay: rootDelegate.formattedTime extraHeight: 0 textContentWidth: textEditId.width textContentHeight: textEditId.height @@ -122,34 +134,34 @@ Loader { id: dataTransferItem transferId: Id - property var transferStats: MessagesAdapter.getTransferStats(transferId, root.transferStatus) - property bool canOpen: root.transferStatus === Interaction.TransferStatus.TRANSFER_FINISHED || isOutgoing - property real maxMsgWidth: root.width - senderMargin - 2 * hPadding - avatarBlockWidth - buttonsLoader.width - 24 - 6 - 24 + property var transferStats: MessagesAdapter.getTransferStats(transferId, rootDelegate.transferStatus) + property bool canOpen: rootDelegate.transferStatus === Interaction.TransferStatus.TRANSFER_FINISHED || isOutgoing + property real maxMsgWidth: rootDelegate.width - senderMargin - 2 * hPadding - avatarBlockWidth - buttonsLoader.width - 24 - 6 - 24 // Timer to update the translation bar Loader { id: timerLoader - active: root.transferStatus === Interaction.TransferStatus.TRANSFER_ONGOING + active: rootDelegate.transferStatus === Interaction.TransferStatus.TRANSFER_ONGOING sourceComponent: Timer { interval: 1000 // Update every second running: true repeat: true onTriggered: { - transferStats = MessagesAdapter.getTransferStats(transferId, root.transferStatus); + transferStats = MessagesAdapter.getTransferStats(transferId, rootDelegate.transferStatus); } } } isOutgoing: Author === CurrentAccount.uri - showTime: root.showTime - seq: root.seq + showTime: rootDelegate.showTime + seq: rootDelegate.seq author: Author location: Body transferName: TransferName readers: Readers - timestamp: root.timestamp - formattedTime: root.formattedTime - formattedDay: root.formattedTime + timestamp: rootDelegate.timestamp + formattedTime: rootDelegate.formattedTime + formattedDay: rootDelegate.formattedTime extraHeight: progressBar.visible ? 25 : 0 innerContent.children: [ @@ -178,7 +190,7 @@ Loader { Layout.margins: 8 sourceComponent: { - switch (root.transferStatus) { + switch (rootDelegate.transferStatus) { case Interaction.TransferStatus.TRANSFER_CREATED: case Interaction.TransferStatus.TRANSFER_FINISHED: iconSource = JamiResources.link_black_24dp_svg; @@ -225,7 +237,7 @@ Loader { normalColor: JamiTheme.chatviewBgColor imageColor: JamiTheme.chatviewButtonColor onClicked: { - if (root.transferStatus === Interaction.TransferStatus.TRANSFER_ONGOING) { + if (rootDelegate.transferStatus === Interaction.TransferStatus.TRANSFER_ONGOING) { MessagesAdapter.cancelFile(transferId); } else { buttonsLoader.iconSource = JamiResources.connecting_black_24dp_svg; @@ -287,7 +299,7 @@ Loader { ProgressBar { id: progressBar - visible: root.transferStatus === Interaction.TransferStatus.TRANSFER_ONGOING + visible: rootDelegate.transferStatus === Interaction.TransferStatus.TRANSFER_ONGOING height: visible * implicitHeight value: transferStats.progress / transferStats.totalSize width: transferItem.width @@ -305,15 +317,15 @@ Loader { isOutgoing: Author === CurrentAccount.uri transferId: Id - property var transferStats: MessagesAdapter.getTransferStats(transferId, root.transferStatus) - showTime: root.showTime - seq: root.seq + property var transferStats: MessagesAdapter.getTransferStats(transferId, rootDelegate.transferStatus) + showTime: rootDelegate.showTime + seq: rootDelegate.seq author: Author location: Body transferName: TransferName readers: Readers - formattedTime: MessagesAdapter.getFormattedTime(root.timestamp) - formattedDay: MessagesAdapter.getFormattedDay(root.timestamp) + formattedTime: MessagesAdapter.getFormattedTime(rootDelegate.timestamp) + formattedDay: MessagesAdapter.getFormattedDay(rootDelegate.timestamp) property real contentWidth diff --git a/src/app/commoncomponents/GeneratedMessageDelegate.qml b/src/app/commoncomponents/GeneratedMessageDelegate.qml index 7cf1ea73..1ad7d2ff 100644 --- a/src/app/commoncomponents/GeneratedMessageDelegate.qml +++ b/src/app/commoncomponents/GeneratedMessageDelegate.qml @@ -21,7 +21,7 @@ import net.jami.Adapters 1.1 import net.jami.Constants 1.1 Column { - id: root + id: rootDelegate property bool showTime: false property bool showDay: false @@ -34,6 +34,18 @@ Column { spacing: 2 topPadding: 12 bottomPadding: 12 + + Accessible.name: { + let name = UtilsAdapter.getBestNameForUri(CurrentAccount.id, Author); + return name + ": " + Body + " " + formattedTime + " " + formattedDay; + } + Accessible.description: { + let status = ""; + if (IsLastSent) + status += JamiStrings.sent + " "; + return status; + } + ColumnLayout { width: parent.width @@ -42,10 +54,10 @@ Column { TimestampInfo { id: timestampItem - showDay: root.showDay - showTime: root.showTime - formattedTime: root.formattedTime - formattedDay: root.formattedDay + showDay: rootDelegate.showDay + showTime: rootDelegate.showTime + formattedTime: rootDelegate.formattedTime + formattedDay: rootDelegate.formattedDay Layout.alignment: Qt.AlignHCenter } @@ -60,7 +72,7 @@ Column { } opacity: 0 - Behavior on opacity { + Behavior on opacity { NumberAnimation { duration: 100 } diff --git a/src/app/commoncomponents/SBSMessageBase.qml b/src/app/commoncomponents/SBSMessageBase.qml index 260608b3..00a7e948 100644 --- a/src/app/commoncomponents/SBSMessageBase.qml +++ b/src/app/commoncomponents/SBSMessageBase.qml @@ -24,6 +24,7 @@ import net.jami.Constants 1.1 Control { id: root + Accessible.role: Accessible.StaticText property alias avatarBlockWidth: avatarBlock.width property alias innerContent: innerContent @@ -64,6 +65,7 @@ Control { property bool bigMsg property bool timeUnderBubble: false property var type: Type + property var shouldBeVisible: msgRowlayout.msgHovered || root.activeFocus || reply.activeFocus || more.activeFocus || share.activeFocus // If the ListView attached properties are not available, // then the root delegate is likely a Loader. @@ -81,6 +83,16 @@ Control { rightPadding: hPadding leftPadding: hPadding + background: Rectangle { + id: focusIndicator + visible: rootDelegate.activeFocus + radius: 4 + border.color: JamiTheme.tintedBlue + border.width: 2 + color: "transparent" + z: 1 + } + contentItem: ColumnLayout { id: mainColumnLayout @@ -301,7 +313,7 @@ Control { anchors.verticalCenter: parent.verticalCenter anchors.right: isOutgoing ? optionButtonItem.right : undefined anchors.left: !isOutgoing ? optionButtonItem.left : undefined - visible: msgRowlayout.msgHovered + visible: shouldBeVisible source: JamiResources.more_vert_24dp_svg width: optionButtonItem.width / 4 height: optionButtonItem.height @@ -311,7 +323,7 @@ Control { function setBindings() { more.isOpen = false; - visible = Qt.binding(() => msgRowlayout.msgHovered); + visible = Qt.binding(() => shouldBeVisible); imageColor = Qt.binding(() => hovered ? JamiTheme.chatViewFooterImgHoverColor : JamiTheme.chatViewFooterImgColor); normalColor = Qt.binding(() => JamiTheme.primaryBackgroundColor); } @@ -356,7 +368,7 @@ Control { anchors.rightMargin: 5 anchors.right: isOutgoing ? more.left : undefined anchors.left: !isOutgoing ? more.right : undefined - visible: msgRowlayout.msgHovered + visible: shouldBeVisible onClicked: { MessagesAdapter.editId = ""; @@ -380,13 +392,14 @@ Control { anchors.rightMargin: 5 anchors.right: isOutgoing ? reply.left : undefined anchors.left: !isOutgoing ? reply.right : undefined - visible: msgRowlayout.msgHovered + + visible: shouldBeVisible property bool isOpen: false property var obj: undefined function setBindings() { // when the popup is closed, setBindings is called to reset the icon's visual settings share.isOpen = false; - visible = Qt.binding(() => msgRowlayout.msgHovered); + visible = Qt.binding(() => shouldBeVisible); imageColor = Qt.binding(() => hovered ? JamiTheme.chatViewFooterImgHoverColor : JamiTheme.chatViewFooterImgColor); normalColor = Qt.binding(() => JamiTheme.primaryBackgroundColor); } diff --git a/src/app/commoncomponents/TextMessageDelegate.qml b/src/app/commoncomponents/TextMessageDelegate.qml index 91ed8bce..6d90acda 100644 --- a/src/app/commoncomponents/TextMessageDelegate.qml +++ b/src/app/commoncomponents/TextMessageDelegate.qml @@ -24,7 +24,21 @@ import net.jami.Constants 1.1 import net.jami.Enums 1.1 SBSMessageBase { - id: root + id: rootDelegate + + Accessible.role: Accessible.StaticText + Accessible.name: { + let name = isOutgoing ? JamiStrings.inReplyToYou : UtilsAdapter.getBestNameForUri(CurrentAccount.id, Author); + return name + ": " + Body + " " + formattedTime; + } + Accessible.description: { + let status = ""; + if (bubble.isEdited) + status += JamiStrings.edited + " "; + return status + (readers.length > 0 ? JamiStrings.readBy + " " + readers.map(function (uri) { + return UtilsAdapter.getBestNameForUri(CurrentAccount.id, uri); + }).join(", ") : ""); + } property bool isRemoteImage property bool isEmojiOnly: IsEmojiOnly @@ -34,11 +48,11 @@ SBSMessageBase { Connections { target: bubble function onColorChanged(color) { - root.colorUrl = UtilsAdapter.luma(bubble.color) ? JamiTheme.chatviewLinkColorLight : JamiTheme.chatviewLinkColorDark; - root.colorText = UtilsAdapter.luma(bubble.color) ? JamiTheme.chatviewTextColorLight : JamiTheme.chatviewTextColorDark; + rootDelegate.colorUrl = UtilsAdapter.luma(bubble.color) ? JamiTheme.chatviewLinkColorLight : JamiTheme.chatviewLinkColorDark; + rootDelegate.colorText = UtilsAdapter.luma(bubble.color) ? JamiTheme.chatviewTextColorLight : JamiTheme.chatviewTextColorDark; // Update parsed body with correct colors if (Body !== "") - MessagesAdapter.parseMessage(Id, Body, UtilsAdapter.getAppValue(Settings.DisplayHyperlinkPreviews), root.colorUrl, bubble.color); + MessagesAdapter.parseMessage(Id, Body, UtilsAdapter.getAppValue(Settings.DisplayHyperlinkPreviews), rootDelegate.colorUrl, bubble.color); } } @@ -53,7 +67,7 @@ SBSMessageBase { textContentWidth: textEditId.width textContentHeight: textEditId.height - bigMsg: textContentWidth >= (2 / 3) * root.maxMsgWidth || extraContent.active + bigMsg: textContentWidth >= (2 / 3) * rootDelegate.maxMsgWidth || extraContent.active innerContent.children: [ TextEdit { @@ -63,10 +77,10 @@ SBSMessageBase { topPadding: bubble.isDeleted ? 6 : 10 bottomPadding: bubble.isDeleted ? 6 : 10 anchors.right: isOutgoing ? parent.right : undefined - anchors.rightMargin: isOutgoing && !isEmojiOnly && !bigMsg ? root.timeWidth + root.editedWidth : 0 + anchors.rightMargin: isOutgoing && !isEmojiOnly && !bigMsg ? rootDelegate.timeWidth + rootDelegate.editedWidth : 0 text: { if (Body !== "" && ParsedBody.length === 0) { - MessagesAdapter.parseMessage(Id, Body, UtilsAdapter.getAppValue(Settings.DisplayHyperlinkPreviews), root.colorUrl, bubble.color); + MessagesAdapter.parseMessage(Id, Body, UtilsAdapter.getAppValue(Settings.DisplayHyperlinkPreviews), rootDelegate.colorUrl, bubble.color); return ""; } if (ParsedBody !== "") @@ -82,11 +96,11 @@ SBSMessageBase { width: { if (extraContent.active) - Math.max(extraContent.width, Math.min((2 / 3) * root.maxMsgWidth, implicitWidth - avatarBlockWidth, extraContent.minSize) - senderMargin); + Math.max(extraContent.width, Math.min((2 / 3) * rootDelegate.maxMsgWidth, implicitWidth - avatarBlockWidth, extraContent.minSize) - senderMargin); else if (isEmojiOnly) - Math.min((2 / 3) * root.maxMsgWidth, implicitWidth, innerContent.width - senderMargin - (innerContent.width - senderMargin) % (JamiTheme.chatviewEmojiSize + 2)); + Math.min((2 / 3) * rootDelegate.maxMsgWidth, implicitWidth, innerContent.width - senderMargin - (innerContent.width - senderMargin) % (JamiTheme.chatviewEmojiSize + 2)); else - Math.min((2 / 3) * root.maxMsgWidth, implicitWidth + 5, innerContent.width - senderMargin + 5); + Math.min((2 / 3) * rootDelegate.maxMsgWidth, implicitWidth + 5, innerContent.width - senderMargin + 5); } wrapMode: Label.WrapAtWordBoundaryOrAnywhere @@ -96,7 +110,7 @@ SBSMessageBase { renderType: Text.NativeRendering textFormat: Text.RichText clip: true - onLinkHovered: root.hoveredLink = hoveredLink + onLinkHovered: rootDelegate.hoveredLink = hoveredLink onLinkActivated: Qt.openUrlExternally(new URL(hoveredLink)) readOnly: true color: (ParsedBody !== "") ? getBaseColor() : (UtilsAdapter.luma(bubble.color) ? "white" : "dark") @@ -150,7 +164,7 @@ SBSMessageBase { HoverHandler { target: previewContent onHoveredChanged: { - root.hoveredLink = hovered ? LinkPreviewInfo.url : ""; + rootDelegate.hoveredLink = hovered ? LinkPreviewInfo.url : ""; } cursorShape: Qt.PointingHandCursor } @@ -204,7 +218,7 @@ SBSMessageBase { wrapMode: Label.WrapAtWordBoundaryOrAnywhere renderType: Text.NativeRendering textFormat: TextEdit.RichText - color: root.colorText + color: rootDelegate.colorText visible: LinkPreviewInfo.title.length > 0 text: LinkPreviewInfo.title lineHeight: 1.3 @@ -217,9 +231,9 @@ SBSMessageBase { renderType: Text.NativeRendering textFormat: TextEdit.RichText visible: LinkPreviewInfo.description.length > 0 - font.underline: root.hoveredLink + font.underline: rootDelegate.hoveredLink text: LinkPreviewInfo.description - color: root.colorUrl + color: rootDelegate.colorUrl lineHeight: 1.3 } Label { @@ -229,7 +243,7 @@ SBSMessageBase { wrapMode: Label.WrapAtWordBoundaryOrAnywhere renderType: Text.NativeRendering textFormat: TextEdit.RichText - color: root.colorText + color: rootDelegate.colorText text: LinkPreviewInfo.domain lineHeight: 1.3 } diff --git a/src/app/mainview/components/MessageListView.qml b/src/app/mainview/components/MessageListView.qml index 21fe02d3..bad23e66 100644 --- a/src/app/mainview/components/MessageListView.qml +++ b/src/app/mainview/components/MessageListView.qml @@ -24,8 +24,26 @@ import net.jami.Adapters 1.1 import net.jami.Constants 1.1 import "../../commoncomponents" -JamiListView { +ListView { id: root + property alias verticalScrollBar: verticalScrollBar + layer.mipmap: false + clip: true + + ScrollBar.vertical: JamiScrollBar { + id: verticalScrollBar + + attachedFlickableMoving: root.moving + } + + keyNavigationEnabled: true + keyNavigationWraps: false + + focus: true + activeFocusOnTab: true + + Accessible.role: Accessible.List + Accessible.name: JamiStrings.conversationMessages function getDistanceToBottom() { const scrollDiff = ScrollBar.vertical.position - (1.0 - ScrollBar.vertical.size); @@ -139,7 +157,10 @@ JamiListView { } // fade-in mechanism - Component.onCompleted: fadeAnimation.start() + Component.onCompleted: { + positionViewAtBeginning(); + fadeAnimation.start(); + } Rectangle { id: overlay anchors.fill: parent @@ -194,13 +215,19 @@ JamiListView { boundsBehavior: Flickable.StopAtBounds currentIndex: -1 + Connections { + target: CurrentConversation + function onIdChanged() { + currentIndex = -1; + } + } + model: MessagesAdapter.messageListModel delegate: DelegateChooser { id: delegateChooser role: "Type" DelegateChoice { - id: delegateChoice roleValue: Interaction.Type.TEXT TextMessageDelegate { diff --git a/src/app/net/jami/Constants/JamiStrings.qml b/src/app/net/jami/Constants/JamiStrings.qml index 0c072d1e..a4c5d20e 100644 --- a/src/app/net/jami/Constants/JamiStrings.qml +++ b/src/app/net/jami/Constants/JamiStrings.qml @@ -947,4 +947,8 @@ Item { property string tipDescription: qsTr("Tips to help you use Jami more effectively") property string showMoreMessagingOptions: qsTr("Show more messaging options") property string showMoreMessagingOptionsDescription: qsTr("Open a menu that allows you to send voice and video messages as well as sharing your location") + property string conversationMessages: qsTr("Conversation messages list. Use arrow keys to navigate through messages.") + property string dataTransfer: qsTr("Data transfer") + property string status: qsTr("Status") + property string readBy: qsTr("Read by") }