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
pull/956/head
Nicolas Werner 3 years ago
parent 2c9ef11254
commit 46fbb0e749
No known key found for this signature in database
GPG Key ID: C8D75E610773F2D9
  1. 2
      CMakeLists.txt
  2. 1
      resources/qml/Avatar.qml
  3. 41
      resources/qml/MessageView.qml
  4. 2
      src/MainWindow.cpp
  5. 61
      src/ui/NhekoEventObserver.cpp
  6. 27
      src/ui/NhekoEventObserver.h

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

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

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

@ -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");

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

@ -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();
};
Loading…
Cancel
Save