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
|