Krita Source Code Documentation
Loading...
Searching...
No Matches
StoryboardView.cpp
Go to the documentation of this file.
1/*
2 SPDX-FileCopyrightText: 2020 Saurabh Kumar <saurabhk660@gmail.com>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include <QDebug>
8#include <QPainter>
9#include <QPaintEvent>
10#include <QMenu>
11#include <QProxyStyle>
12#include <QStyleFactory>
13
14#include "StoryboardView.h"
15#include "StoryboardModel.h"
16#include "StoryboardDelegate.h"
18
19class StoryboardStyle : public QProxyStyle
20{
21public:
22 StoryboardStyle(QStyle *baseStyle = 0) : QProxyStyle(baseStyle) {}
23
24 void drawPrimitive(PrimitiveElement element,
25 const QStyleOption *option,
26 QPainter *painter,
27 const QWidget *widget) const override
28 {
29 if (element == QStyle::PE_IndicatorItemViewItemDrop)
30 {
31 QColor color(widget->palette().color(QPalette::Highlight).lighter());
32 if (option->rect.width() == 0 && option->rect.height() == 0){
33 return;
34 }
35 else if (option->rect.width() == 0) {
36 QBrush brush(color);
37
38 QRect r(option->rect);
39 r.setLeft(r.left() - 4);
40 r.setRight(r.right() + 4);
41
42 painter->fillRect(r, brush);
43 }
44 else if (option->rect.height() == 0) {
45 QBrush brush(color);
46
47 QRect r(option->rect);
48 r.setTop(r.top() - 4);
49 r.setBottom(r.bottom() + 4);
50
51 painter->fillRect(r, brush);
52 }
53 }
54 else
55 {
56 QProxyStyle::drawPrimitive(element, option, painter, widget);
57 }
58 }
59};
60
68 : QListView(parent)
69 , m_itemOrientation(Qt::Vertical)
70 , m_commentIsVisible(true)
71 , m_thumbnailIsVisible(true)
72{
73 setSelectionBehavior(SelectRows);
74 setDefaultDropAction(Qt::MoveAction);
75 setResizeMode(QListView::Adjust);
76 setUniformItemSizes(true);
77 setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
78 setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
79 QWidget::setMouseTracking(true);
80 setContextMenuPolicy(Qt::CustomContextMenu);
81 setDragEnabled(true);
82 viewport()->setAcceptDrops(true);
83 setDropIndicatorShown(true);
84 setDragDropMode(QAbstractItemView::InternalMove);
85
86 QStyle *newStyle = QStyleFactory::create(this->style()->objectName());
87 // proxy style steals the ownership of the style and deletes it later
88 StoryboardStyle *proxyStyle = new StoryboardStyle(newStyle);
89 proxyStyle->setParent(this);
90 setStyle(proxyStyle);
91
92 connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
93 this, SLOT(slotContextMenuRequested(const QPoint &)));
94
95 connect(this, &StoryboardView::clicked,
97}
98
101
102void StoryboardView::paintEvent(QPaintEvent *event)
103{
104 event->accept();
105 QListView::paintEvent(event);
106
107 //ask delegate to draw the child nodes too
108 QPainter painter(viewport());
109 int itemNum = model()->rowCount();
110 for (int row = 0; row < itemNum; row++) {
111 QModelIndex index = model()->index(row, 0);
112 int childNum = model()->rowCount(index);
113 for (int childRow = 0; childRow < childNum; childRow++) {
114
115 QModelIndex childIndex = model()->index(childRow, 0, index);
116
117 QStyleOptionViewItem option;
118 if (selectionModel()->isSelected(childIndex)) {
119 option.state |= QStyle::State_Selected;
120 }
121 if (childIndex == selectionModel()->currentIndex()) {
122 option.state |= QStyle::State_HasFocus;
123 }
124 option.font = font();
125 option.fontMetrics = fontMetrics();
126 option.rect = visualRect(childIndex);
127 itemDelegate()->paint(&painter, option, childIndex);
128 }
129 }
130}
131
132QRect StoryboardView::visualRect(const QModelIndex &index) const
133{
134 /*
135 * fw = fontWidth
136 *
137 * (3*fw+2), (5*fw+10) _____ (4*fw+10)
138 * | | /
139 * | | /
140 * ,_________________________,
141 * |__|_____________|____|___| ---------->(fontHeight)
142 * | |
143 * | |
144 * | |
145 * | |
146 * |_________________________|
147 */
148
149 if (!index.isValid() || !index.parent().isValid()) {
150 return QListView::visualRect(index);
151 }
152 else {
153 QRect parentRect = visualRect(index.parent());
154 parentRect.setTopLeft(parentRect.topLeft() + QPoint(5, 5));
155 parentRect.setBottomRight(parentRect.bottomRight() - QPoint(5, 5));
156 int fontHeight = fontMetrics().height() + 3;
157 int numericFontWidth = fontMetrics().horizontalAdvance("0");
158
159 int parentWidth = parentRect.width();
160 int childRow = index.row();
161
162 int thumbnailWidth = parentWidth;
163 if (m_itemOrientation == Qt::Horizontal) {
164 thumbnailWidth = 250;
165 }
166 switch (childRow)
167 {
169 {
170 //the frame thumbnail rect
171 if (!thumbnailIsVisible()) {
172 parentRect.setSize(QSize(3*numericFontWidth + 2, fontHeight));
173 return parentRect;
174 }
175
176 parentRect.setSize(QSize(thumbnailWidth, 120));
177 parentRect.translate(0, fontHeight);
178 return parentRect;
179 }
181 {
182 QRect itemNameRect = parentRect;
183 itemNameRect.setSize(QSize(thumbnailWidth - (12 * numericFontWidth + 22), fontHeight));
184 itemNameRect.moveLeft(parentRect.left() + 3*numericFontWidth + 2);
185 return itemNameRect;
186 }
188 {
189 QRect secondRect = parentRect;
190 secondRect.setSize(QSize(5 * numericFontWidth + 10, fontHeight));
191 secondRect.moveLeft(parentRect.left() + thumbnailWidth - 9*numericFontWidth -20);
192 return secondRect;
193 }
195 {
196 QRect frameRect = parentRect;
197 frameRect.setSize(QSize(4 * numericFontWidth + 10, fontHeight));
198 frameRect.moveLeft(parentRect.left() + thumbnailWidth - 4*numericFontWidth - 10);
199 return frameRect;
200 }
201 default:
202 {
203 //comment rect
204 if (!commentIsVisible()) {
205 return QRect();
206 }
207
208 int thumbnailheight = thumbnailIsVisible() ? 120 : 0;
209 if (m_itemOrientation == Qt::Vertical) {
210 const StoryboardModel* Model = dynamic_cast<const StoryboardModel*>(model());
212 parentRect.setTop(parentRect.top() + thumbnailheight + fontHeight + Model->visibleCommentsUpto(index) * 100);
213 parentRect.setHeight(100);
214 return parentRect;
215 }
216 else {
217 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(model(), QRect());
218 const StoryboardModel* storyboardModel = dynamic_cast<const StoryboardModel*>(model());
219 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(storyboardModel, QRect());
220 int numVisibleComments = storyboardModel->visibleCommentCount();
221 int commentWidth = 200;
222 if (numVisibleComments) {
223 commentWidth = qMax(200, (viewport()->width() - 250) / numVisibleComments);
224 }
225 parentRect.setSize(QSize(commentWidth, thumbnailheight + fontHeight));
226 parentRect.moveLeft(parentRect.left() + thumbnailWidth + storyboardModel->visibleCommentsUpto(index) * commentWidth);
227 return parentRect;
228 }
229 }
230 }
231 }
232}
233
234QModelIndex StoryboardView::indexAt(const QPoint &point) const
235{
236 QModelIndex index = QListView::indexAt(point);
237 if (index.isValid()) {
238 //look for the index in children of the current index
239 int numChild = model()->rowCount(index);
240 for (int row = 0; row < numChild; row++) {
241 QRect childRect = visualRect(model()->index(row, 0, index));
242 if (childRect.contains(point)) {
243 return model()->index(row, 0, index);
244 }
245 }
246 }
247 return index;
248}
249
250void StoryboardView::setItemOrientation(Qt::Orientation orientation)
251{
252 m_itemOrientation = orientation;
253}
254
256{
257 return m_itemOrientation;
258}
259
261{
262 return m_commentIsVisible;
263}
264
269
274
279
281{
282 StoryboardModel* pModel = dynamic_cast<StoryboardModel*>(model());
283 QMenu contextMenu;
284 QModelIndex index = indexAt(point);
285 if (!index.isValid()) {
286 contextMenu.addAction(i18nc("Add new scene as the last storyboard", "Add Scene"), [index, pModel] {pModel->insertItem(index, false); });
287 }
288 else if (index.parent().isValid()) {
289 index = index.parent();
290 }
291
292 if (index.isValid()) {
293 contextMenu.addAction(i18nc("Add scene after active scene", "Add Scene After"), [index, pModel] {pModel->insertItem(index, true); });
294 if (index.row() > 0) {
295 contextMenu.addAction(i18nc("Add scene before active scene", "Add Scene Before"), [index, pModel] {pModel->insertItem(index, false); });
296 }
297
298 contextMenu.addAction(i18nc("Duplicate current scene from storyboard docker", "Duplicate Scene"), [index, pModel] {
299 int row = index.row();
301 command->redo();
302 pModel->pushUndoCommand(command);
303 });
304
305 contextMenu.addAction(i18nc("Remove current scene from storyboards", "Remove Scene"), [index, pModel] {
306 int row = index.row();
307 KisRemoveStoryboardCommand *command = new KisRemoveStoryboardCommand(row, pModel->getData().at(row), pModel);
308 pModel->removeItem(index, command);
309 pModel->pushUndoCommand(command);
310 });
311 }
312 contextMenu.exec(viewport()->mapToGlobal(point));
313}
314
315void StoryboardView::slotItemClicked(const QModelIndex &clicked)
316{
317 StoryboardModel* sbModel = dynamic_cast<StoryboardModel*>(model());
318
319 if(sbModel) {
320 sbModel->visualizeScene(clicked.parent().isValid() ? clicked.parent() : clicked);
321 }
322}
323
325{
327 const StoryboardModel* sbModel = dynamic_cast<const StoryboardModel*>(model());
329 QModelIndex index = sbModel->indexFromFrame(frame);
330 if (index.isValid()) {
331 selectionModel()->select(index, QItemSelectionModel::ClearAndSelect);
332 selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect);
333 scrollTo(index);
334 }
335}
336
337void StoryboardView::mouseReleaseEvent(QMouseEvent *event) {
338 QModelIndex index = indexAt(event->pos());
339
340 // To prevent selection changes from occurring when hitting the "plus" button,
341 // we want to filter out these inputs before passing it up to QListView / QAbstractItemView
342 if (index.isValid() && index.parent().isValid() && index.row() == StoryboardItem::FrameNumber) {
343 StoryboardDelegate* sbDelegate = dynamic_cast<StoryboardDelegate*>(itemDelegate(index));
344 QRect itemRect = visualRect(index);
345 if (sbDelegate && sbDelegate->isOverlappingActionIcons(itemRect, event)) {
346 return;
347 }
348 }
349
350 QListView::mouseReleaseEvent(event);
351}
352
354 if (model()) {
355 StoryboardModel* m_storyboardModel = static_cast<StoryboardModel*>(model());
356 const bool hasContent = m_storyboardModel->hasIndex(0,0);
357 if (hasContent) {
358 const bool hasComments = m_storyboardModel->visibleCommentCount() > 0;
359 const bool hasMoreThanOneComment = m_storyboardModel->visibleCommentCount() > 1;
360 const float commentPadding = hasComments ? 1.0f + (0.1f * hasMoreThanOneComment) : 0.0f;
361 const int thumbnailWidth = 286;
362 const int commentWidth = 200 * commentPadding;
363 return QSize(thumbnailWidth + commentWidth, 128);
364 }
365 }
366
367 return QSize(250, 128);
368}
float value(const T *src, size_t ch)
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
bool isOverlappingActionIcons(const QRect &rect, const QMouseEvent *event)
@ DurationFrame
Store the duration in frame at index 3. Data type stored here should be int.
@ FrameNumber
Store the frame number at index 0. Data type stored here should be ThumbnailData.
@ DurationSecond
Store the duration in second at index 2. Data type stored here should be int.
@ ItemName
Store the item name at index 1. Data type stored here should be string.
The main storyboard model. This class manages a StoryboardItemList which is a list of StoryboardItem ...
void visualizeScene(const QModelIndex &index, bool useUndo=true)
StoryboardItemList getData()
QModelIndex indexFromFrame(int frame, bool framePerfect=true) const
Returns the index of the item corresponding the frame, if there is an item with that frame.
bool insertItem(QModelIndex index, bool after)
inserts item after or before index based on after parameter
void pushUndoCommand(KUndo2Command *command)
bool removeItem(QModelIndex index, KUndo2Command *command=nullptr)
removes item, deletes keyframes within and shifts keyframe after the scene to fill in the gap
int visibleCommentCount() const
Used in StoryboardDelegate and StoryboardView to get size of one storyboard item.
int visibleCommentsUpto(QModelIndex index) const
Used in StoryboardView to design the layout of storyboard item.
void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const override
StoryboardStyle(QStyle *baseStyle=0)
QRect visualRect(const QModelIndex &index) const override
QModelIndex indexAt(const QPoint &point) const override
Qt::Orientation itemOrientation()
whether Comments are below or on the right of Thumbnail
bool thumbnailIsVisible() const
void setThumbnailVisibility(bool value)
Sets the visibility of thumbnails.
void setCurrentItem(int frame)
changes the currentIndex and selectedIndex to frame
void mouseReleaseEvent(QMouseEvent *event) override
Qt::Orientation m_itemOrientation
void setItemOrientation(Qt::Orientation orientation)
void setCommentVisibility(bool value)
Sets the visibility of comments.
void paintEvent(QPaintEvent *event) override
~StoryboardView() override
StoryboardView(QWidget *parent=0)
void slotItemClicked(const QModelIndex &clicked)
QSize sizeHint() const override
void slotContextMenuRequested(const QPoint &)
bool commentIsVisible() const
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128