diff --git a/plugins/dockers/CMakeLists.txt b/plugins/dockers/CMakeLists.txt index db5e216577..4d24a32269 100644 --- a/plugins/dockers/CMakeLists.txt +++ b/plugins/dockers/CMakeLists.txt @@ -1,36 +1,37 @@ add_subdirectory(layerdocker) if(HAVE_OPENEXR) add_subdirectory(smallcolorselector) endif() add_subdirectory(specificcolorselector) add_subdirectory(digitalmixer) add_subdirectory(advancedcolorselector) add_subdirectory(presetdocker) add_subdirectory(historydocker) add_subdirectory(channeldocker) add_subdirectory(artisticcolorselector) add_subdirectory(tasksetdocker) add_subdirectory(compositiondocker) add_subdirectory(patterndocker) add_subdirectory(griddocker) add_subdirectory(arrangedocker) if(HAVE_OCIO) add_subdirectory(lut) endif() add_subdirectory(overview) add_subdirectory(palettedocker) add_subdirectory(animation) add_subdirectory(presethistory) add_subdirectory(svgcollectiondocker) add_subdirectory(histogram) add_subdirectory(gamutmask) if(NOT APPLE AND HAVE_QT_QUICK) add_subdirectory(touchdocker) option(ENABLE_CPU_THROTTLE "Build the CPU Throttle Docker" OFF) if (ENABLE_CPU_THROTTLE) add_subdirectory(throttle) endif() endif() add_subdirectory(logdocker) add_subdirectory(snapshotdocker) +add_subdirectory(storyboarddocker) diff --git a/plugins/dockers/storyboarddocker/CMakeLists.txt b/plugins/dockers/storyboarddocker/CMakeLists.txt new file mode 100644 index 0000000000..00a42f89c6 --- /dev/null +++ b/plugins/dockers/storyboarddocker/CMakeLists.txt @@ -0,0 +1,34 @@ +#add_subdirectory(tests) + +set(KRITA_STORYBOARDDOCKER_SOURCES + commentModel.cpp + storyboarddocker.cpp + storyboarddocker_dock.cpp + commentDelegate.cpp + storyboardModel.cpp + storyboardItem.cpp + storyboardDelegate.cpp + storyboardView.cpp + ) + +set(KRITA_STORYBOARDDOCKER_PART_HEADERS + commentModel.h + storyboarddocker.h + storyboarddocker_dock.h + commentDelegate.h + storyboardModel.h + storyboardItem.h + storyboardDelegate.h + storyboardView.h +) + +ki18n_wrap_ui(KRITA_STORYBOARDDOCKER_SOURCES + wdgstoryboarddock.ui wdgarrangemenu.ui wdgcommentmenu.ui +) + +add_library(kritastoryboarddocker MODULE ${KRITA_STORYBOARDDOCKER_SOURCES} + ${KRITA_STORYBOARDDOCKER_PART_HEADERS} +) + +target_link_libraries(kritastoryboarddocker kritaui) +install(TARGETS kritastoryboarddocker DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) diff --git a/plugins/dockers/storyboarddocker/commentDelegate.cpp b/plugins/dockers/storyboarddocker/commentDelegate.cpp new file mode 100644 index 0000000000..9532f65a6e --- /dev/null +++ b/plugins/dockers/storyboarddocker/commentDelegate.cpp @@ -0,0 +1,132 @@ +/* + * 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 "commentDelegate.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +CommentDelegate::CommentDelegate(QObject *parent) + : QStyledItemDelegate(parent) +{ +} + +CommentDelegate::~CommentDelegate() +{ +} + +void CommentDelegate::paint(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + p->save(); + { + QStyle *style = option.widget ? option.widget->style() : QApplication::style(); + style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, p, option.widget); + + p->setFont(option.font); + + { + QIcon icon =index.model()->data(index, Qt::DecorationRole).value(); + QRect r = option.rect; + r.setSize(QSize(22, 22)); + icon.paint(p, r); + } + { + QRect r = option.rect; + r.translate(25, 0); + QString value = index.model()->data(index, Qt::DisplayRole).toString(); + + p->drawText(r, Qt::AlignLeft | Qt::AlignVCenter, value); + } + } + p->restore(); +} + +QSize CommentDelegate::sizeHint(const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + Q_UNUSED(index); + return QSize(option.rect.width(), 22); +} + + +QWidget *CommentDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option , + const QModelIndex &index) const +{ + QLineEdit *editor = new QLineEdit(parent); + return editor; +} + +bool CommentDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) +{ + QStyleOptionViewItem newOption = option; + + if ((event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonDblClick) + && (index.flags() & Qt::ItemIsEnabled)) + { + QMouseEvent *mouseEvent = static_cast(event); + + QRect visibilityRect = option.rect; + visibilityRect.setSize(QSize(22, 22)); + const bool visibilityClicked = visibilityRect.isValid() && + visibilityRect.contains(mouseEvent->pos()); + + const bool leftButton = mouseEvent->buttons() & Qt::LeftButton; + + if (leftButton && visibilityClicked) { + model->setData(index, true, Qt::DecorationRole); + return true; + } + } + return false; +} + +//set the existing data in the editor +void CommentDelegate::setEditorData(QWidget *editor, + const QModelIndex &index) const +{ + QString value = index.model()->data(index, Qt::EditRole).toString(); + + QLineEdit *lineEdit = static_cast(editor); + lineEdit->setText(value); +} + +void CommentDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const +{ + QLineEdit *lineEdit = static_cast(editor); + QString value = lineEdit->text(); + + //don't add empty string + model->setData(index, value, Qt::EditRole); + + //do we need to emit closeEditor() ??? +} + +void CommentDelegate::updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + editor->setGeometry(option.rect); +} \ No newline at end of file diff --git a/plugins/dockers/storyboarddocker/commentDelegate.h b/plugins/dockers/storyboarddocker/commentDelegate.h new file mode 100644 index 0000000000..30aa2e7a12 --- /dev/null +++ b/plugins/dockers/storyboarddocker/commentDelegate.h @@ -0,0 +1,51 @@ +/* + * 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 __COMMENT_DELEGATE_H +#define __COMMENT_DELEGATE_H + +#include + +class CommentModel; + +class CommentDelegate : public QStyledItemDelegate +{ +public: + CommentDelegate(QObject *parent); + ~CommentDelegate() override; + + //display functions + void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; + + + bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index); + + //editor functions. + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const; + + void updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const; +}; + +#endif diff --git a/plugins/dockers/storyboarddocker/commentModel.cpp b/plugins/dockers/storyboarddocker/commentModel.cpp new file mode 100644 index 0000000000..801f70ffb9 --- /dev/null +++ b/plugins/dockers/storyboarddocker/commentModel.cpp @@ -0,0 +1,194 @@ +/* + * 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 "commentModel.h" + +#include +#include + +#include + +CommentModel::CommentModel(QObject *parent) + : QAbstractListModel(parent) +{ + //initialize variables +} + +int CommentModel::rowCount(const QModelIndex &parent) const +{ + return m_commentList.count(); +} + +QVariant CommentModel::data(const QModelIndex &index, int role) const +{ + + if(!index.isValid()) + return QVariant(); + if(index.row() >= m_commentList.size()) + return QVariant(); + + if (role == Qt::DisplayRole || role == Qt::EditRole) { + return m_commentList.at(index.row()).name; + } + if (role == Qt::DecorationRole) { + if (m_commentList.at(index.row()).visibility){ + return KisIconUtils::loadIcon("visible"); + } + else { + return KisIconUtils::loadIcon("novisible"); + } + } + return QVariant(); + +} + +bool CommentModel::setData(const QModelIndex & index, const QVariant & value, int role) +{ + if (index.isValid() && (role == Qt::EditRole || role == Qt::DisplayRole)) + { + + m_commentList[index.row()].name = value.toString(); + emit dataChanged(index, index); + return true; + } + + if (index.isValid() && role == Qt::DecorationRole){ + m_commentList[index.row()].visibility = !m_commentList[index.row()].visibility; + emit dataChanged(index, index); + return true; + } + return false; +} + +Qt::ItemFlags CommentModel::flags(const QModelIndex & index) const +{ + if(!index.isValid()) + return Qt::ItemIsDropEnabled; + + return Qt::ItemIsDragEnabled | Qt::ItemIsSelectable | + Qt::ItemIsEditable | Qt::ItemIsEnabled ; +} + +bool CommentModel::insertRows(int position, int rows, const QModelIndex &parent) +{ + beginInsertRows(QModelIndex(), position, position+rows-1); + + for (int row = 0; row < rows; ++row) { + Comment newcomment; //maybe set a default name like comment 1 + newcomment.name = ""; + newcomment.visibility = true; + + if (position < 0 && position>=m_commentList.size()) return false; + m_commentList.insert(position, newcomment); + } + + endInsertRows(); + return true; +} + +bool CommentModel::removeRows(int position, int rows, const QModelIndex &parent) +{ + beginRemoveRows(QModelIndex(), position, position+rows-1); + + for (int row = 0; row < rows; ++row) { + if (position < 0 || position >= m_commentList.size()) return false; + m_commentList.removeAt(position); + } + endRemoveRows(); + return true; +} + +bool CommentModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int count, + const QModelIndex &destinationParent, int destinationChild) +{ + 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 (int row = 0; row < count; row++){ + if (sourceRow < 0 || sourceRow >= m_commentList.size()) return false; + if (destinationChild + row < 0 || destinationChild + row >= m_commentList.size()) return false; + m_commentList.move(sourceRow, destinationChild + row); + } + endMoveRows(); + return true; +} + +QMimeData *CommentModel::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 CommentModel::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 = createIndex(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 CommentModel::supportedDropActions() const +{ + return Qt::CopyAction | Qt::MoveAction; +} + +Qt::DropActions CommentModel::supportedDragActions() const +{ + return Qt::CopyAction | Qt::MoveAction; +} diff --git a/plugins/dockers/storyboarddocker/commentModel.h b/plugins/dockers/storyboarddocker/commentModel.h new file mode 100644 index 0000000000..41ba1b578b --- /dev/null +++ b/plugins/dockers/storyboarddocker/commentModel.h @@ -0,0 +1,63 @@ +/* + * 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 COMMENT_MODEL +#define COMMENT_MODEL + +#include + +class StoryboardModel; +struct Comment +{ + QString name; + bool visibility; +}; + +/* + This model manages the comment data of StoryboardModel +*/ +class CommentModel : public QAbstractListModel +{ + + Q_OBJECT + +public: + CommentModel(QObject *parent = 0); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + Qt::ItemFlags flags(const QModelIndex &index) const override; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + + 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); + 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; + +private: + QVector m_commentList; + friend class StoryboardModel; +}; + +#endif \ No newline at end of file diff --git a/plugins/dockers/storyboarddocker/krita_storyboarddocker.json b/plugins/dockers/storyboarddocker/krita_storyboarddocker.json new file mode 100644 index 0000000000..00edfe8064 --- /dev/null +++ b/plugins/dockers/storyboarddocker/krita_storyboarddocker.json @@ -0,0 +1,9 @@ +{ + "Id": "Storyboard Docker", + "Type": "Service", + "X-KDE-Library": "kritastoryboarddocker", + "X-KDE-ServiceTypes": [ + "Krita/Dock" + ], + "X-Krita-Version": "28" +} diff --git a/plugins/dockers/storyboarddocker/storyboardDelegate.cpp b/plugins/dockers/storyboarddocker/storyboardDelegate.cpp new file mode 100644 index 0000000000..561f0b2b6a --- /dev/null +++ b/plugins/dockers/storyboarddocker/storyboardDelegate.cpp @@ -0,0 +1,564 @@ +/* + * 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 "storyboardDelegate.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "storyboardModel.h" + +StoryboardDelegate::StoryboardDelegate(QObject *parent) + : QStyledItemDelegate(parent) +{ +} + +StoryboardDelegate::~StoryboardDelegate() +{ +} + +void StoryboardDelegate::paint(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + p->save(); + { + QStyle *style = option.widget ? option.widget->style() : QApplication::style(); + style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, p, option.widget); + + p->setFont(option.font); + if (!index.isValid()){ + p->restore(); + return; + } + if (!index.parent().isValid()){ + QRect parentRect = option.rect; + p->drawRect(parentRect); + + parentRect.setTopLeft(parentRect.topLeft() + QPoint(4, 4)); + parentRect.setBottomRight(parentRect.bottomRight() - QPoint(4, 4)); + + if (option.state & QStyle::State_Selected){ + p->fillRect(option.rect, option.palette.foreground()); + } + else { + p->fillRect(option.rect, option.palette.background()); + } + p->eraseRect(parentRect); + } + else{ + //paint Child index (the indices that hold data) + QModelIndex parent = index.parent(); + + //draw the child items + int childNum = index.row(); + QString data = index.model()->data(index, Qt::DisplayRole).toString(); + + switch (childNum) + { + case 0: + { + if (m_view->thumbnailIsVisible()){ + QRect frameNumRect = option.rect; + frameNumRect.setHeight(m_view->fontMetrics().height()+3); + frameNumRect.setWidth(3 * m_view->fontMetrics().width("0")+2); + frameNumRect.moveBottom(option.rect.top()-1); + p->setPen(QPen(option.palette.dark(), 2)); + p->drawRect(frameNumRect); + p->setPen(QPen(option.palette.text(), 1)); + p->drawText(frameNumRect, Qt::AlignHCenter | Qt::AlignVCenter, data); + + QIcon icon = KisIconUtils::loadIcon("krita-base"); + QRect thumbnailRect = option.rect; + //TO DO make thumbnail keep aspect ratio + icon.paint(p, option.rect); + p->setPen(QPen(option.palette.dark(), 2)); + p->drawRect(option.rect); + + if (option.state & QStyle::State_MouseOver){ + QRect buttonsRect = option.rect; + buttonsRect.setTop(option.rect.bottom() - 22); + p->fillRect(buttonsRect, option.palette.background()); + + buttonsRect.setWidth(22); + buttonsRect.moveBottomLeft(option.rect.bottomLeft()); + QIcon addIcon = KisIconUtils::loadIcon("list-add"); + addIcon.paint(p, buttonsRect); + + buttonsRect.moveBottomRight(option.rect.bottomRight()); + QIcon deleteIcon = KisIconUtils::loadIcon("trash-empty"); + deleteIcon.paint(p, buttonsRect); + } + } + else { + QRect frameNumRect = option.rect; + p->setPen(QPen(option.palette.dark(), 2)); + p->drawRect(frameNumRect); + p->setPen(QPen(option.palette.text(), 1)); + p->drawText(frameNumRect, Qt::AlignHCenter | Qt::AlignVCenter, data); + } + break; + } + case 1: + { + QRect itemNameRect = option.rect; + itemNameRect.setLeft(option.rect.left() + 5); + p->setPen(QPen(option.palette.text(), 1)); + p->drawText(itemNameRect, Qt::AlignLeft | Qt::AlignVCenter, data); + p->setPen(QPen(option.palette.dark(), 2)); + p->drawRect(option.rect); + break; + } + case 2: //time duration + case 3: //frame duration + { + drawSpinBox(p, option, data); + break; + } + default: + { + const StoryboardModel* model = dynamic_cast(index.model()); + if (m_view->commentIsVisible() && model->getComment(index.row() - 4).visibility){ + p->setPen(QPen(option.palette.dark(), 2)); + drawComment(p, option, index); + } + break; + } + } + } + } + p->restore(); +} + +void StoryboardDelegate::drawSpinBox(QPainter *p, const QStyleOptionViewItem &option, QString data) const +{ + QStyle *style = option.widget ? option.widget->style() : QApplication::style(); + QStyleOptionSpinBox spinBoxOption; + spinBoxOption.stepEnabled = QAbstractSpinBox::StepDownEnabled | QAbstractSpinBox::StepUpEnabled; + spinBoxOption.subControls = QStyle::SC_SpinBoxUp | QStyle::SC_SpinBoxDown; + spinBoxOption.rect = option.rect; + p->setPen(QPen(option.palette.dark(), 2)); + p->drawRect(option.rect); + style->drawComplexControl(QStyle::CC_SpinBox, &spinBoxOption, p, option.widget); + + QRect rect = style->subControlRect(QStyle::CC_SpinBox, &spinBoxOption, + QStyle::QStyle::SC_SpinBoxEditField); + rect.moveTopLeft(option.rect.topLeft()); + p->setPen(QPen(option.palette.text(), 1)); + p->drawText(rect, Qt::AlignHCenter | Qt::AlignVCenter, data); +} + +QStyleOptionSlider StoryboardDelegate::drawComment(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QStyle *style = option.widget ? option.widget->style() : QApplication::style(); + const StoryboardModel* model = dynamic_cast(index.model()); + QString data = index.model()->data(index, Qt::DisplayRole).toString(); + + QRect titleRect = option.rect; + titleRect.setHeight(option.fontMetrics.height() + 3); + if (p){ + p->setPen(QPen(option.palette.text(), 1)); + p->drawText(titleRect, Qt::AlignLeft | Qt::AlignVCenter, model->getComment(index.row() - 4).name); + p->setPen(QPen(option.palette.dark(), 2)); + p->drawRect(titleRect); + } + + QRect contentRect = option.rect; + contentRect.setTop(option.rect.top() + option.fontMetrics.height() + 3); + if (p){ + p->setPen(QPen(option.palette.dark(), 2)); + p->drawRect(contentRect); + p->save(); + } + contentRect.setTopLeft(contentRect.topLeft() + QPoint(5, 5)); + contentRect.setBottomRight(contentRect.bottomRight() - QPoint(5, 5)); + + int scrollValue = index.model()->data(index, Qt::UserRole).toInt(); + + //draw comment + QRect commentRect = contentRect; + commentRect.setRight(contentRect.right() - 15); + QTextDocument doc; + + doc.setTextWidth(commentRect.width()); + doc.setDocumentMargin(0); + doc.setDefaultFont(option.font); + doc.setPlainText(data.simplified()); + QRectF clipRect = commentRect; + clipRect.moveTopLeft(QPoint(0, 0 + scrollValue)); + if (p){ + p->translate(QPoint(commentRect.topLeft().x(), commentRect.topLeft().y() - scrollValue)); + p->setPen(QPen(option.palette.text(), 1)); + doc.drawContents(p, clipRect); + p->restore(); + } + //draw scroll bar + QStyleOptionSlider scrollbarOption; + scrollbarOption.sliderPosition = scrollValue; + scrollbarOption.minimum = 0; + scrollbarOption.maximum = qMax(0.0, doc.size().height() - contentRect.height()); + scrollbarOption.sliderPosition = qMin(scrollValue, scrollbarOption.maximum); + scrollbarOption.pageStep = contentRect.height() - 2; + scrollbarOption.orientation = Qt::Vertical; + + QRect scrollRect = option.rect; + scrollRect.setSize(QSize(15, option.rect.height() - option.fontMetrics.height() - 3)); + scrollRect.moveTopLeft(QPoint(0, 0)); + scrollbarOption.rect = scrollRect; + + if (p){ + p->save(); + p->setPen(QPen(option.palette.dark(), 2)); + p->translate(QPoint( option.rect.right()-15, option.rect.top() + option.fontMetrics.height() + 3)); + style->drawComplexControl(QStyle::CC_ScrollBar, &scrollbarOption, p, option.widget); + p->restore(); + } + return scrollbarOption; +} + +QSize StoryboardDelegate::sizeHint(const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + if (!index.parent().isValid()){ + if (m_view->itemOrientation() == Qt::Vertical){ + int width = option.widget->width() - 17; + const StoryboardModel* model = dynamic_cast(index.model()); + int numComments = model->visibleCommentCount(); + int numItem = width/250; + if(numItem <=0){ + return QSize(0, 0); + } + + int thumbnailheight = m_view->thumbnailIsVisible() ? 120 : 0; + int commentHeight = m_view->commentIsVisible() ? numComments*100 : 0; + return QSize(width / numItem, thumbnailheight + option.fontMetrics.height() + 3 + commentHeight + 10); + } + else{ + const StoryboardModel* model = dynamic_cast(index.model()); + int numComments = model->visibleCommentCount(); + int commentWidth = 0; + if (numComments && m_view->commentIsVisible()){ + commentWidth = qMax(200, (m_view->viewport()->width() - 250) / numComments); + } + int width = 250 + numComments * commentWidth; + return QSize(width + 10, 120 + option.fontMetrics.height() + 3 + 10); + } + } + else { + return option.rect.size(); + } + return QSize(0,0); +} + +QWidget *StoryboardDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option , + const QModelIndex &index) const +{ + //only create editor for children + if (index.parent().isValid()){ + int row = index.row(); + switch (row) + { + case 0: //frame thumbnail is uneditable + return nullptr; + case 1: + { + QLineEdit *editor = new QLineEdit(parent); + return editor; + } + case 2: //second and frame spin box + case 3: + { + QSpinBox *spinbox = new QSpinBox(parent); + spinbox->setRange(0, 999); + return spinbox; + } + default: // for itemName and comments + { + QTextEdit *editor = new QTextEdit(parent); + return editor; + } + } + } + return nullptr; +} + +bool StoryboardDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) +{ + if ((event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonDblClick) + && (index.flags() & Qt::ItemIsEnabled)) + { + QMouseEvent *mouseEvent = static_cast(event); + const bool leftButton = mouseEvent->buttons() & Qt::LeftButton; + + //handle the duration edit event + if (index.parent().isValid() && (index.row() == 2 || index.row() == 3)){ + QRect upButton = spinBoxUpButton(option); + QRect downButton = spinBoxDownButton(option); + + bool upButtonClicked = upButton.isValid() && upButton.contains(mouseEvent->pos()); + bool downButtonClicked = downButton.isValid() && downButton.contains(mouseEvent->pos()); + + if (leftButton && upButtonClicked){ + model->setData(index, index.data().toInt() + 1); + return true; + } + else if (leftButton && downButtonClicked){ + model->setData(index, std::max(0,index.data().toInt() - 1)); + return true; + } + } + else if (index.parent().isValid() && index.row() > 3){ //comment box scroll events + QStyleOptionSlider scrollBarOption = drawComment(nullptr, option, index); + QRect upButton = scrollUpButton(option, scrollBarOption); + QRect downButton = scrollDownButton(option, scrollBarOption); + + bool upButtonClicked = upButton.isValid() && upButton.contains(mouseEvent->pos()); + bool downButtonClicked = downButton.isValid() && downButton.contains(mouseEvent->pos()); + + if (leftButton && upButtonClicked){ + int lastValue = model->data(index, Qt::UserRole).toInt(); + int value = lastValue - option.fontMetrics.height(); + StoryboardModel* modelSB = dynamic_cast(model); + modelSB->setCommentScrollData(index, qMax(0, value)); + return true; + } + else if (leftButton && downButtonClicked){ + int lastValue = model->data(index, Qt::UserRole).toInt(); + int value = lastValue + option.fontMetrics.height(); + StoryboardModel* modelSB = dynamic_cast(model); + modelSB->setCommentScrollData(index, qMin(scrollBarOption.maximum, value)); + return true; + } + } + + else if (index.parent().isValid() && index.row() == 0 && m_view->thumbnailIsVisible()){ //thumbnail add/delete events + QRect addItemButton(QPoint(0, 0), QSize(22, 22)); + addItemButton.moveBottomLeft(option.rect.bottomLeft()); + + QRect deleteItemButton(QPoint(0, 0), QSize(22, 22)); + deleteItemButton.moveBottomRight(option.rect.bottomRight()); + + bool addItemButtonClicked = addItemButton.isValid() && addItemButton.contains(mouseEvent->pos()); + bool deleteItemButtonClicked = deleteItemButton.isValid() && deleteItemButton.contains(mouseEvent->pos()); + + if (leftButton && addItemButtonClicked){ + model->insertRows(index.parent().row() + 1, 1); + return true; + } + else if (leftButton && deleteItemButtonClicked){ + model->removeRows(index.parent().row(), 1); + return true; + } + } + } + + if ((event->type() == QEvent::MouseMove) && (index.flags() & Qt::ItemIsEnabled)) + { + QMouseEvent *mouseEvent = static_cast(event); + const bool leftButton = mouseEvent->buttons() & Qt::LeftButton; + + QStyleOptionSlider scrollBarOption = drawComment(nullptr, option, index); + QRect scrollBarRect = scrollBar(option, scrollBarOption); + + bool lastClickPosInScroll = scrollBarRect.isValid() && scrollBarRect.contains(m_lastDragPos); + bool currClickPosInScroll = scrollBarRect.isValid() && scrollBarRect.contains(mouseEvent->pos()); + + if (leftButton && index.parent().isValid() && index.row() > 3){ + if (lastClickPosInScroll && currClickPosInScroll){ + int lastValue = model->data(index, Qt::UserRole).toInt(); + int value = lastValue + mouseEvent->pos().y() - m_lastDragPos.y(); + StoryboardModel* modelSB = dynamic_cast(model); + if (value >= 0 && value <= scrollBarOption.maximum){ + modelSB->setCommentScrollData(index, value); + return true; + } + return false; + } + m_lastDragPos = mouseEvent->pos(); + } + } + return false; +} + +//set the existing data in the editor +void StoryboardDelegate::setEditorData(QWidget *editor, + const QModelIndex &index) const +{ + QVariant value = index.data(); + if (index.parent().isValid()){ + int row = index.row(); + switch (row) + { + case 0: //frame thumbnail is uneditable + return; + case 1: //for itemName + { + QLineEdit *lineEdit = static_cast(editor); + lineEdit->setText(value.toString()); + return; + } + case 2: //second and frame spin box + case 3: + { + QSpinBox *spinbox = static_cast(editor); + spinbox->setValue(value.toInt()); + return; + } + default: // for comments + { + QTextEdit *textEdit = static_cast(editor); + textEdit->setText(value.toString()); + textEdit->moveCursor(QTextCursor::End, QTextCursor::MoveAnchor); + textEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + textEdit->verticalScrollBar()->setProperty("index", index); + connect(textEdit->verticalScrollBar(), SIGNAL(sliderMoved(int)), this, SLOT(slotCommentScrolledTo(int))); + return; + } + } + } +} + +void StoryboardDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const +{ + QVariant value = index.data(); + if (index.parent().isValid()){ + int row = index.row(); + switch (row) + { + case 0: //frame thumbnail is uneditable + return; + case 1: // for itemName + { + QLineEdit *lineEdit = static_cast(editor); + QString value = lineEdit->text(); + model->setData(index, value, Qt::EditRole); + return; + } + case 2: //second and frame spin box + case 3: + { + QSpinBox *spinbox = static_cast(editor); + int value = spinbox->value(); + model->setData(index, value, Qt::EditRole); + return; + } + default: // for comments + { + QTextEdit *textEdit = static_cast(editor); + QString value = textEdit->toPlainText(); + model->setData(index, value, Qt::EditRole); + return; + } + } + } +} + +void StoryboardDelegate::updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + if (index.row() < 4){ + editor->setGeometry(option.rect); + } + else { //for comment textedits + QRect commentRect = option.rect; + commentRect.setTop(option.rect.top() + option.fontMetrics.height() + 3); + editor->setGeometry(commentRect); + } +} + +void StoryboardDelegate::setView(StoryboardView *view) +{ + m_view = view; +} + +QRect StoryboardDelegate::spinBoxUpButton(const QStyleOptionViewItem &option) +{ + QStyle *style = option.widget ? option.widget->style() : QApplication::style(); + QStyleOptionSpinBox spinOption; + spinOption.rect = option.rect; + QRect rect = style->subControlRect(QStyle::CC_SpinBox, &spinOption, + QStyle::QStyle::SC_SpinBoxUp); + rect.moveTopRight(option.rect.topRight()); + return rect; +} + +QRect StoryboardDelegate::spinBoxDownButton(const QStyleOptionViewItem &option) +{ + QStyle *style = option.widget ? option.widget->style() : QApplication::style(); + QStyleOptionSpinBox spinOption; + spinOption.rect = option.rect; + QRect rect = style->subControlRect(QStyle::CC_SpinBox, &spinOption, + QStyle::QStyle::SC_SpinBoxDown); + rect.moveBottomRight(option.rect.bottomRight()); + return rect; +} + +QRect StoryboardDelegate::spinBoxEditField(const QStyleOptionViewItem &option) +{ + QStyle *style = option.widget ? option.widget->style() : QApplication::style(); + QStyleOptionSpinBox spinOption; + spinOption.rect = option.rect; + QRect rect = style->subControlRect(QStyle::CC_SpinBox, &spinOption, + QStyle::QStyle::SC_SpinBoxEditField); + rect.moveTopLeft(option.rect.topLeft()); + return rect; +} + +void StoryboardDelegate::slotCommentScrolledTo(int value) const +{ + const QModelIndex index = sender()->property("index").toModelIndex(); + StoryboardModel* model = dynamic_cast(m_view->model()); + model->setCommentScrollData(index, value); +} + +QRect StoryboardDelegate::scrollBar(const QStyleOptionViewItem &option, QStyleOptionSlider &scrollBarOption) const +{ + QStyle *style = option.widget ? option.widget->style() : QApplication::style(); + QRect rect = style->subControlRect(QStyle::CC_ScrollBar, &scrollBarOption, + QStyle::QStyle::SC_ScrollBarSlider); + rect.moveTopLeft(rect.topLeft() + scrollBarOption.rect.topLeft()); + return rect; +} + +QRect StoryboardDelegate::scrollDownButton(const QStyleOptionViewItem &option, QStyleOptionSlider &scrollBarOption) +{ + QStyle *style = option.widget ? option.widget->style() : QApplication::style(); + QRect rect = style->subControlRect(QStyle::CC_ScrollBar, &scrollBarOption, + QStyle::QStyle::SC_ScrollBarAddLine); + rect.moveTopLeft(rect.topLeft() + scrollBarOption.rect.topLeft()); + return rect; +} + +QRect StoryboardDelegate::scrollUpButton(const QStyleOptionViewItem &option, QStyleOptionSlider &scrollBarOption) +{ + QStyle *style = option.widget ? option.widget->style() : QApplication::style(); + QRect rect = style->subControlRect(QStyle::CC_ScrollBar, &scrollBarOption, + QStyle::QStyle::SC_ScrollBarSubLine); + rect.moveTopLeft(rect.topLeft() + scrollBarOption.rect.topLeft()); + return rect; +} diff --git a/plugins/dockers/storyboarddocker/storyboardDelegate.h b/plugins/dockers/storyboarddocker/storyboardDelegate.h new file mode 100644 index 0000000000..6371abb8c6 --- /dev/null +++ b/plugins/dockers/storyboarddocker/storyboardDelegate.h @@ -0,0 +1,69 @@ +/* + * 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_DELEGATE_H +#define __STORYBOARD_DELEGATE_H + +#include +#include "storyboardView.h" + +class QListView; +class StoryboardModel; + +class StoryboardDelegate : public QStyledItemDelegate +{ + Q_OBJECT +public: + StoryboardDelegate(QObject *parent); + ~StoryboardDelegate() override; + + void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; + + bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index); + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const; + + void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const; + + void setView(StoryboardView *view); + void drawSpinBox(QPainter *p, const QStyleOptionViewItem &option, QString data) const; + QStyleOptionSlider drawComment(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const; + + QRect spinBoxUpButton(const QStyleOptionViewItem &option); + QRect spinBoxDownButton(const QStyleOptionViewItem &option); + QRect spinBoxEditField(const QStyleOptionViewItem &option); + + QRect scrollBar(const QStyleOptionViewItem &option, QStyleOptionSlider &scrollBarOption) const; + QRect scrollDownButton(const QStyleOptionViewItem &option, QStyleOptionSlider &scrollBarOption); + QRect scrollUpButton(const QStyleOptionViewItem &option, QStyleOptionSlider &scrollBarOption); + +private Q_SLOTS: + void slotCommentScrolledTo(int value) const; + +private: + StoryboardView *m_view; + QPoint m_lastDragPos = QPoint(0, 0); +}; + +#endif diff --git a/plugins/dockers/storyboarddocker/storyboardItem.cpp b/plugins/dockers/storyboarddocker/storyboardItem.cpp new file mode 100644 index 0000000000..06606568c7 --- /dev/null +++ b/plugins/dockers/storyboarddocker/storyboardItem.cpp @@ -0,0 +1,62 @@ +/* + * 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 "storyboardItem.h" + +StoryboardItem::StoryboardItem() +{} + +StoryboardItem::~StoryboardItem() +{ + qDeleteAll(m_childData); +} + +void StoryboardItem::appendChild(QVariant data) +{ + StoryboardChild* child = new StoryboardChild(data, this); + m_childData.append(child); +} + +void StoryboardItem::insertChild(int row, QVariant data) +{ + StoryboardChild* child = new StoryboardChild(data, this); + m_childData.insert(row, child); +} + +void StoryboardItem::removeChild(int row) +{ + delete m_childData.at(row); + m_childData.removeAt(row); +} + +void StoryboardItem::moveChild(int from, int to) +{ + m_childData.move(from, to); +} + +int StoryboardItem::childCount() const +{ + return m_childData.count(); +} + +StoryboardChild* StoryboardItem::child(int row) +{ + if (row < 0 || row >= m_childData.size()) + return nullptr; + return m_childData.at(row); +} diff --git a/plugins/dockers/storyboarddocker/storyboardItem.h b/plugins/dockers/storyboarddocker/storyboardItem.h new file mode 100644 index 0000000000..6ca55ad678 --- /dev/null +++ b/plugins/dockers/storyboarddocker/storyboardItem.h @@ -0,0 +1,63 @@ +/* + * 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_ITEM +#define STORYBOARD_ITEM + +#include +#include + +//each storyboardItem contains pointer to child data +class StoryboardItem; + +class StoryboardChild +{ +public: + StoryboardChild(QVariant data, StoryboardItem *parent) + : m_data(data) + , m_parentItem(parent) + {} + StoryboardItem *parent(){ return m_parentItem;} + QVariant data(){ return m_data;} + void setData(QVariant value){ + m_data = value; + } + +private: + QVariant m_data; + StoryboardItem *m_parentItem; +}; + +class StoryboardItem +{ +public: + explicit StoryboardItem(); + ~StoryboardItem(); + + void appendChild(QVariant data = QVariant()); + void insertChild(int row, QVariant data = QVariant()); + void removeChild(int row); + void moveChild(int from, int to); + int childCount() const; + StoryboardChild *child(int row); + +private: + QVector m_childData; +}; + +#endif diff --git a/plugins/dockers/storyboarddocker/storyboardModel.cpp b/plugins/dockers/storyboarddocker/storyboardModel.cpp new file mode 100644 index 0000000000..df677f5855 --- /dev/null +++ b/plugins/dockers/storyboarddocker/storyboardModel.cpp @@ -0,0 +1,438 @@ +/* + * 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) + , m_commentCount(0) +{ + connect(this, SIGNAL(rowsInserted(const QModelIndex, int, int)), + this, SLOT(slotInsertCommentRows(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 = static_cast(parent.internalPointer()); + 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 *childItem = static_cast(index.internalPointer()); + if (m_items.contains(childItem)){ + 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 = static_cast(parent.internalPointer()); + 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 = static_cast(index.internalPointer()); + 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 = static_cast(index.internalPointer()); + if (child){ + int fps = 24; + 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 = static_cast(index.internalPointer()); + 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()){ + 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 = static_cast(parent.internalPointer()); + 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()){ + beginRemoveRows(QModelIndex(), position, position+rows-1); + for (int row = 0; row < rows; ++row){ + //deleting all the child nodes + QModelIndex currentIndex = index(position, 0); + removeRows(0, rowCount(currentIndex), currentIndex); + + //deleting the actual parent node + delete m_items.at(position); + m_items.removeAt(position); + } + endRemoveRows(); + return true; + } + else if (!parent.parent().isValid()){ //remove 2nd level nodes + StoryboardItem *item = static_cast(parent.internalPointer()); + 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 = static_cast(parent.internalPointer()); + 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 = createIndex(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); +} + +void StoryboardModel::slotCommentDataChanged() +{ + m_commentList = m_commentModel->m_commentList; + emit(dataChanged(QModelIndex(), QModelIndex())); +} + +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::slotInsertCommentRows(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 new file mode 100644 index 0000000000..0764c51f7a --- /dev/null +++ b/plugins/dockers/storyboarddocker/storyboardModel.h @@ -0,0 +1,104 @@ +/* + * 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" + +/* + 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 StoryboardModel : public QAbstractItemModel +{ + Q_OBJECT + +public: +//if we don't need this constructor change it + 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; + +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 slotInsertCommentRows(const QModelIndex parent, int first, int last); + +private: + QVector m_items; + int m_commentCount = 0; + 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 new file mode 100644 index 0000000000..6363058778 --- /dev/null +++ b/plugins/dockers/storyboarddocker/storyboardView.cpp @@ -0,0 +1,247 @@ +/* + 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; + } + if (childIndex == m_hoverIndex){ + option.state |= QStyle::State_MouseOver; + } + 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::mouseMoveEvent(QMouseEvent *event) +{ + event->accept(); + QListView::mouseMoveEvent(event); + + m_hoverIndex = indexAt(event->pos()); +} + +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)); +} diff --git a/plugins/dockers/storyboarddocker/storyboardView.h b/plugins/dockers/storyboarddocker/storyboardView.h new file mode 100644 index 0000000000..ea1c372f0d --- /dev/null +++ b/plugins/dockers/storyboarddocker/storyboardView.h @@ -0,0 +1,63 @@ +/* + 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 mouseMoveEvent(QMouseEvent *event) override; + void setItemOrientation(Qt::Orientation orientation); + Qt::Orientation itemOrientation(); + bool commentIsVisible() const; + bool thumbnailIsVisible() const; + void setCommentVisibility(bool value); + void setThumbnailVisibility(bool value); + +private Q_SLOTS: + void slotContextMenuRequested(const QPoint &); + +private: + QModelIndex m_hoverIndex; + Qt::Orientation m_itemOrientation; + bool m_commentIsVisible; + bool m_thumbnailIsVisible; +}; + +#endif diff --git a/plugins/dockers/storyboarddocker/storyboarddocker.cpp b/plugins/dockers/storyboarddocker/storyboarddocker.cpp new file mode 100644 index 0000000000..db124717c9 --- /dev/null +++ b/plugins/dockers/storyboarddocker/storyboarddocker.cpp @@ -0,0 +1,76 @@ +/* + * 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 "storyboarddocker.h" + +#include +#include + +#include +#include + +#include "storyboarddocker_dock.h" + +K_PLUGIN_FACTORY_WITH_JSON(StoryboardDockerPluginFactory, "krita_storyboarddocker.json", registerPlugin();) + +class StoryboardDockerDockFactory : public KoDockFactoryBase { +public: + StoryboardDockerDockFactory() + { + } + + QString id() const override + { + return QString( "StoryboardDocker" ); + } + + virtual Qt::DockWidgetArea defaultDockWidgetArea() const + { + return Qt::RightDockWidgetArea; + } + + QDockWidget* createDockWidget() override + { + StoryboardDockerDock * dockWidget = new StoryboardDockerDock(); + + dockWidget->setObjectName(id()); + + return dockWidget; + } + + DockPosition defaultDockPosition() const override + { + return DockMinimized; + } +private: + + +}; + + +StoryboardDockerPlugin::StoryboardDockerPlugin(QObject *parent, const QVariantList &) + : QObject(parent) +{ + KoDockRegistry::instance()->add(new StoryboardDockerDockFactory()); +} + +StoryboardDockerPlugin::~StoryboardDockerPlugin() +{ +} + +#include "storyboarddocker.moc" diff --git a/plugins/dockers/storyboarddocker/storyboarddocker.h b/plugins/dockers/storyboarddocker/storyboarddocker.h new file mode 100644 index 0000000000..39f7e2a609 --- /dev/null +++ b/plugins/dockers/storyboarddocker/storyboarddocker.h @@ -0,0 +1,37 @@ +/* + * 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. + */ + +#ifndef STORYBOARDDOCKER_H +#define STORYBOARDDOCKER_H + +#include +#include + + +/** + * Storyboard Docker showing frames and comments + */ +class StoryboardDockerPlugin : public QObject +{ + Q_OBJECT +public: + StoryboardDockerPlugin(QObject *parent, const QVariantList &); + ~StoryboardDockerPlugin() override; +}; + +#endif \ No newline at end of file diff --git a/plugins/dockers/storyboarddocker/storyboarddocker_dock.cpp b/plugins/dockers/storyboarddocker/storyboarddocker_dock.cpp new file mode 100644 index 0000000000..93776774cf --- /dev/null +++ b/plugins/dockers/storyboarddocker/storyboarddocker_dock.cpp @@ -0,0 +1,279 @@ +/* + * 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 +#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); + + 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) +{ +} + +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(); +} + +#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 new file mode 100644 index 0000000000..8337ea8229 --- /dev/null +++ b/plugins/dockers/storyboarddocker/storyboarddocker_dock.h @@ -0,0 +1,76 @@ +/* + * 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 + +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 setViewManager(KisViewManager* kisview) override; + +private: + 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); + //the comment button slots + + void slotLockClicked(bool); + void slotModeChanged(QAbstractButton*); + void slotViewChanged(QAbstractButton*); +}; + + +#endif diff --git a/plugins/dockers/storyboarddocker/tests/CMakeLists.txt b/plugins/dockers/storyboarddocker/tests/CMakeLists.txt new file mode 100644 index 0000000000..de932c2a0b --- /dev/null +++ b/plugins/dockers/storyboarddocker/tests/CMakeLists.txt @@ -0,0 +1,19 @@ +set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} ) + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ${CMAKE_SOURCE_DIR}/libs/ui/tests + ${CMAKE_CURRENT_BINARY_DIR}/.. +) + +macro_add_unittest_definitions() + +ecm_add_test( + storyboardModelTest.cpp + ../storyboardModel.cpp + ../commentModel.cpp + ${CMAKE_SOURCE_DIR}/libs/ui/tests/modeltest.cpp + TEST_NAME kritastoryboardmodeltest + NAME_PREFIX "plugins-dockers-storyboarddocker-" + LINK_LIBRARIES kritastoryboarddocker kritaui Qt5::Test +) \ No newline at end of file diff --git a/plugins/dockers/storyboarddocker/tests/storyboardModelTest.cpp b/plugins/dockers/storyboarddocker/tests/storyboardModelTest.cpp new file mode 100644 index 0000000000..61b049c2c1 --- /dev/null +++ b/plugins/dockers/storyboarddocker/tests/storyboardModelTest.cpp @@ -0,0 +1,169 @@ +/* + * 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 "storyboardModelTest.h" + +#include +#include +#include "storyboardModel.h" +#include "commentModel.h" + +void StoryboardModelTest::init() +{ + m_commentModel = new CommentModel(0); + m_storyboardModel = new StoryboardModel(0); + + m_commentModel->insertRows(m_commentModel->rowCount(),1); + QCOMPARE(m_commentModel->rowCount(), 1); +} + +void StoryboardModelTest::cleanup() +{ + delete m_storyboardModel; + delete m_commentModel; +} + +void StoryboardModelTest::testAddComment() +{ + + auto tester = new QAbstractItemModelTester(m_storyboardModel, 0); + auto tester = new QAbstractItemModelTester(m_commentModel, 0); + + int commentStoryboard = m_storyboardModel->commentCount(); + int rowsComment = m_commentModel->rowCount(); + + QCOMPARE(commentStoryboard, rowsComment); + + m_commentModel->insertRows(m_commentModel->rowCount(),1); + + QCOMPARE(rowsComment + 1, m_commentModel->rowCount()); + QCOMPARE(m_storyboardModel->commentCount(), m_commentModel->rowCount()); + + //add at an invalid position + m_commentModel->insertRows(-1, 1); + + QCOMPARE(rowsComment + 1, m_commentModel->rowCount()); + QCOMPARE(m_storyboardModel->commentCount(), m_commentModel->rowCount()); + +} + +void StoryboardModelTest::testRemoveComment() +{ + + auto tester = new QAbstractItemModelTester(m_storyboardModel, 0); + auto tester = new QAbstractItemModelTester(m_commentModel, 0); + int commentStoryboard = m_storyboardModel->commentCount(); + int rowsComment = m_commentModel->rowCount(); + + QCOMPARE(commentStoryboard, rowsComment); + + m_commentModel->removeRows(m_commentModel->rowCount(),1); + + QCOMPARE(rowsComment - 1, m_commentModel->rowCount()); + QCOMPARE(m_storyboardModel->commentCount(), m_commentModel->rowCount()); + + m_commentModel->removeRows(-1,1); + + QCOMPARE(rowsComment - 1, m_commentModel->rowCount()); + QCOMPARE(m_storyboardModel->commentCount(), m_commentModel->rowCount()); +} + +void StoryboardModelTest::testCommentNameChanged() +{ + auto tester = new QAbstractItemModelTester(m_commentModel, 0); + QModelIndex index = m_commentModel->createIndex(m_commentModel->rowCount(),m_commentModel->columnCount()); + QVariant value = QVariant(QString("newValue")); + m_commentModel->setData(index, value); + + QCOMPARE(QString("newValue"), m_commentModel->data(index)); +} + +void StoryboardModelTest::testCommentVisibilityChanged() +{ + QModelIndex index = m_commentModel->createIndex(m_commentModel->rowCount(),m_commentModel->columnCount()); + QIcon prevIcon = m_commentModel->data(index, Qt::DecorationRole); + m_commentModel->setData(index, true, Qt::DecorationRole); + QIcon currIcon = m_commentModel->data(index, Qt::DecorationRole); + + QVERIFY(prevIcon != currIcon); +} + +void StoryboardModelTest::testFrameAdded() +{ + int rows = m_storyboardModel->rowCount(); + auto tester = new QAbstractItemModelTester(m_storyboardModel, 0); + m_storyboardModel->insertRows(m_storyboardModel->rowCount(),1); + + QCOMPARE(rows + 1, m_storyboardModel->rowCount()); +} + +void StoryboardModelTest::testFrameRemoved() +{ + int rows = m_storyboardModel->rowCount(); + auto tester = new QAbstractItemModelTester(m_storyboardModel, 0); + m_storyboardModel->removeRows(m_storyboardModel->rowCount(),1); + + QCOMPARE(rows-1, m_storyboardModel->rowCount()); +} + +void StoryboardModelTest::testFrameChanged() +{ + + auto tester = new QAbstractItemModelTester(m_storyboardModel, 0); + QModelIndex index = m_commentModel->createIndex(m_storyboardModel->rowCount(), 1); + QVariant value = QVariant(100); + m_stroyboardModel->setData(index, value, Qt::EditRole); + + QCOMPARE(m_stroyboardModel->data(index), 100); + + //invalid value shouldn't change anything + QVariant value = QVariant(-100); + m_stroyboardModel->setData(index, value, Qt::EditRole); + + QVERIFY(m_stroyboardModel->data(index), 100); + + +} + +void StoryboardModelTest::testDurationChanged() +{ + auto tester = new QAbstractItemModelTester(m_storyboardModel, 0); + QModelIndex index = m_commentModel->createIndex(m_storyboardModel->rowCount(), 2); + QVariant value = QVariant(100); + m_stroyboardModel->setData(index, value, Qt::EditRole); + + QCOMPARE(m_stroyboardModel->data(index), 100); + + //invalid value shouldn't change anything + QVariant value = QVariant(-1); + m_stroyboardModel->setData(index, value, Qt::EditRole); + + QVERIFY(m_stroyboardModel->data(index), 100); +} + +void StoryboardModelTest::testCommentChanged() +{ + auto tester = new QAbstractItemModelTester(m_storyboardModel, 0); + QModelIndex index = m_commentModel->createIndex(m_storyboardModel->rowCount(),4); + QVariant value = QVariant(QString("newComment")); + m_stroyboardModel->setData(index, value, Qt::EditRole); + + QCOMPARE(m_stroyboardModel->data(index,Qt::EditRole), QString("newComment")); +} + +QTEST_MAIN(StoryboardModelTest) diff --git a/plugins/dockers/storyboarddocker/tests/storyboardModelTest.h b/plugins/dockers/storyboarddocker/tests/storyboardModelTest.h new file mode 100644 index 0000000000..952bc683b7 --- /dev/null +++ b/plugins/dockers/storyboarddocker/tests/storyboardModelTest.h @@ -0,0 +1,53 @@ +/* + * 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_TEST_H +#define __STORYBOARD_MODEL_TEST_H + +#include + +class CommentModel; +class StoryboardModel; + +class StoryboardModelTest : public QObject +{ + Q_OBJECT +private Q_SLOTS: + void init(); + void cleanup(); + + //interaction with comment model + void testAddComment() + void testRemoveComment(); + void testCommentNameChanged(); + void testCommentVisibilityChanged(); + + //"storyboard model only" tests + void testFrameAdded(); + void testFrameRemoved(); + void testFrameChanged(); + void testDurationChanged(); + void testCommentChanged(); + +private: + CommentModel *m_commentModel; + StoryboardModel *m_storyboardModel; + +}; + +#endif /* __STORYBOARD_MODEL_TEST_H */ diff --git a/plugins/dockers/storyboarddocker/wdgarrangemenu.ui b/plugins/dockers/storyboarddocker/wdgarrangemenu.ui new file mode 100644 index 0000000000..e71a2f08d9 --- /dev/null +++ b/plugins/dockers/storyboarddocker/wdgarrangemenu.ui @@ -0,0 +1,94 @@ + + + WdgArrangeMenu + + + + 0 + 0 + 174 + 275 + + + + + 0 + 0 + + + + + + + + + + + + Mode + + + + + + + Column + + + + + + + Row + + + + + + + Grid + + + + + + + Qt::Horizontal + + + + + + + View + + + + + + + All + + + + + + + Thumbnails Only + + + + + + + Comments Only + + + + + + + + + + diff --git a/plugins/dockers/storyboarddocker/wdgcommentmenu.ui b/plugins/dockers/storyboarddocker/wdgcommentmenu.ui new file mode 100644 index 0000000000..c6e7b58dea --- /dev/null +++ b/plugins/dockers/storyboarddocker/wdgcommentmenu.ui @@ -0,0 +1,130 @@ + + + WdgCommentMenu + + + + 0 + 0 + 159 + 237 + + + + + 0 + 0 + + + + + 0 + 0 + + + + + + + + QLayout::SetMinimumSize + + + 0 + + + + + 0 + + + QLayout::SetNoConstraint + + + + + 0 + + + QLayout::SetNoConstraint + + + + + + 0 + 0 + + + + + 158 + 10000 + + + + Qt::ScrollBarAsNeeded + + + QAbstractScrollArea::AdjustToContents + + + QAbstractItemView::InternalMove + + + Qt::MoveAction + + + + + + + + + 1 + + + QLayout::SetNoConstraint + + + + + + + + + + 22 + 22 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + ... + + + + + + + + + + + + diff --git a/plugins/dockers/storyboarddocker/wdgstoryboarddock.ui b/plugins/dockers/storyboarddocker/wdgstoryboarddock.ui new file mode 100644 index 0000000000..031d28d6cc --- /dev/null +++ b/plugins/dockers/storyboarddocker/wdgstoryboarddock.ui @@ -0,0 +1,111 @@ + + + WdgStoryboardDock + + + + 0 + 0 + 604 + 561 + + + + Form + + + + + + + + + + Export + + + true + + + + + + + Comments + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + true + + + Lock + + + false + + + false + + + Qt::ToolButtonIconOnly + + + true + + + + + + + Mode + + + true + + + Qt::NoArrow + + + + + + + + + + + + + + + + + + + StoryboardView + QListView +
storyboardView.h
+
+
+ + +