Commit c3aeedc0 authored by Scott Petrovic's avatar Scott Petrovic

Improve timeline UI to show holds and empty frames

parent c677e6d8
......@@ -123,6 +123,10 @@ void KisKeyframe::setColorLabel(int label)
m_d->colorLabel = label;
}
bool KisKeyframe::hasContent() const {
return true;
}
KisKeyframeChannel *KisKeyframe::channel() const
{
return m_d->channel;
......
......@@ -64,6 +64,7 @@ public:
int colorLabel() const;
void setColorLabel(int label);
virtual bool hasContent() const; // does any content exist in keyframe, or is it empty?
KisKeyframeChannel *channel() const;
......
......@@ -45,6 +45,12 @@ struct KisRasterKeyframe : public KisKeyframe
return toQShared(new KisRasterKeyframe(this, channel));
}
bool hasContent() const override {
KisRasterKeyframeChannel *channel = dynamic_cast<KisRasterKeyframeChannel*>(this->channel());
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(channel, true);
return channel->keyframeHasContent(this);
}
};
struct KisRasterKeyframeChannel::Private
......@@ -83,7 +89,12 @@ KisRasterKeyframeChannel::~KisRasterKeyframeChannel()
int KisRasterKeyframeChannel::frameId(KisKeyframeSP keyframe) const
{
KisRasterKeyframe *key = dynamic_cast<KisRasterKeyframe*>(keyframe.data());
return frameId(keyframe.data());
}
int KisRasterKeyframeChannel::frameId(const KisKeyframe *keyframe) const
{
const KisRasterKeyframe *key = dynamic_cast<const KisRasterKeyframe*>(keyframe);
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(key, -1);
return key->frameId;
}
......@@ -280,6 +291,11 @@ KisKeyframeSP KisRasterKeyframeChannel::loadKeyframe(const QDomElement &keyframe
return keyframe;
}
bool KisRasterKeyframeChannel::keyframeHasContent(const KisKeyframe *keyframe) const
{
return !m_d->paintDevice->framesInterface()->frameBounds(frameId(keyframe)).isEmpty();
}
bool KisRasterKeyframeChannel::hasScalarValue() const
{
return false;
......
......@@ -79,10 +79,14 @@ protected:
void saveKeyframe(KisKeyframeSP keyframe, QDomElement keyframeElement, const QString &layerFilename) override;
KisKeyframeSP loadKeyframe(const QDomElement &keyframeNode) override;
friend class KisRasterKeyframe;
bool keyframeHasContent(const KisKeyframe *keyframe) const;
private:
void setFrameFilename(int frameId, const QString &filename);
QString chooseFrameFilename(int frameId, const QString &layerFilename);
int frameId(KisKeyframeSP keyframe) const;
int frameId(const KisKeyframe *keyframe) const;
struct Private;
QScopedPointer<Private> m_d;
......
......@@ -308,6 +308,10 @@ void Node::setLocked(bool value)
d->node->setUserLocked(value);
}
bool Node::hasExtents()
{
return !d->node->extent().isEmpty();
}
QString Node::name() const
{
......
......@@ -232,6 +232,13 @@ public Q_SLOTS:
*/
void setLocked(bool value);
/**
* @brief does the node have any content in it?
* @return if node has any content in it
*/
bool hasExtents();
/**
* @return the user-visible name of this node.
*/
......
......@@ -72,7 +72,8 @@ public:
FrameCachedRole,
FrameEditableRole,
FramesPerSecondRole,
UserRole
UserRole,
FrameHasContent // is it an empty frame with nothing in it?
};
protected:
......@@ -82,12 +83,11 @@ protected:
KUndo2Command* createOffsetFramesCommand(QModelIndexList srcIndexes, const QPoint &offset, bool copyFrames, KUndo2Command *parentCommand = 0, bool moveEmptyFrames = true);
private Q_SLOTS:
void slotFramerateChanged();
void slotCurrentTimeChanged(int time);
void slotCacheChanged();
void slotInternalScrubPreviewRequested(int time);
void slotPlaybackFrameChanged();
......
......@@ -87,24 +87,6 @@ QBrush TimelineColorScheme::headerActive() const
return selectorColor();
}
QColor TimelineColorScheme::frameColor(bool present, bool active) const
{
QColor color = Qt::transparent;
if (present && !active) {
color = m_d->baseColor;
} else if (present && active) {
QColor bgColor = qApp->palette().color(QPalette::Base);
int darkenCoeff = bgColor.value() > 128 ? 130 : 80;
color = m_d->baseColor.darker(darkenCoeff);
} else if (!present && active) {
QColor bgColor = qApp->palette().color(QPalette::Base);
return KritaUtils::blendColors(m_d->baseColor, bgColor, 0.2);
}
return color;
}
QColor TimelineColorScheme::onionSkinsSliderEnabledColor() const
{
return m_d->baseColor;
......
......@@ -43,8 +43,6 @@ public:
QBrush headerCachedFrame() const;
QBrush headerActive() const;
QColor frameColor(bool present, bool active)const ;
QColor onionSkinsSliderEnabledColor() const;
QColor onionSkinsSliderDisabledColor() const;
QColor onionSkinsButtonColor() const;
......
......@@ -32,6 +32,7 @@
#include "timeline_frames_model.h"
#include "timeline_frames_view.h"
#include "kis_animation_frame_cache.h"
#include "kis_image_animation_interface.h"
#include "kis_signal_auto_connection.h"
#include "kis_node_manager.h"
......
......@@ -21,7 +21,7 @@
#include <QPen>
#include <QPainter>
#include <QApplication>
#include "krita_utils.h"
#include "timeline_frames_model.h"
#include "timeline_color_scheme.h"
......@@ -71,17 +71,30 @@ void TimelineFramesItemDelegate::paintActiveFrameSelector(QPainter *painter, con
}
}
void TimelineFramesItemDelegate::paintSpecialKeyframeIndicator(QPainter *painter, const QModelIndex &index, const QRect &rc)
void TimelineFramesItemDelegate::paintSpecialKeyframeIndicator(QPainter *painter, const QModelIndex &index, const QRect &rc) const
{
bool active = index.data(TimelineFramesModel::ActiveLayerRole).toBool();
bool framePresent = index.data(TimelineFramesModel::FrameExistsRole).toBool();
bool editable = index.data(TimelineFramesModel::FrameEditableRole).toBool();
bool doesFrameExist = index.data(TimelineFramesModel::FrameExistsRole).toBool(); /// does normal keyframe exist
bool isEditable = index.data(TimelineFramesModel::FrameEditableRole).toBool();
bool hasContent = index.data(TimelineFramesModel::FrameHasContent).toBool(); /// find out if frame is empty
QColor color = TimelineColorScheme::instance()->frameColor(!framePresent, active);
QColor color = qApp->palette().color(QPalette::Highlight);
QColor baseColor = qApp->palette().color(QPalette::Base);
QColor noLabelSetColor = qApp->palette().color(QPalette::Highlight); // if no color label is used
if (!editable && color.alpha() > 0) {
const int l = color.lightness();
color = QColor(l, l, l);
// use color label if it exists. coloring follows very similar logic to the drawBackground() function except this is a bit simpler
QVariant colorLabel = index.data(TimelineFramesModel::FrameColorLabelIndexRole);
if (colorLabel.isValid()) {
color = labelColors.at(colorLabel.toInt());
} else {
color = noLabelSetColor;
}
if (!isEditable) {
color = KritaUtils::blendColors(baseColor, color, 0.5);
}
if (doesFrameExist && hasContent) {
color = baseColor; // special keyframe will be over filled in frame, so switch color so it is shown
}
QPen oldPen = painter->pen();
......@@ -105,20 +118,88 @@ void TimelineFramesItemDelegate::paintSpecialKeyframeIndicator(QPainter *painter
void TimelineFramesItemDelegate::drawBackground(QPainter *painter, const QModelIndex &index, const QRect &rc) const
{
bool active = index.data(TimelineFramesModel::ActiveLayerRole).toBool();
bool present = index.data(TimelineFramesModel::FrameExistsRole).toBool();
bool editable = index.data(TimelineFramesModel::FrameEditableRole).toBool();
/// is the current layer actively selected (this is not the same as visibility)
bool hasActiveLayerRole = index.data(TimelineFramesModel::ActiveLayerRole).toBool();
bool doesFrameExist = index.data(TimelineFramesModel::FrameExistsRole).toBool(); /// does keyframe exist
bool isEditable = index.data(TimelineFramesModel::FrameEditableRole).toBool(); /// is layer editable
bool hasContent = index.data(TimelineFramesModel::FrameHasContent).toBool(); /// find out if frame is empty
QColor color; // will get re-used for determining color
QColor noLabelSetColor = qApp->palette().color(QPalette::Highlight); // if no color label is used
QColor highlightColor = qApp->palette().color(QPalette::Highlight);
QColor baseColor = qApp->palette().color(QPalette::Base);
// pass for filling in the active row with slightly color difference
if (hasActiveLayerRole) {
color = KritaUtils::blendColors(baseColor, highlightColor, 0.8);
painter->fillRect(rc, color);
}
// assign background color for frame depending on if the frame has a color label or not
QVariant colorLabel = index.data(TimelineFramesModel::FrameColorLabelIndexRole);
if (colorLabel.isValid()) {
color = labelColors.at(colorLabel.toInt());
} else {
color = noLabelSetColor;
}
// if layer is hidden, make the entire color more subtle.
// this should be in effect for both fill color and empty outline color
if (!isEditable) {
color = KritaUtils::blendColors(baseColor, color, 0.7);
}
// how do we fill in a frame that has content
// a keyframe will be totally filled in. A hold frame will have a line running through it
if (hasContent && doesFrameExist) {
painter->fillRect(rc, color);
}
// pass of outline for empty keyframes
if (doesFrameExist && !hasContent) {
QColor color = colorLabel.isValid() ? labelColors.at(colorLabel.toInt()) :
TimelineColorScheme::instance()->frameColor(present, active);
QPen oldPen = painter->pen();
QBrush oldBrush(painter->brush());
painter->setPen(QPen(color, 2));
painter->setBrush(Qt::NoBrush);
painter->drawRect(rc);
painter->setBrush(oldBrush);
painter->setPen(oldPen);
}
// pass of hold frame line
if (!doesFrameExist && hasContent) {
if (!editable && color.alpha() > 0) {
const int l = color.lightness();
color = QColor(l, l, l);
// pretty much the same check as "isValid" above, but that isn't working with hold frames
if (colorLabel.toInt() == 0) {
color = noLabelSetColor;
if (!isEditable) {
color = KritaUtils::blendColors(baseColor, color, 0.7);
}
}
QPoint lineStart(rc.x(), (rc.y() + rc.height()/2));
QPoint lineEnd(rc.x() + rc.width(), (rc.y() + rc.height()/2));
QPen holdFramePen(color);
holdFramePen.setWidth(1);
painter->setPen(holdFramePen);
painter->drawLine(QLine(lineStart, lineEnd));
}
painter->fillRect(rc, color);
}
void TimelineFramesItemDelegate::drawFocus(QPainter *painter,
......@@ -146,12 +227,13 @@ void TimelineFramesItemDelegate::drawFocus(QPainter *painter,
void TimelineFramesItemDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
{
// draws background as well as fills normal keyframes
drawBackground(painter, index, option.rect);
// creates a semi transparent orange rectangle in the frame that is actively selected on the active row
if (option.showDecorationSelected &&
(option.state & QStyle::State_Selected)) {
QPalette::ColorGroup cg = option.state & QStyle::State_Enabled
? QPalette::Normal : QPalette::Disabled;
if (cg == QPalette::Normal && !(option.state & QStyle::State_Active))
......@@ -165,13 +247,16 @@ void TimelineFramesItemDelegate::paint(QPainter *painter,
painter->setOpacity(oldOpacity);
}
// not sure what this is drawing
drawFocus(painter, option, option.rect);
// opacity keyframe, but NOT normal keyframes
bool specialKeys = index.data(TimelineFramesModel::SpecialKeyframeExists).toBool();
if (specialKeys) {
paintSpecialKeyframeIndicator(painter, index, option.rect);
}
// creates a border and dot on the selected frame on the active row
bool active = index.data(TimelineFramesModel::ActiveFrameRole).toBool();
bool layerIsCurrent = index.data(TimelineFramesModel::ActiveLayerRole).toBool();
if (active) {
......
......@@ -29,7 +29,10 @@ public:
~TimelineFramesItemDelegate() override;
static void paintActiveFrameSelector(QPainter *painter, const QRect &rc, bool isCurrentFrame);
static void paintSpecialKeyframeIndicator(QPainter *painter, const QModelIndex &index, const QRect &rc);
/// the opacity keyframe
void paintSpecialKeyframeIndicator(QPainter *painter, const QModelIndex &index, const QRect &rc) const;
void drawBackground(QPainter *painter, const QModelIndex &index, const QRect &rc) const;
void drawFocus(QPainter *painter,
const QStyleOptionViewItem &option,
......
......@@ -104,6 +104,20 @@ struct TimelineFramesModel::Private
return (primaryChannel && primaryChannel->keyframeAt(column));
}
bool frameHasContent(int row, int column) {
KisNodeDummy *dummy = converter->dummyFromRow(row);
KisKeyframeChannel *primaryChannel = dummy->node()->getKeyframeChannel(KisKeyframeChannel::Content.id());
if (!primaryChannel) return false;
// first check if we are a key frame
KisKeyframeSP frame = primaryChannel->activeKeyframeAt(column);
if (!frame) return false;
return frame->hasContent();
}
bool specialKeyframeExists(int row, int column) {
KisNodeDummy *dummy = converter->dummyFromRow(row);
if (!dummy) return false;
......@@ -122,7 +136,7 @@ struct TimelineFramesModel::Private
KisKeyframeChannel *primaryChannel = dummy->node()->getKeyframeChannel(KisKeyframeChannel::Content.id());
if (!primaryChannel) return -1;
KisKeyframeSP frame = primaryChannel->keyframeAt(column);
KisKeyframeSP frame = primaryChannel->activeKeyframeAt(column);
if (!frame) return -1;
return frame->colorLabel();
......@@ -260,6 +274,7 @@ void TimelineFramesModel::setDummiesFacade(KisDummiesFacadeBase *dummiesFacade,
SIGNAL(sigAudioChannelChanged()), SIGNAL(sigAudioChannelChanged()));
connect(m_d->image->animationInterface(),
SIGNAL(sigAudioVolumeChanged()), SIGNAL(sigAudioChannelChanged()));
connect(m_d->image, SIGNAL(sigImageModified()), SLOT(slotImageContentChanged()));
}
if (m_d->dummiesFacade != oldDummiesFacade) {
......@@ -281,6 +296,16 @@ void TimelineFramesModel::slotDummyChanged(KisNodeDummy *dummy)
m_d->updateTimer.start();
}
void TimelineFramesModel::slotImageContentChanged()
{
if (m_d->activeLayerIndex < 0) return;
KisNodeDummy *dummy = m_d->converter->dummyFromRow(m_d->activeLayerIndex);
if (!dummy) return;
slotDummyChanged(dummy);
}
void TimelineFramesModel::processUpdateQueue()
{
Q_FOREACH (KisNodeDummy *dummy, m_d->updateQueue) {
......@@ -339,6 +364,9 @@ QVariant TimelineFramesModel::data(const QModelIndex &index, int role) const
case FrameEditableRole: {
return m_d->layerEditable(index.row());
}
case FrameHasContent: {
return m_d->frameHasContent(index.row(), index.column());
}
case FrameExistsRole: {
return m_d->frameExists(index.row(), index.column());
}
......
......@@ -132,6 +132,7 @@ protected:
private Q_SLOTS:
void slotDummyChanged(KisNodeDummy *dummy);
void slotImageContentChanged();
void processUpdateQueue();
public Q_SLOTS:
......
......@@ -770,6 +770,7 @@ QPixmap TimelineFramesView::Private::renderToPixmap(const QModelIndexList &index
option.rect = paintPairs.at(j).first.translated(-r->topLeft());
const QModelIndex &current = paintPairs.at(j).second;
//adjustViewOptionsForIndex(&option, current);
q->itemDelegate(current)->paint(&painter, option, current);
}
return pixmap;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment