• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.14.38 API Reference
  • KDE Home
  • Contact Us
 

Plasma

  • plasma
  • widgets
pushbutton.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2008 Aaron Seigo <aseigo@kde.org>
3 * Copyright 2008 Marco Martin <notmart@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Library General Public License as
7 * published by the Free Software Foundation; either version 2, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20
21#include "pushbutton.h"
22
23#include <QDir>
24#include <QPainter>
25#include <QStyleOptionGraphicsItem>
26#include <QWeakPointer>
27
28#include <kicon.h>
29#include <kiconeffect.h>
30#include <kmimetype.h>
31#include <kpushbutton.h>
32
33#include "animator.h"
34#include "animations/animation.h"
35#include "framesvg.h"
36#include "paintutils.h"
37#include "private/actionwidgetinterface_p.h"
38#include "private/focusindicator_p.h"
39#include "private/themedwidgetinterface_p.h"
40#include "theme.h"
41
42namespace Plasma
43{
44
45class PushButtonPrivate : public ActionWidgetInterface<PushButton>
46{
47public:
48 PushButtonPrivate(PushButton *pushButton)
49 : ActionWidgetInterface<PushButton>(pushButton),
50 background(0),
51 fadeIn(false),
52 svg(0)
53 {
54 }
55
56 ~PushButtonPrivate()
57 {
58 delete svg;
59 }
60
61 void setPixmap()
62 {
63 if (imagePath.isEmpty()) {
64 delete svg;
65 svg = 0;
66 return;
67 }
68
69 KMimeType::Ptr mime = KMimeType::findByPath(absImagePath);
70 QPixmap pm;
71
72 if (mime->is("image/svg+xml") || mime->is("image/svg+xml-compressed")) {
73 if (!svg || svg->imagePath() != absImagePath) {
74 delete svg;
75 svg = new Svg();
76 svg->setImagePath(imagePath);
77 QObject::connect(svg, SIGNAL(repaintNeeded()), q, SLOT(setPixmap()));
78 if (!svgElement.isNull()) {
79 svg->setContainsMultipleImages(true);
80 }
81 }
82
83 //QPainter p(&pm);
84
85 if (!svgElement.isEmpty() && svg->hasElement(svgElement)) {
86 svg->resize();
87 QSizeF elementSize = svg->elementSize(svgElement);
88 float scale = q->nativeWidget()->iconSize().width() / qMax(elementSize.width(), elementSize.height());
89
90 svg->resize(elementSize * scale);
91 pm = svg->pixmap(svgElement);
92 } else {
93 svg->resize(q->nativeWidget()->iconSize());
94 pm = svg->pixmap();
95 }
96 } else {
97 delete svg;
98 svg = 0;
99 pm = QPixmap(absImagePath);
100 }
101
102 static_cast<KPushButton*>(q->widget())->setIcon(KIcon(pm));
103 }
104
105 void pressedChanged()
106 {
107 if (q->nativeWidget()->isDown() || q->nativeWidget()->isChecked()) {
108 focusIndicator->animateVisibility(false);
109 } else {
110 focusIndicator->animateVisibility(true);
111 }
112 }
113
114 void syncFrame()
115 {
116 if (background) {
117 //resize all panels
118 background->setElementPrefix("pressed");
119 background->resizeFrame(q->size());
120
121 syncActiveRect();
122
123 background->setElementPrefix("normal");
124 background->resizeFrame(q->size());
125 hoverAnimation->setProperty("startPixmap", background->framePixmap());
126
127 background->setElementPrefix("active");
128 background->resizeFrame(activeRect.size());
129 hoverAnimation->setProperty("targetPixmap", background->framePixmap());
130 }
131 }
132
133 void syncActiveRect();
134 void syncBorders();
135
136 FrameSvg *background;
137 bool fadeIn;
138 qreal opacity;
139 QRectF activeRect;
140
141 Animation *hoverAnimation;
142
143 FocusIndicator *focusIndicator;
144 QString imagePath;
145 QString absImagePath;
146 Svg *svg;
147 QString svgElement;
148};
149
150void PushButtonPrivate::syncActiveRect()
151{
152 background->setElementPrefix("normal");
153
154 qreal left, top, right, bottom;
155 background->getMargins(left, top, right, bottom);
156
157 background->setElementPrefix("active");
158 qreal activeLeft, activeTop, activeRight, activeBottom;
159 background->getMargins(activeLeft, activeTop, activeRight, activeBottom);
160
161 activeRect = QRectF(QPointF(0, 0), q->size());
162 activeRect.adjust(left - activeLeft, top - activeTop,
163 -(right - activeRight), -(bottom - activeBottom));
164
165 background->setElementPrefix("normal");
166}
167
168void PushButtonPrivate::syncBorders()
169{
170 //set margins from the normal element
171 qreal left, top, right, bottom;
172
173 background->setElementPrefix("normal");
174 background->getMargins(left, top, right, bottom);
175 q->setContentsMargins(left, top, right, bottom);
176
177 //calc the rect for the over effect
178 syncActiveRect();
179}
180
181
182PushButton::PushButton(QGraphicsWidget *parent)
183 : QGraphicsProxyWidget(parent),
184 d(new PushButtonPrivate(this))
185{
186 d->background = new FrameSvg(this);
187 d->background->setImagePath("widgets/button");
188 d->background->setCacheAllRenderedFrames(true);
189
190 d->background->setElementPrefix("normal");
191
192 d->hoverAnimation = Animator::create(Animator::PixmapTransitionAnimation);
193 d->hoverAnimation->setTargetWidget(this);
194
195 KPushButton *native = new KPushButton;
196 connect(native, SIGNAL(pressed()), this, SIGNAL(pressed()));
197 connect(native, SIGNAL(pressed()), this, SLOT(pressedChanged()));
198 connect(native, SIGNAL(released()), this, SIGNAL(released()));
199 connect(native, SIGNAL(released()), this, SLOT(pressedChanged()));
200 connect(native, SIGNAL(clicked()), this, SIGNAL(clicked()));
201 connect(native, SIGNAL(toggled(bool)), this, SIGNAL(toggled(bool)));
202 setWidget(native);
203 native->setAttribute(Qt::WA_NoSystemBackground);
204 native->setWindowIcon(QIcon());
205
206 setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
207
208 d->focusIndicator = new FocusIndicator(this, d->background);
209
210 d->syncBorders();
211 setAcceptHoverEvents(true);
212
213 connect(d->background, SIGNAL(repaintNeeded()), SLOT(syncBorders()));
214 d->initTheming();
215 d->syncFrame();
216}
217
218PushButton::~PushButton()
219{
220 delete d;
221}
222
223void PushButton::setText(const QString &text)
224{
225 static_cast<KPushButton*>(widget())->setText(text);
226 updateGeometry();
227}
228
229QString PushButton::text() const
230{
231 return static_cast<KPushButton*>(widget())->text();
232}
233
234void PushButton::setImage(const QString &path)
235{
236 if (d->imagePath == path) {
237 return;
238 }
239
240 delete d->svg;
241 d->svg = 0;
242 d->imagePath = path;
243
244 bool absolutePath = !path.isEmpty() &&
245 #ifdef Q_WS_WIN
246 !QDir::isRelativePath(path)
247 #else
248 (path[0] == '/' || path.startsWith(QLatin1String(":/")))
249 #endif
250 ;
251
252 if (absolutePath) {
253 d->absImagePath = path;
254 } else {
255 //TODO: package support
256 d->absImagePath = Theme::defaultTheme()->imagePath(path);
257 }
258
259 d->setPixmap();
260}
261
262void PushButton::setImage(const QString &path, const QString &elementid)
263{
264 d->svgElement = elementid;
265 setImage(path);
266}
267
268QString PushButton::image() const
269{
270 return d->imagePath;
271}
272
273void PushButton::setStyleSheet(const QString &stylesheet)
274{
275 d->focusIndicator->setVisible(stylesheet.isEmpty());
276 widget()->setStyleSheet(stylesheet);
277}
278
279QString PushButton::styleSheet()
280{
281 return widget()->styleSheet();
282}
283
284void PushButton::setAction(QAction *action)
285{
286 d->setAction(action);
287}
288
289QAction *PushButton::action() const
290{
291 return d->action;
292}
293
294void PushButton::setIcon(const KIcon &icon)
295{
296 nativeWidget()->setIcon(icon);
297}
298
299void PushButton::setIcon(const QIcon &icon)
300{
301 setIcon(KIcon(icon));
302}
303
304QIcon PushButton::icon() const
305{
306 return nativeWidget()->icon();
307}
308
309void PushButton::setCheckable(bool checkable)
310{
311 nativeWidget()->setCheckable(checkable);
312}
313
314bool PushButton::isCheckable() const
315{
316 return nativeWidget()->isCheckable();
317}
318
319void PushButton::setChecked(bool checked)
320{
321 nativeWidget()->setChecked(checked);
322}
323
324void PushButton::click()
325{
326 nativeWidget()->animateClick();
327}
328
329bool PushButton::isChecked() const
330{
331 return nativeWidget()->isChecked();
332}
333
334bool PushButton::isDown() const
335{
336 return nativeWidget()->isDown();
337}
338
339KPushButton *PushButton::nativeWidget() const
340{
341 return static_cast<KPushButton*>(widget());
342}
343
344void PushButton::resizeEvent(QGraphicsSceneResizeEvent *event)
345{
346 d->setPixmap();
347
348 d->syncFrame();
349
350 QGraphicsProxyWidget::resizeEvent(event);
351}
352
353void PushButton::paint(QPainter *painter,
354 const QStyleOptionGraphicsItem *option,
355 QWidget *widget)
356{
357 if (!styleSheet().isNull() || Theme::defaultTheme()->useNativeWidgetStyle()) {
358 QGraphicsProxyWidget::paint(painter, option, widget);
359 return;
360 }
361
362 QPixmap bufferPixmap;
363
364 //Normal button, pressed or not
365 if (isEnabled()) {
366 if (nativeWidget()->isDown() || nativeWidget()->isChecked()) {
367 d->background->setElementPrefix("pressed");
368 } else {
369 d->background->setElementPrefix("normal");
370 }
371
372 //flat or disabled
373 } else if (!isEnabled() || nativeWidget()->isFlat()) {
374 bufferPixmap = QPixmap(rect().size().toSize());
375 bufferPixmap.fill(Qt::transparent);
376
377 QPainter buffPainter(&bufferPixmap);
378 d->background->paintFrame(&buffPainter);
379 buffPainter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
380 buffPainter.fillRect(bufferPixmap.rect(), QColor(0, 0, 0, 128));
381
382 painter->drawPixmap(0, 0, bufferPixmap);
383 }
384
385 //if is under mouse draw the animated glow overlay
386 if (!nativeWidget()->isDown() && !nativeWidget()->isChecked() && isEnabled() && acceptHoverEvents() && d->background->hasElementPrefix("active")) {
387 if (d->hoverAnimation->state() == QAbstractAnimation::Running && !isUnderMouse() && !nativeWidget()->isDefault()) {
388 d->background->setElementPrefix("active");
389 d->background->paintFrame(painter, d->activeRect.topLeft());
390 } else {
391 painter->drawPixmap(
392 d->activeRect.topLeft(),
393 d->hoverAnimation->property("currentPixmap").value<QPixmap>());
394 }
395 } else if (isEnabled()) {
396 d->background->paintFrame(painter);
397 }
398
399
400 painter->setPen(Plasma::Theme::defaultTheme()->color(Theme::ButtonTextColor));
401
402 if (nativeWidget()->isDown()) {
403 painter->translate(QPoint(1, 1));
404 }
405
406 QRectF rect = contentsRect();
407
408 if (!nativeWidget()->icon().isNull()) {
409 const qreal iconSize = qMin(rect.width(), rect.height());
410 QPixmap iconPix = nativeWidget()->icon().pixmap(iconSize);
411 if (!isEnabled()) {
412 KIconEffect *effect = KIconLoader::global()->iconEffect();
413 iconPix = effect->apply(iconPix, KIconLoader::Toolbar, KIconLoader::DisabledState);
414 }
415
416 QRect pixmapRect;
417 if (nativeWidget()->text().isEmpty()) {
418 pixmapRect = nativeWidget()->style()->alignedRect(option->direction, Qt::AlignCenter, iconPix.size(), rect.toRect());
419 } else {
420 pixmapRect = nativeWidget()->style()->alignedRect(option->direction, Qt::AlignLeft|Qt::AlignVCenter, iconPix.size(), rect.toRect());
421 }
422 painter->drawPixmap(pixmapRect.topLeft(), iconPix);
423
424 if (option->direction == Qt::LeftToRight) {
425 rect.adjust(rect.height(), 0, 0, 0);
426 } else {
427 rect.adjust(0, 0, -rect.height(), 0);
428 }
429 }
430
431 QFontMetricsF fm(font());
432 // If the height is too small increase the Height of the button to shall the whole text #192988
433 if (rect.height() < fm.height()) {
434 rect.setHeight(fm.height());
435 rect.moveTop(boundingRect().center().y() - rect.height() / 2);
436 }
437
438 // If there is not enough room for the text make it to fade out
439 if (rect.width() < fm.width(nativeWidget()->text())) {
440 if (bufferPixmap.isNull()) {
441 bufferPixmap = QPixmap(rect.size().toSize());
442 }
443 bufferPixmap.fill(Qt::transparent);
444
445 QPainter p(&bufferPixmap);
446 p.setPen(painter->pen());
447 p.setFont(font());
448
449 // Create the alpha gradient for the fade out effect
450 QLinearGradient alphaGradient(0, 0, 1, 0);
451 alphaGradient.setCoordinateMode(QGradient::ObjectBoundingMode);
452 if (option->direction == Qt::LeftToRight) {
453 alphaGradient.setColorAt(0, QColor(0, 0, 0, 255));
454 alphaGradient.setColorAt(1, QColor(0, 0, 0, 0));
455 p.drawText(bufferPixmap.rect(), Qt::AlignLeft|Qt::AlignVCenter|Qt::TextShowMnemonic,
456 nativeWidget()->text());
457 } else {
458 alphaGradient.setColorAt(0, QColor(0, 0, 0, 0));
459 alphaGradient.setColorAt(1, QColor(0, 0, 0, 255));
460 p.drawText(bufferPixmap.rect(), Qt::AlignRight|Qt::AlignVCenter|Qt::TextShowMnemonic,
461 nativeWidget()->text());
462 }
463
464 p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
465 p.fillRect(bufferPixmap.rect(), alphaGradient);
466
467 painter->drawPixmap(rect.topLeft(), bufferPixmap);
468 } else {
469 painter->setFont(font());
470 painter->drawText(rect, Qt::AlignCenter|Qt::TextShowMnemonic, nativeWidget()->text());
471 }
472}
473
474void PushButton::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
475{
476 if (nativeWidget()->isDown() || d->background->hasElementPrefix("hover")) {
477 return;
478 }
479
480 d->hoverAnimation->setProperty("duration", 75);
481
482 d->background->setElementPrefix("normal");
483 d->hoverAnimation->setProperty("startPixmap", d->background->framePixmap());
484
485 d->background->setElementPrefix("active");
486 d->hoverAnimation->setProperty("targetPixmap", d->background->framePixmap());
487
488 d->hoverAnimation->start();
489
490 QGraphicsProxyWidget::hoverEnterEvent(event);
491}
492
493void PushButton::changeEvent(QEvent *event)
494{
495 d->changeEvent(event);
496 QGraphicsProxyWidget::changeEvent(event);
497}
498
499void PushButton::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
500{
501 if (nativeWidget()->isDown() || d->background->hasElementPrefix("hover")) {
502 return;
503 }
504
505 d->hoverAnimation->setProperty("duration", 150);
506
507 d->background->setElementPrefix("active");
508 d->hoverAnimation->setProperty("startPixmap", d->background->framePixmap());
509
510 d->background->setElementPrefix("normal");
511 d->hoverAnimation->setProperty("targetPixmap", d->background->framePixmap());
512
513 d->hoverAnimation->start();
514
515 QGraphicsProxyWidget::hoverLeaveEvent(event);
516}
517
518
519QSizeF PushButton::sizeHint(Qt::SizeHint which, const QSizeF & constraint) const
520{
521 QSizeF hint = QGraphicsProxyWidget::sizeHint(which, constraint);
522
523 if (hint.isEmpty()) {
524 return hint;
525 }
526
527 //replace the native margin with the Svg one
528 QStyleOption option;
529 option.initFrom(nativeWidget());
530 int nativeMargin = nativeWidget()->style()->pixelMetric(QStyle::PM_ButtonMargin, &option, nativeWidget());
531 qreal left, top, right, bottom;
532 d->background->getMargins(left, top, right, bottom);
533 hint = hint - QSize(nativeMargin, nativeMargin) + QSize(left+right, top+bottom);
534 return hint;
535}
536
537} // namespace Plasma
538
539#include <pushbutton.moc>
540
animation.h
animator.h
Plasma::Animator::create
static Plasma::Animation * create(Animator::Animation type, QObject *parent=0)
Factory to build new animation objects.
Definition: animator.cpp:61
Plasma::Animator::PixmapTransitionAnimation
@ PixmapTransitionAnimation
Definition: animator.h:68
Plasma::FrameSvg
Provides an SVG with borders.
Definition: framesvg.h:77
Plasma::PushButton::toggled
void toggled(bool)
Emitted when the button changes state from up to down.
Plasma::PushButton::styleSheet
QString styleSheet
Definition: pushbutton.h:48
Plasma::PushButton::icon
QIcon icon
Definition: pushbutton.h:51
Plasma::PushButton::checkable
bool checkable
Definition: pushbutton.h:52
Plasma::PushButton::isChecked
bool isChecked() const
Definition: pushbutton.cpp:329
Plasma::PushButton::isDown
bool isDown() const
Definition: pushbutton.cpp:334
Plasma::PushButton::nativeWidget
KPushButton * nativeWidget
Definition: pushbutton.h:49
Plasma::PushButton::checked
bool checked
Definition: pushbutton.h:53
Plasma::PushButton::clicked
void clicked()
Emitted when the button is pressed then released, completing a click.
Plasma::PushButton::setImage
void setImage(const QString &path)
Sets the path to an image to display.
Definition: pushbutton.cpp:234
Plasma::PushButton::setIcon
void setIcon(const QIcon &icon)
sets the icon for this push button
Definition: pushbutton.cpp:299
Plasma::PushButton::pressed
void pressed()
Emitted when the button is pressed down; usually the clicked() signal will suffice,...
Plasma::PushButton::released
void released()
Emitted when the button is released; usually the clicked() signal will suffice, however.
Plasma::PushButton::action
QAction * action
Definition: pushbutton.h:50
Plasma::PushButton::setText
void setText(const QString &text)
Sets the display text for this PushButton.
Definition: pushbutton.cpp:223
Plasma::PushButton::text
QString text
Definition: pushbutton.h:46
Plasma::Theme::imagePath
Q_INVOKABLE QString imagePath(const QString &name) const
Retrieve the path for an SVG image in the current theme.
Definition: theme.cpp:794
Plasma::Theme::defaultTheme
static Theme * defaultTheme()
Singleton pattern accessor.
Definition: theme.cpp:544
Plasma::Theme::ButtonTextColor
@ ButtonTextColor
Definition: theme.h:67
QGraphicsProxyWidget
QGraphicsWidget
QStyleOptionGraphicsItem
QWidget
framesvg.h
Plasma
Namespace for everything in libplasma.
Definition: abstractdialogmanager.cpp:25
paintutils.h
pushbutton.h
theme.h
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Feb 20 2023 00:00:00 by doxygen 1.9.6 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

Plasma

Skip menu "Plasma"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdelibs-4.14.38 API Reference

Skip menu "kdelibs-4.14.38 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal