1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the demonstration applications of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:BSD$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** BSD License Usage |
18 | ** Alternatively, you may use this file under the terms of the BSD license |
19 | ** as follows: |
20 | ** |
21 | ** "Redistribution and use in source and binary forms, with or without |
22 | ** modification, are permitted provided that the following conditions are |
23 | ** met: |
24 | ** * Redistributions of source code must retain the above copyright |
25 | ** notice, this list of conditions and the following disclaimer. |
26 | ** * Redistributions in binary form must reproduce the above copyright |
27 | ** notice, this list of conditions and the following disclaimer in |
28 | ** the documentation and/or other materials provided with the |
29 | ** distribution. |
30 | ** * Neither the name of The Qt Company Ltd nor the names of its |
31 | ** contributors may be used to endorse or promote products derived |
32 | ** from this software without specific prior written permission. |
33 | ** |
34 | ** |
35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." |
46 | ** |
47 | ** $QT_END_LICENSE$ |
48 | ** |
49 | ****************************************************************************/ |
50 | |
51 | #include "customproxy.h" |
52 | |
53 | #include <QGraphicsScene> |
54 | #include <QPainter> |
55 | #include <QStyleOptionGraphicsItem> |
56 | |
57 | CustomProxy::CustomProxy(QGraphicsItem *parent, Qt::WindowFlags wFlags) |
58 | : QGraphicsProxyWidget(parent, wFlags), timeLine(new QTimeLine(250, this)) |
59 | { |
60 | connect(timeLine, &QTimeLine::valueChanged, |
61 | this, &CustomProxy::updateStep); |
62 | connect(timeLine, &QTimeLine::stateChanged, |
63 | this, &CustomProxy::stateChanged); |
64 | } |
65 | |
66 | QRectF CustomProxy::boundingRect() const |
67 | { |
68 | return QGraphicsProxyWidget::boundingRect().adjusted(0, 0, 10, 10); |
69 | } |
70 | |
71 | void CustomProxy::paintWindowFrame(QPainter *painter, const QStyleOptionGraphicsItem *option, |
72 | QWidget *widget) |
73 | { |
74 | const QColor color(0, 0, 0, 64); |
75 | |
76 | QRectF r = windowFrameRect(); |
77 | QRectF right(r.right(), r.top() + 10, 10, r.height() - 10); |
78 | QRectF bottom(r.left() + 10, r.bottom(), r.width(), 10); |
79 | bool intersectsRight = right.intersects(option->exposedRect); |
80 | bool intersectsBottom = bottom.intersects(option->exposedRect); |
81 | if (intersectsRight && intersectsBottom) { |
82 | QPainterPath path; |
83 | path.addRect(right); |
84 | path.addRect(bottom); |
85 | painter->setPen(Qt::NoPen); |
86 | painter->setBrush(color); |
87 | painter->drawPath(path); |
88 | } else if (intersectsBottom) { |
89 | painter->fillRect(bottom, color); |
90 | } else if (intersectsRight) { |
91 | painter->fillRect(right, color); |
92 | } |
93 | |
94 | QGraphicsProxyWidget::paintWindowFrame(painter, option, widget); |
95 | } |
96 | |
97 | void CustomProxy::hoverEnterEvent(QGraphicsSceneHoverEvent *event) |
98 | { |
99 | QGraphicsProxyWidget::hoverEnterEvent(event); |
100 | scene()->setActiveWindow(this); |
101 | if (qFuzzyCompare(timeLine->currentValue(), 1)) |
102 | zoomIn(); |
103 | } |
104 | |
105 | void CustomProxy::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) |
106 | { |
107 | QGraphicsProxyWidget::hoverLeaveEvent(event); |
108 | if (!popupShown |
109 | && (timeLine->direction() != QTimeLine::Backward || qFuzzyIsNull(timeLine->currentValue()))) { |
110 | zoomOut(); |
111 | } |
112 | } |
113 | |
114 | bool CustomProxy::sceneEventFilter(QGraphicsItem *watched, QEvent *event) |
115 | { |
116 | if (watched->isWindow() |
117 | && (event->type() == QEvent::UngrabMouse || event->type() == QEvent::GrabMouse)) { |
118 | popupShown = watched->isVisible(); |
119 | if (!popupShown && !isUnderMouse()) |
120 | zoomOut(); |
121 | } |
122 | return QGraphicsProxyWidget::sceneEventFilter(watched, event); |
123 | } |
124 | |
125 | QVariant CustomProxy::itemChange(GraphicsItemChange change, const QVariant &value) |
126 | { |
127 | if (change == ItemChildAddedChange || change == ItemChildRemovedChange) { |
128 | if (change == ItemChildAddedChange) { |
129 | currentPopup = qvariant_cast<QGraphicsItem *>(value); |
130 | currentPopup->setCacheMode(ItemCoordinateCache); |
131 | if (scene()) |
132 | currentPopup->installSceneEventFilter(this); |
133 | } else if (scene()) { |
134 | currentPopup->removeSceneEventFilter(this); |
135 | currentPopup = nullptr; |
136 | } |
137 | } else if (currentPopup && change == ItemSceneHasChanged) { |
138 | currentPopup->installSceneEventFilter(this); |
139 | } |
140 | return QGraphicsProxyWidget::itemChange(change, value); |
141 | } |
142 | |
143 | void CustomProxy::updateStep(qreal step) |
144 | { |
145 | QRectF r = boundingRect(); |
146 | setTransform(QTransform() |
147 | .translate(r.width() / 2, r.height() / 2) |
148 | .rotate(step * 30, Qt::XAxis) |
149 | .rotate(step * 10, Qt::YAxis) |
150 | .rotate(step * 5, Qt::ZAxis) |
151 | .scale(1 + 1.5 * step, 1 + 1.5 * step) |
152 | .translate(-r.width() / 2, -r.height() / 2)); |
153 | } |
154 | |
155 | void CustomProxy::stateChanged(QTimeLine::State state) |
156 | { |
157 | if (state == QTimeLine::Running) { |
158 | if (timeLine->direction() == QTimeLine::Forward) |
159 | setCacheMode(ItemCoordinateCache); |
160 | } else if (state == QTimeLine::NotRunning) { |
161 | if (timeLine->direction() == QTimeLine::Backward) |
162 | setCacheMode(DeviceCoordinateCache); |
163 | } |
164 | } |
165 | |
166 | void CustomProxy::zoomIn() |
167 | { |
168 | if (timeLine->direction() != QTimeLine::Forward) |
169 | timeLine->setDirection(QTimeLine::Forward); |
170 | if (timeLine->state() == QTimeLine::NotRunning) |
171 | timeLine->start(); |
172 | } |
173 | |
174 | void CustomProxy::zoomOut() |
175 | { |
176 | if (timeLine->direction() != QTimeLine::Backward) |
177 | timeLine->setDirection(QTimeLine::Backward); |
178 | if (timeLine->state() == QTimeLine::NotRunning) |
179 | timeLine->start(); |
180 | } |
181 | |