File indexing completed on 2025-02-02 04:11:33
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 "compound_timeline_widget.hpp" 0008 #include "ui_compound_timeline_widget.h" 0009 0010 #include <QMenu> 0011 #include <QScrollBar> 0012 #include <QClipboard> 0013 #include <QMimeData> 0014 #include <QSignalBlocker> 0015 #include <QActionGroup> 0016 0017 #include "math/bezier/meta.hpp" 0018 #include "model/shapes/precomp_layer.hpp" 0019 #include "command/animation_commands.hpp" 0020 #include "command/undo_macro_guard.hpp" 0021 0022 #include "item_models/property_model_full.hpp" 0023 #include "item_models/comp_filter_model.hpp" 0024 0025 #include "glaxnimate_app.hpp" 0026 0027 #include "style/property_delegate.hpp" 0028 #include "style/fixed_height_delegate.hpp" 0029 #include "widgets/dialogs/keyframe_editor_dialog.hpp" 0030 0031 #ifndef Q_OS_ANDROID 0032 #include "widgets/menus/node_menu.hpp" 0033 #endif 0034 #include "widgets/menus/animated_property_menu.hpp" 0035 #include "widgets/timeline/keyframe_transition_data.hpp" 0036 0037 using namespace glaxnimate::gui; 0038 using namespace glaxnimate; 0039 0040 class CompoundTimelineWidget::Private 0041 { 0042 public: 0043 void setupUi(CompoundTimelineWidget* parent) 0044 { 0045 ui.setupUi(parent); 0046 comp_model.setSourceModel(&property_model); 0047 0048 QPalette prop_pal = ui.properties->palette(); 0049 prop_pal.setBrush( 0050 QPalette::Inactive, 0051 QPalette::Highlight, 0052 prop_pal.brush(QPalette::Active, QPalette::Highlight) 0053 ); 0054 prop_pal.setBrush( 0055 QPalette::Inactive, 0056 QPalette::HighlightedText, 0057 prop_pal.brush(QPalette::Active, QPalette::HighlightedText) 0058 ); 0059 ui.properties->setPalette(prop_pal); 0060 0061 ui.properties->setModel(&comp_model); 0062 ui.timeline->set_model(&comp_model, &property_model, ui.properties); 0063 connect(&property_model, &QAbstractItemModel::dataChanged, 0064 ui.properties->viewport(), (void (QWidget::*)())&QWidget::update); 0065 0066 ui.properties->setItemDelegate(&fixed_height_delegate); 0067 ui.properties->setItemDelegateForColumn(item_models::PropertyModelFull::ColumnValue, &property_delegate); 0068 ui.properties->setItemDelegateForColumn(item_models::PropertyModelFull::ColumnColor, &color_delegate); 0069 0070 ui.properties->header()->setSectionResizeMode(item_models::PropertyModelFull::ColumnName, QHeaderView::ResizeToContents); 0071 ui.properties->header()->setSectionResizeMode(item_models::PropertyModelFull::ColumnValue, QHeaderView::Stretch); 0072 ui.properties->header()->setSectionResizeMode(item_models::PropertyModelFull::ColumnColor, QHeaderView::ResizeToContents); 0073 ui.properties->header()->setSectionResizeMode(item_models::PropertyModelFull::ColumnLocked, QHeaderView::ResizeToContents); 0074 ui.properties->header()->setSectionResizeMode(item_models::PropertyModelFull::ColumnVisible, QHeaderView::ResizeToContents); 0075 ui.properties->header()->moveSection(item_models::PropertyModelFull::ColumnColor, 0); 0076 ui.properties->header()->moveSection(item_models::PropertyModelFull::ColumnVisible, 1); 0077 ui.properties->header()->moveSection(item_models::PropertyModelFull::ColumnLocked, 2); 0078 0079 ui.properties->setUniformRowHeights(true); 0080 ui.properties->header()->setFixedHeight(ui.timeline->header_height()); 0081 property_delegate.set_forced_height(ui.timeline->header_height()); 0082 fixed_height_delegate.set_height(ui.timeline->row_height()); 0083 ui.properties->setRootIsDecorated(true); 0084 0085 ui.properties->verticalScrollBar()->installEventFilter(parent); 0086 ui.properties->verticalScrollBar()->setSingleStep(ui.scrollbar->singleStep()); 0087 ui.properties->verticalScrollBar()->setPageStep(ui.scrollbar->pageStep()); 0088 connect(ui.properties->verticalScrollBar(), &QScrollBar::valueChanged, ui.scrollbar, &QScrollBar::setValue); 0089 connect(ui.scrollbar, &QScrollBar::valueChanged, ui.properties->verticalScrollBar(), &QScrollBar::setValue); 0090 connect(ui.properties->verticalScrollBar(), &QScrollBar::rangeChanged, ui.scrollbar, &QScrollBar::setRange); 0091 connect(ui.scrollbar, &QScrollBar::valueChanged, parent, &CompoundTimelineWidget::on_scroll); 0092 0093 connect(ui.timeline, &TimelineWidget::line_clicked, parent, &CompoundTimelineWidget::select_line); 0094 connect(ui.properties->selectionModel(), &QItemSelectionModel::currentChanged, parent, &CompoundTimelineWidget::select_index); 0095 connect(ui.properties->selectionModel(), &QItemSelectionModel::selectionChanged, parent, &CompoundTimelineWidget::_on_selection_changed); 0096 0097 ui.action_add_keyframe->setIcon(QIcon::fromTheme("keyframe-add")); 0098 connect(ui.action_add_keyframe, &QAction::triggered, parent, &CompoundTimelineWidget::add_keyframe); 0099 0100 connect(parent, &QWidget::customContextMenuRequested, parent, &CompoundTimelineWidget::custom_context_menu); 0101 0102 setup_menu(parent); 0103 0104 } 0105 0106 void setup_menu(CompoundTimelineWidget* parent) 0107 { 0108 for ( int i = 0; i < KeyframeTransitionData::count; i++ ) 0109 { 0110 auto data = KeyframeTransitionData::from_index(i, KeyframeTransitionData::Full); 0111 actions_enter[i].setIcon(data.icon()); 0112 actions_enter[i].setActionGroup(&enter); 0113 actions_enter[i].setData(data.variant()); 0114 0115 actions_leave[i].setIcon(data.icon()); 0116 actions_leave[i].setActionGroup(&exit); 0117 actions_leave[i].setData(data.variant()); 0118 } 0119 0120 menu_keyframe.addAction(&action_kf_remove); 0121 action_kf_remove.setIcon(QIcon::fromTheme("keyframe-remove")); 0122 connect(&action_kf_remove, &QAction::triggered, parent, &CompoundTimelineWidget::remove_keyframe); 0123 0124 0125 menu_keyframe.addAction(&action_kf_copy); 0126 action_kf_copy.setIcon(QIcon::fromTheme("keyframe-duplicate")); 0127 connect(&action_kf_copy, &QAction::triggered, parent, &CompoundTimelineWidget::copy_keyframe); 0128 0129 action_kf_paste.setIcon(QIcon::fromTheme("edit-paste")); 0130 menu_keyframe.addAction(&action_kf_paste); 0131 0132 menu_keyframe.addSeparator(); 0133 0134 menu_keyframe.addAction(&action_kf_remove_all); 0135 action_kf_remove_all.setIcon(QIcon::fromTheme("edit-clear-all")); 0136 connect(&action_kf_remove_all, &QAction::triggered, &menu_property, &AnimatedPropertyMenu::remove_all_keyframes); 0137 0138 action_enter = menu_keyframe.addSeparator(); 0139 for ( QAction* ac : enter.actions() ) 0140 { 0141 ac->setCheckable(true); 0142 menu_keyframe.addAction(ac); 0143 connect(ac, &QAction::triggered, parent, &CompoundTimelineWidget::keyframe_action_enter); 0144 } 0145 0146 action_exit = menu_keyframe.addSeparator(); 0147 for ( QAction* ac : exit.actions() ) 0148 { 0149 ac->setCheckable(true); 0150 menu_keyframe.addAction(ac); 0151 connect(ac, &QAction::triggered, parent, &CompoundTimelineWidget::keyframe_action_exit); 0152 } 0153 0154 retranslate_menu(); 0155 } 0156 0157 void retranslate_menu() 0158 { 0159 action_enter->setText(i18n("Transition From Previous")); 0160 action_exit->setText(i18n("Transition To Next")); 0161 0162 0163 for ( int i = 0; i < KeyframeTransitionData::count; i++ ) 0164 { 0165 if ( i == model::KeyframeTransition::Custom ) 0166 { 0167 actions_enter[i].setText(i18n("Custom...")); 0168 actions_leave[i].setText(actions_enter[i].text()); 0169 } 0170 else 0171 { 0172 actions_leave[i].setText(KeyframeTransitionData::from_index(i, KeyframeTransitionData::Start).name); 0173 actions_enter[i].setText(KeyframeTransitionData::from_index(i, KeyframeTransitionData::Finish).name); 0174 } 0175 } 0176 0177 action_kf_remove.setText(i18n("Remove Keyframe")); 0178 action_kf_remove_all.setText(i18n("Clear Animations")); 0179 0180 action_kf_copy.setText(i18n("Copy Keyframe")); 0181 action_kf_paste.setText(i18n("Paste Keyframe")); 0182 } 0183 0184 void retranslateUi(CompoundTimelineWidget* parent) 0185 { 0186 ui.retranslateUi(parent); 0187 retranslate_menu(); 0188 } 0189 0190 void clear_menu_data() 0191 { 0192 menu_property.set_property(nullptr); 0193 menu_kf_enter = nullptr; 0194 menu_kf_exit = nullptr; 0195 } 0196 0197 void keyframe_action(CompoundTimelineWidget* parent, model::KeyframeBase* keyframe, bool before_transition, QAction* action) 0198 { 0199 0200 if ( !keyframe || !menu_property.property() ) 0201 return; 0202 0203 int index = menu_property.property()->keyframe_index(keyframe); 0204 if ( index == -1 ) 0205 return; 0206 0207 QVariant data = action->data(); 0208 if ( data.isValid() && data.toInt() != model::KeyframeTransition::Custom ) 0209 { 0210 menu_property.property()->object()->push_command( 0211 new command::SetKeyframeTransition( 0212 menu_property.property(), index, data.value<model::KeyframeTransition::Descriptive>(), 0213 before_transition ? keyframe->transition().before() : keyframe->transition().after(), 0214 before_transition 0215 ) 0216 ); 0217 } 0218 else 0219 { 0220 KeyframeEditorDialog keyframe_editor(keyframe->transition(), parent); 0221 keyframe_editor.setWindowModality(Qt::ApplicationModal); 0222 if ( keyframe_editor.exec() ) 0223 { 0224 menu_property.property()->object()->push_command( 0225 new command::SetKeyframeTransition( 0226 menu_property.property(), index, 0227 keyframe_editor.transition() 0228 ) 0229 ); 0230 } 0231 } 0232 } 0233 0234 void toggle_paste() 0235 { 0236 action_kf_paste.setEnabled(menu_property.can_paste()); 0237 } 0238 0239 void emit_click(CompoundTimelineWidget* parent, QModelIndex index) 0240 { 0241 model::VisualNode* node = nullptr; 0242 do 0243 { 0244 node = property_model.visual_node(comp_model.mapToSource(index)); 0245 index = index.parent(); 0246 } 0247 while ( !node && index.isValid() ); 0248 0249 if ( node ) 0250 Q_EMIT parent->current_node_changed(node); 0251 } 0252 0253 model::VisualNode* index_node_or_parent(QModelIndex property_index) 0254 { 0255 while ( property_index.isValid() ) 0256 { 0257 auto item = property_model.item(property_index); 0258 model::Object* obj = item.object; 0259 if ( !obj && item.property ) 0260 { 0261 obj = item.property->object(); 0262 if ( !obj ) 0263 return nullptr; 0264 } 0265 0266 if ( auto visual = obj->cast<model::VisualNode>() ) 0267 return visual; 0268 0269 if ( obj->is_instance<model::Asset>() ) 0270 return nullptr; 0271 0272 property_index = property_index.parent(); 0273 } 0274 0275 return nullptr; 0276 } 0277 0278 0279 Ui_CompoundTimelineWidget ui; 0280 item_models::PropertyModelFull property_model; 0281 style::PropertyDelegate property_delegate; 0282 color_widgets::ColorDelegate color_delegate; 0283 style::FixedHeightDelegate fixed_height_delegate; 0284 item_models::CompFilterModel comp_model; 0285 0286 AnimatedPropertyMenu menu_property; 0287 0288 QAction* action_enter; 0289 QAction* action_exit; 0290 QAction action_kf_copy; 0291 QAction action_kf_paste; 0292 QAction action_kf_remove; 0293 QAction action_kf_remove_all; 0294 0295 std::array<QAction, KeyframeTransitionData::count> actions_enter; 0296 std::array<QAction, KeyframeTransitionData::count> actions_leave; 0297 0298 QMenu menu_keyframe; 0299 QActionGroup enter{&menu_keyframe}; 0300 QActionGroup exit{&menu_keyframe}; 0301 0302 model::KeyframeBase* menu_kf_enter = nullptr; 0303 model::KeyframeBase* menu_kf_exit = nullptr; 0304 0305 GlaxnimateWindow* window = nullptr; 0306 }; 0307 0308 CompoundTimelineWidget::CompoundTimelineWidget(QWidget* parent) 0309 : QWidget(parent), d(std::make_unique<Private>()) 0310 { 0311 d->setupUi(this); 0312 connect(d->ui.tab_bar, &CompositionTabBar::switch_composition, this, &CompoundTimelineWidget::switch_composition); 0313 connect(&d->property_model, &QAbstractItemModel::rowsRemoved, this, &CompoundTimelineWidget::rows_removed); 0314 } 0315 0316 0317 CompoundTimelineWidget::~CompoundTimelineWidget() = default; 0318 0319 void CompoundTimelineWidget::changeEvent ( QEvent* e ) 0320 { 0321 QWidget::changeEvent(e); 0322 if ( e->type() == QEvent::LanguageChange) 0323 { 0324 d->retranslateUi(this); 0325 } 0326 } 0327 0328 void CompoundTimelineWidget::set_composition(model::Composition* comp) 0329 { 0330 QSignalBlocker g(d->ui.tab_bar); 0331 d->ui.tab_bar->set_current_composition(comp); 0332 d->comp_model.set_composition(comp); 0333 0334 on_scroll(d->ui.scrollbar->value()); 0335 } 0336 0337 void CompoundTimelineWidget::set_current_node(model::DocumentNode* node) 0338 { 0339 QModelIndex index = d->comp_model.mapFromSource(d->property_model.object_index(node)); 0340 d->ui.properties->expand(index); 0341 d->ui.properties->setCurrentIndex(index); 0342 d->clear_menu_data(); 0343 } 0344 0345 void CompoundTimelineWidget::set_document(model::Document* document) 0346 { 0347 d->ui.timeline->set_document(document); 0348 d->property_model.set_document(document); 0349 d->clear_menu_data(); 0350 d->ui.tab_bar->set_document(document); 0351 d->comp_model.set_composition(nullptr); 0352 } 0353 0354 void CompoundTimelineWidget::clear_document() 0355 { 0356 set_document(nullptr); 0357 } 0358 0359 void CompoundTimelineWidget::select_index(const QModelIndex& index) 0360 { 0361 // d->ui.timeline->select(index); 0362 // d->ui.properties->viewport()->update(); 0363 d->emit_click(this, index); 0364 } 0365 0366 void CompoundTimelineWidget::select_line(quintptr id, bool selected, bool replace_selection) 0367 { 0368 QModelIndex index = d->comp_model.mapFromSource(d->property_model.index_by_id(id)); 0369 if ( selected ) 0370 { 0371 d->ui.properties->selectionModel()->setCurrentIndex( 0372 index, 0373 QItemSelectionModel::Rows | QItemSelectionModel::Select | 0374 ( replace_selection ? QItemSelectionModel::Clear : QItemSelectionModel::NoUpdate ) 0375 ); 0376 } 0377 else 0378 { 0379 d->ui.properties->selectionModel()->select( 0380 index, 0381 QItemSelectionModel::Rows | QItemSelectionModel::Deselect 0382 ); 0383 } 0384 d->ui.properties->viewport()->update(); 0385 d->emit_click(this, index); 0386 } 0387 0388 void CompoundTimelineWidget::custom_context_menu(const QPoint& p) 0389 { 0390 d->clear_menu_data(); 0391 0392 d->ui.timeline->keep_highlight(); 0393 0394 QPoint glob = static_cast<QWidget*>(sender())->mapToGlobal(p); 0395 0396 item_models::PropertyModelFull::Item item; 0397 if ( d->ui.properties->rect().contains(p) ) 0398 { 0399 item = d->property_model.item(d->comp_model.mapToSource( 0400 d->ui.properties->indexAt( 0401 d->ui.properties->viewport()->mapFromGlobal(glob) 0402 ) 0403 )); 0404 } 0405 else 0406 { 0407 std::tie(d->menu_kf_enter, d->menu_kf_exit) = d->ui.timeline->keyframe_at( 0408 d->ui.timeline->viewport()->mapFromGlobal(glob) 0409 ); 0410 0411 item = d->ui.timeline->item_at( 0412 d->ui.timeline->viewport()->mapFromGlobal(glob) 0413 ); 0414 } 0415 0416 if ( item.property && item.property->traits().flags & model::PropertyTraits::Animated ) 0417 d->menu_property.set_property(static_cast<model::AnimatableBase*>(item.property)); 0418 0419 if ( d->menu_kf_exit ) 0420 { 0421 d->enter.setEnabled(d->menu_kf_enter); 0422 if ( d->menu_kf_enter ) 0423 { 0424 d->actions_enter[d->menu_kf_enter->transition().after_descriptive()].setChecked(true); 0425 } 0426 0427 d->actions_leave[d->menu_kf_exit->transition().before_descriptive()].setChecked(true); 0428 0429 d->toggle_paste(); 0430 d->menu_keyframe.exec(glob); 0431 } 0432 else if ( d->menu_property.property() ) 0433 { 0434 d->menu_property.refresh_actions(); 0435 d->menu_property.exec(glob); 0436 } 0437 #ifndef MOBILE_UI 0438 else if ( auto dn = qobject_cast<model::DocumentNode*>(item.object) ) 0439 { 0440 NodeMenu(dn, d->window, this).exec(glob); 0441 } 0442 #endif 0443 } 0444 0445 void CompoundTimelineWidget::add_keyframe() 0446 { 0447 if ( !d->menu_property.property() ) 0448 return; 0449 0450 d->menu_property.property()->add_smooth_keyframe_undoable( 0451 d->ui.timeline->highlighted_time(), d->menu_property.property()->value() 0452 ); 0453 } 0454 0455 void CompoundTimelineWidget::on_scroll(int amount) 0456 { 0457 int scroll = -d->ui.timeline->header_height() + amount * d->ui.timeline->row_height(); 0458 d->ui.timeline->verticalScrollBar()->setValue(scroll); 0459 } 0460 0461 void CompoundTimelineWidget::keyframe_action_enter() 0462 { 0463 d->keyframe_action(this, d->menu_kf_enter, false, static_cast<QAction*>(sender())); 0464 } 0465 0466 void CompoundTimelineWidget::keyframe_action_exit() 0467 { 0468 d->keyframe_action(this, d->menu_kf_exit, true, static_cast<QAction*>(sender())); 0469 } 0470 0471 void CompoundTimelineWidget::remove_keyframe() 0472 { 0473 if ( !d->menu_kf_exit || !d->menu_property.property() ) 0474 return; 0475 0476 d->menu_property.property()->object()->document()->undo_stack().push( 0477 new command::RemoveKeyframeTime(d->menu_property.property(), d->menu_kf_exit->time()) 0478 ); 0479 } 0480 0481 void CompoundTimelineWidget::load_state(const QByteArray& state) 0482 { 0483 d->ui.splitter->restoreState(state); 0484 } 0485 0486 QByteArray CompoundTimelineWidget::save_state() const 0487 { 0488 return d->ui.splitter->saveState(); 0489 } 0490 0491 #ifndef MOBILE_UI 0492 void CompoundTimelineWidget::set_controller(GlaxnimateWindow* window) 0493 { 0494 d->window = window; 0495 d->menu_property.set_controller(window); 0496 } 0497 #endif 0498 0499 void CompoundTimelineWidget::copy_keyframe() 0500 { 0501 if ( !d->menu_kf_exit || !d->menu_property.property() ) 0502 return; 0503 0504 QMimeData* data = new QMimeData; 0505 QByteArray encoded; 0506 QDataStream stream(&encoded, QIODevice::WriteOnly); 0507 stream << int(d->menu_property.property()->traits().type); 0508 stream << d->menu_kf_exit->value(); 0509 data->setData("application/x.glaxnimate-keyframe", encoded); 0510 QGuiApplication::clipboard()->setMimeData(data); 0511 } 0512 0513 0514 void CompoundTimelineWidget::collapse_index(const QModelIndex& index) 0515 { 0516 d->ui.timeline->collapse(index); 0517 } 0518 0519 void CompoundTimelineWidget::expand_index(const QModelIndex& index) 0520 { 0521 d->ui.timeline->expand(index); 0522 } 0523 0524 0525 void CompoundTimelineWidget::click_index ( const QModelIndex& index ) 0526 { 0527 auto source_index = d->comp_model.mapToSource(index); 0528 if ( auto node = d->property_model.visual_node(source_index) ) 0529 { 0530 if ( index.column() == item_models::PropertyModelFull::ColumnVisible ) 0531 node->visible.set_undoable(!node->visible.get()); 0532 else if ( index.column() == item_models::PropertyModelFull::ColumnLocked ) 0533 node->locked.set_undoable(!node->locked.get()); 0534 } 0535 else if ( auto anprop = d->property_model.animatable(source_index) ) 0536 { 0537 if ( index.column() == item_models::PropertyModelFull::ColumnToggleKeyframe ) 0538 { 0539 auto time = d->property_model.document()->current_time(); 0540 if ( anprop->has_keyframe(time) ) 0541 { 0542 d->property_model.document()->push_command(new command::RemoveKeyframeTime(anprop, time)); 0543 } 0544 else 0545 { 0546 d->property_model.document()->push_command(new command::SetKeyframe(anprop, time, anprop->value(), true)); 0547 } 0548 } 0549 else if ( index.column() == item_models::PropertyModelFull::ColumnPrevKeyframe ) 0550 { 0551 if ( anprop->keyframe_count() < 2 ) 0552 return; 0553 0554 auto time = d->property_model.document()->current_time(); 0555 auto kfindex = anprop->keyframe_index(time); 0556 auto kf = anprop->keyframe(kfindex); 0557 0558 if ( qFuzzyCompare(kf->time(), time) || kf->time() > time ) 0559 { 0560 kfindex -= 1; 0561 } 0562 0563 if ( kfindex < 0 ) 0564 kfindex = anprop->keyframe_count() - 1; 0565 0566 d->property_model.document()->set_current_time(anprop->keyframe(kfindex)->time()); 0567 } 0568 else if ( index.column() == item_models::PropertyModelFull::ColumnNextKeyframe ) 0569 { 0570 if ( anprop->keyframe_count() < 2 ) 0571 return; 0572 0573 auto time = d->property_model.document()->current_time(); 0574 auto kfindex = anprop->keyframe_index(time); 0575 auto kf = anprop->keyframe(kfindex); 0576 0577 if ( qFuzzyCompare(kf->time(), time) || kf->time() < time ) 0578 { 0579 kfindex += 1; 0580 } 0581 0582 if ( kfindex == anprop->keyframe_count() ) 0583 kfindex = 0; 0584 0585 d->property_model.document()->set_current_time(anprop->keyframe(kfindex)->time()); 0586 } 0587 } 0588 } 0589 0590 void CompoundTimelineWidget::_on_selection_changed(const QItemSelection &selected, const QItemSelection &deselected) 0591 { 0592 std::vector<model::VisualNode*> selected_nodes; 0593 std::vector<model::VisualNode*> deselected_nodes; 0594 0595 for ( const auto& index : selected.indexes() ) 0596 { 0597 if ( auto node = d->index_node_or_parent(d->comp_model.mapToSource(index)) ) 0598 selected_nodes.push_back(node); 0599 } 0600 0601 for ( const auto& index : deselected.indexes() ) 0602 { 0603 if ( auto node = d->index_node_or_parent(d->comp_model.mapToSource(index)) ) 0604 deselected_nodes.push_back(node); 0605 } 0606 0607 d->ui.timeline->select(selected, deselected); 0608 0609 Q_EMIT selection_changed(selected_nodes, deselected_nodes); 0610 } 0611 0612 QAbstractItemModel * CompoundTimelineWidget::raw_model() const 0613 { 0614 return &d->property_model; 0615 } 0616 0617 QAbstractItemModel * CompoundTimelineWidget::filtered_model() const 0618 { 0619 return &d->comp_model; 0620 } 0621 0622 TimelineWidget * CompoundTimelineWidget::timeline() const 0623 { 0624 return d->ui.timeline; 0625 } 0626 0627 void CompoundTimelineWidget::rows_removed(const QModelIndex& index, int first, int last) 0628 { 0629 if ( d->property_model.document() ) 0630 { 0631 for ( int i = first; i <= last; i++ ) 0632 { 0633 auto id = d->property_model.index(i, 0, index).internalId(); 0634 if ( id == d->comp_model.get_root_id() ) 0635 { 0636 set_composition(d->comp_model.composition()); 0637 return; 0638 } 0639 } 0640 } 0641 } 0642 0643 bool CompoundTimelineWidget::eventFilter(QObject*, QEvent* event) 0644 { 0645 // For some reason scrolling on the tree view doesn't respect step size 0646 if ( event->type() == QEvent::Wheel ) 0647 { 0648 QApplication::sendEvent(d->ui.scrollbar, event); 0649 return true; 0650 } 0651 0652 return false; 0653 } 0654 0655 void CompoundTimelineWidget::reset_view() 0656 { 0657 d->ui.timeline->reset_view(); 0658 } 0659 0660 void CompoundTimelineWidget::select(const std::vector<model::VisualNode *>& selected, const std::vector<model::VisualNode *>& deselected) 0661 { 0662 QItemSelection selected_indices; 0663 QItemSelection deselected_indices; 0664 0665 for ( const auto& node : selected ) 0666 { 0667 auto index = d->comp_model.mapFromSource(d->property_model.node_index(node)); 0668 if ( index.isValid() ) 0669 selected_indices.push_back(QItemSelectionRange(index)); 0670 } 0671 0672 for ( const auto& node : deselected ) 0673 { 0674 auto index = d->comp_model.mapFromSource(d->property_model.node_index(node)); 0675 if ( index.isValid() ) 0676 deselected_indices.push_back(QItemSelectionRange(index)); 0677 } 0678 0679 d->ui.timeline->select(selected_indices, deselected_indices); 0680 0681 0682 d->ui.properties->selectionModel()->select( 0683 selected_indices, 0684 QItemSelectionModel::Rows | QItemSelectionModel::Select 0685 ); 0686 0687 d->ui.properties->selectionModel()->select( 0688 deselected_indices, 0689 QItemSelectionModel::Rows | QItemSelectionModel::Deselect 0690 ); 0691 } 0692 0693 model::DocumentNode * glaxnimate::gui::CompoundTimelineWidget::current_node() const 0694 { 0695 return d->index_node_or_parent(d->comp_model.mapToSource(d->ui.properties->currentIndex())); 0696 } 0697 0698 QModelIndex glaxnimate::gui::CompoundTimelineWidget::current_index_raw() const 0699 { 0700 return d->comp_model.mapToSource(d->ui.properties->currentIndex()); 0701 } 0702 0703 QModelIndex glaxnimate::gui::CompoundTimelineWidget::current_index_filtered() const 0704 { 0705 return d->ui.properties->currentIndex(); 0706 }