delegatesizehelper updates

Make `DelegateSizeHelper` take a `QQuickItem` rather than a width value and move `TimelineDelegate` to use it rather than duplicting the code.

This requires the creation of `LibNeoChat` so that both the main and timeline modules can get access to the class.

Note: ideally more stuff will go into `LibNeoChat` but it turns out our dependencies are kinda spaghetti like and so will take some untangling.
This commit is contained in:
James Graham
2025-04-03 15:24:31 +00:00
parent f026e4e5ed
commit 054f87cae2
12 changed files with 214 additions and 172 deletions

View File

@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include <QObject> #include <QObject>
#include <QQuickItem>
#include <QTest> #include <QTest>
#include "delegatesizehelper.h" #include "delegatesizehelper.h"
@@ -30,7 +31,7 @@ void DelegateSizeHelperTest::risingPercentage_data()
QTest::addColumn<int>("currentPercentageWidth"); QTest::addColumn<int>("currentPercentageWidth");
QTest::addColumn<qreal>("currentWidth"); QTest::addColumn<qreal>("currentWidth");
QTest::newRow("zero") << qreal(0) << int(0) << qreal(0); QTest::newRow("zero") << qreal(0) << int(100) << qreal(0);
QTest::newRow("one hundred") << qreal(100) << 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("one fifty") << qreal(150) << int(50) << qreal(75);
QTest::newRow("two hundred") << qreal(200) << int(100) << qreal(200); QTest::newRow("two hundred") << qreal(200) << int(100) << qreal(200);
@@ -43,16 +44,18 @@ void DelegateSizeHelperTest::risingPercentage()
QFETCH(int, currentPercentageWidth); QFETCH(int, currentPercentageWidth);
QFETCH(qreal, currentWidth); QFETCH(qreal, currentWidth);
auto item = QQuickItem();
item.setWidth(parentWidth);
DelegateSizeHelper delegateSizeHelper; DelegateSizeHelper delegateSizeHelper;
delegateSizeHelper.setParentItem(&item);
delegateSizeHelper.setStartBreakpoint(100); delegateSizeHelper.setStartBreakpoint(100);
delegateSizeHelper.setEndBreakpoint(200); delegateSizeHelper.setEndBreakpoint(200);
delegateSizeHelper.setStartPercentWidth(0); delegateSizeHelper.setStartPercentWidth(0);
delegateSizeHelper.setEndPercentWidth(100); delegateSizeHelper.setEndPercentWidth(100);
delegateSizeHelper.setParentWidth(parentWidth); QCOMPARE(delegateSizeHelper.availablePercentageWidth(), currentPercentageWidth);
QCOMPARE(delegateSizeHelper.availableWidth(), currentWidth);
QCOMPARE(delegateSizeHelper.currentPercentageWidth(), currentPercentageWidth);
QCOMPARE(delegateSizeHelper.currentWidth(), currentWidth);
} }
void DelegateSizeHelperTest::fallingPercentage_data() void DelegateSizeHelperTest::fallingPercentage_data()
@@ -74,16 +77,18 @@ void DelegateSizeHelperTest::fallingPercentage()
QFETCH(int, currentPercentageWidth); QFETCH(int, currentPercentageWidth);
QFETCH(qreal, currentWidth); QFETCH(qreal, currentWidth);
auto item = QQuickItem();
item.setWidth(parentWidth);
DelegateSizeHelper delegateSizeHelper; DelegateSizeHelper delegateSizeHelper;
delegateSizeHelper.setParentItem(&item);
delegateSizeHelper.setStartBreakpoint(100); delegateSizeHelper.setStartBreakpoint(100);
delegateSizeHelper.setEndBreakpoint(200); delegateSizeHelper.setEndBreakpoint(200);
delegateSizeHelper.setStartPercentWidth(100); delegateSizeHelper.setStartPercentWidth(100);
delegateSizeHelper.setEndPercentWidth(0); delegateSizeHelper.setEndPercentWidth(0);
delegateSizeHelper.setParentWidth(parentWidth); QCOMPARE(delegateSizeHelper.availablePercentageWidth(), currentPercentageWidth);
QCOMPARE(delegateSizeHelper.availableWidth(), currentWidth);
QCOMPARE(delegateSizeHelper.currentPercentageWidth(), currentPercentageWidth);
QCOMPARE(delegateSizeHelper.currentWidth(), currentWidth);
} }
void DelegateSizeHelperTest::equalPercentage_data() void DelegateSizeHelperTest::equalPercentage_data()
@@ -105,16 +110,18 @@ void DelegateSizeHelperTest::equalPercentage()
QFETCH(int, currentPercentageWidth); QFETCH(int, currentPercentageWidth);
QFETCH(qreal, currentWidth); QFETCH(qreal, currentWidth);
auto item = QQuickItem();
item.setWidth(parentWidth);
DelegateSizeHelper delegateSizeHelper; DelegateSizeHelper delegateSizeHelper;
delegateSizeHelper.setParentItem(&item);
delegateSizeHelper.setStartBreakpoint(100); delegateSizeHelper.setStartBreakpoint(100);
delegateSizeHelper.setEndBreakpoint(200); delegateSizeHelper.setEndBreakpoint(200);
delegateSizeHelper.setStartPercentWidth(50); delegateSizeHelper.setStartPercentWidth(50);
delegateSizeHelper.setEndPercentWidth(50); delegateSizeHelper.setEndPercentWidth(50);
delegateSizeHelper.setParentWidth(parentWidth); QCOMPARE(delegateSizeHelper.availablePercentageWidth(), currentPercentageWidth);
QCOMPARE(delegateSizeHelper.availableWidth(), currentWidth);
QCOMPARE(delegateSizeHelper.currentPercentageWidth(), currentPercentageWidth);
QCOMPARE(delegateSizeHelper.currentWidth(), currentWidth);
} }
void DelegateSizeHelperTest::equalBreakpoint_data() void DelegateSizeHelperTest::equalBreakpoint_data()
@@ -124,9 +131,9 @@ void DelegateSizeHelperTest::equalBreakpoint_data()
QTest::addColumn<int>("currentPercentageWidth"); QTest::addColumn<int>("currentPercentageWidth");
QTest::addColumn<qreal>("currentWidth"); QTest::addColumn<qreal>("currentWidth");
QTest::newRow("start higher") << int(100) << int(0) << int(-1) << qreal(0); QTest::newRow("start higher") << int(100) << int(0) << int(100) << qreal(1000);
QTest::newRow("equal") << int(50) << int(50) << int(50) << qreal(500); QTest::newRow("equal") << int(50) << int(50) << int(50) << qreal(500);
QTest::newRow("end higher") << int(0) << int(100) << int(-1) << qreal(0); QTest::newRow("end higher") << int(0) << int(100) << int(100) << qreal(1000);
} }
/** /**
@@ -140,16 +147,18 @@ void DelegateSizeHelperTest::equalBreakpoint()
QFETCH(int, currentPercentageWidth); QFETCH(int, currentPercentageWidth);
QFETCH(qreal, currentWidth); QFETCH(qreal, currentWidth);
auto item = QQuickItem();
item.setWidth(1000);
DelegateSizeHelper delegateSizeHelper; DelegateSizeHelper delegateSizeHelper;
delegateSizeHelper.setParentItem(&item);
delegateSizeHelper.setStartBreakpoint(100); delegateSizeHelper.setStartBreakpoint(100);
delegateSizeHelper.setEndBreakpoint(100); delegateSizeHelper.setEndBreakpoint(100);
delegateSizeHelper.setStartPercentWidth(startPercentageWidth); delegateSizeHelper.setStartPercentWidth(startPercentageWidth);
delegateSizeHelper.setEndPercentWidth(endPercentageWidth); delegateSizeHelper.setEndPercentWidth(endPercentageWidth);
delegateSizeHelper.setParentWidth(1000); QCOMPARE(delegateSizeHelper.availablePercentageWidth(), currentPercentageWidth);
QCOMPARE(delegateSizeHelper.availableWidth(), currentWidth);
QCOMPARE(delegateSizeHelper.currentPercentageWidth(), currentPercentageWidth);
QCOMPARE(delegateSizeHelper.currentWidth(), currentWidth);
} }
QTEST_GUILESS_MAIN(DelegateSizeHelperTest) QTEST_GUILESS_MAIN(DelegateSizeHelperTest)

View File

@@ -7,6 +7,8 @@ if (NOT ANDROID AND NOT WIN32 AND NOT APPLE AND NOT NEOCHAT_FLATPAK AND NOT NEOC
add_subdirectory(purpose) add_subdirectory(purpose)
endif() endif()
add_subdirectory(libneochat)
add_library(neochat STATIC add_library(neochat STATIC
controller.cpp controller.cpp
controller.h controller.h
@@ -109,8 +111,6 @@ add_library(neochat STATIC
events/imagepackevent.h events/imagepackevent.h
models/reactionmodel.cpp models/reactionmodel.cpp
models/reactionmodel.h models/reactionmodel.h
delegatesizehelper.cpp
delegatesizehelper.h
models/livelocationsmodel.cpp models/livelocationsmodel.cpp
models/livelocationsmodel.h models/livelocationsmodel.h
models/locationsmodel.cpp models/locationsmodel.cpp
@@ -413,6 +413,7 @@ endif()
target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/models ${CMAKE_CURRENT_SOURCE_DIR}/enums) target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/models ${CMAKE_CURRENT_SOURCE_DIR}/enums)
target_link_libraries(neochat PRIVATE settingsplugin timelineplugin devtoolsplugin loginplugin chatbarplugin) target_link_libraries(neochat PRIVATE settingsplugin timelineplugin devtoolsplugin loginplugin chatbarplugin)
target_link_libraries(neochat PUBLIC target_link_libraries(neochat PUBLIC
LibNeoChat
Qt::Core Qt::Core
Qt::Quick Qt::Quick
Qt::Qml Qt::Qml

View File

@@ -8,7 +8,9 @@ import QtQuick.Controls as QQC2
import QtQuick.Layouts import QtQuick.Layouts
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
import org.kde.neochat import org.kde.neochat
import org.kde.neochat.libneochat as LibNeoChat
/** /**
* @brief A component for typing and sending chat messages. * @brief A component for typing and sending chat messages.
@@ -165,7 +167,7 @@ QQC2.Control {
} }
leftPadding: rightPadding leftPadding: rightPadding
rightPadding: (root.width - chatBarSizeHelper.currentWidth) / 2 rightPadding: (root.width - chatBarSizeHelper.availableWidth) / 2
topPadding: 0 topPadding: 0
bottomPadding: 0 bottomPadding: 0
@@ -353,15 +355,14 @@ QQC2.Control {
} }
} }
} }
DelegateSizeHelper { LibNeoChat.DelegateSizeHelper {
id: chatBarSizeHelper id: chatBarSizeHelper
parentItem: root
startBreakpoint: Kirigami.Units.gridUnit * 46 startBreakpoint: Kirigami.Units.gridUnit * 46
endBreakpoint: Kirigami.Units.gridUnit * 66 endBreakpoint: Kirigami.Units.gridUnit * 66
startPercentWidth: 100 startPercentWidth: 100
endPercentWidth: NeoChatConfig.compactLayout ? 100 : 85 endPercentWidth: NeoChatConfig.compactLayout ? 100 : 85
maxWidth: NeoChatConfig.compactLayout ? -1 : Kirigami.Units.gridUnit * 60 maxWidth: NeoChatConfig.compactLayout ? -1 : Kirigami.Units.gridUnit * 60
parentWidth: root.width
} }
Component { Component {

View File

@@ -0,0 +1,19 @@
# SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
# SPDX-License-Identifier: BSD-3-Clause
add_library(LibNeoChat STATIC)
target_sources(LibNeoChat PRIVATE
delegatesizehelper.cpp
)
ecm_add_qml_module(LibNeoChat GENERATE_PLUGIN_SOURCE
URI org.kde.neochat.libneochat
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/neochat/libneochat
)
generate_export_header(LibNeoChat BASE_NAME LibNeoChat)
target_link_libraries(LibNeoChat PUBLIC
Qt::Core
Qt::Quick
)

View File

@@ -12,20 +12,60 @@ DelegateSizeHelper::DelegateSizeHelper(QObject *parent)
{ {
} }
qreal DelegateSizeHelper::parentWidth() const QQuickItem *DelegateSizeHelper::parentItem() const
{ {
return m_parentWidth; return m_parentItem;
} }
void DelegateSizeHelper::setParentWidth(qreal parentWidth) void DelegateSizeHelper::setParentItem(QQuickItem *parentItem)
{ {
if (parentWidth == m_parentWidth) { if (parentItem == m_parentItem) {
return; return;
} }
m_parentWidth = parentWidth; m_parentItem = parentItem;
Q_EMIT parentWidthChanged();
Q_EMIT currentPercentageWidthChanged(); if (m_parentItem) {
Q_EMIT currentWidthChanged(); connect(m_parentItem, &QQuickItem::widthChanged, this, [this]() {
Q_EMIT availablePercentageWidthChanged();
Q_EMIT availableWidthChanged();
});
}
Q_EMIT parentItemChanged();
Q_EMIT availablePercentageWidthChanged();
Q_EMIT availableWidthChanged();
}
qreal DelegateSizeHelper::leftPadding() const
{
return m_leftPadding;
}
void DelegateSizeHelper::setLeftPadding(qreal leftPadding)
{
if (qFuzzyCompare(leftPadding, m_leftPadding)) {
return;
}
m_leftPadding = leftPadding;
Q_EMIT leftPaddingChanged();
Q_EMIT availablePercentageWidthChanged();
Q_EMIT availableWidthChanged();
}
qreal DelegateSizeHelper::rightPadding() const
{
return m_rightPadding;
}
void DelegateSizeHelper::setRightPadding(qreal rightPadding)
{
if (qFuzzyCompare(rightPadding, m_rightPadding)) {
return;
}
m_rightPadding = rightPadding;
Q_EMIT rightPaddingChanged();
Q_EMIT availablePercentageWidthChanged();
Q_EMIT availableWidthChanged();
} }
qreal DelegateSizeHelper::startBreakpoint() const qreal DelegateSizeHelper::startBreakpoint() const
@@ -86,7 +126,14 @@ void DelegateSizeHelper::setEndPercentWidth(int endPercentWidth)
qreal DelegateSizeHelper::maxWidth() const qreal DelegateSizeHelper::maxWidth() const
{ {
return m_maxWidth; if (m_maxWidth == std::nullopt) {
if (m_parentItem) {
return m_parentItem->width();
}
return 0.0;
}
return *m_maxWidth;
} }
void DelegateSizeHelper::setMaxWidth(qreal maxWidth) void DelegateSizeHelper::setMaxWidth(qreal maxWidth)
@@ -98,21 +145,23 @@ void DelegateSizeHelper::setMaxWidth(qreal maxWidth)
Q_EMIT maxWidthChanged(); Q_EMIT maxWidthChanged();
} }
int DelegateSizeHelper::calculateCurrentPercentageWidth() const qreal DelegateSizeHelper::maxAvailableWidth() const
{ {
// Don't do anything if m_parentWidth hasn't been set yet. if (!m_parentItem || qFuzzyCompare(m_parentItem->width(), 0)) {
if (m_parentWidth < 0) { return 0;
return -1;
} }
return std::max(m_parentItem->width() - m_leftPadding - m_rightPadding, 0.0);
}
int DelegateSizeHelper::calculateAvailablePercentageWidth() const
{
// Don't bother with calculations for a horizontal line. // Don't bother with calculations for a horizontal line.
if (m_startPercentWidth == m_endPercentWidth) { if (m_startPercentWidth == m_endPercentWidth) {
return m_startPercentWidth; return m_startPercentWidth;
} }
// Dividing by zero is a bad idea. // Dividing by zero is a bad idea.
if (m_startBreakpoint == m_endBreakpoint) { if (m_startBreakpoint == m_endBreakpoint || qFuzzyCompare(maxAvailableWidth(), 0)) {
qWarning() << "DelegateSizeHelper::calculateCurrentPercentageWidth() - m_startBreakpoint is equal to m_endBreakpoint this would lead to divide by " return 100;
"zero, aborting";
return -1;
} }
// Fit to y = mx + c // Fit to y = mx + c
@@ -124,32 +173,19 @@ int DelegateSizeHelper::calculateCurrentPercentageWidth() const
int maxPercentWidth = endPercentBigger ? m_endPercentWidth : m_startPercentWidth; int maxPercentWidth = endPercentBigger ? m_endPercentWidth : m_startPercentWidth;
int minPercentWidth = endPercentBigger ? m_startPercentWidth : m_endPercentWidth; int minPercentWidth = endPercentBigger ? m_startPercentWidth : m_endPercentWidth;
int calcPercentWidth = std::round(m * m_parentWidth + c); int calcPercentWidth = std::round(m * maxAvailableWidth() + c);
return std::clamp(calcPercentWidth, minPercentWidth, maxPercentWidth); return std::clamp(calcPercentWidth, minPercentWidth, maxPercentWidth);
} }
int DelegateSizeHelper::currentPercentageWidth() const int DelegateSizeHelper::availablePercentageWidth() const
{ {
return calculateCurrentPercentageWidth(); return calculateAvailablePercentageWidth();
} }
qreal DelegateSizeHelper::currentWidth() const qreal DelegateSizeHelper::availableWidth() const
{ {
if (m_parentWidth < 0) { qreal absoluteWidth = maxAvailableWidth() * availablePercentageWidth() * 0.01;
return 0.0; return std::round(std::min(absoluteWidth, maxWidth()));
}
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::round(absoluteWidth);
} else {
return std::round(std::min(absoluteWidth, m_maxWidth));
}
} }
#include "moc_delegatesizehelper.cpp" #include "moc_delegatesizehelper.cpp"

View File

@@ -5,6 +5,7 @@
#include <QObject> #include <QObject>
#include <QQmlEngine> #include <QQmlEngine>
#include <QQuickItem>
/** /**
* @class DelegateSizeHelper * @class DelegateSizeHelper
@@ -27,9 +28,25 @@ class DelegateSizeHelper : public QObject
QML_ELEMENT QML_ELEMENT
/** /**
* @brief The width of the component's parent. * @brief The parent item that defines the available content area.
*/ */
Q_PROPERTY(qreal parentWidth READ parentWidth WRITE setParentWidth NOTIFY parentWidthChanged) Q_PROPERTY(QQuickItem *parentItem READ parentItem WRITE setParentItem NOTIFY parentItemChanged)
/**
* @brief The amount of padding to be removed from the left of the available content area.
*
* The padding is removed before calculating the available width, i.e. max available width
* at 100% is equal to parent width minus padding.
*/
Q_PROPERTY(qreal leftPadding READ leftPadding WRITE setLeftPadding NOTIFY leftPaddingChanged)
/**
* @brief The amount of padding to be removed from the right of the available content area.
*
* The padding is removed before calculating the available width, i.e. max available width
* at 100% is equal to parent width minus padding.
*/
Q_PROPERTY(qreal rightPadding READ rightPadding WRITE setRightPadding NOTIFY rightPaddingChanged)
/** /**
* @brief The width (in px) when the width percentage should start to transition. * @brief The width (in px) when the width percentage should start to transition.
@@ -67,7 +84,7 @@ class DelegateSizeHelper : public QObject
* *
* @sa parentWidth, startBreakpoint, endBreakpoint * @sa parentWidth, startBreakpoint, endBreakpoint
*/ */
Q_PROPERTY(int currentPercentageWidth READ currentPercentageWidth NOTIFY currentPercentageWidthChanged) Q_PROPERTY(int availablePercentageWidth READ availablePercentageWidth NOTIFY availablePercentageWidthChanged)
/** /**
* @brief The width (in px) of the component at the current parentWidth. * @brief The width (in px) of the component at the current parentWidth.
@@ -76,50 +93,60 @@ class DelegateSizeHelper : public QObject
* *
* @sa parentWidth * @sa parentWidth
*/ */
Q_PROPERTY(qreal currentWidth READ currentWidth NOTIFY currentWidthChanged) Q_PROPERTY(qreal availableWidth READ availableWidth NOTIFY availableWidthChanged)
public: public:
explicit DelegateSizeHelper(QObject *parent = nullptr); explicit DelegateSizeHelper(QObject *parent = nullptr);
qreal parentWidth() const; QQuickItem *parentItem() const;
void setParentWidth(qreal parentWidth); void setParentItem(QQuickItem *parentItem);
qreal leftPadding() const;
void setLeftPadding(qreal leftPadding);
qreal rightPadding() const;
void setRightPadding(qreal rightPadding);
qreal startBreakpoint() const; qreal startBreakpoint() const;
void setStartBreakpoint(qreal startBreakpoint); void setStartBreakpoint(qreal startBreakpoint);
qreal endBreakpoint() const; qreal endBreakpoint() const;
void setEndBreakpoint(qreal endBreakpoint); void setEndBreakpoint(qreal endBreakpoint);
int startPercentWidth() const; int startPercentWidth() const;
void setStartPercentWidth(int startPercentWidth); void setStartPercentWidth(int startPercentWidth);
int endPercentWidth() const; int endPercentWidth() const;
void setEndPercentWidth(int endPercentWidth); void setEndPercentWidth(int endPercentWidth);
qreal maxWidth() const; qreal maxWidth() const;
void setMaxWidth(qreal maxWidth); void setMaxWidth(qreal maxWidth);
int currentPercentageWidth() const; qreal maxAvailableWidth() const;
qreal currentWidth() const; int availablePercentageWidth() const;
qreal availableWidth() const;
Q_SIGNALS: Q_SIGNALS:
void parentWidthChanged(); void parentItemChanged();
void leftPaddingChanged();
void rightPaddingChanged();
void startBreakpointChanged(); void startBreakpointChanged();
void endBreakpointChanged(); void endBreakpointChanged();
void startPercentWidthChanged(); void startPercentWidthChanged();
void endPercentWidthChanged(); void endPercentWidthChanged();
void maxWidthChanged(); void maxWidthChanged();
void currentPercentageWidthChanged(); void availablePercentageWidthChanged();
void currentWidthChanged(); void availableWidthChanged();
private: private:
qreal m_parentWidth = -1.0; QPointer<QQuickItem> m_parentItem;
qreal m_startBreakpoint;
qreal m_endBreakpoint;
int m_startPercentWidth;
int m_endPercentWidth;
qreal m_maxWidth = -1.0;
int calculateCurrentPercentageWidth() const; qreal m_leftPadding = 0.0;
qreal m_rightPadding = 0.0;
qreal m_startBreakpoint = 0.0;
qreal m_endBreakpoint = 0.0;
int m_startPercentWidth = 100;
int m_endPercentWidth = 85;
std::optional<qreal> m_maxWidth = std::nullopt;
int calculateAvailablePercentageWidth() const;
}; };

