File indexing completed on 2024-12-22 04:14:44

0001 /*
0002  *  SPDX-FileCopyrightText: 2015 Dmitry Kazakov <dimula73@gmail.com>
0003  *  SPDX-FileCopyrightText: 2020 Emmet O 'Neill <emmetoneill.pdx@gmail.com>
0004  *  SPDX-FileCopyrightText: 2020 Eoin O 'Neill <eoinoneill1991@gmail.com>
0005  *
0006  *  SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #include "KisAnimTimelineLayersHeader.h"
0010 
0011 #include "kis_debug.h"
0012 #include "kis_icon_utils.h"
0013 #include "kis_global.h"
0014 
0015 #include <QPainter>
0016 #include <QHelpEvent>
0017 #include <QToolTip>
0018 
0019 #include "KisAnimTimelineFramesModel.h"
0020 #include "KisAnimTimelineColors.h"
0021 
0022 
0023 struct KisAnimTimelineLayersHeader::Private
0024 {
0025     Private(KisAnimTimelineLayersHeader *_q) : q(_q) {}
0026 
0027     KisAnimTimelineLayersHeader *q;
0028 
0029     int numIcons(int logicalIndex) const;
0030     int iconSectionWidth(int layerIndex) const;
0031     QPoint getSectionLocalPosition(int layerIndex, QPoint widgetPosition) const;
0032     QRect getSectionRect(int layerIndex) const;
0033     QRect propertyIconRect(int logicalIndex, int iconIndex) const;
0034     int propertyIconAt(int logicalIndex, const QPoint &pt);
0035 
0036     KisAnimTimelineFramesModel::Property* getPropertyAt(KisAnimTimelineFramesModel::PropertyList &props, int index);
0037 };
0038 
0039 
0040 KisAnimTimelineLayersHeader::KisAnimTimelineLayersHeader(QWidget *parent)
0041    : QHeaderView(Qt::Vertical, parent),
0042      m_d(new Private(this))
0043 {
0044 }
0045 
0046 KisAnimTimelineLayersHeader::~KisAnimTimelineLayersHeader()
0047 {
0048 }
0049 
0050 void KisAnimTimelineLayersHeader::paintSection(QPainter *painter, const QRect &areaRect, int layerIndex) const
0051 {
0052     QRect remainingArea = areaRect;
0053 
0054     {   // Paint background..
0055         QColor bgFillColor = palette().color(QPalette::Base);
0056 
0057         QVariant variant = model()->headerData(layerIndex, orientation(), Qt::BackgroundRole);
0058         if (variant.canConvert<QBrush>()) {
0059             QBrush brush = qvariant_cast<QBrush>(variant);
0060             painter->setBrush(brush);
0061             painter->setPen(Qt::NoPen);
0062             painter->drawRect(areaRect);
0063 
0064             bgFillColor = brush.color();
0065         }
0066 
0067         QColor rimlight = bgFillColor.lighter(115);
0068         painter->setPen(QPen(rimlight, 2));
0069         painter->setBrush(rimlight);
0070         painter->drawLine(areaRect.topLeft(), areaRect.topRight());
0071     }
0072 
0073     // Paint active highlight..
0074     const bool isLayerActive = model()->headerData(layerIndex, orientation(), KisAnimTimelineFramesModel::ActiveLayerRole).toBool();
0075 
0076     if (isLayerActive) {
0077         QColor lineColor = KisAnimTimelineColors::instance()->activeLayerBackground();
0078         const int lineWidth = 2;
0079 
0080         painter->setPen(QPen(lineColor, lineWidth));
0081         painter->setBrush(lineColor);
0082 
0083         QVector<QLine> lines;
0084         lines << QLine(areaRect.topLeft(), areaRect.topRight()).translated(0,1);
0085         lines << QLine(areaRect.bottomLeft(), areaRect.bottomRight()).translated(0,-1);
0086         painter->drawLines(lines);
0087     }
0088 
0089     // Paint pushpin icon..
0090     painter->save();
0091     const bool isPinned = model()->headerData(layerIndex, orientation(), KisAnimTimelineFramesModel::PinnedToTimelineRole).toBool();
0092     const uint pinWidth = areaRect.height() - 4;
0093     QRect pinArea = kisTrimLeft(pinWidth, remainingArea);
0094     const uint difference = pinArea.height() - pinWidth;
0095     pinArea.setHeight(pinWidth); // Square to width.
0096     pinArea.translate(0, difference / 2); // Center.
0097 
0098     QIcon icon = KisIconUtils::loadIcon("krita_tool_reference_images");
0099     QRect iconRect = pinArea - QMargins(5,5,5,5);
0100 
0101     if (!isPinned) {
0102         iconRect = pinArea - QMargins(6,4,4,6);
0103         painter->setOpacity(0.35);
0104     }
0105 
0106     icon.paint(painter, iconRect);
0107     painter->restore();
0108 
0109     // Paint layer name..
0110     const int textSpace = remainingArea.width() - m_d->iconSectionWidth(layerIndex);
0111     const QRect textArea = kisTrimLeft(textSpace, remainingArea);
0112     QString text = model()->headerData(layerIndex, orientation(), Qt::DisplayRole).toString();
0113     text = fontMetrics().elidedText(text, textElideMode(), textArea.width());
0114     style()->drawItemText(painter, textArea, Qt::AlignLeft | Qt::AlignVCenter, palette(), isEnabled(), text, QPalette::ButtonText);
0115 
0116     // Paint property (hidden, alpha inherit, etc.) icons..
0117     QVariant value =  model()->headerData(layerIndex, orientation(), KisAnimTimelineFramesModel::TimelinePropertiesRole);
0118     KisAnimTimelineFramesModel::PropertyList props = value.value<KisAnimTimelineFramesModel::PropertyList>();
0119 
0120     const int numIcons = m_d->numIcons(layerIndex);
0121     for (int i = 0; i < numIcons; i++) {
0122         KisAnimTimelineFramesModel::Property *prop = m_d->getPropertyAt(props, i);
0123 
0124         const bool isActive = prop->state.toBool();
0125         QIcon icon = isActive ? prop->onIcon : prop->offIcon;
0126         if (!isActive) {
0127             painter->setOpacity(0.35);
0128         }
0129         QRect iconRect = m_d->propertyIconRect(layerIndex, i).translated(areaRect.topLeft());
0130         icon.paint(painter, iconRect);
0131         painter->setOpacity(1.0);
0132     }
0133 }
0134 
0135 KisAnimTimelineFramesModel::Property*
0136 KisAnimTimelineLayersHeader::Private::getPropertyAt(KisAnimTimelineFramesModel::PropertyList &props, int index)
0137 {
0138     int logical = 0;
0139     for (int i = 0; i < props.size(); i++) {
0140         if (props[i].isMutable) {
0141             if (logical == index) {
0142                 return &props[i];
0143             }
0144 
0145             logical++;
0146         }
0147     }
0148 
0149     return 0;
0150 }
0151 
0152 int KisAnimTimelineLayersHeader::Private::numIcons(int logicalIndex) const
0153 {
0154     int result = 0;
0155 
0156     QVariant value =  q->model()->headerData(logicalIndex, q->orientation(), KisAnimTimelineFramesModel::TimelinePropertiesRole);
0157     if (value.isValid()) {
0158         KisAnimTimelineFramesModel::PropertyList props = value.value<KisAnimTimelineFramesModel::PropertyList>();
0159 
0160         Q_FOREACH (const KisAnimTimelineFramesModel::Property &p, props) {
0161             if (p.isMutable) {
0162                 result++;
0163             }
0164         }
0165     }
0166 
0167     return result;
0168 }
0169 
0170 int KisAnimTimelineLayersHeader::Private::iconSectionWidth(int layerIndex) const
0171 {
0172     const int iconSize = 16;
0173     const int iconPadding = 2;
0174     return (iconSize + iconPadding) * numIcons(layerIndex);
0175 }
0176 
0177 QPoint KisAnimTimelineLayersHeader::Private::getSectionLocalPosition(int layerIndex, QPoint widgetPosition) const
0178 {
0179     QPoint sectionTopLeft(0, q->sectionViewportPosition(layerIndex));
0180     return widgetPosition - sectionTopLeft;
0181 }
0182 
0183 QSize KisAnimTimelineLayersHeader::sectionSizeFromContents(int layerIndex) const
0184 {
0185     QSize baseSize = QHeaderView::sectionSizeFromContents(layerIndex);
0186     const int pinSpace = baseSize.height() - 4;
0187     const int padding = 8;
0188 
0189     baseSize.setWidth(baseSize.width() + pinSpace
0190                       + m_d->iconSectionWidth(layerIndex) + padding);
0191     return baseSize;
0192 }
0193 
0194 QRect KisAnimTimelineLayersHeader::Private::getSectionRect(int layerIndex) const
0195 {
0196     QSize sectionSize(q->viewport()->width(), q->sectionSize(layerIndex));
0197     QPoint sectionTopLeft(0, q->sectionViewportPosition(layerIndex));
0198     return QRect(sectionTopLeft, sectionSize);
0199 }
0200 
0201 QRect KisAnimTimelineLayersHeader::Private::propertyIconRect(int logicalIndex, int iconIndex) const
0202 {
0203     QSize sectionSize(q->viewport()->width(), q->sectionSize(logicalIndex));
0204 
0205     const int iconWidth = 16;
0206     const int iconHeight = 16;
0207 
0208     const int y = (sectionSize.height() - iconHeight) / 2;
0209     const int x = sectionSize.width() -
0210         (numIcons(logicalIndex) - iconIndex) * (iconWidth + 2);
0211 
0212     return QRect(x, y, iconWidth, iconHeight);
0213 }
0214 
0215 int KisAnimTimelineLayersHeader::Private::propertyIconAt(int logicalIndex, const QPoint &pt)
0216 {
0217     QPoint localPos = getSectionLocalPosition(logicalIndex, pt);
0218 
0219     for (int i = 0; i < numIcons(logicalIndex); i++) {
0220         QRect rc = propertyIconRect(logicalIndex, i);
0221 
0222         if (rc.contains(localPos)) {
0223             return i;
0224         }
0225     }
0226 
0227     return -1;
0228 }
0229 
0230 bool KisAnimTimelineLayersHeader::viewportEvent(QEvent *event)
0231 {
0232     switch (event->type()) {
0233     case QEvent::ToolTip: {
0234         /**
0235          * Override tooltip if the cursor is over the properties icons.
0236          */
0237         QHelpEvent *he = static_cast<QHelpEvent*>(event);
0238         int logical = logicalIndexAt(he->pos());
0239         if (logical != -1) {
0240 
0241             const int iconIndex = m_d->propertyIconAt(logical, he->pos());
0242             if (iconIndex != -1) {
0243 
0244                 QVariant value =  model()->headerData(logical, orientation(), KisAnimTimelineFramesModel::TimelinePropertiesRole);
0245                 KisAnimTimelineFramesModel::PropertyList props = value.value<KisAnimTimelineFramesModel::PropertyList>();
0246 
0247                 KisAnimTimelineFramesModel::Property *p =
0248                     m_d->getPropertyAt(props, iconIndex);
0249 
0250                 QString text = QString("%1 (%2)")
0251                     .arg(p->name)
0252                     .arg(p->state.toBool() ? i18n("on") : i18n("off"));
0253 
0254                 QToolTip::showText(he->globalPos(), text, this);
0255                 return true;
0256             }
0257         }
0258         break; }
0259     default:
0260         break;
0261     }
0262 
0263     return QHeaderView::viewportEvent(event);
0264 }
0265 
0266 void KisAnimTimelineLayersHeader::mousePressEvent(QMouseEvent *e)
0267 {
0268     int layerIndex = logicalIndexAt(e->pos());
0269     if (layerIndex != -1) {
0270         // Handle pin click..
0271         QRect layerHeaderRect = m_d->getSectionRect(layerIndex);
0272         const uint pinWidth = layerHeaderRect.height() - 4;
0273         QRect pinArea = kisTrimLeft(pinWidth, layerHeaderRect);
0274         if (pinArea.contains(e->pos())) {
0275             const bool isPinned = model()->headerData(layerIndex, orientation(), KisAnimTimelineFramesModel::PinnedToTimelineRole).toBool();
0276             model()->setHeaderData(layerIndex, orientation(), !isPinned, KisAnimTimelineFramesModel::PinnedToTimelineRole);
0277             return;
0278         }
0279 
0280         // Handle property click..
0281         const int propertyIconIndex = m_d->propertyIconAt(layerIndex, e->pos());
0282         if (propertyIconIndex != -1) {
0283             QVariant value =  model()->headerData(layerIndex, orientation(), KisAnimTimelineFramesModel::TimelinePropertiesRole);
0284             KisAnimTimelineFramesModel::PropertyList props = value.value<KisAnimTimelineFramesModel::PropertyList>();
0285 
0286             KisAnimTimelineFramesModel::Property *p =
0287                 m_d->getPropertyAt(props, propertyIconIndex);
0288 
0289             bool currentState = p->state.toBool();
0290             p->state = !currentState;
0291 
0292             value.setValue(props);
0293 
0294             model()->setHeaderData(layerIndex, orientation(), value, KisAnimTimelineFramesModel::TimelinePropertiesRole);
0295             return;
0296 
0297         } else if (e->button() == Qt::RightButton) {
0298             model()->setHeaderData(layerIndex, orientation(), true, KisAnimTimelineFramesModel::ActiveLayerRole);
0299             emit sigRequestContextMenu(e->globalPos());
0300             return;
0301         } else if (e->button() == Qt::LeftButton) {
0302             model()->setHeaderData(layerIndex, orientation(), true, KisAnimTimelineFramesModel::ActiveLayerRole);
0303         }
0304     }
0305 
0306     QHeaderView::mousePressEvent(e);
0307 }