File indexing completed on 2025-02-02 04:11:30
0001 /* 0002 * SPDX-FileCopyrightText: 2019-2023 Mattia Basaglia <dev@dragon.best> 0003 * 0004 * SPDX-License-Identifier: GPL-3.0-or-later 0005 */ 0006 0007 #include "animated_property_menu.hpp" 0008 0009 #include <QClipboard> 0010 #include <QMimeData> 0011 0012 #include "widgets/dialogs/follow_path_dialog.hpp" 0013 #include "command/animation_commands.hpp" 0014 #include "command/undo_macro_guard.hpp" 0015 #include "glaxnimate_app.hpp" 0016 #include "widgets/dialogs/glaxnimate_window.hpp" 0017 0018 using namespace glaxnimate::gui; 0019 0020 class glaxnimate::gui::AnimatedPropertyMenu::Private 0021 { 0022 public: 0023 Private(AnimatedPropertyMenu* parent) 0024 { 0025 parent->setIcon(QIcon::fromTheme("label")); 0026 action_title = parent->addSeparator(); 0027 0028 parent->addAction(&action_kf_paste); 0029 action_kf_paste.setIcon(QIcon::fromTheme("edit-paste")); 0030 connect(&action_kf_paste, &QAction::triggered, parent, &AnimatedPropertyMenu::paste_keyframe); 0031 0032 parent->addAction(&action_add_keyframe); 0033 action_add_keyframe.setIcon(QIcon::fromTheme("keyframe-add")); 0034 connect(&action_add_keyframe, &QAction::triggered, parent, &AnimatedPropertyMenu::add_keyframe); 0035 0036 parent->addAction(&action_remove_keyframe); 0037 action_remove_keyframe.setIcon(QIcon::fromTheme("keyframe-remove")); 0038 connect(&action_remove_keyframe, &QAction::triggered, parent, &AnimatedPropertyMenu::remove_keyframe); 0039 0040 action_remove_all_keyframes.setIcon(QIcon::fromTheme("edit-clear-all")); 0041 connect(&action_remove_all_keyframes, &QAction::triggered, parent, &AnimatedPropertyMenu::remove_all_keyframes); 0042 parent->addAction(&action_remove_all_keyframes); 0043 0044 parent->addSeparator(); 0045 0046 action_kf_loop.setIcon(QIcon::fromTheme("media-repeat-all")); 0047 connect(&action_kf_loop, &QAction::triggered, parent, &AnimatedPropertyMenu::loop_keyframes); 0048 parent->addAction(&action_kf_loop); 0049 0050 action_follow_path.setIcon(QIcon::fromTheme("draw-bezier-curves")); 0051 connect(&action_follow_path, &QAction::triggered, parent, &AnimatedPropertyMenu::follow_path); 0052 parent->addAction(&action_follow_path); 0053 action_follow_path.setVisible(false); 0054 0055 retranslate_menu(); 0056 } 0057 0058 void retranslate_menu() 0059 { 0060 action_remove_all_keyframes.setText(i18n("Clear Animations")); 0061 action_kf_paste.setText(i18n("Paste Keyframe")); 0062 action_kf_loop.setText(i18n("Loop Animation")); 0063 action_follow_path.setText(i18n("Follow Path...")); 0064 action_add_keyframe.setText(i18n("Add Keyframe")); 0065 action_remove_keyframe.setText(i18n("Remove Keyframe")); 0066 } 0067 0068 model::Composition* comp() 0069 { 0070 if ( auto shape = property->object()->cast<model::ShapeElement>() ) 0071 return shape->owner_composition(); 0072 0073 return window->current_composition(); 0074 } 0075 0076 model::AnimatableBase* property = nullptr; 0077 QAction* action_title; 0078 QAction action_kf_loop; 0079 QAction action_kf_paste; 0080 QAction action_follow_path; 0081 QAction action_add_keyframe; 0082 QAction action_remove_keyframe; 0083 QAction action_remove_all_keyframes; 0084 SelectionManager* window = nullptr; 0085 }; 0086 0087 glaxnimate::gui::AnimatedPropertyMenu::AnimatedPropertyMenu(QWidget* parent) 0088 : QMenu(parent), d(std::make_unique<Private>(this)) 0089 { 0090 } 0091 0092 glaxnimate::gui::AnimatedPropertyMenu::~AnimatedPropertyMenu() = default; 0093 0094 void glaxnimate::gui::AnimatedPropertyMenu::paste_keyframe() 0095 { 0096 if ( !d->property ) 0097 return; 0098 0099 const QMimeData* data = QGuiApplication::clipboard()->mimeData(); 0100 if ( !data->hasFormat("application/x.glaxnimate-keyframe") ) 0101 return; 0102 0103 QByteArray encoded = data->data("application/x.glaxnimate-keyframe"); 0104 QDataStream stream(&encoded, QIODevice::ReadOnly); 0105 int type = model::PropertyTraits::Unknown; 0106 stream >> type; 0107 if ( type != d->property->traits().type ) 0108 return; 0109 0110 QVariant value; 0111 stream >> value; 0112 0113 d->property->object()->push_command( 0114 new command::SetKeyframe(d->property, d->property->time(), value, true) 0115 ); 0116 } 0117 0118 void glaxnimate::gui::AnimatedPropertyMenu::loop_keyframes() 0119 { 0120 if ( !d->property || d->property->keyframe_count() < 1 ) 0121 return; 0122 0123 d->property->object()->push_command(new command::SetKeyframe( 0124 d->property, 0125 d->comp()->animation->last_frame.get(), 0126 d->property->keyframe(0)->value(), 0127 true 0128 )); 0129 } 0130 0131 void glaxnimate::gui::AnimatedPropertyMenu::follow_path() 0132 { 0133 if ( d->property && d->property->traits().type == model::PropertyTraits::Point ) 0134 { 0135 auto prop = static_cast<model::AnimatedProperty<QPointF>*>(d->property); 0136 FollowPathDialog(prop, d->comp(), d->window->model(), parentWidget()).exec(); 0137 } 0138 } 0139 0140 void glaxnimate::gui::AnimatedPropertyMenu::remove_all_keyframes() 0141 { 0142 if ( !d->property ) 0143 return; 0144 0145 d->property->clear_keyframes_undoable(); 0146 } 0147 0148 void glaxnimate::gui::AnimatedPropertyMenu::set_controller(glaxnimate::gui::SelectionManager* window) 0149 { 0150 d->window = window; 0151 } 0152 0153 void glaxnimate::gui::AnimatedPropertyMenu::changeEvent(QEvent* e) 0154 { 0155 QWidget::changeEvent(e); 0156 if ( e->type() == QEvent::LanguageChange) 0157 { 0158 d->retranslate_menu(); 0159 } 0160 } 0161 0162 void glaxnimate::gui::AnimatedPropertyMenu::set_property(model::AnimatableBase* property) 0163 { 0164 d->property = property; 0165 if ( property ) 0166 { 0167 setTitle(d->property->name()); 0168 d->action_title->setText(d->property->name()); 0169 d->action_follow_path.setVisible(property->traits().type == model::PropertyTraits::Point); 0170 refresh_actions(); 0171 } 0172 } 0173 0174 void glaxnimate::gui::AnimatedPropertyMenu::refresh_actions() 0175 { 0176 if ( d->property ) 0177 { 0178 bool has_kf = d->property->has_keyframe(d->property->time()); 0179 d->action_remove_keyframe.setEnabled(has_kf); 0180 d->action_remove_all_keyframes.setEnabled(d->property->keyframe_count() > 0); 0181 d->action_kf_loop.setEnabled(d->property->keyframe_count() > 0); 0182 d->action_kf_paste.setEnabled(can_paste()); 0183 } 0184 } 0185 0186 0187 glaxnimate::model::AnimatableBase * glaxnimate::gui::AnimatedPropertyMenu::property() const 0188 { 0189 return d->property; 0190 } 0191 0192 bool glaxnimate::gui::AnimatedPropertyMenu::can_paste() const 0193 { 0194 const QMimeData* data = QGuiApplication::clipboard()->mimeData(); 0195 if ( d->property && data->hasFormat("application/x.glaxnimate-keyframe") ) 0196 { 0197 QByteArray encoded = data->data("application/x.glaxnimate-keyframe"); 0198 QDataStream stream(&encoded, QIODevice::ReadOnly); 0199 int type = model::PropertyTraits::Unknown; 0200 stream >> type; 0201 return type == d->property->traits().type; 0202 } 0203 0204 return false; 0205 } 0206 0207 0208 void glaxnimate::gui::AnimatedPropertyMenu::add_keyframe() 0209 { 0210 if ( d->property ) 0211 d->property->add_smooth_keyframe_undoable(d->property->time(), d->property->value()); 0212 } 0213 0214 void glaxnimate::gui::AnimatedPropertyMenu::remove_keyframe() 0215 { 0216 if ( d->property ) 0217 d->property->object()->push_command( 0218 new command::RemoveKeyframeTime(d->property, d->property->time()) 0219 ); 0220 } 0221