From fa37f28c9479f28d2c167dd11d9fa96d6e59510b Mon Sep 17 00:00:00 2001 From: James Graham Date: Tue, 30 May 2023 18:14:25 +0000 Subject: [PATCH] Improve Delegate Width Sizing Create a cpp helper class to calculate the correct delegate width given it's parent width. This is designed to be more robust and hopefully easier to understand than the current mass of javascript calcs. BUG: 470167 --- autotests/CMakeLists.txt | 6 + autotests/delegatesizehelpertest.cpp | 156 ++++++++++++++++++ src/CMakeLists.txt | 1 + src/delegatesizehelper.cpp | 153 +++++++++++++++++ src/delegatesizehelper.h | 123 ++++++++++++++ src/main.cpp | 2 + src/qml/Component/ChatBox/ChatBar.qml | 21 ++- src/qml/Component/ChatBox/ChatBox.qml | 3 - src/qml/Component/Timeline/StateDelegate.qml | 17 +- .../Component/Timeline/TimelineContainer.qml | 33 ++-- 10 files changed, 492 insertions(+), 23 deletions(-) create mode 100644 autotests/delegatesizehelpertest.cpp create mode 100644 src/delegatesizehelper.cpp create mode 100644 src/delegatesizehelper.h diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt index f3ca64ac1..c32990d90 100644 --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -14,3 +14,9 @@ ecm_add_test( LINK_LIBRARIES neochat Qt::Test TEST_NAME texthandlertest ) + +ecm_add_test( + delegatesizehelpertest.cpp + LINK_LIBRARIES neochat Qt::Test + TEST_NAME delegatesizehelpertest +) diff --git a/autotests/delegatesizehelpertest.cpp b/autotests/delegatesizehelpertest.cpp new file mode 100644 index 000000000..9ea52fdd3 --- /dev/null +++ b/autotests/delegatesizehelpertest.cpp @@ -0,0 +1,156 @@ +// SPDX-FileCopyrightText: 2023 James Graham +// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL + +#include +#include + +#include "delegatesizehelper.h" + +class DelegateSizeHelperTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void risingPercentage_data(); + void risingPercentage(); + + void fallingPercentage_data(); + void fallingPercentage(); + + void equalPercentage_data(); + void equalPercentage(); + + void equalBreakpoint_data(); + void equalBreakpoint(); +}; + +void DelegateSizeHelperTest::risingPercentage_data() +{ + QTest::addColumn("parentWidth"); + QTest::addColumn("currentPercentageWidth"); + QTest::addColumn("currentWidth"); + + QTest::newRow("zero") << qreal(0) << int(0) << qreal(0); + QTest::newRow("one hundred") << qreal(100) << int(0) << qreal(0); + QTest::newRow("one fifty") << qreal(150) << int(50) << qreal(75); + QTest::newRow("two hundred") << qreal(200) << int(100) << qreal(200); + QTest::newRow("three hundred") << qreal(300) << int(100) << qreal(300); +} + +void DelegateSizeHelperTest::risingPercentage() +{ + QFETCH(qreal, parentWidth); + QFETCH(int, currentPercentageWidth); + QFETCH(qreal, currentWidth); + + DelegateSizeHelper delegateSizeHelper; + delegateSizeHelper.setStartBreakpoint(100); + delegateSizeHelper.setEndBreakpoint(200); + delegateSizeHelper.setStartPercentWidth(0); + delegateSizeHelper.setEndPercentWidth(100); + + delegateSizeHelper.setParentWidth(parentWidth); + + QCOMPARE(delegateSizeHelper.currentPercentageWidth(), currentPercentageWidth); + QCOMPARE(delegateSizeHelper.currentWidth(), currentWidth); +} + +void DelegateSizeHelperTest::fallingPercentage_data() +{ + QTest::addColumn("parentWidth"); + QTest::addColumn("currentPercentageWidth"); + QTest::addColumn("currentWidth"); + + QTest::newRow("zero") << qreal(0) << int(100) << qreal(0); + QTest::newRow("one hundred") << qreal(100) << int(100) << qreal(100); + QTest::newRow("one fifty") << qreal(150) << int(50) << qreal(75); + QTest::newRow("two hundred") << qreal(200) << int(0) << qreal(0); + QTest::newRow("three hundred") << qreal(300) << int(0) << qreal(0); +} + +void DelegateSizeHelperTest::fallingPercentage() +{ + QFETCH(qreal, parentWidth); + QFETCH(int, currentPercentageWidth); + QFETCH(qreal, currentWidth); + + DelegateSizeHelper delegateSizeHelper; + delegateSizeHelper.setStartBreakpoint(100); + delegateSizeHelper.setEndBreakpoint(200); + delegateSizeHelper.setStartPercentWidth(100); + delegateSizeHelper.setEndPercentWidth(0); + + delegateSizeHelper.setParentWidth(parentWidth); + + QCOMPARE(delegateSizeHelper.currentPercentageWidth(), currentPercentageWidth); + QCOMPARE(delegateSizeHelper.currentWidth(), currentWidth); +} + +void DelegateSizeHelperTest::equalPercentage_data() +{ + QTest::addColumn("parentWidth"); + QTest::addColumn("currentPercentageWidth"); + QTest::addColumn("currentWidth"); + + QTest::newRow("zero") << qreal(0) << int(50) << qreal(0); + QTest::newRow("one hundred") << qreal(100) << int(50) << qreal(50); + QTest::newRow("one fifty") << qreal(150) << int(50) << qreal(75); + QTest::newRow("two hundred") << qreal(200) << int(50) << qreal(100); + QTest::newRow("three hundred") << qreal(300) << int(50) << qreal(150); +} + +void DelegateSizeHelperTest::equalPercentage() +{ + QFETCH(qreal, parentWidth); + QFETCH(int, currentPercentageWidth); + QFETCH(qreal, currentWidth); + + DelegateSizeHelper delegateSizeHelper; + delegateSizeHelper.setStartBreakpoint(100); + delegateSizeHelper.setEndBreakpoint(200); + delegateSizeHelper.setStartPercentWidth(50); + delegateSizeHelper.setEndPercentWidth(50); + + delegateSizeHelper.setParentWidth(parentWidth); + + QCOMPARE(delegateSizeHelper.currentPercentageWidth(), currentPercentageWidth); + QCOMPARE(delegateSizeHelper.currentWidth(), currentWidth); +} + +void DelegateSizeHelperTest::equalBreakpoint_data() +{ + QTest::addColumn("startPercentageWidth"); + QTest::addColumn("endPercentageWidth"); + QTest::addColumn("currentPercentageWidth"); + QTest::addColumn("currentWidth"); + + QTest::newRow("start higher") << int(100) << int(0) << int(-1) << qreal(0); + QTest::newRow("equal") << int(50) << int(50) << int(50) << qreal(500); + QTest::newRow("end higher") << int(0) << int(100) << int(-1) << qreal(0); +} + +/** + * We expect a default return except in the case where the the two percentages are + * equal as that case can be calculated without dividing by zero. + */ +void DelegateSizeHelperTest::equalBreakpoint() +{ + QFETCH(int, startPercentageWidth); + QFETCH(int, endPercentageWidth); + QFETCH(int, currentPercentageWidth); + QFETCH(qreal, currentWidth); + + DelegateSizeHelper delegateSizeHelper; + delegateSizeHelper.setStartBreakpoint(100); + delegateSizeHelper.setEndBreakpoint(100); + delegateSizeHelper.setStartPercentWidth(startPercentageWidth); + delegateSizeHelper.setEndPercentWidth(endPercentageWidth); + + delegateSizeHelper.setParentWidth(1000); + + QCOMPARE(delegateSizeHelper.currentPercentageWidth(), currentPercentageWidth); + QCOMPARE(delegateSizeHelper.currentWidth(), currentWidth); +} + +QTEST_GUILESS_MAIN(DelegateSizeHelperTest) +#include "delegatesizehelpertest.moc" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 264937af6..b11edb092 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -55,6 +55,7 @@ add_library(neochat STATIC events/joinrulesevent.cpp events/stickerevent.cpp models/reactionmodel.cpp + delegatesizehelper.cpp ) ecm_qt_declare_logging_category(neochat diff --git a/src/delegatesizehelper.cpp b/src/delegatesizehelper.cpp new file mode 100644 index 000000000..b1bc2f6bf --- /dev/null +++ b/src/delegatesizehelper.cpp @@ -0,0 +1,153 @@ +// SPDX-FileCopyrightText: 2023 James Graham +// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL + +#include "delegatesizehelper.h" + +#include + +#include + +DelegateSizeHelper::DelegateSizeHelper(QObject *parent) + : QObject(parent) +{ +} + +qreal DelegateSizeHelper::parentWidth() const +{ + return m_parentWidth; +} + +void DelegateSizeHelper::setParentWidth(qreal parentWidth) +{ + if (parentWidth == m_parentWidth) { + return; + } + m_parentWidth = parentWidth; + Q_EMIT parentWidthChanged(); + Q_EMIT currentPercentageWidthChanged(); + Q_EMIT currentWidthChanged(); +} + +qreal DelegateSizeHelper::startBreakpoint() const +{ + return m_startBreakpoint; +} + +void DelegateSizeHelper::setStartBreakpoint(qreal startBreakpoint) +{ + if (startBreakpoint == m_startBreakpoint) { + return; + } + m_startBreakpoint = startBreakpoint; + Q_EMIT startBreakpointChanged(); +} + +qreal DelegateSizeHelper::endBreakpoint() const +{ + return m_endBreakpoint; +} + +void DelegateSizeHelper::setEndBreakpoint(qreal endBreakpoint) +{ + if (endBreakpoint == m_endBreakpoint) { + return; + } + m_endBreakpoint = endBreakpoint; + Q_EMIT endBreakpointChanged(); +} + +int DelegateSizeHelper::startPercentWidth() const +{ + return m_startPercentWidth; +} + +void DelegateSizeHelper::setStartPercentWidth(int startPercentWidth) +{ + if (startPercentWidth == m_startPercentWidth) { + return; + } + m_startPercentWidth = startPercentWidth; + Q_EMIT startPercentWidthChanged(); +} + +int DelegateSizeHelper::endPercentWidth() const +{ + return m_endPercentWidth; +} + +void DelegateSizeHelper::setEndPercentWidth(int endPercentWidth) +{ + if (endPercentWidth == m_endPercentWidth) { + return; + } + m_endPercentWidth = endPercentWidth; + Q_EMIT endPercentWidthChanged(); +} + +qreal DelegateSizeHelper::maxWidth() const +{ + return m_maxWidth; +} + +void DelegateSizeHelper::setMaxWidth(qreal maxWidth) +{ + if (maxWidth == m_maxWidth) { + return; + } + m_maxWidth = maxWidth; + Q_EMIT maxWidthChanged(); +} + +int DelegateSizeHelper::calculateCurrentPercentageWidth() const +{ + // Don't do anything if m_parentWidth hasn't been set yet. + if (m_parentWidth < 0) { + return -1; + } + // Don't bother with calculations for a horizontal line. + if (m_startPercentWidth == m_endPercentWidth) { + return m_startPercentWidth; + } + // Dividing by zero is a bad idea. + if (m_startBreakpoint == m_endBreakpoint) { + qWarning() << "DelegateSizeHelper::calculateCurrentPercentageWidth() - m_startBreakpoint is equal to m_endBreakpoint this would lead to divide by " + "zero, aborting"; + return -1; + } + + // Fit to y = mx + c + qreal m = (m_endPercentWidth - m_startPercentWidth) / (m_endBreakpoint - m_startBreakpoint); + qreal c = m_startPercentWidth - m * m_startBreakpoint; + + // This allows us to clamp correctly if the start or end width is bigger. + bool endPercentBigger = m_endPercentWidth > m_startPercentWidth; + int maxPercentWidth = endPercentBigger ? m_endPercentWidth : m_startPercentWidth; + int minPercentWidth = endPercentBigger ? m_startPercentWidth : m_endPercentWidth; + + int calcPercentWidth = std::ceil(m * m_parentWidth + c); + return std::clamp(calcPercentWidth, minPercentWidth, maxPercentWidth); +} + +int DelegateSizeHelper::currentPercentageWidth() const +{ + return calculateCurrentPercentageWidth(); +} + +qreal DelegateSizeHelper::currentWidth() const +{ + if (m_parentWidth < 0) { + return 0.0; + } + int percentWidth = calculateCurrentPercentageWidth(); + // - 1 means bad input values so don't try to calculate. + if (percentWidth == -1) { + return 0.0; + } + + qreal absoluteWidth = m_parentWidth * percentWidth * 0.01; + if (m_maxWidth < 0.0) { + return std::ceil(absoluteWidth); + } else { + return std::ceil(std::min(absoluteWidth, m_maxWidth)); + } +} diff --git a/src/delegatesizehelper.h b/src/delegatesizehelper.h new file mode 100644 index 000000000..1fcbddca1 --- /dev/null +++ b/src/delegatesizehelper.h @@ -0,0 +1,123 @@ +// SPDX-FileCopyrightText: 2023 James Graham +// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL + +#pragma once + +#include + +/** + * @class DelegateSizeHelper + * + * A class to help calculate the current width of a chat delegate/bar. + * + * The aim is to support a dynamic sizing based upon the width of the page. There is + * a built in curve that allows the width to transition between two percentages based + * upon a start and finish break point. This is to provide better convergence where + * generally the delegate will need to fill all or most the screen when thin but + * should max out in size and only fill a lower percentage of the screen when wide. + * + * @note While the main intended usage is to start with a high percentage when the parentWidth + * is small and transition to a lower one when large, the math is setup for the + * general case so any combination of parameters works. + */ +class DelegateSizeHelper : public QObject +{ + Q_OBJECT + + /** + * @brief The width of the component's parent. + */ + Q_PROPERTY(qreal parentWidth READ parentWidth WRITE setParentWidth NOTIFY parentWidthChanged) + + /** + * @brief The width (in px) when the width percentage should start to transition. + */ + Q_PROPERTY(qreal startBreakpoint READ startBreakpoint WRITE setStartBreakpoint NOTIFY startBreakpointChanged) + + /** + * @brief The width (in px) when the width percentage should finish transitioning. + */ + Q_PROPERTY(qreal endBreakpoint READ endBreakpoint WRITE setEndBreakpoint NOTIFY endBreakpointChanged) + + /** + * @brief The width (in %) of the component at or before the startBreakpoint. + * + * @sa startBreakpoint + */ + Q_PROPERTY(int startPercentWidth READ startPercentWidth WRITE setStartPercentWidth NOTIFY startPercentWidthChanged) + + /** + * @brief The width (in %) of the component at or after the endBreakpoint. + * + * @sa endBreakpoint + */ + Q_PROPERTY(int endPercentWidth READ endPercentWidth WRITE setEndPercentWidth NOTIFY endPercentWidthChanged) + + /** + * @brief The absolute maximum width (in px) the component can be. + */ + Q_PROPERTY(qreal maxWidth READ maxWidth WRITE setMaxWidth NOTIFY maxWidthChanged) + + /** + * @brief The width (in %) of the component at the current parentWidth. + * + * Will return -1 if no parentWidth is set or startBreakpoint == endBreakpoint. + * + * @sa parentWidth, startBreakpoint, endBreakpoint + */ + Q_PROPERTY(int currentPercentageWidth READ currentPercentageWidth NOTIFY currentPercentageWidthChanged) + + /** + * @brief The width (in px) of the component at the current parentWidth. + * + * Will return 0.0 if no parentWidth is set. + * + * @sa parentWidth + */ + Q_PROPERTY(qreal currentWidth READ currentWidth NOTIFY currentWidthChanged) + +public: + DelegateSizeHelper(QObject *parent = nullptr); + + qreal parentWidth() const; + void setParentWidth(qreal parentWidth); + + qreal startBreakpoint() const; + void setStartBreakpoint(qreal startBreakpoint); + + qreal endBreakpoint() const; + void setEndBreakpoint(qreal endBreakpoint); + + int startPercentWidth() const; + void setStartPercentWidth(int startPercentWidth); + + int endPercentWidth() const; + void setEndPercentWidth(int endPercentWidth); + + qreal maxWidth() const; + void setMaxWidth(qreal maxWidth); + + int currentPercentageWidth() const; + + qreal currentWidth() const; + +Q_SIGNALS: + void parentWidthChanged(); + void startBreakpointChanged(); + void endBreakpointChanged(); + void startPercentWidthChanged(); + void endPercentWidthChanged(); + void maxWidthChanged(); + void currentPercentageWidthChanged(); + void currentWidthChanged(); + +private: + qreal m_parentWidth = -1.0; + qreal m_startBreakpoint; + qreal m_endBreakpoint; + int m_startPercentWidth; + int m_endPercentWidth; + qreal m_maxWidth = -1.0; + + int calculateCurrentPercentageWidth() const; +}; diff --git a/src/main.cpp b/src/main.cpp index 01d9789e9..d8599f8ce 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -43,6 +43,7 @@ #include "chatdocumenthandler.h" #include "clipboard.h" #include "controller.h" +#include "delegatesizehelper.h" #include "filetypesingleton.h" #include "linkpreviewer.h" #include "logger.h" @@ -254,6 +255,7 @@ int main(int argc, char *argv[]) qmlRegisterType("org.kde.neochat", 1, 0, "ImagePacksModel"); qmlRegisterType("org.kde.neochat", 1, 0, "AccountEmoticonModel"); qmlRegisterType("org.kde.neochat", 1, 0, "EmoticonFilterModel"); + qmlRegisterType("org.kde.neochat", 1, 0, "DelegateSizeHelper"); qmlRegisterUncreatableType("org.kde.neochat", 1, 0, "RoomMessageEvent", "ENUM"); qmlRegisterUncreatableType("org.kde.neochat", 1, 0, "PushNotificationState", "ENUM"); qmlRegisterUncreatableType("org.kde.neochat", 1, 0, "PushNotificationAction", "ENUM"); diff --git a/src/qml/Component/ChatBox/ChatBar.qml b/src/qml/Component/ChatBox/ChatBar.qml index 177a1e1fe..af07db51f 100644 --- a/src/qml/Component/ChatBox/ChatBar.qml +++ b/src/qml/Component/ChatBox/ChatBar.qml @@ -119,7 +119,7 @@ QQC2.Control { QQC2.TextArea { id: textField - x: Math.round((root.width - chatBoxMaxWidth) / 2) - (root.width > chatBoxMaxWidth ? Kirigami.Units.largeSpacing * 1.5 : 0) + x: Math.round((root.width - chatBarSizeHelper.currentWidth) / 2) - (root.width > chatBarSizeHelper.currentWidth + Kirigami.Units.largeSpacing * 2.5 ? Kirigami.Units.largeSpacing * 1.5 : 0) topPadding: Kirigami.Units.largeSpacing + (paneLoader.visible ? paneLoader.height : 0) bottomPadding: Kirigami.Units.largeSpacing leftPadding: LayoutMirroring.enabled ? actionsRow.width : Kirigami.Units.largeSpacing @@ -232,9 +232,9 @@ QQC2.Control { anchors.top: parent.top anchors.left: parent.left - anchors.leftMargin: root.width > chatBoxMaxWidth ? 0 : Kirigami.Units.largeSpacing + anchors.leftMargin: Kirigami.Units.largeSpacing anchors.right: parent.right - anchors.rightMargin: root.width > chatBoxMaxWidth ? 0 : (chatBarScrollView.QQC2.ScrollBar.vertical.visible ? Kirigami.Units.largeSpacing * 3.5 : Kirigami.Units.largeSpacing) + anchors.rightMargin: root.width > chatBarSizeHelper.currentWidth ? 0 : (chatBarScrollView.QQC2.ScrollBar.vertical.visible ? Kirigami.Units.largeSpacing * 3.5 : Kirigami.Units.largeSpacing) active: visible visible: root.isReplying || root.attachmentPaneVisible @@ -299,7 +299,7 @@ QQC2.Control { id: cancelButton anchors.top: parent.top anchors.right: parent.right - anchors.rightMargin: (root.width - chatBoxMaxWidth) / 2 + Kirigami.Units.largeSpacing + (chatBarScrollView.QQC2.ScrollBar.vertical.visible && !(root.width > chatBoxMaxWidth) ? Kirigami.Units.largeSpacing * 2.5 : 0) + anchors.rightMargin: (root.width - chatBarSizeHelper.currentWidth) / 2 + Kirigami.Units.largeSpacing + (chatBarScrollView.QQC2.ScrollBar.vertical.visible && !(root.width > chatBarSizeHelper.currentWidth) ? Kirigami.Units.largeSpacing * 2.5 : 0) visible: root.isReplying display: QQC2.AbstractButton.IconOnly @@ -320,7 +320,7 @@ QQC2.Control { padding: Kirigami.Units.smallSpacing spacing: Kirigami.Units.smallSpacing anchors.right: parent.right - property var requiredMargin: (root.width - chatBoxMaxWidth) / 2 + Kirigami.Units.largeSpacing + (chatBarScrollView.QQC2.ScrollBar.vertical.visible && !(root.width > chatBoxMaxWidth) ? Kirigami.Units.largeSpacing * 2.5 : 0) + property var requiredMargin: (root.width - chatBarSizeHelper.currentWidth) / 2 + Kirigami.Units.largeSpacing + (chatBarScrollView.QQC2.ScrollBar.vertical.visible && !(root.width > chatBarSizeHelper.currentWidth) ? Kirigami.Units.largeSpacing * 2.5 : 0) anchors.leftMargin: layoutDirection === Qt.RightToLeft ? requiredMargin : 0 anchors.rightMargin: layoutDirection === Qt.RightToLeft ? 0 : requiredMargin anchors.bottom: parent.bottom @@ -400,6 +400,17 @@ QQC2.Control { } } + DelegateSizeHelper { + id: chatBarSizeHelper + startBreakpoint: Kirigami.Units.gridUnit * 46 + endBreakpoint: Kirigami.Units.gridUnit * 66 + startPercentWidth: 100 + endPercentWidth: Config.compactLayout ? 100 : 85 + maxWidth: Config.compactLayout ? -1 : Kirigami.Units.gridUnit * 60 + + parentWidth: root.width + } + function forceActiveFocus() { textField.forceActiveFocus(); // set the cursor to the end of the text diff --git a/src/qml/Component/ChatBox/ChatBox.qml b/src/qml/Component/ChatBox/ChatBox.qml index 41b194315..ebc3026de 100644 --- a/src/qml/Component/ChatBox/ChatBox.qml +++ b/src/qml/Component/ChatBox/ChatBox.qml @@ -16,9 +16,6 @@ ColumnLayout { property alias chatBar: chatBar - readonly property int extraWidth: width >= Kirigami.Units.gridUnit * 47 ? Math.min((width - Kirigami.Units.gridUnit * 47), Kirigami.Units.gridUnit * 20) : 0 - readonly property int chatBoxMaxWidth: Config.compactLayout ? width : Math.min(width, Kirigami.Units.gridUnit * 39 + extraWidth) - spacing: 0 Kirigami.Theme.colorSet: Kirigami.Theme.View diff --git a/src/qml/Component/Timeline/StateDelegate.qml b/src/qml/Component/Timeline/StateDelegate.qml index 2f4fb08be..84748c50a 100644 --- a/src/qml/Component/Timeline/StateDelegate.qml +++ b/src/qml/Component/Timeline/StateDelegate.qml @@ -14,11 +14,7 @@ QQC2.Control { readonly property bool sectionVisible: model.showSection - // extraWidth defines how the delegate can grow after the listView gets very wide - readonly property int extraWidth: parent ? (parent.width >= Kirigami.Units.gridUnit * 46 ? Math.min((parent.width - Kirigami.Units.gridUnit * 46), Kirigami.Units.gridUnit * 20) : 0) : 0 - readonly property int delegateMaxWidth: parent ? (Config.compactLayout ? parent.width: Math.min(parent.width, Kirigami.Units.gridUnit * 40 + extraWidth)) : 0 - - width: delegateMaxWidth + width: stateDelegateSizeHelper.currentWidth state: Config.compactLayout ? "alignLeft" : "alignCenter" // Align left when in compact mode and center when using bubbles @@ -170,4 +166,15 @@ QQC2.Control { excessAvatars: excessReadMarkers } } + + DelegateSizeHelper { + id: stateDelegateSizeHelper + startBreakpoint: Kirigami.Units.gridUnit * 46 + endBreakpoint: Kirigami.Units.gridUnit * 66 + startPercentWidth: 100 + endPercentWidth: Config.compactLayout ? 100 : 85 + maxWidth: Config.compactLayout ? -1 : Kirigami.Units.gridUnit * 60 + + parentWidth: stateDelegate.parent ? stateDelegate.parent.width : 0 + } } diff --git a/src/qml/Component/Timeline/TimelineContainer.qml b/src/qml/Component/Timeline/TimelineContainer.qml index 1a77b7509..47c40907f 100644 --- a/src/qml/Component/Timeline/TimelineContainer.qml +++ b/src/qml/Component/Timeline/TimelineContainer.qml @@ -66,7 +66,6 @@ ColumnLayout { /** * @brief The delegate type of the message. - */ required property int delegateType /** @@ -290,15 +289,9 @@ ColumnLayout { onTriggered: isTemporaryHighlighted = false } - // TODO: make these private - // The bubble and delegate widths are allowed to grow once the ListView gets beyond a certain size - // extraWidth defines this as the excess after a certain ListView width, capped to a max value - readonly property int extraWidth: parent ? (parent.width >= Kirigami.Units.gridUnit * 46 ? Math.min((parent.width - Kirigami.Units.gridUnit * 46), Kirigami.Units.gridUnit * 20) : 0) : 0 - readonly property int bubbleMaxWidth: Kirigami.Units.gridUnit * 20 + extraWidth * 0.5 - readonly property int delegateWidth: parent ? (Config.compactLayout ? parent.width - Kirigami.Units.smallSpacing - (ListView.view.width >= Kirigami.Units.gridUnit * 20 ? Kirigami.Units.gridUnit * 2 + Kirigami.Units.largeSpacing : 0) : Math.min(parent.width, Kirigami.Units.gridUnit * 40 + extraWidth)) : 0 - readonly property int contentMaxWidth: Config.compactLayout ? width - (Config.showAvatarInTimeline ? Kirigami.Units.gridUnit * 2 : 0) - Kirigami.Units.largeSpacing * 4 : Math.min(width - Kirigami.Units.gridUnit * 2 - Kirigami.Units.largeSpacing * 6, bubbleMaxWidth) + readonly property int contentMaxWidth: bubbleSizeHelper.currentWidth - width: delegateWidth + width: parent ? timelineDelegateSizeHelper.currentWidth : 0 spacing: Kirigami.Units.smallSpacing state: Config.compactLayout ? "alignLeft" : "alignCenter" @@ -567,7 +560,7 @@ ColumnLayout { } ReactionDelegate { - Layout.maximumWidth: delegateWidth - Kirigami.Units.largeSpacing * 2 + Layout.maximumWidth: root.width - Kirigami.Units.largeSpacing * 2 Layout.alignment: showUserMessageOnRight ? Qt.AlignRight : Qt.AlignLeft Layout.leftMargin: showUserMessageOnRight ? 0 : bubble.x + bubble.anchors.leftMargin Layout.rightMargin: showUserMessageOnRight ? Kirigami.Units.largeSpacing : 0 @@ -621,4 +614,24 @@ ColumnLayout { function setHoverActionsToDelegate() { ListView.view.setHoverActionsToDelegate(root) } + + DelegateSizeHelper { + id: timelineDelegateSizeHelper + startBreakpoint: Kirigami.Units.gridUnit * 46 + endBreakpoint: Kirigami.Units.gridUnit * 66 + startPercentWidth: 100 + endPercentWidth: Config.compactLayout ? 100 : 85 + maxWidth: Config.compactLayout ? -1 : Kirigami.Units.gridUnit * 60 + + parentWidth: root.parent ? root.parent.width - (Config.compactLayout && root.ListView.view.width >= Kirigami.Units.gridUnit * 20 ? Kirigami.Units.gridUnit * 2 + Kirigami.Units.largeSpacing : 0) : 0 + } + DelegateSizeHelper { + id: bubbleSizeHelper + startBreakpoint: Kirigami.Units.gridUnit * 25 + endBreakpoint: Kirigami.Units.gridUnit * 40 + startPercentWidth: Config.compactLayout ? 100 : 90 + endPercentWidth: Config.compactLayout ? 100 : 60 + + parentWidth: mainContainer.availableWidth - (Config.showAvatarInTimeline ? avatar.width + bubble.anchors.leftMargin : 0) + } }