/***************************************************************************** * Copyright 2007 - 2010 Craig Drummond * * Copyright 2013 - 2015 Yichao Yu * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * * published by the Free Software Foundation; either version 2.1 of the * * License, or (at your option) version 3, or any later version accepted * * by the membership of KDE e.V. (or its successor approved by the * * membership of KDE e.V.), which shall act as a proxy defined in * * Section 6 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library. If not, * * see . * *****************************************************************************/ /* based on the window decoration "Plastik": Copyright (C) 2003-2005 Sandro Giessl based on the window decoration "Web": Copyright (C) 2001 Rik Hemsley (rikkus) */ #include "qtcurvebutton.h" #include #include #include #include #include #include #include #include "qtcurveclient.h" #include namespace QtCurve { namespace KWin { QtCurveButton::QtCurveButton(ButtonType type, QtCurveClient *parent) : KCommonDecorationButton(type, parent), m_client(parent), m_iconType(NumButtonIcons), m_hover(false) { // setAttribute(Qt::WA_PaintOnScreen, false); setAttribute(Qt::WA_NoSystemBackground, true); setAutoFillBackground(false); // setFocusPolicy(Qt::NoFocus); // setAttribute(Qt::WA_OpaquePaintEvent, false); // setAttribute(Qt::WA_Hover, true); setCursor(Qt::ArrowCursor); reset(DecorationReset); } void QtCurveButton::reset(unsigned long changed) { if (changed & DecorationReset || changed & ManualReset || changed & SizeChange || changed & StateChange) { switch (type()) { #if KDE_IS_VERSION(4, 9, 85) case AppMenuButton: m_iconType = MenuIcon; break; #endif // TODO ItemMenuButton? case ItemCloseButton: m_iconType = CloseTabIcon; break; case CloseButton: m_iconType = CloseIcon; break; case HelpButton: m_iconType = HelpIcon; break; case MinButton: m_iconType = MinIcon; break; case MaxButton: m_iconType = isChecked() ? MaxRestoreIcon : MaxIcon; break; case OnAllDesktopsButton: m_iconType = isChecked() ? NotOnAllDesktopsIcon : OnAllDesktopsIcon; break; case ShadeButton: m_iconType = isChecked() ? UnShadeIcon : ShadeIcon; break; case AboveButton: m_iconType = isChecked() ? NoKeepAboveIcon : KeepAboveIcon; break; case BelowButton: m_iconType = isChecked() ? NoKeepBelowIcon : KeepBelowIcon; break; case MenuButton: m_iconType = MenuIcon; break; default: m_iconType = NumButtonIcons; // empty... break; } this->update(); } } void QtCurveButton::enterEvent(QEvent *e) { m_hover = true; KCommonDecorationButton::enterEvent(e); update(); // Hacky NVIDIA fix - sometimes mouseover state gets 'stuck' - // but only for some windows!!! QTimer::singleShot(50, this, SLOT(update())); } void QtCurveButton::leaveEvent(QEvent *e) { m_hover = false; KCommonDecorationButton::leaveEvent(e); update(); // Hacky NVIDIA fix - sometimes mouseover state gets 'stuck' - // but only for some windows!!! QTimer::singleShot(50, this, SLOT(update())); } void QtCurveButton::paintEvent(QPaintEvent *ev) { if (m_client->compositingActive()) { QPainter p(this); p.setClipRect(rect().intersected(ev->rect())); drawButton(&p); } else { QPixmap pixmap(size()); { QPainter p(&pixmap); p.setRenderHints(QPainter::Antialiasing); parentWidget()->render(&p, QPoint(), geometry(), QWidget::DrawWindowBackground); drawButton(&p); } QPainter p(this); p.setClipRect(ev->rect()); p.drawPixmap(QPoint(), pixmap); } } void QtCurveButton::drawButton(QPainter *painter) { int flags = Handler()->wStyle()->pixelMetric( (QStyle::PixelMetric)QtC_TitleBarButtons, nullptr, nullptr); bool active(m_client->isActive()); if (!active && !m_hover && flags & TITLEBAR_BUTTOM_HIDE_ON_INACTIVE_WINDOW) return; QRect r(0, 0, width(), height()); int versionHack = 0; bool sunken(isDown()); bool drawFrame(!(flags & TITLEBAR_BUTTON_NO_FRAME) && (m_hover || sunken || !(flags & TITLEBAR_BUTTON_HOVER_FRAME))); bool drewFrame(false); bool iconForMenu(TITLEBAR_ICON_MENU_BUTTON == Handler()->wStyle()->pixelMetric( (QStyle::PixelMetric)QtC_TitleBarIcon, nullptr, nullptr)); QColor buttonColor(KDecoration::options()->color(KDecoration::ColorTitleBar, active)); QPixmap buffer(width(), height()); buffer.fill(Qt::transparent); QPainter bP(&buffer); bool isTabClose(ItemCloseButton == type()); // isItemMenu? if (isTabClose && drawFrame && !(sunken || m_hover)) drawFrame = false; // if(CloseButton == type()) // buttonColor = midColor(QColor(180,64,32), buttonColor); switch (type()) { case HelpButton: versionHack = TBAR_VERSION_HACK + TITLEBAR_HELP; break; case MaxButton: versionHack = TBAR_VERSION_HACK + TITLEBAR_MAX; break; case MinButton: versionHack = TBAR_VERSION_HACK + TITLEBAR_MIN; break; case ItemCloseButton: case CloseButton: versionHack = TBAR_VERSION_HACK + TITLEBAR_CLOSE; break; #if KDE_IS_VERSION(4, 9, 85) case AppMenuButton: #endif case MenuButton: versionHack = TBAR_VERSION_HACK + TITLEBAR_MENU; break; case OnAllDesktopsButton: versionHack = TBAR_VERSION_HACK + TITLEBAR_ALL_DESKTOPS; break; case AboveButton: versionHack = TBAR_VERSION_HACK + TITLEBAR_KEEP_ABOVE; break; case BelowButton: versionHack = TBAR_VERSION_HACK + TITLEBAR_KEEP_BELOW; break; case ShadeButton: versionHack = TBAR_VERSION_HACK + TITLEBAR_SHADE; default: break; } if (drawFrame && (/*!(flags&TITLEBAR_BUTTON_ROUND) || */ MenuButton != type() || !iconForMenu)) { QStyleOption opt; int offset = flags & TITLEBAR_BUTTON_ROUND && !m_client->isToolWindow() ? 1 : 0; if (flags & TITLEBAR_BUTTON_SUNKEN_BACKGROUND) // && flags & TITLEBAR_BUTTON_ROUND) offset++; opt.init(this); opt.rect = QRect(offset, offset, width() - (2 * offset), height() - (2 * offset)); opt.state |= (isDown() ? QStyle::State_Sunken : QStyle::State_Raised) | (active ? QStyle::State_Active : QStyle::State_None) | (m_hover ? QStyle::State_MouseOver : QStyle::State_None) | QStyle::State_Horizontal | QtC_StateKWin; opt.state &= ~QStyle::State_HasFocus; if (!isEnabled()) { opt.palette.setColor(QPalette::Button, buttonColor); } else { if (!(flags & TITLEBAR_BUTTON_STD_COLOR) || (flags & TITLEBAR_BUTTON_COLOR_MOUSE_OVER && !m_hover && !(flags&TITLEBAR_BUTTON_COLOR))) opt.palette.setColor(QPalette::Button, buttonColor); if (flags & TITLEBAR_BUTTON_COLOR && !(flags & TITLEBAR_BUTTON_COLOR_SYMBOL)) opt.version = versionHack; } Handler()->wStyle()->drawPrimitive(QStyle::PE_PanelButtonCommand, &opt, &bP, nullptr); drewFrame = true; } if (MenuButton == type() && iconForMenu) { QPixmap menuIcon(m_client->icon().pixmap( style()->pixelMetric(QStyle::PM_SmallIconSize))); if (width() < menuIcon.width() || height() < menuIcon.height()) menuIcon = menuIcon.scaled(width(), height()); int dX(((width() - menuIcon.width()) / 2.0) + 0.5), dY(((height() - menuIcon.height()) / 2.0) + 0.5); if (sunken) { dY++; dX++; } bP.drawPixmap(dX, dY, menuIcon); } else if (isEnabled() && (!(flags & TITLEBAR_BUTTON_HOVER_SYMBOL_FULL) || sunken || m_hover)) { const QBitmap &icon = Handler()->buttonBitmap(m_iconType, size(), decoration()->isToolWindow()); bool customCol(false); bool faded(!m_hover && flags & TITLEBAR_BUTTON_HOVER_SYMBOL); QColor col(KDecoration::options()->color(KDecoration::ColorFont, active/* || faded*/)); int dX(r.x()+(r.width() - icon.width())/2); int dY(r.y()+(r.height() - icon.height())/2); EEffect effect((EEffect)(style()->pixelMetric((QStyle::PixelMetric)QtC_TitleBarEffect))); if(EFFECT_ETCH==effect && drewFrame) effect=EFFECT_SHADOW; if(m_hover || !(flags&TITLEBAR_BUTTON_HOVER_SYMBOL)) { bool userDefined=flags&TITLEBAR_BUTTON_ICON_COLOR; if(userDefined || (flags&TITLEBAR_BUTTON_COLOR && flags&TITLEBAR_BUTTON_COLOR_SYMBOL)) { QStyleOption opt; opt.init(this); if(userDefined) { versionHack+=NUM_TITLEBAR_BUTTONS; if(!active) versionHack+=NUM_TITLEBAR_BUTTONS; } opt.version=versionHack; col=QColor(QRgb(Handler()->wStyle()->pixelMetric((QStyle::PixelMetric)QtC_TitleBarIconColor, &opt, nullptr))); customCol=true; } } if (sunken) { dY++; dX++; } else if (!faded && EFFECT_NONE != effect) { QColor shadow(WINDOW_SHADOW_COLOR(effect)); shadow.setAlphaF(WINDOW_TEXT_SHADOW_ALPHA(effect)); bP.setPen(shadow); bP.drawPixmap(EFFECT_SHADOW==effect ? dX+1 : dX, dY+1, icon); } if (m_hover && !sunken && !(flags&TITLEBAR_BUTTON_COLOR) && !customCol) { if (CloseButton == type() || isTabClose) { col = CLOSE_COLOR; } else if(flags&TITLEBAR_BUTTON_USE_HOVER_COLOR) { col = Handler()->hoverCol(active); } } if (faded) { col.setAlphaF(HOVER_BUTTON_ALPHA(col)); } else { // If dont set an alpha level here, then (at least on intel) // the background colour is used! col.setAlpha(254); } bP.setPen(col); bP.drawPixmap(dX, dY, icon); } bP.end(); painter->drawPixmap(0, 0, buffer); } QBitmap IconEngine::icon(ButtonIcon icon, int size, QStyle *style) { if (size%2 == 0) --size; QBitmap bitmap(size, size); bitmap.fill(Qt::color0); QPainter p(&bitmap); p.setPen(Qt::color1); QRect r = bitmap.rect(); // line widths int lwTitleBar = 1; if (r.width() > 16) lwTitleBar = 4; else if (r.width() > 4) lwTitleBar = 2; switch (icon) { case CloseTabIcon: r.adjust(1, 1, -1, -1); case CloseIcon: { int lineWidth = 1; if (r.width() > 16) { lineWidth = 3; } else if (r.width() > 4) { lineWidth = 2; } drawObject(p, DiagonalLine, r.x(), r.y(), r.width(), lineWidth); drawObject(p, CrossDiagonalLine, r.x(), r.bottom(), r.width(), lineWidth); break; } case MaxIcon: if (Handler()->wStyle()->pixelMetric((QStyle::PixelMetric)QtC_TitleBarButtons, nullptr, nullptr) & TITLEBAR_BUTTOM_ARROW_MIN_MAX) { QStyleOption opt; opt.rect = r; opt.state = QStyle::State_Enabled | QtC_StateKWin; style->drawPrimitive(QStyle::PE_IndicatorArrowUp, &opt, &p, nullptr); } else { int lineWidth2 = 1; // frame if (r.width() > 16) { lineWidth2 = 2; } else if (r.width() > 4) { lineWidth2 = 1; } drawObject(p, HorizontalLine, r.x(), r.top(), r.width(), lwTitleBar); drawObject(p, HorizontalLine, r.x(), r.bottom() - (lineWidth2 - 1), r.width(), lineWidth2); drawObject(p, VerticalLine, r.x(), r.top(), r.height(), lineWidth2); drawObject(p, VerticalLine, r.right() - (lineWidth2 - 1), r.top(), r.height(), lineWidth2); } break; case MaxRestoreIcon: if (Handler()->wStyle()->pixelMetric((QStyle::PixelMetric)QtC_TitleBarButtons, nullptr, nullptr) & TITLEBAR_BUTTOM_ARROW_MIN_MAX) { p.drawLine(r.x() + 1, r.y(), r.x() + r.width() - 2, r.y()); p.drawLine(r.x() + 1, r.y() + r.height() - 1, r.x() + r.width() - 2, r.y() + r.height() - 1); p.drawLine(r.x(), r.y() + 1, r.x(), r.y() + r.height() - 2); p.drawLine(r.x() + r.width() - 1, r.y() + 1, r.x() + r.width() - 1, r.y() + r.height() - 2); p.drawRect(r.x() + 1, r.y() + 1, r.width() - 3, r.height() - 3); } else { int lineWidth2 = 1; // frame if (r.width() > 16) { lineWidth2 = 2; } else if (r.width() > 4) { lineWidth2 = 1; } int margin1, margin2; margin1 = margin2 = lineWidth2 * 2 + 1; if (r.width() < 8) margin1 = 1; int margin1h = margin1 - 1; int margin2h = margin2 - 1; // background window drawObject(p, HorizontalLine, r.x() + margin1h, r.top(), r.width() - margin1, lwTitleBar); drawObject(p, HorizontalLine, r.right() - margin2h, r.bottom() - (lineWidth2 - 1) - margin1, margin2h, lineWidth2); drawObject(p, VerticalLine, r.x() + margin1h, r.top(), margin2, lineWidth2); drawObject(p, VerticalLine, r.right() - (lineWidth2 - 1), r.top(), r.height() - margin1, lineWidth2); // foreground window drawObject(p, HorizontalLine, r.x(), r.top() + margin2, r.width() - margin2h, lwTitleBar); drawObject(p, HorizontalLine, r.x(), r.bottom() - (lineWidth2 - 1), r.width() - margin2h, lineWidth2); drawObject(p, VerticalLine, r.x(), r.top() + margin2, r.height(), lineWidth2); drawObject(p, VerticalLine, r.right() - (lineWidth2 - 1) - margin2h, r.top() + margin2, r.height(), lineWidth2); } break; case MinIcon: if (Handler()->wStyle()->pixelMetric((QStyle::PixelMetric)QtC_TitleBarButtons, nullptr, nullptr)&TITLEBAR_BUTTOM_ARROW_MIN_MAX) { QStyleOption opt; opt.rect = r; opt.state = QStyle::State_Enabled | QtC_StateKWin; style->drawPrimitive(QStyle::PE_IndicatorArrowDown, &opt, &p, nullptr); } else { drawObject(p, HorizontalLine, r.x(), r.bottom() - (lwTitleBar - 1), r.width(), lwTitleBar); } break; case HelpIcon: { int center = r.x()+r.width()/2; // -1; int side = r.width()/4; // paint a question mark... code is quite messy, // to be cleaned up later...! :o if (r.width() > 16) { int lineWidth = 3; // top bar drawObject(p, HorizontalLine, center - side + 3, r.y(), 2 * side - 3 - 1, lineWidth); // top bar rounding drawObject(p, CrossDiagonalLine, center - side - 1, r.y() + 5, 6, lineWidth); drawObject(p, DiagonalLine, center + side - 3, r.y(), 5, lineWidth); // right bar drawObject(p, VerticalLine, center + side + 2 - lineWidth, r.y() + 3, r.height() - (2 * lineWidth + side + 2 + 1), lineWidth); // bottom bar drawObject(p, CrossDiagonalLine, center, r.bottom() - 2 * lineWidth, side + 2, lineWidth); drawObject(p, HorizontalLine, center, r.bottom() - 3 * lineWidth + 2, lineWidth, lineWidth); // the dot drawObject(p, HorizontalLine, center, r.bottom() - (lineWidth - 1), lineWidth, lineWidth); } else if (r.width() > 8) { int lineWidth = 2; // top bar drawObject(p, HorizontalLine, center - (side - 1), r.y(), 2 * side - 1, lineWidth); // top bar rounding if (r.width() > 9) drawObject(p, CrossDiagonalLine, center - side - 1, r.y() + 3, 3, lineWidth); else drawObject(p, CrossDiagonalLine, center - side - 1, r.y() + 2, 3, lineWidth); drawObject(p, DiagonalLine, center + side - 1, r.y(), 3, lineWidth); // right bar drawObject(p, VerticalLine, center + side + 2 - lineWidth, r.y() + 2, r.height() - (2*lineWidth + side + 1), lineWidth); // bottom bar drawObject(p, CrossDiagonalLine, center, r.bottom() - 2*lineWidth + 1, side + 2, lineWidth); // the dot drawObject(p, HorizontalLine, center, r.bottom() - (lineWidth - 1), lineWidth, lineWidth); } else { int lineWidth = 1; // top bar drawObject(p, HorizontalLine, center - (side - 1), r.y(), 2*side, lineWidth); // top bar rounding drawObject(p, CrossDiagonalLine, center - side - 1, r.y() + 1, 2, lineWidth); // right bar drawObject(p, VerticalLine, center + side + 1, r.y(), r.height() - (side + 2 + 1), lineWidth); // bottom bar drawObject(p, CrossDiagonalLine, center, r.bottom() - 2, side + 2, lineWidth); // the dot drawObject(p, HorizontalLine, center, r.bottom(), 1, 1); } break; } case NotOnAllDesktopsIcon: { r.adjust(1, 1, -1, -1); int lwMark = (r.width() - 1)/2; if (lwMark < 1) lwMark = 3; drawObject(p, HorizontalLine, r.x(), r.y(), lwMark, lwMark); drawObject(p, HorizontalLine, r.x() + (r.width() - lwMark), r.y(), lwMark, lwMark); drawObject(p, HorizontalLine, r.x() + (r.width() - lwMark), r.y() + (r.height() - lwMark), lwMark, lwMark); drawObject(p, HorizontalLine, r.x(), r.y() + (r.height() - lwMark), lwMark, lwMark); break; } case OnAllDesktopsIcon: { int lwMark = r.width() - lwTitleBar*2 - 2; if (lwMark < 1) lwMark = 3; drawObject(p, HorizontalLine, r.x() + (r.width() - lwMark)/2, r.y() + (r.height() - lwMark)/2, lwMark, lwMark); break; } case NoKeepAboveIcon: case KeepAboveIcon: { QStyleOption opt; opt.rect=r.adjusted(0, -(1 + lwTitleBar), 0, 0); opt.rect.adjust(2, 2, -2, -2); opt.state=QStyle::State_Enabled|QtC_StateKWin; opt.rect.adjust(0, -1, 0, -1); if(NoKeepAboveIcon==icon) { int w=r.width()/3; if(0==w%2) w++; drawObject(p, HorizontalLine, r.x() + (r.width() - w)/2, r.y() + 1, w, 2); } else style->drawPrimitive(QStyle::PE_IndicatorArrowUp, &opt, &p, nullptr); opt.rect.adjust(0, 4, 0, 4); style->drawPrimitive(QStyle::PE_IndicatorArrowUp, &opt, &p, nullptr); break; } case NoKeepBelowIcon: case KeepBelowIcon: { QStyleOption opt; opt.rect=r.adjusted(0, -(1 + lwTitleBar), 0, 0); opt.rect.adjust(2, 2, -2, -2); opt.state=QStyle::State_Enabled|QtC_StateKWin; opt.rect.adjust(0, -1, 0, -1); style->drawPrimitive(QStyle::PE_IndicatorArrowDown, &opt, &p, nullptr); opt.rect.adjust(0, 4, 0, 4); if(NoKeepBelowIcon==icon) { int w=r.width()/3; if(0==w%2) w++; drawObject(p, HorizontalLine, r.x() + (r.width() - w)/2, r.y() + r.height() - 3, w, 2); } else style->drawPrimitive(QStyle::PE_IndicatorArrowDown, &opt, &p, nullptr); break; } case UnShadeIcon: case ShadeIcon: { QStyleOption opt; //opt.init(btn); opt.rect=r.adjusted(0, -(1 + lwTitleBar), 0, 0); opt.rect.adjust(2, 2, -2, -2); if(UnShadeIcon==icon) opt.rect.adjust(0, -1, 0, -1); opt.state=QStyle::State_Enabled|QtC_StateKWin; //opt.palette.setColor(QPalette::Button, Qt::red); style->drawPrimitive(icon == ShadeIcon ? QStyle::PE_IndicatorArrowDown : QStyle::PE_IndicatorArrowUp, &opt, &p, nullptr); drawObject(p, HorizontalLine, r.x() + 1, r.bottom() - (lwTitleBar - 1), r.width() - 2, lwTitleBar); break; } case MenuIcon: { int offset=(r.height() - 7)/2; for (int i = 0;i < 3;i++) drawObject(p, HorizontalLine, r.x() + 1, r.y() + offset + (i*3), r.width() - 2, 1); break; } default: break; } p.end(); bitmap.setMask(bitmap); return bitmap; } void IconEngine::drawObject(QPainter &p, Object object, int x, int y, int length, int lineWidth) { switch(object) { case DiagonalLine: if (lineWidth <= 1) { for (int i = 0;i < length;i++) { p.drawPoint(x + i, y + i); } } else if (lineWidth <= 2) { for (int i = 0;i < length;i++) { p.drawPoint(x + i, y + i); } for (int i = 0;i < length - 1;i++) { p.drawPoint(x + 1 + i, y + i); p.drawPoint(x + i, y + 1 + i); } } else { for (int i = 1;i < length - 1;i++) { p.drawPoint(x + i, y + i); } for (int i = 0;i < length - 1;i++) { p.drawPoint(x + 1 + i, y + i); p.drawPoint(x + i, y + 1 + i); } for (int i = 0;i < (length - 2);i++) { p.drawPoint(x + 2 + i, y + i); p.drawPoint(x + i, y + 2 + i); } } break; case CrossDiagonalLine: if (lineWidth <= 1) { for (int i = 0;i < length;i++) { p.drawPoint(x + i, y - i); } } else if (lineWidth <= 2) { for (int i = 0;i < length;i++) { p.drawPoint(x + i, y - i); } for (int i = 0;i < length - 1;i++) { p.drawPoint(x + 1 + i, y - i); p.drawPoint(x + i, y - 1 - i); } } else { for (int i = 1;i < length - 1;i++) { p.drawPoint(x + i, y - i); } for (int i = 0;i < length - 1;i++) { p.drawPoint(x + 1 + i, y - i); p.drawPoint(x + i, y - 1 - i); } for (int i = 0;i < length - 2;i++) { p.drawPoint(x + 2 + i, y - i); p.drawPoint(x + i, y - 2 - i); } } break; case HorizontalLine: for (int i = 0;i < lineWidth;i++) { p.drawLine(x, y + i, x + length - 1, y + i); } break; case VerticalLine: for (int i = 0;i < lineWidth;i++) { p.drawLine(x + i, y, x + i, y + length - 1); } default: break; } } } }