Skip to content

Commit

Permalink
Keyframe toggle icon on the timeline
Browse files Browse the repository at this point in the history
  • Loading branch information
mbasaglia committed Sep 17, 2023
1 parent 84397c9 commit 56f06ca
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 28 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* Alt + click on bezier points cycles between tangent symmetry modes (Ctrl+click still works)
* Changing a bezier point from corner to smooth will add tangents if they are missing
* The import image dialog now allows importing multiple images at once
* There is an icon on the timeline to quickly toggle keyframes
* UI:
* Middle mouse drag now pans the timeline
* Misc:
Expand All @@ -15,6 +16,7 @@
* When drawing bezier points that don't have tangents are correctly marked as corner
* The play button now resumes from the current frame rather than resetting to the start
* Fixed saving custom templates
* Toggling visibility / lock of a layer by clicking on its icon now adds an undo/redo action

## 0.5.4

Expand Down
17 changes: 17 additions & 0 deletions src/gui/item_models/property_model_base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -760,6 +760,23 @@ model::DocumentNode * item_models::PropertyModelBase::node(const QModelIndex& in
return qobject_cast<model::DocumentNode*>(tree->object);
}

model::AnimatableBase* item_models::PropertyModelBase::Private::animatable(Subtree* tree)
{
if ( !tree || !tree->prop )
return nullptr;

model::PropertyTraits traits = tree->prop->traits();
if ( traits.flags & model::PropertyTraits::Animated )
return static_cast<model::AnimatableBase*>(tree->prop);

return nullptr;
}

model::AnimatableBase* item_models::PropertyModelBase::animatable(const QModelIndex& index) const
{
return d->animatable(d->node_from_index(index));
}


