summary refs log tree commit diff
path: root/resources/qml
diff options
context:
space:
mode:
Diffstat (limited to 'resources/qml')
-rw-r--r--resources/qml/ActiveCallBar.qml2
-rw-r--r--resources/qml/Avatar.qml2
-rw-r--r--resources/qml/MatrixText.qml4
-rw-r--r--resources/qml/MessageInput.qml90
-rw-r--r--resources/qml/MessageView.qml202
-rw-r--r--resources/qml/Reactions.qml2
-rw-r--r--resources/qml/ReplyPopup.qml47
-rw-r--r--resources/qml/TimelineRow.qml2
-rw-r--r--resources/qml/TimelineView.qml454
-rw-r--r--resources/qml/TopBar.qml127
-rw-r--r--resources/qml/TypingIndicator.qml35
-rw-r--r--resources/qml/delegates/FileMessage.qml2
-rw-r--r--resources/qml/delegates/ImageMessage.qml33
-rw-r--r--resources/qml/delegates/NoticeMessage.qml2
-rw-r--r--resources/qml/delegates/Pill.qml2
-rw-r--r--resources/qml/delegates/PlayableMediaMessage.qml2
-rw-r--r--resources/qml/delegates/TextMessage.qml2
-rw-r--r--resources/qml/emoji/EmojiPicker.qml5
18 files changed, 598 insertions, 417 deletions
diff --git a/resources/qml/ActiveCallBar.qml b/resources/qml/ActiveCallBar.qml

