1 //////////////////////////////////////////////////////////////////////////////
4 // Oxygen 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"
65 K_GLOBAL_STATIC_WITH_ARGS(OxygenHelper
, globalHelper
, ("OxygenDeco"))
67 OxygenHelper
*oxygenHelper()
72 static void oxkwincleanupBefore()
74 OxygenHelper
*h
= globalHelper
;
75 h
->invalidateCaches();
78 void renderDot(QPainter
*p
, const QPointF
&point
, qreal diameter
)
80 p
->drawEllipse(QRectF(point
.x()-diameter
/2, point
.y()-diameter
/2, diameter
, diameter
));
84 OxygenClient::OxygenClient(KDecorationBridge
*b
, KDecorationFactory
*f
)
85 : KCommonDecorationUnstable(b
, f
)
86 , colorCacheInvalid_(true)
87 , helper_(*globalHelper
)
89 qAddPostRoutine(oxkwincleanupBefore
);
92 OxygenClient::~OxygenClient()
95 QString
OxygenClient::visibleName() const
97 return i18n("Oxygen");
100 void OxygenClient::init()
102 KCommonDecoration::init();
104 widget()->setAutoFillBackground(false);
105 widget()->setAttribute(Qt::WA_OpaquePaintEvent
);
108 bool OxygenClient::decorationBehaviour(DecorationBehaviour behaviour
) const
112 return true;//Handler()->menuClose();
118 return KCommonDecoration::decorationBehaviour(behaviour
);
122 int OxygenClient::layoutMetric(LayoutMetric lm
, bool respectWindowState
, const KCommonDecorationButton
*btn
) const
124 bool maximized
= maximizeMode()==MaximizeFull
&& !options()->moveResizeMaximizedWindows();
129 case LM_BorderBottom
:
131 if (respectWindowState
&& maximized
) {
138 case LM_TitleEdgeTop
:
140 if (respectWindowState
&& maximized
) {
147 case LM_TitleEdgeBottom
:
152 case LM_TitleEdgeLeft
:
153 case LM_TitleEdgeRight
:
155 if (respectWindowState
&& maximized
) {
162 case LM_TitleBorderLeft
:
163 case LM_TitleBorderRight
:
167 case LM_ButtonHeight
:
170 if (respectWindowState
&& isToolWindow()) {
171 return OXYGEN_BUTTONSIZE
;
173 return OXYGEN_BUTTONSIZE
;
177 case LM_ButtonSpacing
:
180 case LM_ButtonMarginTop
:
184 return KCommonDecoration::layoutMetric(lm
, respectWindowState
, btn
);
189 KCommonDecorationButton
*OxygenClient::createButton(::ButtonType type
)
193 return new OxygenButton(*this, i18n("Menu"), ButtonMenu
);
196 return new OxygenButton(*this, i18n("Help"), ButtonHelp
);
199 return new OxygenButton(*this, i18n("Minimize"), ButtonMin
);
202 return new OxygenButton(*this, i18n("Maximize"), ButtonMax
);
205 return new OxygenButton(*this, i18n("Close"), ButtonClose
);
208 return new OxygenButton(*this, i18n("Keep Above Others"), ButtonAbove
);
211 return new OxygenButton(*this, i18n("Keep Below Others"), ButtonBelow
);
213 case OnAllDesktopsButton
:
214 return new OxygenButton(*this, i18n("On All Desktops"), ButtonSticky
);
217 return new OxygenButton(*this, i18n("Shade Button"), ButtonShade
);
227 // t - target contrast ratio
228 QColor
reduceContrast(const QColor
&c0
, const QColor
&c1
, double t
)
230 double s
= KColorUtils::contrastRatio(c0
, c1
);
234 double l
= 0.0, h
= 1.0;
237 for (int maxiter
= 16; maxiter
; --maxiter
) {
239 r
= KColorUtils::mix(c0
, c1
, a
);
240 x
= KColorUtils::contrastRatio(c0
, r
);
241 if (fabs(x
- t
) < 0.01)
252 QColor
OxygenClient::titlebarTextColor(const QPalette
&palette
)
255 return palette
.color(QPalette::Active
, QPalette::WindowText
);
257 if(colorCacheInvalid_
) {
258 QColor ab
= palette
.color(QPalette::Active
, QPalette::Window
);
259 QColor af
= palette
.color(QPalette::Active
, QPalette::WindowText
);
260 QColor nb
= palette
.color(QPalette::Inactive
, QPalette::Window
);
261 QColor nf
= palette
.color(QPalette::Inactive
, QPalette::WindowText
);
263 colorCacheInvalid_
= false;
264 cachedTitlebarTextColor_
= reduceContrast(nb
, nf
, qMax(qreal(2.5), KColorUtils::contrastRatio(ab
, KColorUtils::mix(ab
, af
, 0.4))));
266 return cachedTitlebarTextColor_
;
271 void OxygenClient::paintEvent(QPaintEvent
*e
)
274 if (!OxygenFactory::initialized()) return;
278 QPalette palette
= widget()->palette();
279 QPainter
painter(widget());
281 // Set palette to the right group.
282 // TODO - fix KWin to do this for us :-).
284 palette
.setCurrentColorGroup(QPalette::Active
);
286 palette
.setCurrentColorGroup(QPalette::Inactive
);
289 QRect frame
= widget()->frameGeometry();
290 QColor color
= palette
.window().color();
291 QColor light
= helper_
.calcLightColor( color
);
292 QColor dark
= helper_
.calcDarkColor( color
);
294 const int titleHeight
= layoutMetric(LM_TitleHeight
);
295 const int titleTop
= layoutMetric(LM_TitleEdgeTop
) + frame
.top();
296 const int titleEdgeLeft
= layoutMetric(LM_TitleEdgeLeft
);
297 const int marginLeft
= layoutMetric(LM_TitleBorderLeft
);
298 const int marginRight
= layoutMetric(LM_TitleBorderRight
);
300 const int titleLeft
= frame
.left() + titleEdgeLeft
+ buttonsLeftWidth() + marginLeft
;
301 const int titleWidth
= frame
.width() -
302 titleEdgeLeft
- layoutMetric(LM_TitleEdgeRight
) -
303 buttonsLeftWidth() - buttonsRightWidth() -
304 marginLeft
- marginRight
;
306 // draw window background
307 helper_
.renderWindowBackground(&painter
, frame
, this->widget(), palette
, 0);
310 painter
.setFont(options()->font(isActive(), false));
311 painter
.setPen(titlebarTextColor(palette
));
312 painter
.drawText(titleLeft
, titleTop
-1, titleWidth
, titleHeight
, // -1 is to go into top resizearea
313 OxygenFactory::titleAlignment() | Qt::AlignVCenter
, caption());
315 painter
.setRenderHint(QPainter::Antialiasing
);
317 // Draw dividing line
318 frame
= widget()->rect();
319 if (shadowsActive()) {
320 frame
.adjust(-1,-1,1,1);
322 frame
.getRect(&x
, &y
, &w
, &h
);
325 helper_
.drawSeparator(&painter
, QRect(x
, titleTop
+titleHeight
-1.5, w
, 2), color
, Qt::Horizontal
);
328 // draw stripes as indicator for active windows
329 if (isActive() && OxygenFactory::showStripes()) {
330 Qt::Alignment align
= OxygenFactory::titleAlignment();
331 if (widget()->layoutDirection() == Qt::RightToLeft
)
333 if (align
== Qt::AlignLeft
)
334 align
= Qt::AlignRight
;
335 else if (align
== Qt::AlignRight
)
336 align
= Qt::AlignLeft
;
339 if (align
& Qt::AlignLeft
) {
340 int left
= titleLeft
+ QFontMetrics(options()->font(isActive(), false)).width(caption()) + 4;
341 int right
= titleLeft
+ titleWidth
;
342 drawScratch(&painter
, palette
, left
, right
, titleTop
+6);
344 if (align
& Qt::AlignRight
) {
345 int left
= titleLeft
;
346 int right
= titleLeft
+ titleWidth
- QFontMetrics(options()->font(isActive(), false)).width(caption()) - 4;
347 drawScratch(&painter
, palette
, right
, left
, titleTop
+6);
349 if (align
& Qt::AlignHCenter
) {
350 int textWidth
= QFontMetrics(options()->font(isActive(), false)).width(caption());
351 int left
= titleLeft
;
352 int centerLeft
= titleLeft
+ titleWidth
/2 - textWidth
/2 - 4;
353 int centerRight
= titleLeft
+ titleWidth
/2 + textWidth
/2 + 4;
354 int right
= titleLeft
+ titleWidth
;
355 drawScratch(&painter
, palette
, centerLeft
, left
, titleTop
+6);
356 drawScratch(&painter
, palette
, centerRight
, right
, titleTop
+6);
360 // Draw shadows of the frame
361 bool maximized
= maximizeMode()==MaximizeFull
&& !options()->moveResizeMaximizedWindows();
366 helper_
.drawFloatFrame(&painter
, frame
, color
, !shadowsActive(), isActive(),
367 KDecoration::options()->color(ColorTitleBar
));
372 // Draw the 3-dots resize handles
373 qreal cenY
= frame
.height() / 2 + x
+ 0.5;
374 qreal posX
= frame
.width() + y
- 2.5;
375 painter
.setPen(Qt::NoPen
);
376 painter
.setBrush(QColor(0, 0, 0, 66));
377 renderDot(&painter
, QPointF(posX
, cenY
- 3), 1.8);
378 renderDot(&painter
, QPointF(posX
, cenY
), 1.8);
379 renderDot(&painter
, QPointF(posX
, cenY
+ 3), 1.8);
381 painter
.translate(x
+ frame
.width()-9, y
+ frame
.height()-9);
382 renderDot(&painter
, QPointF(2.5, 6.5), 1.8);
383 renderDot(&painter
, QPointF(5.5, 5.5), 1.8);
384 renderDot(&painter
, QPointF(6.5, 2.5), 1.8);
387 void OxygenClient::drawScratch(QPainter
*p
, QPalette
&palette
, const int start
, const int end
, const int topMargin
)
389 QLinearGradient
scratchlg(QPoint(start
,0), QPoint(end
,0));
390 scratchlg
.setColorAt(0.0, Qt::transparent
);
391 scratchlg
.setColorAt(0.05, KDecoration::options()->color(ColorTitleBar
));
392 scratchlg
.setColorAt(1.0, Qt::transparent
);
393 QPen
pen1(scratchlg
, 0.5);
395 QLinearGradient
scratchlg2(QPoint(start
,0), QPoint(end
,0));
396 scratchlg2
.setColorAt(0.0, Qt::transparent
);
397 scratchlg2
.setColorAt(0.05, helper_
.calcLightColor(palette
.color(QPalette::Window
)));
398 scratchlg2
.setColorAt(1.0, Qt::transparent
);
399 QPen
pen2(scratchlg2
, 0.5);
401 bool antialiasing
= p
->testRenderHint(QPainter::Antialiasing
);
402 p
->setRenderHint(QPainter::Antialiasing
, false);
403 for (int i
= 0; i
< 3; ++i
)
406 p
->drawLine(QPointF(start
, topMargin
+4*i
), QPointF(end
, topMargin
+4*i
));
408 p
->drawLine(QPointF(start
, topMargin
+4*i
+1), QPointF(end
, topMargin
+4*i
+1));
410 p
->setRenderHint(QPainter::Antialiasing
, antialiasing
);
414 void OxygenClient::updateWindowShape()
416 bool maximized
= maximizeMode()==MaximizeFull
&& !options()->moveResizeMaximizedWindows();
417 int w
=widget()->width();
418 int h
=widget()->height();
421 QRegion
mask(0,0,w
,h
);
426 if (!shadowsActive()) {
427 QRegion
mask(4, 0, w
-8, h
);
428 mask
+= QRegion(0, 4, w
, h
-8);
429 mask
+= QRegion(2, 1, w
-4, h
-2);
430 mask
+= QRegion(1, 2, w
-2, h
-4);
435 QRegion
mask(5, 0, w
-10, h
-0);
436 mask
+= QRegion(0, 5, w
-0, h
-10);
437 mask
+= QRegion(2, 2, w
-4, h
-4);
438 mask
+= QRegion(3, 1, w
-6, h
-2);
439 mask
+= QRegion(1, 3, w
-2, h
-6);
445 QList
<QRect
> OxygenClient::shadowQuads( ShadowType type
) const
449 QSize size
= widget()->size();
450 int outside
=21, underlap
=4, cornersize
=25;
451 // These are underlap under the decoration so the corners look nicer 10px on the outside
453 quads
.append(QRect(-outside
, size
.height()-underlap
, cornersize
, cornersize
));
454 quads
.append(QRect(underlap
, size
.height()-underlap
, size
.width()-2*underlap
, cornersize
));
455 quads
.append(QRect(size
.width()-underlap
, size
.height()-underlap
, cornersize
, cornersize
));
456 quads
.append(QRect(-outside
, underlap
, cornersize
, size
.height()-2*underlap
));
457 quads
.append(QRect(size
.width()-underlap
, underlap
, cornersize
, size
.height()-2*underlap
));
458 quads
.append(QRect(-outside
, -outside
, cornersize
, cornersize
));
459 quads
.append(QRect(underlap
, -outside
, size
.width()-2*underlap
, cornersize
));
460 quads
.append(QRect(size
.width()-underlap
, -outside
, cornersize
, cornersize
));
464 double OxygenClient::shadowOpacity( ShadowType type
) const
467 case ShadowBorderedActive
:
471 case ShadowBorderedInactive
:
476 abort(); // Should never be reached
483 //#include "oxygenclient.moc"