1 /***************************************************************************
2                              qgsmodelcomponentgraphicitem.h
3                              ----------------------------------
4     Date                 : March 2020
5     Copyright            : (C) 2020 Nyall Dawson
6     Email                : nyall dot dawson at gmail dot com
7  ***************************************************************************
8  *                                                                         *
9  *   This program is free software; you can redistribute it and/or modify  *
10  *   it under the terms of the GNU General Public License as published by  *
11  *   the Free Software Foundation; either version 2 of the License, or     *
12  *   (at your option) any later version.                                   *
13  *                                                                         *
14  ***************************************************************************/
15 
16 #ifndef QGSMODELCOMPONENTGRAPHICITEM_H
17 #define QGSMODELCOMPONENTGRAPHICITEM_H
18 
19 #include "qgis.h"
20 #include "qgis_gui.h"
21 #include <QGraphicsObject>
22 #include <QFont>
23 #include <QPicture>
24 #include <QPointer>
25 
26 class QgsProcessingModelComponent;
27 class QgsProcessingModelParameter;
28 class QgsProcessingModelChildAlgorithm;
29 class QgsProcessingModelOutput;
30 class QgsProcessingModelComment;
31 class QgsProcessingModelAlgorithm;
32 class QgsModelDesignerFlatButtonGraphicItem;
33 class QgsModelDesignerFoldButtonGraphicItem;
34 class QgsModelGraphicsView;
35 class QgsModelViewMouseEvent;
36 class QgsProcessingModelGroupBox;
37 
38 ///@cond NOT_STABLE
39 
40 /**
41  * \ingroup gui
42  * \brief Base class for graphic items representing model components in the model designer.
43  * \warning Not stable API
44  * \since QGIS 3.14
45  */
46 class GUI_EXPORT QgsModelComponentGraphicItem : public QGraphicsObject
47 {
48     Q_OBJECT
49 
50   public:
51 
52     //! Available item states
53     enum State
54     {
55       Normal, //!< Normal state
56       Selected, //!< Item is selected
57       Hover, //!< Cursor is hovering over an deselected item
58     };
59 
60     //! Available flags
61     enum Flag
62     {
63       // For future API flexibility only and to avoid sip issues, remove when real entries are added to flags.
64       Unused = 1 << 0, //!< Temporary unused entry
65     };
66     Q_DECLARE_FLAGS( Flags, Flag )
67 
68     /**
69      * Constructor for QgsModelComponentGraphicItem for the specified \a component, with the specified \a parent item.
70      *
71      * The \a model argument specifies the associated processing model. Ownership of \a model is not transferred, and
72      * it must exist for the lifetime of this object.
73      *
74      * Ownership of \a component is transferred to the item.
75      */
76     QgsModelComponentGraphicItem( QgsProcessingModelComponent *component SIP_TRANSFER,
77                                   QgsProcessingModelAlgorithm *model,
78                                   QGraphicsItem *parent SIP_TRANSFERTHIS );
79 
80     ~QgsModelComponentGraphicItem() override;
81 
82     /**
83      * Returns item flags.
84      */
85     virtual Flags flags() const;
86 
87     /**
88      * Returns the model component associated with this item.
89      */
90     QgsProcessingModelComponent *component();
91 
92     /**
93      * Returns the model component associated with this item.
94      */
95     const QgsProcessingModelComponent *component() const SIP_SKIP;
96 
97     /**
98      * Returns the model associated with this item.
99      */
100     QgsProcessingModelAlgorithm *model();
101 
102     /**
103      * Returns the associated view.
104      */
105     QgsModelGraphicsView *view();
106 
107     /**
108      * Returns the font used to render text in the item.
109      * \see setFont()
110      */
111     QFont font() const;
112 
113     /**
114      * Sets the \a font used to render text in the item.
115      * \see font()
116      */
117     void setFont( const QFont &font );
118 
119     /**
120      * Moves the component by the specified \a dx and \a dy.
121      *
122      * \warning Call this method, not QGraphicsItem::moveBy!
123      */
124     void moveComponentBy( qreal dx, qreal dy );
125 
126     /**
127      * Shows a preview of moving the item from its stored position by \a dx, \a dy.
128      */
129     void previewItemMove( qreal dx, qreal dy );
130 
131     /**
132      * Sets a new scene \a rect for the item.
133      */
134     void setItemRect( QRectF rect );
135 
136 #ifndef SIP_RUN
137 
138     /**
139      * Shows a preview of setting a new \a rect for the item.
140      */
141     QRectF previewItemRectChange( QRectF rect );
142 
143     /**
144      * Sets a new scene \a rect for the item.
145      */
146     void finalizePreviewedItemRectChange( QRectF rect );
147 
148     /**
149      * Handles a model hover enter \a event.
150      */
151     virtual void modelHoverEnterEvent( QgsModelViewMouseEvent *event );
152 
153     /**
154      * Handles a model hover move \a event.
155      */
156     virtual void modelHoverMoveEvent( QgsModelViewMouseEvent *event );
157 
158     /**
159      * Handles a model hover leave \a event.
160      */
161     virtual void modelHoverLeaveEvent( QgsModelViewMouseEvent *event );
162 
163     /**
164      * Handles a model double-click \a event.
165      */
166     virtual void modelDoubleClickEvent( QgsModelViewMouseEvent *event );
167 #endif
168     void mouseDoubleClickEvent( QGraphicsSceneMouseEvent *event ) override;
169     void hoverEnterEvent( QGraphicsSceneHoverEvent *event ) override;
170     void hoverMoveEvent( QGraphicsSceneHoverEvent *event ) override;
171     void hoverLeaveEvent( QGraphicsSceneHoverEvent *event ) override;
172     QVariant itemChange( GraphicsItemChange change, const QVariant &value ) override;
173     QRectF boundingRect() const override;
174     bool contains( const QPointF &point ) const override;
175     void paint( QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr ) override;
176 
177     /**
178      * Returns the rectangle representing the body of the item.
179      */
180     QRectF itemRect( bool storedRect = false ) const;
181 
182     /**
183      * Returns the item's label text.
184      *
185      * \see setLabel()
186      */
187     QString label() const;
188 
189     /**
190      * Returns the item's \a label text.
191      *
192      * \see label()
193      */
194     void setLabel( const QString &label );
195 
196     /**
197      * Returns the item's current state.
198      */
199     State state() const;
200 
201     /**
202      * Returns the number of link points associated with the component on the specified \a edge.
203      */
204     virtual int linkPointCount( Qt::Edge edge ) const;
205 
206     /**
207      * Returns the text to use for the link point with the specified \a index on the specified \a edge.
208      */
209     virtual QString linkPointText( Qt::Edge edge, int index ) const;
210 
211     /**
212      * Returns the location of the link point with the specified \a index on the specified \a edge.
213      */
214     QPointF linkPoint( Qt::Edge edge, int index, bool incoming ) const;
215 
216     /**
217      * Returns the best link point to use for a link originating at a specified \a other item.
218      *
219      * \param other item at other end of link
220      * \param edge item edge for calculated best link point
221      * \returns calculated link point in item coordinates.
222      */
223     QPointF calculateAutomaticLinkPoint( QgsModelComponentGraphicItem *other, Qt::Edge &edge SIP_OUT ) const;
224 
225     /**
226      * Returns the best link point to use for a link originating at a specified \a other point.
227      *
228      * \param other point for other end of link (in scene coordinates)
229      * \param edge item edge for calculated best link point
230      * \returns calculated link point in item coordinates.
231      */
232     QPointF calculateAutomaticLinkPoint( const QPointF &point, Qt::Edge &edge SIP_OUT ) const;
233 
234     /**
235      * Called when the comment attached to the item should be edited.
236      *
237      * The default implementation does nothing.
238      */
editComment()239     virtual void editComment() {}
240 
241     /**
242      * Returns TRUE if the component can be deleted.
243      */
canDeleteComponent()244     virtual bool canDeleteComponent() { return false; }
245 
246     /**
247      * Called when the component should be deleted.
248      *
249      * The default implementation does nothing.
250      */
deleteComponent()251     virtual void deleteComponent() {}
252 
253   signals:
254 
255     // TODO - rework this, should be triggered externally when the model actually changes!
256 
257     /**
258      * Emitted by the item to request a repaint of the parent model scene.
259      */
260     void requestModelRepaint();
261 
262     /**
263      * Emitted when the definition of the associated component is about to be changed
264      * by the item.
265      *
266      * The \a text argument gives the translated text describing the change about to occur, and the
267      * optional \a id can be used to group the associated undo commands.
268      */
269     void aboutToChange( const QString &text, int id = 0 );
270 
271     /**
272      * Emitted when the definition of the associated component is changed
273      * by the item.
274      */
275     void changed();
276 
277     /**
278      * Emitted when item requests that all connected arrows are repainted.
279      */
280     void repaintArrows();
281 
282     /**
283      * Emitted when item requires that all connected arrow paths are recalculated.
284      */
285     void updateArrowPaths();
286 
287     /**
288      * Emitted when the item's size or position changes.
289      */
290     void sizePositionChanged();
291 
292   protected slots:
293 
294     /**
295      * Called when the component should be edited.
296      *
297      * The default implementation does nothing.
298      */
editComponent()299     virtual void editComponent() {}
300 
301   protected:
302 
303     /**
304      * Truncates a \a text string so that it fits nicely within the item's width,
305      * accounting for margins and interactive buttons.
306      */
307     QString truncatedTextForItem( const QString &text ) const;
308 
309     /**
310      * Returns the fill color for the item for the specified \a state.
311      */
312     virtual QColor fillColor( State state ) const = 0;
313 
314     /**
315      * Returns the stroke color for the item for the specified \a state.
316      */
317     virtual QColor strokeColor( State state ) const = 0;
318 
319     /**
320      * Returns the label text color for the item for the specified \a state.
321      */
322     virtual QColor textColor( State state ) const = 0;
323 
324     /**
325      * Returns the stroke style to use while rendering the outline of the item.
326      */
327     virtual Qt::PenStyle strokeStyle( State state ) const;
328 
329     /**
330      * Returns the title alignment
331      */
332     virtual Qt::Alignment titleAlignment() const;
333 
334     /**
335      * Returns a QPicture version of the item's icon, if available.
336      */
337     virtual QPicture iconPicture() const;
338 
339     /**
340      * Returns a QPixmap version of the item's icon, if available.
341      */
342     virtual QPixmap iconPixmap() const;
343 
344     /**
345      * Updates the position and size stored in the model for the associated comment
346      */
347     virtual void updateStoredComponentPosition( const QPointF &pos, const QSizeF &size ) = 0;
348 
349     /**
350      * Updates the item's button positions, based on the current item rect.
351      */
352     void updateButtonPositions();
353 
354   private:
355 
356     QSizeF itemSize() const;
357 
358     void updateToolTip( const QPointF &pos );
359 
360     void fold( Qt::Edge edge, bool folded );
361 
362     std::unique_ptr< QgsProcessingModelComponent > mComponent;
363     QgsProcessingModelAlgorithm *mModel = nullptr;
364 
365     bool mInitialized = false;
366     QgsModelDesignerFoldButtonGraphicItem *mExpandTopButton = nullptr;
367     QgsModelDesignerFoldButtonGraphicItem *mExpandBottomButton = nullptr;
368 
369     QString mLabel;
370 
371     QgsModelDesignerFlatButtonGraphicItem *mEditButton = nullptr;
372     QgsModelDesignerFlatButtonGraphicItem *mDeleteButton = nullptr;
373 
374     static constexpr double MIN_COMPONENT_WIDTH = 70;
375     static constexpr double MIN_COMPONENT_HEIGHT = 30;
376 
377     static constexpr double DEFAULT_BUTTON_WIDTH = 16;
378     static constexpr double DEFAULT_BUTTON_HEIGHT = 16;
379     static constexpr double BUTTON_MARGIN = 2;
380     static constexpr double TEXT_MARGIN = 4;
381     static constexpr double RECT_PEN_SIZE = 2;
382     QSizeF mButtonSize { DEFAULT_BUTTON_WIDTH, DEFAULT_BUTTON_HEIGHT };
383 
384     QFont mFont;
385 
386     bool mIsHovering = false;
387     bool mIsMoving = false;
388     QSizeF mTempSize;
389 
390 };
Q_DECLARE_OPERATORS_FOR_FLAGS(QgsModelComponentGraphicItem::Flags)391 Q_DECLARE_OPERATORS_FOR_FLAGS( QgsModelComponentGraphicItem::Flags )
392 
393 /**
394  * \ingroup gui
395  * \brief A graphic item representing a model parameter (input) in the model designer.
396  * \warning Not stable API
397  * \since QGIS 3.14
398  */
399 class GUI_EXPORT QgsModelParameterGraphicItem : public QgsModelComponentGraphicItem
400 {
401     Q_OBJECT
402 
403   public:
404 
405     /**
406      * Constructor for QgsModelParameterGraphicItem for the specified \a parameter, with the specified \a parent item.
407      *
408      * The \a model argument specifies the associated processing model. Ownership of \a model is not transferred, and
409      * it must exist for the lifetime of this object.
410      *
411      * Ownership of \a parameter is transferred to the item.
412      */
413     QgsModelParameterGraphicItem( QgsProcessingModelParameter *parameter SIP_TRANSFER,
414                                   QgsProcessingModelAlgorithm *model,
415                                   QGraphicsItem *parent SIP_TRANSFERTHIS );
416 
417     void contextMenuEvent( QGraphicsSceneContextMenuEvent *event ) override;
418     bool canDeleteComponent() override;
419 
420   protected:
421 
422     QColor fillColor( State state ) const override;
423     QColor strokeColor( State state ) const override;
424     QColor textColor( State state ) const override;
425     QPicture iconPicture() const override;
426     void updateStoredComponentPosition( const QPointF &pos, const QSizeF &size ) override;
427 
428   protected slots:
429 
430     void deleteComponent() override;
431 
432   private:
433     QPicture mPicture;
434 
435 };
436 
437 /**
438  * \ingroup gui
439  * \brief A graphic item representing a child algorithm in the model designer.
440  * \warning Not stable API
441  * \since QGIS 3.14
442  */
443 class GUI_EXPORT QgsModelChildAlgorithmGraphicItem : public QgsModelComponentGraphicItem
444 {
445     Q_OBJECT
446 
447   public:
448 
449     /**
450      * Constructor for QgsModelChildAlgorithmGraphicItem for the specified \a child, with the specified \a parent item.
451      *
452      * The \a model argument specifies the associated processing model. Ownership of \a model is not transferred, and
453      * it must exist for the lifetime of this object.
454      *
455      * Ownership of \a child is transferred to the item.
456      */
457     QgsModelChildAlgorithmGraphicItem( QgsProcessingModelChildAlgorithm *child SIP_TRANSFER,
458                                        QgsProcessingModelAlgorithm *model,
459                                        QGraphicsItem *parent SIP_TRANSFERTHIS );
460     void contextMenuEvent( QGraphicsSceneContextMenuEvent *event ) override;
461     bool canDeleteComponent() override;
462 
463     /**
464      * Sets the results obtained for this child algorithm for the last model execution through the dialog.
465      */
466     void setResults( const QVariantMap &results );
467 
468     /**
469      * Sets the inputs used for this child algorithm for the last model execution through the dialog.
470      */
471     void setInputs( const QVariantMap &inputs );
472 
473   protected:
474 
475     QColor fillColor( State state ) const override;
476     QColor strokeColor( State state ) const override;
477     QColor textColor( State state ) const override;
478     QPixmap iconPixmap() const override;
479     QPicture iconPicture() const override;
480 
481     int linkPointCount( Qt::Edge edge ) const override;
482     QString linkPointText( Qt::Edge edge, int index ) const override;
483     void updateStoredComponentPosition( const QPointF &pos, const QSizeF &size ) override;
484 
485   protected slots:
486 
487     void deleteComponent() override;
488 
489   private slots:
490     void deactivateAlgorithm();
491     void activateAlgorithm();
492 
493   private:
494     QPicture mPicture;
495     QPixmap mPixmap;
496     QVariantMap mResults;
497     QVariantMap mInputs;
498     bool mIsValid = true;
499 };
500 
501 
502 /**
503  * \ingroup gui
504  * \brief A graphic item representing a model output in the model designer.
505  * \warning Not stable API
506  * \since QGIS 3.14
507  */
508 class GUI_EXPORT QgsModelOutputGraphicItem : public QgsModelComponentGraphicItem
509 {
510     Q_OBJECT
511 
512   public:
513 
514     /**
515      * Constructor for QgsModelOutputGraphicItem for the specified \a output, with the specified \a parent item.
516      *
517      * The \a model argument specifies the associated processing model. Ownership of \a model is not transferred, and
518      * it must exist for the lifetime of this object.
519      *
520      * Ownership of \a output is transferred to the item.
521      */
522     QgsModelOutputGraphicItem( QgsProcessingModelOutput *output SIP_TRANSFER,
523                                QgsProcessingModelAlgorithm *model,
524                                QGraphicsItem *parent SIP_TRANSFERTHIS );
525 
526     bool canDeleteComponent() override;
527 
528   protected:
529 
530     QColor fillColor( State state ) const override;
531     QColor strokeColor( State state ) const override;
532     QColor textColor( State state ) const override;
533     QPicture iconPicture() const override;
534     void updateStoredComponentPosition( const QPointF &pos, const QSizeF &size ) override;
535 
536   protected slots:
537 
538     void deleteComponent() override;
539 
540   private:
541 
542     QPicture mPicture;
543 };
544 
545 
546 
547 /**
548  * \ingroup gui
549  * \brief A graphic item representing a model comment in the model designer.
550  * \warning Not stable API
551  * \since QGIS 3.14
552  */
553 class GUI_EXPORT QgsModelCommentGraphicItem : public QgsModelComponentGraphicItem
554 {
555     Q_OBJECT
556 
557   public:
558 
559     /**
560      * Constructor for QgsModelCommentGraphicItem for the specified \a comment, with the specified \a parent item.
561      *
562      * The \a model argument specifies the associated processing model. Ownership of \a model is not transferred, and
563      * it must exist for the lifetime of this object.
564      *
565      * Ownership of \a output is transferred to the item.
566      */
567     QgsModelCommentGraphicItem( QgsProcessingModelComment *comment SIP_TRANSFER,
568                                 QgsModelComponentGraphicItem *parentItem,
569                                 QgsProcessingModelAlgorithm *model,
570                                 QGraphicsItem *parent SIP_TRANSFERTHIS );
571     ~QgsModelCommentGraphicItem() override;
572     void contextMenuEvent( QGraphicsSceneContextMenuEvent *event ) override;
573     bool canDeleteComponent() override;
574 
575     /**
576      * Returns the parent model component item.
577      */
578     QgsModelComponentGraphicItem *parentComponentItem() const;
579 
580   protected:
581 
582     QColor fillColor( State state ) const override;
583     QColor strokeColor( State state ) const override;
584     QColor textColor( State state ) const override;
585     Qt::PenStyle strokeStyle( State state ) const override;
586     void updateStoredComponentPosition( const QPointF &pos, const QSizeF &size ) override;
587 
588   protected slots:
589 
590     void deleteComponent() override;
591     void editComponent() override;
592   private:
593 
594     QgsProcessingModelComment *modelComponent();
595 
596     std::unique_ptr< QgsProcessingModelComponent > mParentComponent;
597     QPointer< QgsModelComponentGraphicItem > mParentItem;
598 
599 
600 };
601 
602 
603 /**
604  * \ingroup gui
605  * \brief A graphic item representing a group box in the model designer.
606  * \warning Not stable API
607  * \since QGIS 3.14
608  */
609 class GUI_EXPORT QgsModelGroupBoxGraphicItem : public QgsModelComponentGraphicItem
610 {
611     Q_OBJECT
612 
613   public:
614 
615     /**
616      * Constructor for QgsModelGroupBoxGraphicItem for the specified group \a box, with the specified \a parent item.
617      *
618      * The \a model argument specifies the associated processing model. Ownership of \a model is not transferred, and
619      * it must exist for the lifetime of this object.
620      *
621      * Ownership of \a output is transferred to the item.
622      */
623     QgsModelGroupBoxGraphicItem( QgsProcessingModelGroupBox *box SIP_TRANSFER,
624                                  QgsProcessingModelAlgorithm *model,
625                                  QGraphicsItem *parent SIP_TRANSFERTHIS );
626     ~QgsModelGroupBoxGraphicItem() override;
627     void contextMenuEvent( QGraphicsSceneContextMenuEvent *event ) override;
628     bool canDeleteComponent() override;
629   protected:
630 
631     QColor fillColor( State state ) const override;
632     QColor strokeColor( State state ) const override;
633     QColor textColor( State state ) const override;
634     Qt::PenStyle strokeStyle( State state ) const override;
635     Qt::Alignment titleAlignment() const override;
636     void updateStoredComponentPosition( const QPointF &pos, const QSizeF &size ) override;
637 
638   protected slots:
639 
640     void deleteComponent() override;
641     void editComponent() override;
642   private:
643 
644 
645 };
646 
647 ///@endcond
648 
649 #endif // QGSMODELCOMPONENTGRAPHICITEM_H
650