Use ListView without scrollview for messages

That way we can autohide the scollbar if needed, it should fix some
jumping issues, it makes it possible to flick on mobile, etc.

Some related bugs:

https://bugreports.qt.io/browse/QTBUG-75223
https://bugreports.qt.io/browse/QTBUG-44902
This commit is contained in:
Nicolas Werner 2022-02-19 02:49:58 +01:00
parent 0629ea5932
commit f7ea4c604c
No known key found for this signature in database
GPG key ID: C8D75E610773F2D9
6 changed files with 593 additions and 481 deletions

View file

@ -336,6 +336,7 @@ set(SRC_FILES
src/ui/MxcAnimatedImage.cpp
src/ui/MxcMediaProxy.cpp
src/ui/NhekoCursorShape.cpp
src/ui/NhekoEventObserver.cpp
src/ui/NhekoDropArea.cpp
src/ui/NhekoGlobalObject.cpp
src/ui/RoomSettings.cpp
@ -532,6 +533,7 @@ qt5_wrap_cpp(MOC_HEADERS
src/ui/MxcAnimatedImage.h
src/ui/MxcMediaProxy.h
src/ui/NhekoCursorShape.h
src/ui/NhekoEventObserver.h
src/ui/NhekoDropArea.h
src/ui/NhekoGlobalObject.h
src/ui/RoomSettings.h

View file

@ -105,6 +105,7 @@ Rectangle {
id: mouseArea
onSingleTapped: avatar.clicked(eventPoint)
dragThreshold: 0
}
Ripple {

View file

@ -14,16 +14,32 @@ import QtQuick.Layouts 1.2
import QtQuick.Window 2.13
import im.nheko 1.0
ScrollView {
clip: false
palette: Nheko.colors
padding: 8
ScrollBar.horizontal.visible: false
Item {
id: chatRoot
property int padding: Nheko.paddingMedium
property int availableWidth: width
ScrollBar {
id: scrollbar
interactive: !touchObserver.wasTouched
parent: chat.parent
anchors.top: parent.top
anchors.right: parent.right
anchors.bottom: parent.bottom
}
EventObserver {
id: touchObserver
anchors.fill: parent
ListView {
id: chat
property int delegateMaxWidth: ((Settings.timelineMaxWidth > 100 && Settings.timelineMaxWidth < parent.availableWidth) ? Settings.timelineMaxWidth : parent.availableWidth) - parent.padding * 2
anchors.fill: parent
property int delegateMaxWidth: ((Settings.timelineMaxWidth > 100 && Settings.timelineMaxWidth < chatRoot.availableWidth) ? Settings.timelineMaxWidth : chatRoot.availableWidth) - chatRoot.padding * 2 - scrollbar.width
displayMarginBeginning: height / 2
displayMarginEnd: height / 2
@ -32,16 +48,19 @@ ScrollView {
//onModelChanged: if (room) room.sendReset()
//reuseItems: true
boundsBehavior: Flickable.StopAtBounds
pixelAligned: true
//pixelAligned: true
spacing: 2
verticalLayoutDirection: ListView.BottomToTop
onCountChanged: {
// Mark timeline as read
if (atYEnd && room)
model.currentIndex = 0;
if (atYEnd && room) model.currentIndex = 0;
}
ScrollBar.vertical: scrollbar
anchors.rightMargin: scrollbar.interactive ? scrollbar.width : 0
Rectangle {
//closePolicy: Popup.NoAutoClose
@ -164,7 +183,6 @@ ScrollView {
ScrollHelper {
flickable: parent
anchors.fill: parent
enabled: !Settings.mobileMode
}
Shortcut {
@ -323,6 +341,7 @@ ScrollView {
TapHandler {
onSingleTapped: chat.model.openUserProfile(userId)
dragThreshold: 0
}
CursorShape {
@ -553,6 +572,7 @@ ScrollView {
}
}
}
Platform.Menu {
id: messageContextMenu
@ -732,5 +752,4 @@ ScrollView {
}
}
}

View file

@ -49,6 +49,7 @@
#include "ui/MxcMediaProxy.h"
#include "ui/NhekoCursorShape.h"
#include "ui/NhekoDropArea.h"
#include "ui/NhekoEventObserver.h"
#include "ui/NhekoGlobalObject.h"
#include "ui/UIA.h"
#include "voip/WebRTCSession.h"
@ -164,6 +165,7 @@ MainWindow::registerQmlTypes()
qmlRegisterType<DelegateChooser>("im.nheko", 1, 0, "DelegateChooser");
qmlRegisterType<NhekoDropArea>("im.nheko", 1, 0, "NhekoDropArea");
qmlRegisterType<NhekoCursorShape>("im.nheko", 1, 0, "CursorShape");
qmlRegisterType<NhekoEventObserver>("im.nheko", 1, 0, "EventObserver");
qmlRegisterType<MxcAnimatedImage>("im.nheko", 1, 0, "MxcAnimatedImage");
qmlRegisterType<MxcMediaProxy>("im.nheko", 1, 0, "MxcMedia");
qmlRegisterType<RoomDirectoryModel>("im.nheko", 1, 0, "RoomDirectoryModel");

View file

@ -0,0 +1,61 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
// SPDX-FileCopyrightText: 2022 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
#include "NhekoEventObserver.h"
#include <QMouseEvent>
#include "Logging.h"
NhekoEventObserver::NhekoEventObserver(QQuickItem *parent)
: QQuickItem(parent)
{
setFiltersChildMouseEvents(true);
}
bool
NhekoEventObserver::childMouseEventFilter(QQuickItem * /*item*/, QEvent *event)
{
// nhlog::ui()->debug("Touched {}", item->metaObject()->className());
auto setTouched = [this](bool touched) {
if (touched != this->wasTouched_) {
this->wasTouched_ = touched;
emit wasTouchedChanged();
}
};
// see
// https://code.qt.io/cgit/qt/qtdeclarative.git/tree/src/quicktemplates2/qquickscrollview.cpp?id=7f29e89c26ae2babc358b1c4e6f965af6ec759f4#n471
switch (event->type()) {
case QEvent::TouchBegin:
case QEvent::TouchEnd:
setTouched(true);
break;
case QEvent::MouseButtonPress:
if (static_cast<QMouseEvent *>(event)->source() == Qt::MouseEventNotSynthesized) {
setTouched(false);
}
break;
case QEvent::MouseMove:
case QEvent::MouseButtonRelease:
if (static_cast<QMouseEvent *>(event)->source() == Qt::MouseEventNotSynthesized)
setTouched(false);
break;
case QEvent::HoverEnter:
case QEvent::HoverMove:
case QEvent::Wheel:
setTouched(false);
break;
default:
break;
}
return false;
}

View file

@ -0,0 +1,27 @@
// SPDX-FileCopyrightText: 2022 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <QQuickItem>
class NhekoEventObserver : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(bool wasTouched READ wasTouched NOTIFY wasTouchedChanged)
public:
explicit NhekoEventObserver(QQuickItem *parent = 0);
bool childMouseEventFilter(QQuickItem *item, QEvent *event) override;
private:
bool wasTouched() { return wasTouched_; }
bool wasTouched_ = false;
signals:
void wasTouchedChanged();
};