parent
6b10c2ef2d
commit
01196e8b50
|
@ -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 {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue