Warning, file /plasma/oxygen/kstyle/oxygenframeshadow.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 //////////////////////////////////////////////////////////////////////////////
0002 // oxygenframeshadow.h
0003 // handle sunken frames' shadows
0004 // -------------------
0005 //
0006 // SPDX-FileCopyrightText: 2010 Hugo Pereira Da Costa <hugo.pereira@free.fr>
0007 //
0008 // Largely inspired from skulpture widget style
0009 // SPDX-FileCopyrightText: 2007-2009 Christoph Feck <christoph@maxiom.de>
0010 //
0011 // SPDX-License-Identifier: MIT
0012 //////////////////////////////////////////////////////////////////////////////
0013 
0014 #include "oxygenframeshadow.h"
0015 
0016 #include <QAbstractScrollArea>
0017 #include <QApplication>
0018 #include <QDebug>
0019 #include <QFrame>
0020 #include <QMouseEvent>
0021 #include <QPainter>
0022 #include <QSplitter>
0023 
0024 #include <KColorUtils>
0025 
0026 namespace Oxygen
0027 {
0028 
0029 //____________________________________________________________________________________
0030 bool FrameShadowFactory::registerWidget(QWidget *widget, StyleHelper &helper)
0031 {
0032     if (!widget)
0033         return false;
0034     if (isRegistered(widget))
0035         return false;
0036 
0037     // check whether widget is a frame, and has the proper shape
0038     bool accepted = false;
0039     bool flat = false;
0040 
0041     // cast to frame and check
0042     QFrame *frame(qobject_cast<QFrame *>(widget));
0043     if (frame) {
0044         // also do not install on QSplitter
0045         /*
0046         due to Qt, splitters are set with a frame style that matches the condition below,
0047         though no shadow should be installed, obviously
0048         */
0049         if (qobject_cast<QSplitter *>(widget))
0050             return false;
0051 
0052         // further checks on frame shape, and parent
0053         if (frame->frameStyle() == (QFrame::StyledPanel | QFrame::Sunken))
0054             accepted = true;
0055         else if (widget->parent() && widget->parent()->inherits("QComboBoxPrivateContainer")) {
0056             accepted = true;
0057             flat = true;
0058         }
0059 
0060     } else if (widget->inherits("KTextEditor::View"))
0061         accepted = true;
0062 
0063     if (!accepted)
0064         return false;
0065 
0066     // make sure that the widget is not embedded into a KHTMLView
0067     QWidget *parent(widget->parentWidget());
0068     while (parent && !parent->isTopLevel()) {
0069         if (parent->inherits("KHTMLView"))
0070             return false;
0071         parent = parent->parentWidget();
0072     }
0073 
0074     // store in set
0075     _registeredWidgets.insert(widget);
0076 
0077     // catch object destruction
0078     connect(widget, SIGNAL(destroyed(QObject *)), SLOT(widgetDestroyed(QObject *)));
0079 
0080     // install shadow
0081     installShadows(widget, helper, flat);
0082 
0083     return true;
0084 }
0085 
0086 //____________________________________________________________________________________
0087 void FrameShadowFactory::unregisterWidget(QWidget *widget)
0088 {
0089     if (!isRegistered(widget))
0090         return;
0091     _registeredWidgets.remove(widget);
0092     removeShadows(widget);
0093 }
0094 
0095 //____________________________________________________________________________________
0096 bool FrameShadowFactory::eventFilter(QObject *object, QEvent *event)
0097 {
0098     switch (event->type()) {
0099     // TODO: possibly implement ZOrderChange event, to make sure that
0100     // the shadow is always painted on top
0101     case QEvent::ZOrderChange: {
0102         raiseShadows(object);
0103         break;
0104     }
0105 
0106     case QEvent::Show:
0107         updateShadowsGeometry(object);
0108         update(object);
0109         break;
0110 
0111     case QEvent::Resize:
0112         updateShadowsGeometry(object);
0113         break;
0114 
0115     default:
0116         break;
0117     }
0118 
0119     return QObject::eventFilter(object, event);
0120 }
0121 
0122 //____________________________________________________________________________________
0123 void FrameShadowFactory::installShadows(QWidget *widget, StyleHelper &helper, bool flat)
0124 {
0125     removeShadows(widget);
0126 
0127     widget->installEventFilter(this);
0128 
0129     widget->installEventFilter(&_addEventFilter);
0130     if (!flat) {
0131         installShadow(widget, helper, ShadowAreaLeft);
0132         installShadow(widget, helper, ShadowAreaRight);
0133     }
0134 
0135     installShadow(widget, helper, ShadowAreaTop, flat);
0136     installShadow(widget, helper, ShadowAreaBottom, flat);
0137     widget->removeEventFilter(&_addEventFilter);
0138 }
0139 
0140 //____________________________________________________________________________________
0141 void FrameShadowFactory::removeShadows(QWidget *widget)
0142 {
0143     widget->removeEventFilter(this);
0144 
0145     const QList<QObject *> children = widget->children();
0146     for (QObject *child : children) {
0147         if (FrameShadowBase *shadow = qobject_cast<FrameShadowBase *>(child)) {
0148             shadow->hide();
0149             shadow->setParent(nullptr);
0150             shadow->deleteLater();
0151         }
0152     }
0153 }
0154 
0155 //____________________________________________________________________________________
0156 void FrameShadowFactory::updateShadowsGeometry(QObject *object) const
0157 {
0158     const QList<QObject *> children = object->children();
0159     for (QObject *child : children) {
0160         if (FrameShadowBase *shadow = qobject_cast<FrameShadowBase *>(child)) {
0161             shadow->updateGeometry();
0162         }
0163     }
0164 }
0165 
0166 //____________________________________________________________________________________
0167 void FrameShadowFactory::updateShadowsGeometry(const QObject *object, QRect rect) const
0168 {
0169     const QList<QObject *> children = object->children();
0170     for (QObject *child : children) {
0171         if (FrameShadowBase *shadow = qobject_cast<FrameShadowBase *>(child)) {
0172             shadow->updateGeometry(rect);
0173         }
0174     }
0175 }
0176 
0177 //____________________________________________________________________________________
0178 void FrameShadowFactory::raiseShadows(QObject *object) const
0179 {
0180     const QList<QObject *> children = object->children();
0181     for (QObject *child : children) {
0182         if (FrameShadowBase *shadow = qobject_cast<FrameShadowBase *>(child)) {
0183             shadow->raise();
0184         }
0185     }
0186 }
0187 
0188 //____________________________________________________________________________________
0189 void FrameShadowFactory::update(QObject *object) const
0190 {
0191     const QList<QObject *> children = object->children();
0192     for (QObject *child : children) {
0193         if (FrameShadowBase *shadow = qobject_cast<FrameShadowBase *>(child)) {
0194             shadow->update();
0195         }
0196     }
0197 }
0198 
0199 //____________________________________________________________________________________
0200 void FrameShadowFactory::setHasContrast(const QWidget *widget, bool value) const
0201 {
0202     const QList<QObject *> children = widget->children();
0203     for (QObject *child : children) {
0204         if (FrameShadowBase *shadow = qobject_cast<FrameShadowBase *>(child)) {
0205             shadow->setHasContrast(value);
0206         }
0207     }
0208 }
0209 
0210 //____________________________________________________________________________________
0211 void FrameShadowFactory::updateState(const QWidget *widget, bool focus, bool hover, qreal opacity, AnimationMode mode) const
0212 {
0213     const QList<QObject *> children = widget->children();
0214     for (QObject *child : children) {
0215         if (FrameShadowBase *shadow = qobject_cast<FrameShadowBase *>(child)) {
0216             shadow->updateState(focus, hover, opacity, mode);
0217         }
0218     }
0219 }
0220 
0221 //____________________________________________________________________________________
0222 void FrameShadowFactory::installShadow(QWidget *widget, StyleHelper &helper, ShadowArea area, bool flat) const
0223 {
0224     FrameShadowBase *shadow(nullptr);
0225     if (flat)
0226         shadow = new FlatFrameShadow(area, helper);
0227     else
0228         shadow = new SunkenFrameShadow(area, helper);
0229     shadow->setParent(widget);
0230     shadow->hide();
0231 }
0232 
0233 //____________________________________________________________________________________
0234 void FrameShadowFactory::widgetDestroyed(QObject *object)
0235 {
0236     _registeredWidgets.remove(object);
0237 }
0238 
0239 //____________________________________________________________________________________
0240 void FrameShadowBase::init()
0241 {
0242     setAttribute(Qt::WA_OpaquePaintEvent, false);
0243 
0244     setFocusPolicy(Qt::NoFocus);
0245     setAttribute(Qt::WA_TransparentForMouseEvents, true);
0246     setContextMenuPolicy(Qt::NoContextMenu);
0247 
0248     // grab viewport widget
0249     QWidget *viewport(FrameShadowBase::viewport());
0250     if (!viewport && parentWidget()) {
0251         viewport = parentWidget();
0252     }
0253 
0254     // set cursor from viewport
0255     if (viewport)
0256         setCursor(viewport->cursor());
0257 }
0258 
0259 //____________________________________________________________________________________
0260 QWidget *FrameShadowBase::viewport(void) const
0261 {
0262     if (!parentWidget())
0263         return nullptr;
0264     else if (QAbstractScrollArea *widget = qobject_cast<QAbstractScrollArea *>(parentWidget())) {
0265         return widget->viewport();
0266 
0267     } else
0268         return nullptr;
0269 }
0270 
0271 //____________________________________________________________________________________
0272 void SunkenFrameShadow::updateGeometry(QRect rect)
0273 {
0274     // show on first call
0275     if (isHidden())
0276         show();
0277 
0278     // store offsets between passed rect and parent widget rect
0279     QRect parentRect(parentWidget()->contentsRect());
0280     setMargins(
0281         QMargins(rect.left() - parentRect.left(), rect.top() - parentRect.top(), rect.right() - parentRect.right(), rect.bottom() - parentRect.bottom()));
0282 
0283     // adjust geometry to take out part that is not rendered anyway
0284     rect.adjust(1, 1, -1, -1);
0285 
0286     // adjust geometry
0287     const int shadowSize(3);
0288     switch (shadowArea()) {
0289     case ShadowAreaTop:
0290         rect.setHeight(shadowSize);
0291         break;
0292 
0293     case ShadowAreaBottom:
0294         rect.setTop(rect.bottom() - shadowSize + 1);
0295         break;
0296 
0297     case ShadowAreaLeft:
0298         rect.setWidth(shadowSize);
0299         rect.adjust(0, shadowSize, 0, -shadowSize);
0300         break;
0301 
0302     case ShadowAreaRight:
0303         rect.setLeft(rect.right() - shadowSize + 1);
0304         rect.adjust(0, shadowSize, 0, -shadowSize);
0305         break;
0306 
0307     default:
0308         return;
0309     }
0310 
0311     setGeometry(rect);
0312 }
0313 
0314 //____________________________________________________________________________________
0315 void SunkenFrameShadow::updateState(bool focus, bool hover, qreal opacity, AnimationMode mode)
0316 {
0317     bool changed(false);
0318     if (_hasFocus != focus) {
0319         _hasFocus = focus;
0320         changed |= true;
0321     }
0322     if (_mouseOver != hover) {
0323         _mouseOver = hover;
0324         changed |= !_hasFocus;
0325     }
0326     if (_mode != mode) {
0327         _mode = mode;
0328         changed |= (_mode == AnimationNone) || (_mode == AnimationFocus) || (_mode == AnimationHover && !_hasFocus);
0329     }
0330 
0331     if (_opacity != opacity) {
0332         _opacity = opacity;
0333         changed |= (_mode != AnimationNone);
0334     }
0335     if (changed) {
0336         if (QWidget *viewport = this->viewport()) {
0337             // need to disable viewport updates to avoid some redundant painting
0338             // besides it fixes one visual glitch (from Qt) in QTableViews
0339             viewport->setUpdatesEnabled(false);
0340             update();
0341             viewport->setUpdatesEnabled(true);
0342 
0343         } else
0344             update();
0345     }
0346 }
0347 
0348 //____________________________________________________________________________________
0349 void SunkenFrameShadow::paintEvent(QPaintEvent *event)
0350 {
0351     // this fixes shadows in frames that change frameStyle() after polish()
0352     if (QFrame *frame = qobject_cast<QFrame *>(parentWidget())) {
0353         if (frame->frameStyle() != (QFrame::StyledPanel | QFrame::Sunken))
0354             return;
0355     }
0356 
0357     const QRect parentRect(parentWidget()->contentsRect().translated(mapFromParent(QPoint(0, 0))));
0358     const QRect rect(parentRect.adjusted(margins().left(), margins().top(), margins().right(), margins().bottom()));
0359 
0360     // render
0361     QPainter painter(this);
0362     painter.setClipRegion(event->region());
0363 
0364     StyleOptions options(HoleOutline);
0365     if (_hasFocus)
0366         options |= Focus;
0367     if (_mouseOver)
0368         options |= Hover;
0369     if (hasContrast())
0370         options |= HoleContrast;
0371     _helper.renderHole(&painter, palette().color(QPalette::Window), rect, options, _opacity, _mode, TileSet::Ring);
0372 
0373     return;
0374 }
0375 
0376 //____________________________________________________________________________________
0377 void FlatFrameShadow::updateGeometry()
0378 {
0379     if (QWidget *widget = parentWidget())
0380         updateGeometry(widget->contentsRect());
0381 }
0382 
0383 //____________________________________________________________________________________
0384 void FlatFrameShadow::updateGeometry(QRect rect)
0385 {
0386     // show on first call
0387     if (isHidden())
0388         show();
0389 
0390     // store offsets between passed rect and parent widget rect
0391     QRect parentRect(parentWidget()->contentsRect());
0392     setMargins(
0393         QMargins(rect.left() - parentRect.left(), rect.top() - parentRect.top(), rect.right() - parentRect.right(), rect.bottom() - parentRect.bottom()));
0394 
0395     const int shadowSize(3);
0396     switch (shadowArea()) {
0397     case ShadowAreaTop:
0398         rect.setHeight(shadowSize);
0399         break;
0400 
0401     case ShadowAreaBottom:
0402         rect.setTop(rect.bottom() - shadowSize + 1);
0403         break;
0404 
0405     default:
0406         return;
0407     }
0408 
0409     setGeometry(rect);
0410 }
0411 
0412 //____________________________________________________________________________________
0413 void FlatFrameShadow::paintEvent(QPaintEvent *event)
0414 {
0415     // this fixes shadows in frames that change frameStyle() after polish()
0416     if (QFrame *frame = qobject_cast<QFrame *>(parentWidget())) {
0417         if (frame->frameStyle() != QFrame::NoFrame)
0418             return;
0419     }
0420 
0421     const QWidget *parent(parentWidget());
0422     const QRect parentRect(parent->contentsRect());
0423     const QRect rect(parentRect.adjusted(margins().left(), margins().top(), margins().right(), margins().bottom()));
0424 
0425     QPixmap pixmap(_helper.highDpiPixmap(size()));
0426     {
0427         pixmap.fill(Qt::transparent);
0428         QPainter painter(&pixmap);
0429         painter.setClipRegion(event->region());
0430         painter.setRenderHints(QPainter::Antialiasing);
0431         painter.translate(-geometry().topLeft());
0432         painter.setCompositionMode(QPainter::CompositionMode_DestinationOver);
0433         painter.setPen(Qt::NoPen);
0434         _helper.renderMenuBackground(&painter, geometry(), parent, parent->palette());
0435 
0436         // mask
0437         painter.setCompositionMode(QPainter::CompositionMode_DestinationOut);
0438         painter.setBrush(Qt::black);
0439         painter.drawRoundedRect(QRectF(rect), 2.5, 2.5);
0440     }
0441 
0442     QPainter painter(this);
0443     painter.setClipRegion(event->region());
0444     painter.fillRect(rect, Qt::transparent);
0445     painter.drawPixmap(QPoint(0, 0), pixmap);
0446 
0447     return;
0448 }
0449 }