mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-23 19:38:48 +03:00
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:
parent
0629ea5932
commit
f7ea4c604c
6 changed files with 593 additions and 481 deletions
|
@ -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");
|
||||||
|
|
61
src/ui/NhekoEventObserver.cpp
Normal file
61
src/ui/NhekoEventObserver.cpp
Normal 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;
|
||||||
|
}
|
27
src/ui/NhekoEventObserver.h
Normal file
27
src/ui/NhekoEventObserver.h
Normal 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();
|
||||||
|
};
|
Loading…
Reference in a new issue