parent
91fc03aba5
commit
ae538a7c45
|
@ -0,0 +1,113 @@
|
||||||
|
import QtQuick 2.9
|
||||||
|
import QtQuick.Controls 2.2
|
||||||
|
|
||||||
|
TextArea {
|
||||||
|
property real progress: 0
|
||||||
|
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
placeholderText: "Send a Message"
|
||||||
|
leftPadding: 16
|
||||||
|
topPadding: 0
|
||||||
|
bottomPadding: 0
|
||||||
|
selectByMouse: true
|
||||||
|
verticalAlignment: TextEdit.AlignVCenter
|
||||||
|
|
||||||
|
text: currentRoom ? currentRoom.cachedInput : ""
|
||||||
|
|
||||||
|
background: Item {
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
ToolTip.visible: currentRoom
|
||||||
|
&& currentRoom.hasUsersTyping
|
||||||
|
ToolTip.text: currentRoom ? currentRoom.usersTyping : ""
|
||||||
|
|
||||||
|
Keys.onReturnPressed: {
|
||||||
|
if (event.modifiers & Qt.ShiftModifier) {
|
||||||
|
insert(cursorPosition, "\n")
|
||||||
|
} else {
|
||||||
|
postMessage(text)
|
||||||
|
text = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onTextChanged: {
|
||||||
|
timeoutTimer.restart()
|
||||||
|
repeatTimer.start()
|
||||||
|
currentRoom.cachedInput = text
|
||||||
|
}
|
||||||
|
|
||||||
|
function postMessage(text) {
|
||||||
|
if (text.trim().length === 0) { return }
|
||||||
|
if(!currentRoom) { return }
|
||||||
|
|
||||||
|
var PREFIX_ME = '/me '
|
||||||
|
var PREFIX_NOTICE = '/notice '
|
||||||
|
var PREFIX_RAINBOW = '/rainbow '
|
||||||
|
var PREFIX_HTML = '/html '
|
||||||
|
var PREFIX_MARKDOWN = '/md '
|
||||||
|
|
||||||
|
var replyRe = new RegExp("^> <(.*)><(.*)> (.*)\n\n(.*)")
|
||||||
|
if (text.match(replyRe)) {
|
||||||
|
var matches = text.match(replyRe)
|
||||||
|
currentRoom.sendReply(matches[1], matches[2], matches[3], matches[4])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text.indexOf(PREFIX_ME) === 0) {
|
||||||
|
text = text.substr(PREFIX_ME.length)
|
||||||
|
currentRoom.postMessage(text, RoomMessageEvent.Emote)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (text.indexOf(PREFIX_NOTICE) === 0) {
|
||||||
|
text = text.substr(PREFIX_NOTICE.length)
|
||||||
|
currentRoom.postMessage(text, RoomMessageEvent.Notice)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (text.indexOf(PREFIX_RAINBOW) === 0) {
|
||||||
|
text = text.substr(PREFIX_RAINBOW.length)
|
||||||
|
|
||||||
|
var parsedText = ""
|
||||||
|
var rainbowColor = ["#ff2b00", "#ff5500", "#ff8000", "#ffaa00", "#ffd500", "#ffff00", "#d4ff00", "#aaff00", "#80ff00", "#55ff00", "#2bff00", "#00ff00", "#00ff2b", "#00ff55", "#00ff80", "#00ffaa", "#00ffd5", "#00ffff", "#00d4ff", "#00aaff", "#007fff", "#0055ff", "#002bff", "#0000ff", "#2a00ff", "#5500ff", "#7f00ff", "#aa00ff", "#d400ff", "#ff00ff", "#ff00d4", "#ff00aa", "#ff0080", "#ff0055", "#ff002b", "#ff0000"]
|
||||||
|
for (var i = 0; i < text.length; i++) {
|
||||||
|
parsedText = parsedText + "<font color='" + rainbowColor[i % rainbowColor.length] + "'>" + text.charAt(i) + "</font>"
|
||||||
|
}
|
||||||
|
currentRoom.postHtmlMessage(text, parsedText, RoomMessageEvent.Text)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (text.indexOf(PREFIX_HTML) === 0) {
|
||||||
|
text = text.substr(PREFIX_HTML.length)
|
||||||
|
var re = new RegExp("<.*?>")
|
||||||
|
var plainText = text.replace(re, "")
|
||||||
|
currentRoom.postHtmlMessage(plainText, text, RoomMessageEvent.Text)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (text.indexOf(PREFIX_MARKDOWN) === 0) {
|
||||||
|
text = text.substr(PREFIX_MARKDOWN.length)
|
||||||
|
var parsedText = Markdown.markdown_parser(text)
|
||||||
|
currentRoom.postHtmlMessage(text, parsedText, RoomMessageEvent.Text)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
currentRoom.postPlainText(text)
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,8 @@ import QtQuick.Controls.Material 2.2
|
||||||
import Spectral 0.1
|
import Spectral 0.1
|
||||||
import Spectral.Setting 0.1
|
import Spectral.Setting 0.1
|
||||||
|
|
||||||
|
import Spectral.Component 2.0
|
||||||
|
|
||||||
import "qrc:/js/util.js" as Util
|
import "qrc:/js/util.js" as Util
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
|
|
@ -2,3 +2,6 @@ module Spectral.Component
|
||||||
AutoMouseArea 2.0 AutoMouseArea.qml
|
AutoMouseArea 2.0 AutoMouseArea.qml
|
||||||
MaterialIcon 2.0 MaterialIcon.qml
|
MaterialIcon 2.0 MaterialIcon.qml
|
||||||
SideNavButton 2.0 SideNavButton.qml
|
SideNavButton 2.0 SideNavButton.qml
|
||||||
|
AutoImage 2.0 AutoImage.qml
|
||||||
|
AutoLabel 2.0 AutoLabel.qml
|
||||||
|
AutoTextArea 2.0 AutoTextArea.qml
|
||||||
|
|
|
@ -1,539 +0,0 @@
|
||||||
import QtQuick 2.9
|
|
||||||
import QtQuick.Controls 2.2
|
|
||||||
import QtQuick.Layouts 1.3
|
|
||||||
import QtQuick.Controls.Material 2.2
|
|
||||||
|
|
||||||
import Spectral.Component 2.0
|
|
||||||
import Spectral.Component.Emoji 2.0
|
|
||||||
import Spectral.Component.Timeline 2.0
|
|
||||||
import Spectral.Menu 2.0
|
|
||||||
import Spectral.Effect 2.0
|
|
||||||
|
|
||||||
import Spectral 0.1
|
|
||||||
import Spectral.Setting 0.1
|
|
||||||
import SortFilterProxyModel 0.2
|
|
||||||
|
|
||||||
import "qrc:/js/md.js" as Markdown
|
|
||||||
import "qrc:/js/util.js" as Util
|
|
||||||
|
|
||||||
Item {
|
|
||||||
property var currentRoom: null
|
|
||||||
|
|
||||||
id: item
|
|
||||||
|
|
||||||
MessageEventModel {
|
|
||||||
id: messageEventModel
|
|
||||||
room: currentRoom
|
|
||||||
}
|
|
||||||
|
|
||||||
RoomDrawer {
|
|
||||||
width: Math.min(item.width * 0.7, 480)
|
|
||||||
height: item.height
|
|
||||||
|
|
||||||
id: roomDrawer
|
|
||||||
|
|
||||||
room: currentRoom
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
visible: !currentRoom
|
|
||||||
text: "Please choose a room."
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
visible: currentRoom
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredHeight: 64
|
|
||||||
|
|
||||||
z: 10
|
|
||||||
|
|
||||||
color: Material.accent
|
|
||||||
|
|
||||||
ItemDelegate {
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
onClicked: roomDrawer.open()
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: 12
|
|
||||||
|
|
||||||
spacing: 12
|
|
||||||
|
|
||||||
ImageItem {
|
|
||||||
Layout.preferredWidth: height
|
|
||||||
Layout.fillHeight: true
|
|
||||||
|
|
||||||
hint: currentRoom ? currentRoom.displayName : "No name"
|
|
||||||
image: spectralController.safeImage(currentRoom ? currentRoom.avatar : null)
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
|
|
||||||
visible: parent.width > 64
|
|
||||||
|
|
||||||
Label {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
|
|
||||||
text: currentRoom ? currentRoom.displayName : ""
|
|
||||||
color: "white"
|
|
||||||
font.pointSize: 12
|
|
||||||
elide: Text.ElideRight
|
|
||||||
wrapMode: Text.NoWrap
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
|
|
||||||
text: currentRoom ? (currentRoom.topic).replace(/(\r\n\t|\n|\r\t)/gm,"") : ""
|
|
||||||
color: "white"
|
|
||||||
elide: Text.ElideRight
|
|
||||||
wrapMode: Text.NoWrap
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ProgressBar {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
z: 10
|
|
||||||
|
|
||||||
visible: currentRoom && currentRoom.busy
|
|
||||||
indeterminate: true
|
|
||||||
}
|
|
||||||
|
|
||||||
ListView {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.leftMargin: 16
|
|
||||||
Layout.rightMargin: 16
|
|
||||||
|
|
||||||
id: messageListView
|
|
||||||
|
|
||||||
displayMarginBeginning: 40
|
|
||||||
displayMarginEnd: 40
|
|
||||||
verticalLayoutDirection: ListView.BottomToTop
|
|
||||||
spacing: 8
|
|
||||||
|
|
||||||
cacheBuffer: 200
|
|
||||||
|
|
||||||
boundsBehavior: Flickable.DragOverBounds
|
|
||||||
|
|
||||||
property int largestVisibleIndex: count > 0 ? indexAt(contentX, contentY + height - 1) : -1
|
|
||||||
|
|
||||||
onContentYChanged: {
|
|
||||||
if(verticalVelocity < 0 && contentY - 5000 < originY)
|
|
||||||
currentRoom.getPreviousContent(50);
|
|
||||||
}
|
|
||||||
|
|
||||||
onMovementEnded: {
|
|
||||||
currentRoom.saveViewport(sortedMessageEventModel.mapToSource(indexAt(contentX, contentY)), sortedMessageEventModel.mapToSource(largestVisibleIndex))
|
|
||||||
var newReadMarker = sortedMessageEventModel.get(largestVisibleIndex).eventId
|
|
||||||
if (newReadMarker) currentRoom.readMarkerEventId = newReadMarker
|
|
||||||
}
|
|
||||||
|
|
||||||
displaced: Transition {
|
|
||||||
NumberAnimation {
|
|
||||||
property: "y"; duration: 200
|
|
||||||
easing.type: Easing.OutQuad
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
model: SortFilterProxyModel {
|
|
||||||
id: sortedMessageEventModel
|
|
||||||
|
|
||||||
sourceModel: messageEventModel
|
|
||||||
|
|
||||||
filters: ExpressionFilter {
|
|
||||||
expression: marks !== 0x08 && marks !== 0x10
|
|
||||||
}
|
|
||||||
|
|
||||||
onModelReset: {
|
|
||||||
if (currentRoom)
|
|
||||||
{
|
|
||||||
var lastScrollPosition = mapFromSource(currentRoom.savedTopVisibleIndex())
|
|
||||||
if (lastScrollPosition === 0)
|
|
||||||
messageListView.positionViewAtBeginning()
|
|
||||||
else
|
|
||||||
{
|
|
||||||
console.log("Scrolling to position", lastScrollPosition)
|
|
||||||
messageListView.currentIndex = lastScrollPosition
|
|
||||||
}
|
|
||||||
if (messageListView.contentY < messageListView.originY + 10 || currentRoom.timelineSize === 0)
|
|
||||||
currentRoom.getPreviousContent(100)
|
|
||||||
}
|
|
||||||
console.log("Model timeline reset")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate: ColumnLayout {
|
|
||||||
width: parent.width
|
|
||||||
|
|
||||||
id: delegateColumn
|
|
||||||
|
|
||||||
spacing: 8
|
|
||||||
|
|
||||||
Label {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
|
|
||||||
visible: section !== aboveSection
|
|
||||||
|
|
||||||
text: section
|
|
||||||
color: "white"
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
leftPadding: 8
|
|
||||||
rightPadding: 8
|
|
||||||
topPadding: 4
|
|
||||||
bottomPadding: 4
|
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: MSettings.darkTheme ? "#484848" : "grey"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MessageDelegate {
|
|
||||||
visible: eventType === "notice" || eventType === "message" || eventType === "image" || eventType === "video" || eventType === "audio" || eventType === "file"
|
|
||||||
}
|
|
||||||
|
|
||||||
StateDelegate {
|
|
||||||
Layout.maximumWidth: messageListView.width * 0.8
|
|
||||||
|
|
||||||
visible: eventType === "emote" || eventType === "state"
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
|
|
||||||
visible: eventType === "other"
|
|
||||||
|
|
||||||
text: display
|
|
||||||
color: "grey"
|
|
||||||
font.italic: true
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
|
|
||||||
visible: readMarker === true && index !== 0
|
|
||||||
|
|
||||||
text: "And Now"
|
|
||||||
color: "white"
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
leftPadding: 8
|
|
||||||
rightPadding: 8
|
|
||||||
topPadding: 4
|
|
||||||
bottomPadding: 4
|
|
||||||
|
|
||||||
background: Rectangle { color: MSettings.darkTheme ? "#484848" : "grey" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RoundButton {
|
|
||||||
width: 64
|
|
||||||
height: 64
|
|
||||||
|
|
||||||
id: goTopFab
|
|
||||||
|
|
||||||
visible: !(parent.atYEnd || messageListView.moving)
|
|
||||||
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
|
|
||||||
contentItem: MaterialIcon {
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
icon: "\ue313"
|
|
||||||
color: "white"
|
|
||||||
}
|
|
||||||
|
|
||||||
Material.background: Material.accent
|
|
||||||
|
|
||||||
onClicked: parent.positionViewAtBeginning()
|
|
||||||
|
|
||||||
Behavior on opacity { NumberAnimation { duration: 200 } }
|
|
||||||
}
|
|
||||||
|
|
||||||
MessageContextMenu { id: messageContextMenu }
|
|
||||||
|
|
||||||
Popup {
|
|
||||||
property string sourceText
|
|
||||||
|
|
||||||
x: (window.width - width) / 2
|
|
||||||
y: (window.height - height) / 2
|
|
||||||
width: 480
|
|
||||||
|
|
||||||
id: sourceDialog
|
|
||||||
|
|
||||||
parent: ApplicationWindow.overlay
|
|
||||||
|
|
||||||
modal: true
|
|
||||||
|
|
||||||
padding: 16
|
|
||||||
|
|
||||||
closePolicy: Dialog.CloseOnEscape | Dialog.CloseOnPressOutside
|
|
||||||
|
|
||||||
contentItem: ScrollView {
|
|
||||||
TextArea {
|
|
||||||
readOnly: true
|
|
||||||
selectByMouse: true
|
|
||||||
|
|
||||||
text: sourceDialog.sourceText
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Popup {
|
|
||||||
property alias listModel: readMarkerListView.model
|
|
||||||
|
|
||||||
x: (window.width - width) / 2
|
|
||||||
y: (window.height - height) / 2
|
|
||||||
width: 320
|
|
||||||
|
|
||||||
id: readMarkerDialog
|
|
||||||
|
|
||||||
parent: ApplicationWindow.overlay
|
|
||||||
|
|
||||||
modal: true
|
|
||||||
padding: 16
|
|
||||||
|
|
||||||
closePolicy: Dialog.CloseOnEscape | Dialog.CloseOnPressOutside
|
|
||||||
|
|
||||||
contentItem: ListView {
|
|
||||||
implicitHeight: Math.min(window.height - 64, readMarkerListView.contentHeight)
|
|
||||||
|
|
||||||
id: readMarkerListView
|
|
||||||
|
|
||||||
clip: true
|
|
||||||
boundsBehavior: Flickable.DragOverBounds
|
|
||||||
|
|
||||||
delegate: ItemDelegate {
|
|
||||||
width: parent.width
|
|
||||||
height: 48
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: 8
|
|
||||||
spacing: 12
|
|
||||||
|
|
||||||
ImageItem {
|
|
||||||
Layout.preferredWidth: height
|
|
||||||
Layout.fillHeight: true
|
|
||||||
|
|
||||||
image: modelData.avatar
|
|
||||||
hint: modelData.displayName
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
text: modelData.displayName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ScrollBar.vertical: ScrollBar {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredHeight: 40
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredHeight: 40
|
|
||||||
Layout.leftMargin: 16
|
|
||||||
Layout.rightMargin: 16
|
|
||||||
|
|
||||||
color: Material.background
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
anchors.verticalCenter: parent.top
|
|
||||||
width: parent.width
|
|
||||||
height: 48
|
|
||||||
|
|
||||||
color: MSettings.darkTheme ? "#303030" : "#fafafa"
|
|
||||||
|
|
||||||
layer.enabled: true
|
|
||||||
layer.effect: ElevationEffect {
|
|
||||||
elevation: 2
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
ItemDelegate {
|
|
||||||
Layout.preferredWidth: 48
|
|
||||||
Layout.preferredHeight: 48
|
|
||||||
|
|
||||||
contentItem: MaterialIcon { icon: "\ue226" }
|
|
||||||
|
|
||||||
onClicked: currentRoom.chooseAndUploadFile()
|
|
||||||
}
|
|
||||||
|
|
||||||
ScrollView {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredHeight: 48
|
|
||||||
|
|
||||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
|
||||||
|
|
||||||
clip: true
|
|
||||||
|
|
||||||
TextArea {
|
|
||||||
property real progress: 0
|
|
||||||
|
|
||||||
id: inputField
|
|
||||||
|
|
||||||
wrapMode: Text.Wrap
|
|
||||||
placeholderText: "Send a Message"
|
|
||||||
leftPadding: 16
|
|
||||||
topPadding: 0
|
|
||||||
bottomPadding: 0
|
|
||||||
selectByMouse: true
|
|
||||||
verticalAlignment: TextEdit.AlignVCenter
|
|
||||||
|
|
||||||
text: currentRoom ? currentRoom.cachedInput : ""
|
|
||||||
|
|
||||||
background: Item {}
|
|
||||||
|
|
||||||
onTextChanged: {
|
|
||||||
timeoutTimer.restart()
|
|
||||||
repeatTimer.start()
|
|
||||||
currentRoom.cachedInput = text
|
|
||||||
}
|
|
||||||
|
|
||||||
ToolTip.visible: currentRoom && currentRoom.hasUsersTyping
|
|
||||||
ToolTip.text: currentRoom ? currentRoom.usersTyping : ""
|
|
||||||
|
|
||||||
Keys.onReturnPressed: {
|
|
||||||
if (event.modifiers & Qt.ShiftModifier) {
|
|
||||||
inputField.insert(inputField.cursorPosition, "\n")
|
|
||||||
} else {
|
|
||||||
inputField.postMessage(inputField.text)
|
|
||||||
inputField.text = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
function postMessage(text) {
|
|
||||||
if (text.trim().length === 0) { return }
|
|
||||||
if(!currentRoom) { return }
|
|
||||||
|
|
||||||
var PREFIX_ME = '/me '
|
|
||||||
var PREFIX_NOTICE = '/notice '
|
|
||||||
var PREFIX_RAINBOW = '/rainbow '
|
|
||||||
var PREFIX_HTML = '/html '
|
|
||||||
var PREFIX_MARKDOWN = '/md '
|
|
||||||
|
|
||||||
var replyRe = new RegExp("^> <(.*)><(.*)> (.*)\n\n(.*)")
|
|
||||||
if (text.match(replyRe)) {
|
|
||||||
var matches = text.match(replyRe)
|
|
||||||
currentRoom.sendReply(matches[1], matches[2], matches[3], matches[4])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (text.indexOf(PREFIX_ME) === 0) {
|
|
||||||
text = text.substr(PREFIX_ME.length)
|
|
||||||
currentRoom.postMessage(text, RoomMessageEvent.Emote)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (text.indexOf(PREFIX_NOTICE) === 0) {
|
|
||||||
text = text.substr(PREFIX_NOTICE.length)
|
|
||||||
currentRoom.postMessage(text, RoomMessageEvent.Notice)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (text.indexOf(PREFIX_RAINBOW) === 0) {
|
|
||||||
text = text.substr(PREFIX_RAINBOW.length)
|
|
||||||
|
|
||||||
var parsedText = ""
|
|
||||||
var rainbowColor = ["#ff2b00", "#ff5500", "#ff8000", "#ffaa00", "#ffd500", "#ffff00", "#d4ff00", "#aaff00", "#80ff00", "#55ff00", "#2bff00", "#00ff00", "#00ff2b", "#00ff55", "#00ff80", "#00ffaa", "#00ffd5", "#00ffff", "#00d4ff", "#00aaff", "#007fff", "#0055ff", "#002bff", "#0000ff", "#2a00ff", "#5500ff", "#7f00ff", "#aa00ff", "#d400ff", "#ff00ff", "#ff00d4", "#ff00aa", "#ff0080", "#ff0055", "#ff002b", "#ff0000"]
|
|
||||||
for (var i = 0; i < text.length; i++) {
|
|
||||||
parsedText = parsedText + "<font color='" + rainbowColor[i % rainbowColor.length] + "'>" + text.charAt(i) + "</font>"
|
|
||||||
}
|
|
||||||
currentRoom.postHtmlMessage(text, parsedText, RoomMessageEvent.Text)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (text.indexOf(PREFIX_HTML) === 0) {
|
|
||||||
text = text.substr(PREFIX_HTML.length)
|
|
||||||
var re = new RegExp("<.*?>")
|
|
||||||
var plainText = text.replace(re, "")
|
|
||||||
currentRoom.postHtmlMessage(plainText, text, RoomMessageEvent.Text)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (text.indexOf(PREFIX_MARKDOWN) === 0) {
|
|
||||||
text = text.substr(PREFIX_MARKDOWN.length)
|
|
||||||
var parsedText = Markdown.markdown_parser(text)
|
|
||||||
currentRoom.postHtmlMessage(text, parsedText, RoomMessageEvent.Text)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
currentRoom.postPlainText(text)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ItemDelegate {
|
|
||||||
Layout.preferredWidth: 48
|
|
||||||
Layout.preferredHeight: 48
|
|
||||||
|
|
||||||
id: emojiButton
|
|
||||||
|
|
||||||
contentItem: MaterialIcon { icon: "\ue24e" }
|
|
||||||
|
|
||||||
onClicked: emojiPicker.visible ? emojiPicker.close() : emojiPicker.open()
|
|
||||||
|
|
||||||
EmojiPicker {
|
|
||||||
x: window.width - 370
|
|
||||||
y: window.height - 400
|
|
||||||
|
|
||||||
width: 360
|
|
||||||
height: 320
|
|
||||||
|
|
||||||
id: emojiPicker
|
|
||||||
|
|
||||||
parent: ApplicationWindow.overlay
|
|
||||||
|
|
||||||
Material.elevation: 2
|
|
||||||
|
|
||||||
textArea: inputField
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,266 +0,0 @@
|
||||||
import QtQuick 2.9
|
|
||||||
import QtQuick.Controls 2.2
|
|
||||||
import QtQuick.Layouts 1.3
|
|
||||||
import QtGraphicalEffects 1.0
|
|
||||||
import QtQuick.Controls.Material 2.2
|
|
||||||
import QtQml.Models 2.3
|
|
||||||
|
|
||||||
import Spectral.Component 2.0
|
|
||||||
import Spectral.Menu 2.0
|
|
||||||
import Spectral.Effect 2.0
|
|
||||||
|
|
||||||
import Spectral 0.1
|
|
||||||
import Spectral.Setting 0.1
|
|
||||||
import SortFilterProxyModel 0.2
|
|
||||||
|
|
||||||
import "qrc:/js/util.js" as Util
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
property alias listModel: sortedRoomListModel.sourceModel
|
|
||||||
property int filter: 0
|
|
||||||
property var enteredRoom: null
|
|
||||||
|
|
||||||
color: MSettings.darkTheme ? "#323232" : "#f3f3f3"
|
|
||||||
|
|
||||||
Label {
|
|
||||||
text: MSettings.miniMode ? "Empty" : "Here? No, not here."
|
|
||||||
anchors.centerIn: parent
|
|
||||||
visible: listView.count === 0
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
TextField {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredHeight: 40
|
|
||||||
Layout.margins: 12
|
|
||||||
|
|
||||||
id: searchField
|
|
||||||
|
|
||||||
leftPadding: MSettings.miniMode ? 4 : 32
|
|
||||||
topPadding: 0
|
|
||||||
bottomPadding: 0
|
|
||||||
placeholderText: "Search..."
|
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: MSettings.darkTheme ? "#303030" : "#fafafa"
|
|
||||||
layer.enabled: true
|
|
||||||
layer.effect: ElevationEffect {
|
|
||||||
elevation: searchField.focus ? 2 : 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Shortcut {
|
|
||||||
sequence: StandardKey.Find
|
|
||||||
onActivated: searchField.forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SortFilterProxyModel {
|
|
||||||
id: sortedRoomListModel
|
|
||||||
|
|
||||||
proxyRoles: ExpressionRole {
|
|
||||||
name: "display"
|
|
||||||
expression: {
|
|
||||||
switch (category) {
|
|
||||||
case 1: return "Invited"
|
|
||||||
case 2: return "Favorites"
|
|
||||||
case 3: return "Rooms"
|
|
||||||
case 4: return "People"
|
|
||||||
case 5: return "Low Priority"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sorters: [
|
|
||||||
RoleSorter { roleName: "category" },
|
|
||||||
RoleSorter {
|
|
||||||
roleName: "lastActiveTime"
|
|
||||||
sortOrder: Qt.DescendingOrder
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
SortFilterProxyModel {
|
|
||||||
id: roomListProxyModel
|
|
||||||
|
|
||||||
sourceModel: sortedRoomListModel
|
|
||||||
|
|
||||||
filters: [
|
|
||||||
RegExpFilter {
|
|
||||||
roleName: "name"
|
|
||||||
pattern: searchField.text
|
|
||||||
caseSensitivity: Qt.CaseInsensitive
|
|
||||||
},
|
|
||||||
ExpressionFilter {
|
|
||||||
enabled: filter === 1
|
|
||||||
expression: unreadCount > 0
|
|
||||||
},
|
|
||||||
ExpressionFilter {
|
|
||||||
enabled: filter === 2
|
|
||||||
expression: category === 1 || category === 2 || category === 4
|
|
||||||
},
|
|
||||||
ExpressionFilter {
|
|
||||||
enabled: filter === 3
|
|
||||||
expression: category === 3 || category === 5
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
ListView {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
|
|
||||||
id: listView
|
|
||||||
|
|
||||||
spacing: 1
|
|
||||||
clip: true
|
|
||||||
|
|
||||||
model: roomListProxyModel
|
|
||||||
|
|
||||||
currentIndex: -1
|
|
||||||
|
|
||||||
highlightFollowsCurrentItem: true
|
|
||||||
|
|
||||||
highlightMoveDuration: 200
|
|
||||||
highlightResizeDuration: 0
|
|
||||||
|
|
||||||
boundsBehavior: Flickable.DragOverBounds
|
|
||||||
|
|
||||||
ScrollBar.vertical: ScrollBar {}
|
|
||||||
|
|
||||||
delegate: Rectangle {
|
|
||||||
readonly property bool highlighted: currentRoom === enteredRoom
|
|
||||||
|
|
||||||
width: parent.width
|
|
||||||
height: 64
|
|
||||||
|
|
||||||
color: MSettings.darkTheme ? "#303030" : "#fafafa"
|
|
||||||
|
|
||||||
AutoMouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
hoverEnabled: MSettings.miniMode
|
|
||||||
|
|
||||||
onSecondaryClicked: {
|
|
||||||
roomContextMenu.model = model
|
|
||||||
roomContextMenu.popup()
|
|
||||||
}
|
|
||||||
onPrimaryClicked: {
|
|
||||||
if (category === RoomType.Invited) {
|
|
||||||
inviteDialog.currentRoom = currentRoom
|
|
||||||
inviteDialog.open()
|
|
||||||
} else {
|
|
||||||
enteredRoom = currentRoom
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ToolTip.visible: MSettings.miniMode && containsMouse
|
|
||||||
ToolTip.text: name
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
visible: highlightCount > 0 || highlighted
|
|
||||||
color: Material.accent
|
|
||||||
opacity: 0.1
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: unreadCount > 0 || highlighted ? 4 : 0
|
|
||||||
height: parent.height
|
|
||||||
|
|
||||||
color: Material.accent
|
|
||||||
|
|
||||||
Behavior on width {
|
|
||||||
PropertyAnimation { easing.type: Easing.InOutCubic; duration: 200 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: 12
|
|
||||||
|
|
||||||
spacing: 12
|
|
||||||
|
|
||||||
ImageItem {
|
|
||||||
id: imageItem
|
|
||||||
|
|
||||||
Layout.preferredWidth: height
|
|
||||||
Layout.fillHeight: true
|
|
||||||
|
|
||||||
hint: name || "No Name"
|
|
||||||
|
|
||||||
image: avatar
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
|
|
||||||
visible: parent.width > 64
|
|
||||||
|
|
||||||
Label {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
|
|
||||||
text: name || "No Name"
|
|
||||||
font.pointSize: 12
|
|
||||||
elide: Text.ElideRight
|
|
||||||
wrapMode: Text.NoWrap
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
|
|
||||||
text: (lastEvent == "" ? topic : lastEvent).replace(/(\r\n\t|\n|\r\t)/gm,"")
|
|
||||||
elide: Text.ElideRight
|
|
||||||
wrapMode: Text.NoWrap
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
section.property: "display"
|
|
||||||
section.criteria: ViewSection.FullString
|
|
||||||
section.delegate: Label {
|
|
||||||
width: parent.width
|
|
||||||
height: 24
|
|
||||||
|
|
||||||
text: section
|
|
||||||
color: "grey"
|
|
||||||
leftPadding: MSettings.miniMode ? undefined : 16
|
|
||||||
elide: Text.ElideRight
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
horizontalAlignment: MSettings.miniMode ? Text.AlignHCenter : undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
RoomContextMenu { id: roomContextMenu }
|
|
||||||
|
|
||||||
Dialog {
|
|
||||||
property var currentRoom
|
|
||||||
|
|
||||||
id: inviteDialog
|
|
||||||
parent: ApplicationWindow.overlay
|
|
||||||
|
|
||||||
x: (window.width - width) / 2
|
|
||||||
y: (window.height - height) / 2
|
|
||||||
width: 360
|
|
||||||
|
|
||||||
title: "Action Required"
|
|
||||||
modal: true
|
|
||||||
standardButtons: Dialog.Ok | Dialog.Cancel
|
|
||||||
|
|
||||||
contentItem: Label { text: "Accept this invitation?" }
|
|
||||||
|
|
||||||
onAccepted: currentRoom.acceptInvitation()
|
|
||||||
onRejected: currentRoom.forget()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
module Spectral.Form
|
|
||||||
RoomForm 2.0 RoomForm.qml
|
|
||||||
RoomListForm 2.0 RoomListForm.qml
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
module Spectral.Menu
|
module Spectral.Menu
|
||||||
MessageContextMenu 2.0 MessageContextMenu.qml
|
MessageContextMenu 2.0 MessageContextMenu.qml
|
||||||
RoomContextMenu 2.0 RoomContextMenu.qml
|
RoomContextMenu 2.0 RoomContextMenu.qml
|
||||||
|
|
||||||
|
|
|
@ -1,145 +1,7 @@
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Layouts 1.3
|
|
||||||
import QtGraphicalEffects 1.0
|
|
||||||
import QtQuick.Controls 2.2
|
|
||||||
import QtQuick.Controls.Material 2.2
|
|
||||||
|
|
||||||
import Spectral.Component 2.0
|
LoginForm {
|
||||||
|
loginButton.onClicked: {
|
||||||
import Spectral.Setting 0.1
|
|
||||||
|
|
||||||
Page {
|
|
||||||
property var controller
|
|
||||||
|
|
||||||
Row {
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
Pane {
|
|
||||||
width: parent.width / 2
|
|
||||||
height: parent.height
|
|
||||||
|
|
||||||
background: Item {
|
|
||||||
Image {
|
|
||||||
id: background
|
|
||||||
anchors.fill: parent
|
|
||||||
source: "qrc:/assets/img/background.jpg"
|
|
||||||
fillMode: Image.PreserveAspectCrop
|
|
||||||
cache: false
|
|
||||||
}
|
|
||||||
|
|
||||||
ColorOverlay {
|
|
||||||
anchors.fill: background
|
|
||||||
source: background
|
|
||||||
color: Material.accent
|
|
||||||
opacity: 0.7
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
|
||||||
x: 32
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
|
||||||
Label {
|
|
||||||
text: "MATRIX LOGIN."
|
|
||||||
font.pointSize: 28
|
|
||||||
font.bold: true
|
|
||||||
color: "white"
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
text: "A NEW METHOD OF MESSAGING"
|
|
||||||
font.pointSize: 12
|
|
||||||
color: "white"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Pane {
|
|
||||||
width: parent.width / 2
|
|
||||||
height: parent.height
|
|
||||||
|
|
||||||
padding: 64
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
width: parent.width
|
|
||||||
|
|
||||||
id: mainCol
|
|
||||||
|
|
||||||
TextField {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
id: serverField
|
|
||||||
|
|
||||||
leftPadding: 16
|
|
||||||
topPadding: 0
|
|
||||||
bottomPadding: 0
|
|
||||||
|
|
||||||
text: "https://matrix.org"
|
|
||||||
placeholderText: "Server"
|
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
implicitHeight: 48
|
|
||||||
|
|
||||||
color: MSettings.darkTheme ? "#242424" : "#eaeaea"
|
|
||||||
border.color: parent.activeFocus ? Material.accent : "transparent"
|
|
||||||
border.width: 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TextField {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
id: usernameField
|
|
||||||
|
|
||||||
leftPadding: 16
|
|
||||||
topPadding: 0
|
|
||||||
bottomPadding: 0
|
|
||||||
|
|
||||||
placeholderText: "Username"
|
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
implicitHeight: 48
|
|
||||||
|
|
||||||
color: MSettings.darkTheme ? "#242424" : "#eaeaea"
|
|
||||||
border.color: parent.activeFocus ? Material.accent : "transparent"
|
|
||||||
border.width: 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TextField {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
id: passwordField
|
|
||||||
|
|
||||||
leftPadding: 16
|
|
||||||
topPadding: 0
|
|
||||||
bottomPadding: 0
|
|
||||||
|
|
||||||
placeholderText: "Password"
|
|
||||||
echoMode: TextInput.Password
|
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
implicitHeight: 48
|
|
||||||
|
|
||||||
color: MSettings.darkTheme ? "#242424" : "#eaeaea"
|
|
||||||
border.color: parent.activeFocus ? Material.accent : "transparent"
|
|
||||||
border.width: 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
id: loginButton
|
|
||||||
|
|
||||||
text: "LOGIN"
|
|
||||||
highlighted: true
|
|
||||||
|
|
||||||
ToolTip {
|
|
||||||
id: loginButtonTooltip
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
if (!(serverField.text.startsWith("http") && serverField.text.includes("://"))) {
|
if (!(serverField.text.startsWith("http") && serverField.text.includes("://"))) {
|
||||||
loginButtonTooltip.text = "Server address should start with http(s)://"
|
loginButtonTooltip.text = "Server address should start with http(s)://"
|
||||||
loginButtonTooltip.open()
|
loginButtonTooltip.open()
|
||||||
|
@ -155,8 +17,4 @@ Page {
|
||||||
|
|
||||||
controller.connectionAdded.connect(function() { stackView.pop() })
|
controller.connectionAdded.connect(function() { stackView.pop() })
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,147 @@
|
||||||
|
import QtQuick 2.9
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
import QtQuick.Controls 2.2
|
||||||
|
import QtQuick.Controls.Material 2.2
|
||||||
|
|
||||||
|
import Spectral.Component 2.0
|
||||||
|
|
||||||
|
import Spectral.Setting 0.1
|
||||||
|
|
||||||
|
Page {
|
||||||
|
property var controller
|
||||||
|
|
||||||
|
property alias loginButton: loginButton
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Pane {
|
||||||
|
width: parent.width / 2
|
||||||
|
height: parent.height
|
||||||
|
|
||||||
|
background: Item {
|
||||||
|
Image {
|
||||||
|
id: background
|
||||||
|
anchors.fill: parent
|
||||||
|
source: "qrc:/assets/img/background.jpg"
|
||||||
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
cache: false
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorOverlay {
|
||||||
|
anchors.fill: background
|
||||||
|
source: background
|
||||||
|
color: Material.accent
|
||||||
|
opacity: 0.7
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
x: 32
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: "MATRIX LOGIN."
|
||||||
|
font.pointSize: 28
|
||||||
|
font.bold: true
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: "A NEW METHOD OF MESSAGING"
|
||||||
|
font.pointSize: 12
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Pane {
|
||||||
|
width: parent.width / 2
|
||||||
|
height: parent.height
|
||||||
|
|
||||||
|
padding: 64
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
id: mainCol
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
id: serverField
|
||||||
|
|
||||||
|
leftPadding: 16
|
||||||
|
topPadding: 0
|
||||||
|
bottomPadding: 0
|
||||||
|
|
||||||
|
text: "https://matrix.org"
|
||||||
|
placeholderText: "Server"
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
implicitHeight: 48
|
||||||
|
|
||||||
|
color: MSettings.darkTheme ? "#242424" : "#eaeaea"
|
||||||
|
border.color: parent.activeFocus ? Material.accent : "transparent"
|
||||||
|
border.width: 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
id: usernameField
|
||||||
|
|
||||||
|
leftPadding: 16
|
||||||
|
topPadding: 0
|
||||||
|
bottomPadding: 0
|
||||||
|
|
||||||
|
placeholderText: "Username"
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
implicitHeight: 48
|
||||||
|
|
||||||
|
color: MSettings.darkTheme ? "#242424" : "#eaeaea"
|
||||||
|
border.color: parent.activeFocus ? Material.accent : "transparent"
|
||||||
|
border.width: 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
id: passwordField
|
||||||
|
|
||||||
|
leftPadding: 16
|
||||||
|
topPadding: 0
|
||||||
|
bottomPadding: 0
|
||||||
|
|
||||||
|
placeholderText: "Password"
|
||||||
|
echoMode: TextInput.Password
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
implicitHeight: 48
|
||||||
|
|
||||||
|
color: MSettings.darkTheme ? "#242424" : "#eaeaea"
|
||||||
|
border.color: parent.activeFocus ? Material.accent : "transparent"
|
||||||
|
border.width: 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
id: loginButton
|
||||||
|
|
||||||
|
text: "LOGIN"
|
||||||
|
highlighted: true
|
||||||
|
|
||||||
|
ToolTip {
|
||||||
|
id: loginButtonTooltip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,55 +1,5 @@
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.2
|
|
||||||
import QtQuick.Layouts 1.3
|
|
||||||
import QtQuick.Controls.Material 2.2
|
|
||||||
|
|
||||||
import Spectral.Form 2.0
|
RoomForm {
|
||||||
import Spectral.Component 2.0
|
roomListModel.onNewMessage: if (!window.active) spectralController.showMessage(roomName, content, icon)
|
||||||
import Spectral.Effect 2.0
|
|
||||||
|
|
||||||
import Spectral 0.1
|
|
||||||
import Spectral.Setting 0.1
|
|
||||||
|
|
||||||
Page {
|
|
||||||
property alias connection: roomListModel.connection
|
|
||||||
property alias filter: roomListForm.filter
|
|
||||||
|
|
||||||
id: page
|
|
||||||
|
|
||||||
RoomListModel {
|
|
||||||
id: roomListModel
|
|
||||||
|
|
||||||
onNewMessage: if (!window.active) spectralController.showMessage(roomName, content, icon)
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
RoomListForm {
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.preferredWidth: MSettings.miniMode ? 64 : page.width * 0.35
|
|
||||||
Layout.minimumWidth: 64
|
|
||||||
Layout.maximumWidth: 360
|
|
||||||
|
|
||||||
id: roomListForm
|
|
||||||
|
|
||||||
listModel: roomListModel
|
|
||||||
|
|
||||||
layer.enabled: true
|
|
||||||
layer.effect: ElevationEffect {
|
|
||||||
elevation: 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RoomForm {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
|
|
||||||
id: roomForm
|
|
||||||
|
|
||||||
currentRoom: roomListForm.enteredRoom
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
import QtQuick 2.9
|
||||||
|
import QtQuick.Controls 2.2
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
import QtQuick.Controls.Material 2.2
|
||||||
|
|
||||||
|
import Spectral.Panel 2.0
|
||||||
|
import Spectral.Component 2.0
|
||||||
|
import Spectral.Effect 2.0
|
||||||
|
|
||||||
|
import Spectral 0.1
|
||||||
|
import Spectral.Setting 0.1
|
||||||
|
|
||||||
|
Page {
|
||||||
|
property alias connection: roomListModel.connection
|
||||||
|
property alias filter: roomListForm.filter
|
||||||
|
|
||||||
|
property alias roomListModel: roomListModel
|
||||||
|
|
||||||
|
id: page
|
||||||
|
|
||||||
|
RoomListModel {
|
||||||
|
id: roomListModel
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
RoomListPanel {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.preferredWidth: MSettings.miniMode ? 64 : page.width * 0.35
|
||||||
|
Layout.minimumWidth: 64
|
||||||
|
Layout.maximumWidth: 360
|
||||||
|
|
||||||
|
id: roomListForm
|
||||||
|
|
||||||
|
listModel: roomListModel
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: ElevationEffect {
|
||||||
|
elevation: 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RoomPanel {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
id: roomForm
|
||||||
|
|
||||||
|
currentRoom: roomListForm.enteredRoom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*##^## Designer {
|
||||||
|
D{i:0;autoSize:false;height:480;width:640}
|
||||||
|
}
|
||||||
|
##^##*/
|
|
@ -1,297 +1,5 @@
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.2
|
|
||||||
import QtQuick.Controls.Material 2.2
|
|
||||||
import QtQuick.Layouts 1.3
|
|
||||||
|
|
||||||
import Spectral.Component 2.0
|
SettingForm {
|
||||||
import Spectral.Effect 2.0
|
addAccountButton.onClicked: stackView.push(loginPage)
|
||||||
|
|
||||||
import Spectral 0.1
|
|
||||||
import Spectral.Setting 0.1
|
|
||||||
|
|
||||||
import "qrc:/js/util.js" as Util
|
|
||||||
|
|
||||||
Page {
|
|
||||||
property alias listModel: accountSettingsListView.model
|
|
||||||
|
|
||||||
Page {
|
|
||||||
id: accountForm
|
|
||||||
|
|
||||||
parent: null
|
|
||||||
|
|
||||||
padding: 64
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
ListView {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
|
|
||||||
id: accountSettingsListView
|
|
||||||
|
|
||||||
boundsBehavior: Flickable.DragOverBounds
|
|
||||||
|
|
||||||
clip: true
|
|
||||||
|
|
||||||
delegate: Column {
|
|
||||||
property bool expanded: false
|
|
||||||
|
|
||||||
spacing: 8
|
|
||||||
|
|
||||||
ItemDelegate {
|
|
||||||
width: accountSettingsListView.width
|
|
||||||
height: 64
|
|
||||||
|
|
||||||
Row {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: 8
|
|
||||||
|
|
||||||
spacing: 8
|
|
||||||
|
|
||||||
ImageItem {
|
|
||||||
width: parent.height
|
|
||||||
height: parent.height
|
|
||||||
|
|
||||||
hint: user.displayName
|
|
||||||
image: user.avatar
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
Label {
|
|
||||||
text: user.displayName
|
|
||||||
}
|
|
||||||
Label {
|
|
||||||
text: user.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked: expanded = !expanded
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
width: parent.width - 32
|
|
||||||
height: expanded ? implicitHeight : 0
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
|
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
clip: true
|
|
||||||
|
|
||||||
ListView {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredHeight: 24
|
|
||||||
|
|
||||||
orientation: ListView.Horizontal
|
|
||||||
|
|
||||||
spacing: 8
|
|
||||||
|
|
||||||
model: ["#498882", "#42a5f5", "#5c6bc0", "#7e57c2", "#ab47bc", "#ff7043"]
|
|
||||||
|
|
||||||
delegate: Rectangle {
|
|
||||||
width: parent.height
|
|
||||||
height: parent.height
|
|
||||||
radius: width / 2
|
|
||||||
|
|
||||||
color: modelData
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
onClicked: spectralController.setColor(connection.localUserId, modelData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
Label { text: "Homeserver:" }
|
|
||||||
TextField {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
text: connection.homeserver
|
|
||||||
selectByMouse: true
|
|
||||||
readOnly: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
spacing: 16
|
|
||||||
|
|
||||||
Label { text: "Device ID:" }
|
|
||||||
TextField {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
text: connection.deviceId
|
|
||||||
selectByMouse: true
|
|
||||||
readOnly: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
spacing: 16
|
|
||||||
|
|
||||||
Label { text: "Access Token:" }
|
|
||||||
TextField {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
text: connection.accessToken
|
|
||||||
selectByMouse: true
|
|
||||||
readOnly: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
highlighted: true
|
|
||||||
text: "Logout"
|
|
||||||
|
|
||||||
onClicked: spectralController.logout(connection)
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on height {
|
|
||||||
PropertyAnimation { easing.type: Easing.InOutCubic; duration: 200 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
text: "Add Account"
|
|
||||||
flat: true
|
|
||||||
highlighted: true
|
|
||||||
|
|
||||||
onClicked: stackView.push(loginPage)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Page {
|
|
||||||
id: generalForm
|
|
||||||
|
|
||||||
parent: null
|
|
||||||
|
|
||||||
padding: 64
|
|
||||||
|
|
||||||
Column {
|
|
||||||
Switch {
|
|
||||||
text: "Use press and hold instead of right click"
|
|
||||||
checked: MSettings.pressAndHold
|
|
||||||
|
|
||||||
onCheckedChanged: MSettings.pressAndHold = checked
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Page {
|
|
||||||
id: appearanceForm
|
|
||||||
|
|
||||||
parent: null
|
|
||||||
|
|
||||||
padding: 64
|
|
||||||
|
|
||||||
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:/assets/img/icon.png"
|
|
||||||
}
|
|
||||||
Label { text: "Spectral, an IM client for the Matrix protocol." }
|
|
||||||
Label { text: "Released under GNU General Public License, version 3." }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: 240
|
|
||||||
height: parent.height
|
|
||||||
z: 10
|
|
||||||
|
|
||||||
id: settingDrawer
|
|
||||||
|
|
||||||
color: MSettings.darkTheme ? "#323232" : "#f3f3f3"
|
|
||||||
|
|
||||||
layer.enabled: true
|
|
||||||
layer.effect: ElevationEffect {
|
|
||||||
elevation: 4
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
ItemDelegate {
|
|
||||||
width: parent.width
|
|
||||||
|
|
||||||
text: "Account"
|
|
||||||
onClicked: pushToStack(accountForm)
|
|
||||||
}
|
|
||||||
|
|
||||||
ItemDelegate {
|
|
||||||
width: parent.width
|
|
||||||
|
|
||||||
text: "General"
|
|
||||||
onClicked: pushToStack(generalForm)
|
|
||||||
}
|
|
||||||
|
|
||||||
ItemDelegate {
|
|
||||||
width: parent.width
|
|
||||||
|
|
||||||
text: "Appearance"
|
|
||||||
onClicked: pushToStack(appearanceForm)
|
|
||||||
}
|
|
||||||
|
|
||||||
ItemDelegate {
|
|
||||||
width: parent.width
|
|
||||||
|
|
||||||
text: "About"
|
|
||||||
onClicked: pushToStack(aboutForm)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StackView {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.leftMargin: settingDrawer.width
|
|
||||||
|
|
||||||
id: settingStackView
|
|
||||||
}
|
|
||||||
|
|
||||||
function pushToStack(item) {
|
|
||||||
settingStackView.clear()
|
|
||||||
settingStackView.push(item)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
import QtQuick 2.9
|
||||||
|
import QtQuick.Controls 2.2
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
|
||||||
|
import Spectral.Component 2.0
|
||||||
|
|
||||||
|
import Spectral 0.1
|
||||||
|
import Spectral.Setting 0.1
|
||||||
|
|
||||||
|
Column {
|
||||||
|
property bool expanded: false
|
||||||
|
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
ItemDelegate {
|
||||||
|
width: accountSettingsListView.width
|
||||||
|
height: 64
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 8
|
||||||
|
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
ImageItem {
|
||||||
|
width: parent.height
|
||||||
|
height: parent.height
|
||||||
|
|
||||||
|
hint: user.displayName
|
||||||
|
image: user.avatar
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Label {
|
||||||
|
text: user.displayName
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
text: user.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onClicked: expanded = !expanded
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
width: parent.width - 32
|
||||||
|
height: expanded ? implicitHeight : 0
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 24
|
||||||
|
|
||||||
|
orientation: ListView.Horizontal
|
||||||
|
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
model: ["#498882", "#42a5f5", "#5c6bc0", "#7e57c2", "#ab47bc", "#ff7043"]
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
width: parent.height
|
||||||
|
height: parent.height
|
||||||
|
radius: width / 2
|
||||||
|
|
||||||
|
color: modelData
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
onClicked: spectralController.setColor(connection.localUserId, modelData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: "Homeserver:"
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
text: connection.homeserver
|
||||||
|
selectByMouse: true
|
||||||
|
readOnly: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
spacing: 16
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: "Device ID:"
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
text: connection.deviceId
|
||||||
|
selectByMouse: true
|
||||||
|
readOnly: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
spacing: 16
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: "Access Token:"
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
text: connection.accessToken
|
||||||
|
selectByMouse: true
|
||||||
|
readOnly: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
highlighted: true
|
||||||
|
text: "Logout"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
import QtQuick 2.9
|
||||||
|
import QtQuick.Controls 2.2
|
||||||
|
|
||||||
|
ItemDelegate {
|
||||||
|
text: category
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
settingStackView.clear()
|
||||||
|
settingStackView.push([accountForm, generalForm, appearanceForm, aboutForm][form])
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,178 @@
|
||||||
|
import QtQuick 2.9
|
||||||
|
import QtQuick.Controls 2.2
|
||||||
|
import QtQuick.Controls.Material 2.2
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
|
||||||
|
import Spectral.Component 2.0
|
||||||
|
import Spectral.Effect 2.0
|
||||||
|
|
||||||
|
import Spectral 0.1
|
||||||
|
import Spectral.Setting 0.1
|
||||||
|
|
||||||
|
Page {
|
||||||
|
property alias listModel: accountSettingsListView.model
|
||||||
|
|
||||||
|
property alias addAccountButton: addAccountButton
|
||||||
|
|
||||||
|
implicitWidth: 400
|
||||||
|
implicitHeight: 300
|
||||||
|
|
||||||
|
Page {
|
||||||
|
id: accountForm
|
||||||
|
|
||||||
|
parent: null
|
||||||
|
|
||||||
|
padding: 64
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
id: accountSettingsListView
|
||||||
|
|
||||||
|
boundsBehavior: Flickable.DragOverBounds
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
delegate: SettingAccountDelegate {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
id: addAccountButton
|
||||||
|
|
||||||
|
text: "Add Account"
|
||||||
|
flat: true
|
||||||
|
highlighted: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Page {
|
||||||
|
id: generalForm
|
||||||
|
|
||||||
|
parent: null
|
||||||
|
|
||||||
|
padding: 64
|
||||||
|
|
||||||
|
Column {
|
||||||
|
Switch {
|
||||||
|
text: "Use press and hold instead of right click"
|
||||||
|
checked: MSettings.pressAndHold
|
||||||
|
|
||||||
|
onCheckedChanged: MSettings.pressAndHold = checked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Page {
|
||||||
|
id: appearanceForm
|
||||||
|
|
||||||
|
parent: null
|
||||||
|
|
||||||
|
padding: 64
|
||||||
|
|
||||||
|
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:/assets/img/icon.png"
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
text: "Spectral, an IM client for the Matrix protocol."
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
text: "Released under GNU General Public License, version 3."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 240
|
||||||
|
height: parent.height
|
||||||
|
z: 10
|
||||||
|
|
||||||
|
id: settingDrawer
|
||||||
|
|
||||||
|
color: MSettings.darkTheme ? "#323232" : "#f3f3f3"
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: ElevationEffect {
|
||||||
|
elevation: 4
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: ListModel {
|
||||||
|
ListElement {
|
||||||
|
category: "Account"
|
||||||
|
form: 0
|
||||||
|
}
|
||||||
|
ListElement {
|
||||||
|
category: "General"
|
||||||
|
form: 1
|
||||||
|
}
|
||||||
|
ListElement {
|
||||||
|
category: "Appearance"
|
||||||
|
form: 2
|
||||||
|
}
|
||||||
|
ListElement {
|
||||||
|
category: "About"
|
||||||
|
form: 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: SettingCategoryDelegate {
|
||||||
|
width: parent.width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StackView {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: settingDrawer.width
|
||||||
|
|
||||||
|
id: settingStackView
|
||||||
|
|
||||||
|
initialItem: aboutForm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*##^## Designer {
|
||||||
|
D{i:0;autoSize:true;height:480;width:640}
|
||||||
|
}
|
||||||
|
##^##*/
|
|
@ -2,4 +2,3 @@ module Spectral.Page
|
||||||
Login 2.0 Login.qml
|
Login 2.0 Login.qml
|
||||||
Room 2.0 Room.qml
|
Room 2.0 Room.qml
|
||||||
Setting 2.0 Setting.qml
|
Setting 2.0 Setting.qml
|
||||||
|
|
||||||
|
|
|
@ -203,4 +203,3 @@ Drawer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
import QtQuick 2.9
|
||||||
|
import QtQuick.Controls 2.2
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
import QtQuick.Controls.Material 2.2
|
||||||
|
|
||||||
|
import Spectral 0.1
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
property alias image: headerImage.image
|
||||||
|
property alias topic: headerTopicLabel.text
|
||||||
|
signal clicked()
|
||||||
|
|
||||||
|
id: header
|
||||||
|
|
||||||
|
color: Material.accent
|
||||||
|
|
||||||
|
ItemDelegate {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
id: roomHeader
|
||||||
|
|
||||||
|
onClicked: header.clicked()
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 12
|
||||||
|
|
||||||
|
spacing: 12
|
||||||
|
|
||||||
|
ImageItem {
|
||||||
|
Layout.preferredWidth: height
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
id: headerImage
|
||||||
|
|
||||||
|
hint: currentRoom ? currentRoom.displayName : "No name"
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
visible: parent.width > 64
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
text: currentRoom ? currentRoom.displayName : ""
|
||||||
|
color: "white"
|
||||||
|
font.pointSize: 12
|
||||||
|
elide: Text.ElideRight
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
id: headerTopicLabel
|
||||||
|
|
||||||
|
color: "white"
|
||||||
|
elide: Text.ElideRight
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
import QtQuick 2.9
|
||||||
|
import QtQuick.Controls 2.2
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
import QtQuick.Controls.Material 2.2
|
||||||
|
|
||||||
|
import Spectral 0.1
|
||||||
|
import Spectral.Setting 0.1
|
||||||
|
|
||||||
|
import Spectral.Component 2.0
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
readonly property bool highlighted: currentRoom === enteredRoom
|
||||||
|
|
||||||
|
color: MSettings.darkTheme ? "#303030" : "#fafafa"
|
||||||
|
|
||||||
|
AutoMouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
hoverEnabled: MSettings.miniMode
|
||||||
|
|
||||||
|
onSecondaryClicked: {
|
||||||
|
roomContextMenu.model = model
|
||||||
|
roomContextMenu.popup()
|
||||||
|
}
|
||||||
|
onPrimaryClicked: {
|
||||||
|
if (category === RoomType.Invited) {
|
||||||
|
inviteDialog.currentRoom = currentRoom
|
||||||
|
inviteDialog.open()
|
||||||
|
} else {
|
||||||
|
enteredRoom = currentRoom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ToolTip.visible: MSettings.miniMode && containsMouse
|
||||||
|
ToolTip.text: name
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
visible: highlightCount > 0 || highlighted
|
||||||
|
color: Material.accent
|
||||||
|
opacity: 0.1
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: unreadCount > 0 || highlighted ? 4 : 0
|
||||||
|
height: parent.height
|
||||||
|
|
||||||
|
color: Material.accent
|
||||||
|
|
||||||
|
Behavior on width {
|
||||||
|
PropertyAnimation { easing.type: Easing.InOutCubic; duration: 200 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 12
|
||||||
|
|
||||||
|
spacing: 12
|
||||||
|
|
||||||
|
ImageItem {
|
||||||
|
id: imageItem
|
||||||
|
|
||||||
|
Layout.preferredWidth: height
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
hint: name || "No Name"
|
||||||
|
|
||||||
|
image: avatar
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
||||||
|
visible: parent.width > 64
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
text: name || "No Name"
|
||||||
|
font.pointSize: 12
|
||||||
|
elide: Text.ElideRight
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
text: (lastEvent == "" ? topic : lastEvent).replace(/(\r\n\t|\n|\r\t)/gm,"")
|
||||||
|
elide: Text.ElideRight
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
import QtQuick 2.9
|
||||||
|
import QtQuick.Controls 2.2
|
||||||
|
|
||||||
|
import SortFilterProxyModel 0.2
|
||||||
|
|
||||||
|
RoomListPanelForm {
|
||||||
|
sortedRoomListModel.proxyRoles: ExpressionRole {
|
||||||
|
name: "display"
|
||||||
|
expression: {
|
||||||
|
switch (category) {
|
||||||
|
case 1: return "Invited"
|
||||||
|
case 2: return "Favorites"
|
||||||
|
case 3: return "Rooms"
|
||||||
|
case 4: return "People"
|
||||||
|
case 5: return "Low Priority"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Shortcut {
|
||||||
|
sequence: StandardKey.Find
|
||||||
|
onActivated: searchField.forceActiveFocus()
|
||||||
|
}
|
||||||
|
|
||||||
|
Dialog {
|
||||||
|
property var currentRoom
|
||||||
|
|
||||||
|
id: inviteDialog
|
||||||
|
parent: ApplicationWindow.overlay
|
||||||
|
|
||||||
|
x: (window.width - width) / 2
|
||||||
|
y: (window.height - height) / 2
|
||||||
|
width: 360
|
||||||
|
|
||||||
|
title: "Action Required"
|
||||||
|
modal: true
|
||||||
|
standardButtons: Dialog.Ok | Dialog.Cancel
|
||||||
|
|
||||||
|
contentItem: Label { text: "Accept this invitation?" }
|
||||||
|
|
||||||
|
onAccepted: currentRoom.acceptInvitation()
|
||||||
|
onRejected: currentRoom.forget()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,141 @@
|
||||||
|
import QtQuick 2.9
|
||||||
|
import QtQuick.Controls 2.2
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
import QtQuick.Controls.Material 2.2
|
||||||
|
import QtQml.Models 2.3
|
||||||
|
|
||||||
|
import Spectral.Component 2.0
|
||||||
|
import Spectral.Menu 2.0
|
||||||
|
import Spectral.Effect 2.0
|
||||||
|
|
||||||
|
import Spectral 0.1
|
||||||
|
import Spectral.Setting 0.1
|
||||||
|
import SortFilterProxyModel 0.2
|
||||||
|
|
||||||
|
import "qrc:/js/util.js" as Util
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
property alias listModel: sortedRoomListModel.sourceModel
|
||||||
|
property int filter: 0
|
||||||
|
property var enteredRoom: null
|
||||||
|
|
||||||
|
property alias searchField: searchField
|
||||||
|
property alias sortedRoomListModel: sortedRoomListModel
|
||||||
|
|
||||||
|
color: MSettings.darkTheme ? "#323232" : "#f3f3f3"
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: MSettings.miniMode ? "Empty" : "Here? No, not here."
|
||||||
|
anchors.centerIn: parent
|
||||||
|
visible: listView.count === 0
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 40
|
||||||
|
Layout.margins: 12
|
||||||
|
|
||||||
|
id: searchField
|
||||||
|
|
||||||
|
leftPadding: MSettings.miniMode ? 4 : 32
|
||||||
|
topPadding: 0
|
||||||
|
bottomPadding: 0
|
||||||
|
placeholderText: "Search..."
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: MSettings.darkTheme ? "#303030" : "#fafafa"
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: ElevationEffect {
|
||||||
|
elevation: searchField.focus ? 2 : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SortFilterProxyModel {
|
||||||
|
id: sortedRoomListModel
|
||||||
|
|
||||||
|
sorters: [
|
||||||
|
RoleSorter { roleName: "category" },
|
||||||
|
RoleSorter {
|
||||||
|
roleName: "lastActiveTime"
|
||||||
|
sortOrder: Qt.DescendingOrder
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
SortFilterProxyModel {
|
||||||
|
id: roomListProxyModel
|
||||||
|
|
||||||
|
sourceModel: sortedRoomListModel
|
||||||
|
|
||||||
|
filters: [
|
||||||
|
RegExpFilter {
|
||||||
|
roleName: "name"
|
||||||
|
pattern: searchField.text
|
||||||
|
caseSensitivity: Qt.CaseInsensitive
|
||||||
|
},
|
||||||
|
ExpressionFilter {
|
||||||
|
enabled: filter === 1
|
||||||
|
expression: unreadCount > 0
|
||||||
|
},
|
||||||
|
ExpressionFilter {
|
||||||
|
enabled: filter === 2
|
||||||
|
expression: category === 1 || category === 2 || category === 4
|
||||||
|
},
|
||||||
|
ExpressionFilter {
|
||||||
|
enabled: filter === 3
|
||||||
|
expression: category === 3 || category === 5
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
id: listView
|
||||||
|
|
||||||
|
spacing: 1
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
model: roomListProxyModel
|
||||||
|
|
||||||
|
currentIndex: -1
|
||||||
|
|
||||||
|
highlightFollowsCurrentItem: true
|
||||||
|
|
||||||
|
highlightMoveDuration: 200
|
||||||
|
highlightResizeDuration: 0
|
||||||
|
|
||||||
|
boundsBehavior: Flickable.DragOverBounds
|
||||||
|
|
||||||
|
ScrollBar.vertical: ScrollBar {}
|
||||||
|
|
||||||
|
delegate: RoomListDelegate {
|
||||||
|
width: parent.width
|
||||||
|
height: 64
|
||||||
|
}
|
||||||
|
|
||||||
|
section.property: "display"
|
||||||
|
section.criteria: ViewSection.FullString
|
||||||
|
section.delegate: Label {
|
||||||
|
width: parent.width
|
||||||
|
height: 24
|
||||||
|
|
||||||
|
text: section
|
||||||
|
color: "grey"
|
||||||
|
leftPadding: MSettings.miniMode ? undefined : 16
|
||||||
|
elide: Text.ElideRight
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
horizontalAlignment: MSettings.miniMode ? Text.AlignHCenter : undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
RoomContextMenu { id: roomContextMenu }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
import QtQuick 2.9
|
||||||
|
|
||||||
|
RoomPanelForm {
|
||||||
|
roomHeader.onClicked: roomDrawer.open()
|
||||||
|
roomHeader.image: spectralController.safeImage(currentRoom ? currentRoom.avatar : null)
|
||||||
|
roomHeader.topic: currentRoom ? (currentRoom.topic).replace(/(\r\n\t|\n|\r\t)/gm,"") : ""
|
||||||
|
|
||||||
|
sortedMessageEventModel.onModelReset: {
|
||||||
|
if (currentRoom)
|
||||||
|
{
|
||||||
|
var lastScrollPosition = sortedMessageEventModel.mapFromSource(currentRoom.savedTopVisibleIndex())
|
||||||
|
if (lastScrollPosition === 0)
|
||||||
|
messageListView.positionViewAtBeginning()
|
||||||
|
else
|
||||||
|
{
|
||||||
|
console.log("Scrolling to position", lastScrollPosition)
|
||||||
|
messageListView.currentIndex = lastScrollPosition
|
||||||
|
}
|
||||||
|
if (messageListView.contentY < messageListView.originY + 10 || currentRoom.timelineSize === 0)
|
||||||
|
currentRoom.getPreviousContent(100)
|
||||||
|
}
|
||||||
|
console.log("Model timeline reset")
|
||||||
|
}
|
||||||
|
|
||||||
|
messageListView {
|
||||||
|
property int largestVisibleIndex: messageListView.count > 0 ? messageListView.indexAt(messageListView.contentX, messageListView.contentY + height - 1) : -1
|
||||||
|
|
||||||
|
onContentYChanged: {
|
||||||
|
if(messageListView.verticalVelocity < 0 && messageListView.contentY - 5000 < messageListView.originY)
|
||||||
|
currentRoom.getPreviousContent(50);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMovementEnded: {
|
||||||
|
currentRoom.saveViewport(sortedMessageEventModel.mapToSource(messageListView.indexAt(messageListView.contentX, messageListView.contentY)), sortedMessageEventModel.mapToSource(largestVisibleIndex))
|
||||||
|
var newReadMarker = sortedMessageEventModel.get(largestVisibleIndex).eventId
|
||||||
|
if (newReadMarker) currentRoom.readMarkerEventId = newReadMarker
|
||||||
|
}
|
||||||
|
|
||||||
|
displaced: Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
property: "y"; duration: 200
|
||||||
|
easing.type: Easing.OutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
goTopFab {
|
||||||
|
onClicked: messageListView.positionViewAtBeginning()
|
||||||
|
|
||||||
|
Behavior on opacity { NumberAnimation { duration: 200 } }
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadButton.onClicked: currentRoom.chooseAndUploadFile()
|
||||||
|
|
||||||
|
emojiButton.onClicked: emojiPicker.visible ? emojiPicker.close() : emojiPicker.open()
|
||||||
|
}
|
|
@ -0,0 +1,364 @@
|
||||||
|
import QtQuick 2.9
|
||||||
|
import QtQuick.Controls 2.2
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
import QtQuick.Controls.Material 2.2
|
||||||
|
|
||||||
|
import Spectral.Component 2.0
|
||||||
|
import Spectral.Component.Emoji 2.0
|
||||||
|
import Spectral.Component.Timeline 2.0
|
||||||
|
import Spectral.Menu 2.0
|
||||||
|
import Spectral.Effect 2.0
|
||||||
|
|
||||||
|
import Spectral 0.1
|
||||||
|
import Spectral.Setting 0.1
|
||||||
|
import SortFilterProxyModel 0.2
|
||||||
|
|
||||||
|
import "qrc:/js/md.js" as Markdown
|
||||||
|
import "qrc:/js/util.js" as Util
|
||||||
|
|
||||||
|
Item {
|
||||||
|
property var currentRoom: null
|
||||||
|
|
||||||
|
property alias roomHeader: roomHeader
|
||||||
|
property alias messageListView: messageListView
|
||||||
|
property alias goTopFab: goTopFab
|
||||||
|
property alias uploadButton: uploadButton
|
||||||
|
property alias emojiButton: emojiButton
|
||||||
|
property alias emojiPicker: emojiPicker
|
||||||
|
property alias sortedMessageEventModel: sortedMessageEventModel
|
||||||
|
property alias inputField: inputField
|
||||||
|
property alias roomDrawer: roomDrawer
|
||||||
|
|
||||||
|
id: root
|
||||||
|
|
||||||
|
MessageEventModel {
|
||||||
|
id: messageEventModel
|
||||||
|
room: currentRoom
|
||||||
|
}
|
||||||
|
|
||||||
|
RoomDrawer {
|
||||||
|
width: Math.min(root.width * 0.7, 480)
|
||||||
|
height: root.height
|
||||||
|
|
||||||
|
id: roomDrawer
|
||||||
|
|
||||||
|
room: currentRoom
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
visible: !currentRoom
|
||||||
|
text: "Please choose a room."
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
visible: currentRoom
|
||||||
|
|
||||||
|
RoomHeader {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 64
|
||||||
|
z: 10
|
||||||
|
|
||||||
|
id: roomHeader
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgressBar {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
z: 10
|
||||||
|
|
||||||
|
visible: currentRoom && currentRoom.busy
|
||||||
|
indeterminate: true
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
|
||||||
|
id: messageListView
|
||||||
|
|
||||||
|
displayMarginBeginning: 40
|
||||||
|
displayMarginEnd: 40
|
||||||
|
verticalLayoutDirection: ListView.BottomToTop
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
boundsBehavior: Flickable.DragOverBounds
|
||||||
|
|
||||||
|
model: SortFilterProxyModel {
|
||||||
|
id: sortedMessageEventModel
|
||||||
|
|
||||||
|
sourceModel: messageEventModel
|
||||||
|
|
||||||
|
filters: ExpressionFilter {
|
||||||
|
expression: marks !== 0x08 && marks !== 0x10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: ColumnLayout {
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
id: delegateColumn
|
||||||
|
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
||||||
|
visible: section !== aboveSection
|
||||||
|
|
||||||
|
text: section
|
||||||
|
color: "white"
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
leftPadding: 8
|
||||||
|
rightPadding: 8
|
||||||
|
topPadding: 4
|
||||||
|
bottomPadding: 4
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: MSettings.darkTheme ? "#484848" : "grey"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageDelegate {
|
||||||
|
visible: eventType === "notice" || eventType === "message"
|
||||||
|
|| eventType === "image" || eventType === "video"
|
||||||
|
|| eventType === "audio" || eventType === "file"
|
||||||
|
}
|
||||||
|
|
||||||
|
StateDelegate {
|
||||||
|
Layout.maximumWidth: messageListView.width * 0.8
|
||||||
|
|
||||||
|
visible: eventType === "emote" || eventType === "state"
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
||||||
|
visible: eventType === "other"
|
||||||
|
|
||||||
|
text: display
|
||||||
|
color: "grey"
|
||||||
|
font.italic: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
||||||
|
visible: readMarker === true && index !== 0
|
||||||
|
|
||||||
|
text: "And Now"
|
||||||
|
color: "white"
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
leftPadding: 8
|
||||||
|
rightPadding: 8
|
||||||
|
topPadding: 4
|
||||||
|
bottomPadding: 4
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: MSettings.darkTheme ? "#484848" : "grey"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RoundButton {
|
||||||
|
width: 64
|
||||||
|
height: 64
|
||||||
|
|
||||||
|
id: goTopFab
|
||||||
|
|
||||||
|
visible: !(parent.atYEnd || messageListView.moving)
|
||||||
|
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
|
||||||
|
contentItem: MaterialIcon {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
icon: "\ue313"
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
|
||||||
|
Material.background: Material.accent
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageContextMenu {
|
||||||
|
id: messageContextMenu
|
||||||
|
}
|
||||||
|
|
||||||
|
Popup {
|
||||||
|
property string sourceText
|
||||||
|
|
||||||
|
x: (window.width - width) / 2
|
||||||
|
y: (window.height - height) / 2
|
||||||
|
width: 480
|
||||||
|
|
||||||
|
id: sourceDialog
|
||||||
|
|
||||||
|
parent: ApplicationWindow.overlay
|
||||||
|
|
||||||
|
modal: true
|
||||||
|
|
||||||
|
padding: 16
|
||||||
|
|
||||||
|
closePolicy: Dialog.CloseOnEscape | Dialog.CloseOnPressOutside
|
||||||
|
|
||||||
|
contentItem: ScrollView {
|
||||||
|
TextArea {
|
||||||
|
readOnly: true
|
||||||
|
selectByMouse: true
|
||||||
|
|
||||||
|
text: sourceDialog.sourceText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Popup {
|
||||||
|
property alias listModel: readMarkerListView.model
|
||||||
|
|
||||||
|
x: (window.width - width) / 2
|
||||||
|
y: (window.height - height) / 2
|
||||||
|
width: 320
|
||||||
|
|
||||||
|
id: readMarkerDialog
|
||||||
|
|
||||||
|
parent: ApplicationWindow.overlay
|
||||||
|
|
||||||
|
modal: true
|
||||||
|
padding: 16
|
||||||
|
|
||||||
|
closePolicy: Dialog.CloseOnEscape | Dialog.CloseOnPressOutside
|
||||||
|
|
||||||
|
contentItem: ListView {
|
||||||
|
implicitHeight: Math.min(window.height - 64,
|
||||||
|
readMarkerListView.contentHeight)
|
||||||
|
|
||||||
|
id: readMarkerListView
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
boundsBehavior: Flickable.DragOverBounds
|
||||||
|
|
||||||
|
delegate: ItemDelegate {
|
||||||
|
width: parent.width
|
||||||
|
height: 48
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 8
|
||||||
|
spacing: 12
|
||||||
|
|
||||||
|
ImageItem {
|
||||||
|
Layout.preferredWidth: height
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
image: modelData.avatar
|
||||||
|
hint: modelData.displayName
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
text: modelData.displayName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollBar.vertical: ScrollBar {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 40
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 40
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
|
||||||
|
color: Material.background
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.verticalCenter: parent.top
|
||||||
|
width: parent.width
|
||||||
|
height: 48
|
||||||
|
|
||||||
|
color: MSettings.darkTheme ? "#303030" : "#fafafa"
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: ElevationEffect {
|
||||||
|
elevation: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
ItemDelegate {
|
||||||
|
Layout.preferredWidth: 48
|
||||||
|
Layout.preferredHeight: 48
|
||||||
|
|
||||||
|
id: uploadButton
|
||||||
|
|
||||||
|
contentItem: MaterialIcon {
|
||||||
|
icon: "\ue226"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollView {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 48
|
||||||
|
|
||||||
|
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
AutoTextArea {
|
||||||
|
id: inputField
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemDelegate {
|
||||||
|
Layout.preferredWidth: 48
|
||||||
|
Layout.preferredHeight: 48
|
||||||
|
|
||||||
|
id: emojiButton
|
||||||
|
|
||||||
|
contentItem: MaterialIcon {
|
||||||
|
icon: "\ue24e"
|
||||||
|
}
|
||||||
|
|
||||||
|
EmojiPicker {
|
||||||
|
x: window.width - 370
|
||||||
|
y: window.height - 400
|
||||||
|
|
||||||
|
width: 360
|
||||||
|
height: 320
|
||||||
|
|
||||||
|
id: emojiPicker
|
||||||
|
|
||||||
|
parent: ApplicationWindow.overlay
|
||||||
|
|
||||||
|
Material.elevation: 2
|
||||||
|
|
||||||
|
textArea: inputField
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*##^## Designer {
|
||||||
|
D{i:0;autoSize:true;height:480;width:640}
|
||||||
|
}
|
||||||
|
##^##*/
|
|
@ -0,0 +1,3 @@
|
||||||
|
module Spectral.Panel
|
||||||
|
RoomPanel 2.0 RoomPanel.qml
|
||||||
|
RoomListPanel 2.0 RoomListPanel.qml
|
24
res.qrc
24
res.qrc
|
@ -7,8 +7,6 @@
|
||||||
<file>imports/Spectral/Component/Emoji/EmojiButton.qml</file>
|
<file>imports/Spectral/Component/Emoji/EmojiButton.qml</file>
|
||||||
<file>imports/Spectral/Component/Emoji/EmojiPicker.qml</file>
|
<file>imports/Spectral/Component/Emoji/EmojiPicker.qml</file>
|
||||||
<file>imports/Spectral/Component/Emoji/qmldir</file>
|
<file>imports/Spectral/Component/Emoji/qmldir</file>
|
||||||
<file>imports/Spectral/Component/Timeline/AutoImage.qml</file>
|
|
||||||
<file>imports/Spectral/Component/Timeline/AutoLabel.qml</file>
|
|
||||||
<file>imports/Spectral/Component/Timeline/DownloadableContent.qml</file>
|
<file>imports/Spectral/Component/Timeline/DownloadableContent.qml</file>
|
||||||
<file>imports/Spectral/Component/Timeline/GenericBubble.qml</file>
|
<file>imports/Spectral/Component/Timeline/GenericBubble.qml</file>
|
||||||
<file>imports/Spectral/Component/Timeline/MessageDelegate.qml</file>
|
<file>imports/Spectral/Component/Timeline/MessageDelegate.qml</file>
|
||||||
|
@ -20,17 +18,12 @@
|
||||||
<file>imports/Spectral/Component/SideNavButton.qml</file>
|
<file>imports/Spectral/Component/SideNavButton.qml</file>
|
||||||
<file>imports/Spectral/Effect/ElevationEffect.qml</file>
|
<file>imports/Spectral/Effect/ElevationEffect.qml</file>
|
||||||
<file>imports/Spectral/Effect/qmldir</file>
|
<file>imports/Spectral/Effect/qmldir</file>
|
||||||
<file>imports/Spectral/Form/qmldir</file>
|
|
||||||
<file>imports/Spectral/Form/RoomDrawer.qml</file>
|
|
||||||
<file>imports/Spectral/Form/RoomForm.qml</file>
|
|
||||||
<file>imports/Spectral/Form/RoomListForm.qml</file>
|
|
||||||
<file>imports/Spectral/Menu/MessageContextMenu.qml</file>
|
<file>imports/Spectral/Menu/MessageContextMenu.qml</file>
|
||||||
<file>imports/Spectral/Menu/qmldir</file>
|
<file>imports/Spectral/Menu/qmldir</file>
|
||||||
<file>imports/Spectral/Menu/RoomContextMenu.qml</file>
|
<file>imports/Spectral/Menu/RoomContextMenu.qml</file>
|
||||||
<file>imports/Spectral/Page/Login.qml</file>
|
<file>imports/Spectral/Page/Login.qml</file>
|
||||||
<file>imports/Spectral/Page/qmldir</file>
|
<file>imports/Spectral/Page/qmldir</file>
|
||||||
<file>imports/Spectral/Page/Room.qml</file>
|
<file>imports/Spectral/Page/Room.qml</file>
|
||||||
<file>imports/Spectral/Page/Setting.qml</file>
|
|
||||||
<file>assets/font/material.ttf</file>
|
<file>assets/font/material.ttf</file>
|
||||||
<file>assets/img/avatar.png</file>
|
<file>assets/img/avatar.png</file>
|
||||||
<file>assets/img/background.jpg</file>
|
<file>assets/img/background.jpg</file>
|
||||||
|
@ -41,5 +34,22 @@
|
||||||
<file>imports/Spectral/Font/MaterialFont.qml</file>
|
<file>imports/Spectral/Font/MaterialFont.qml</file>
|
||||||
<file>imports/Spectral/Font/qmldir</file>
|
<file>imports/Spectral/Font/qmldir</file>
|
||||||
<file>imports/Spectral/Setting/qmldir</file>
|
<file>imports/Spectral/Setting/qmldir</file>
|
||||||
|
<file>imports/Spectral/Page/Setting.qml</file>
|
||||||
|
<file>imports/Spectral/Page/SettingForm.ui.qml</file>
|
||||||
|
<file>imports/Spectral/Page/SettingCategoryDelegate.qml</file>
|
||||||
|
<file>imports/Spectral/Page/SettingAccountDelegate.qml</file>
|
||||||
|
<file>imports/Spectral/Page/RoomForm.ui.qml</file>
|
||||||
|
<file>imports/Spectral/Page/LoginForm.ui.qml</file>
|
||||||
|
<file>imports/Spectral/Panel/qmldir</file>
|
||||||
|
<file>imports/Spectral/Panel/RoomDrawer.qml</file>
|
||||||
|
<file>imports/Spectral/Panel/RoomListPanel.qml</file>
|
||||||
|
<file>imports/Spectral/Panel/RoomListPanelForm.ui.qml</file>
|
||||||
|
<file>imports/Spectral/Panel/RoomPanel.qml</file>
|
||||||
|
<file>imports/Spectral/Panel/RoomPanelForm.ui.qml</file>
|
||||||
|
<file>imports/Spectral/Panel/RoomHeader.qml</file>
|
||||||
|
<file>imports/Spectral/Panel/RoomListDelegate.qml</file>
|
||||||
|
<file>imports/Spectral/Component/AutoImage.qml</file>
|
||||||
|
<file>imports/Spectral/Component/AutoLabel.qml</file>
|
||||||
|
<file>imports/Spectral/Component/AutoTextArea.qml</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QMimeDatabase>
|
#include <QMimeDatabase>
|
||||||
|
#include <QMetaObject>
|
||||||
|
|
||||||
SpectralRoom::SpectralRoom(Connection* connection, QString roomId,
|
SpectralRoom::SpectralRoom(Connection* connection, QString roomId,
|
||||||
JoinState joinState)
|
JoinState joinState)
|
||||||
|
@ -180,5 +181,8 @@ void SpectralRoom::saveViewport(int topIndex, int bottomIndex) {
|
||||||
|
|
||||||
void SpectralRoom::getPreviousContent(int limit) {
|
void SpectralRoom::getPreviousContent(int limit) {
|
||||||
setBusy(true);
|
setBusy(true);
|
||||||
Room::getPreviousContent(limit);
|
QMetaObject::invokeMethod(
|
||||||
|
this,
|
||||||
|
[=] { Room::getPreviousContent(limit); },
|
||||||
|
Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue