Files
qt6-migration-pipeline/Lts/Common/LtsTimeline/timeline_header_view.cpp
T

680 lines
20 KiB
C++
Executable File

#include "timeline_header_view.h"
#include "timeline_view.h"
#include <QGuiApplication>
#include <QMouseEvent>
#include <QPainter>
#include <QPolygon>
#include <QPoint>
#include <QRect>
#include <QPen>
#include <QBrush>
#include <QFont>
#include <QTime>
#include <QTimer>
#include <QDebug>
namespace LtsTimeline
{
QPixmap getMinLimitIndicator()
{
QPixmap pixmap(11, 11);
pixmap.fill(Qt::transparent);
QPolygon triangle;
triangle << QPoint(4, 5);
triangle << QPoint(10, 0);
triangle << QPoint(10, 10);
triangle << QPoint(4, 5);
QPainter painter(&pixmap);
painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
painter.setPen(QGuiApplication::palette().color(QPalette::Text));
painter.setBrush(QBrush(QGuiApplication::palette().color(QPalette::Text)));
painter.drawLine(1, 0, 1, 10);
painter.drawConvexPolygon(triangle);
return pixmap;
}
QPixmap getMaxLimitIndicator()
{
QPixmap pixmap(11, 11);
pixmap.fill(Qt::transparent);
QPolygon triangle;
triangle << QPoint(6, 5);
triangle << QPoint(0, 0);
triangle << QPoint(0, 10);
triangle << QPoint(6, 5);
QPainter painter(&pixmap);
painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
painter.setPen(QGuiApplication::palette().color(QPalette::Text));
painter.setBrush(QBrush(QGuiApplication::palette().color(QPalette::Text)));
painter.drawLine(10, 0, 10, 10);
painter.drawConvexPolygon(triangle);
return pixmap;
}
const QColor TimelineHeaderView::backgroundColor()
{
return QGuiApplication::palette().color(QPalette::Base);
}
const QColor TimelineHeaderView::highlightColor()
{
return QRgb(0x43b0e0);
}
const int TimelineHeaderView::s_limitHeight = 22;
TimelineHeaderView::TimelineHeaderView(QWidget *parent)
: QHeaderView(Qt::Horizontal, parent)
, m_mouseHandler(new TimelineHeaderViewMouseHandler(this))
, m_liveTimer(new QTimer(this))
, m_minLimitIndicator(getMinLimitIndicator())
, m_maxLimitIndicator(getMaxLimitIndicator())
{
const QColor color = QGuiApplication::palette().color(QPalette::Inactive, QPalette::Highlight);
setStyleSheet(QStringLiteral("QTreeView { background-color: %1 }").arg(color.name(QColor::HexRgb)));
try
{
m_timelineView = qobject_cast<TimelineView *>(parent);
updateSection(Section::eActuator);
updateSection(Section::eItems);
if (m_timelineView)
{
connect(m_timelineView, &TimelineView::currentTimeChanged, this, &TimelineHeaderView::onUpdateTicks);
connect(m_timelineView, &TimelineView::rangeChanged, this, &TimelineHeaderView::onUpdateTicks);
connect(m_timelineView, &TimelineView::zoomChanged, this, &TimelineHeaderView::onUpdateTicks);
}
else
qWarning() << "WARNING: main functionalities are inoperable without a TimelineView as parent.";
}
catch (...)
{
qWarning() << "WARNING: main functionalities are inoperable without a TimelineView as parent.";
}
setStretchLastSection(true);
setMinimumSectionSize(200);
setFixedHeight(50);
connect(this, &TimelineHeaderView::sectionResized, this, &TimelineHeaderView::onUpdateTicks);
connect(m_liveTimer, &QTimer::timeout, this, &TimelineHeaderView::onLiveTimeout);
m_liveIndicatorColor = QGuiApplication::palette().color(QPalette::Link);
}
TimelineHeaderView::~TimelineHeaderView()
{
m_timelineView = nullptr;
}
void TimelineHeaderView::setParent(QWidget *)
{
qWarning() << QStringLiteral("WARNING: setting parent after construction is not allowed.");
}
void TimelineHeaderView::setTrackLabel(const QString &trackLabel)
{
m_trackLabel = trackLabel;
updateSection(Section::eActuator);
}
void TimelineHeaderView::setDateTimeFormat(DateTimeFormat dateTimeFormat)
{
if (!m_isDateTimeFormatChangable)
return;
m_dateTimeFormat = dateTimeFormat;
for (Tick &majorTick : m_majorTicks)
majorTick.label = DateTime::toString(majorTick.time, m_dateTimeFormat);
for (Tick &mediumTick : m_mediumTicks)
mediumTick.label = DateTime::toString(mediumTick.time, m_dateTimeFormat);
for (Tick &minorTick : m_minorTicks)
minorTick.label = DateTime::toString(minorTick.time, m_dateTimeFormat);
updateSection(Section::eItems);
}
void TimelineHeaderView::setIndicatorScalingFactor(double factor)
{
if (factor == m_indicatorScalingFactor || factor <= 0.)
return;
m_indicatorScalingFactor = factor;
viewport()->update();
}
void TimelineHeaderView::setIndicatorTime(qint64 time)
{
if (time != -1 && m_timelineView)
if (m_timelineView->timelineModel())
if (m_timelineView->timelineModel()->isSnapEnabled())
time = m_timelineView->timelineModel()->snapTime(time);
if (time == m_indicatorTime)
return;
m_indicatorTime = time;
viewport()->update();
emit indicatorTimeChanged(m_indicatorTime);
}
void TimelineHeaderView::setIndicatorPosition(int position)
{
setIndicatorTime(toTime(position));
}
void TimelineHeaderView::setIndicatorVisible(bool isIndicatorVisible)
{
if (isIndicatorVisible == m_isIndicatorVisible)
return;
m_isIndicatorVisible = isIndicatorVisible;
m_timelineView->viewport()->update();
emit indicatorVisibilityChanged(m_isIndicatorVisible);
}
void TimelineHeaderView::setLiveIndicatorScalingFactor(double factor)
{
if (factor == m_liveIndicatorScalingFactor || factor <= 0.)
return;
m_liveIndicatorScalingFactor = factor;
viewport()->update();
}
void TimelineHeaderView::setLiveIndicatorColor(const QColor &color)
{
if (color == m_liveIndicatorColor)
return;
m_liveIndicatorColor = color;
viewport()->update();
}
void TimelineHeaderView::setLiveIndicatorTime(qint64 time)
{
if (time != -1 && m_timelineView)
if (m_timelineView->timelineModel())
if (m_timelineView->timelineModel()->isSnapEnabled())
time = m_timelineView->timelineModel()->snapTime(time);
if (time == m_liveIndicatorTime)
return;
m_liveIndicatorTime = time;
viewport()->update();
emit liveIndicatorTimeChanged(m_liveIndicatorTime);
}
void TimelineHeaderView::setLiveIndicatorPosition(int position)
{
setLiveIndicatorTime(toTime(position));
}
void TimelineHeaderView::setLiveIndicatorVisible(bool isLiveIndicatorVisible)
{
if (isLiveIndicatorVisible == m_isLiveIndicatorVisible)
return;
m_isLiveIndicatorVisible = isLiveIndicatorVisible;
viewport()->update();
m_timelineView->viewport()->update();
emit liveIndicatorVisibilityChanged(m_isLiveIndicatorVisible);
}
void TimelineHeaderView::startLiveTimer(qint64 startTime, int interval)
{
if (interval <= 0)
{
qCritical() << LOGFUNCTION << "invalid time interval:" << interval;
return;
}
if (m_liveTimer->isActive())
{
qWarning() << LOGFUNCTION << "timer is already active - restarting";
m_liveTimer->stop();
}
if (startTime < 0)
startTime = m_timelineView->currentTime();
m_liveTimerInterval = interval;
setLiveIndicatorTime(startTime);
setLiveIndicatorVisible(true);
m_liveTimer->setSingleShot(false);
m_liveTimer->start(m_liveTimerInterval);
}
void TimelineHeaderView::stopLiveTimer()
{
if (!m_liveTimer->isActive())
return;
qDebug() << LOGFUNCTION;
m_liveTimer->stop();
m_liveIndicatorTime = -1;
setLiveIndicatorVisible(false);
}
void TimelineHeaderView::setHoverTime(qint64 time)
{
if (!m_isHoverVisible)
return;
if (time < m_timelineView->currentTime() || m_timelineView->currentTime() + m_timelineView->zoomFactor() < time)
time = -1;
m_hoverTime = time;
m_hoverPosition = toPixel(m_hoverTime);
updateSection(Section::eItems);
emit hoverPositionChanged(m_hoverPosition);
}
void TimelineHeaderView::setHoverPosition(int position)
{
if (!m_isHoverVisible)
return;
m_hoverPosition = position;
m_hoverTime = toTime(m_hoverPosition);
updateSection(Section::eItems);
emit hoverPositionChanged(m_hoverPosition);
}
void TimelineHeaderView::setHoverVisible(bool visible)
{
if (visible == m_isHoverVisible)
return;
m_isHoverVisible = visible;
updateSection(Section::eItems);
m_timelineView->viewport()->update();
emit hoverVisibilityChanged(m_isHoverVisible);
}
void TimelineHeaderView::setHoverTimeVisible(bool visible)
{
if (visible == m_isHoverTimeVisible)
return;
m_isHoverTimeVisible = visible;
updateSection(Section::eItems);
m_timelineView->viewport()->update();
}
void TimelineHeaderView::setHoverDateTimeFormat(DateTimeFormat format)
{
if (format == m_hoverDateTimeFormat)
return;
m_hoverDateTimeFormat = format;
updateSection(Section::eItems);
m_timelineView->viewport()->update();
}
void TimelineHeaderView::setLimitsVisible(bool visible)
{
if (visible == m_isLimitsVisible)
return;
m_isLimitsVisible = visible;
if (m_isLimitsVisible)
setFixedHeight(70);
else
setFixedHeight(50);
viewport()->update();
}
void TimelineHeaderView::setLimitsDateTimeFormat(DateTimeFormat format)
{
if (format == m_limitsDateTimeFormat)
return;
m_limitsDateTimeFormat = format;
viewport()->update();
}
int TimelineHeaderView::toPixel(qint64 time) const
{
if (time == -1)
return -1;
return static_cast<int>(double(time - m_timelineView->currentTime()) / m_timelineView->zoomFactor() *
(geometry().width() - sectionPosition(Section::eItems)) + sectionPosition(Section::eItems));
}
qint64 TimelineHeaderView::toTime(int pixel) const
{
return m_timelineView->currentTime() +
(m_timelineView->zoomFactor() * (double(pixel - sectionPosition(Section::eItems)) /
(geometry().width() - sectionPosition(Section::eItems))));
}
void TimelineHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const
{
if (logicalIndex == Section::eActuator)
{
painter->save();
painter->setRenderHints(QPainter::TextAntialiasing | QPainter::HighQualityAntialiasing);
painter->setPen(backgroundColor());
painter->setBrush(QBrush(backgroundColor()));
painter->drawRect(rect);
painter->setPen(QPen(QGuiApplication::palette().color(QPalette::Text), 2));
painter->drawLine(rect.topRight(), QPoint(rect.right() - rect.height() / 2, rect.top() + rect.height() / 2));
painter->drawLine(QPoint(rect.right() - rect.height() / 2, rect.top() + rect.height() / 2), rect.bottomRight());
painter->setFont(m_font);
painter->drawText(rect, Qt::AlignCenter, m_trackLabel);
painter->restore();
return;
}
painter->save();
painter->setClipping(true);
painter->setClipRect(rect);
painter->setFont(m_font);
painter->setRenderHints(QPainter::TextAntialiasing | QPainter::HighQualityAntialiasing);
painter->setPen(backgroundColor());
painter->setBrush(backgroundColor());
painter->drawRect(rect);
int bottom = m_isLimitsVisible ? rect.bottom() - s_limitHeight : rect.bottom();
int height = (bottom - rect.top()) / 2;
for (const Tick &tick : qAsConst(m_majorTicks))
{
painter->setPen(QPen(QGuiApplication::palette().color(QPalette::Text), 1));
painter->drawText(QRect(tick.pos - 160 / 2, rect.top(), 160, height), Qt::AlignCenter, tick.label);
if (m_isLimitsVisible)
painter->drawLine(tick.pos, bottom, tick.pos, rect.bottom());
painter->setPen(QPen(QGuiApplication::palette().color(QPalette::Text), 1));
painter->drawLine(tick.pos, rect.top() + height, tick.pos, bottom);
}
painter->setPen(QPen(QGuiApplication::palette().color(QPalette::Text), 1));
for (const Tick &tick : qAsConst(m_mediumTicks))
painter->drawLine(tick.pos, bottom - static_cast<int>(double(height) * 2 / 3), tick.pos, bottom);
for (const Tick &tick : qAsConst(m_minorTicks))
painter->drawLine(tick.pos, bottom - static_cast<int>(double(height) / 3), tick.pos, bottom);
if (m_isLimitsVisible)
{
QString minLimitString = DateTime::toString(m_timelineView->m_currentTime, m_limitsDateTimeFormat);
QString maxLimitString = DateTime::toString(m_timelineView->m_currentTime + m_timelineView->m_zoomFactor, m_limitsDateTimeFormat);
int minLimitWidth = painter->fontMetrics().horizontalAdvance(minLimitString);
int maxLimitWidth = painter->fontMetrics().horizontalAdvance(maxLimitString);
painter->setPen(backgroundColor());
painter->setBrush(QBrush(backgroundColor()));
painter->drawRect(rect.left() + 4, geometry().bottom() - s_limitHeight, minLimitWidth + 11, 19);
painter->drawRect(rect.right() - maxLimitWidth - 18, geometry().bottom() - s_limitHeight, maxLimitWidth + 11, 19);
painter->setPen(QGuiApplication::palette().color(QPalette::Text));
painter->drawPixmap(rect.left() + 4, geometry().bottom() - 15, 11, 11, m_minLimitIndicator);
painter->drawPixmap(rect.right() - 15, geometry().bottom() - 15, 11, 11, m_maxLimitIndicator);
painter->drawText(
QRect(rect.left() + 18, geometry().bottom() - s_limitHeight, minLimitWidth, s_limitHeight - 1),
Qt::AlignLeft, minLimitString
);
painter->drawText(
QRect(rect.right() - maxLimitWidth - 18, geometry().bottom() - s_limitHeight, maxLimitWidth, s_limitHeight - 1),
Qt::AlignRight, maxLimitString
);
}
if (m_isHoverVisible)
{
if (m_hoverPosition != -1)
{
painter->setPen(highlightColor());
painter->drawLine(m_hoverPosition, rect.top(), m_hoverPosition, rect.bottom());
if (m_isHoverTimeVisible)
{
QString dateTimeString = DateTime::toString(m_hoverTime, m_hoverDateTimeFormat);
const int textWidth = painter->fontMetrics().horizontalAdvance(dateTimeString) + 2;
const int textHeight = m_isLimitsVisible ? s_limitHeight - 1 : 19;
QRect dateTimeRect(m_hoverPosition, geometry().bottom() - textHeight, textWidth, textHeight - 1);
if (dateTimeRect.right() >= geometry().right())
{
dateTimeRect.setLeft(m_hoverPosition - textWidth);
dateTimeRect.setWidth(textWidth);
}
painter->setBrush(QBrush(backgroundColor()));
painter->drawRect(dateTimeRect);
painter->drawText(dateTimeRect, Qt::AlignCenter, dateTimeString);
}
}
}
if (m_isIndicatorVisible)
{
int pos = toPixel(m_indicatorTime);
if (m_indicatorTime != -1 && (m_timelineView->currentTime() <= m_indicatorTime &&
m_indicatorTime <= m_timelineView->currentTime() + m_timelineView->zoomFactor()))
{
painter->setPen(highlightColor());
painter->setBrush(QBrush(highlightColor()));
int sideLength = double(m_isLimitsVisible ? rect.height() - height : rect.height()) * m_indicatorScalingFactor;
int top = bottom - static_cast<int>(0.866 * sideLength);
QPolygon polygon;
polygon << QPoint(pos, bottom);
polygon << QPoint(pos + sideLength / 2, top);
polygon << QPoint(pos - sideLength / 2, top);
polygon << QPoint(pos, bottom);
QString dateTimeString = DateTime::toString(m_indicatorTime, m_hoverDateTimeFormat);
const int textWidth = painter->fontMetrics().horizontalAdvance(dateTimeString);
painter->setClipRect(rect.adjusted(-textWidth / 2, 0, 0, 0));
painter->drawPolygon(polygon);
if (m_isLimitsVisible)
painter->drawLine(pos, bottom, pos, rect.bottom());
QRect dateTimeRect(pos - textWidth / 2, geometry().top(), textWidth + 2, 19);
painter->setBrush(QBrush(backgroundColor()));
painter->drawRect(dateTimeRect);
painter->drawText(dateTimeRect, Qt::AlignCenter, dateTimeString);
painter->drawLine(pos, geometry().top() + 19, pos, bottom);
}
}
if (m_isLiveIndicatorVisible)
{
int pos = toPixel(m_liveIndicatorTime);
if (pos != -1 && (m_timelineView->currentTime() <= m_liveIndicatorTime &&
m_liveIndicatorTime <= m_timelineView->currentTime() + m_timelineView->zoomFactor()))
{
painter->setPen(m_liveIndicatorColor);
painter->setBrush(QBrush(m_liveIndicatorColor));
int sideLength = double(m_isLimitsVisible ? rect.height() - height : rect.height()) * m_liveIndicatorScalingFactor;
int top = bottom - static_cast<int>(0.866 * sideLength);
QPolygon polygon;
polygon << QPoint(pos, bottom);
polygon << QPoint(pos + sideLength / 2, top);
polygon << QPoint(pos - sideLength / 2, top);
polygon << QPoint(pos, bottom);
QString dateTimeString = DateTime::toString(m_liveIndicatorTime, m_hoverDateTimeFormat);
const int textWidth = painter->fontMetrics().horizontalAdvance(dateTimeString);
painter->setClipRect(rect.adjusted(-textWidth / 2, 0, 0, 0));
painter->drawPolygon(polygon);
QRect dateTimeRect(pos - textWidth / 2, geometry().top(), textWidth + 2, 19);
if (dateTimeRect.right() > geometry().right() - 20)
dateTimeRect.adjust(-22, 0, -22, 0);
painter->setBrush(QBrush(backgroundColor()));
painter->drawRect(dateTimeRect);
painter->drawText(dateTimeRect, Qt::AlignCenter, dateTimeString);
}
}
painter->restore();
}
void TimelineHeaderView::updateMajorTicks()
{
m_majorTicks.clear();
if (m_timelineView->duration() <= 0)
return;
const int interval = Zoom::majorInterval(m_timelineView->zoomFactor());
if (interval == -1)
return;
qint64 time = m_timelineView->currentTime();
const int mod = time % interval;
if (mod)
time += interval - mod;
const qint64 end = m_timelineView->currentTime() + m_timelineView->zoomFactor();
while (time <= end)
{
QString label = DateTime::toString(time, m_dateTimeFormat);
Tick tick(toPixel(time), time, label);
m_majorTicks.append(tick);
time += interval;
}
}
void TimelineHeaderView::updateMediumTicks()
{
m_mediumTicks.clear();
if (m_timelineView->duration() <= 0)
return;
const int interval = Zoom::mediumInterval(m_timelineView->zoomFactor());
if (interval == -1)
return;
qint64 time = m_timelineView->currentTime();
const int mod = time % interval;
if (mod)
time += interval - mod;
const qint64 end = m_timelineView->currentTime() + m_timelineView->zoomFactor();
while (time <= end)
{
Tick tick(time);
auto it = std::find_if(m_majorTicks.begin(), m_majorTicks.end(), [tick](const Tick &other)
{
return tick.time == other.time;
});
if (it != m_majorTicks.end())
{
time += interval;
continue;
}
tick.pos = toPixel(tick.time);
tick.label = DateTime::toString(tick.time, m_dateTimeFormat);
m_mediumTicks.append(tick);
time += interval;
}
}
void TimelineHeaderView::updateMinorTicks()
{
m_minorTicks.clear();
if (m_timelineView->duration() <= 0)
return;
const int interval = Zoom::minorInterval(m_timelineView->zoomFactor());
if (interval == -1)
return;
qint64 time = m_timelineView->currentTime();
const int mod = time % interval;
if (mod)
time += interval - mod;
const qint64 end = m_timelineView->currentTime() + m_timelineView->zoomFactor();
while (time <= end)
{
Tick tick(time);
auto it = std::find_if(m_majorTicks.begin(), m_majorTicks.end(), [tick](const Tick &other)
{
return tick.time == other.time;
});
if (it != m_majorTicks.end())
{
time += interval;
continue;
}
it = std::find_if(m_mediumTicks.begin(), m_mediumTicks.end(), [tick](const Tick &other)
{
return tick.time == other.time;
});
if (it != m_mediumTicks.end())
{
time += interval;
continue;
}
tick.pos = toPixel(tick.time);
tick.label = DateTime::toString(tick.time, m_dateTimeFormat);
m_minorTicks.append(tick);
time += interval;
}
}
void TimelineHeaderView::onUpdateTicks()
{
updateMajorTicks();
updateMediumTicks();
updateMinorTicks();
if (m_isIndicatorVisible)
setIndicatorTime(m_indicatorTime);
if (m_isLiveIndicatorVisible)
setLiveIndicatorTime(m_liveIndicatorTime);
updateSection(Section::eItems);
emit ticksChanged(m_majorTicks);
}
void TimelineHeaderView::onLiveTimeout()
{
setLiveIndicatorTime(m_liveIndicatorTime + m_liveTimerInterval);
}
}