From 01196e8b50fffbf23799b61672eff2b97a6f1120 Mon Sep 17 00:00:00 2001 From: Black Hat Date: Fri, 28 Sep 2018 19:47:17 +0800 Subject: [PATCH] Init infinite scroll. It constanatly crashes, TBD. --- qml/form/RoomForm.qml | 355 ++++++++++++++++++++++-------------------- src/spectralroom.cpp | 28 ++++ src/spectralroom.h | 3 + 3 files changed, 215 insertions(+), 171 deletions(-) diff --git a/qml/form/RoomForm.qml b/qml/form/RoomForm.qml index 1c2be59..2a9a319 100644 --- a/qml/form/RoomForm.qml +++ b/qml/form/RoomForm.qml @@ -19,6 +19,23 @@ Item { MessageEventModel { id: messageEventModel room: currentRoom + + onModelReset: { + if (currentRoom) + { + var lastScrollPosition = currentRoom.savedTopVisibleIndex() + if (lastScrollPosition === 0) + messageListView.positionViewAtBeginning() + else + { + console.log("Scrolling to position", lastScrollPosition) + messageListView.positionViewAtIndex(lastScrollPosition, ListView.Contain) + } + if (messageListView.contentY < messageListView.originY + 10) + currentRoom.getPreviousContent(100) + } + console.log("Model timeline reset") + } } RoomDrawer { @@ -46,6 +63,8 @@ Item { Layout.fillWidth: true Layout.preferredHeight: 64 + z: 10 + color: Material.accent ItemDelegate { @@ -98,221 +117,215 @@ Item { } } - RowLayout { + ListView { Layout.fillWidth: true Layout.fillHeight: true - Layout.leftMargin: 16 + Layout.margins: 16 - z: -10 + id: messageListView - spacing: 0 + displayMarginBeginning: 40 + displayMarginEnd: 40 + verticalLayoutDirection: ListView.BottomToTop + spacing: 8 - ListView { - Layout.fillWidth: true - Layout.fillHeight: true + flickDeceleration: 4096 - id: messageListView + boundsBehavior: Flickable.DragOverBounds + + property int largestVisibleIndex: count > 0 ? indexAt(contentX, contentY + height - 1) : -1 + + onContentYChanged: { + // Check whether we're about to bump into the ceiling in 2 seconds + var curVelocity = verticalVelocity // Snapshot the current speed + if( curVelocity < 0 && contentY + curVelocity*2 < originY) + { + // Request the amount of messages enough to scroll at this + // rate for 3 more seconds + var avgHeight = contentHeight / count + currentRoom.getPreviousContent(-curVelocity*3 / avgHeight); + } + } + + onMovementEnded: currentRoom.saveViewport(indexAt(contentX, contentY), largestVisibleIndex) + + model: SortFilterProxyModel { + id: sortedRoomListModel + + sourceModel: messageEventModel + + filters: ExpressionFilter { + expression: marks !== 0x08 && marks !== 0x10 + } + } + + delegate: ColumnLayout { + width: parent.width + + id: delegateColumn - displayMarginBeginning: 40 - displayMarginEnd: 40 - verticalLayoutDirection: ListView.BottomToTop spacing: 8 - boundsBehavior: Flickable.DragOverBounds - // flickDeceleration: 4096 + Label { + Layout.alignment: Qt.AlignHCenter - // cacheBuffer: 200 + visible: section !== aboveSection - model: SortFilterProxyModel { - id: sortedRoomListModel + text: section + color: "white" + verticalAlignment: Text.AlignVCenter + leftPadding: 8 + rightPadding: 8 + topPadding: 4 + bottomPadding: 4 - sourceModel: messageEventModel - - filters: ExpressionFilter { - expression: marks !== 0x08 && marks !== 0x10 + background: Rectangle { + color: MSettings.darkTheme ? "#484848" : "grey" } } - 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" } - } + MessageDelegate { + visible: eventType === "notice" || eventType === "message" || eventType === "image" || eventType === "video" || eventType === "audio" || eventType === "file" } - ScrollBar.vertical: messageListViewScrollBar + StateDelegate { + Layout.maximumWidth: messageListView.width * 0.8 - onAtYBeginningChanged: atYBeginning && currentRoom ? currentRoom.getPreviousContent(20) : {} - onAtYEndChanged: atYEnd && currentRoom ? currentRoom.markAllMessagesAsRead() : {} - - 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 } } + visible: eventType === "emote" || eventType === "state" } - MessageContextMenu { id: messageContextMenu } + Label { + Layout.alignment: Qt.AlignHCenter - Popup { - property string sourceText + visible: eventType === "other" - 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 - } - } + text: display + color: "grey" + font.italic: true } - Popup { - property alias listModel: readMarkerListView.model + Label { + Layout.alignment: Qt.AlignHCenter - x: (window.width - width) / 2 - y: (window.height - height) / 2 - width: 320 + visible: readMarker === true && index !== 0 - id: readMarkerDialog + text: "And Now" + color: "white" + verticalAlignment: Text.AlignVCenter + leftPadding: 8 + rightPadding: 8 + topPadding: 4 + bottomPadding: 4 - parent: ApplicationWindow.overlay + background: Rectangle { color: MSettings.darkTheme ? "#484848" : "grey" } + } + } - modal: true - padding: 16 + RoundButton { + width: 64 + height: 64 - closePolicy: Dialog.CloseOnEscape | Dialog.CloseOnPressOutside + id: goTopFab - contentItem: ListView { - implicitHeight: Math.min(window.height - 64, readMarkerListView.contentHeight) + visible: !(parent.atYEnd || messageListView.moving) - id: readMarkerListView + anchors.right: parent.right + anchors.bottom: parent.bottom - clip: true - boundsBehavior: Flickable.DragOverBounds + contentItem: MaterialIcon { + anchors.fill: parent - delegate: ItemDelegate { - width: parent.width - height: 48 + icon: "\ue313" + color: "white" + } - RowLayout { - anchors.fill: parent - anchors.margins: 8 - spacing: 12 + Material.background: Material.accent - ImageItem { - Layout.preferredWidth: height - Layout.fillHeight: true + onClicked: parent.positionViewAtBeginning() - image: modelData.avatar - hint: modelData.displayName - } + Behavior on opacity { NumberAnimation { duration: 200 } } + } - Label { - Layout.fillWidth: true + MessageContextMenu { id: messageContextMenu } - text: modelData.displayName - } - } - } + Popup { + property string sourceText - ScrollBar.vertical: ScrollBar {} + 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 } } } - ScrollBar { - Layout.preferredWidth: 16 - Layout.fillHeight: true + Popup { + property alias listModel: readMarkerListView.model - id: messageListViewScrollBar + 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 {} + } } } diff --git a/src/spectralroom.cpp b/src/spectralroom.cpp index eef0542..151b9ba 100644 --- a/src/spectralroom.cpp +++ b/src/spectralroom.cpp @@ -147,3 +147,31 @@ QDateTime SpectralRoom::lastActiveTime() { } float SpectralRoom::orderForTag(QString name) { return tag(name).order; } + +int SpectralRoom::savedTopVisibleIndex() const { + return firstDisplayedMarker() == timelineEdge() + ? 0 + : firstDisplayedMarker() - messageEvents().rbegin(); +} + +int SpectralRoom::savedBottomVisibleIndex() const { + return lastDisplayedMarker() == timelineEdge() + ? 0 + : lastDisplayedMarker() - messageEvents().rbegin(); +} + +void SpectralRoom::saveViewport(int topIndex, int bottomIndex) { + if (topIndex == -1 || bottomIndex == -1 || + (bottomIndex == savedBottomVisibleIndex() && + (bottomIndex == 0 || topIndex == savedTopVisibleIndex()))) + return; + if (bottomIndex == 0) { + qDebug() << "Saving viewport as the latest available"; + setFirstDisplayedEventId({}); + setLastDisplayedEventId({}); + return; + } + qDebug() << "Saving viewport:" << topIndex << "thru" << bottomIndex; + setFirstDisplayedEvent(maxTimelineIndex() - topIndex); + setLastDisplayedEvent(maxTimelineIndex() - bottomIndex); +} diff --git a/src/spectralroom.h b/src/spectralroom.h index 3dd5c34..52f812e 100644 --- a/src/spectralroom.h +++ b/src/spectralroom.h @@ -38,6 +38,9 @@ class SpectralRoom : public Room { QDateTime lastActiveTime(); Q_INVOKABLE float orderForTag(QString name); + Q_INVOKABLE int savedTopVisibleIndex() const; + Q_INVOKABLE int savedBottomVisibleIndex() const; + Q_INVOKABLE void saveViewport(int topIndex, int bottomIndex); private: QString m_cachedInput;