add more spacing
[personal-kdebase.git] / workspace / kwin / clients / plastik / plastikbutton.cpp
blobf0e016670521b62cb432194fb70666dafd7e64b4
1 /* Plastik KWin window decoration
2 Copyright (C) 2003-2005 Sandro Giessl <sandro@giessl.com>
4 based on the window decoration "Web":
5 Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org>
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public
9 License as published by the Free Software Foundation; either
10 version 2 of the License, or (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; see the file COPYING. If not, write to
19 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 Boston, MA 02110-1301, USA.
23 #include "plastikbutton.h"
25 // #include <kwin/options.h>
27 #include <QAbstractButton>
28 #include <QStyle>
29 #include <QBitmap>
30 #include <QPainter>
31 #include <QPixmap>
32 #include <QTimer>
34 #include "plastikbutton.moc"
35 #include "plastikclient.h"
37 #include <KGlobalSettings>
38 #include <KColorScheme>
39 #include <KColorUtils>
41 namespace KWinPlastik
44 static const uint TIMERINTERVAL = 50; // msec
45 static const uint ANIMATIONSTEPS = 4;
47 PlastikButton::PlastikButton(ButtonType type, PlastikClient *parent)
48 : KCommonDecorationButton(type, parent),
49 m_client(parent),
50 m_iconType(NumButtonIcons),
51 hover(false)
53 setAttribute(Qt::WA_NoSystemBackground);
55 // no need to reset here as the button will be reseted on first resize.
57 animTmr = new QTimer(this);
58 animTmr->setSingleShot(true); // single-shot
59 connect(animTmr, SIGNAL(timeout() ), this, SLOT(animate() ) );
60 animProgress = 0;
63 PlastikButton::~PlastikButton()
67 void PlastikButton::reset(unsigned long changed)
69 if (changed&DecorationReset || changed&ManualReset || changed&SizeChange || changed&StateChange) {
70 switch (type() ) {
71 case CloseButton:
72 m_iconType = CloseIcon;
73 break;
74 case HelpButton:
75 m_iconType = HelpIcon;
76 break;
77 case MinButton:
78 m_iconType = MinIcon;
79 break;
80 case MaxButton:
81 if (isChecked()) {
82 m_iconType = MaxRestoreIcon;
83 } else {
84 m_iconType = MaxIcon;
86 break;
87 case OnAllDesktopsButton:
88 if (isChecked()) {
89 m_iconType = NotOnAllDesktopsIcon;
90 } else {
91 m_iconType = OnAllDesktopsIcon;
93 break;
94 case ShadeButton:
95 if (isChecked()) {
96 m_iconType = UnShadeIcon;
97 } else {
98 m_iconType = ShadeIcon;
100 break;
101 case AboveButton:
102 if (isChecked()) {
103 m_iconType = NoKeepAboveIcon;
104 } else {
105 m_iconType = KeepAboveIcon;
107 break;
108 case BelowButton:
109 if (isChecked()) {
110 m_iconType = NoKeepBelowIcon;
111 } else {
112 m_iconType = KeepBelowIcon;
114 break;
115 default:
116 m_iconType = NumButtonIcons; // empty...
117 break;
120 this->update();
124 void PlastikButton::animate()
126 animTmr->stop();
128 if(hover) {
129 if(animProgress < ANIMATIONSTEPS) {
130 if (Handler()->animateButtons() ) {
131 animProgress++;
132 } else {
133 animProgress = ANIMATIONSTEPS;
135 animTmr->start(TIMERINTERVAL); // single-shot timer
137 } else {
138 if(animProgress > 0) {
139 if (Handler()->animateButtons() ) {
140 animProgress--;
141 } else {
142 animProgress = 0;
144 animTmr->start(TIMERINTERVAL); // single-shot timer
148 repaint();
151 void PlastikButton::enterEvent(QEvent *e)
153 QAbstractButton::enterEvent(e);
155 hover = true;
156 animate();
159 void PlastikButton::leaveEvent(QEvent *e)
161 QAbstractButton::leaveEvent(e);
163 hover = false;
164 animate();
167 void PlastikButton::paintEvent(QPaintEvent *)
169 QPainter p(this);
170 drawButton(&p);
173 void PlastikButton::drawButton(QPainter *painter)
175 QRect r(0,0,width(),height());
177 bool active = m_client->isActive();
178 QPixmap tempPixmap;
181 double c = KGlobalSettings::contrastF();
182 QColor titleBar = KDecoration::options()->color(KDecoration::ColorTitleBar, active);
183 QColor contourTop = KColorScheme::shade(titleBar, KColorScheme::DarkShade, c - 0.4);
184 QColor contourBottom = KColorScheme::shade(titleBar, KColorScheme::MidShade, c);
185 QColor surfaceTop = KColorScheme::shade(titleBar, KColorScheme::MidlightShade, c - 0.4);
186 QColor surfaceBottom = KColorScheme::shade(titleBar, KColorScheme::LightShade, c - 0.4);
188 QColor highlightColor = titleBar;
189 double alpha = 0;
190 if (type() == CloseButton) {
191 KColorScheme kcs(active ? QPalette::Active : QPalette::Inactive, KColorScheme::Button);
192 highlightColor = kcs.foreground(KColorScheme::NegativeText).color();
194 if (isDown()) {
195 highlightColor = KColorScheme::shade(highlightColor, KColorScheme::ShadowShade);
196 alpha = 0.3;
198 else if (animProgress > 0) {
199 alpha = 0.6 * (double)animProgress / (double)ANIMATIONSTEPS;
200 highlightColor = KColorScheme::shade(highlightColor, KColorScheme::LightShade, qMin(1.0, c + 0.4));
203 if (alpha > 0.0) {
204 contourTop = KColorUtils::mix(contourTop, highlightColor, alpha*0.4);
205 contourBottom = KColorUtils::mix(contourBottom, highlightColor, alpha*0.4);
206 surfaceTop = KColorUtils::mix(surfaceTop, highlightColor, alpha);
207 surfaceBottom = KColorUtils::mix(surfaceBottom, highlightColor, alpha);
210 QPixmap buffer(width(), height());
211 QPainter bP(&buffer);
213 // fake the titlebar background
214 bP.drawTiledPixmap(0, 0, width(), width(), m_client->getTitleBarTile(active) );
216 if (type() != MenuButton || hover || animProgress != 0) {
217 qreal rxo = 600/width();
218 qreal ryo = 600/height();
219 qreal rxi = 500/width();
220 qreal ryi = 500/height();
221 bP.setPen(Qt::NoPen);
222 bP.setRenderHints(QPainter::Antialiasing);
223 // contour
224 QLinearGradient outlineGradient(0, 0, 0, r.height());
225 outlineGradient.setColorAt(0.0, contourTop);
226 outlineGradient.setColorAt(1.0, contourBottom);
227 bP.setBrush(outlineGradient);
228 bP.drawRoundRect(r, rxo, ryo);
229 // surface
230 QLinearGradient surfaceGradient(0, 0, 0, r.height());
231 surfaceGradient.setColorAt(0.0, surfaceTop);
232 surfaceGradient.setColorAt(1.0, surfaceBottom);
233 bP.setBrush(surfaceGradient);
234 bP.drawRoundRect(r.adjusted(1,1,-1,-1), rxi, ryi);
237 if (type() == MenuButton)
239 QPixmap menuIcon(m_client->icon().pixmap( style()->pixelMetric( QStyle::PM_SmallIconSize ) ));
240 if (width() < menuIcon.width() || height() < menuIcon.height() ) {
241 menuIcon = menuIcon.scaled(width(), height());
243 bP.drawPixmap((width()-menuIcon.width())/2, (height()-menuIcon.height())/2, menuIcon);
245 else
247 int dX,dY;
248 const QBitmap &icon = Handler()->buttonBitmap(m_iconType, size(), decoration()->isToolWindow() );
249 dX = r.x()+(r.width()-icon.width())/2;
250 dY = r.y()+(r.height()-icon.height())/2;
251 if (isDown() ) {
252 dY++;
255 QColor fontColor = Handler()->getColor(TitleFont,active);
256 if(!isDown() && Handler()->titleShadow() ) {
257 QColor shadowColor = KColorScheme::shade(fontColor, KColorScheme::ShadowShade);
258 shadowColor.setAlphaF(shadowColor.alphaF() * 0.3);
259 bP.setPen(shadowColor);
260 bP.drawPixmap(dX+1, dY+1, icon);
263 bP.setPen(fontColor );
264 bP.drawPixmap(dX, dY, icon);
267 bP.end();
268 painter->drawPixmap(0, 0, buffer);
271 QBitmap IconEngine::icon(ButtonIcon icon, int size)
273 if (size%2 == 0)
274 --size;
276 QBitmap bitmap(size,size);
277 bitmap.fill(Qt::color0);
278 QPainter p(&bitmap);
280 p.setPen(Qt::color1);
282 QRect r = bitmap.rect();
284 // line widths
285 int lwTitleBar = 1;
286 if (r.width() > 16) {
287 lwTitleBar = 4;
288 } else if (r.width() > 4) {
289 lwTitleBar = 2;
291 int lwArrow = 1;
292 if (r.width() > 16) {
293 lwArrow = 4;
294 } else if (r.width() > 7) {
295 lwArrow = 2;
298 switch(icon) {
299 case CloseIcon:
301 int lineWidth = 1;
302 if (r.width() > 16) {
303 lineWidth = 3;
304 } else if (r.width() > 4) {
305 lineWidth = 2;
308 drawObject(p, DiagonalLine, r.x(), r.y(), r.width(), lineWidth);
309 drawObject(p, CrossDiagonalLine, r.x(), r.bottom(), r.width(), lineWidth);
311 break;
314 case MaxIcon:
316 int lineWidth2 = 1; // frame
317 if (r.width() > 16) {
318 lineWidth2 = 2;
319 } else if (r.width() > 4) {
320 lineWidth2 = 1;
323 drawObject(p, HorizontalLine, r.x(), r.top(), r.width(), lwTitleBar);
324 drawObject(p, HorizontalLine, r.x(), r.bottom()-(lineWidth2-1), r.width(), lineWidth2);
325 drawObject(p, VerticalLine, r.x(), r.top(), r.height(), lineWidth2);
326 drawObject(p, VerticalLine, r.right()-(lineWidth2-1), r.top(), r.height(), lineWidth2);
328 break;
331 case MaxRestoreIcon:
333 int lineWidth2 = 1; // frame
334 if (r.width() > 16) {
335 lineWidth2 = 2;
336 } else if (r.width() > 4) {
337 lineWidth2 = 1;
340 int margin1, margin2;
341 margin1 = margin2 = lineWidth2*2;
342 if (r.width() < 8)
343 margin1 = 1;
345 // background window
346 drawObject(p, HorizontalLine, r.x()+margin1, r.top(), r.width()-margin1, lineWidth2);
347 drawObject(p, HorizontalLine, r.right()-margin2, r.bottom()-(lineWidth2-1)-margin1, margin2, lineWidth2);
348 drawObject(p, VerticalLine, r.x()+margin1, r.top(), margin2, lineWidth2);
349 drawObject(p, VerticalLine, r.right()-(lineWidth2-1), r.top(), r.height()-margin1, lineWidth2);
351 // foreground window
352 drawObject(p, HorizontalLine, r.x(), r.top()+margin2, r.width()-margin2, lwTitleBar);
353 drawObject(p, HorizontalLine, r.x(), r.bottom()-(lineWidth2-1), r.width()-margin2, lineWidth2);
354 drawObject(p, VerticalLine, r.x(), r.top()+margin2, r.height(), lineWidth2);
355 drawObject(p, VerticalLine, r.right()-(lineWidth2-1)-margin2, r.top()+margin2, r.height(), lineWidth2);
357 break;
360 case MinIcon:
362 drawObject(p, HorizontalLine, r.x(), r.bottom()-(lwTitleBar-1), r.width(), lwTitleBar);
364 break;
367 case HelpIcon:
369 int center = r.x()+r.width()/2 -1;
370 int side = r.width()/4;
372 // paint a question mark... code is quite messy, to be cleaned up later...! :o
374 if (r.width() > 16) {
375 int lineWidth = 3;
377 // top bar
378 drawObject(p, HorizontalLine, center-side+3, r.y(), 2*side-3-1, lineWidth);
379 // top bar rounding
380 drawObject(p, CrossDiagonalLine, center-side-1, r.y()+5, 6, lineWidth);
381 drawObject(p, DiagonalLine, center+side-3, r.y(), 5, lineWidth);
382 // right bar
383 drawObject(p, VerticalLine, center+side+2-lineWidth, r.y()+3, r.height()-(2*lineWidth+side+2+1), lineWidth);
384 // bottom bar
385 drawObject(p, CrossDiagonalLine, center, r.bottom()-2*lineWidth, side+2, lineWidth);
386 drawObject(p, HorizontalLine, center, r.bottom()-3*lineWidth+2, lineWidth, lineWidth);
387 // the dot
388 drawObject(p, HorizontalLine, center, r.bottom()-(lineWidth-1), lineWidth, lineWidth);
389 } else if (r.width() > 8) {
390 int lineWidth = 2;
392 // top bar
393 drawObject(p, HorizontalLine, center-(side-1), r.y(), 2*side-1, lineWidth);
394 // top bar rounding
395 if (r.width() > 9) {
396 drawObject(p, CrossDiagonalLine, center-side-1, r.y()+3, 3, lineWidth);
397 } else {
398 drawObject(p, CrossDiagonalLine, center-side-1, r.y()+2, 3, lineWidth);
400 drawObject(p, DiagonalLine, center+side-1, r.y(), 3, lineWidth);
401 // right bar
402 drawObject(p, VerticalLine, center+side+2-lineWidth, r.y()+2, r.height()-(2*lineWidth+side+1), lineWidth);
403 // bottom bar
404 drawObject(p, CrossDiagonalLine, center, r.bottom()-2*lineWidth+1, side+2, lineWidth);
405 // the dot
406 drawObject(p, HorizontalLine, center, r.bottom()-(lineWidth-1), lineWidth, lineWidth);
407 } else {
408 int lineWidth = 1;
410 // top bar
411 drawObject(p, HorizontalLine, center-(side-1), r.y(), 2*side, lineWidth);
412 // top bar rounding
413 drawObject(p, CrossDiagonalLine, center-side-1, r.y()+1, 2, lineWidth);
414 // right bar
415 drawObject(p, VerticalLine, center+side+1, r.y(), r.height()-(side+2+1), lineWidth);
416 // bottom bar
417 drawObject(p, CrossDiagonalLine, center, r.bottom()-2, side+2, lineWidth);
418 // the dot
419 drawObject(p, HorizontalLine, center, r.bottom(), 1, 1);
422 break;
425 case NotOnAllDesktopsIcon:
427 int lwMark = r.width()-lwTitleBar*2-2;
428 if (lwMark < 1)
429 lwMark = 3;
431 drawObject(p, HorizontalLine, r.x()+(r.width()-lwMark)/2, r.y()+(r.height()-lwMark)/2, lwMark, lwMark);
433 // Fall through to OnAllDesktopsIcon intended!
435 case OnAllDesktopsIcon:
437 // horizontal bars
438 drawObject(p, HorizontalLine, r.x()+lwTitleBar, r.y(), r.width()-2*lwTitleBar, lwTitleBar);
439 drawObject(p, HorizontalLine, r.x()+lwTitleBar, r.bottom()-(lwTitleBar-1), r.width()-2*lwTitleBar, lwTitleBar);
440 // vertical bars
441 drawObject(p, VerticalLine, r.x(), r.y()+lwTitleBar, r.height()-2*lwTitleBar, lwTitleBar);
442 drawObject(p, VerticalLine, r.right()-(lwTitleBar-1), r.y()+lwTitleBar, r.height()-2*lwTitleBar, lwTitleBar);
445 break;
448 case NoKeepAboveIcon:
450 int center = r.x()+r.width()/2;
452 // arrow
453 drawObject(p, CrossDiagonalLine, r.x(), center+2*lwArrow, center-r.x(), lwArrow);
454 drawObject(p, DiagonalLine, r.x()+center, r.y()+1+2*lwArrow, center-r.x(), lwArrow);
455 if (lwArrow>1)
456 drawObject(p, HorizontalLine, center-(lwArrow-2), r.y()+2*lwArrow, (lwArrow-2)*2, lwArrow);
458 // Fall through to KeepAboveIcon intended!
460 case KeepAboveIcon:
462 int center = r.x()+r.width()/2;
464 // arrow
465 drawObject(p, CrossDiagonalLine, r.x(), center, center-r.x(), lwArrow);
466 drawObject(p, DiagonalLine, r.x()+center, r.y()+1, center-r.x(), lwArrow);
467 if (lwArrow>1)
468 drawObject(p, HorizontalLine, center-(lwArrow-2), r.y(), (lwArrow-2)*2, lwArrow);
470 break;
473 case NoKeepBelowIcon:
475 int center = r.x()+r.width()/2;
477 // arrow
478 drawObject(p, DiagonalLine, r.x(), center-2*lwArrow, center-r.x(), lwArrow);
479 drawObject(p, CrossDiagonalLine, r.x()+center, r.bottom()-1-2*lwArrow, center-r.x(), lwArrow);
480 if (lwArrow>1)
481 drawObject(p, HorizontalLine, center-(lwArrow-2), r.bottom()-(lwArrow-1)-2*lwArrow, (lwArrow-2)*2, lwArrow);
483 // Fall through to KeepBelowIcon intended!
485 case KeepBelowIcon:
487 int center = r.x()+r.width()/2;
489 // arrow
490 drawObject(p, DiagonalLine, r.x(), center, center-r.x(), lwArrow);
491 drawObject(p, CrossDiagonalLine, r.x()+center, r.bottom()-1, center-r.x(), lwArrow);
492 if (lwArrow>1)
493 drawObject(p, HorizontalLine, center-(lwArrow-2), r.bottom()-(lwArrow-1), (lwArrow-2)*2, lwArrow);
495 break;
498 case ShadeIcon:
500 drawObject(p, HorizontalLine, r.x(), r.y(), r.width(), lwTitleBar);
502 break;
505 case UnShadeIcon:
507 int lw1 = 1;
508 int lw2 = 1;
509 if (r.width() > 16) {
510 lw1 = 4;
511 lw2 = 2;
512 } else if (r.width() > 7) {
513 lw1 = 2;
514 lw2 = 1;
517 int h = qMax( (r.width()/2), (lw1+2*lw2) );
519 // horizontal bars
520 drawObject(p, HorizontalLine, r.x(), r.y(), r.width(), lw1);
521 drawObject(p, HorizontalLine, r.x(), r.x()+h-(lw2-1), r.width(), lw2);
522 // vertical bars
523 drawObject(p, VerticalLine, r.x(), r.y(), h, lw2);
524 drawObject(p, VerticalLine, r.right()-(lw2-1), r.y(), h, lw2);
526 break;
529 default:
530 break;
533 p.end();
535 bitmap.setMask(bitmap);
537 return bitmap;
540 void IconEngine::drawObject(QPainter &p, Object object, int x, int y, int length, int lineWidth)
542 switch(object) {
543 case DiagonalLine:
544 if (lineWidth <= 1) {
545 for (int i = 0; i < length; ++i) {
546 p.drawPoint(x+i,y+i);
548 } else if (lineWidth <= 2) {
549 for (int i = 0; i < length; ++i) {
550 p.drawPoint(x+i,y+i);
552 for (int i = 0; i < (length-1); ++i) {
553 p.drawPoint(x+1+i,y+i);
554 p.drawPoint(x+i,y+1+i);
556 } else {
557 for (int i = 1; i < (length-1); ++i) {
558 p.drawPoint(x+i,y+i);
560 for (int i = 0; i < (length-1); ++i) {
561 p.drawPoint(x+1+i,y+i);
562 p.drawPoint(x+i,y+1+i);
564 for (int i = 0; i < (length-2); ++i) {
565 p.drawPoint(x+2+i,y+i);
566 p.drawPoint(x+i,y+2+i);
569 break;
570 case CrossDiagonalLine:
571 if (lineWidth <= 1) {
572 for (int i = 0; i < length; ++i) {
573 p.drawPoint(x+i,y-i);
575 } else if (lineWidth <= 2) {
576 for (int i = 0; i < length; ++i) {
577 p.drawPoint(x+i,y-i);
579 for (int i = 0; i < (length-1); ++i) {
580 p.drawPoint(x+1+i,y-i);
581 p.drawPoint(x+i,y-1-i);
583 } else {
584 for (int i = 1; i < (length-1); ++i) {
585 p.drawPoint(x+i,y-i);
587 for (int i = 0; i < (length-1); ++i) {
588 p.drawPoint(x+1+i,y-i);
589 p.drawPoint(x+i,y-1-i);
591 for (int i = 0; i < (length-2); ++i) {
592 p.drawPoint(x+2+i,y-i);
593 p.drawPoint(x+i,y-2-i);
596 break;
597 case HorizontalLine:
598 for (int i = 0; i < lineWidth; ++i) {
599 p.drawLine(x,y+i, x+length-1, y+i);
601 break;
602 case VerticalLine:
603 for (int i = 0; i < lineWidth; ++i) {
604 p.drawLine(x+i,y, x+i, y+length-1);
606 break;
607 default:
608 break;
612 } // KWinPlastik