View File

@@ -10,6 +10,7 @@ import org.kde.kirigamiaddons.delegates as Delegates
import org.kde.kirigamiaddons.labs.components as Components import org.kde.kirigamiaddons.labs.components as Components
import org.kde.neochat import org.kde.neochat
import org.kde.neochat.libneochat as LibNeoChat
Item { Item {
id: root id: root
@@ -44,7 +45,7 @@ Item {
anchors.horizontalCenter: root.horizontalCenter anchors.horizontalCenter: root.horizontalCenter
anchors.verticalCenter: root.verticalCenter anchors.verticalCenter: root.verticalCenter
width: sizeHelper.currentWidth width: sizeHelper.availableWidth
highlighted: dropArea.containsDrag highlighted: dropArea.containsDrag
@@ -201,15 +202,14 @@ Item {
} }
} }
DelegateSizeHelper { LibNeoChat.DelegateSizeHelper {
id: sizeHelper id: sizeHelper
parentItem: root
startBreakpoint: Kirigami.Units.gridUnit * 46 startBreakpoint: Kirigami.Units.gridUnit * 46
endBreakpoint: Kirigami.Units.gridUnit * 66 endBreakpoint: Kirigami.Units.gridUnit * 66
startPercentWidth: 100 startPercentWidth: 100
endPercentWidth: 85 endPercentWidth: 85
maxWidth: Kirigami.Units.gridUnit * 60 maxWidth: Kirigami.Units.gridUnit * 60
parentWidth: root.treeView ? root.treeView.width : 0
} }
Component { Component {

View File

@@ -8,6 +8,7 @@ import QtQuick.Layouts
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
import org.kde.neochat import org.kde.neochat
import org.kde.neochat.libneochat as LibNeoChat
import org.kde.neochat.settings import org.kde.neochat.settings
ColumnLayout { ColumnLayout {
@@ -93,15 +94,14 @@ ColumnLayout {
onTextChanged: spaceChildSortFilterModel.filterText = text onTextChanged: spaceChildSortFilterModel.filterText = text
} }
} }
DelegateSizeHelper { LibNeoChat.DelegateSizeHelper {
id: sizeHelper id: sizeHelper
parentItem: root
startBreakpoint: Kirigami.Units.gridUnit * 46 startBreakpoint: Kirigami.Units.gridUnit * 46
endBreakpoint: Kirigami.Units.gridUnit * 66 endBreakpoint: Kirigami.Units.gridUnit * 66
startPercentWidth: 100 startPercentWidth: 100
endPercentWidth: 85 endPercentWidth: 85
maxWidth: Kirigami.Units.gridUnit * 60 maxWidth: Kirigami.Units.gridUnit * 60
parentWidth: root.width
} }
} }
Kirigami.Separator { Kirigami.Separator {

View File

@@ -90,4 +90,5 @@ ecm_add_qml_module(timeline GENERATE_PLUGIN_SOURCE
target_link_libraries(timeline PRIVATE target_link_libraries(timeline PRIVATE
Qt::Quick Qt::Quick
KF6::Kirigami KF6::Kirigami
LibNeoChat
) )

View File

@@ -10,6 +10,7 @@ import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.components as KirigamiComponents import org.kde.kirigamiaddons.components as KirigamiComponents
import org.kde.neochat import org.kde.neochat
import org.kde.neochat.libneochat as LibNeoChat
/** /**
* @brief The base delegate for all messages in the timeline. * @brief The base delegate for all messages in the timeline.
@@ -185,7 +186,7 @@ TimelineDelegate {
/** /**
* @brief The width available to the bubble content. * @brief The width available to the bubble content.
*/ */
property real contentMaxWidth: (root.isThreaded ? bubbleSizeHelper.parentWidth : bubbleSizeHelper.currentWidth) - bubble.leftPadding - bubble.rightPadding property real contentMaxWidth: (root.isThreaded ? bubbleSizeHelper.parentWidth : bubbleSizeHelper.availableWidth) - bubble.leftPadding - bubble.rightPadding
Message.room: root.room Message.room: root.room
Message.timeline: root.ListView.view Message.timeline: root.ListView.view
@@ -321,14 +322,14 @@ TimelineDelegate {
model: root.readMarkers model: root.readMarkers
} }
DelegateSizeHelper { LibNeoChat.DelegateSizeHelper {
id: bubbleSizeHelper id: bubbleSizeHelper
parentItem: mainContainer
leftPadding: avatar.anchors.leftMargin + (NeoChatConfig.showAvatarInTimeline ? avatar.width + bubble.anchors.leftMargin : 0)
startBreakpoint: Kirigami.Units.gridUnit * 25 startBreakpoint: Kirigami.Units.gridUnit * 25
endBreakpoint: Kirigami.Units.gridUnit * 40 endBreakpoint: Kirigami.Units.gridUnit * 40
startPercentWidth: root.alwaysFillWidth ? 100 : 90 startPercentWidth: root.alwaysFillWidth ? 100 : 90
endPercentWidth: root.alwaysFillWidth ? 100 : 60 endPercentWidth: root.alwaysFillWidth ? 100 : 60
parentWidth: mainContainer.availableWidth - (NeoChatConfig.showAvatarInTimeline ? avatar.width + bubble.anchors.leftMargin : 0)
} }
} }

