timearea_dock.cpp 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. /*
  2. * timearea_dock.cpp - time area / timeline dock
  3. * Copyright (C) 2017 caryoscelus
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include <QMenu>
  19. #include <QContextMenuEvent>
  20. #include <QScrollBar>
  21. #include <core/node/abstract_value.h>
  22. #include <widgets/timeline_area.h>
  23. #include <generic/timeline_editor.h>
  24. #include <models/node_list.h>
  25. #include "timearea_dock.h"
  26. #include "ui_timearea_dock.h"
  27. namespace rainynite::studio {
  28. TimeareaDock::TimeareaDock(shared_ptr<EditorContext> context_, QWidget* parent) :
  29. DockWidget(parent),
  30. ContextListener(context_),
  31. ui(make_unique<Ui::TimeareaDock>()),
  32. node_list_model(make_unique<NodeListModel>())
  33. {
  34. ui->setupUi(this);
  35. auto cursor = add_canvas_named_editor(*ui->timeline, "TimelineCursor");
  36. ui->timeline->add_misc_editor(cursor);
  37. ui->timeline->setAlignment(Qt::AlignLeft | Qt::AlignTop);
  38. ui->node_list->setModel(node_list_model.get());
  39. connect(node_list_model.get(), &QAbstractItemModel::layoutChanged, this, &TimeareaDock::update_editors);
  40. connect(node_list_model.get(), &QAbstractItemModel::rowsInserted, this, &TimeareaDock::update_editors);
  41. connect(node_list_model.get(), &QAbstractItemModel::rowsMoved, this, &TimeareaDock::update_editors);
  42. connect(node_list_model.get(), &QAbstractItemModel::rowsRemoved, this, &TimeareaDock::update_editors);
  43. ui->timeline->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  44. ui->node_list->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
  45. connect(ui->timeline->verticalScrollBar(), &QScrollBar::valueChanged, ui->node_list->verticalScrollBar(), &QScrollBar::setValue);
  46. connect(ui->node_list->verticalScrollBar(), &QScrollBar::valueChanged, ui->timeline->verticalScrollBar(), &QScrollBar::setValue);
  47. connect(ui->timeline->horizontalScrollBar(), &QScrollBar::valueChanged, this, &TimeareaDock::update_ruler);
  48. ui->timeline->installEventFilter(this);
  49. update_ruler();
  50. set_context(get_context());
  51. }
  52. TimeareaDock::~TimeareaDock() {
  53. }
  54. bool TimeareaDock::eventFilter(QObject* object, QEvent* event) {
  55. if (object == ui->timeline && event->type() == QEvent::Resize)
  56. update_ruler();
  57. return false;
  58. }
  59. void TimeareaDock::contextMenuEvent(QContextMenuEvent* event) {
  60. auto index = ui->node_list->selectionModel()->currentIndex();
  61. if (index.isValid()) {
  62. QMenu menu(this);
  63. if (index.row() == node_list_model->rowCount()-1 && !pinned) {
  64. menu.addAction(
  65. QIcon::fromTheme("list-add"),
  66. "Pin to timeline area",
  67. [this]() {
  68. pinned = true;
  69. }
  70. );
  71. } else {
  72. menu.addAction(
  73. QIcon::fromTheme("list-remove"),
  74. "Remove from timeline area",
  75. [this, index]() {
  76. node_list_model->removeRow(index.row());
  77. }
  78. );
  79. }
  80. menu.exec(event->globalPos());
  81. }
  82. }
  83. void TimeareaDock::set_context(shared_ptr<EditorContext> context) {
  84. ContextListener::set_context(context);
  85. ui->timeline->set_context(context);
  86. connect_boost(
  87. context->changed_active_node(),
  88. [this](core::AbstractReference node) {
  89. if (!pinned && node_list_model->rowCount() > 0)
  90. node_list_model->removeRow(node_list_model->rowCount()-1);
  91. pinned = !node_list_model->insert_unique_node(node);
  92. }
  93. );
  94. }
  95. void TimeareaDock::update_editors() {
  96. ui->timeline->clear_editors();
  97. for (int i = 0; i < node_list_model->rowCount(); ++i) {
  98. auto index = node_list_model->index(i, 0);
  99. auto node = node_list_model->get_node(index);
  100. if (auto editor = add_canvas_node_editor(*ui->timeline, node)) {
  101. auto rect = ui->node_list->visualRect(index);
  102. if (auto timeline_editor = dynamic_cast<TimelineEditor*>(editor.get()))
  103. timeline_editor->set_position_hint(rect.y(), rect.height());
  104. }
  105. }
  106. }
  107. void TimeareaDock::update_ruler() {
  108. if (ui == nullptr)
  109. return;
  110. auto timeline_x = ui->timeline->mapFromScene(0, 0);
  111. auto global_x = ui->timeline->mapToGlobal(timeline_x);
  112. // no idea why that +4 offset is necessary..
  113. ui->ruler->set_scroll(ui->ruler->mapFromGlobal(global_x).x()+4);
  114. ui->ruler->set_zoom(ui->timeline->transform().m11());
  115. }
  116. } // namespace rainynite::studio