A lot of improvements.

Fix laggish RoomListView when dragging.
Remove per-room timer and add timer in RoomForm.
Remove singleton module and use file as singleton.
Minor UI tweak in RoomListView.
Pass room to RoomListView via "currentRoom" delegate property and remove
RoomListForm-wide currentRoom.
Put menu files in a separate folder.
Show initial image in ImageStatus when avatar is not loaded.
Add about page.
Merge all setting pages into Setting.qml.
Add option to rearrange rooms by activity.
Add option to use RichText parser.
Add document url.
This commit is contained in:
Black Hat 2018-08-24 13:25:41 +08:00
parent 391473e559
commit cfa8043596
28 changed files with 247 additions and 287 deletions

View File

@ -32,7 +32,7 @@ RESOURCES += \
res.qrc res.qrc
# Additional import path used to resolve QML modules in Qt Creator's code model # Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH += qml/MatriqueSettings QML_IMPORT_PATH =
# Additional import path used to resolve QML modules just for Qt Quick Designer # Additional import path used to resolve QML modules just for Qt Quick Designer
QML_DESIGNER_IMPORT_PATH = QML_DESIGNER_IMPORT_PATH =

View File

@ -40,6 +40,7 @@
</categories> </categories>
<url type="homepage">https://gitlab.com/b0/matrique</url> <url type="homepage">https://gitlab.com/b0/matrique</url>
<url type="bugtracker">https://gitlab.com/b0/matrique/issues</url> <url type="bugtracker">https://gitlab.com/b0/matrique/issues</url>
<url type="help">https://doc.matrique.encom.eu.org</url>
<content_rating type="oars-1.0"> <content_rating type="oars-1.0">
<content_attribute id="social-chat">intense</content_attribute> <content_attribute id="social-chat">intense</content_attribute>
<content_attribute id="social-audio">intense</content_attribute> <content_attribute id="social-audio">intense</content_attribute>

View File

