1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25
26 #include "navigatortreeview.h"
27
28 #include <qmath.h>
29
30 #include "navigatorview.h"
31 #include "navigatortreemodel.h"
32 #include "qproxystyle.h"
33 #include "previewtooltip.h"
34
35 #include <metainfo.h>
36 #include <theme.h>
37
38 #include <utils/icon.h>
39 #include <utils/utilsicons.h>
40
41 #include <QLineEdit>
42 #include <QPen>
43 #include <QPixmapCache>
44 #include <QMouseEvent>
45 #include <QPainter>
46 #include <QStyleFactory>
47 #include <QEvent>
48 #include <QImage>
49
50 namespace QmlDesigner {
51
52 namespace {
53
54 // This style basically allows us to span the entire row
55 // including the arrow indicators which would otherwise not be
56 // drawn by the delegate
57 class TableViewStyle : public QProxyStyle
58 {
59 public:
TableViewStyle(QObject * parent)60 TableViewStyle(QObject *parent) : QProxyStyle(QStyleFactory::create("fusion"))
61 {
62 setParent(parent);
63 baseStyle()->setParent(parent);
64 }
65
drawPrimitive(PrimitiveElement element,const QStyleOption * option,QPainter * painter,const QWidget * widget=nullptr) const66 void drawPrimitive(PrimitiveElement element,
67 const QStyleOption *option,
68 QPainter *painter,
69 const QWidget *widget = nullptr) const override
70 {
71 static QRect mouseOverStateSavedFrameRectangle;
72 if (element == QStyle::PE_PanelItemViewRow) {
73 if (option->state & QStyle::State_MouseOver)
74 mouseOverStateSavedFrameRectangle = option->rect;
75
76 painter->fillRect(option->rect.adjusted(0, delegateMargin, 0, -delegateMargin),
77 Theme::getColor(Theme::Color::DSnavigatorItemBackground));
78 } else if (element == PE_IndicatorItemViewItemDrop) {
79 // between elements and on elements we have a width
80 if (option->rect.width() > 0) {
81 m_currentTextColor = option->palette.text().color();
82 QRect frameRectangle = adjustedRectangleToWidgetWidth(option->rect, widget);
83 painter->save();
84 if (option->rect.height() == 0) {
85 bool isNotRootItem = option->rect.top() > 10 && mouseOverStateSavedFrameRectangle.top() > 10;
86 if (isNotRootItem)
87 drawIndicatorLine(frameRectangle.topLeft(), frameRectangle.topRight(), painter);
88 } else {
89 drawHighlightFrame(frameRectangle, painter);
90 }
91 painter->restore();
92 }
93 } else if (element == PE_FrameFocusRect) {
94 // don't draw
95 } else if (element == PE_IndicatorBranch) {
96 painter->save();
97 static const int decoration_size = 10;
98 int mid_h = option->rect.x() + option->rect.width() / 2;
99 int mid_v = option->rect.y() + option->rect.height() / 2;
100 int bef_h = mid_h;
101 int bef_v = mid_v;
102 int aft_h = mid_h;
103 int aft_v = mid_v;
104
105 QBrush brush(Theme::getColor(Theme::Color::DSnavigatorBranch), Qt::SolidPattern);
106 if (option->state & State_Item) {
107 if (option->direction == Qt::RightToLeft)
108 painter->fillRect(option->rect.left(), mid_v, bef_h - option->rect.left(), 1, brush);
109 else
110 painter->fillRect(aft_h, mid_v, option->rect.right() - aft_h + 1, 1, brush);
111 }
112 if (option->state & State_Sibling)
113 painter->fillRect(mid_h, aft_v, 1, option->rect.bottom() - aft_v + 1, brush);
114 if (option->state & (State_Open | State_Children | State_Item | State_Sibling))
115 painter->fillRect(mid_h, option->rect.y(), 1, bef_v - option->rect.y(), brush);
116 if (option->state & State_Children) {
117 int delta = decoration_size / 2;
118 bef_h -= delta;
119 bef_v -= delta;
120 //aft_h += delta;
121 //aft_v += delta;
122
123 const QRectF rect(bef_h, bef_v, decoration_size + 1, decoration_size + 1);
124 painter->fillRect(rect, QBrush(Theme::getColor(Theme::Color::DSpanelBackground)));
125
126 static const QPointF collapsePoints[3] = {
127 QPointF(0.0, 0.0),
128 QPointF(4.0, 4.0),
129 QPointF(0.0, 8.0)
130 };
131
132 static const QPointF expandPoints[3] = {
133 QPointF(0.0, 0.0),
134 QPointF(8.0, 0.0),
135 QPointF(4.0, 4.0)
136 };
137
138 auto color = Theme::getColor(Theme::Color::DSnavigatorBranchIndicator);
139 painter->setPen(color);
140 painter->setBrush(color);
141
142 if (option->state & QStyle::State_Open) {
143 painter->translate(QPointF(mid_h - 4, mid_v - 2));
144 painter->drawConvexPolygon(expandPoints, 3);
145 } else {
146 painter->translate(QPointF(mid_h - 2, mid_v - 4));
147 painter->drawConvexPolygon(collapsePoints, 3);
148 }
149 }
150 painter->restore();
151
152 } else {
153 QProxyStyle::drawPrimitive(element, option, painter, widget);
154 }
155 }
156
styleHint(StyleHint hint,const QStyleOption * option=nullptr,const QWidget * widget=nullptr,QStyleHintReturn * returnData=nullptr) const157 int styleHint(StyleHint hint,
158 const QStyleOption *option = nullptr,
159 const QWidget *widget = nullptr,
160 QStyleHintReturn *returnData = nullptr) const override
161 {
162 if (hint == SH_ItemView_ShowDecorationSelected)
163 return 0;
164 else
165 return QProxyStyle::styleHint(hint, option, widget, returnData);
166 }
167
168 private: // functions
highlightBrushColor() const169 QColor highlightBrushColor() const
170 {
171 QColor color = Theme::getColor(Theme::Color::DSnavigatorDropIndicatorBackground);
172 color.setAlphaF(0.7f);
173 return color;
174 }
highlightLineColor() const175 QColor highlightLineColor() const
176 {
177 QColor color = Theme::getColor(Theme::Color::DSnavigatorDropIndicatorOutline);
178 color.setAlphaF(0.7f);
179 return color;
180 }
181
drawHighlightFrame(const QRect & frameRectangle,QPainter * painter) const182 void drawHighlightFrame(const QRect &frameRectangle, QPainter *painter) const
183 {
184 painter->setPen(QPen(highlightLineColor(), 2));
185 painter->setBrush(highlightBrushColor());
186 painter->drawRect(frameRectangle);
187 }
188
drawIndicatorLine(const QPoint & leftPoint,const QPoint & rightPoint,QPainter * painter) const189 void drawIndicatorLine(const QPoint &leftPoint, const QPoint &rightPoint, QPainter *painter) const
190 {
191 painter->setPen(QPen(highlightLineColor(), 3));
192 painter->drawLine(leftPoint, rightPoint);
193 }
194
adjustedRectangleToWidgetWidth(const QRect & originalRectangle,const QWidget * widget) const195 QRect adjustedRectangleToWidgetWidth(const QRect &originalRectangle, const QWidget *widget) const
196 {
197 QRect adjustesRectangle = originalRectangle;
198 adjustesRectangle.setLeft(0);
199 adjustesRectangle.setWidth(widget->rect().width());
200 return adjustesRectangle.adjusted(0, 0, -1, -1);
201 }
202 private: // variables
203 mutable QColor m_currentTextColor;
204 };
205
206 }
207
NavigatorTreeView(QWidget * parent)208 NavigatorTreeView::NavigatorTreeView(QWidget *parent)
209 : QTreeView(parent)
210 {
211 setStyle(new TableViewStyle(this));
212 setMinimumWidth(240);
213 setRootIsDecorated(false);
214 setIndentation(indentation() * 0.5);
215 viewport()->setAttribute(Qt::WA_Hover);
216 }
217
drawSelectionBackground(QPainter * painter,const QStyleOption & option)218 void NavigatorTreeView::drawSelectionBackground(QPainter *painter, const QStyleOption &option)
219 {
220 painter->save();
221 painter->fillRect(option.rect.adjusted(0, delegateMargin, 0, -delegateMargin),
222 Theme::getColor(Theme::Color::DSnavigatorItemBackgroundSelected));
223 painter->restore();
224 }
225
viewportEvent(QEvent * event)226 bool NavigatorTreeView::viewportEvent(QEvent *event)
227 {
228 const QPoint offset(10, 5);
229
230 if (event->type() == QEvent::ToolTip) {
231 auto navModel = qobject_cast<NavigatorTreeModel *>(model());
232 if (navModel) {
233 QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);
234 QModelIndex index = indexAt(helpEvent->pos());
235 QVariantMap imgMap = navModel->data(index, ToolTipImageRole).toMap();
236
237 if (!imgMap.isEmpty()) {
238 m_previewToolTipNodeId = index.internalId();
239 if (!m_previewToolTip || devicePixelRatioF() != m_previewToolTip->devicePixelRatioF()) {
240 if (!m_previewToolTip) {
241 connect(navModel, &NavigatorTreeModel::toolTipPixmapUpdated,
242 [this](const QString &id, const QPixmap &pixmap) {
243 if (m_previewToolTip && m_previewToolTip->id() == id)
244 m_previewToolTip->setPixmap(pixmap);
245 });
246 } else {
247 delete m_previewToolTip;
248 }
249 m_previewToolTip = new PreviewToolTip();
250 }
251 m_previewToolTip->setId(imgMap["id"].toString());
252 m_previewToolTip->setType(imgMap["type"].toString());
253 m_previewToolTip->setInfo(imgMap["info"].toString());
254 m_previewToolTip->setPixmap(imgMap["pixmap"].value<QPixmap>());
255 m_previewToolTip->move(helpEvent->globalPos() + offset);
256 if (!m_previewToolTip->isVisible())
257 m_previewToolTip->show();
258 } else if (m_previewToolTip) {
259 m_previewToolTip->hide();
260 m_previewToolTipNodeId = -1;
261 }
262 }
263 } else if (event->type() == QEvent::Leave) {
264 if (m_previewToolTip) {
265 m_previewToolTip->hide();
266 m_previewToolTipNodeId = -1;
267 }
268 } else if (event->type() == QEvent::HoverMove) {
269 if (m_previewToolTip && m_previewToolTip->isVisible()) {
270 auto *he = static_cast<QHoverEvent *>(event);
271 QModelIndex index = indexAt(he->pos());
272 if (!index.isValid() || index.internalId() != quintptr(m_previewToolTipNodeId)) {
273 m_previewToolTip->hide();
274 m_previewToolTipNodeId = -1;
275 } else {
276 m_previewToolTip->move(mapToGlobal(he->pos()) + offset);
277 }
278 }
279 }
280
281 return QTreeView::viewportEvent(event);
282 }
283
284 }
285