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/MxcAnimatedImage.cpp
src/ui/MxcMediaProxy.cpp src/ui/MxcMediaProxy.cpp
src/ui/NhekoCursorShape.cpp src/ui/NhekoCursorShape.cpp
src/ui/NhekoEventObserver.cpp
src/ui/NhekoDropArea.cpp src/ui/NhekoDropArea.cpp
src/ui/NhekoGlobalObject.cpp src/ui/NhekoGlobalObject.cpp
src/ui/RoomSettings.cpp src/ui/RoomSettings.cpp
@ -532,6 +533,7 @@ qt5_wrap_cpp(MOC_HEADERS
src/ui/MxcAnimatedImage.h src/ui/MxcAnimatedImage.h
src/ui/MxcMediaProxy.h src/ui/MxcMediaProxy.h
src/ui/NhekoCursorShape.h src/ui/NhekoCursorShape.h
src/ui/NhekoEventObserver.h
src/ui/NhekoDropArea.h src/ui/NhekoDropArea.h
src/ui/NhekoGlobalObject.h src/ui/NhekoGlobalObject.h
src/ui/RoomSettings.h src/ui/RoomSettings.h

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

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

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