Plasma
pushbutton.cpp
Go to the documentation of this file.
00001 /* 00002 * Copyright 2008 Aaron Seigo <aseigo@kde.org> 00003 * Copyright 2008 Marco Martin <notmart@gmail.com> 00004 * 00005 * This program is free software; you can redistribute it and/or modify 00006 * it under the terms of the GNU Library General Public License as 00007 * published by the Free Software Foundation; either version 2, or 00008 * (at your option) any later version. 00009 * 00010 * This program is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 * GNU General Public License for more details 00014 * 00015 * You should have received a copy of the GNU Library General Public 00016 * License along with this program; if not, write to the 00017 * Free Software Foundation, Inc., 00018 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00019 */ 00020 00021 #include "pushbutton.h" 00022 00023 #include <QDir> 00024 #include <QPainter> 00025 #include <QStyleOptionGraphicsItem> 00026 #include <QWeakPointer> 00027 00028 #include <kicon.h> 00029 #include <kiconeffect.h> 00030 #include <kmimetype.h> 00031 #include <kpushbutton.h> 00032 00033 #include "animator.h" 00034 #include "animations/animation.h" 00035 #include "framesvg.h" 00036 #include "paintutils.h" 00037 #include "private/actionwidgetinterface_p.h" 00038 #include "private/focusindicator_p.h" 00039 #include "private/themedwidgetinterface_p.h" 00040 #include "theme.h" 00041 00042 namespace Plasma 00043 { 00044 00045 class PushButtonPrivate : public ActionWidgetInterface<PushButton> 00046 { 00047 public: 00048 PushButtonPrivate(PushButton *pushButton) 00049 : ActionWidgetInterface<PushButton>(pushButton), 00050 background(0), 00051 fadeIn(false), 00052 svg(0) 00053 { 00054 } 00055 00056 ~PushButtonPrivate() 00057 { 00058 delete svg; 00059 } 00060 00061 void setPixmap() 00062 { 00063 if (imagePath.isEmpty()) { 00064 delete svg; 00065 svg = 0; 00066 return; 00067 } 00068 00069 KMimeType::Ptr mime = KMimeType::findByPath(absImagePath); 00070 QPixmap pm; 00071 00072 if (mime->is("image/svg+xml") || mime->is("image/svg+xml-compressed")) { 00073 if (!svg || svg->imagePath() != absImagePath) { 00074 delete svg; 00075 svg = new Svg(); 00076 svg->setImagePath(imagePath); 00077 QObject::connect(svg, SIGNAL(repaintNeeded()), q, SLOT(setPixmap())); 00078 if (!svgElement.isNull()) { 00079 svg->setContainsMultipleImages(true); 00080 } 00081 } 00082 00083 //QPainter p(&pm); 00084 00085 if (!svgElement.isEmpty() && svg->hasElement(svgElement)) { 00086 svg->resize(); 00087 QSizeF elementSize = svg->elementSize(svgElement); 00088 float scale = q->nativeWidget()->iconSize().width() / qMax(elementSize.width(), elementSize.height()); 00089 00090 svg->resize(elementSize * scale); 00091 pm = svg->pixmap(svgElement); 00092 } else { 00093 svg->resize(q->nativeWidget()->iconSize()); 00094 pm = svg->pixmap(); 00095 } 00096 } else { 00097 delete svg; 00098 svg = 0; 00099 pm = QPixmap(absImagePath); 00100 } 00101 00102 static_cast<KPushButton*>(q->widget())->setIcon(KIcon(pm)); 00103 } 00104 00105 void pressedChanged() 00106 { 00107 if (q->nativeWidget()->isDown() || q->nativeWidget()->isChecked()) { 00108 focusIndicator->animateVisibility(false); 00109 } else { 00110 focusIndicator->animateVisibility(true); 00111 } 00112 } 00113 00114 void syncActiveRect(); 00115 void syncBorders(); 00116 00117 FrameSvg *background; 00118 bool fadeIn; 00119 qreal opacity; 00120 QRectF activeRect; 00121 00122 Animation *hoverAnimation; 00123 00124 FocusIndicator *focusIndicator; 00125 QString imagePath; 00126 QString absImagePath; 00127 Svg *svg; 00128 QString svgElement; 00129 }; 00130 00131 void PushButtonPrivate::syncActiveRect() 00132 { 00133 background->setElementPrefix("normal"); 00134 00135 qreal left, top, right, bottom; 00136 background->getMargins(left, top, right, bottom); 00137 00138 background->setElementPrefix("active"); 00139 qreal activeLeft, activeTop, activeRight, activeBottom; 00140 background->getMargins(activeLeft, activeTop, activeRight, activeBottom); 00141 00142 activeRect = QRectF(QPointF(0, 0), q->size()); 00143 activeRect.adjust(left - activeLeft, top - activeTop, 00144 -(right - activeRight), -(bottom - activeBottom)); 00145 00146 background->setElementPrefix("normal"); 00147 } 00148 00149 void PushButtonPrivate::syncBorders() 00150 { 00151 //set margins from the normal element 00152 qreal left, top, right, bottom; 00153 00154 background->setElementPrefix("normal"); 00155 background->getMargins(left, top, right, bottom); 00156 q->setContentsMargins(left, top, right, bottom); 00157 00158 //calc the rect for the over effect 00159 syncActiveRect(); 00160 } 00161 00162 00163 PushButton::PushButton(QGraphicsWidget *parent) 00164 : QGraphicsProxyWidget(parent), 00165 d(new PushButtonPrivate(this)) 00166 { 00167 d->background = new FrameSvg(this); 00168 d->background->setImagePath("widgets/button"); 00169 d->background->setCacheAllRenderedFrames(true); 00170 00171 d->background->setElementPrefix("normal"); 00172 00173 d->hoverAnimation = Animator::create(Animator::PixmapTransitionAnimation); 00174 d->hoverAnimation->setTargetWidget(this); 00175 00176 KPushButton *native = new KPushButton; 00177 connect(native, SIGNAL(pressed()), this, SIGNAL(pressed())); 00178 connect(native, SIGNAL(pressed()), this, SLOT(pressedChanged())); 00179 connect(native, SIGNAL(released()), this, SIGNAL(released())); 00180 connect(native, SIGNAL(released()), this, SLOT(pressedChanged())); 00181 connect(native, SIGNAL(clicked()), this, SIGNAL(clicked())); 00182 connect(native, SIGNAL(toggled(bool)), this, SIGNAL(toggled(bool))); 00183 setWidget(native); 00184 native->setAttribute(Qt::WA_NoSystemBackground); 00185 native->setWindowIcon(QIcon()); 00186 00187 setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); 00188 00189 d->focusIndicator = new FocusIndicator(this, d->background); 00190 00191 d->syncBorders(); 00192 setAcceptHoverEvents(true); 00193 00194 connect(d->background, SIGNAL(repaintNeeded()), SLOT(syncBorders())); 00195 d->initTheming(); 00196 } 00197 00198 PushButton::~PushButton() 00199 { 00200 delete d; 00201 } 00202 00203 void PushButton::setText(const QString &text) 00204 { 00205 static_cast<KPushButton*>(widget())->setText(text); 00206 updateGeometry(); 00207 } 00208 00209 QString PushButton::text() const 00210 { 00211 return static_cast<KPushButton*>(widget())->text(); 00212 } 00213 00214 void PushButton::setImage(const QString &path) 00215 { 00216 if (d->imagePath == path) { 00217 return; 00218 } 00219 00220 delete d->svg; 00221 d->svg = 0; 00222 d->imagePath = path; 00223 00224 bool absolutePath = !path.isEmpty() && 00225 #ifdef Q_WS_WIN 00226 !QDir::isRelativePath(path) 00227 #else 00228 (path[0] == '/' || path.startsWith(QLatin1String(":/"))) 00229 #endif 00230 ; 00231 00232 if (absolutePath) { 00233 d->absImagePath = path; 00234 } else { 00235 //TODO: package support 00236 d->absImagePath = Theme::defaultTheme()->imagePath(path); 00237 } 00238 00239 d->setPixmap(); 00240 } 00241 00242 void PushButton::setImage(const QString &path, const QString &elementid) 00243 { 00244 d->svgElement = elementid; 00245 setImage(path); 00246 } 00247 00248 QString PushButton::image() const 00249 { 00250 return d->imagePath; 00251 } 00252 00253 void PushButton::setStyleSheet(const QString &stylesheet) 00254 { 00255 d->focusIndicator->setVisible(stylesheet.isEmpty()); 00256 widget()->setStyleSheet(stylesheet); 00257 } 00258 00259 QString PushButton::styleSheet() 00260 { 00261 return widget()->styleSheet(); 00262 } 00263 00264 void PushButton::setAction(QAction *action) 00265 { 00266 d->setAction(action); 00267 } 00268 00269 QAction *PushButton::action() const 00270 { 00271 return d->action; 00272 } 00273 00274 void PushButton::setIcon(const KIcon &icon) 00275 { 00276 nativeWidget()->setIcon(icon); 00277 } 00278 00279 void PushButton::setIcon(const QIcon &icon) 00280 { 00281 setIcon(KIcon(icon)); 00282 } 00283 00284 QIcon PushButton::icon() const 00285 { 00286 return nativeWidget()->icon(); 00287 } 00288 00289 void PushButton::setCheckable(bool checkable) 00290 { 00291 nativeWidget()->setCheckable(checkable); 00292 } 00293 00294 bool PushButton::isCheckable() const 00295 { 00296 return nativeWidget()->isCheckable(); 00297 } 00298 00299 void PushButton::setChecked(bool checked) 00300 { 00301 nativeWidget()->setChecked(checked); 00302 } 00303 00304 void PushButton::click() 00305 { 00306 nativeWidget()->animateClick(); 00307 } 00308 00309 bool PushButton::isChecked() const 00310 { 00311 return nativeWidget()->isChecked(); 00312 } 00313 00314 bool PushButton::isDown() const 00315 { 00316 return nativeWidget()->isDown(); 00317 } 00318 00319 KPushButton *PushButton::nativeWidget() const 00320 { 00321 return static_cast<KPushButton*>(widget()); 00322 } 00323 00324 void PushButton::resizeEvent(QGraphicsSceneResizeEvent *event) 00325 { 00326 d->setPixmap(); 00327 00328 if (d->background) { 00329 //resize all panels 00330 d->background->setElementPrefix("pressed"); 00331 d->background->resizeFrame(size()); 00332 00333 d->syncActiveRect(); 00334 00335 d->background->setElementPrefix("normal"); 00336 d->background->resizeFrame(size()); 00337 d->hoverAnimation->setProperty("startPixmap", d->background->framePixmap()); 00338 00339 d->background->setElementPrefix("active"); 00340 d->background->resizeFrame(d->activeRect.size()); 00341 d->hoverAnimation->setProperty("targetPixmap", d->background->framePixmap()); 00342 } 00343 00344 QGraphicsProxyWidget::resizeEvent(event); 00345 } 00346 00347 void PushButton::paint(QPainter *painter, 00348 const QStyleOptionGraphicsItem *option, 00349 QWidget *widget) 00350 { 00351 if (!styleSheet().isNull() || Theme::defaultTheme()->useNativeWidgetStyle()) { 00352 QGraphicsProxyWidget::paint(painter, option, widget); 00353 return; 00354 } 00355 00356 QPixmap bufferPixmap; 00357 00358 //Normal button, pressed or not 00359 if (isEnabled()) { 00360 if (nativeWidget()->isDown() || nativeWidget()->isChecked()) { 00361 d->background->setElementPrefix("pressed"); 00362 } else { 00363 d->background->setElementPrefix("normal"); 00364 } 00365 00366 //flat or disabled 00367 } else if (!isEnabled() || nativeWidget()->isFlat()) { 00368 bufferPixmap = QPixmap(rect().size().toSize()); 00369 bufferPixmap.fill(Qt::transparent); 00370 00371 QPainter buffPainter(&bufferPixmap); 00372 d->background->paintFrame(&buffPainter); 00373 buffPainter.setCompositionMode(QPainter::CompositionMode_DestinationIn); 00374 buffPainter.fillRect(bufferPixmap.rect(), QColor(0, 0, 0, 128)); 00375 00376 painter->drawPixmap(0, 0, bufferPixmap); 00377 } 00378 00379 //if is under mouse draw the animated glow overlay 00380 if (!nativeWidget()->isDown() && !nativeWidget()->isChecked() && isEnabled() && acceptHoverEvents() && d->background->hasElementPrefix("active")) { 00381 if (d->hoverAnimation->state() == QAbstractAnimation::Running && !isUnderMouse() && !nativeWidget()->isDefault()) { 00382 d->background->setElementPrefix("active"); 00383 d->background->paintFrame(painter, d->activeRect.topLeft()); 00384 } else { 00385 painter->drawPixmap( 00386 d->activeRect.topLeft(), 00387 d->hoverAnimation->property("currentPixmap").value<QPixmap>()); 00388 } 00389 } else if (isEnabled()) { 00390 d->background->paintFrame(painter); 00391 } 00392 00393 00394 painter->setPen(Plasma::Theme::defaultTheme()->color(Theme::ButtonTextColor)); 00395 00396 if (nativeWidget()->isDown()) { 00397 painter->translate(QPoint(1, 1)); 00398 } 00399 00400 QRectF rect = contentsRect(); 00401 00402 if (!nativeWidget()->icon().isNull()) { 00403 const qreal iconSize = qMin(rect.width(), rect.height()); 00404 QPixmap iconPix = nativeWidget()->icon().pixmap(iconSize); 00405 if (!isEnabled()) { 00406 KIconEffect *effect = KIconLoader::global()->iconEffect(); 00407 iconPix = effect->apply(iconPix, KIconLoader::Toolbar, KIconLoader::DisabledState); 00408 } 00409 00410 QRect pixmapRect; 00411 if (nativeWidget()->text().isEmpty()) { 00412 pixmapRect = nativeWidget()->style()->alignedRect(option->direction, Qt::AlignCenter, iconPix.size(), rect.toRect()); 00413 } else { 00414 pixmapRect = nativeWidget()->style()->alignedRect(option->direction, Qt::AlignLeft|Qt::AlignVCenter, iconPix.size(), rect.toRect()); 00415 } 00416 painter->drawPixmap(pixmapRect.topLeft(), iconPix); 00417 00418 if (option->direction == Qt::LeftToRight) { 00419 rect.adjust(rect.height(), 0, 0, 0); 00420 } else { 00421 rect.adjust(0, 0, -rect.height(), 0); 00422 } 00423 } 00424 00425 QFontMetricsF fm(font()); 00426 // If the height is too small increase the Height of the button to shall the whole text #192988 00427 if (rect.height() < fm.height()) { 00428 rect.setHeight(fm.height()); 00429 rect.moveTop(boundingRect().center().y() - rect.height() / 2); 00430 } 00431 00432 // If there is not enough room for the text make it to fade out 00433 if (rect.width() < fm.width(nativeWidget()->text())) { 00434 if (bufferPixmap.isNull()) { 00435 bufferPixmap = QPixmap(rect.size().toSize()); 00436 } 00437 bufferPixmap.fill(Qt::transparent); 00438 00439 QPainter p(&bufferPixmap); 00440 p.setPen(painter->pen()); 00441 p.setFont(font()); 00442 00443 // Create the alpha gradient for the fade out effect 00444 QLinearGradient alphaGradient(0, 0, 1, 0); 00445 alphaGradient.setCoordinateMode(QGradient::ObjectBoundingMode); 00446 if (option->direction == Qt::LeftToRight) { 00447 alphaGradient.setColorAt(0, QColor(0, 0, 0, 255)); 00448 alphaGradient.setColorAt(1, QColor(0, 0, 0, 0)); 00449 p.drawText(bufferPixmap.rect(), Qt::AlignLeft|Qt::AlignVCenter|Qt::TextShowMnemonic, 00450 nativeWidget()->text()); 00451 } else { 00452 alphaGradient.setColorAt(0, QColor(0, 0, 0, 0)); 00453 alphaGradient.setColorAt(1, QColor(0, 0, 0, 255)); 00454 p.drawText(bufferPixmap.rect(), Qt::AlignRight|Qt::AlignVCenter|Qt::TextShowMnemonic, 00455 nativeWidget()->text()); 00456 } 00457 00458 p.setCompositionMode(QPainter::CompositionMode_DestinationIn); 00459 p.fillRect(bufferPixmap.rect(), alphaGradient); 00460 00461 painter->drawPixmap(rect.topLeft(), bufferPixmap); 00462 } else { 00463 painter->setFont(font()); 00464 painter->drawText(rect, Qt::AlignCenter|Qt::TextShowMnemonic, nativeWidget()->text()); 00465 } 00466 } 00467 00468 void PushButton::hoverEnterEvent(QGraphicsSceneHoverEvent *event) 00469 { 00470 if (nativeWidget()->isDown() || d->background->hasElementPrefix("hover")) { 00471 return; 00472 } 00473 00474 d->hoverAnimation->setProperty("duration", 75); 00475 00476 d->background->setElementPrefix("normal"); 00477 d->hoverAnimation->setProperty("startPixmap", d->background->framePixmap()); 00478 00479 d->background->setElementPrefix("active"); 00480 d->hoverAnimation->setProperty("targetPixmap", d->background->framePixmap()); 00481 00482 d->hoverAnimation->start(); 00483 00484 QGraphicsProxyWidget::hoverEnterEvent(event); 00485 } 00486 00487 void PushButton::changeEvent(QEvent *event) 00488 { 00489 d->changeEvent(event); 00490 QGraphicsProxyWidget::changeEvent(event); 00491 } 00492 00493 void PushButton::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) 00494 { 00495 if (nativeWidget()->isDown() || d->background->hasElementPrefix("hover")) { 00496 return; 00497 } 00498 00499 d->hoverAnimation->setProperty("duration", 150); 00500 00501 d->background->setElementPrefix("active"); 00502 d->hoverAnimation->setProperty("startPixmap", d->background->framePixmap()); 00503 00504 d->background->setElementPrefix("normal"); 00505 d->hoverAnimation->setProperty("targetPixmap", d->background->framePixmap()); 00506 00507 d->hoverAnimation->start(); 00508 00509 QGraphicsProxyWidget::hoverLeaveEvent(event); 00510 } 00511 00512 00513 QSizeF PushButton::sizeHint(Qt::SizeHint which, const QSizeF & constraint) const 00514 { 00515 QSizeF hint = QGraphicsProxyWidget::sizeHint(which, constraint); 00516 00517 if (hint.isEmpty()) { 00518 return hint; 00519 } 00520 00521 //replace the native margin with the Svg one 00522 QStyleOption option; 00523 option.initFrom(nativeWidget()); 00524 int nativeMargin = nativeWidget()->style()->pixelMetric(QStyle::PM_ButtonMargin, &option, nativeWidget()); 00525 qreal left, top, right, bottom; 00526 d->background->getMargins(left, top, right, bottom); 00527 hint = hint - QSize(nativeMargin, nativeMargin) + QSize(left+right, top+bottom); 00528 return hint; 00529 } 00530 00531 } // namespace Plasma 00532 00533 #include <pushbutton.moc> 00534
KDE 4.6 API Reference