diff --git a/plugins/dockers/storyboarddocker/storyboardDelegate.cpp b/plugins/dockers/storyboarddocker/storyboardDelegate.cpp index 0e4dd6f2bb..561f0b2b6a 100644 --- a/plugins/dockers/storyboarddocker/storyboardDelegate.cpp +++ b/plugins/dockers/storyboarddocker/storyboardDelegate.cpp @@ -1,545 +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)); - //TODO: change highlight color and the area that is highlighted + 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 rowNum = index.model()->rowCount(parent); 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){ + 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/storyboardItem.cpp b/plugins/dockers/storyboarddocker/storyboardItem.cpp index e0b3b0fd11..bd5ef5b9fc 100644 --- a/plugins/dockers/storyboarddocker/storyboardItem.cpp +++ b/plugins/dockers/storyboarddocker/storyboardItem.cpp @@ -1,67 +1,68 @@ /* * 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" //create 3 rows by default StoryboardItem::StoryboardItem() { insertChild(childCount(), 1); insertChild(childCount(), QString("scene 1")); insertChild(childCount(), 100); //duration-seconds insertChild(childCount(), 23); //duration-frames } 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/storyboardModel.cpp b/plugins/dockers/storyboarddocker/storyboardModel.cpp index 8560883537..2168a6557e 100644 --- a/plugins/dockers/storyboarddocker/storyboardModel.cpp +++ b/plugins/dockers/storyboarddocker/storyboardModel.cpp @@ -1,419 +1,420 @@ /* * 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) {} QModelIndex StoryboardModel::index(int row, int column, const QModelIndex &parent) const { if (!hasIndex(row, column, parent)) return QModelIndex(); if (row < 0 || row >= rowCount()) 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(); if (index.row() >= m_items.size()) return QVariant(); if(role == Qt::SizeHintRole){ return QSize(200,200); } //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(); + for (int row = 0; row < rows; ++row) { + insertRows(rowCount(index(position + row, 0)), m_commentList.count(), index(position + row, 0)); + } return true; } - - //insert 2nd level nodes - if (!parent.parent().isValid()){ + else if (!parent.parent().isValid()){ //insert 2nd level nodes StoryboardItem *item = static_cast(parent.internalPointer()); - beginInsertRows(QModelIndex(), position, position+rows-1); + 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) { - delete m_items.at(row); + for (int row = 0; row < rows; ++row){ + QModelIndex currentIndex = index(position, 0); + removeRows(0, rowCount(currentIndex), currentIndex); + delete m_items.at(position); m_items.removeAt(position); } endRemoveRows(); return true; } - - //remove 2nd level nodes - if (!parent.parent().isValid()){ + else if (!parent.parent().isValid()){ //remove 2nd level nodes StoryboardItem *item = static_cast(parent.internalPointer()); if (m_items.contains(item)){ - beginRemoveRows(QModelIndex(), position, position+rows-1); + 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(); } \ No newline at end of file diff --git a/plugins/dockers/storyboarddocker/storyboardView.cpp b/plugins/dockers/storyboarddocker/storyboardView.cpp index eef47986ff..533b67e12a 100644 --- a/plugins/dockers/storyboarddocker/storyboardView.cpp +++ b/plugins/dockers/storyboarddocker/storyboardView.cpp @@ -1,226 +1,225 @@ /* 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 "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) { - setWrapping(true); - setFlow(QListView::LeftToRight); + setSelectionBehavior(SelectRows); + setDefaultDropAction(Qt::MoveAction); setResizeMode(QListView::Adjust); setUniformItemSizes(true); setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); QWidget::setMouseTracking(true); - //make drag and drop work as expected setDragEnabled(true); viewport()->setAcceptDrops(true); setDropIndicatorShown(true); setDragDropMode(QAbstractItemView::InternalMove); } 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 height = parentRect.height(); 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; } diff --git a/plugins/dockers/storyboarddocker/tests/storyboardDelegateTest.cpp b/plugins/dockers/storyboarddocker/tests/storyboardDelegateTest.cpp deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/dockers/storyboarddocker/tests/storyboardDelegateTest.h b/plugins/dockers/storyboarddocker/tests/storyboardDelegateTest.h deleted file mode 100644 index e69de29bb2..0000000000