diff --git a/plugins/dockers/storyboarddocker/storyboardModel.cpp b/plugins/dockers/storyboarddocker/storyboardModel.cpp index 69b9dfe521..f0c4c6e45c 100644 --- a/plugins/dockers/storyboarddocker/storyboardModel.cpp +++ b/plugins/dockers/storyboarddocker/storyboardModel.cpp @@ -1,466 +1,487 @@ /* * Copyright (c) 2020 Saurabh Kumar * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "storyboardModel.h" #include #include #include StoryboardModel::StoryboardModel(QObject *parent) : QAbstractItemModel(parent) { connect(this, SIGNAL(rowsInserted(const QModelIndex, int, int)), this, SLOT(slotInsertChildRows(const QModelIndex, int, int))); } QModelIndex StoryboardModel::index(int row, int column, const QModelIndex &parent) const { if (!hasIndex(row, column, parent)) { return QModelIndex(); } if (row < 0 || row >= rowCount(parent)) { return QModelIndex(); } if (column !=0) { return QModelIndex(); } //1st level node has invalid parent if (!parent.isValid()) { return createIndex(row, column, m_items.at(row)); } else if (!parent.parent().isValid()) { StoryboardItem *parentItem = m_items.at(parent.row()); StoryboardChild *childItem = parentItem->child(row); if (childItem) { return createIndex(row, column, childItem); } } return QModelIndex(); } QModelIndex StoryboardModel::parent(const QModelIndex &index) const { if (!index.isValid()) { return QModelIndex(); } //no parent for 1st level node StoryboardItem *childItemFirstLevel = static_cast(index.internalPointer()); if (m_items.contains(childItemFirstLevel)) { return QModelIndex(); } //return parent only for 2nd level nodes StoryboardChild *childItem = static_cast(index.internalPointer()); StoryboardItem *parentItem = childItem->parent(); int indexOfParent = m_items.indexOf(const_cast(parentItem)); return createIndex(indexOfParent, 0, parentItem); } int StoryboardModel::rowCount(const QModelIndex &parent) const { if (!parent.isValid()) { return m_items.count(); } else if (!parent.parent().isValid()) { StoryboardItem *parentItem = m_items.at(parent.row()); return parentItem->childCount(); } return 0; //2nd level nodes have no child } int StoryboardModel::columnCount(const QModelIndex &parent) const { if (!parent.isValid()) { return 1; } //1st level nodes have 1 column if (!parent.parent().isValid()) { return 1; } //end level nodes have no child return 0; } QVariant StoryboardModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } //return data only for the storyboardChild i.e. 2nd level nodes if (!index.parent().isValid()) { return QVariant(); } if (role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::UserRole) { StoryboardChild *child = m_items.at(index.parent().row())->child(index.row()); if (index.row() > 3) { if (role == Qt::UserRole) { //scroll bar position CommentBox commentBox = qvariant_cast(child->data()); return commentBox.scrollValue; } else { CommentBox commentBox = qvariant_cast(child->data()); return commentBox.content; } } return child->data(); } return QVariant(); } bool StoryboardModel::setData(const QModelIndex & index, const QVariant & value, int role) { if (index.isValid() && (role == Qt::EditRole || role == Qt::DisplayRole)) { if (!index.parent().isValid()) { return false; } StoryboardChild *child = m_items.at(index.parent().row())->child(index.row()); if (child) { int fps = 24; if ((index.row() <= 3 && index.row() != 1) && value.toInt() < 0) { return false; } if (index.row() == 3 && value.toInt() >= fps) { //TODO : set fps QModelIndex secondIndex = index.siblingAtRow(2); setData(secondIndex, secondIndex.data().toInt() + value.toInt() / fps, role); child->setData(value.toInt() % fps); } else if (index.row() > 3) { CommentBox commentBox = qvariant_cast(child->data()); commentBox.content = value.toString(); child->setData(QVariant::fromValue(commentBox)); } else { child->setData(value); } emit dataChanged(index, index); return true; } } return false; } bool StoryboardModel::setCommentScrollData(const QModelIndex & index, const QVariant & value) { StoryboardChild *child = m_items.at(index.parent().row())->child(index.row()); if (child) { CommentBox commentBox = qvariant_cast(child->data()); commentBox.scrollValue = value.toInt(); child->setData(QVariant::fromValue(commentBox)); return true; } return false; } Qt::ItemFlags StoryboardModel::flags(const QModelIndex & index) const { if(!index.isValid()) { return Qt::ItemIsDropEnabled; } //1st level nodes if (!index.parent().isValid()) { return Qt::ItemIsDragEnabled | Qt::ItemIsSelectable | Qt::ItemIsEnabled; } //2nd level nodes return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren; } bool StoryboardModel::insertRows(int position, int rows, const QModelIndex &parent) { if (!parent.isValid()) { if (position < 0 || position > m_items.count()) { return false; } beginInsertRows(QModelIndex(), position, position+rows-1); for (int row = 0; row < rows; ++row) { StoryboardItem *newItem = new StoryboardItem(); m_items.insert(position, newItem); } endInsertRows(); return true; } else if (!parent.parent().isValid()) { //insert 2nd level nodes StoryboardItem *item = m_items.at(parent.row()); if (position < 0 || position > item->childCount()) { return false; } beginInsertRows(parent, position, position+rows-1); for (int row = 0; row < rows; ++row) { item->insertChild(position, QVariant()); } endInsertRows(); return true; } //we can't insert to 2nd level nodes as they are leaf nodes return false; } bool StoryboardModel::removeRows(int position, int rows, const QModelIndex &parent) { //remove 1st level nodes if (!parent.isValid()) { if (position < 0 || position >= m_items.count()) { return false; } beginRemoveRows(QModelIndex(), position, position+rows-1); for (int row = position + rows - 1; row >= position; row--) { delete m_items.at(row); m_items.removeAt(row); } endRemoveRows(); return true; } else if (!parent.parent().isValid()) { //remove 2nd level nodes StoryboardItem *item = m_items.at(parent.row()); if (position < 0 || position >= item->childCount()) { return false; } if (m_items.contains(item)) { beginRemoveRows(parent, position, position+rows-1); for (int row = 0; row < rows; ++row) { item->removeChild(position); } endRemoveRows(); return true; } } //2nd level node has no child return false; } bool StoryboardModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) { if (sourceParent != destinationParent) { return false; } if (destinationChild == sourceRow || destinationChild == sourceRow + 1) { return false; } if (destinationChild > sourceRow + count - 1) { //we adjust for the upward shift, see qt doc for why this is needed beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destinationParent, destinationChild + count - 1); destinationChild = destinationChild - count; } else { beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destinationParent, destinationChild); } //for moves within the 1st level nodes for comment nodes if (sourceParent == destinationParent && sourceParent.isValid() && !sourceParent.parent().isValid()) { const QModelIndex parent = sourceParent; for (int row = 0; row < count; row++) { if (sourceRow < 4 || sourceRow >= rowCount(parent)) { return false; } if (destinationChild + row < 4 || destinationChild + row >= rowCount(parent)) { return false; } StoryboardItem *item = m_items.at(parent.row()); item->moveChild(sourceRow, destinationChild + row); } endMoveRows(); return true; } else if (!sourceParent.isValid()) { //for moves of 1st level nodes for (int row = 0; row < count; row++) { if (sourceRow < 0 || sourceRow >= rowCount()) { return false; } if (destinationChild + row < 0 || destinationChild + row >= rowCount()) { return false; } m_items.move(sourceRow, destinationChild + row); } endMoveRows(); return true; } else { return false; } } QMimeData *StoryboardModel::mimeData(const QModelIndexList &indexes) const { QMimeData *mimeData = new QMimeData(); QByteArray encodeData; QDataStream stream(&encodeData, QIODevice::WriteOnly); //take the row number of the index where drag started foreach (QModelIndex index, indexes) { if (index.isValid()) { int row = index.row(); stream << row; } } mimeData->setData("application/x-qabstractitemmodeldatalist", encodeData); //default mimetype return mimeData; } bool StoryboardModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { if (action == Qt::IgnoreAction) { return false; } if (action == Qt::MoveAction && data->hasFormat("application/x-qabstractitemmodeldatalist")) { QByteArray bytes = data->data("application/x-qabstractitemmodeldatalist"); QDataStream stream(&bytes, QIODevice::ReadOnly); if (parent.isValid()) { return false; } int sourceRow; QModelIndexList moveRowIndexes; while (!stream.atEnd()) { stream >> sourceRow; QModelIndex index = this->index(sourceRow, 0); moveRowIndexes.append(index); } moveRows(QModelIndex(), moveRowIndexes.at(0).row(), moveRowIndexes.count(), parent, row); //returning true deletes the source row return false; } return false; } Qt::DropActions StoryboardModel::supportedDropActions() const { return Qt::CopyAction | Qt::MoveAction; } Qt::DropActions StoryboardModel::supportedDragActions() const { return Qt::CopyAction | Qt::MoveAction; } int StoryboardModel::visibleCommentCount() const { int visibleComments = 0; foreach(Comment comment, m_commentList) { if (comment.visibility) { visibleComments++; } } return visibleComments; } int StoryboardModel::visibleCommentsUpto(QModelIndex index) const { int commentRow = index.row() - 4; int visibleComments = 0; for (int row = 0; row < commentRow; row++) { if (m_commentList.at(row).visibility) { visibleComments++; } } return visibleComments; } void StoryboardModel::setCommentModel(CommentModel *commentModel) { m_commentModel = commentModel; connect(m_commentModel, SIGNAL(dataChanged(const QModelIndex ,const QModelIndex)), this, SLOT(slotCommentDataChanged())); connect(m_commentModel, SIGNAL(rowsRemoved(const QModelIndex ,int, int)), this, SLOT(slotCommentRowRemoved(const QModelIndex ,int, int))); connect(m_commentModel, SIGNAL(rowsInserted(const QModelIndex, int, int)), this, SLOT(slotCommentRowInserted(const QModelIndex, int, int))); connect(m_commentModel, SIGNAL(rowsMoved(const QModelIndex, int, int, const QModelIndex, int)), this, SLOT(slotCommentRowMoved(const QModelIndex, int, int, const QModelIndex, int))); } Comment StoryboardModel::getComment(int row) const { return m_commentList.at(row); } +QModelIndex StoryboardModel::indexFromFrame(int frame) const +{ + int end = rowCount(), begin = 0; + while (end >= begin) { + int row = begin + (end - begin) / 2; + QModelIndex parentIndex = index(row, 0); + QModelIndex childIndex = index(0, 0, parentIndex); + if (childIndex.data().toInt() == frame) { + return parentIndex; + } + else if (childIndex.data().toInt() > frame) { + end = row - 1; + } + else if (childIndex.data().toInt() < frame) { + begin = row + 1; + } + } + return QModelIndex(); +} + + void StoryboardModel::slotCommentDataChanged() { m_commentList = m_commentModel->m_commentList; emit(layoutChanged()); } void StoryboardModel::slotCommentRowInserted(const QModelIndex parent, int first, int last) { int numItems = rowCount(); for(int row = 0; row < numItems; row++) { QModelIndex parentIndex = index(row, 0); insertRows(4 + first, last - first + 1, parentIndex); //four indices are already there } slotCommentDataChanged(); } void StoryboardModel::slotCommentRowRemoved(const QModelIndex parent, int first, int last) { int numItems = rowCount(); for(int row = 0; row < numItems; row++) { QModelIndex parentIndex = index(row, 0); removeRows(4 + first, last - first + 1, parentIndex); } slotCommentDataChanged(); } void StoryboardModel::slotCommentRowMoved(const QModelIndex &sourceParent, int start, int end, const QModelIndex &destinationParent, int destinationRow) { int numItems = rowCount(); for(int row = 0; row < numItems; row++) { QModelIndex parentIndex = index(row, 0); moveRows(parentIndex, start + 4, end - start + 1, parentIndex, destinationRow + 4); } slotCommentDataChanged(); } void StoryboardModel::slotInsertChildRows(const QModelIndex parent, int first, int last) { if (!parent.isValid()) { int rows = last - first + 1; for (int row = 0; row < rows; ++row) { QModelIndex parentIndex = index(first + row, 0); insertRows(0, 4 + m_commentList.count(), parentIndex); setData (index (0, 0, parentIndex), m_lastFrame); m_lastFrame++; QString sceneName = "scene " + QString::number(m_lastScene); setData (index (1, 0, parentIndex), sceneName); m_lastScene++; setData (index (2, 0, parentIndex), 20); setData (index (3, 0, parentIndex), 20); } } } diff --git a/plugins/dockers/storyboarddocker/storyboardModel.h b/plugins/dockers/storyboarddocker/storyboardModel.h index 67d471b0c8..8eca29297b 100644 --- a/plugins/dockers/storyboarddocker/storyboardModel.h +++ b/plugins/dockers/storyboarddocker/storyboardModel.h @@ -1,103 +1,105 @@ /* * Copyright (c) 2020 Saurabh Kumar * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef STORYBOARD_MODEL #define STORYBOARD_MODEL #include #include #include "storyboardItem.h" #include "commentModel.h" #include /* The main storyboard model. */ class CommentBox { public: CommentBox() : content("") , scrollValue(0) {} CommentBox(const CommentBox& other) : content(other.content) , scrollValue(other.scrollValue) {} ~CommentBox() {} QVariant content; QVariant scrollValue; }; Q_DECLARE_METATYPE(CommentBox) class KRITAUI_EXPORT StoryboardModel : public QAbstractItemModel { Q_OBJECT public: StoryboardModel(QObject *parent = nullptr); QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &index) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; bool setCommentScrollData(const QModelIndex & index, const QVariant & value); Qt::ItemFlags flags(const QModelIndex &index) const override; //for removing and inserting rows bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex()); bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex()); bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild); //for drag and drop QMimeData *mimeData(const QModelIndexList &indexes) const; bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); Qt::DropActions supportedDropActions() const override; Qt::DropActions supportedDragActions() const override; //these function access the value from the comment model int visibleCommentCount() const; int visibleCommentsUpto(QModelIndex index) const; void setCommentModel(CommentModel *commentModel); Comment getComment(int row) const; + QModelIndex indexFromFrame(int frame) const; + private Q_SLOTS: void slotCommentDataChanged(); void slotCommentRowInserted(const QModelIndex, int, int); void slotCommentRowRemoved(const QModelIndex, int, int); void slotCommentRowMoved(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild); void slotInsertChildRows(const QModelIndex parent, int first, int last); private: QVector m_items; QVector m_commentList; CommentModel *m_commentModel; int m_lastFrame = 0; int m_lastScene = 0; }; #endif \ No newline at end of file diff --git a/plugins/dockers/storyboarddocker/storyboardView.cpp b/plugins/dockers/storyboarddocker/storyboardView.cpp index abda9bde95..b195345234 100644 --- a/plugins/dockers/storyboarddocker/storyboardView.cpp +++ b/plugins/dockers/storyboarddocker/storyboardView.cpp @@ -1,238 +1,248 @@ /* Copyright (c) 2020 Saurabh Kumar This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include "storyboardView.h" #include "storyboardModel.h" /** * This view draws the children of every index in the first column of * the model inside the parent * * */ StoryboardView::StoryboardView(QWidget *parent) : QListView(parent) , m_commentIsVisible(true) , m_thumbnailIsVisible(true) { setSelectionBehavior(SelectRows); setDefaultDropAction(Qt::MoveAction); setResizeMode(QListView::Adjust); setUniformItemSizes(true); setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); QWidget::setMouseTracking(true); setContextMenuPolicy(Qt::CustomContextMenu); setDragEnabled(true); viewport()->setAcceptDrops(true); setDropIndicatorShown(true); setDragDropMode(QAbstractItemView::InternalMove); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(slotContextMenuRequested(const QPoint &))); } StoryboardView::~StoryboardView() {} void StoryboardView::paintEvent(QPaintEvent *event) { event->accept(); QListView::paintEvent(event); //ask delegate to draw the child nodes too QPainter painter(viewport()); int itemNum = model()->rowCount(); for (int row = 0; row < itemNum; row++) { QModelIndex index = model()->index(row, 0); int childNum = model()->rowCount(index); for (int childRow = 0; childRow < childNum; childRow++) { QModelIndex childIndex = model()->index(childRow, 0, index); QStyleOptionViewItem option; if (selectionModel()->isSelected(childIndex)) { option.state |= QStyle::State_Selected; } if (childIndex == selectionModel()->currentIndex()) { option.state |= QStyle::State_HasFocus; } option.font = font(); option.fontMetrics = fontMetrics(); option.rect = visualRect(childIndex); itemDelegate()->paint(&painter, option, childIndex); } } } QRect StoryboardView::visualRect(const QModelIndex &index) const { if (!index.isValid() || !index.parent().isValid()) { return QListView::visualRect(index); } else { QRect parentRect = visualRect(index.parent()); parentRect.setTopLeft(parentRect.topLeft() + QPoint(5, 5)); parentRect.setBottomRight(parentRect.bottomRight() - QPoint(5, 5)); int fontHeight = fontMetrics().height() + 3; int numericFontWidth = fontMetrics().width("0"); int parentWidth = parentRect.width(); int childRow = index.row(); int thumbnailWidth = parentWidth; if (m_itemOrientation == Qt::Horizontal) { thumbnailWidth = 250; } switch (childRow) { case 0: { //the frame thumbnail rect if (!thumbnailIsVisible()) { parentRect.setSize(QSize(3*numericFontWidth + 2, fontHeight)); return parentRect; } parentRect.setSize(QSize(thumbnailWidth, 120)); parentRect.translate(0, fontHeight); return parentRect; } case 1: { QRect itemNameRect = parentRect; itemNameRect.setSize(QSize(thumbnailWidth - (10 * numericFontWidth + 22), fontHeight)); itemNameRect.moveLeft(parentRect.left() + 3*numericFontWidth + 2); return itemNameRect; } case 2: { QRect secondRect = parentRect; secondRect.setSize(QSize(4 * numericFontWidth + 10, fontHeight)); //secondRect.moveRight(parentRect.right() - 3*numericFontWidth -10); secondRect.moveLeft(parentRect.left() - 7*numericFontWidth + thumbnailWidth -20); return secondRect; } case 3: { QRect frameRect = parentRect; frameRect.setSize(QSize(3 * numericFontWidth + 10, fontHeight)); frameRect.moveRight(parentRect.right()); frameRect.moveLeft(parentRect.left() - 3*numericFontWidth + thumbnailWidth - 10); return frameRect; } default: { //comment rect if (!commentIsVisible()) { return QRect(); } int thumbnailheight = thumbnailIsVisible() ? 120 : 0; if (m_itemOrientation == Qt::Vertical) { const StoryboardModel* Model = dynamic_cast(model()); parentRect.setTop(parentRect.top() + thumbnailheight + fontHeight + Model->visibleCommentsUpto(index) * 100); parentRect.setHeight(100); return parentRect; } else { const StoryboardModel* Model = dynamic_cast(model()); int numVisibleComments = Model->visibleCommentCount(); int commentWidth = 200; if (numVisibleComments) { commentWidth = qMax(200, (viewport()->width() - 250) / numVisibleComments); } parentRect.setSize(QSize(commentWidth, thumbnailheight + fontHeight)); parentRect.moveLeft(parentRect.left() + thumbnailWidth + Model->visibleCommentsUpto(index) * commentWidth); return parentRect; } } } } return QRect(); } QModelIndex StoryboardView::indexAt(const QPoint &point) const { QModelIndex index = QListView::indexAt(point); if (index.isValid()) { //look for the index in children of the current index int numChild = model()->rowCount(index); for (int row = 0; row < numChild; row++) { QRect childRect = visualRect(model()->index(row, 0, index)); if (childRect.contains(point)) { return model()->index(row, 0, index); } } } return index; } void StoryboardView::setItemOrientation(Qt::Orientation orientation) { m_itemOrientation = orientation; } Qt::Orientation StoryboardView::itemOrientation() { return m_itemOrientation; } bool StoryboardView::commentIsVisible() const { return m_commentIsVisible; } bool StoryboardView::thumbnailIsVisible() const { return m_thumbnailIsVisible; } void StoryboardView::setCommentVisibility(bool value) { m_commentIsVisible = value; } void StoryboardView::setThumbnailVisibility(bool value) { m_thumbnailIsVisible = value; } void StoryboardView::slotContextMenuRequested(const QPoint &point) { QMenu contextMenu; QModelIndex index = indexAt(point); if (!index.isValid()) { contextMenu.addAction("Add Stroyboard Item", [this, index] {model()->insertRows(model()->rowCount(), 1); }); } else if (index.parent().isValid()) { index = index.parent(); contextMenu.addAction("Add Stroyboard Item After", [this, index] {model()->insertRows(index.row() + 1, 1); }); } if (index.isValid()) { contextMenu.addAction("Add Stroyboard Item Before", [this, index] {model()->insertRows(index.row(), 1); }); contextMenu.addAction("Remove Stroyboard Item", [this, index] {model()->removeRows(index.row(), 1); }); } contextMenu.exec(viewport()->mapToGlobal(point)); } + +void StoryboardView::setCurrentItem(int frame) +{ + const StoryboardModel* Model = dynamic_cast(model()); + QModelIndex index = Model->indexFromFrame(frame); + if (index.isValid()) { + selectionModel()->select(index, QItemSelectionModel::ClearAndSelect); + scrollTo(index); + } +} diff --git a/plugins/dockers/storyboarddocker/storyboardView.h b/plugins/dockers/storyboarddocker/storyboardView.h index 1b09b39a4e..678c1055fa 100644 --- a/plugins/dockers/storyboarddocker/storyboardView.h +++ b/plugins/dockers/storyboarddocker/storyboardView.h @@ -1,61 +1,62 @@ /* Copyright (c) 2020 Saurabh Kumar This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef STORYBOARD_VIEW_H #define STORYBOARD_VIEW_H #include #include class QStyleOptionViewItem; class StoryboardModel; /** * This view draws the children of every index in the first column of * the model inside the parent index * * */ class StoryboardView: public QListView { Q_OBJECT public: explicit StoryboardView(QWidget *parent = 0); ~StoryboardView() override; void paintEvent(QPaintEvent *event) override; QRect visualRect(const QModelIndex &index) const override; QModelIndex indexAt(const QPoint &point) const override; void setItemOrientation(Qt::Orientation orientation); Qt::Orientation itemOrientation(); bool commentIsVisible() const; bool thumbnailIsVisible() const; void setCommentVisibility(bool value); void setThumbnailVisibility(bool value); + void setCurrentItem(int frame); private Q_SLOTS: void slotContextMenuRequested(const QPoint &); private: Qt::Orientation m_itemOrientation; bool m_commentIsVisible; bool m_thumbnailIsVisible; }; #endif diff --git a/plugins/dockers/storyboarddocker/storyboarddocker_dock.cpp b/plugins/dockers/storyboarddocker/storyboarddocker_dock.cpp index eb18a4ce99..02401ed36b 100644 --- a/plugins/dockers/storyboarddocker/storyboarddocker_dock.cpp +++ b/plugins/dockers/storyboarddocker/storyboarddocker_dock.cpp @@ -1,276 +1,309 @@ /* * Copyright (c) 2020 Saurabh Kumar * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "storyboarddocker_dock.h" #include "commentDelegate.h" #include "commentModel.h" #include "storyboardModel.h" #include "storyboardDelegate.h" +#include "storyboardView.h" #include #include #include #include #include +#include #include #include #include #include #include +#include #include "ui_wdgstoryboarddock.h" #include "ui_wdgcommentmenu.h" #include "ui_wdgarrangemenu.h" class CommentMenu: public QMenu { Q_OBJECT public: CommentMenu(QWidget *parent, CommentModel *m_model) : QMenu(parent) , m_menuUI(new Ui_WdgCommentMenu()) , model(m_model) , delegate(new CommentDelegate(this)) { QWidget* commentWidget = new QWidget(this); m_menuUI->setupUi(commentWidget); m_menuUI->fieldListView->setDragEnabled(true); m_menuUI->fieldListView->setAcceptDrops(true); m_menuUI->fieldListView->setDropIndicatorShown(true); m_menuUI->fieldListView->setDragDropMode(QAbstractItemView::InternalMove); m_menuUI->fieldListView->setModel(model); m_menuUI->fieldListView->setItemDelegate(delegate); m_menuUI->fieldListView->setEditTriggers(QAbstractItemView::AnyKeyPressed | QAbstractItemView::DoubleClicked ); m_menuUI->btnAddField->setIcon(KisIconUtils::loadIcon("list-add")); m_menuUI->btnDeleteField->setIcon(KisIconUtils::loadIcon("trash-empty")); m_menuUI->btnAddField->setIconSize(QSize(22, 22)); m_menuUI->btnDeleteField->setIconSize(QSize(22, 22)); connect(m_menuUI->btnAddField, SIGNAL(clicked()), this, SLOT(slotaddItem())); connect(m_menuUI->btnDeleteField, SIGNAL(clicked()), this, SLOT(slotdeleteItem())); KisAction *commentAction = new KisAction(commentWidget); commentAction->setDefaultWidget(commentWidget); this->addAction(commentAction); } private Q_SLOTS: void slotaddItem() { int row = m_menuUI->fieldListView->currentIndex().row()+1; model->insertRows(row, 1); QModelIndex index = model->index(row); m_menuUI->fieldListView->setCurrentIndex(index); m_menuUI->fieldListView->edit(index); } void slotdeleteItem() { model->removeRows(m_menuUI->fieldListView->currentIndex().row(), 1); } private: QScopedPointer m_menuUI; CommentModel *model; CommentDelegate *delegate; }; class ArrangeMenu: public QMenu { public: ArrangeMenu(QWidget *parent) : QMenu(parent) , m_menuUI(new Ui_WdgArrangeMenu()) , modeGroup(new QButtonGroup(this)) , viewGroup(new QButtonGroup(this)) { QWidget* arrangeWidget = new QWidget(this); m_menuUI->setupUi(arrangeWidget); modeGroup->addButton(m_menuUI->btnColumnMode, 0); modeGroup->addButton(m_menuUI->btnRowMode, 1); modeGroup->addButton(m_menuUI->btnGridMode, 2); viewGroup->addButton(m_menuUI->btnAllView, 0); viewGroup->addButton(m_menuUI->btnThumbnailsView, 1); viewGroup->addButton(m_menuUI->btnCommentsView, 2); KisAction *arrangeAction = new KisAction(arrangeWidget); arrangeAction->setDefaultWidget(arrangeWidget); this->addAction(arrangeAction); } QButtonGroup* getModeGroup(){ return modeGroup;} QButtonGroup* getViewGroup(){ return viewGroup;} private: QScopedPointer m_menuUI; QButtonGroup *modeGroup; QButtonGroup *viewGroup; }; StoryboardDockerDock::StoryboardDockerDock( ) : QDockWidget(i18n("Storyboard")) , m_ui(new Ui_WdgStoryboardDock()) { QWidget* mainWidget = new QWidget(this); setWidget(mainWidget); m_ui->setupUi(mainWidget); m_exportMenu = new QMenu(this); m_ui->btnExport->setMenu(m_exportMenu); m_ui->btnExport->setPopupMode(QToolButton::MenuButtonPopup); m_exportAsPdfAction = new KisAction("Export as PDF", m_exportMenu); m_exportMenu->addAction(m_exportAsPdfAction); m_exportAsSvgAction = new KisAction("Export as SVG"); m_exportMenu->addAction(m_exportAsSvgAction); connect(m_exportAsPdfAction, SIGNAL(triggered()), this, SLOT(slotExportAsPdf())); connect(m_exportAsSvgAction, SIGNAL(triggered()), this, SLOT(slotExportAsSvg())); m_commentModel = new CommentModel(this); m_commentMenu = new CommentMenu(this, m_commentModel); m_ui->btnComment->setMenu(m_commentMenu); m_ui->btnComment->setPopupMode(QToolButton::MenuButtonPopup); m_lockAction = new KisAction(KisIconUtils::loadIcon("unlocked"), "Lock", m_ui->btnLock); m_lockAction->setCheckable(true); m_ui->btnLock->setDefaultAction(m_lockAction); m_ui->btnLock->setIconSize(QSize(22, 22)); connect(m_lockAction, SIGNAL(toggled(bool)), this, SLOT(slotLockClicked(bool))); m_arrangeMenu = new ArrangeMenu(this); m_ui->btnArrange->setMenu(m_arrangeMenu); m_ui->btnArrange->setPopupMode(QToolButton::InstantPopup); m_ui->btnArrange->setIcon(KisIconUtils::loadIcon("view-choose")); m_ui->btnArrange->setIconSize(QSize(22, 22)); m_modeGroup = m_arrangeMenu->getModeGroup(); m_viewGroup = m_arrangeMenu->getViewGroup(); connect(m_modeGroup, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(slotModeChanged(QAbstractButton*))); connect(m_viewGroup, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(slotViewChanged(QAbstractButton*))); m_storyboardModel = new StoryboardModel(this); StoryboardDelegate *delegate = new StoryboardDelegate(this); delegate->setView(m_ui->listView); m_ui->listView->setModel(m_storyboardModel); m_ui->listView->setItemDelegate(delegate); - delegate->setView(m_ui->listView); + + connect(m_ui->listView, SIGNAL(currentItemChanged(int)), this, SLOT(slotChangeFrameGlobal(int))); + + connect(m_ui->listView->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), + this, SLOT(slotChangeFrameGlobal(QItemSelection, QItemSelection))); m_storyboardModel->insertRows(0, 10); m_storyboardModel->setCommentModel(m_commentModel); m_modeGroup->button(2)->click(); m_viewGroup->button(0)->click(); } StoryboardDockerDock::~StoryboardDockerDock() { } void StoryboardDockerDock::setCanvas(KoCanvasBase *canvas) { + if (m_canvas == canvas) { + return; + } + + m_canvas = dynamic_cast(canvas); + setEnabled(m_canvas); + + if (m_canvas) { + connect(m_canvas->image()->animationInterface(), SIGNAL(sigUiTimeChanged(int)), this, SLOT(slotFrameChanged(int))); + } +} + +void StoryboardDockerDock::unsetCanvas() +{ + setCanvas(0); } void StoryboardDockerDock::setViewManager(KisViewManager* kisview) { } void StoryboardDockerDock::slotExportAsPdf() { qDebug()<<"export as pdf"; slotExport("pdf"); } void StoryboardDockerDock::slotExportAsSvg() { qDebug()<<"export as svg"; slotExport("svg"); } void StoryboardDockerDock::slotExport(QString mode) { qDebug()<<"mode is "<setIcon(KisIconUtils::loadIcon("locked")); } else { m_lockAction->setIcon(KisIconUtils::loadIcon("unlocked")); } } void StoryboardDockerDock::slotModeChanged(QAbstractButton* button) { QString mode = button->text(); if (mode == "Column") { m_ui->listView->setFlow(QListView::LeftToRight); m_ui->listView->setWrapping(false); m_ui->listView->setItemOrientation(Qt::Vertical); m_viewGroup->button(2)->setEnabled(true); } else if (mode == "Row") { m_ui->listView->setFlow(QListView::TopToBottom); m_ui->listView->setWrapping(false); m_ui->listView->setItemOrientation(Qt::Horizontal); m_viewGroup->button(2)->setEnabled(false); //disable the comments only view } else if (mode == "Grid") { m_ui->listView->setFlow(QListView::LeftToRight); m_ui->listView->setWrapping(true); m_ui->listView->setItemOrientation(Qt::Vertical); m_viewGroup->button(2)->setEnabled(true); } m_storyboardModel->layoutChanged(); } void StoryboardDockerDock::slotViewChanged(QAbstractButton* button) { QString view = button->text(); if (view == "All") { m_ui->listView->setCommentVisibility(true); m_ui->listView->setThumbnailVisibility(true); m_modeGroup->button(1)->setEnabled(true); } else if (view == "Thumbnails Only") { m_ui->listView->setCommentVisibility(false); m_ui->listView->setThumbnailVisibility(true); m_modeGroup->button(1)->setEnabled(true); } else if (view == "Comments Only") { m_ui->listView->setCommentVisibility(true); m_ui->listView->setThumbnailVisibility(false); m_modeGroup->button(1)->setEnabled(false); //disable the row mode } m_storyboardModel->layoutChanged(); } +void StoryboardDockerDock::slotFrameChanged(int frameId) +{ + m_ui->listView->setCurrentItem(frameId); +} + +void StoryboardDockerDock::slotChangeFrameGlobal(QItemSelection selected, QItemSelection deselected) +{ + int frameId = m_storyboardModel->data(m_storyboardModel->index(0, 0, selected.indexes().at(0))).toInt(); + m_canvas->image()->animationInterface()->switchCurrentTimeAsync(frameId); +} + #include "storyboarddocker_dock.moc" \ No newline at end of file diff --git a/plugins/dockers/storyboarddocker/storyboarddocker_dock.h b/plugins/dockers/storyboarddocker/storyboarddocker_dock.h index 522adbcf87..b7603aefe3 100644 --- a/plugins/dockers/storyboarddocker/storyboarddocker_dock.h +++ b/plugins/dockers/storyboarddocker/storyboarddocker_dock.h @@ -1,75 +1,81 @@ /* * Copyright (c) 2020 Saurabh Kumar * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _STORYBOARDDOCKER_DOCK_H_ #define _STORYBOARDDOCKER_DOCK_H_ #include #include +#include #include #include #include class Ui_WdgStoryboardDock; class CommentMenu; class ArrangeMenu; class CommentModel; class StoryboardModel; class StoryboardDockerDock : public QDockWidget, public KisMainwindowObserver{ Q_OBJECT public: StoryboardDockerDock(); ~StoryboardDockerDock() override; QString observerName() override { return "StoryboardDockerDock"; } void setCanvas(KoCanvasBase *canvas) override; - void unsetCanvas() override {} + void unsetCanvas() override; void setViewManager(KisViewManager* kisview) override; +private Q_SLOTS: + void slotExportAsPdf(); + void slotExportAsSvg(); + void slotExport(QString); + + void slotLockClicked(bool); + void slotModeChanged(QAbstractButton*); + void slotViewChanged(QAbstractButton*); + + void slotFrameChanged(int frameId); + void slotChangeFrameGlobal(QItemSelection selected, QItemSelection deselected); + private: + KisCanvas2* m_canvas; QScopedPointer m_ui; QMenu *m_exportMenu; KisAction *m_exportAsPdfAction; KisAction *m_exportAsSvgAction; QPointer m_commentModel; CommentMenu *m_commentMenu; KisAction *m_lockAction; ArrangeMenu *m_arrangeMenu; QButtonGroup *m_modeGroup; QButtonGroup *m_viewGroup; QPointer m_storyboardModel; -private Q_SLOTS: - void slotExportAsPdf(); - void slotExportAsSvg(); - void slotExport(QString); - - void slotLockClicked(bool); - void slotModeChanged(QAbstractButton*); - void slotViewChanged(QAbstractButton*); }; #endif