File indexing completed on 2024-12-15 04:01:00
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 "animation_commands.hpp" 0008 0009 #include "model/document.hpp" 0010 0011 glaxnimate::command::SetKeyframe::SetKeyframe( 0012 model::AnimatableBase* prop, 0013 model::FrameTime time, 0014 const QVariant& value, 0015 bool commit, 0016 bool force_insert 0017 ) : Parent(i18n("Update %1 keyframe at %2", prop->name(), time), commit), 0018 prop(prop), 0019 time(time), 0020 before(prop->value(time)), 0021 after(value), 0022 had_before(prop->has_keyframe(time) && !force_insert), 0023 force_insert(force_insert) 0024 {} 0025 0026 void glaxnimate::command::SetKeyframe::undo() 0027 { 0028 if ( had_before ) 0029 prop->set_keyframe(time, before); 0030 else 0031 prop->remove_keyframe_at_time(time); 0032 0033 if ( insert_index > 0 ) 0034 prop->keyframe(insert_index-1)->set_transition(trans_before); 0035 } 0036 0037 void glaxnimate::command::SetKeyframe::redo() 0038 { 0039 if ( !calculated ) 0040 { 0041 auto mid = prop->mid_transition(time); 0042 model::AnimatableBase::SetKeyframeInfo info; 0043 auto kf = prop->set_keyframe(time, after, &info, force_insert); 0044 if ( kf && info.insertion && info.index > 0 && info.index + 1 < prop->keyframe_count() ) 0045 { 0046 if ( mid.type != model::AnimatableBase::MidTransition::Middle ) 0047 { 0048 insert_index = -1; 0049 } 0050 else 0051 { 0052 insert_index = info.index; 0053 0054 auto kf_before = prop->keyframe(info.index - 1); 0055 trans_before = kf_before->transition(); 0056 0057 left = mid.from_previous; 0058 right = mid.to_next; 0059 } 0060 } 0061 } 0062 else 0063 { 0064 prop->set_keyframe(time, after, nullptr, force_insert); 0065 } 0066 0067 if ( insert_index > 0 ) 0068 { 0069 prop->keyframe(insert_index-1)->set_transition(left); 0070 prop->keyframe(insert_index)->set_transition(right); 0071 } 0072 0073 } 0074 0075 bool glaxnimate::command::SetKeyframe::merge_with(const SetKeyframe& other) 0076 { 0077 if ( other.prop != prop ) 0078 return false; 0079 after = other.after; 0080 return true; 0081 } 0082 0083 glaxnimate::command::RemoveKeyframeTime::RemoveKeyframeTime( 0084 model::AnimatableBase* prop, 0085 model::FrameTime time 0086 ) : QUndoCommand(i18n("Remove %1 keyframe at %2", prop->name(), time)), 0087 prop(prop), 0088 time(time), 0089 index(prop->keyframe_index(time)), 0090 before(prop->keyframe(index)->value()) 0091 { 0092 if ( index > 0 ) 0093 { 0094 prev_transition_after = prev_transition_before = prop->keyframe(index-1)->transition(); 0095 if ( !prev_transition_after.hold() ) 0096 prev_transition_after.set_after(prop->keyframe(index)->transition().after()); 0097 } 0098 } 0099 0100 void glaxnimate::command::RemoveKeyframeTime::undo() 0101 { 0102 prop->set_keyframe(time, before); 0103 if ( index > 0 ) 0104 prop->keyframe(index-1)->set_transition(prev_transition_before); 0105 } 0106 0107 void glaxnimate::command::RemoveKeyframeTime::redo() 0108 { 0109 if ( index > 0 ) 0110 prop->keyframe(index-1)->set_transition(prev_transition_after); 0111 prop->remove_keyframe(index); 0112 } 0113 0114 glaxnimate::command::RemoveKeyframeIndex::RemoveKeyframeIndex( 0115 model::AnimatableBase* prop, 0116 int index 0117 ) : QUndoCommand(i18n("Remove %1 keyframe %2", prop->name(), index)), 0118 prop(prop), 0119 index(index), 0120 time(prop->keyframe(index)->time()), 0121 before(prop->keyframe(index)->value()) 0122 { 0123 if ( index > 0 ) 0124 { 0125 prev_transition_after = prev_transition_before = prop->keyframe(index-1)->transition(); 0126 if ( !prev_transition_after.hold() ) 0127 prev_transition_after.set_after(prop->keyframe(index)->transition().after()); 0128 } 0129 } 0130 0131 void glaxnimate::command::RemoveKeyframeIndex::undo() 0132 { 0133 prop->set_keyframe(time, before, nullptr, true); 0134 if ( index > 0 ) 0135 prop->keyframe(index-1)->set_transition(prev_transition_before); 0136 0137 } 0138 0139 void glaxnimate::command::RemoveKeyframeIndex::redo() 0140 { 0141 if ( index > 0 ) 0142 prop->keyframe(index-1)->set_transition(prev_transition_after); 0143 prop->remove_keyframe(index); 0144 } 0145 0146 glaxnimate::command::SetMultipleAnimated::SetMultipleAnimated(model::AnimatableBase* prop, QVariant after, bool commit) 0147 : SetMultipleAnimated( 0148 auto_name(prop), 0149 {prop}, 0150 {}, 0151 {after}, 0152 commit 0153 ) 0154 {} 0155 0156 glaxnimate::command::SetMultipleAnimated::SetMultipleAnimated( 0157 const QString& name, 0158 const std::vector<model::AnimatableBase*>& props, 0159 const QVariantList& before, 0160 const QVariantList& after, 0161 bool commit 0162 ) 0163 : Parent(name, commit), 0164 props(props), 0165 before(before), 0166 after(after), 0167 keyframe_after(props[0]->object()->document()->record_to_keyframe()), 0168 time(props[0]->time()) 0169 { 0170 bool add_before = before.empty(); 0171 0172 for ( auto prop : props ) 0173 { 0174 if ( add_before ) 0175 this->before.push_back(prop->value()); 0176 keyframe_before.push_back(prop->has_keyframe(time)); 0177 add_0.push_back(time != 0 && !prop->animated() && prop->object()->document()->record_to_keyframe()); 0178 } 0179 } 0180 0181 0182 glaxnimate::command::SetMultipleAnimated::SetMultipleAnimated(const QString& name, bool commit) 0183 : Parent(name, commit) 0184 { 0185 } 0186 0187 void glaxnimate::command::SetMultipleAnimated::push_property(model::AnimatableBase* prop, const QVariant& after_val) 0188 { 0189 keyframe_after = prop->object()->document()->record_to_keyframe(); 0190 time = prop->time(); 0191 int insert = props.size(); 0192 props.push_back(prop); 0193 before.insert(before.begin() + insert, prop->value()); 0194 after.insert(after.begin() + insert, after_val); 0195 keyframe_before.push_back(prop->has_keyframe(time)); 0196 add_0.push_back(!prop->animated() && prop->object()->document()->record_to_keyframe()); 0197 } 0198 0199 void glaxnimate::command::SetMultipleAnimated::push_property_not_animated(model::BaseProperty* prop, const QVariant& after_val) 0200 { 0201 props_not_animated.push_back(prop); 0202 before.push_back(prop->value()); 0203 after.push_back(after_val); 0204 } 0205 0206 void glaxnimate::command::SetMultipleAnimated::undo() 0207 { 0208 for ( int i = 0; i < int(props.size()); i++ ) 0209 { 0210 auto prop = props[i]; 0211 0212 if ( add_0[i] ) 0213 prop->remove_keyframe_at_time(0); 0214 0215 if ( keyframe_after ) 0216 { 0217 if ( keyframe_before[i] ) 0218 { 0219 prop->set_keyframe(time, before[i]); 0220 } 0221 else 0222 { 0223 prop->remove_keyframe_at_time(time); 0224 prop->set_value(before[i]); 0225 } 0226 } 0227 else 0228 { 0229 if ( keyframe_before[i] ) 0230 prop->set_keyframe(time, before[i]); 0231 else if ( !prop->animated() || prop->time() == time ) 0232 prop->set_value(before[i]); 0233 } 0234 0235 } 0236 0237 for ( int i = 0; i < int(props_not_animated.size()); i++ ) 0238 { 0239 props_not_animated[i]->set_value(before[i+props.size()]); 0240 } 0241 } 0242 0243 void glaxnimate::command::SetMultipleAnimated::redo() 0244 { 0245 for ( int i = 0; i < int(props.size()); i++ ) 0246 { 0247 auto prop = props[i]; 0248 0249 if ( add_0[i] ) 0250 prop->set_keyframe(0, before[i]); 0251 0252 if ( keyframe_after ) 0253 prop->set_keyframe(time, after[i]); 0254 else if ( !prop->animated() || prop->time() == time ) 0255 prop->set_value(after[i]); 0256 } 0257 0258 for ( int i = 0; i < int(props_not_animated.size()); i++ ) 0259 { 0260 props_not_animated[i]->set_value(after[i+props.size()]); 0261 } 0262 } 0263 0264 0265 bool glaxnimate::command::SetMultipleAnimated::merge_with(const SetMultipleAnimated& other) 0266 { 0267 if ( other.props.size() != props.size() || keyframe_after != other.keyframe_after || 0268 time != other.time || other.props_not_animated.size() != props_not_animated.size()) 0269 return false; 0270 0271 for ( int i = 0; i < int(props.size()); i++ ) 0272 if ( props[i] != other.props[i] ) 0273 return false; 0274 0275 for ( int i = 0; i < int(props_not_animated.size()); i++ ) 0276 if ( props_not_animated[i] != other.props_not_animated[i] ) 0277 return false; 0278 0279 after = other.after; 0280 return true; 0281 } 0282 0283 QString glaxnimate::command::SetMultipleAnimated::auto_name(model::AnimatableBase* prop) 0284 { 0285 bool key_before = prop->has_keyframe(prop->time()); 0286 bool key_after = prop->object()->document()->record_to_keyframe(); 0287 0288 if ( key_after && !key_before ) 0289 return i18n("Add keyframe for %1 at %2", prop->name(), prop->time()); 0290 0291 if ( key_before ) 0292 return i18n("Update %1 at %2", prop->name(), prop->time()); 0293 0294 return i18n("Update %1", prop->name()); 0295 } 0296 0297 bool glaxnimate::command::SetMultipleAnimated::empty() const 0298 { 0299 return props.empty() && props_not_animated.empty(); 0300 } 0301 0302 glaxnimate::command::SetKeyframeTransition::SetKeyframeTransition( 0303 model::AnimatableBase* prop, 0304 int keyframe_index, 0305 const model::KeyframeTransition& transition 0306 ) 0307 : QUndoCommand(i18n("Update keyframe transition")), 0308 prop(prop), 0309 keyframe_index(keyframe_index), 0310 undo_value(keyframe()->transition()), 0311 redo_value(transition) 0312 { 0313 } 0314 0315 glaxnimate::command::SetKeyframeTransition::SetKeyframeTransition( 0316 model::AnimatableBase* prop, 0317 int keyframe_index, 0318 model::KeyframeTransition::Descriptive desc, 0319 const QPointF& point, 0320 bool before_transition 0321 ) : SetKeyframeTransition(prop, keyframe_index, prop->keyframe(keyframe_index)->transition()) 0322 { 0323 if ( desc == model::KeyframeTransition::Custom ) 0324 { 0325 if ( before_transition ) 0326 redo_value.set_before(point); 0327 else 0328 redo_value.set_after(point); 0329 } 0330 else 0331 { 0332 if ( before_transition ) 0333 redo_value.set_before_descriptive(desc); 0334 else 0335 redo_value.set_after_descriptive(desc); 0336 } 0337 } 0338 0339 void glaxnimate::command::SetKeyframeTransition::undo() 0340 { 0341 keyframe()->set_transition(undo_value); 0342 } 0343 0344 void glaxnimate::command::SetKeyframeTransition::redo() 0345 { 0346 keyframe()->set_transition(redo_value); 0347 } 0348 0349 glaxnimate::model::KeyframeBase* glaxnimate::command::SetKeyframeTransition::keyframe() const 0350 { 0351 return prop->keyframe(keyframe_index); 0352 } 0353 0354 glaxnimate::command::MoveKeyframe::MoveKeyframe( 0355 model::AnimatableBase* prop, 0356 int keyframe_index, 0357 model::FrameTime time_after 0358 ) : QUndoCommand(i18n("Move keyframe")), 0359 prop(prop), 0360 keyframe_index_before(keyframe_index), 0361 time_before(prop->keyframe(keyframe_index)->time()), 0362 time_after(time_after) 0363 {} 0364 0365 void glaxnimate::command::MoveKeyframe::undo() 0366 { 0367 prop->move_keyframe(keyframe_index_after, time_before); 0368 } 0369 0370 void glaxnimate::command::MoveKeyframe::redo() 0371 { 0372 keyframe_index_after = prop->move_keyframe(keyframe_index_before, time_after); 0373 } 0374 0375 int glaxnimate::command::MoveKeyframe::redo_index() const 0376 { 0377 return keyframe_index_after; 0378 } 0379 0380 glaxnimate::command::RemoveAllKeyframes::RemoveAllKeyframes(model::AnimatableBase* prop, QVariant after) 0381 : QUndoCommand(i18n("Remove animations from %1", prop->name())), 0382 prop(prop), 0383 before(prop->value()), 0384 after(std::move(after)) 0385 { 0386 int count = prop->keyframe_count(); 0387 keyframes.reserve(count); 0388 for ( int i = 0; i < count; i++ ) 0389 { 0390 auto kf = prop->keyframe(i); 0391 keyframes.push_back({ 0392 kf->time(), 0393 kf->value(), 0394 kf->transition() 0395 }); 0396 } 0397 } 0398 0399 void glaxnimate::command::RemoveAllKeyframes::redo() 0400 { 0401 prop->clear_keyframes(); 0402 prop->set_value(after); 0403 } 0404 0405 void glaxnimate::command::RemoveAllKeyframes::undo() 0406 { 0407 for ( const auto& kf : keyframes ) 0408 { 0409 prop->set_keyframe(kf.time, kf.value, nullptr, true)->set_transition(kf.transition); 0410 } 0411 prop->set_time(prop->time()); 0412 prop->set_value(before); 0413 } 0414 0415 0416 glaxnimate::command::SetPositionBezier::SetPositionBezier( 0417 model::detail::AnimatedPropertyPosition* prop, 0418 math::bezier::Bezier after, 0419 bool commit, 0420 const QString& name 0421 ) 0422 : SetPositionBezier(prop, prop->bezier(), std::move(after), commit, name) 0423 { 0424 } 0425 0426 glaxnimate::command::SetPositionBezier::SetPositionBezier( 0427 model::detail::AnimatedPropertyPosition* prop, 0428 math::bezier::Bezier before, 0429 math::bezier::Bezier after, 0430 bool commit, 0431 const QString& name 0432 ) : Parent(name.isEmpty() ? i18n("Update animation path") : name, commit), 0433 property(prop), 0434 before(std::move(before)), 0435 after(std::move(after)) 0436 { 0437 } 0438 0439 bool glaxnimate::command::SetPositionBezier::merge_with(const glaxnimate::command::SetPositionBezier& other) 0440 { 0441 return property == other.property; 0442 } 0443 0444 void glaxnimate::command::SetPositionBezier::undo() 0445 { 0446 property->set_bezier(before); 0447 } 0448 0449 void glaxnimate::command::SetPositionBezier::redo() 0450 { 0451 property->set_bezier(after); 0452 }