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) + } }