index 49b5d059..7137197b 100644 --- a/resources/qml/ActiveCallBar.qml +++ b/resources/qml/ActiveCallBar.qml
@@ -8,7 +8,7 @@ Rectangle { visible: TimelineManager.callState != WebRTCState.DISCONNECTED color: "#2ECC71" - implicitHeight: rowLayout.height + 8 + implicitHeight: visible ? rowLayout.height + 8 : 0 MouseArea { anchors.fill: parent diff --git a/resources/qml/Avatar.qml b/resources/qml/Avatar.qml
index a247bffe..34b029a6 100644 --- a/resources/qml/Avatar.qml +++ b/resources/qml/Avatar.qml
@@ -13,7 +13,7 @@ Rectangle { width: 48 height: 48 radius: Settings.avatarCircles ? height / 2 : 3 - color: colors.base + color: colors.alternateBase Label { anchors.fill: parent diff --git a/resources/qml/MatrixText.qml b/resources/qml/MatrixText.qml
index 6c96a539..a5781c73 100644 --- a/resources/qml/MatrixText.qml +++ b/resources/qml/MatrixText.qml
@@ -6,8 +6,7 @@ TextEdit { textFormat: TextEdit.RichText readOnly: true wrapMode: Text.Wrap - selectByMouse: true - activeFocusOnPress: false + selectByMouse: !Settings.mobileMode color: colors.text onLinkActivated: { if (/^https:\/\/matrix.to\/#\/(@.*)$/.test(link)) { @@ -29,7 +28,6 @@ TextEdit { id: ma anchors.fill: parent - propagateComposedEvents: true acceptedButtons: Qt.NoButton cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor } diff --git a/resources/qml/MessageInput.qml b/resources/qml/MessageInput.qml new file mode 100644
index 00000000..71da9cae --- /dev/null +++ b/resources/qml/MessageInput.qml
@@ -0,0 +1,90 @@ +import QtQuick 2.9 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.2 +import QtQuick.Window 2.2 + +Rectangle { + color: colors.window + Layout.fillWidth: true + Layout.preferredHeight: textInput.height + Layout.minimumHeight: 40 + + RowLayout { + id: inputBar + + anchors.fill: parent + spacing: 16 + + ImageButton { + Layout.alignment: Qt.AlignBottom + hoverEnabled: true + width: 22 + height: 22 + image: ":/icons/icons/ui/place-call.png" + Layout.topMargin: 8 + Layout.bottomMargin: 8 + Layout.leftMargin: 16 + } + + ImageButton { + Layout.alignment: Qt.AlignBottom + hoverEnabled: true + width: 22 + height: 22 + image: ":/icons/icons/ui/paper-clip-outline.png" + Layout.topMargin: 8 + Layout.bottomMargin: 8 + } + + ScrollView { + id: textInput + + Layout.alignment: Qt.AlignBottom + Layout.maximumHeight: Window.height / 4 + Layout.fillWidth: true + + TextArea { + placeholderText: qsTr("Write a message...") + placeholderTextColor: colors.buttonText + color: colors.text + wrapMode: TextEdit.Wrap + + MouseArea { + // workaround for wrong cursor shape on some platforms + anchors.fill: parent + acceptedButtons: Qt.NoButton + cursorShape: Qt.IBeamCursor + } + + background: Rectangle { + color: colors.window + } + + } + + } + + ImageButton { + Layout.alignment: Qt.AlignRight | Qt.AlignBottom + hoverEnabled: true + width: 22 + height: 22 + image: ":/icons/icons/ui/smile.png" + Layout.topMargin: 8 + Layout.bottomMargin: 8 + } + + ImageButton { + Layout.alignment: Qt.AlignRight | Qt.AlignBottom + hoverEnabled: true + width: 22 + height: 22 + image: ":/icons/icons/ui/cursor.png" + Layout.topMargin: 8 + Layout.bottomMargin: 8 + Layout.rightMargin: 16 + } + + } + +} diff --git a/resources/qml/MessageView.qml b/resources/qml/MessageView.qml new file mode 100644
index 00000000..09220a71 --- /dev/null +++ b/resources/qml/MessageView.qml
@@ -0,0 +1,202 @@ +import "./delegates" +import QtGraphicalEffects 1.0 +import QtQuick 2.9 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.2 +import QtQuick.Window 2.2 +import im.nheko 1.0 + +ListView { + id: chat + + property int delegateMaxWidth: (Settings.timelineMaxWidth > 100 && (parent.width - Settings.timelineMaxWidth) > scrollbar.width * 2) ? Settings.timelineMaxWidth : (parent.width - scrollbar.width * 2) + + Layout.fillWidth: true + Layout.fillHeight: true + cacheBuffer: 400 + model: TimelineManager.timeline + boundsBehavior: Flickable.StopAtBounds + pixelAligned: true + spacing: 4 + verticalLayoutDirection: ListView.BottomToTop + onCountChanged: { + if (atYEnd) + model.currentIndex = 0; + + } // Mark last event as read, since we are at the bottom + + ScrollHelper { + flickable: parent + anchors.fill: parent + } + + Shortcut { + sequence: StandardKey.MoveToPreviousPage + onActivated: { + chat.contentY = chat.contentY - chat.height / 2; + chat.returnToBounds(); + } + } + + Shortcut { + sequence: StandardKey.MoveToNextPage + onActivated: { + chat.contentY = chat.contentY + chat.height / 2; + chat.returnToBounds(); + } + } + + Shortcut { + sequence: StandardKey.Cancel + onActivated: chat.model.reply = undefined + } + + Shortcut { + sequence: "Alt+Up" + onActivated: chat.model.reply = chat.model.indexToId(chat.model.reply ? chat.model.idToIndex(chat.model.reply) + 1 : 0) + } + + Shortcut { + sequence: "Alt+Down" + onActivated: { + var idx = chat.model.reply ? chat.model.idToIndex(chat.model.reply) - 1 : -1; + chat.model.reply = idx >= 0 ? chat.model.indexToId(idx) : undefined; + } + } + + section { + property: "section" + } + + Component { + id: sectionHeader + + Column { + property var modelData + property string section + property string nextSection + + topPadding: 4 + bottomPadding: 4 + spacing: 8 + visible: !!modelData + width: parent.width + height: (section.includes(" ") ? dateBubble.height + 8 + userName.height : userName.height) + 8 + + Label { + id: dateBubble + + anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined + visible: section.includes(" ") + text: chat.model.formatDateSeparator(modelData.timestamp) + color: colors.text + height: fontMetrics.height * 1.4 + width: contentWidth * 1.2 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + background: Rectangle { + radius: parent.height / 2 + color: colors.window + } + + } + + Row { + height: userName.height + spacing: 8 + + Avatar { + width: avatarSize + height: avatarSize + url: chat.model.avatarUrl(modelData.userId).replace("mxc://", "image://MxcImage/") + displayName: modelData.userName + userid: modelData.userId + + MouseArea { + anchors.fill: parent + onClicked: chat.model.openUserProfile(modelData.userId) + cursorShape: Qt.PointingHandCursor + propagateComposedEvents: true + } + + } + + Label { + id: userName + + text: TimelineManager.escapeEmoji(modelData.userName) + color: TimelineManager.userColor(modelData.userId, colors.window) + textFormat: Text.RichText + + MouseArea { + anchors.fill: parent + Layout.alignment: Qt.AlignHCenter + onClicked: chat.model.openUserProfile(modelData.userId) + cursorShape: Qt.PointingHandCursor + propagateComposedEvents: true + } + + } + + } + + } + + } + + ScrollBar.vertical: ScrollBar { + id: scrollbar + } + + delegate: Item { + id: wrapper + + // This would normally be previousSection, but our model's order is inverted. + property bool sectionBoundary: (ListView.nextSection != "" && ListView.nextSection !== ListView.section) || model.index === chat.count - 1 + property Item section + + anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined + width: chat.delegateMaxWidth + height: section ? section.height + timelinerow.height : timelinerow.height + onSectionBoundaryChanged: { + if (sectionBoundary) { + var properties = { + "modelData": model.dump, + "section": ListView.section, + "nextSection": ListView.nextSection + }; + section = sectionHeader.createObject(wrapper, properties); + } else { + section.destroy(); + section = null; + } + } + + TimelineRow { + id: timelinerow + + y: section ? section.y + section.height : 0 + } + + Connections { + function onMovementEnded() { + if (y + height + 2 * chat.spacing > chat.contentY + chat.height && y < chat.contentY + chat.height) + chat.model.currentIndex = index; + + } + + target: chat + } + + } + + footer: BusyIndicator { + anchors.horizontalCenter: parent.horizontalCenter + running: chat.model && chat.model.paginationInProgress + height: 50 + width: 50 + z: 3 + } + +} diff --git a/resources/qml/Reactions.qml b/resources/qml/Reactions.qml
index 6487f512..836087ef 100644 --- a/resources/qml/Reactions.qml +++ b/resources/qml/Reactions.qml
@@ -83,7 +83,7 @@ Flow { 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 + color: modelData.selfReactedEvent !== '' ? Qt.hsla(highlightHue, highlightSat, highlightLight, 0.2) : colors.window border.width: 1 radius: reaction.height / 2 } diff --git a/resources/qml/ReplyPopup.qml b/resources/qml/ReplyPopup.qml new file mode 100644
index 00000000..4659e075 --- /dev/null +++ b/resources/qml/ReplyPopup.qml
@@ -0,0 +1,47 @@ +import "./delegates/" +import QtQuick 2.9 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.2 +import im.nheko 1.0 + +Rectangle { + id: replyPopup + + property var room: TimelineManager.timeline + + Layout.fillWidth: true + visible: room && room.reply + // Height of child, plus margins, plus border + implicitHeight: replyPreview.height + 10 + color: colors.window + z: 3 + + Reply { + id: replyPreview + + anchors.left: parent.left + anchors.leftMargin: 2 * 22 + 3 * 16 + anchors.right: closeReplyButton.left + anchors.rightMargin: 2 * 22 + 3 * 16 + anchors.bottom: parent.bottom + modelData: room ? room.getDump(room.reply, room.id) : { + } + userColor: TimelineManager.userColor(modelData.userId, colors.window) + } + + ImageButton { + id: closeReplyButton + + anchors.right: parent.right + anchors.rightMargin: 15 + anchors.top: replyPreview.top + hoverEnabled: true + width: 16 + height: 16 + image: ":/icons/icons/ui/remove-symbol.png" + ToolTip.visible: closeReplyButton.hovered + ToolTip.text: qsTr("Close") + onClicked: room.reply = undefined + } + +} diff --git a/resources/qml/TimelineRow.qml b/resources/qml/TimelineRow.qml
index 38597673..57fded90 100644 --- a/resources/qml/TimelineRow.qml +++ b/resources/qml/TimelineRow.qml
@@ -48,7 +48,7 @@ Item { Reply { visible: model.replyTo modelData: chat.model.getDump(model.replyTo, model.id) - userColor: TimelineManager.userColor(modelData.userId, colors.window) + userColor: TimelineManager.userColor(modelData.userId, colors.base) } // actual message content diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml
index d69d5568..9d00442c 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml
@@ -43,6 +43,14 @@ Page { } + Component { + id: userProfileComponent + + UserProfile { + } + + } + Menu { id: messageContextMenu @@ -69,12 +77,12 @@ Page { MenuItem { text: qsTr("Reply") - onClicked: chat.model.replyAction(messageContextMenu.eventId) + onClicked: TimelineManager.timeline.replyAction(messageContextMenu.eventId) } MenuItem { text: qsTr("Read receipts") - onTriggered: chat.model.readReceiptsAction(messageContextMenu.eventId) + onTriggered: TimelineManager.timeline.readReceiptsAction(messageContextMenu.eventId) } MenuItem { @@ -83,19 +91,19 @@ Page { MenuItem { text: qsTr("View raw message") - onTriggered: chat.model.viewRawMessage(messageContextMenu.eventId) + onTriggered: TimelineManager.timeline.viewRawMessage(messageContextMenu.eventId) } MenuItem { visible: messageContextMenu.isEncrypted height: visible ? implicitHeight : 0 text: qsTr("View decrypted raw message") - onTriggered: chat.model.viewDecryptedRawMessage(messageContextMenu.eventId) + onTriggered: TimelineManager.timeline.viewDecryptedRawMessage(messageContextMenu.eventId) } MenuItem { text: qsTr("Redact message") - onTriggered: chat.model.redactEvent(messageContextMenu.eventId) + onTriggered: TimelineManager.timeline.redactEvent(messageContextMenu.eventId) } MenuItem { @@ -159,436 +167,76 @@ Page { } ColumnLayout { + visible: TimelineManager.timeline != null anchors.fill: parent + spacing: 0 - Rectangle { - id: topBar + TopBar { + } + Rectangle { Layout.fillWidth: true - implicitHeight: topLayout.height + 16 + height: 1 z: 3 - color: colors.base - - MouseArea { - anchors.fill: parent - onClicked: TimelineManager.openRoomSettings() - } - - GridLayout { - //Layout.margins: 8 - - id: topLayout - - anchors.left: parent.left - anchors.right: parent.right - anchors.margins: 8 - anchors.verticalCenter: parent.verticalCenter - - ImageButton { - id: backToRoomsButton - - Layout.column: 0 - Layout.row: 0 - Layout.rowSpan: 2 - Layout.alignment: Qt.AlignVCenter - visible: TimelineManager.isNarrowView - image: ":/icons/icons/ui/angle-pointing-to-left.png" - ToolTip.visible: hovered - ToolTip.text: qsTr("Back to room list") - onClicked: TimelineManager.backToRooms() - } - - Avatar { - Layout.column: 1 - Layout.row: 0 - Layout.rowSpan: 2 - Layout.alignment: Qt.AlignVCenter - width: avatarSize - height: avatarSize - url: chat.model ? chat.model.roomAvatarUrl.replace("mxc://", "image://MxcImage/") : "" - displayName: chat.model ? chat.model.roomName : qsTr("No room selected") - - MouseArea { - anchors.fill: parent - onClicked: TimelineManager.openRoomSettings() - } - - } - - Label { - Layout.fillWidth: true - Layout.column: 2 - Layout.row: 0 - color: colors.text - font.pointSize: fontMetrics.font.pointSize * 1.1 - text: chat.model ? chat.model.roomName : qsTr("No room selected") - - MouseArea { - anchors.fill: parent - onClicked: TimelineManager.openRoomSettings() - } - - } - - MatrixText { - Layout.fillWidth: true - Layout.column: 2 - Layout.row: 1 - Layout.maximumHeight: fontMetrics.lineSpacing * 2 // show 2 lines - clip: true - text: chat.model ? chat.model.roomTopic : "" - } - - ImageButton { - id: roomOptionsButton - - Layout.column: 3 - Layout.row: 0 - Layout.rowSpan: 2 - Layout.alignment: Qt.AlignVCenter - image: ":/icons/icons/ui/vertical-ellipsis.png" - ToolTip.visible: hovered - ToolTip.text: qsTr("Room options") - onClicked: roomOptionsMenu.popup(roomOptionsButton) - - Menu { - id: roomOptionsMenu - - MenuItem { - text: qsTr("Invite users") - onTriggered: TimelineManager.openInviteUsersDialog() - } - - MenuItem { - text: qsTr("Members") - onTriggered: TimelineManager.openMemberListDialog() - } - - MenuItem { - text: qsTr("Leave room") - onTriggered: TimelineManager.openLeaveRoomDialog() - } - - MenuItem { - text: qsTr("Settings") - onTriggered: TimelineManager.openRoomSettings() - } - - } - - } - - } - + color: colors.mid } - StackLayout { - id: stackLayout - currentIndex: 0 - - Connections { - target: TimelineManager - function onActiveTimelineChanged() { - stackLayout.currentIndex = 0; - } - } - - ListView { - id: chat - - property int delegateMaxWidth: (Settings.timelineMaxWidth > 100 && (parent.width - Settings.timelineMaxWidth) > scrollbar.width * 2) ? Settings.timelineMaxWidth : (parent.width - scrollbar.width * 2) - - visible: TimelineManager.timeline != null - cacheBuffer: 400 - Layout.fillWidth: true - Layout.fillHeight: true - model: TimelineManager.timeline - boundsBehavior: Flickable.StopAtBounds - pixelAligned: true - spacing: 4 - verticalLayoutDirection: ListView.BottomToTop - onCountChanged: { - if (atYEnd) - model.currentIndex = 0; - - } // Mark last event as read, since we are at the bottom - - ScrollHelper { - flickable: parent - anchors.fill: parent - } - - Shortcut { - sequence: StandardKey.MoveToPreviousPage - onActivated: { - chat.contentY = chat.contentY - chat.height / 2; - chat.returnToBounds(); - } - } - - Shortcut { - sequence: StandardKey.MoveToNextPage - onActivated: { - chat.contentY = chat.contentY + chat.height / 2; - chat.returnToBounds(); - } - } - - Shortcut { - sequence: StandardKey.Cancel - onActivated: chat.model.reply = undefined - } - - Shortcut { - sequence: "Alt+Up" - onActivated: chat.model.reply = chat.model.indexToId(chat.model.reply ? chat.model.idToIndex(chat.model.reply) + 1 : 0) - } - - Shortcut { - sequence: "Alt+Down" - onActivated: { - var idx = chat.model.reply ? chat.model.idToIndex(chat.model.reply) - 1 : -1; - chat.model.reply = idx >= 0 ? chat.model.indexToId(idx) : undefined; - } - } - - Component { - id: userProfileComponent - - UserProfile { - } - - } - - section { - property: "section" - } - - Component { - id: sectionHeader - - Column { - property var modelData - property string section - property string nextSection - - topPadding: 4 - bottomPadding: 4 - spacing: 8 - visible: !!modelData - width: parent.width - height: (section.includes(" ") ? dateBubble.height + 8 + userName.height : userName.height) + 8 - - Label { - id: dateBubble - - anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined - visible: section.includes(" ") - text: chat.model.formatDateSeparator(modelData.timestamp) - color: colors.text - height: fontMetrics.height * 1.4 - width: contentWidth * 1.2 - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - - background: Rectangle { - radius: parent.height / 2 - color: colors.base - } - - } - - Row { - height: userName.height - spacing: 8 - - Avatar { - width: avatarSize - height: avatarSize - url: chat.model.avatarUrl(modelData.userId).replace("mxc://", "image://MxcImage/") - displayName: modelData.userName - userid: modelData.userId - - MouseArea { - anchors.fill: parent - onClicked: chat.model.openUserProfile(modelData.userId) - cursorShape: Qt.PointingHandCursor - propagateComposedEvents: true - } - - } - - Label { - id: userName - - text: TimelineManager.escapeEmoji(modelData.userName) - color: TimelineManager.userColor(modelData.userId, colors.window) - textFormat: Text.RichText + Rectangle { + Layout.fillWidth: true + Layout.fillHeight: true + color: colors.base - MouseArea { - anchors.fill: parent - Layout.alignment: Qt.AlignHCenter - onClicked: chat.model.openUserProfile(modelData.userId) - cursorShape: Qt.PointingHandCursor - propagateComposedEvents: true - } + ColumnLayout { + anchors.fill: parent + spacing: 0 - } + StackLayout { + id: stackLayout + currentIndex: 0 + Connections { + target: TimelineManager + function onActiveTimelineChanged() { + stackLayout.currentIndex = 0; } - } - } - - ScrollBar.vertical: ScrollBar { - id: scrollbar - } - - delegate: Item { - id: wrapper - - // This would normally be previousSection, but our model's order is inverted. - property bool sectionBoundary: (ListView.nextSection != "" && ListView.nextSection !== ListView.section) || model.index === chat.count - 1 - property Item section - - anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined - width: chat.delegateMaxWidth - height: section ? section.height + timelinerow.height : timelinerow.height - onSectionBoundaryChanged: { - if (sectionBoundary) { - var properties = { - "modelData": model.dump, - "section": ListView.section, - "nextSection": ListView.nextSection - }; - section = sectionHeader.createObject(wrapper, properties); - } else { - section.destroy(); - section = null; - } + MessageView { + Layout.fillWidth: true + Layout.fillHeight: true } - TimelineRow { - id: timelinerow - - y: section ? section.y + section.height : 0 + Loader { + source: TimelineManager.onVideoCall ? "VideoCall.qml" : "" + onLoaded: TimelineManager.setVideoCallItem() } - - Connections { - function onMovementEnded() { - if (y + height + 2 * chat.spacing > chat.contentY + chat.height && y < chat.contentY + chat.height) - chat.model.currentIndex = index; - - } - - target: chat - } - } - footer: BusyIndicator { - anchors.horizontalCenter: parent.horizontalCenter - running: chat.model && chat.model.paginationInProgress - height: 50 - width: 50 - z: 3 + TypingIndicator { } } - Loader { - id: videoCallLoader - source: TimelineManager.onVideoCall ? "VideoCall.qml" : "" - onLoaded: TimelineManager.setVideoCallItem() - } } - Item { - id: chatFooter - - implicitHeight: Math.max(fontMetrics.height * 1.2, footerContent.height) + ActiveCallBar { Layout.fillWidth: true z: 3 - - Column { - id: footerContent - - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom - - Rectangle { - id: typingRect - - anchors.left: parent.left - anchors.right: parent.right - color: (chat.model && chat.model.typingUsers.length > 0) ? colors.window : "transparent" - height: typingDisplay.height - - Label { - id: typingDisplay - - anchors.left: parent.left - anchors.leftMargin: 10 - anchors.right: parent.right - anchors.rightMargin: 10 - color: colors.text - text: chat.model ? chat.model.formatTypingUsers(chat.model.typingUsers, colors.window) : "" - textFormat: Text.RichText - } - - } - - Rectangle { - id: replyPopup - - anchors.left: parent.left - anchors.right: parent.right - visible: chat.model && chat.model.reply - // Height of child, plus margins, plus border - height: replyPreview.height + 10 - color: colors.base - - Reply { - id: replyPreview - - anchors.left: parent.left - anchors.leftMargin: 10 - anchors.right: closeReplyButton.left - anchors.rightMargin: 20 - anchors.bottom: parent.bottom - modelData: chat.model ? chat.model.getDump(chat.model.reply, chat.model.id) : { - } - userColor: TimelineManager.userColor(modelData.userId, colors.window) - } - - ImageButton { - id: closeReplyButton - - anchors.right: parent.right - anchors.rightMargin: 15 - anchors.top: replyPreview.top - hoverEnabled: true - width: 16 - height: 16 - image: ":/icons/icons/ui/remove-symbol.png" - ToolTip.visible: closeReplyButton.hovered - ToolTip.text: qsTr("Close") - onClicked: chat.model.reply = undefined - } - - } - - } - } - ActiveCallBar { + Rectangle { Layout.fillWidth: true z: 3 + height: 1 + color: colors.mid } + ReplyPopup { + } + + //MessageInput { + //} + } } diff --git a/resources/qml/TopBar.qml b/resources/qml/TopBar.qml new file mode 100644
index 00000000..181b9ba4 --- /dev/null +++ b/resources/qml/TopBar.qml
@@ -0,0 +1,127 @@ +import QtQuick 2.9 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.2 +import im.nheko 1.0 + +Rectangle { + id: topBar + + property var room: TimelineManager.timeline + + Layout.fillWidth: true + implicitHeight: topLayout.height + 16 + z: 3 + color: colors.window + + MouseArea { + anchors.fill: parent + onClicked: TimelineManager.openRoomSettings() + } + + GridLayout { + //Layout.margins: 8 + + id: topLayout + + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: 8 + anchors.verticalCenter: parent.verticalCenter + + ImageButton { + id: backToRoomsButton + + Layout.column: 0 + Layout.row: 0 + Layout.rowSpan: 2 + Layout.alignment: Qt.AlignVCenter + visible: TimelineManager.isNarrowView + image: ":/icons/icons/ui/angle-pointing-to-left.png" + ToolTip.visible: hovered + ToolTip.text: qsTr("Back to room list") + onClicked: TimelineManager.backToRooms() + } + + Avatar { + Layout.column: 1 + Layout.row: 0 + Layout.rowSpan: 2 + Layout.alignment: Qt.AlignVCenter + width: avatarSize + height: avatarSize + url: room ? room.roomAvatarUrl.replace("mxc://", "image://MxcImage/") : "" + displayName: room ? room.roomName : qsTr("No room selected") + + MouseArea { + anchors.fill: parent + onClicked: TimelineManager.openRoomSettings() + } + + } + + Label { + Layout.fillWidth: true + Layout.column: 2 + Layout.row: 0 + color: colors.text + font.pointSize: fontMetrics.font.pointSize * 1.1 + text: room ? room.roomName : qsTr("No room selected") + + MouseArea { + anchors.fill: parent + onClicked: TimelineManager.openRoomSettings() + } + + } + + MatrixText { + Layout.fillWidth: true + Layout.column: 2 + Layout.row: 1 + Layout.maximumHeight: fontMetrics.lineSpacing * 2 // show 2 lines + clip: true + text: room ? room.roomTopic : "" + } + + ImageButton { + id: roomOptionsButton + + Layout.column: 3 + Layout.row: 0 + Layout.rowSpan: 2 + Layout.alignment: Qt.AlignVCenter + image: ":/icons/icons/ui/vertical-ellipsis.png" + ToolTip.visible: hovered + ToolTip.text: qsTr("Room options") + onClicked: roomOptionsMenu.popup(roomOptionsButton) + + Menu { + id: roomOptionsMenu + + MenuItem { + text: qsTr("Invite users") + onTriggered: TimelineManager.openInviteUsersDialog() + } + + MenuItem { + text: qsTr("Members") + onTriggered: TimelineManager.openMemberListDialog() + } + + MenuItem { + text: qsTr("Leave room") + onTriggered: TimelineManager.openLeaveRoomDialog() + } + + MenuItem { + text: qsTr("Settings") + onTriggered: TimelineManager.openRoomSettings() + } + + } + + } + + } + +} diff --git a/resources/qml/TypingIndicator.qml b/resources/qml/TypingIndicator.qml new file mode 100644
index 00000000..239fd662 --- /dev/null +++ b/resources/qml/TypingIndicator.qml
@@ -0,0 +1,35 @@ +import QtQuick 2.9 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.2 +import im.nheko 1.0 + +Item { + property var room: TimelineManager.timeline + + implicitHeight: Math.max(fontMetrics.height * 1.2, typingDisplay.height) + Layout.fillWidth: true + + Rectangle { + id: typingRect + + visible: (room && room.typingUsers.length > 0) + color: colors.base + anchors.fill: parent + z: 3 + + Label { + id: typingDisplay + + anchors.left: parent.left + anchors.leftMargin: 10 + anchors.right: parent.right + anchors.rightMargin: 10 + anchors.bottom: parent.bottom + color: colors.text + text: room ? room.formatTypingUsers(room.typingUsers, colors.base) : "" + textFormat: Text.RichText + } + + } + +} diff --git a/resources/qml/delegates/FileMessage.qml b/resources/qml/delegates/FileMessage.qml
index c6f213ee..ffd1e82b 100644 --- a/resources/qml/delegates/FileMessage.qml +++ b/resources/qml/delegates/FileMessage.qml
@@ -65,7 +65,7 @@ Item { } Rectangle { - color: colors.dark + color: colors.alternateBase z: -1 radius: 10 height: row.height + 24 diff --git a/resources/qml/delegates/ImageMessage.qml b/resources/qml/delegates/ImageMessage.qml
index e2c78fbe..5c3dac95 100644 --- a/resources/qml/delegates/ImageMessage.qml +++ b/resources/qml/delegates/ImageMessage.qml
@@ -31,11 +31,44 @@ Item { fillMode: Image.PreserveAspectFit MouseArea { + id: mouseArea + enabled: model.data.type == MtxEvent.ImageMessage && img.status == Image.Ready + hoverEnabled: true anchors.fill: parent onClicked: TimelineManager.openImageOverlay(model.data.url, model.data.id) } + Item { + id: overlay + + anchors.fill: parent + visible: mouseArea.containsMouse + + Rectangle { + id: container + + width: parent.width + implicitHeight: imgcaption.implicitHeight + anchors.bottom: overlay.bottom + color: colors.window + opacity: 0.75 + } + + Text { + id: imgcaption + + anchors.fill: container + elide: Text.ElideMiddle + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + // See this MSC: https://github.com/matrix-org/matrix-doc/pull/2530 + text: model.data.filename ? model.data.filename : model.data.body + color: colors.text + } + + } + } } diff --git a/resources/qml/delegates/NoticeMessage.qml b/resources/qml/delegates/NoticeMessage.qml
index d9a7a3e7..67a69055 100644 --- a/resources/qml/delegates/NoticeMessage.qml +++ b/resources/qml/delegates/NoticeMessage.qml
@@ -2,5 +2,5 @@ TextMessage { font.italic: true color: colors.buttonText height: isReply ? Math.min(chat.height / 8, implicitHeight) : undefined - clip: true + clip: isReply } diff --git a/resources/qml/delegates/Pill.qml b/resources/qml/delegates/Pill.qml
index 4acf2bef..88f6c7fd 100644 --- a/resources/qml/delegates/Pill.qml +++ b/resources/qml/delegates/Pill.qml
@@ -9,7 +9,7 @@ Label { background: Rectangle { radius: parent.height / 2 - color: colors.dark + color: colors.alternateBase } } diff --git a/resources/qml/delegates/PlayableMediaMessage.qml b/resources/qml/delegates/PlayableMediaMessage.qml
index 9ad115c7..be22687f 100644 --- a/resources/qml/delegates/PlayableMediaMessage.qml +++ b/resources/qml/delegates/PlayableMediaMessage.qml
@@ -8,7 +8,7 @@ Rectangle { id: bg radius: 10 - color: colors.dark + color: colors.alternateBase height: Math.round(content.height + 24) width: parent ? parent.width : undefined diff --git a/resources/qml/delegates/TextMessage.qml b/resources/qml/delegates/TextMessage.qml
index 69f2f0e3..8a1c116e 100644 --- a/resources/qml/delegates/TextMessage.qml +++ b/resources/qml/delegates/TextMessage.qml
@@ -7,6 +7,6 @@ MatrixText { text: "<style type=\"text/css\">a { color:" + colors.link + ";}</style>" + formatted.replace("<pre>", "<pre style='white-space: pre-wrap'>") width: parent ? parent.width : undefined height: isReply ? Math.round(Math.min(timelineRoot.height / 8, implicitHeight)) : undefined - clip: true + clip: isReply font.pointSize: (Settings.enlargeEmojiOnlyMessages && model.data.isOnlyEmoji > 0 && model.data.isOnlyEmoji < 4) ? Settings.fontSize * 3 : Settings.fontSize } diff --git a/resources/qml/emoji/EmojiPicker.qml b/resources/qml/emoji/EmojiPicker.qml
index 3a5ee57a..afe16350 100644 --- a/resources/qml/emoji/EmojiPicker.qml +++ b/resources/qml/emoji/EmojiPicker.qml
@@ -59,6 +59,7 @@ Popup { cellHeight: 52 boundsBehavior: Flickable.StopAtBounds clip: true + currentIndex: -1 // prevent sorting from stealing focus // Individual emoji delegate: AbstractButton { @@ -160,7 +161,7 @@ Popup { Rectangle { Layout.fillWidth: true Layout.preferredHeight: 1 - color: emojiPopup.colors.dark + color: emojiPopup.colors.alternateBase } // Category picker row @@ -280,7 +281,7 @@ Popup { Layout.preferredWidth: 1 implicitWidth: 1 height: parent.height - color: emojiPopup.colors.dark + color: emojiPopup.colors.alternateBase } // Search Button is special