File indexing completed on 2024-05-26 04:32:24

0001 /*
0002  *  SPDX-FileCopyrightText: 2015 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "KisAnimTimelineFrameDelegate.h"
0008 
0009 #include <QPen>
0010 #include <QPainter>
0011 #include <QApplication>
0012 #include <QSvgRenderer>
0013 #include <kis_painting_tweaks.h>
0014 #include "KisAnimTimelineFramesModel.h"
0015 #include "KisAnimTimelineColors.h"
0016 
0017 #include "kis_node_view_color_scheme.h"
0018 
0019 KisAnimTimelineFrameDelegate::KisAnimTimelineFrameDelegate(QObject *parent)
0020     : QItemDelegate(parent),
0021       stripes(64, 64)
0022 {
0023     KisNodeViewColorScheme scm;
0024     labelColors = scm.allColorLabels();
0025 
0026     // Clone frame stripes SVG -> Pixmap..
0027     QImage stripesImage(":diagonal-stripe.svg", "svg");
0028     stripesImage.save("/tmp/krita_stripes.svg", "svg");
0029     stripes = QPixmap::fromImage(stripesImage);
0030 }
0031 
0032 KisAnimTimelineFrameDelegate::~KisAnimTimelineFrameDelegate()
0033 {
0034 }
0035 
0036 void KisAnimTimelineFrameDelegate::paintActiveFrameSelector(QPainter *painter, const QRect &rc, bool isCurrentFrame)
0037 {
0038     painter->save();
0039 
0040     QColor lineColor = KisAnimTimelineColors::instance()->selectorColor();
0041     const int lineWidth = rc.width() > 20 ? 4 : 2;
0042 
0043     const int x0 = rc.x();
0044     const int y0 = rc.y();
0045     const int x1 = rc.right();
0046     const int y1 = rc.bottom();
0047 
0048     QVector<QLine> linesDark;
0049     linesDark << QLine(x0 + lineWidth / 2, y0, x0  + lineWidth / 2, y1);
0050     linesDark << QLine(x1 -  lineWidth / 2 + 1, y0, x1 - lineWidth / 2 + 1, y1);
0051 
0052     QPen oldPen = painter->pen();
0053     painter->setPen(QPen(lineColor, lineWidth));
0054     painter->drawLines(linesDark);
0055     painter->setPen(oldPen);
0056 
0057     if (isCurrentFrame) {
0058         QPen oldPen = painter->pen();
0059         QBrush oldBrush(painter->brush());
0060 
0061         painter->setPen(QPen(lineColor, 0));
0062         painter->setBrush(lineColor);
0063 
0064         painter->drawEllipse(rc.center(), 2,2);
0065 
0066         painter->setBrush(oldBrush);
0067         painter->setPen(oldPen);
0068     }
0069 
0070     painter->restore();
0071 }
0072 
0073 void KisAnimTimelineFrameDelegate::paintSpecialKeyframeIndicator(QPainter *painter, const QModelIndex &index, const QRect &rc) const
0074 {
0075     painter->save();
0076 
0077     bool doesFrameExist = index.data(KisAnimTimelineFramesModel::FrameExistsRole).toBool();   /// does normal keyframe exist
0078     bool isEditable = index.data(KisAnimTimelineFramesModel::FrameEditableRole).toBool();
0079     bool hasContent = index.data(KisAnimTimelineFramesModel::FrameHasContent).toBool();     /// find out if frame is empty
0080 
0081     QColor color = qApp->palette().color(QPalette::Highlight);
0082     QColor baseColor = qApp->palette().color(QPalette::Base);
0083     QColor noLabelSetColor = qApp->palette().color(QPalette::Highlight); // if no color label is used
0084 
0085     // use color label if it exists. coloring follows very similar logic to the drawBackground() function except this is a bit simpler
0086     QVariant colorLabel = index.data(KisAnimTimelineFramesModel::FrameColorLabelIndexRole);
0087     if (colorLabel.isValid()) {
0088         color = labelColors.at(colorLabel.toInt());
0089     } else {
0090         color = noLabelSetColor;
0091     }
0092 
0093     if (!isEditable) {
0094         color = KisPaintingTweaks::blendColors(baseColor, color, 0.5);
0095     }
0096 
0097     if (doesFrameExist && hasContent) {
0098         color = baseColor; // special keyframe will be over filled in frame, so switch color so it is shown
0099     }
0100 
0101     QPen oldPen = painter->pen();
0102     QBrush oldBrush(painter->brush());
0103 
0104     painter->setPen(QPen(color, 0));
0105     painter->setBrush(color);
0106 
0107     QPointF center = rc.center();
0108     QPointF points[4] = {
0109         QPointF(center.x() + 4, center.y()   ),
0110         QPointF(center.x()    , center.y() - 4),
0111         QPointF(center.x() - 4, center.y()   ),
0112         QPointF(center.x()    , center.y() + 4)
0113     };
0114     painter->drawConvexPolygon(points, 4);
0115 
0116     painter->setBrush(oldBrush);
0117     painter->setPen(oldPen);
0118 
0119     painter->restore();
0120 }
0121 
0122 void KisAnimTimelineFrameDelegate::drawBackground(QPainter *painter, const QModelIndex &index, const QRect &rc) const
0123 {
0124     painter->save();
0125 
0126     /// is the current layer actively selected (this is not the same as visibility)
0127     bool hasActiveLayerRole = index.data(KisAnimTimelineFramesModel::ActiveLayerRole).toBool();
0128     bool doesFrameExist = index.data(KisAnimTimelineFramesModel::FrameExistsRole).toBool();   /// does keyframe exist
0129     bool isEditable = index.data(KisAnimTimelineFramesModel::FrameEditableRole).toBool(); /// is layer editable
0130     bool hasContent = index.data(KisAnimTimelineFramesModel::FrameHasContent).toBool();     /// find out if frame is empty
0131 
0132     QColor color; // will get re-used for determining color
0133     QColor noLabelSetColor = qApp->palette().color(QPalette::Highlight); // if no color label is used
0134     QColor highlightColor = qApp->palette().color(QPalette::Highlight);
0135     QColor baseColor = qApp->palette().color(QPalette::Base);
0136 
0137 
0138     // pass for filling in the active row with slightly color difference
0139     if (hasActiveLayerRole) {
0140         color = KisPaintingTweaks::blendColors(baseColor, highlightColor, 0.8);
0141         painter->fillRect(rc, color);
0142     } else {
0143         color = KisPaintingTweaks::blendColors(baseColor, highlightColor, 0.95);
0144         painter->fillRect(rc, color);
0145     }
0146 
0147     // assign background color for frame depending on if the frame has a color label or not
0148     QVariant colorLabel = index.data(KisAnimTimelineFramesModel::FrameColorLabelIndexRole);
0149     if (colorLabel.isValid()) {
0150         color = labelColors.at(colorLabel.toInt());
0151     } else {
0152         color = noLabelSetColor;
0153     }
0154 
0155 
0156     // if layer is hidden, make the entire color more subtle.
0157     // this should be in effect for both fill color and empty outline color
0158     if (!isEditable) {
0159         color = KisPaintingTweaks::blendColors(baseColor, color, 0.7);
0160     }
0161 
0162 
0163     // how do we fill in a frame that has content
0164     // a keyframe will be totally filled in. A hold frame will have a line running through it
0165     if (hasContent && doesFrameExist) {
0166         painter->fillRect(rc, color);
0167     }
0168 
0169     // pass of outline for empty keyframes
0170     if (doesFrameExist && !hasContent) {
0171 
0172         QPen oldPen = painter->pen();
0173         QBrush oldBrush(painter->brush());
0174 
0175         painter->setPen(QPen(color, 2));
0176         painter->setBrush(Qt::NoBrush);
0177         painter->drawRect(rc);
0178 
0179         painter->setBrush(oldBrush);
0180         painter->setPen(oldPen);
0181     }
0182 
0183     // pass of hold frame line
0184     if (!doesFrameExist && hasContent) {
0185 
0186         // pretty much the same check as "isValid" above, but that isn't working with hold frames
0187          if (colorLabel.toInt() == 0) {
0188              color = noLabelSetColor;
0189 
0190              if (!isEditable) {
0191                  color = KisPaintingTweaks::blendColors(baseColor, color, 0.7);
0192              }
0193          }
0194 
0195 
0196         QPoint lineStart(rc.x(), (rc.y() + rc.height()/2));
0197         QPoint lineEnd(rc.x() + rc.width(), (rc.y() + rc.height()/2));
0198 
0199         QPen holdFramePen(color);
0200         holdFramePen.setWidth(1);
0201 
0202         painter->setPen(holdFramePen);
0203         painter->drawLine(QLine(lineStart, lineEnd));
0204     }
0205 
0206     painter->restore();
0207 }
0208 
0209 void KisAnimTimelineFrameDelegate::drawFocus(QPainter *painter,
0210                                            const QStyleOptionViewItem &option,
0211                                            const QRect &rect) const
0212 {
0213     // copied from Qt 4.8!
0214     if ((option.state & QStyle::State_HasFocus) == 0 || !rect.isValid())
0215         return;
0216 
0217     painter->save();
0218 
0219 
0220     QStyleOptionFocusRect o;
0221     o.QStyleOption::operator=(option);
0222     o.rect = rect;
0223     o.state |= QStyle::State_KeyboardFocusChange;
0224     o.state |= QStyle::State_Item;
0225     QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled)
0226                               ? QPalette::Normal : QPalette::Disabled;
0227     o.backgroundColor = option.palette.color(cg, (option.state & QStyle::State_Selected)
0228                                              ? QPalette::Highlight : QPalette::Window);
0229     const QWidget *widget = qobject_cast<QWidget*>(parent());
0230     QStyle *style = widget ? widget->style() : QApplication::style();
0231     style->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter, widget);
0232 
0233     painter->restore();
0234 }
0235 
0236 void KisAnimTimelineFrameDelegate::drawCloneGraphics(QPainter *painter, const QRect &rect) const
0237 {
0238     painter->save();
0239 
0240     QBrush brush(stripes);
0241     brush.setStyle(Qt::TexturePattern);
0242 
0243     painter->setPen(Qt::NoPen);
0244     painter->setBrush(brush);
0245     painter->setOpacity(0.25f);
0246     painter->drawRect(rect);
0247 
0248     painter->restore();
0249 }
0250 
0251 void KisAnimTimelineFrameDelegate::paint(QPainter *painter,
0252                                const QStyleOptionViewItem &option,
0253                                const QModelIndex &index) const
0254 {
0255     // draws background as well as fills normal keyframes
0256     drawBackground(painter, index, option.rect);
0257 
0258     // Clone graphics..
0259     if (index.data(KisAnimTimelineFramesModel::CloneOfActiveFrame).toBool() && index.data(KisAnimTimelineFramesModel::ActiveLayerRole).toBool()) {
0260         drawCloneGraphics(painter, option.rect);
0261     }
0262 
0263     // creates a semi transparent orange rectangle in the frame that is actively selected on the active row
0264     if (option.showDecorationSelected &&
0265         (option.state & QStyle::State_Selected)) {
0266         painter->save();
0267 
0268         const QVariant data = index.data(KisAnimTimelineFramesModel::FrameEditableRole);
0269         bool isEditable = data.isValid() ? data.toBool() : true;
0270 
0271         QColor highlightColor = KisAnimTimelineColors::instance()->selectionColor();
0272         highlightColor.setAlpha(isEditable ? 128 : 64);
0273         QBrush brush = highlightColor;
0274         painter->fillRect(option.rect, brush);
0275 
0276         painter->restore();
0277     }
0278 
0279     // not sure what this is drawing
0280     drawFocus(painter, option, option.rect);
0281 
0282     // opacity keyframe, but NOT normal keyframes
0283     bool specialKeys = index.data(KisAnimTimelineFramesModel::SpecialKeyframeExists).toBool();
0284     if (specialKeys) {
0285         paintSpecialKeyframeIndicator(painter, index, option.rect);
0286     }
0287 
0288     // creates a border and dot on the selected frame on the active row
0289     bool active = index.data(KisAnimTimelineFramesModel::ActiveFrameRole).toBool();
0290     bool layerIsCurrent = index.data(KisAnimTimelineFramesModel::ActiveLayerRole).toBool();
0291     if (active) {
0292         paintActiveFrameSelector(painter, option.rect, layerIsCurrent);
0293     }
0294 
0295     { // Shade over anything that's outside of the animation range...
0296         if (index.data(KisAnimTimelineFramesModel::WithinClipRange).toBool() == false) {
0297             painter->save();
0298 
0299             painter->setOpacity(0.50f);
0300             painter->fillRect(option.rect, qApp->palette().color(QPalette::Base).darker(110));
0301 
0302             painter->restore();
0303         }
0304     }
0305 }