From dec5369a8f01d1e6cf35aa600ade071eb283941c Mon Sep 17 00:00:00 2001 From: James Graham Date: Sun, 1 Jun 2025 10:37:30 +0100 Subject: [PATCH] Actually position the view at the end It turns out that for whatever reason ListView.posiitionViewAtEnd() ignores any whitespace. This means that when we use the function it goes to the last delegate. This is no good as we have some padding at the bottom to make space for the typing indicator. So the fix for this is stupid and involves adding a "spacer" delegate to the timeline beginning model which is completely invisible but qml see as a delegate so we can both leave the space and properly position the view at the end. BUG: 501075 --- src/timeline/CMakeLists.txt | 1 + src/timeline/EventDelegate.qml | 5 +++++ src/timeline/SpacerDelegate.qml | 11 +++++++++++ src/timeline/TimelineView.qml | 1 - src/timeline/enums/delegatetype.h | 1 + src/timeline/models/timelinemodel.cpp | 6 ++++-- 6 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 src/timeline/SpacerDelegate.qml diff --git a/src/timeline/CMakeLists.txt b/src/timeline/CMakeLists.txt index b3d43f7a0..2a8917302 100644 --- a/src/timeline/CMakeLists.txt +++ b/src/timeline/CMakeLists.txt @@ -13,6 +13,7 @@ ecm_add_qml_module(Timeline GENERATE_PLUGIN_SOURCE LoadingDelegate.qml PredecessorDelegate.qml ReadMarkerDelegate.qml + SpacerDelegate.qml StateDelegate.qml SuccessorDelegate.qml TimelineEndDelegate.qml diff --git a/src/timeline/EventDelegate.qml b/src/timeline/EventDelegate.qml index 8dd0ea802..595c69e95 100644 --- a/src/timeline/EventDelegate.qml +++ b/src/timeline/EventDelegate.qml @@ -62,6 +62,11 @@ DelegateChooser { } } + DelegateChoice { + roleValue: DelegateType.Spacer + delegate: SpacerDelegate {} + } + DelegateChoice { roleValue: DelegateType.Other delegate: NeoChatConfig.showAllEvents ? hiddenDelegate : emptyDelegate diff --git a/src/timeline/SpacerDelegate.qml b/src/timeline/SpacerDelegate.qml new file mode 100644 index 000000000..6c8c651c6 --- /dev/null +++ b/src/timeline/SpacerDelegate.qml @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2025 James Graham +// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL + +import QtQuick + +import org.kde.kirigami as Kirigami + +Item { + width: parent?.width + height: Kirigami.Units.largeSpacing + Math.round(Kirigami.Theme.defaultFont.pointSize * 2) +} diff --git a/src/timeline/TimelineView.qml b/src/timeline/TimelineView.qml index ed782018d..71667ad1a 100644 --- a/src/timeline/TimelineView.qml +++ b/src/timeline/TimelineView.qml @@ -68,7 +68,6 @@ QQC2.ScrollView { verticalLayoutDirection: ListView.BottomToTop clip: true interactive: Kirigami.Settings.isMobile - bottomMargin: Kirigami.Units.largeSpacing + Math.round(Kirigami.Theme.defaultFont.pointSize * 2) model: root.messageFilterModel diff --git a/src/timeline/enums/delegatetype.h b/src/timeline/enums/delegatetype.h index 9425b0dde..7b0209831 100644 --- a/src/timeline/enums/delegatetype.h +++ b/src/timeline/enums/delegatetype.h @@ -42,6 +42,7 @@ public: TimelineEnd, /**< A delegate to inform that all messages are loaded. */ Predecessor, /**< A delegate to show a room predecessor. */ Successor, /**< A delegate to show a room successor. */ + Spacer, /**< A spacer because ListView.positionViewAtBeginning() is stupid and ignores white space. */ Other, /**< Anything that cannot be classified as another type. */ }; Q_ENUM(Type); diff --git a/src/timeline/models/timelinemodel.cpp b/src/timeline/models/timelinemodel.cpp index 90d2ff0e5..b16416c21 100644 --- a/src/timeline/models/timelinemodel.cpp +++ b/src/timeline/models/timelinemodel.cpp @@ -81,12 +81,14 @@ void TimelineBeginningModel::setRoom(NeoChatRoom *room) QVariant TimelineBeginningModel::data(const QModelIndex &idx, int role) const { - Q_UNUSED(idx) if (m_room == nullptr) { return {}; } if (role == DelegateTypeRole) { + if (idx.row() == 0) { + return DelegateType::Spacer; + } return DelegateType::Successor; } return {}; @@ -98,7 +100,7 @@ int TimelineBeginningModel::rowCount(const QModelIndex &parent) const if (m_room == nullptr) { return 1; } - return m_room->successorId().isEmpty() ? 0 : 1; + return m_room->successorId().isEmpty() ? 1 : 2; } QHash TimelineBeginningModel::roleNames() const