Somewhat better hierarchy.

Init separate .qml files.
This commit is contained in:
Black Hat
2018-10-02 10:07:47 +08:00
parent 91fc03aba5
commit ae538a7c45
28 changed files with 1468 additions and 1321 deletions

View File

@@ -0,0 +1,205 @@
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 0.1
import "qrc:/js/util.js" as Util
Drawer {
property var room
id: roomDrawer
edge: Qt.RightEdge
interactive: false
ToolButton {
contentItem: MaterialIcon { icon: "\ue5c4" }
onClicked: roomDrawer.close()
}
ColumnLayout {
anchors.fill: parent
anchors.margins: 32
ImageItem {
Layout.preferredWidth: 64
Layout.preferredHeight: 64
Layout.alignment: Qt.AlignHCenter
hint: room ? room.displayName : "No name"
image: spectralController.safeImage(room ? room.avatar : null)
}
Label {
Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter
text: room && room.id ? room.id : ""
}
Label {
Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter
text: room && room.canonicalAlias ? room.canonicalAlias : "No Canonical Alias"
}
Label {
Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter
text: room ? room.memberCount + " Members" : "No Member Count"
}
RowLayout {
Layout.fillWidth: true
TextField {
Layout.fillWidth: true
id: roomNameField
text: room && room.name ? room.name : ""
selectByMouse: true
}
ItemDelegate {
Layout.preferredWidth: height
Layout.preferredHeight: parent.height
contentItem: MaterialIcon { icon: "\ue5ca" }
onClicked: room.setName(roomNameField.text)
}
}
RowLayout {
Layout.fillWidth: true
TextField {
Layout.fillWidth: true
id: roomTopicField
text: room && room.topic ? room.topic : ""
selectByMouse: true
}
ItemDelegate {
Layout.preferredWidth: height
Layout.preferredHeight: parent.height
contentItem: MaterialIcon { icon: "\ue5ca" }
onClicked: room.setTopic(roomTopicField.text)
}
}
ListView {
Layout.fillWidth: true
Layout.fillHeight: true
id: userListView
clip: true
boundsBehavior: Flickable.DragOverBounds
model: UserListModel {
room: roomDrawer.room
}
delegate: Column {
property bool expanded: false
ItemDelegate {
width: userListView.width
height: 48
RowLayout {
anchors.fill: parent
anchors.margins: 8
spacing: 12
ImageItem {
Layout.preferredWidth: height
Layout.fillHeight: true
image: avatar
hint: name
}
Label {
Layout.fillWidth: true
text: name
}
}
onClicked: expanded = !expanded
}
ColumnLayout {
width: parent.width - 32
height: expanded ? implicitHeight : 0
anchors.horizontalCenter: parent.horizontalCenter
spacing: 0
clip: true
Button {
Layout.fillWidth: true
text: "Kick"
highlighted: true
onClicked: room.kickMember(userId)
}
Behavior on height {
PropertyAnimation { easing.type: Easing.InOutCubic; duration: 200 }
}
}
}
ScrollBar.vertical: ScrollBar {}
}
Button {
Layout.fillWidth: true
text: "Invite User"
flat: true
highlighted: true
onClicked: inviteUserDialog.open()
Dialog {
x: (window.width - width) / 2
y: (window.height - height) / 2
width: 360
id: inviteUserDialog
parent: ApplicationWindow.overlay
title: "Input User ID"
modal: true
standardButtons: Dialog.Ok | Dialog.Cancel
contentItem: TextField {
id: inviteUserDialogTextField
placeholderText: "@bot:matrix.org"
}
onAccepted: room.inviteToRoom(inviteUserDialogTextField.text)
}
}
}
}

View File

@@ -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
}
}
}
}
}

View File

@@ -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
}
}
}
}

View File

@@ -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()
}
}

View File

@@ -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 }
}
}
}

View File

@@ -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()
}

View File

@@ -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}
}
##^##*/

View File

@@ -0,0 +1,3 @@
module Spectral.Panel
RoomPanel 2.0 RoomPanel.qml
RoomListPanel 2.0 RoomListPanel.qml