Update copyright headers
[qt:qt.git] / demos / qtdemo / textbutton.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the demonstration applications of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
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 http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "textbutton.h"
43 #include "demoitemanimation.h"
44 #include "demotextitem.h"
45 #include "colors.h"
46 #include "menumanager.h"
47
48 #define BUTTON_WIDTH 180
49 #define BUTTON_HEIGHT 19
50
51 class ButtonBackground : public DemoItem
52 {
53 public:
54     TextButton::BUTTONTYPE type;
55     bool highlighted;
56     bool pressed;
57     QSize logicalSize;
58
59     ButtonBackground(TextButton::BUTTONTYPE type, bool highlighted, bool pressed, QSize logicalSize,
60         QGraphicsScene *scene, QGraphicsItem *parent) : DemoItem(scene, parent)
61     {
62         this->type = type;
63         this->highlighted = highlighted;
64         this->pressed = pressed;
65         this->logicalSize = logicalSize;
66         useSharedImage(QString(__FILE__) + static_cast<int>(type) + highlighted + pressed);
67     }
68
69 protected:
70     QImage *createImage(const QMatrix &matrix) const
71     {
72         if (type == TextButton::SIDEBAR || type == TextButton::PANEL)
73             return createRoundButtonBackground(matrix);
74         else
75             return createArrowBackground(matrix);
76     }
77
78     QImage *createRoundButtonBackground(const QMatrix &matrix) const
79     {
80         QRect scaledRect;
81         scaledRect = matrix.mapRect(QRect(0, 0, this->logicalSize.width(), this->logicalSize.height()));
82
83         QImage *image = new QImage(scaledRect.width(), scaledRect.height(), QImage::Format_ARGB32_Premultiplied);
84         image->fill(QColor(0, 0, 0, 0).rgba());
85         QPainter painter(image);
86         painter.setRenderHint(QPainter::SmoothPixmapTransform);
87         painter.setRenderHint(QPainter::Antialiasing);
88         painter.setPen(Qt::NoPen);
89
90         if (Colors::useEightBitPalette){
91             painter.setPen(QColor(120, 120, 120));
92             if (this->pressed)
93                 painter.setBrush(QColor(60, 60, 60));
94             else if (this->highlighted)
95                 painter.setBrush(QColor(100, 100, 100));
96             else
97                 painter.setBrush(QColor(80, 80, 80));
98         }
99         else {
100             QLinearGradient outlinebrush(0, 0, 0, scaledRect.height());
101             QLinearGradient brush(0, 0, 0, scaledRect.height());
102
103             brush.setSpread(QLinearGradient::PadSpread);
104             QColor highlight(255, 255, 255, 70);
105             QColor shadow(0, 0, 0, 70);
106             QColor sunken(220, 220, 220, 30);
107             QColor normal1(255, 255, 245, 60);
108             QColor normal2(255, 255, 235, 10);
109
110             if (this->type == TextButton::PANEL){
111                 normal1 = QColor(200, 170, 160, 50);
112                 normal2 = QColor(50, 10, 0, 50);
113             }
114
115            if (pressed) {
116                outlinebrush.setColorAt(0.0f, shadow);
117                outlinebrush.setColorAt(1.0f, highlight);
118                brush.setColorAt(0.0f, sunken);
119                painter.setPen(Qt::NoPen);
120            } else {
121                outlinebrush.setColorAt(1.0f, shadow);
122                outlinebrush.setColorAt(0.0f, highlight);
123                brush.setColorAt(0.0f, normal1);
124                if (!this->highlighted)
125                    brush.setColorAt(1.0f, normal2);
126                painter.setPen(QPen(outlinebrush, 1));
127            }
128            painter.setBrush(brush);
129         }
130
131         if (this->type == TextButton::PANEL)
132             painter.drawRect(0, 0, scaledRect.width(), scaledRect.height());
133         else
134             painter.drawRoundedRect(0, 0, scaledRect.width(), scaledRect.height(), 10, 90, Qt::RelativeSize);
135         return image;
136     }
137
138     QImage *createArrowBackground(const QMatrix &matrix) const
139     {
140         QRect scaledRect;
141         scaledRect = matrix.mapRect(QRect(0, 0, this->logicalSize.width(), this->logicalSize.height()));
142
143         QImage *image = new QImage(scaledRect.width(), scaledRect.height(), QImage::Format_ARGB32_Premultiplied);
144         image->fill(QColor(0, 0, 0, 0).rgba());
145         QPainter painter(image);
146         painter.setRenderHint(QPainter::SmoothPixmapTransform);
147         painter.setRenderHint(QPainter::Antialiasing);
148         painter.setPen(Qt::NoPen);
149
150         if (Colors::useEightBitPalette){
151             painter.setPen(QColor(120, 120, 120));
152             if (this->pressed)
153                 painter.setBrush(QColor(60, 60, 60));
154             else if (this->highlighted)
155                 painter.setBrush(QColor(100, 100, 100));
156             else
157                 painter.setBrush(QColor(80, 80, 80));
158         }
159         else {
160             QLinearGradient outlinebrush(0, 0, 0, scaledRect.height());
161             QLinearGradient brush(0, 0, 0, scaledRect.height());
162
163             brush.setSpread(QLinearGradient::PadSpread);
164             QColor highlight(255, 255, 255, 70);
165             QColor shadow(0, 0, 0, 70);
166             QColor sunken(220, 220, 220, 30);
167             QColor normal1 = QColor(200, 170, 160, 50);
168             QColor normal2 = QColor(50, 10, 0, 50);
169
170            if (pressed) {
171                outlinebrush.setColorAt(0.0f, shadow);
172                outlinebrush.setColorAt(1.0f, highlight);
173                brush.setColorAt(0.0f, sunken);
174                painter.setPen(Qt::NoPen);
175            } else {
176                outlinebrush.setColorAt(1.0f, shadow);
177                outlinebrush.setColorAt(0.0f, highlight);
178                brush.setColorAt(0.0f, normal1);
179                if (!this->highlighted)
180                    brush.setColorAt(1.0f, normal2);
181                painter.setPen(QPen(outlinebrush, 1));
182            }
183            painter.setBrush(brush);
184         }
185
186         painter.drawRect(0, 0, scaledRect.width(), scaledRect.height());
187
188         float xOff = scaledRect.width() / 2;
189         float yOff = scaledRect.height() / 2;
190         float sizex = 3.0f * matrix.m11();
191         float sizey = 1.5f * matrix.m22();
192         if (this->type == TextButton::UP)
193             sizey *= -1;
194         QPainterPath path;
195         path.moveTo(xOff, yOff + (5 * sizey));
196         path.lineTo(xOff - (4 * sizex), yOff - (3 * sizey));
197         path.lineTo(xOff + (4 * sizex), yOff - (3 * sizey));
198         path.lineTo(xOff, yOff + (5 * sizey));
199         painter.drawPath(path);
200
201         return image;
202     }
203
204 };
205
206 TextButton::TextButton(const QString &text, ALIGNMENT align, int userCode,
207     QGraphicsScene *scene, QGraphicsItem *parent, BUTTONTYPE type)
208     : DemoItem(scene, parent)
209 {
210     this->menuString = text;
211     this->buttonLabel = text;
212     this->alignment = align;
213     this->buttonType = type;
214     this->userCode = userCode;
215     this->bgOn = 0;
216     this->bgOff = 0;
217     this->bgHighlight = 0;
218     this->bgDisabled = 0;
219     this->state = OFF;
220
221     this->setAcceptsHoverEvents(true);
222     this->setCursor(Qt::PointingHandCursor);
223
224     // Calculate button size:
225     const int w = 180;
226     const int h = 19;
227     if (type == SIDEBAR || type == PANEL)
228         this->logicalSize = QSize(w, h);
229     else
230         this->logicalSize = QSize(int((w / 2.0f) - 5), int(h * 1.5f));
231 }
232
233 void TextButton::setMenuString(const QString &menu)
234 {
235     this->menuString = menu;
236 }
237
238 void TextButton::prepare()
239 {
240     if (!this->prepared){
241         this->prepared = true;
242         this->setupHoverText();
243         this->setupScanItem();
244         this->setupButtonBg();
245     }
246 }
247
248 TextButton::~TextButton()
249 {
250     if (this->prepared){
251         if (Colors::useButtonBalls)
252             delete this->scanAnim;
253     }
254 }
255
256 QRectF TextButton::boundingRect() const
257 {
258     return QRectF(0, 0, this->logicalSize.width(), this->logicalSize.height());
259 };
260
261 void TextButton::setupHoverText()
262 {
263     if (this->buttonLabel.isEmpty())
264         return;
265
266     DemoTextItem *textItem = new DemoTextItem(this->buttonLabel, Colors::buttonFont(), Colors::buttonText, -1, this->scene(), this);
267     textItem->setZValue(zValue() + 2);
268     textItem->setPos(16, 0);
269 }
270
271 void TextButton::setupScanItem()
272 {
273     if (Colors::useButtonBalls){
274         ScanItem *scanItem = new ScanItem(0, this);
275         scanItem->setZValue(zValue() + 1);
276
277         this->scanAnim = new DemoItemAnimation(scanItem);
278         this->scanAnim->timeline->setLoopCount(1);
279
280         float x = 1;
281         float y = 1.5f;
282         float stop = BUTTON_WIDTH - scanItem->boundingRect().width() - x;
283         if (this->alignment == LEFT){
284             this->scanAnim->setDuration(2500);
285             this->scanAnim->setPosAt(0.0, QPointF(x, y));
286             this->scanAnim->setPosAt(0.5, QPointF(x, y));
287             this->scanAnim->setPosAt(0.7, QPointF(stop, y));
288             this->scanAnim->setPosAt(1.0, QPointF(x, y));
289             scanItem->setPos(QPointF(x, y));
290         }
291         else {
292             this->scanAnim->setPosAt(0.0, QPointF(stop, y));
293             this->scanAnim->setPosAt(0.5, QPointF(x, y));
294             this->scanAnim->setPosAt(1.0, QPointF(stop, y));
295             scanItem->setPos(QPointF(stop, y));
296         }
297     }
298 }
299
300 void TextButton::setState(STATE state)
301 {
302     this->state = state;
303     this->bgOn->setRecursiveVisible(state == ON);
304     this->bgOff->setRecursiveVisible(state == OFF);
305     this->bgHighlight->setRecursiveVisible(state == HIGHLIGHT);
306     this->bgDisabled->setRecursiveVisible(state == DISABLED);
307     this->setCursor(state == DISABLED ? Qt::ArrowCursor : Qt::PointingHandCursor);
308
309 }
310
311 void TextButton::setupButtonBg()
312 {
313     this->bgOn = new ButtonBackground(this->buttonType, true, true, this->logicalSize, this->scene(), this);
314     this->bgOff = new ButtonBackground(this->buttonType, false, false, this->logicalSize, this->scene(), this);
315     this->bgHighlight = new ButtonBackground(this->buttonType, true, false, this->logicalSize, this->scene(), this);
316     this->bgDisabled = new ButtonBackground(this->buttonType, true, true, this->logicalSize, this->scene(), this);
317     this->setState(OFF);
318 }
319
320 void TextButton::hoverEnterEvent(QGraphicsSceneHoverEvent *)
321 {
322     if (this->locked || this->state == DISABLED)
323         return;
324
325     if (this->state == OFF){
326         this->setState(HIGHLIGHT);
327
328         if (Colors::noAnimations && Colors::useButtonBalls){
329             // wait a bit in the beginning
330             // to enhance the effect. Have to this here
331             // so that the adaption can be dynamic
332             this->scanAnim->setDuration(1000);
333             this->scanAnim->setPosAt(0.2, this->scanAnim->posAt(0));
334         }
335
336         if (MenuManager::instance()->window->fpsMedian > 10
337             || Colors::noAdapt
338             || Colors::noTimerUpdate){
339             if (Colors::useButtonBalls)
340                 this->scanAnim->play(true, true);
341         }
342     }
343 }
344
345 void TextButton::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
346 {
347     Q_UNUSED(event);
348     if (this->state == DISABLED)
349         return;
350
351     this->setState(OFF);
352
353     if (Colors::noAnimations && Colors::useButtonBalls)
354         this->scanAnim->stop();
355 }
356
357 void TextButton::mousePressEvent(QGraphicsSceneMouseEvent *)
358 {
359     if (this->state == DISABLED)
360         return;
361
362     if (this->state == HIGHLIGHT || this->state == OFF)
363         this->setState(ON);
364 }
365
366 void TextButton::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
367 {
368     if (this->state == ON){
369         this->setState(OFF);
370         if (!this->locked && this->boundingRect().contains(event->pos())){
371             MenuManager::instance()->itemSelected(this->userCode, this->menuString);
372         }
373     }
374 }
375
376 void TextButton::animationStarted(int)
377 {
378     if (this->state == DISABLED)
379         return;
380     this->setState(OFF);
381 }
382
383
384