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 syncFrame() 00115 { 00116 if (background) { 00117 //resize all panels 00118 background->setElementPrefix("pressed"); 00119 background->resizeFrame(q->size()); 00120 00121 syncActiveRect(); 00122 00123 background->setElementPrefix("normal"); 00124 background->resizeFrame(q->size()); 00125 hoverAnimation->setProperty("startPixmap", background->framePixmap()); 00126 00127 background->setElementPrefix("active"); 00128 background->resizeFrame(activeRect.size()); 00129 hoverAnimation->setProperty("targetPixmap", background->framePixmap()); 00130 } 00131 } 00132 00133 void syncActiveRect(); 00134 void syncBorders(); 00135 00136 FrameSvg *background; 00137 bool fadeIn; 00138 qreal opacity; 00139 QRectF activeRect; 00140 00141 Animation *hoverAnimation; 00142 00143 FocusIndicator *focusIndicator; 00144 QString imagePath; 00145 QString absImagePath; 00146 Svg *svg; 00147 QString svgElement; 00148 }; 00149 00150 void PushButtonPrivate::syncActiveRect() 00151 { 00152 background->setElementPrefix("normal"); 00153 00154 qreal left, top, right, bottom; 00155 background->getMargins(left, top, right, bottom); 00156 00157 background->setElementPrefix("active"); 00158 qreal activeLeft, activeTop, activeRight, activeBottom; 00159 background->getMargins(activeLeft, activeTop, activeRight, activeBottom); 00160 00161 activeRect = QRectF(QPointF(0, 0), q->size()); 00162 activeRect.adjust(left - activeLeft, top - activeTop, 00163 -(right - activeRight), -(bottom - activeBottom)); 00164 00165 background->setElementPrefix("normal"); 00166 } 00167 00168 void PushButtonPrivate::syncBorders() 00169 { 00170 //set margins from the normal element 00171 qreal left, top, right, bottom; 00172 00173 background->setElementPrefix("normal"); 00174 background->getMargins(left, top, right, bottom); 00175 q->setContentsMargins(left, top, right, bottom); 00176 00177 //calc the rect for the over effect 00178 syncActiveRect(); 00179 } 00180 00181 00182 PushButton::PushButton(QGraphicsWidget *parent) 00183 : QGraphicsProxyWidget(parent), 00184 d(new PushButtonPrivate(this)) 00185 { 00186 d->background = new FrameSvg(this); 00187 d->background->setImagePath("widgets/button"); 00188 d->background->setCacheAllRenderedFrames(true); 00189 00190 d->background->setElementPrefix("normal"); 00191 00192 d->hoverAnimation = Animator::create(Animator::PixmapTransitionAnimation); 00193 d->hoverAnimation->setTargetWidget(this); 00194 00195 KPushButton *native = new KPushButton; 00196 connect(native, SIGNAL(pressed()), this, SIGNAL(pressed())); 00197 connect(native, SIGNAL(pressed()), this, SLOT(pressedChanged())); 00198 connect(native, SIGNAL(released()), this, SIGNAL(released())); 00199 connect(native, SIGNAL(released()), this, SLOT(pressedChanged())); 00200 connect(native, SIGNAL(clicked()), this, SIGNAL(clicked())); 00201 connect(native, SIGNAL(toggled(bool)), this, SIGNAL(toggled(bool))); 00202 setWidget(native); 00203 native->setAttribute(Qt::WA_NoSystemBackground); 00204 native->setWindowIcon(QIcon()); 00205 00206 setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); 00207 00208 d->focusIndicator = new FocusIndicator(this, d->background); 00209 00210 d->syncBorders(); 00211 setAcceptHoverEvents(true); 00212 00213 connect(d->background, SIGNAL(repaintNeeded()), SLOT(syncBorders())); 00214 d->initTheming(); 00215 d->syncFrame(); 00216 } 00217 00218 PushButton::~PushButton() 00219 { 00220 delete d; 00221 } 00222 00223 void PushButton::setText(const QString &text) 00224 { 00225 static_cast<KPushButton*>(widget())->setText(text); 00226 updateGeometry(); 00227 } 00228 00229 QString PushButton::text() const 00230 { 00231 return static_cast<KPushButton*>(widget())->text(); 00232 } 00233 00234 void PushButton::setImage(const QString &path) 00235 { 00236 if (d->imagePath == path) { 00237 return; 00238 } 00239 00240 delete d->svg; 00241 d->svg = 0; 00242 d->imagePath = path; 00243 00244 bool absolutePath = !path.isEmpty() && 00245 #ifdef Q_WS_WIN 00246 !QDir::isRelativePath(path) 00247 #else 00248 (path[0] == '/' || path.startsWith(QLatin1String(":/"))) 00249 #endif 00250 ; 00251 00252 if (absolutePath) { 00253 d->absImagePath = path; 00254 } else { 00255 //TODO: package support 00256 d->absImagePath = Theme::defaultTheme()->imagePath(path); 00257 } 00258 00259 d->setPixmap(); 00260 } 00261 00262 void PushButton::setImage(const QString &path, const QString &elementid) 00263 { 00264 d->svgElement = elementid; 00265 setImage(path); 00266 } 00267 00268 QString PushButton::image() const 00269 { 00270 return d->imagePath; 00271 } 00272 00273 void PushButton::setStyleSheet(const QString &stylesheet) 00274 { 00275 d->focusIndicator->setVisible(stylesheet.isEmpty()); 00276 widget()->setStyleSheet(stylesheet); 00277 } 00278 00279 QString PushButton::styleSheet() 00280 { 00281 return widget()->styleSheet(); 00282 } 00283 00284 void PushButton::setAction(QAction *action) 00285 { 00286 d->setAction(action); 00287 } 00288 00289 QAction *PushButton::action() const 00290 { 00291 return d->action; 00292 } 00293 00294 void PushButton::setIcon(const KIcon &icon) 00295 { 00296 nativeWidget()->setIcon(icon); 00297 } 00298 00299 void PushButton::setIcon(const QIcon &icon) 00300 { 00301 setIcon(KIcon(icon)); 00302 } 00303 00304 QIcon PushButton::icon() const 00305 { 00306 return nativeWidget()->icon(); 00307 } 00308 00309 void PushButton::setCheckable(bool checkable) 00310 { 00311 nativeWidget()->setCheckable(checkable); 00312 } 00313 00314 bool PushButton::isCheckable() const 00315 { 00316 return nativeWidget()->isCheckable(); 00317 } 00318 00319 void PushButton::setChecked(bool checked) 00320 { 00321 nativeWidget()->setChecked(checked); 00322 } 00323 00324 void PushButton::click() 00325 { 00326 nativeWidget()->animateClick(); 00327 } 00328 00329 bool PushButton::isChecked() const 00330 { 00331 return nativeWidget()->isChecked(); 00332 } 00333 00334 bool PushButton::isDown() const 00335 { 00336 return nativeWidget()->isDown(); 00337 } 00338 00339 KPushButton *PushButton::nativeWidget() const 00340 { 00341 return static_cast<KPushButton*>(widget()); 00342 } 00343 00344 void PushButton::resizeEvent(QGraphicsSceneResizeEvent *event) 00345 { 00346 d->setPixmap(); 00347 00348 d->syncFrame(); 00349 00350 QGraphicsProxyWidget::resizeEvent(event); 00351 } 00352 00353 void PushButton::paint(QPainter *painter, 00354 const QStyleOptionGraphicsItem *option, 00355 QWidget *widget) 00356 { 00357 if (!styleSheet().isNull() || Theme::defaultTheme()->useNativeWidgetStyle()) { 00358 QGraphicsProxyWidget::paint(painter, option, widget); 00359 return; 00360 } 00361 00362 QPixmap bufferPixmap; 00363 00364 //Normal button, pressed or not 00365 if (isEnabled()) { 00366 if (nativeWidget()->isDown() || nativeWidget()->isChecked()) { 00367 d->background->setElementPrefix("pressed"); 00368 } else { 00369 d->background->setElementPrefix("normal"); 00370 } 00371 00372 //flat or disabled 00373 } else if (!isEnabled() || nativeWidget()->isFlat()) { 00374 bufferPixmap = QPixmap(rect().size().toSize()); 00375 bufferPixmap.fill(Qt::transparent); 00376 00377 QPainter buffPainter(&bufferPixmap); 00378 d->background->paintFrame(&buffPainter); 00379 buffPainter.setCompositionMode(QPainter::CompositionMode_DestinationIn); 00380 buffPainter.fillRect(bufferPixmap.rect(), QColor(0, 0, 0, 128)); 00381 00382 painter->drawPixmap(0, 0, bufferPixmap); 00383 } 00384 00385 //if is under mouse draw the animated glow overlay 00386 if (!nativeWidget()->isDown() && !nativeWidget()->isChecked() && isEnabled() && acceptHoverEvents() && d->background->hasElementPrefix("active")) { 00387 if (d->hoverAnimation->state() == QAbstractAnimation::Running && !isUnderMouse() && !nativeWidget()->isDefault()) { 00388 d->background->setElementPrefix("active"); 00389 d->background->paintFrame(painter, d->activeRect.topLeft()); 00390 } else { 00391 painter->drawPixmap( 00392 d->activeRect.topLeft(), 00393 d->hoverAnimation->property("currentPixmap").value<QPixmap>()); 00394 } 00395 } else if (isEnabled()) { 00396 d->background->paintFrame(painter); 00397 } 00398 00399 00400 painter->setPen(Plasma::Theme::defaultTheme()->color(Theme::ButtonTextColor)); 00401 00402 if (nativeWidget()->isDown()) { 00403 painter->translate(QPoint(1, 1)); 00404 } 00405 00406 QRectF rect = contentsRect(); 00407 00408 if (!nativeWidget()->icon().isNull()) { 00409 const qreal iconSize = qMin(rect.width(), rect.height()); 00410 QPixmap iconPix = nativeWidget()->icon().pixmap(iconSize); 00411 if (!isEnabled()) { 00412 KIconEffect *effect = KIconLoader::global()->iconEffect(); 00413 iconPix = effect->apply(iconPix, KIconLoader::Toolbar, KIconLoader::DisabledState); 00414 } 00415 00416 QRect pixmapRect; 00417 if (nativeWidget()->text().isEmpty()) { 00418 pixmapRect = nativeWidget()->style()->alignedRect(option->direction, Qt::AlignCenter, iconPix.size(), rect.toRect()); 00419 } else { 00420 pixmapRect = nativeWidget()->style()->alignedRect(option->direction, Qt::AlignLeft|Qt::AlignVCenter, iconPix.size(), rect.toRect()); 00421 } 00422 painter->drawPixmap(pixmapRect.topLeft(), iconPix); 00423 00424 if (option->direction == Qt::LeftToRight) { 00425 rect.adjust(rect.height(), 0, 0, 0); 00426 } else { 00427 rect.adjust(0, 0, -rect.height(), 0); 00428 } 00429 } 00430 00431 QFontMetricsF fm(font()); 00432 // If the height is too small increase the Height of the button to shall the whole text #192988 00433 if (rect.height() < fm.height()) { 00434 rect.setHeight(fm.height()); 00435 rect.moveTop(boundingRect().center().y() - rect.height() / 2); 00436 } 00437 00438 // If there is not enough room for the text make it to fade out 00439 if (rect.width() < fm.width(nativeWidget()->text())) { 00440 if (bufferPixmap.isNull()) { 00441 bufferPixmap = QPixmap(rect.size().toSize()); 00442 } 00443 bufferPixmap.fill(Qt::transparent); 00444 00445 QPainter p(&bufferPixmap); 00446 p.setPen(painter->pen()); 00447 p.setFont(font()); 00448 00449 // Create the alpha gradient for the fade out effect 00450 QLinearGradient alphaGradient(0, 0, 1, 0); 00451 alphaGradient.setCoordinateMode(QGradient::ObjectBoundingMode); 00452 if (option->direction == Qt::LeftToRight) { 00453 alphaGradient.setColorAt(0, QColor(0, 0, 0, 255)); 00454 alphaGradient.setColorAt(1, QColor(0, 0, 0, 0)); 00455 p.drawText(bufferPixmap.rect(), Qt::AlignLeft|Qt::AlignVCenter|Qt::TextShowMnemonic, 00456 nativeWidget()->text()); 00457 } else { 00458 alphaGradient.setColorAt(0, QColor(0, 0, 0, 0)); 00459 alphaGradient.setColorAt(1, QColor(0, 0, 0, 255)); 00460 p.drawText(bufferPixmap.rect(), Qt::AlignRight|Qt::AlignVCenter|Qt::TextShowMnemonic, 00461 nativeWidget()->text()); 00462 } 00463 00464 p.setCompositionMode(QPainter::CompositionMode_DestinationIn); 00465 p.fillRect(bufferPixmap.rect(), alphaGradient); 00466 00467 painter->drawPixmap(rect.topLeft(), bufferPixmap); 00468 } else { 00469 painter->setFont(font()); 00470 painter->drawText(rect, Qt::AlignCenter|Qt::TextShowMnemonic, nativeWidget()->text()); 00471 } 00472 } 00473 00474 void PushButton::hoverEnterEvent(QGraphicsSceneHoverEvent *event) 00475 { 00476 if (nativeWidget()->isDown() || d->background->hasElementPrefix("hover")) { 00477 return; 00478 } 00479 00480 d->hoverAnimation->setProperty("duration", 75); 00481 00482 d->background->setElementPrefix("normal"); 00483 d->hoverAnimation->setProperty("startPixmap", d->background->framePixmap()); 00484 00485 d->background->setElementPrefix("active"); 00486 d->hoverAnimation->setProperty("targetPixmap", d->background->framePixmap()); 00487 00488 d->hoverAnimation->start(); 00489 00490 QGraphicsProxyWidget::hoverEnterEvent(event); 00491 } 00492 00493 void PushButton::changeEvent(QEvent *event) 00494 { 00495 d->changeEvent(event); 00496 QGraphicsProxyWidget::changeEvent(event); 00497 } 00498 00499 void PushButton::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) 00500 { 00501 if (nativeWidget()->isDown() || d->background->hasElementPrefix("hover")) { 00502 return; 00503 } 00504 00505 d->hoverAnimation->setProperty("duration", 150); 00506 00507 d->background->setElementPrefix("active"); 00508 d->hoverAnimation->setProperty("startPixmap", d->background->framePixmap()); 00509 00510 d->background->setElementPrefix("normal"); 00511 d->hoverAnimation->setProperty("targetPixmap", d->background->framePixmap()); 00512 00513 d->hoverAnimation->start(); 00514 00515 QGraphicsProxyWidget::hoverLeaveEvent(event); 00516 } 00517 00518 00519 QSizeF PushButton::sizeHint(Qt::SizeHint which, const QSizeF & constraint) const 00520 { 00521 QSizeF hint = QGraphicsProxyWidget::sizeHint(which, constraint); 00522 00523 if (hint.isEmpty()) { 00524 return hint; 00525 } 00526 00527 //replace the native margin with the Svg one 00528 QStyleOption option; 00529 option.initFrom(nativeWidget()); 00530 int nativeMargin = nativeWidget()->style()->pixelMetric(QStyle::PM_ButtonMargin, &option, nativeWidget()); 00531 qreal left, top, right, bottom; 00532 d->background->getMargins(left, top, right, bottom); 00533 hint = hint - QSize(nativeMargin, nativeMargin) + QSize(left+right, top+bottom); 00534 return hint; 00535 } 00536 00537 } // namespace Plasma 00538 00539 #include <pushbutton.moc> 00540
KDE 4.7 API Reference