View File

@@ -6,6 +6,17 @@
TimelineDelegate::TimelineDelegate(QQuickItem *parent) TimelineDelegate::TimelineDelegate(QQuickItem *parent)
: QQuickItem(parent) : QQuickItem(parent)
{ {
m_sizeHelper.setParentItem(this);
connect(&m_sizeHelper, &DelegateSizeHelper::leftPaddingChanged, this, [this]() {
Q_EMIT leftPaddingChanged();
resizeContent();
updatePolish();
});
connect(&m_sizeHelper, &DelegateSizeHelper::rightPaddingChanged, this, [this]() {
Q_EMIT rightPaddingChanged();
resizeContent();
updatePolish();
});
} }
QQuickItem *TimelineDelegate::contentItem() QQuickItem *TimelineDelegate::contentItem()
@@ -56,38 +67,22 @@ void TimelineDelegate::setAlwaysFillWidth(bool alwaysFillWidth)
qreal TimelineDelegate::leftPadding() qreal TimelineDelegate::leftPadding()
{ {
return m_leftPadding; return m_sizeHelper.leftPadding();
} }
void TimelineDelegate::setLeftPadding(qreal leftPadding) void TimelineDelegate::setLeftPadding(qreal leftPadding)
{ {
if (qFuzzyCompare(leftPadding, m_leftPadding)) { m_sizeHelper.setLeftPadding(leftPadding);
return;
}
m_leftPadding = leftPadding;
Q_EMIT leftPaddingChanged();
resizeContent();
updatePolish();
} }
qreal TimelineDelegate::rightPadding() qreal TimelineDelegate::rightPadding()
{ {
return m_rightPadding; return m_sizeHelper.rightPadding();
} }
void TimelineDelegate::setRightPadding(qreal rightPadding) void TimelineDelegate::setRightPadding(qreal rightPadding)
{ {
if (qFuzzyCompare(rightPadding, m_rightPadding)) { m_sizeHelper.setRightPadding(rightPadding);
return;
}
m_rightPadding = rightPadding;
Q_EMIT rightPaddingChanged();
resizeContent();
updatePolish();
} }
void TimelineDelegate::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) void TimelineDelegate::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
@@ -114,68 +109,27 @@ void TimelineDelegate::componentComplete()
void TimelineDelegate::setCurveValues() void TimelineDelegate::setCurveValues()
{ {
m_leftPadding = qreal(m_units->largeSpacing()); m_sizeHelper.setLeftPadding(qreal(m_units->largeSpacing()));
m_rightPadding = qreal(m_units->largeSpacing()); m_sizeHelper.setRightPadding(qreal(m_units->largeSpacing()));
m_startBreakpoint = qreal(m_units->gridUnit() * 46); m_sizeHelper.setStartBreakpoint(qreal(m_units->gridUnit() * 46));
m_endBreakpoint = qreal(m_units->gridUnit() * 66); m_sizeHelper.setEndBreakpoint(qreal(m_units->gridUnit() * 66));
m_maxWidth = qreal(m_units->gridUnit() * 60); m_sizeHelper.setMaxWidth(qreal(m_units->gridUnit() * 60));
resizeContent(); resizeContent();
} }
int TimelineDelegate::availablePercentageWidth() const
{
// 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 || qFuzzyCompare(width(), 0)) {
return 100;
}
// 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::round(m * maxAvailableWidth() + c);
return std::clamp(calcPercentWidth, minPercentWidth, maxPercentWidth);
}
qreal TimelineDelegate::maxAvailableWidth() const
{
if (qFuzzyCompare(width(), 0)) {
return 0;
}
return std::max(width() - m_leftPadding - m_rightPadding, 0.0);
}
qreal TimelineDelegate::availableWidth() const
{
if (m_alwaysFillWidth) {
return maxAvailableWidth();
}
qreal absoluteWidth = maxAvailableWidth() * availablePercentageWidth() * 0.01;
return std::round(std::min(absoluteWidth, m_maxWidth));
}
void TimelineDelegate::resizeContent() void TimelineDelegate::resizeContent()
{ {
if (m_contentItem == nullptr || !isComponentComplete()) { if (m_contentItem == nullptr || !isComponentComplete()) {
return; return;
} }
const auto leftPadding = m_leftPadding + (maxAvailableWidth() - availableWidth()) / 2; auto availableWidth = m_alwaysFillWidth ? m_sizeHelper.maxAvailableWidth() : m_sizeHelper.availableWidth();
const auto leftPadding = m_sizeHelper.leftPadding() + (m_sizeHelper.maxAvailableWidth() - availableWidth) / 2;
m_contentItem->setPosition(QPointF(leftPadding, 0)); m_contentItem->setPosition(QPointF(leftPadding, 0));
m_contentItem->setSize(QSizeF(availableWidth(), m_contentItem->implicitHeight())); m_contentItem->setSize(QSizeF(availableWidth, m_contentItem->implicitHeight()));
} }
void TimelineDelegate::updateImplicitHeight() void TimelineDelegate::updateImplicitHeight()

View File

@@ -8,6 +8,8 @@
#include <Kirigami/Platform/Units> #include <Kirigami/Platform/Units>
#include "delegatesizehelper.h"
/** /**
* @brief The base Item for all delegates in the timeline. * @brief The base Item for all delegates in the timeline.
* *
@@ -77,17 +79,8 @@ private:
Kirigami::Platform::Units *m_units = nullptr; Kirigami::Platform::Units *m_units = nullptr;
void setCurveValues(); void setCurveValues();
qreal m_leftPadding; DelegateSizeHelper m_sizeHelper;
qreal m_rightPadding;
qreal m_startBreakpoint;
qreal m_endBreakpoint;
int m_startPercentWidth = 100;
int m_endPercentWidth = 85;
qreal m_maxWidth;
int availablePercentageWidth() const;
qreal maxAvailableWidth() const;
qreal availableWidth() const;
bool m_alwaysFillWidth = false; bool m_alwaysFillWidth = false;
void resizeContent(); void resizeContent();