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 }