Convert TimelineDelegate to cpp
There are 2 main reason for doing this: 1. Because I can, I wanted to see if I could do it 2. It gets rid of the janky qml re parenting stuff so should be faster.
This commit is contained in:
@@ -44,7 +44,7 @@ QQC2.Control {
|
|||||||
leftPadding: 0
|
leftPadding: 0
|
||||||
rightPadding: 0
|
rightPadding: 0
|
||||||
|
|
||||||
x: delegate ? delegate.contentX + delegate.bubbleX : 0
|
x: delegate ? delegate.contentItem.x + delegate.bubbleX : 0
|
||||||
y: delegate ? delegate.mapToItem(parent, 0, 0).y + delegate.bubbleY - height + Kirigami.Units.smallSpacing : 0
|
y: delegate ? delegate.mapToItem(parent, 0, 0).y + delegate.bubbleY - height + Kirigami.Units.smallSpacing : 0
|
||||||
width: delegate ? delegate.bubbleWidth : Kirigami.Units.gridUnit * 4
|
width: delegate ? delegate.bubbleWidth : Kirigami.Units.gridUnit * 4
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ QQC2.ScrollView {
|
|||||||
roleValue: MediaMessageFilterModel.Image
|
roleValue: MediaMessageFilterModel.Image
|
||||||
delegate: MessageDelegate {
|
delegate: MessageDelegate {
|
||||||
alwaysShowAuthor: true
|
alwaysShowAuthor: true
|
||||||
alwaysMaxWidth: true
|
alwaysFillWidth: true
|
||||||
cardBackground: false
|
cardBackground: false
|
||||||
room: root.currentRoom
|
room: root.currentRoom
|
||||||
}
|
}
|
||||||
@@ -61,7 +61,7 @@ QQC2.ScrollView {
|
|||||||
roleValue: MediaMessageFilterModel.Video
|
roleValue: MediaMessageFilterModel.Video
|
||||||
delegate: MessageDelegate {
|
delegate: MessageDelegate {
|
||||||
alwaysShowAuthor: true
|
alwaysShowAuthor: true
|
||||||
alwaysMaxWidth: true
|
alwaysFillWidth: true
|
||||||
cardBackground: false
|
cardBackground: false
|
||||||
room: root.currentRoom
|
room: root.currentRoom
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ qt_add_qml_module(timeline
|
|||||||
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/neochat/timeline
|
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/neochat/timeline
|
||||||
QML_FILES
|
QML_FILES
|
||||||
EventDelegate.qml
|
EventDelegate.qml
|
||||||
TimelineDelegate.qml
|
|
||||||
HiddenDelegate.qml
|
HiddenDelegate.qml
|
||||||
MessageDelegate.qml
|
MessageDelegate.qml
|
||||||
LoadingDelegate.qml
|
LoadingDelegate.qml
|
||||||
@@ -45,6 +44,9 @@ qt_add_qml_module(timeline
|
|||||||
StateComponent.qml
|
StateComponent.qml
|
||||||
TextComponent.qml
|
TextComponent.qml
|
||||||
VideoComponent.qml
|
VideoComponent.qml
|
||||||
|
SOURCES
|
||||||
|
timelinedelegate.cpp
|
||||||
|
timelinedelegate.h
|
||||||
RESOURCES
|
RESOURCES
|
||||||
images/bike.svg
|
images/bike.svg
|
||||||
images/bus.svg
|
images/bus.svg
|
||||||
@@ -72,3 +74,8 @@ qt_add_qml_module(timeline
|
|||||||
images/wait.svg
|
images/wait.svg
|
||||||
images/walk.svg
|
images/walk.svg
|
||||||
)
|
)
|
||||||
|
|
||||||
|
target_link_libraries(timeline PRIVATE
|
||||||
|
Qt::Quick
|
||||||
|
KF6::Kirigami
|
||||||
|
)
|
||||||
|
|||||||
@@ -41,6 +41,11 @@ TimelineDelegate {
|
|||||||
*/
|
*/
|
||||||
required property var author
|
required property var author
|
||||||
|
|
||||||
|
width: parent?.width
|
||||||
|
rightPadding: Config.compactLayout && root.ListView.view.width >= Kirigami.Units.gridUnit * 20 ? Kirigami.Units.gridUnit * 2 + Kirigami.Units.largeSpacing : Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
|
alwaysFillWidth: Config.compactLayout
|
||||||
|
|
||||||
contentItem: QQC2.Control {
|
contentItem: QQC2.Control {
|
||||||
id: contentControl
|
id: contentControl
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,16 @@ import QtQuick
|
|||||||
|
|
||||||
import org.kde.kirigami as Kirigami
|
import org.kde.kirigami as Kirigami
|
||||||
|
|
||||||
|
import org.kde.neochat
|
||||||
|
|
||||||
TimelineDelegate {
|
TimelineDelegate {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
width: parent?.width
|
||||||
|
rightPadding: Config.compactLayout && root.ListView.view.width >= Kirigami.Units.gridUnit * 20 ? Kirigami.Units.gridUnit * 2 + Kirigami.Units.largeSpacing : Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
|
alwaysFillWidth: Config.compactLayout
|
||||||
|
|
||||||
contentItem: Kirigami.PlaceholderMessage {
|
contentItem: Kirigami.PlaceholderMessage {
|
||||||
text: i18n("Loading…")
|
text: i18n("Loading…")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -202,11 +202,6 @@ TimelineDelegate {
|
|||||||
*/
|
*/
|
||||||
property bool cardBackground: true
|
property bool cardBackground: true
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Whether the delegate should always stretch to the maximum availabel width.
|
|
||||||
*/
|
|
||||||
property bool alwaysMaxWidth: false
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Whether the message should be highlighted.
|
* @brief Whether the message should be highlighted.
|
||||||
*/
|
*/
|
||||||
@@ -241,6 +236,11 @@ TimelineDelegate {
|
|||||||
*/
|
*/
|
||||||
property real contentMaxWidth: bubbleSizeHelper.currentWidth - bubble.leftPadding - bubble.rightPadding
|
property real contentMaxWidth: bubbleSizeHelper.currentWidth - bubble.leftPadding - bubble.rightPadding
|
||||||
|
|
||||||
|
width: parent?.width
|
||||||
|
rightPadding: Config.compactLayout && root.ListView.view.width >= Kirigami.Units.gridUnit * 20 ? Kirigami.Units.gridUnit * 2 + Kirigami.Units.largeSpacing : Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
|
alwaysFillWidth: Config.compactLayout
|
||||||
|
|
||||||
contentItem: ColumnLayout {
|
contentItem: ColumnLayout {
|
||||||
spacing: Kirigami.Units.smallSpacing
|
spacing: Kirigami.Units.smallSpacing
|
||||||
|
|
||||||
@@ -249,7 +249,7 @@ TimelineDelegate {
|
|||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
visible: root.showSection
|
visible: root.showSection
|
||||||
labelText: root.section
|
labelText: root.section
|
||||||
colorSet: Config.compactLayout || root.alwaysMaxWidth ? Kirigami.Theme.View : Kirigami.Theme.Window
|
colorSet: Config.compactLayout || root.alwaysFillWidth ? Kirigami.Theme.View : Kirigami.Theme.Window
|
||||||
}
|
}
|
||||||
QQC2.ItemDelegate {
|
QQC2.ItemDelegate {
|
||||||
id: mainContainer
|
id: mainContainer
|
||||||
@@ -347,7 +347,7 @@ TimelineDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
visible: mainContainer.hovered && (Config.compactLayout || root.alwaysMaxWidth)
|
visible: mainContainer.hovered && (Config.compactLayout || root.alwaysFillWidth)
|
||||||
color: Kirigami.ColorUtils.tintWithAlpha(Kirigami.Theme.backgroundColor, Kirigami.Theme.highlightColor, 0.15)
|
color: Kirigami.ColorUtils.tintWithAlpha(Kirigami.Theme.backgroundColor, Kirigami.Theme.highlightColor, 0.15)
|
||||||
radius: Kirigami.Units.cornerRadius
|
radius: Kirigami.Units.cornerRadius
|
||||||
}
|
}
|
||||||
@@ -387,8 +387,8 @@ TimelineDelegate {
|
|||||||
id: bubbleSizeHelper
|
id: bubbleSizeHelper
|
||||||
startBreakpoint: Kirigami.Units.gridUnit * 25
|
startBreakpoint: Kirigami.Units.gridUnit * 25
|
||||||
endBreakpoint: Kirigami.Units.gridUnit * 40
|
endBreakpoint: Kirigami.Units.gridUnit * 40
|
||||||
startPercentWidth: Config.compactLayout || root.alwaysMaxWidth ? 100 : 90
|
startPercentWidth: root.alwaysFillWidth ? 100 : 90
|
||||||
endPercentWidth: Config.compactLayout || root.alwaysMaxWidth ? 100 : 60
|
endPercentWidth: root.alwaysFillWidth ? 100 : 60
|
||||||
|
|
||||||
parentWidth: mainContainer.availableWidth - (Config.showAvatarInTimeline ? avatar.width + bubble.anchors.leftMargin : 0)
|
parentWidth: mainContainer.availableWidth - (Config.showAvatarInTimeline ? avatar.width + bubble.anchors.leftMargin : 0)
|
||||||
}
|
}
|
||||||
@@ -411,7 +411,7 @@ TimelineDelegate {
|
|||||||
/**
|
/**
|
||||||
* @brief Whether local user messages should be aligned right.
|
* @brief Whether local user messages should be aligned right.
|
||||||
*/
|
*/
|
||||||
property bool showUserMessageOnRight: Config.showLocalMessagesOnRight && root.author.isLocalUser && !Config.compactLayout && !root.alwaysMaxWidth
|
property bool showUserMessageOnRight: Config.showLocalMessagesOnRight && root.author.isLocalUser && !Config.compactLayout && !root.alwaysFillWidth
|
||||||
|
|
||||||
function showMessageMenu() {
|
function showMessageMenu() {
|
||||||
RoomManager.viewEventMenu(root.eventId, root.room, root.selectedText);
|
RoomManager.viewEventMenu(root.eventId, root.room, root.selectedText);
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import QtQuick.Layouts
|
|||||||
import Qt.labs.qmlmodels
|
import Qt.labs.qmlmodels
|
||||||
import org.kde.kirigami as Kirigami
|
import org.kde.kirigami as Kirigami
|
||||||
|
|
||||||
|
import org.kde.neochat
|
||||||
|
|
||||||
TimelineDelegate {
|
TimelineDelegate {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
@@ -16,6 +18,11 @@ TimelineDelegate {
|
|||||||
temporaryHighlightTimer.start();
|
temporaryHighlightTimer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
width: parent?.width
|
||||||
|
rightPadding: Config.compactLayout && root.ListView.view.width >= Kirigami.Units.gridUnit * 20 ? Kirigami.Units.gridUnit * 2 + Kirigami.Units.largeSpacing : Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
|
alwaysFillWidth: Config.compactLayout
|
||||||
|
|
||||||
contentItem: QQC2.ItemDelegate {
|
contentItem: QQC2.ItemDelegate {
|
||||||
padding: Kirigami.Units.largeSpacing
|
padding: Kirigami.Units.largeSpacing
|
||||||
topInset: Kirigami.Units.largeSpacing
|
topInset: Kirigami.Units.largeSpacing
|
||||||
|
|||||||
@@ -73,6 +73,11 @@ TimelineDelegate {
|
|||||||
*/
|
*/
|
||||||
property bool folded: true
|
property bool folded: true
|
||||||
|
|
||||||
|
width: parent?.width
|
||||||
|
rightPadding: Config.compactLayout && root.ListView.view.width >= Kirigami.Units.gridUnit * 20 ? Kirigami.Units.gridUnit * 2 + Kirigami.Units.largeSpacing : Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
|
alwaysFillWidth: Config.compactLayout
|
||||||
|
|
||||||
contentItem: ColumnLayout {
|
contentItem: ColumnLayout {
|
||||||
SectionDelegate {
|
SectionDelegate {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|||||||
@@ -1,102 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2022 James Graham <james.h.graham@protonmail.com>
|
|
||||||
// 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
|
|
||||||
|
|
||||||
import org.kde.neochat
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The base Item for all delegates in the timeline.
|
|
||||||
*
|
|
||||||
* This component handles the placing of the main content for a delegate in the
|
|
||||||
* timeline. The component is designed for all delegates, positioning them in the
|
|
||||||
* timeline with variable padding depending on the window width.
|
|
||||||
*
|
|
||||||
* This component also supports always setting the delegate to fill the available
|
|
||||||
* width in the timeline, e.g. in compact mode.
|
|
||||||
*/
|
|
||||||
Item {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The Item representing the delegate's main content.
|
|
||||||
*/
|
|
||||||
property Item contentItem
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The x position of the content item.
|
|
||||||
*
|
|
||||||
* @note Used for positioning the hover actions.
|
|
||||||
*/
|
|
||||||
property real contentX: contentItemParent.x
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Whether the delegate should always stretch to the maximum available width.
|
|
||||||
*/
|
|
||||||
property bool alwaysMaxWidth: false
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The padding to the left of the content.
|
|
||||||
*/
|
|
||||||
property real leftPadding: Kirigami.Units.largeSpacing
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The padding to the right of the content.
|
|
||||||
*/
|
|
||||||
property real rightPadding: Config.compactLayout && root.ListView.view.width >= Kirigami.Units.gridUnit * 20 ? Kirigami.Units.gridUnit * 2 + Kirigami.Units.largeSpacing : Kirigami.Units.largeSpacing
|
|
||||||
|
|
||||||
width: parent?.width
|
|
||||||
implicitHeight: contentItemParent.implicitHeight
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: contentItemParent
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.leftMargin: state === "alignLeft" ? Kirigami.Units.largeSpacing : 0
|
|
||||||
|
|
||||||
state: Config.compactLayout || root.alwaysMaxWidth ? "alignLeft" : "alignCenter"
|
|
||||||
// Align left when in compact mode and center when using bubbles
|
|
||||||
states: [
|
|
||||||
State {
|
|
||||||
name: "alignLeft"
|
|
||||||
AnchorChanges {
|
|
||||||
target: contentItemParent
|
|
||||||
anchors.horizontalCenter: undefined
|
|
||||||
anchors.left: parent ? parent.left : undefined
|
|
||||||
}
|
|
||||||
},
|
|
||||||
State {
|
|
||||||
name: "alignCenter"
|
|
||||||
AnchorChanges {
|
|
||||||
target: contentItemParent
|
|
||||||
anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined
|
|
||||||
anchors.left: undefined
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
width: (Config.compactLayout || root.alwaysMaxWidth ? root.width : delegateSizeHelper.currentWidth) - root.leftPadding - root.rightPadding
|
|
||||||
implicitHeight: root.contentItem?.implicitHeight ?? 0
|
|
||||||
}
|
|
||||||
|
|
||||||
DelegateSizeHelper {
|
|
||||||
id: delegateSizeHelper
|
|
||||||
startBreakpoint: Kirigami.Units.gridUnit * 46
|
|
||||||
endBreakpoint: Kirigami.Units.gridUnit * 66
|
|
||||||
startPercentWidth: 100
|
|
||||||
endPercentWidth: 85
|
|
||||||
maxWidth: Kirigami.Units.gridUnit * 60
|
|
||||||
|
|
||||||
parentWidth: root.width
|
|
||||||
}
|
|
||||||
|
|
||||||
onContentItemChanged: {
|
|
||||||
if (!contentItem) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
contentItem.parent = contentItemParent;
|
|
||||||
contentItem.anchors.fill = contentItem.parent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -17,6 +17,11 @@ TimelineDelegate {
|
|||||||
*/
|
*/
|
||||||
required property NeoChatRoom room
|
required property NeoChatRoom room
|
||||||
|
|
||||||
|
width: parent?.width
|
||||||
|
rightPadding: Config.compactLayout && root.ListView.view.width >= Kirigami.Units.gridUnit * 20 ? Kirigami.Units.gridUnit * 2 + Kirigami.Units.largeSpacing : Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
|
alwaysFillWidth: Config.compactLayout
|
||||||
|
|
||||||
contentItem: ColumnLayout {
|
contentItem: ColumnLayout {
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.topMargin: Kirigami.Units.largeSpacing
|
Layout.topMargin: Kirigami.Units.largeSpacing
|
||||||
|
|||||||
189
src/timeline/timelinedelegate.cpp
Normal file
189
src/timeline/timelinedelegate.cpp
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||||
|
|
||||||
|
#include "timelinedelegate.h"
|
||||||
|
|
||||||
|
TimelineDelegate::TimelineDelegate(QQuickItem *parent)
|
||||||
|
: QQuickItem(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QQuickItem *TimelineDelegate::contentItem()
|
||||||
|
{
|
||||||
|
return m_contentItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimelineDelegate::setContentItem(QQuickItem *item)
|
||||||
|
{
|
||||||
|
if (m_contentItem == item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_contentItem) {
|
||||||
|
disconnect(m_contentItem, &QQuickItem::implicitHeightChanged, this, &TimelineDelegate::updateImplicitHeight);
|
||||||
|
m_contentItem->setParentItem(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_contentItem = item;
|
||||||
|
|
||||||
|
if (m_contentItem) {
|
||||||
|
m_contentItem->setParentItem(this);
|
||||||
|
connect(m_contentItem, &QQuickItem::implicitHeightChanged, this, &TimelineDelegate::updateImplicitHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_EMIT contentItemChanged();
|
||||||
|
|
||||||
|
updateImplicitHeight();
|
||||||
|
resizeContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TimelineDelegate::alwaysFillWidth()
|
||||||
|
{
|
||||||
|
return m_alwaysFillWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimelineDelegate::setAlwaysFillWidth(bool alwaysFillWidth)
|
||||||
|
{
|
||||||
|
if (alwaysFillWidth == m_alwaysFillWidth) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_alwaysFillWidth = alwaysFillWidth;
|
||||||
|
Q_EMIT alwaysFillWidthChanged();
|
||||||
|
|
||||||
|
resizeContent();
|
||||||
|
updatePolish();
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal TimelineDelegate::leftPadding()
|
||||||
|
{
|
||||||
|
return m_leftPadding;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimelineDelegate::setLeftPadding(qreal leftPadding)
|
||||||
|
{
|
||||||
|
if (qFuzzyCompare(leftPadding, m_leftPadding)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_leftPadding = leftPadding;
|
||||||
|
Q_EMIT leftPaddingChanged();
|
||||||
|
|
||||||
|
resizeContent();
|
||||||
|
updatePolish();
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal TimelineDelegate::rightPadding()
|
||||||
|
{
|
||||||
|
return m_rightPadding;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimelineDelegate::setRightPadding(qreal rightPadding)
|
||||||
|
{
|
||||||
|
if (qFuzzyCompare(rightPadding, m_rightPadding)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_rightPadding = rightPadding;
|
||||||
|
Q_EMIT rightPaddingChanged();
|
||||||
|
|
||||||
|
resizeContent();
|
||||||
|
updatePolish();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimelineDelegate::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
|
||||||
|
{
|
||||||
|
if (newGeometry == oldGeometry) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QQuickItem::geometryChange(newGeometry, oldGeometry);
|
||||||
|
resizeContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimelineDelegate::componentComplete()
|
||||||
|
{
|
||||||
|
QQuickItem::componentComplete();
|
||||||
|
|
||||||
|
auto engine = qmlEngine(this);
|
||||||
|
Q_ASSERT(engine);
|
||||||
|
m_units = engine->singletonInstance<Kirigami::Platform::Units *>("org.kde.kirigami.platform", "Units");
|
||||||
|
Q_ASSERT(m_units);
|
||||||
|
setCurveValues();
|
||||||
|
connect(m_units, &Kirigami::Platform::Units::gridUnitChanged, this, &TimelineDelegate::setCurveValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimelineDelegate::setCurveValues()
|
||||||
|
{
|
||||||
|
m_leftPadding = qreal(m_units->largeSpacing());
|
||||||
|
m_rightPadding = qreal(m_units->largeSpacing());
|
||||||
|
|
||||||
|
m_startBreakpoint = qreal(m_units->gridUnit() * 46);
|
||||||
|
m_endBreakpoint = qreal(m_units->gridUnit() * 66);
|
||||||
|
m_maxWidth = qreal(m_units->gridUnit() * 60);
|
||||||
|
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
if (m_contentItem == nullptr || !isComponentComplete()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto leftPadding = m_leftPadding + (maxAvailableWidth() - availableWidth()) / 2;
|
||||||
|
m_contentItem->setPosition(QPointF(leftPadding, 0));
|
||||||
|
m_contentItem->setSize(QSizeF(availableWidth(), m_contentItem->implicitHeight()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimelineDelegate::updateImplicitHeight()
|
||||||
|
{
|
||||||
|
if (m_contentItem == nullptr) {
|
||||||
|
setImplicitHeight(0);
|
||||||
|
}
|
||||||
|
setImplicitHeight(m_contentItem->implicitHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "moc_timelinedelegate.cpp"
|
||||||
99
src/timeline/timelinedelegate.h
Normal file
99
src/timeline/timelinedelegate.h
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||||
|
|
||||||
|
#ifndef TIMELINEDELEGATE_H
|
||||||
|
#define TIMELINEDELEGATE_H
|
||||||
|
|
||||||
|
#include <QQuickItem>
|
||||||
|
|
||||||
|
#include <Kirigami/Platform/Units>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The base Item for all delegates in the timeline.
|
||||||
|
*
|
||||||
|
* This component handles the placing of the main content for a delegate in the
|
||||||
|
* timeline. The component is designed for all delegates, positioning them in the
|
||||||
|
* timeline with variable padding depending on the window width.
|
||||||
|
*
|
||||||
|
* This component also supports always setting the delegate to fill the available
|
||||||
|
* width in the timeline, e.g. in compact mode.
|
||||||
|
*/
|
||||||
|
class TimelineDelegate : public QQuickItem
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QML_ELEMENT
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This property holds the visual content Item.
|
||||||
|
*
|
||||||
|
* It will be resized both in width and height with the layout resizing.
|
||||||
|
* Its height will be resized to still have room for the heder and footer
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(QQuickItem *contentItem READ contentItem WRITE setContentItem NOTIFY contentItemChanged FINAL)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Whether the contentItem should fill all available width regardless of how wide the delegate is.
|
||||||
|
*
|
||||||
|
* The left and right padding values will still be respected.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(bool alwaysFillWidth READ alwaysFillWidth WRITE setAlwaysFillWidth NOTIFY alwaysFillWidthChanged FINAL)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The minimum padding to the left of the content.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(qreal leftPadding READ leftPadding WRITE setLeftPadding NOTIFY leftPaddingChanged FINAL)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The minimum padding to the right of the content.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(qreal rightPadding READ rightPadding WRITE setRightPadding NOTIFY rightPaddingChanged FINAL)
|
||||||
|
|
||||||
|
public:
|
||||||
|
TimelineDelegate(QQuickItem *parent = nullptr);
|
||||||
|
|
||||||
|
[[nodiscard]] QQuickItem *contentItem();
|
||||||
|
void setContentItem(QQuickItem *item);
|
||||||
|
|
||||||
|
[[nodiscard]] bool alwaysFillWidth();
|
||||||
|
void setAlwaysFillWidth(bool alwaysFillWidth);
|
||||||
|
|
||||||
|
[[nodiscard]] qreal leftPadding();
|
||||||
|
void setLeftPadding(qreal leftPadding);
|
||||||
|
|
||||||
|
[[nodiscard]] qreal rightPadding();
|
||||||
|
void setRightPadding(qreal rightPadding);
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void contentItemChanged();
|
||||||
|
void alwaysFillWidthChanged();
|
||||||
|
void leftPaddingChanged();
|
||||||
|
void rightPaddingChanged();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
|
||||||
|
void componentComplete() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Kirigami::Platform::Units *m_units = nullptr;
|
||||||
|
void setCurveValues();
|
||||||
|
|
||||||
|
qreal m_leftPadding;
|
||||||
|
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;
|
||||||
|
|
||||||
|
void resizeContent();
|
||||||
|
void updateImplicitHeight();
|
||||||
|
|
||||||
|
QPointer<QQuickItem> m_contentItem;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user