1 //////////////////////////////////////////////////////////////////////////////
4 // Ozone window decoration for KDE
6 // Copyright (c) 2006, 2007 Casper Boemann <cbr@boemann.dk>
7 // Copyright (c) 2006, 2007 Riccardo Iaconelli <riccardo@kde.org>
9 // Permission is hereby granted, free of charge, to any person obtaining a copy
10 // of this software and associated documentation files (the "Software"), to
11 // deal in the Software without restriction, including without limitation the
12 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
13 // sell copies of the Software, and to permit persons to whom the Software is
14 // furnished to do so, subject to the following conditions:
16 // The above copyright notice and this permission notice shall be included in
17 // all copies or substantial portions of the Software.
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
26 //////////////////////////////////////////////////////////////////////////////
27 // #ifndef OXYGENCLIENT_H
28 // #define OXYGENCLIENT_H
30 #include "oxygenclient.h"
36 #include <KColorUtils>
45 #include <QGridLayout>
46 #include <QResizeEvent>
47 #include <QMouseEvent>
50 #include <QPaintEvent>
51 #include <QPainterPath>
54 #include <QtGui/QApplication>
58 #include "oxygenclient.moc"
59 #include "oxygenbutton.h"
67 K_GLOBAL_STATIC_WITH_ARGS(OxygenHelper
, globalHelper
, ("OxygenDeco"))
69 OxygenHelper
*oxygenHelper()
74 static void oxkwincleanupBefore()
76 OxygenHelper
*h
= globalHelper
;
77 h
->invalidateCaches();
80 void renderDot(QPainter
*p
, const QPointF
&point
, qreal diameter
)
82 p
->drawEllipse(QRectF(point
.x()-diameter
/2, point
.y()-diameter
/2, diameter
, diameter
));
86 OxygenClient::OxygenClient(KDecorationBridge
*b
, KDecorationFactory
*f
)
87 : KCommonDecorationUnstable(b
, f
)
88 , colorCacheInvalid_(true)
89 , helper_(*globalHelper
)
91 qAddPostRoutine(oxkwincleanupBefore
);
94 OxygenClient::~OxygenClient()
97 QString
OxygenClient::visibleName() const
102 void OxygenClient::init()
104 KCommonDecoration::init();
106 widget()->setAutoFillBackground(false);
107 widget()->setAttribute(Qt::WA_OpaquePaintEvent
);
110 bool OxygenClient::decorationBehaviour(DecorationBehaviour behaviour
) const
114 return true;//Handler()->menuClose();
120 return KCommonDecoration::decorationBehaviour(behaviour
);
124 int OxygenClient::layoutMetric(LayoutMetric lm
, bool respectWindowState
, const KCommonDecorationButton
*btn
) const
126 bool maximized
= maximizeMode()==MaximizeFull
&& !options()->moveResizeMaximizedWindows();
131 case LM_BorderBottom
:
133 if (respectWindowState
&& maximized
) {
140 case LM_TitleEdgeTop
:
142 if (respectWindowState
&& maximized
) {
149 case LM_TitleEdgeBottom
:
154 case LM_TitleEdgeLeft
:
155 case LM_TitleEdgeRight
:
157 if (respectWindowState
&& maximized
) {
164 case LM_TitleBorderLeft
:
165 case LM_TitleBorderRight
:
169 case LM_ButtonHeight
:
172 if (respectWindowState
&& isToolWindow()) {
173 return OXYGEN_BUTTONSIZE
;
175 return OXYGEN_BUTTONSIZE
;
179 case LM_ButtonSpacing
:
182 case LM_ButtonMarginTop
:
186 return KCommonDecoration::layoutMetric(lm
, respectWindowState
, btn
);
191 KCommonDecorationButton
*OxygenClient::createButton(::ButtonType type
)
195 return new OxygenButton(*this, i18n("Menu"), ButtonMenu
);
198 return new OxygenButton(*this, i18n("Help"), ButtonHelp
);
201 return new OxygenButton(*this, i18n("Minimize"), ButtonMin
);
204 return new OxygenButton(*this, i18n("Maximize"), ButtonMax
);
207 return new OxygenButton(*this, i18n("Close"), ButtonClose
);
210 return new OxygenButton(*this, i18n("Keep Above Others"), ButtonAbove
);
213 return new OxygenButton(*this, i18n("Keep Below Others"), ButtonBelow
);
215 case OnAllDesktopsButton
:
216 return new OxygenButton(*this, i18n("On All Desktops"), ButtonSticky
);
219 return new OxygenButton(*this, i18n("Shade Button"), ButtonShade
);
229 // t - target contrast ratio
230 QColor
reduceContrast(const QColor
&c0
, const QColor
&c1
, double t
)
232 double s
= KColorUtils::contrastRatio(c0
, c1
);
236 double l
= 0.0, h
= 1.0;
239 for (int maxiter
= 16; maxiter
; --maxiter
) {
241 r
= KColorUtils::mix(c0
, c1
, a
);
242 x
= KColorUtils::contrastRatio(c0
, r
);
243 if (fabs(x
- t
) < 0.01)
254 QColor
OxygenClient::titlebarTextColor(const QPalette
&palette
)
256 if( !OxygenFactory::blendTitlebarColors())
257 return options()->color(ColorFont
, isActive());
259 return palette
.color(QPalette::Active
, QPalette::WindowText
);
261 if(colorCacheInvalid_
) {
262 QColor ab
= palette
.color(QPalette::Active
, QPalette::Window
);
263 QColor af
= palette
.color(QPalette::Active
, QPalette::WindowText
);
264 QColor nb
= palette
.color(QPalette::Inactive
, QPalette::Window
);
265 QColor nf
= palette
.color(QPalette::Inactive
, QPalette::WindowText
);
267 colorCacheInvalid_
= false;
268 cachedTitlebarTextColor_
= reduceContrast(nb
, nf
, qMax(qreal(2.5), KColorUtils::contrastRatio(ab
, KColorUtils::mix(ab
, af
, 0.4))));
270 return cachedTitlebarTextColor_
;
275 void OxygenClient::paintEvent(QPaintEvent
*e
)
278 if (!OxygenFactory::initialized()) return;
282 QPalette palette
= widget()->palette();
283 QPainter
painter(widget());
285 // Set palette to the right group.
286 // TODO - fix KWin to do this for us :-).
288 palette
.setCurrentColorGroup(QPalette::Active
);
290 palette
.setCurrentColorGroup(QPalette::Inactive
);
293 QRect frame
= widget()->frameGeometry();
294 QColor color
= OxygenFactory::blendTitlebarColors() ? palette
.window().color() : options()->color( ColorTitleBar
, isActive());
295 QColor light
= helper_
.calcLightColor( color
);
296 QColor dark
= helper_
.calcDarkColor( color
);
298 const int titleHeight
= layoutMetric(LM_TitleHeight
);
299 const int titleTop
= layoutMetric(LM_TitleEdgeTop
) + frame
.top();
300 const int titleEdgeLeft
= layoutMetric(LM_TitleEdgeLeft
);
301 const int marginLeft
= layoutMetric(LM_TitleBorderLeft
);
302 const int marginRight
= layoutMetric(LM_TitleBorderRight
);
304 const int titleLeft
= frame
.left() + titleEdgeLeft
+ buttonsLeftWidth() + marginLeft
;
305 const int titleWidth
= frame
.width() -
306 titleEdgeLeft
- layoutMetric(LM_TitleEdgeRight
) -
307 buttonsLeftWidth() - buttonsRightWidth() -
308 marginLeft
- marginRight
;
310 QPalette
pal2( palette
);
311 if( !OxygenFactory::blendTitlebarColors()) {
312 pal2
.setColor( QPalette::Window
, options()->color(
313 KDecorationDefines::ColorTitleBar
, isActive()));
315 // draw window background
316 helper_
.renderWindowBackground(&painter
, frame
, this->widget(), pal2
, 0);
319 painter
.setFont(options()->font(isActive(), false));
320 painter
.setPen(titlebarTextColor(pal2
));
321 painter
.drawText(titleLeft
, titleTop
-1, titleWidth
, titleHeight
, // -1 is to go into top resizearea
322 OxygenFactory::titleAlignment() | Qt::AlignVCenter
, caption());
324 painter
.setRenderHint(QPainter::Antialiasing
);
326 // Draw dividing line
327 frame
= widget()->rect();
328 if (shadowsActive()) {
329 frame
.adjust(-1,-1,1,1);
331 frame
.getRect(&x
, &y
, &w
, &h
);
334 helper_
.drawSeparator(&painter
, QRect(x
, titleTop
+titleHeight
-1.5, w
, 2), color
, Qt::Horizontal
);
337 // draw stripes as indicator for active windows
338 if (isActive() && OxygenFactory::showStripes()) {
339 Qt::Alignment align
= OxygenFactory::titleAlignment();
340 if (widget()->layoutDirection() == Qt::RightToLeft
)
342 if (align
== Qt::AlignLeft
)
343 align
= Qt::AlignRight
;
344 else if (align
== Qt::AlignRight
)
345 align
= Qt::AlignLeft
;
348 if (align
& Qt::AlignLeft
) {
349 int left
= titleLeft
+ QFontMetrics(options()->font(isActive(), false)).width(caption()) + 4;
350 int right
= titleLeft
+ titleWidth
;
351 drawScratch(&painter
, palette
, left
, right
, titleTop
+6);
353 if (align
& Qt::AlignRight
) {
354 int left
= titleLeft
;
355 int right
= titleLeft
+ titleWidth
- QFontMetrics(options()->font(isActive(), false)).width(caption()) - 4;
356 drawScratch(&painter
, palette
, right
, left
, titleTop
+6);
358 if (align
& Qt::AlignHCenter
) {
359 int textWidth
= QFontMetrics(options()->font(isActive(), false)).width(caption());
360 int left
= titleLeft
;
361 int centerLeft
= titleLeft
+ titleWidth
/2 - textWidth
/2 - 4;
362 int centerRight
= titleLeft
+ titleWidth
/2 + textWidth
/2 + 4;
363 int right
= titleLeft
+ titleWidth
;
364 drawScratch(&painter
, palette
, centerLeft
, left
, titleTop
+6);
365 drawScratch(&painter
, palette
, centerRight
, right
, titleTop
+6);
369 // Draw shadows of the frame
370 bool maximized
= maximizeMode()==MaximizeFull
&& !options()->moveResizeMaximizedWindows();
375 helper_
.drawFloatFrame(&painter
, frame
, color
, !shadowsActive(), isActive(),
376 KDecoration::options()->color(ColorTitleBar
));
381 // Draw the 3-dots resize handles
382 qreal cenY
= frame
.height() / 2 + x
+ 0.5;
383 qreal posX
= frame
.width() + y
- 2.5;
384 painter
.setPen(Qt::NoPen
);
385 painter
.setBrush(QColor(0, 0, 0, 66));
386 renderDot(&painter
, QPointF(posX
, cenY
- 3), 1.8);
387 renderDot(&painter
, QPointF(posX
, cenY
), 1.8);
388 renderDot(&painter
, QPointF(posX
, cenY
+ 3), 1.8);
390 painter
.translate(x
+ frame
.width()-9, y
+ frame
.height()-9);
391 renderDot(&painter
, QPointF(2.5, 6.5), 1.8);
392 renderDot(&painter
, QPointF(5.5, 5.5), 1.8);
393 renderDot(&painter
, QPointF(6.5, 2.5), 1.8);
396 void OxygenClient::drawScratch(QPainter
*p
, QPalette
&palette
, const int start
, const int end
, const int topMargin
)
398 QLinearGradient
scratchlg(QPoint(start
,0), QPoint(end
,0));
399 scratchlg
.setColorAt(0.0, Qt::transparent
);
400 scratchlg
.setColorAt(0.05, KDecoration::options()->color(ColorTitleBar
));
401 scratchlg
.setColorAt(1.0, Qt::transparent
);
402 QPen
pen1(scratchlg
, 0.5);
404 QLinearGradient
scratchlg2(QPoint(start
,0), QPoint(end
,0));
405 scratchlg2
.setColorAt(0.0, Qt::transparent
);
406 scratchlg2
.setColorAt(0.05, helper_
.calcLightColor(palette
.color(QPalette::Window
)));
407 scratchlg2
.setColorAt(1.0, Qt::transparent
);
408 QPen
pen2(scratchlg2
, 0.5);
410 bool antialiasing
= p
->testRenderHint(QPainter::Antialiasing
);
411 p
->setRenderHint(QPainter::Antialiasing
, false);
412 for (int i
= 0; i
< 3; ++i
)
415 p
->drawLine(QPointF(start
, topMargin
+4*i
), QPointF(end
, topMargin
+4*i
));
417 p
->drawLine(QPointF(start
, topMargin
+4*i
+1), QPointF(end
, topMargin
+4*i
+1));
419 p
->setRenderHint(QPainter::Antialiasing
, antialiasing
);
423 void OxygenClient::updateWindowShape()
425 bool maximized
= maximizeMode()==MaximizeFull
&& !options()->moveResizeMaximizedWindows();
426 int w
=widget()->width();
427 int h
=widget()->height();
430 QRegion
mask(0,0,w
,h
);
435 if (!shadowsActive()) {
436 QRegion
mask(4, 0, w
-8, h
);
437 mask
+= QRegion(0, 4, w
, h
-8);
438 mask
+= QRegion(2, 1, w
-4, h
-2);
439 mask
+= QRegion(1, 2, w
-2, h
-4);
444 QRegion
mask(5, 0, w
-10, h
-0);
445 mask
+= QRegion(0, 5, w
-0, h
-10);
446 mask
+= QRegion(2, 2, w
-4, h
-4);
447 mask
+= QRegion(3, 1, w
-6, h
-2);
448 mask
+= QRegion(1, 3, w
-2, h
-6);
454 QList
<QRect
> OxygenClient::shadowQuads( ShadowType type
) const
458 QSize size
= widget()->size();
459 int outside
=21, underlap
=4, cornersize
=25;
460 // These are underlap under the decoration so the corners look nicer 10px on the outside
462 quads
.append(QRect(-outside
, size
.height()-underlap
, cornersize
, cornersize
));
463 quads
.append(QRect(underlap
, size
.height()-underlap
, size
.width()-2*underlap
, cornersize
));
464 quads
.append(QRect(size
.width()-underlap
, size
.height()-underlap
, cornersize
, cornersize
));
465 quads
.append(QRect(-outside
, underlap
, cornersize
, size
.height()-2*underlap
));
466 quads
.append(QRect(size
.width()-underlap
, underlap
, cornersize
, size
.height()-2*underlap
));
467 quads
.append(QRect(-outside
, -outside
, cornersize
, cornersize
));
468 quads
.append(QRect(underlap
, -outside
, size
.width()-2*underlap
, cornersize
));
469 quads
.append(QRect(size
.width()-underlap
, -outside
, cornersize
, cornersize
));
473 double OxygenClient::shadowOpacity( ShadowType type
) const
476 case ShadowBorderedActive
:
480 case ShadowBorderedInactive
:
485 abort(); // Should never be reached
493 //#include "oxygenclient.moc"