item_models::PropertyModelBase::Private::Subtree*
item_models::PropertyModelBase::Private::add_property(
Expand Down
1 change: 1 addition & 0 deletions src/gui/item_models/property_model_base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ class PropertyModelBase : public DocumentModelBase
model::DocumentNode* node(const QModelIndex& index) const override;
QModelIndex node_index(model::DocumentNode* node) const override;
model::Document* document() const override;
model::AnimatableBase* animatable(const QModelIndex& index) const;

private slots:
void property_changed(const model::BaseProperty* prop, const QVariant& value);
Expand Down
87 changes: 67 additions & 20 deletions src/gui/item_models/property_model_full.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#include "property_model_full.hpp"
#include "property_model_private.hpp"

#include <QPainter>

#include "model/stretchable_time.hpp"
#include "model/assets/assets.hpp"

Expand Down Expand Up @@ -50,13 +52,13 @@ class item_models::PropertyModelFull::Private : public PropertyModelBase::Privat
QModelIndex ind = node_index(visual);
QModelIndex par = node_index(visual->docnode_parent());
QModelIndex changed = model->index(ind.row(), ColumnVisible, par);
model->dataChanged(changed, changed, {Qt::DecorationRole});
model->dataChanged(changed, changed, {Qt::DecorationRole, Qt::ToolTipRole});
});
connect(visual, &model::VisualNode::docnode_locked_changed, model, [this, visual]() {
QModelIndex ind = node_index(visual);
QModelIndex par = node_index(visual->docnode_parent());
QModelIndex changed = model->index(ind.row(), ColumnLocked, par);
model->dataChanged(changed, changed, {Qt::DecorationRole});
model->dataChanged(changed, changed, {Qt::DecorationRole, Qt::ToolTipRole});
});
connect(visual, &model::VisualNode::docnode_group_color_changed, model, [this, visual]() {
QModelIndex ind = node_index(visual);
Expand Down Expand Up @@ -198,31 +200,84 @@ class item_models::PropertyModelFull::Private : public PropertyModelBase::Privat

QVariant data_color(Subtree* tree, int role)
{
if ( tree->visual_node && ( role == Qt::DisplayRole || role == Qt::EditRole ) )
return tree->visual_node->docnode_group_color();
if ( tree->visual_node )
{
if ( role == Qt::DisplayRole || role == Qt::EditRole )
return tree->visual_node->docnode_group_color();
if ( role == Qt::ToolTipRole )
return tr("Group Color");
}

return {};
}

QIcon transparent_icon(const QIcon& icon, qreal alpha = 0.3)
{
QIcon out;

for ( const auto& size : icon.availableSizes() )
{
QPixmap pixmap = icon.pixmap(size);
QPixmap outpix(pixmap.size());
outpix.fill(Qt::transparent);

QPainter painter(&outpix);
painter.setOpacity(alpha);
painter.drawPixmap(0, 0, pixmap);

out.addPixmap(outpix);
}

return out;
}

QVariant data_visible(Subtree* tree, int role)
{
if ( tree->visual_node && ( role == Qt::DecorationRole ) )
if ( tree->visual_node )
{
if ( role == Qt::DecorationRole )
{
if ( tree->visual_node->visible.get() )
return QIcon::fromTheme("view-visible");
return QIcon::fromTheme("view-hidden");
}
else if ( role == Qt::ToolTipRole )
{
if ( tree->visual_node->visible.get() )
return tr("Visible");
return tr("Hidden");
}
}
else if ( model::AnimatableBase* anprop = animatable(tree) )
{
if ( tree->visual_node->visible.get() )
return QIcon::fromTheme("view-visible");
return QIcon::fromTheme("view-hidden");
if ( role == Qt::DecorationRole )
{
auto frame_status = anprop->keyframe_status(document->current_time());
if ( frame_status == model::AnimatableBase::IsKeyframe )
return QIcon::fromTheme("keyframe");
return transparent_icon(QIcon::fromTheme("keyframe-disable"));
}
}

return {};
}

QVariant data_locked(Subtree* tree, int role)
{
if ( tree->visual_node && ( role == Qt::DecorationRole ) )
if ( tree->visual_node )
{
if ( tree->visual_node->locked.get() )
return QIcon::fromTheme("object-locked");
return QIcon::fromTheme("object-unlocked");
if ( role == Qt::DecorationRole )
{
if ( tree->visual_node->locked.get() )
return QIcon::fromTheme("object-locked");
return QIcon::fromTheme("object-unlocked");
}
else if ( role == Qt::ToolTipRole )
{
if ( tree->visual_node->locked.get() )
return tr("Locked");
return tr("Unlocked");
}
}

return {};
Expand Down Expand Up @@ -380,16 +435,8 @@ QVariant item_models::PropertyModelFull::headerData(int section, Qt::Orientation
return tr("Value");
break;
case ColumnColor:
if ( role == Qt::ToolTipRole )
return tr("Group Color");
break;
case ColumnLocked:
if ( role == Qt::ToolTipRole )
return tr("Locked");
break;
case ColumnVisible:
if ( role == Qt::ToolTipRole )
return tr("Visible");
break;
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/gui/item_models/property_model_private.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ class glaxnimate::gui::item_models::PropertyModelBase::Private

void clean_subtree(Subtree* node);

model::AnimatableBase* animatable(Subtree* node);

model::Document* document = nullptr;
std::vector<Subtree*> roots;
id_type next_id = 1;
Expand Down
32 changes: 24 additions & 8 deletions src/gui/widgets/timeline/compound_timeline_widget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -524,14 +524,30 @@ void CompoundTimelineWidget::expand_index(const QModelIndex& index)

void CompoundTimelineWidget::click_index ( const QModelIndex& index )
{
auto node = d->property_model.visual_node(d->comp_model.mapToSource(index));
if ( !node )
return;

if ( index.column() == item_models::PropertyModelFull::ColumnVisible )
node->visible.set(!node->visible.get());
else if ( index.column() == item_models::PropertyModelFull::ColumnLocked )
node->locked.set(!node->locked.get());
auto source_index = d->comp_model.mapToSource(index);
if ( auto node = d->property_model.visual_node(source_index) )
{
if ( index.column() == item_models::PropertyModelFull::ColumnVisible )
node->visible.set_undoable(!node->visible.get());
else if ( index.column() == item_models::PropertyModelFull::ColumnLocked )
node->locked.set_undoable(!node->locked.get());
}
else if ( auto anprop = d->property_model.animatable(source_index) )
{
if ( index.column() == item_models::PropertyModelFull::ColumnVisible )
{
auto time = d->property_model.document()->current_time();
auto frame_status = anprop->keyframe_status(time);
if ( frame_status == model::AnimatableBase::IsKeyframe )
{
d->property_model.document()->push_command(new command::RemoveKeyframeTime(anprop, time));
}
else
{
d->property_model.document()->push_command(new command::SetKeyframe(anprop, time, anprop->value(), true));
}
}
}
}

void CompoundTimelineWidget::_on_selection_changed(const QItemSelection &selected, const QItemSelection &deselected)
Expand Down

0 comments on commit 56f06ca

Please sign in to comment.