@ -4,6 +4,8 @@ import QtGraphicalEffects 1.0
import QtQuick.Controls 2.2 import QtQuick.Controls 2.2
import QtQuick.Controls.Material 2.2 import QtQuick.Controls.Material 2.2
import Qt.labs.settings 1.0 import Qt.labs.settings 1.0
import Matrique.Settings 0.1
import "qrc:/qml/component" import "qrc:/qml/component"
Page { Page {
@ -76,7 +78,7 @@ Page {
background: Rectangle { background: Rectangle {
implicitHeight: 48 implicitHeight: 48
color: Material.theme == Material.Light ? "#eaeaea" : "#242424" color: MSettings.darkTheme ? "#242424" : "#eaeaea"
border.color: parent.activeFocus ? Material.accent : "transparent" border.color: parent.activeFocus ? Material.accent : "transparent"
border.width: 2 border.width: 2
} }
@ -96,7 +98,7 @@ Page {
background: Rectangle { background: Rectangle {
implicitHeight: 48 implicitHeight: 48
color: Material.theme == Material.Light ? "#eaeaea" : "#242424" color: MSettings.darkTheme ? "#242424" : "#eaeaea"
border.color: parent.activeFocus ? Material.accent : "transparent" border.color: parent.activeFocus ? Material.accent : "transparent"
border.width: 2 border.width: 2
} }
@ -117,7 +119,7 @@ Page {
background: Rectangle { background: Rectangle {
implicitHeight: 48 implicitHeight: 48
color: Material.theme == Material.Light ? "#eaeaea" : "#242424" color: MSettings.darkTheme ? "#242424" : "#eaeaea"
border.color: parent.activeFocus ? Material.accent : "transparent" border.color: parent.activeFocus ? Material.accent : "transparent"
border.width: 2 border.width: 2
} }

View File

@ -7,6 +7,7 @@ Settings {
property bool asyncMessageDelegate property bool asyncMessageDelegate
property bool richText property bool richText
property bool pressAndHold property bool pressAndHold
property bool rearrangeByActivity
property bool darkTheme property bool darkTheme
property bool miniMode property bool miniMode

View File

@ -1,26 +0,0 @@
import QtQuick.tooling 1.2
// This file describes the plugin-supplied types contained in the library.
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
// 'qmlplugindump -nonrelocatable MatriqueSettings 0.1 qml/'
Module {
dependencies: ["Qt.labs.settings 1.0", "QtQuick 2.9"]
Component {
prototype: "QQmlSettings"
name: "MatriqueSettings 0.1"
exports: ["MatriqueSettings 0.1"]
exportMetaObjectRevisions: [1]
isComposite: true
isCreatable: false
isSingleton: true
Property { name: "lazyLoad"; type: "bool" }
Property { name: "asyncMessageDelegate"; type: "bool" }
Property { name: "richText"; type: "bool" }
Property { name: "pressAndHold"; type: "bool" }
Property { name: "darkTheme"; type: "bool" }
Property { name: "miniMode"; type: "bool" }
}
}

View File

@ -1,2 +0,0 @@
module MatriqueSettings
singleton MatriqueSettings 0.1 MatriqueSettings.qml

View File

@ -2,7 +2,7 @@ import QtQuick 2.9
import QtQuick.Controls 2.2 import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3 import QtQuick.Layouts 1.3
import Matrique 0.1 import Matrique 0.1
import MatriqueSettings 0.1 import Matrique.Settings 0.1
import "qrc:/qml/form" import "qrc:/qml/form"
@ -14,10 +14,8 @@ Page {
RoomListModel { RoomListModel {
id: roomListModel id: roomListModel
onRoomAdded: MatriqueSettings.lazyLoad ? {} : room.getPreviousContent(20) onRoomAdded: if (!MSettings.lazyLoad) room.getPreviousContent(20)
onNewMessage: window.active ? {} : matriqueController.showMessage(roomName, content, icon) onNewMessage: if (!window.active) matriqueController.showMessage(roomName, content, icon)
onDataChanged: roomListForm.rawCurrentIndex = -1
} }
RowLayout { RowLayout {
@ -28,13 +26,11 @@ Page {
id: roomListForm id: roomListForm
Layout.fillHeight: true Layout.fillHeight: true
Layout.preferredWidth: MatriqueSettings.miniMode ? 80 : page.width * 0.35 Layout.preferredWidth: MSettings.miniMode ? 80 : page.width * 0.35
Layout.minimumWidth: 80 Layout.minimumWidth: 80
Layout.maximumWidth: 360 Layout.maximumWidth: 360
listModel: roomListModel listModel: roomListModel
onEnterRoom: roomForm.currentRoom = currentRoom
} }
RoomForm { RoomForm {
@ -42,6 +38,8 @@ Page {
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
currentRoom: roomListForm.enteredRoom
} }
} }
} }

View File

@ -2,6 +2,7 @@ import QtQuick 2.9
import QtQuick.Controls 2.2 import QtQuick.Controls 2.2
import QtQuick.Controls.Material 2.2 import QtQuick.Controls.Material 2.2
import QtQuick.Layouts 1.3 import QtQuick.Layouts 1.3
import Matrique.Settings 0.1
import "component" import "component"
import "form" import "form"
@ -9,24 +10,133 @@ import "form"
Page { Page {
property var connection property var connection
SettingAccountForm { Page {
id: accountForm id: accountForm
parent: null parent: null
padding: 64
ColumnLayout {
RowLayout {
Layout.preferredHeight: 60
ImageStatus {
Layout.preferredWidth: height
Layout.fillHeight: true
source: matriqueController.isLogin ? connection.localUser && connection.localUser.avatarUrl ? "image://mxc/" + connection.localUser.avatarUrl : "" : "qrc:/asset/img/avatar.png"
displayText: matriqueController.isLogin && connection.localUser.displayName ? connection.localUser.displayName : ""
} }
SettingGeneralForm { ColumnLayout {
Layout.fillWidth: true
Layout.fillHeight: true
Label {
font.pointSize: 18
text: matriqueController.isLogin ? connection.localUser.displayName : ""
}
Label {
font.pointSize: 12
text: matriqueController.isLogin ? connection.localUser.id : ""
}
}
}
Button {
text: "Logout"
highlighted: true
onClicked: {
matriqueController.logout()
Qt.quit()
}
}
}
}
Page {
id: generalForm id: generalForm
parent: null parent: null
Column {
Switch {
text: "Lazy load at initial sync"
checked: MSettings.lazyLoad
onCheckedChanged: MSettings.lazyLoad = checked
}
Switch {
text: "Force loading message delegates asynchronously"
checked: MSettings.asyncMessageDelegate
onCheckedChanged: MSettings.asyncMessageDelegate = checked
}
Switch {
text: "Use RichText instead of StyledText"
checked: MSettings.richText
onCheckedChanged: MSettings.richText = checked
}
Switch {
text: "Use press and hold instead of right click"
checked: MSettings.pressAndHold
onCheckedChanged: MSettings.pressAndHold = checked
}
Switch {
text: "Rearrange rooms by activity"
checked: MSettings.rearrangeByActivity
onCheckedChanged: MSettings.rearrangeByActivity = checked
} }
SettingAppearanceForm { Button {
text: "Invoke GC"
highlighted: true
onClicked: gc()
}
}
}
Page {
id: appearanceForm id: appearanceForm
parent: null parent: null
Column {
Switch {
text: "Dark theme"
checked: MSettings.darkTheme
onCheckedChanged: MSettings.darkTheme = checked
}
Switch {
text: "Mini Room List"
checked: MSettings.miniMode
onCheckedChanged: MSettings.miniMode = checked
}
}
}
Page {
id: aboutForm
parent: null
padding: 64
ColumnLayout {
spacing: 16
Image {
Layout.preferredWidth: 64
Layout.preferredHeight: 64
source: "qrc:/asset/img/icon.png"
}
Label {
text: "Matrique, an IM client for the Matrix protocol."
}
Label {
text: "Released under GNU General Public License, version 3."
}
}
} }
RowLayout { RowLayout {
ColumnLayout { ColumnLayout {
Material.elevation: 10
Layout.preferredWidth: 240 Layout.preferredWidth: 240
Layout.fillHeight: true Layout.fillHeight: true
@ -57,6 +167,7 @@ Page {
Layout.fillWidth: true Layout.fillWidth: true
text: "About" text: "About"
onClicked: pushToStack(aboutForm)
} }
} }

View File

@ -1,12 +1,11 @@
import QtQuick 2.9 import QtQuick 2.9
import MatriqueSettings 0.1 import Matrique.Settings 0.1
MouseArea { MouseArea {
signal primaryClicked() signal primaryClicked()
signal secondaryClicked() signal secondaryClicked()
propagateComposedEvents: true acceptedButtons: MSettings.pressAndHold ? Qt.LeftButton : (Qt.LeftButton | Qt.RightButton)
acceptedButtons: MatriqueSettings.pressAndHold ? Qt.LeftButton : (Qt.LeftButton | Qt.RightButton)
onClicked: mouse.button == Qt.RightButton ? secondaryClicked() : primaryClicked() onClicked: mouse.button == Qt.RightButton ? secondaryClicked() : primaryClicked()
onPressAndHold: MatriqueSettings.pressAndHold ? secondaryClicked() : {} onPressAndHold: MSettings.pressAndHold ? secondaryClicked() : {}
} }

View File

@ -8,7 +8,7 @@ Item {
property string source: "" property string source: ""
property string displayText: "" property string displayText: ""
readonly property bool showImage: source readonly property bool showImage: source
readonly property bool showInitial: !showImage && displayText readonly property bool showInitial: !showImage && displayText || avatar.status != Image.Ready
id: item id: item
@ -55,6 +55,7 @@ Item {
} }
function getInitials(text) { function getInitials(text) {
if (!text) return "N"
return text.toUpperCase().replace(/[^a-zA-Z- ]/g, "").match(/\b\w/g); return text.toUpperCase().replace(/[^a-zA-Z- ]/g, "").match(/\b\w/g);
} }

View File

@ -1,11 +1,11 @@
import QtQuick 2.9 import QtQuick 2.9
import QtQuick.Controls 2.2 import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3 import QtQuick.Layouts 1.3
import QtQuick.Controls.Material 2.2 import Matrique.Settings 0.1
Item { Item {
property alias icon: iconText.text property alias icon: iconText.text
property var color: Material.theme == Material.Light ? "black" : "white" property var color: MSettings.darkTheme ? "white" : "black"
id: item id: item

View File

@ -2,14 +2,11 @@ import QtQuick 2.9
import QtQuick.Controls 2.2 import QtQuick.Controls 2.2
import QtQuick.Controls.Material 2.2 import QtQuick.Controls.Material 2.2
import Matrique 0.1 import Matrique 0.1
import MatriqueSettings 0.1 import Matrique.Settings 0.1
Item { Item {
readonly property bool hidden: marks === EventStatus.Redacted || marks === EventStatus.Hidden readonly property bool hidden: marks === EventStatus.Redacted || marks === EventStatus.Hidden
readonly property color background: MSettings.darkTheme ? "#242424" : "lightgrey"
readonly property bool darkTheme: Material.theme == Material.Dark
readonly property color background: darkTheme ? "#242424" : "lightgrey"
readonly property bool sentByMe: author === currentRoom.localUser readonly property bool sentByMe: author === currentRoom.localUser
readonly property bool isState: eventType === "state" || eventType === "emote" readonly property bool isState: eventType === "state" || eventType === "emote"
@ -24,13 +21,15 @@ Item {
AutoMouseArea { AutoMouseArea {
anchors.fill: parent anchors.fill: parent
onSecondaryClicked: Qt.createComponent("MessageContextMenu.qml").createObject(this)
propagateComposedEvents: true
onSecondaryClicked: Qt.createComponent("qrc:/qml/menu/MessageContextMenu.qml").createObject(this)
} }
Loader { Loader {
id: delegateLoader id: delegateLoader
asynchronous: MatriqueSettings.asyncMessageDelegate asynchronous: MSettings.asyncMessageDelegate
source: { source: {
if (eventType == "redaction" || hidden) return "" if (eventType == "redaction" || hidden) return ""

View File

@ -1,8 +0,0 @@
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import QtQuick.Controls.Material 2.2
Rectangle {
color: Material.accent
}

View File

@ -2,7 +2,7 @@ import QtQuick 2.9
import QtQuick.Controls 2.2 import QtQuick.Controls 2.2
import QtQuick.Controls.Material 2.2 import QtQuick.Controls.Material 2.2
import QtQuick.Layouts 1.3 import QtQuick.Layouts 1.3
import MatriqueSettings 0.1 import Matrique.Settings 0.1
Rectangle { Rectangle {
property bool flat: false property bool flat: false
@ -46,7 +46,7 @@ Rectangle {
wrapMode: Label.Wrap wrapMode: Label.Wrap
linkColor: darkBackground ? "white" : Material.accent linkColor: darkBackground ? "white" : Material.accent
textFormat: MatriqueSettings.richText ? Text.RichText : Text.StyledText textFormat: MSettings.richText ? Text.RichText : Text.StyledText
onLinkActivated: Qt.openUrlExternally(link) onLinkActivated: Qt.openUrlExternally(link)
} }

View File

@ -5,13 +5,16 @@ import QtQuick.Layouts 1.3
import QtQuick.Controls.Material 2.2 import QtQuick.Controls.Material 2.2
import QtGraphicalEffects 1.0 import QtGraphicalEffects 1.0
import Matrique 0.1 import Matrique 0.1
import Matrique.Settings 0.1
import "qrc:/qml/component" import "qrc:/qml/component"
import "qrc:/js/md.js" as Markdown import "qrc:/js/md.js" as Markdown
Item { Item {
id: item
property var currentRoom: null property var currentRoom: null
id: item
Drawer { Drawer {
id: roomDrawer id: roomDrawer
@ -114,7 +117,7 @@ Item {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 80 Layout.preferredHeight: 80
color: Material.theme == Material.Light ? "#eaeaea" : "#242424" color: MSettings.darkTheme ? "#242424" : "#eaeaea"
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
@ -293,6 +296,26 @@ Item {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 80 Layout.preferredHeight: 80
Timer {
id: timeoutTimer
repeat: false
interval: 2000
onTriggered: {
repeatTimer.stop()
currentRoom.sendTypingNotification(false)
}
}
Timer {
id: repeatTimer
repeat: true
interval: 5000
triggeredOnStart: true
onTriggered: currentRoom.sendTypingNotification(true)
}
RowLayout { RowLayout {
anchors.fill: parent anchors.fill: parent
spacing: 0 spacing: 0
@ -320,7 +343,8 @@ Item {
text: currentRoom ? currentRoom.cachedInput : "" text: currentRoom ? currentRoom.cachedInput : ""
onTextChanged: { onTextChanged: {
currentRoom.isTyping = true timeoutTimer.restart()
repeatTimer.start()
currentRoom.cachedInput = text currentRoom.cachedInput = text
} }
@ -332,7 +356,7 @@ Item {
} }
background: Rectangle { background: Rectangle {
color: Material.theme == Material.Light ? "#eaeaea" : "#242424" color: MSettings.darkTheme ? "#242424" : "#eaeaea"
} }
ToolTip.visible: currentRoom && currentRoom.hasUsersTyping ToolTip.visible: currentRoom && currentRoom.hasUsersTyping
@ -395,7 +419,7 @@ Item {
contentItem: MaterialIcon { icon: "\ue24e" } contentItem: MaterialIcon { icon: "\ue24e" }
background: Rectangle { color: Material.theme == Material.Light ? "#eaeaea" : "#242424" } background: Rectangle { color: MSettings.darkTheme ? "#242424" : "#eaeaea" }
onClicked: emojiPicker.visible ? emojiPicker.close() : emojiPicker.open() onClicked: emojiPicker.visible ? emojiPicker.close() : emojiPicker.open()
@ -417,4 +441,6 @@ Item {
} }
} }
} }
onCurrentRoomChanged: if (currentRoom && currentRoom.timelineSize === 0) currentRoom.getPreviousContent(20)
} }

View File

@ -6,17 +6,13 @@ import QtQuick.Controls.Material 2.2
import QtQml.Models 2.3 import QtQml.Models 2.3
import Matrique 0.1 import Matrique 0.1
import SortFilterProxyModel 0.2 import SortFilterProxyModel 0.2
import MatriqueSettings 0.1 import Matrique.Settings 0.1
import "qrc:/qml/component" import "qrc:/qml/component"
Item { Item {
property alias listModel: roomListProxyModel.sourceModel property alias listModel: roomListProxyModel.sourceModel
property alias rawCurrentIndex: listView.currentIndex property var enteredRoom: null
readonly property int currentIndex: roomListProxyModel.mapToSource(listView.currentIndex)
readonly property var currentRoom: currentIndex != -1 ? listModel.roomAt(currentIndex) : null
readonly property bool mini: MatriqueSettings.miniMode // Used as an indicator of whether the listform should be displayed as "Mini mode".
signal enterRoom()
ColumnLayout { ColumnLayout {
anchors.fill: parent anchors.fill: parent
@ -33,7 +29,7 @@ Item {
width: parent.width - 18 width: parent.width - 18
height: 36 height: 36
color: "white" color: "white"
leftPadding: mini ? 4 : 32 leftPadding: MSettings.miniMode ? 4 : 32
topPadding: 0 topPadding: 0
bottomPadding: 0 bottomPadding: 0
anchors.centerIn: parent anchors.centerIn: parent
@ -45,13 +41,13 @@ Item {
icon: "\ue8b6" icon: "\ue8b6"
color: "white" color: "white"
width: mini ? parent.width : parent.height width: MSettings.miniMode ? parent.width : parent.height
height: parent.height height: parent.height
} }
Label { Label {
height: parent.height height: parent.height
visible: !mini visible: !MSettings.miniMode
text: "Search" text: "Search"
color: "white" color: "white"
font.pointSize: 12 font.pointSize: 12
@ -66,11 +62,11 @@ Item {
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
color: Material.theme == Material.Light ? "#eaeaea" : "#242424" color: MSettings.darkTheme ? "#242424" : "#eaeaea"
Label { Label {
z: 10 z: 10
text: mini ? "Empty" : "Here? No, not here." text: MSettings.miniMode ? "Empty" : "Here? No, not here."
anchors.centerIn: parent anchors.centerIn: parent
visible: listView.count === 0 visible: listView.count === 0
} }
@ -98,6 +94,7 @@ Item {
sorters: [ sorters: [
RoleSorter { roleName: "category" }, RoleSorter { roleName: "category" },
RoleSorter { RoleSorter {
enabled: MSettings.rearrangeByActivity
roleName: "unreadCount" roleName: "unreadCount"
sortOrder: Qt.DescendingOrder sortOrder: Qt.DescendingOrder
}, },
@ -111,34 +108,26 @@ Item {
model: roomListProxyModel model: roomListProxyModel
highlight: Rectangle {
color: Material.accent
opacity: 0.2
}
highlightMoveDuration: 250
highlightResizeDuration: 0
currentIndex: -1 currentIndex: -1
boundsBehavior: Flickable.DragOverBounds boundsBehavior: Flickable.DragOverBounds
ScrollBar.vertical: ScrollBar { id: scrollBar } ScrollBar.vertical: ScrollBar { id: scrollBar }
delegate: Item { delegate: Rectangle {
id: swipeDelegate
width: parent.width width: parent.width
height: 80 height: 80
color: currentRoom === enteredRoom ? Material.background : "transparent"
AutoMouseArea { AutoMouseArea {
anchors.fill: parent anchors.fill: parent
onPressed: listView.currentIndex = index onSecondaryClicked: Qt.createComponent("qrc:/qml/menu/RoomContextMenu.qml").createObject(this)
onSecondaryClicked: roomListMenu.popup() onPrimaryClicked: category === RoomType.Invited ? inviteDialog.open() : enteredRoom = currentRoom
onPrimaryClicked: category === RoomType.Invited ? inviteDialog.open() : enterRoom()
} }
ToolTip.visible: mini && hovered
ToolTip.text: name
Rectangle { Rectangle {
width: 4 width: 4
height: parent.height height: parent.height
@ -186,6 +175,9 @@ Item {
} }
} }
} }
ToolTip.visible: MSettings.miniMode && hovered
ToolTip.text: name
} }
section.property: "display" section.property: "display"
@ -195,13 +187,13 @@ Item {
height: 24 height: 24
text: section text: section
color: "grey" color: "grey"
leftPadding: mini ? undefined : 16 leftPadding: MSettings.miniMode ? undefined : 16
elide: Text.ElideRight elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
horizontalAlignment: mini ? Text.AlignHCenter : undefined horizontalAlignment: MSettings.miniMode ? Text.AlignHCenter : undefined
background: Rectangle { background: Rectangle {
anchors.fill: parent anchors.fill: parent
color: Material.theme == Material.Light ? "#dbdbdb" : "#363636" color: MSettings.darkTheme ? "#363636" : "#dbdbdb"
} }
} }
@ -222,32 +214,6 @@ Item {
onAccepted: currentRoom.acceptInvitation() onAccepted: currentRoom.acceptInvitation()
onRejected: currentRoom.forget() onRejected: currentRoom.forget()
} }
Menu {
id: roomListMenu
MenuItem {
text: "Favourite"
checkable: true
checked: currentRoom && currentRoom.isFavourite
onTriggered: currentRoom.isFavourite ? currentRoom.removeTag("m.favourite") : currentRoom.addTag("m.favourite", "1")
}
MenuItem {
text: "Deprioritize"
checkable: true
checked: currentRoom && currentRoom.isLowPriority
onTriggered: currentRoom.isLowPriority ? currentRoom.removeTag("m.lowpriority") : currentRoom.addTag("m.lowpriority", "1")
}
MenuSeparator {}
MenuItem {
text: "Mark as Read"
onTriggered: currentRoom.markAllMessagesAsRead()
}
MenuItem {
text: "Leave Room"
onTriggered: currentRoom.forget()
}
}
} }
} }
} }

View File

@ -1,48 +0,0 @@
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import "qrc:/qml/component"
Page {
padding: 64
ColumnLayout {
RowLayout {
Layout.preferredHeight: 60
ImageStatus {
Layout.preferredWidth: height
Layout.fillHeight: true
source: matriqueController.isLogin ? connection.localUser && connection.localUser.avatarUrl ? "image://mxc/" + connection.localUser.avatarUrl : "" : "qrc:/asset/img/avatar.png"
displayText: matriqueController.isLogin && connection.localUser.displayName ? connection.localUser.displayName : "N"
}
ColumnLayout {
Layout.fillWidth: true
Layout.fillHeight: true
Label {
font.pointSize: 18
text: matriqueController.isLogin ? connection.localUser.displayName : ""
}
Label {
font.pointSize: 12
text: matriqueController.isLogin ? connection.localUser.id : ""
}
}
}
Button {
text: "Logout"
highlighted: true
onClicked: {
matriqueController.logout()
Qt.quit()
}
}
}
}

View File

@ -1,21 +0,0 @@
import QtQuick 2.9
import QtQuick.Controls 2.2
import MatriqueSettings 0.1
Page {
Column {
Switch {
id: themeSwitch
text: "Dark theme"
checked: MatriqueSettings.darkTheme
onCheckedChanged: MatriqueSettings.darkTheme = checked
}
Switch {
id: miniModeSwitch
text: "Mini Room List"
checked: MatriqueSettings.miniMode
onCheckedChanged: MatriqueSettings.miniMode = checked
}
}
}

View File

@ -1,38 +0,0 @@
import QtQuick 2.9
import QtQuick.Controls 2.2
import MatriqueSettings 0.1
Page {
Column {
Switch {
id: lazyLoadSwitch
text: "Lazy load at initial sync"
checked: MatriqueSettings.lazyLoad
onCheckedChanged: MatriqueSettings.lazyLoad = checked
}
Switch {
id: asyncMessageDelegateSwitch
text: "Force loading message delegates asynchronously"
checked: MatriqueSettings.asyncMessageDelegate
onCheckedChanged: MatriqueSettings.asyncMessageDelegate = checked
}
Switch {
id: richTextSwitch
text: "Use RichText instead of StyledText"
checked: MatriqueSettings.richText
onCheckedChanged: MatriqueSettings.richText = checked
}
Switch {
id: pressAndHoldSwitch
text: "Use press and hold instead of right click"
checked: MatriqueSettings.pressAndHold
onCheckedChanged: MatriqueSettings.pressAndHold = checked
}
Button {
text: "Invoke GC"
highlighted: true
onClicked: gc()
}
}
}

View File

@ -2,10 +2,10 @@ import QtQuick 2.9
import QtQuick.Controls 2.2 import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3 import QtQuick.Layouts 1.3
import QtQuick.Controls.Material 2.2 import QtQuick.Controls.Material 2.2
import Qt.labs.settings 1.0 as Labs import Qt.labs.settings 1.0
import QtGraphicalEffects 1.0 import QtGraphicalEffects 1.0
import Matrique 0.1 import Matrique 0.1
import MatriqueSettings 0.1 import Matrique.Settings 0.1
import "component" import "component"
import "form" import "form"
@ -21,9 +21,9 @@ ApplicationWindow {
minimumHeight: 480 minimumHeight: 480
title: qsTr("Matrique") title: qsTr("Matrique")
Material.theme: MatriqueSettings.darkTheme ? Material.Dark : Material.Light Material.theme: MSettings.darkTheme ? Material.Dark : Material.Light
Labs.Settings { Settings {
property alias homeserver: matriqueController.homeserver property alias homeserver: matriqueController.homeserver
property alias userID: matriqueController.userID property alias userID: matriqueController.userID
property alias token: matriqueController.token property alias token: matriqueController.token
@ -80,10 +80,11 @@ ApplicationWindow {
anchors.fill: parent anchors.fill: parent
spacing: 0 spacing: 0
SideNav { Rectangle {
id: sideNav id: sideNav
Layout.preferredWidth: 80 Layout.preferredWidth: 80
Layout.fillHeight: true Layout.fillHeight: true
color: Material.accent
ColumnLayout { ColumnLayout {
anchors.fill: parent anchors.fill: parent
@ -96,7 +97,7 @@ ApplicationWindow {
anchors.margins: 15 anchors.margins: 15
source: matriqueController.isLogin ? connection.localUser && connection.localUser.avatarUrl ? "image://mxc/" + connection.localUser.avatarUrl : "" : "qrc:/asset/img/avatar.png" source: matriqueController.isLogin ? connection.localUser && connection.localUser.avatarUrl ? "image://mxc/" + connection.localUser.avatarUrl : "" : "qrc:/asset/img/avatar.png"
displayText: matriqueController.isLogin && connection.localUser.displayName ? connection.localUser.displayName : "N" displayText: matriqueController.isLogin && connection.localUser.displayName ? connection.localUser.displayName : ""
} }
page: roomPage page: roomPage

View File

@ -0,0 +1,31 @@
import QtQuick 2.9
import QtQuick.Controls 2.2
Menu {
id: roomListMenu
MenuItem {
text: "Favourite"
checkable: true
checked: currentRoom && currentRoom.isFavourite
onTriggered: currentRoom.isFavourite ? currentRoom.removeTag("m.favourite") : currentRoom.addTag("m.favourite", "1")
}
MenuItem {
text: "Deprioritize"
checkable: true
checked: currentRoom && currentRoom.isLowPriority
onTriggered: currentRoom.isLowPriority ? currentRoom.removeTag("m.lowpriority") : currentRoom.addTag("m.lowpriority", "1")
}
MenuSeparator {}
MenuItem {
text: "Mark as Read"
onTriggered: currentRoom.markAllMessagesAsRead()
}
MenuItem {
text: "Leave Room"
onTriggered: currentRoom.forget()
}
Component.onCompleted: popup()
onClosed: roomListMenu.destroy()
}

11
res.qrc
View File

@ -7,7 +7,6 @@
<file>qml/Login.qml</file> <file>qml/Login.qml</file>
<file>qml/main.qml</file> <file>qml/main.qml</file>
<file>qml/component/ImageStatus.qml</file> <file>qml/component/ImageStatus.qml</file>
<file>qml/component/SideNav.qml</file>
<file>qml/form/RoomForm.qml</file> <file>qml/form/RoomForm.qml</file>
<file>qml/Room.qml</file> <file>qml/Room.qml</file>
<file>qml/component/SideNavButton.qml</file> <file>qml/component/SideNavButton.qml</file>
@ -24,19 +23,15 @@
<file>qml/form/RoomListForm.qml</file> <file>qml/form/RoomListForm.qml</file>
<file>qml/component/AudioBubble.qml</file> <file>qml/component/AudioBubble.qml</file>
<file>qml/Setting.qml</file> <file>qml/Setting.qml</file>
<file>qml/form/SettingAccountForm.qml</file>
<file>qml/form/SettingAppearanceForm.qml</file>
<file>qml/component/TextDelegate.qml</file> <file>qml/component/TextDelegate.qml</file>
<file>qml/component/MessageContextMenu.qml</file>
<file>qml/form/SettingGeneralForm.qml</file>
<file>qml/component/EmojiPicker.qml</file> <file>qml/component/EmojiPicker.qml</file>
<file>qml/component/EmojiButton.qml</file> <file>qml/component/EmojiButton.qml</file>
<file>qml/component/AutoImage.qml</file> <file>qml/component/AutoImage.qml</file>
<file>asset/img/icon.ico</file> <file>asset/img/icon.ico</file>
<file>asset/img/icon.icns</file> <file>asset/img/icon.icns</file>
<file>qml/component/AutoMouseArea.qml</file> <file>qml/component/AutoMouseArea.qml</file>
<file>qml/MatriqueSettings/MatriqueSettings.qml</file> <file>qml/MatriqueSettings.qml</file>
<file>qml/MatriqueSettings/qmldir</file> <file>qml/menu/MessageContextMenu.qml</file>
<file>qml/MatriqueSettings/plugins.qmltypes</file> <file>qml/menu/RoomContextMenu.qml</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@ -6,10 +6,10 @@
#include "controller.h" #include "controller.h"
#include "emojimodel.h" #include "emojimodel.h"
#include "imageprovider.h" #include "imageprovider.h"
#include "matriqueroom.h"
#include "messageeventmodel.h" #include "messageeventmodel.h"
#include "room.h" #include "room.h"
#include "roomlistmodel.h" #include "roomlistmodel.h"
#include "matriqueroom.h"
#include "csapi/joining.h" #include "csapi/joining.h"
#include "csapi/leaving.h" #include "csapi/leaving.h"
@ -28,6 +28,7 @@ int main(int argc, char *argv[]) {
qRegisterMetaType<MatriqueRoom *>("MatriqueRoom*"); qRegisterMetaType<MatriqueRoom *>("MatriqueRoom*");
qRegisterMetaType<User *>("User*"); qRegisterMetaType<User *>("User*");
qRegisterMetaType<MessageEventType>("MessageEventType"); qRegisterMetaType<MessageEventType>("MessageEventType");
qRegisterMetaType<MatriqueRoom *>("MatriqueRoom");
qmlRegisterType<Controller>("Matrique", 0, 1, "Controller"); qmlRegisterType<Controller>("Matrique", 0, 1, "Controller");
qmlRegisterType<RoomListModel>("Matrique", 0, 1, "RoomListModel"); qmlRegisterType<RoomListModel>("Matrique", 0, 1, "RoomListModel");
@ -36,9 +37,10 @@ int main(int argc, char *argv[]) {
qmlRegisterUncreatableType<RoomMessageEvent>("Matrique", 0, 1, qmlRegisterUncreatableType<RoomMessageEvent>("Matrique", 0, 1,
"RoomMessageEvent", "ENUM"); "RoomMessageEvent", "ENUM");
qmlRegisterUncreatableType<RoomType>("Matrique", 0, 1, "RoomType", "ENUM"); qmlRegisterUncreatableType<RoomType>("Matrique", 0, 1, "RoomType", "ENUM");
qmlRegisterSingletonType(QUrl("qrc:/qml/MatriqueSettings.qml"),
"Matrique.Settings", 0, 1, "MSettings");
QQmlApplicationEngine engine; QQmlApplicationEngine engine;
engine.addImportPath("qrc:/qml");
ImageProvider *m_provider = new ImageProvider(); ImageProvider *m_provider = new ImageProvider();

View File

@ -12,25 +12,7 @@
MatriqueRoom::MatriqueRoom(Connection* connection, QString roomId, MatriqueRoom::MatriqueRoom(Connection* connection, QString roomId,
JoinState joinState) JoinState joinState)
: Room(connection, std::move(roomId), joinState) { : Room(connection, std::move(roomId), joinState) {}
m_timeoutTimer->setSingleShot(true);
m_timeoutTimer->setInterval(2000);
m_repeatTimer->setInterval(5000);
connect(m_timeoutTimer, &QTimer::timeout, [=] { setIsTyping(false); });
connect(m_repeatTimer, &QTimer::timeout,
[=] { sendTypingNotification(true); });
connect(this, &MatriqueRoom::isTypingChanged, [=] {
if (m_isTyping) {
m_timeoutTimer->start();
m_repeatTimer->start();
sendTypingNotification(true);
} else {
m_timeoutTimer->stop();
m_repeatTimer->stop();
sendTypingNotification(false);
}
});
}
void MatriqueRoom::chooseAndUploadFile() { void MatriqueRoom::chooseAndUploadFile() {
auto localFile = QFileDialog::getOpenFileUrl(Q_NULLPTR, tr("Save File as")); auto localFile = QFileDialog::getOpenFileUrl(Q_NULLPTR, tr("Save File as"));
@ -88,7 +70,7 @@ QString MatriqueRoom::getUsersTyping() {
for (User* user : users) { for (User* user : users) {
usersTypingStr += user->displayname() + " "; usersTypingStr += user->displayname() + " ";
} }
usersTypingStr += users.count() == 1 ? "is" : "are"; usersTypingStr += users.count() < 2 ? "is" : "are";
usersTypingStr += " typing."; usersTypingStr += " typing.";
return usersTypingStr; return usersTypingStr;
} }

View File

@ -10,8 +10,6 @@ using namespace QMatrixClient;
class MatriqueRoom : public Room { class MatriqueRoom : public Room {
Q_OBJECT Q_OBJECT
Q_PROPERTY(
bool isTyping READ isTyping WRITE setIsTyping NOTIFY isTypingChanged)
Q_PROPERTY(bool hasUsersTyping READ hasUsersTyping NOTIFY typingChanged) Q_PROPERTY(bool hasUsersTyping READ hasUsersTyping NOTIFY typingChanged)
Q_PROPERTY(QString usersTyping READ getUsersTyping NOTIFY typingChanged) Q_PROPERTY(QString usersTyping READ getUsersTyping NOTIFY typingChanged)
Q_PROPERTY(QString cachedInput READ cachedInput WRITE setCachedInput NOTIFY Q_PROPERTY(QString cachedInput READ cachedInput WRITE setCachedInput NOTIFY
@ -20,15 +18,6 @@ class MatriqueRoom : public Room {
explicit MatriqueRoom(Connection* connection, QString roomId, explicit MatriqueRoom(Connection* connection, QString roomId,
JoinState joinState = {}); JoinState joinState = {});
bool isTyping() { return m_isTyping; }
void setIsTyping(bool isTyping) {
if (isTyping) m_timeoutTimer->start();
if (isTyping != m_isTyping) {
m_isTyping = isTyping;
emit isTypingChanged();
}
}
const QString& cachedInput() const { return m_cachedInput; } const QString& cachedInput() const { return m_cachedInput; }
void setCachedInput(const QString& input) { void setCachedInput(const QString& input) {
if (input != m_cachedInput) { if (input != m_cachedInput) {
@ -44,15 +33,11 @@ class MatriqueRoom : public Room {
private: private:
QString m_cachedInput; QString m_cachedInput;
bool m_isTyping;
QTimer* m_timeoutTimer = new QTimer();
QTimer* m_repeatTimer = new QTimer();
QString getMIME(const QUrl& fileUrl) const; QString getMIME(const QUrl& fileUrl) const;
void postFile(const QUrl& localFile, const QUrl& mxcUrl); void postFile(const QUrl& localFile, const QUrl& mxcUrl);
signals: signals:
void isTypingChanged();
void cachedInputChanged(); void cachedInputChanged();
public slots: public slots:

View File

@ -153,6 +153,7 @@ QVariant RoomListModel::data(const QModelIndex& index, int role) const {
} }
if (role == UnreadCountRole) return room->unreadCount(); if (role == UnreadCountRole) return room->unreadCount();
if (role == LastEventRole) return room->lastEvent(); if (role == LastEventRole) return room->lastEvent();
if (role == CurrentRoomRole) return QVariant::fromValue(room);
return QVariant(); return QVariant();
} }
@ -184,5 +185,6 @@ QHash<int, QByteArray> RoomListModel::roleNames() const {
roles[CategoryRole] = "category"; roles[CategoryRole] = "category";
roles[UnreadCountRole] = "unreadCount"; roles[UnreadCountRole] = "unreadCount";
roles[LastEventRole] = "lastEvent"; roles[LastEventRole] = "lastEvent";
roles[CurrentRoomRole] = "currentRoom";
return roles; return roles;
} }

View File

@ -35,7 +35,8 @@ class RoomListModel : public QAbstractListModel {
TopicRole, TopicRole,
CategoryRole, CategoryRole,
UnreadCountRole, UnreadCountRole,
LastEventRole LastEventRole,
CurrentRoomRole,
}; };
RoomListModel(QObject* parent = 0); RoomListModel(QObject* parent = 0);