File indexing completed on 2024-04-28 05:26:20
0001 /* 0002 * SPDX-FileCopyrightText: 2014 Hugo Pereira Da Costa <hugo.pereira@free.fr> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "breezeframeshadow.h" 0008 0009 #include "breezemetrics.h" 0010 0011 #include <QAbstractScrollArea> 0012 #include <QApplication> 0013 #include <QDebug> 0014 #include <QFrame> 0015 #include <QMouseEvent> 0016 #include <QPainter> 0017 #include <QSplitter> 0018 0019 #include <KColorUtils> 0020 0021 namespace Breeze 0022 { 0023 //____________________________________________________________________________________ 0024 bool FrameShadowFactory::registerWidget(QWidget *widget, Helper &helper) 0025 { 0026 if (!widget) { 0027 return false; 0028 } 0029 if (isRegistered(widget)) { 0030 return false; 0031 } 0032 0033 // check whether widget is a frame, and has the proper shape 0034 bool accepted = false; 0035 0036 // cast to frame and check 0037 QFrame *frame(qobject_cast<QFrame *>(widget)); 0038 if (frame) { 0039 // also do not install on QSplitter 0040 /* 0041 due to Qt, splitters are set with a frame style that matches the condition below, 0042 though no shadow should be installed, obviously 0043 */ 0044 if (qobject_cast<QSplitter *>(widget)) { 0045 return false; 0046 } 0047 0048 // further checks on frame shape, and parent 0049 if (frame->frameStyle() == (QFrame::StyledPanel | QFrame::Sunken)) { 0050 accepted = true; 0051 } 0052 0053 } else if (widget->inherits("KTextEditor::View")) { 0054 accepted = true; 0055 } 0056 0057 if (!accepted) { 0058 return false; 0059 } 0060 0061 // make sure that the widget is not embedded into a KHTMLView 0062 QWidget *parent(widget->parentWidget()); 0063 while (parent && !parent->isWindow()) { 0064 if (parent->inherits("KHTMLView")) { 0065 return false; 0066 } 0067 parent = parent->parentWidget(); 0068 } 0069 0070 // store in set 0071 _registeredWidgets.insert(widget); 0072 0073 // catch object destruction 0074 connect(widget, &QObject::destroyed, this, &FrameShadowFactory::widgetDestroyed); 0075 0076 // install shadow 0077 installShadows(widget, helper); 0078 0079 return true; 0080 } 0081 0082 //____________________________________________________________________________________ 0083 void FrameShadowFactory::unregisterWidget(QWidget *widget) 0084 { 0085 if (!isRegistered(widget)) { 0086 return; 0087 } 0088 _registeredWidgets.remove(widget); 0089 removeShadows(widget); 0090 } 0091 0092 //____________________________________________________________________________________ 0093 bool FrameShadowFactory::eventFilter(QObject *object, QEvent *event) 0094 { 0095 switch (event->type()) { 0096 // TODO: possibly implement ZOrderChange event, to make sure that 0097 // the shadow is always painted on top 0098 case QEvent::ZOrderChange: { 0099 raiseShadows(object); 0100 break; 0101 } 0102 0103 default: 0104 break; 0105 } 0106 0107 return QObject::eventFilter(object, event); 0108 } 0109 0110 //____________________________________________________________________________________ 0111 void FrameShadowFactory::installShadows(QWidget *widget, Helper &helper) 0112 { 0113 removeShadows(widget); 0114 0115 widget->installEventFilter(this); 0116 0117 widget->installEventFilter(&_addEventFilter); 0118 installShadow(widget, helper, SideTop); 0119 installShadow(widget, helper, SideBottom); 0120 widget->removeEventFilter(&_addEventFilter); 0121 } 0122 0123 //____________________________________________________________________________________ 0124 void FrameShadowFactory::removeShadows(QWidget *widget) 0125 { 0126 widget->removeEventFilter(this); 0127 0128 const QList<QObject *> children = widget->children(); 0129 for (QObject *child : children) { 0130 if (FrameShadow *shadow = qobject_cast<FrameShadow *>(child)) { 0131 shadow->hide(); 0132 shadow->setParent(nullptr); 0133 shadow->deleteLater(); 0134 } 0135 } 0136 } 0137 0138 //____________________________________________________________________________________ 0139 void FrameShadowFactory::updateShadowsGeometry(const QObject *object, QRect rect) const 0140 { 0141 const QList<QObject *> &children = object->children(); 0142 for (QObject *child : children) { 0143 if (FrameShadow *shadow = qobject_cast<FrameShadow *>(child)) { 0144 shadow->updateGeometry(rect); 0145 } 0146 } 0147 } 0148 0149 //____________________________________________________________________________________ 0150 void FrameShadowFactory::raiseShadows(QObject *object) const 0151 { 0152 const QList<QObject *> &children = object->children(); 0153 for (QObject *child : children) { 0154 if (FrameShadow *shadow = qobject_cast<FrameShadow *>(child)) { 0155 shadow->raise(); 0156 } 0157 } 0158 } 0159 0160 //____________________________________________________________________________________ 0161 void FrameShadowFactory::update(QObject *object) const 0162 { 0163 const QList<QObject *> &children = object->children(); 0164 for (QObject *child : children) { 0165 if (FrameShadow *shadow = qobject_cast<FrameShadow *>(child)) { 0166 shadow->update(); 0167 } 0168 } 0169 } 0170 0171 //____________________________________________________________________________________ 0172 void FrameShadowFactory::updateState(const QWidget *widget, bool focus, bool hover, qreal opacity, AnimationMode mode) const 0173 { 0174 const QList<QObject *> &children = widget->children(); 0175 for (QObject *child : children) { 0176 if (FrameShadow *shadow = qobject_cast<FrameShadow *>(child)) { 0177 shadow->updateState(focus, hover, opacity, mode); 0178 } 0179 } 0180 } 0181 0182 //____________________________________________________________________________________ 0183 void FrameShadowFactory::installShadow(QWidget *widget, Helper &helper, Side area) const 0184 { 0185 FrameShadow *shadow(nullptr); 0186 shadow = new FrameShadow(area, helper); 0187 shadow->setParent(widget); 0188 shadow->hide(); 0189 } 0190 0191 //____________________________________________________________________________________ 0192 void FrameShadowFactory::widgetDestroyed(QObject *object) 0193 { 0194 _registeredWidgets.remove(object); 0195 } 0196 0197 //____________________________________________________________________________________ 0198 FrameShadow::FrameShadow(Side area, Helper &helper) 0199 : _helper(helper) 0200 , _area(area) 0201 { 0202 setAttribute(Qt::WA_OpaquePaintEvent, false); 0203 0204 setFocusPolicy(Qt::NoFocus); 0205 setAttribute(Qt::WA_TransparentForMouseEvents, true); 0206 setContextMenuPolicy(Qt::NoContextMenu); 0207 0208 // grab viewport widget 0209 QWidget *viewport(this->viewport()); 0210 0211 // set cursor from viewport 0212 if (viewport) { 0213 setCursor(viewport->cursor()); 0214 } 0215 } 0216 0217 //____________________________________________________________________________________ 0218 void FrameShadow::updateGeometry(QRect rect) 0219 { 0220 // show on first call 0221 if (isHidden()) { 0222 show(); 0223 } 0224 0225 // store offsets between passed rect and parent widget rect 0226 QRect parentRect(parentWidget()->contentsRect()); 0227 _margins = QMargins(rect.left() - parentRect.left(), rect.top() - parentRect.top(), rect.right() - parentRect.right(), rect.bottom() - parentRect.bottom()); 0228 0229 // for efficiency, take out the part for which nothing is rendered 0230 rect.adjust(1, 1, -1, -1); 0231 0232 // adjust geometry 0233 const int shadowSize(Metrics::Frame_FrameRadius); 0234 switch (_area) { 0235 case SideTop: 0236 rect.setHeight(shadowSize); 0237 break; 0238 0239 case SideBottom: 0240 rect.setTop(rect.bottom() - shadowSize + 1); 0241 break; 0242 0243 case SideLeft: 0244 rect.setWidth(shadowSize); 0245 rect.adjust(0, shadowSize, 0, -shadowSize); 0246 break; 0247 0248 case SideRight: 0249 rect.setLeft(rect.right() - shadowSize + 1); 0250 rect.adjust(0, shadowSize, 0, -shadowSize); 0251 break; 0252 0253 default: 0254 return; 0255 } 0256 0257 setGeometry(rect); 0258 } 0259 0260 //____________________________________________________________________________________ 0261 void FrameShadow::updateState(bool focus, bool hover, qreal opacity, AnimationMode mode) 0262 { 0263 bool changed(false); 0264 if (_hasFocus != focus) { 0265 _hasFocus = focus; 0266 changed |= true; 0267 } 0268 if (_mouseOver != hover) { 0269 _mouseOver = hover; 0270 changed |= !_hasFocus; 0271 } 0272 if (_mode != mode) { 0273 _mode = mode; 0274 changed |= (_mode == AnimationNone) || (_mode == AnimationFocus) || (_mode == AnimationHover && !_hasFocus); 0275 } 0276 0277 if (_opacity != opacity) { 0278 _opacity = opacity; 0279 changed |= (_mode != AnimationNone); 0280 } 0281 if (changed) { 0282 if (QWidget *viewport = this->viewport()) { 0283 // need to disable viewport updates to avoid some redundant painting 0284 // besides it fixes one visual glitch (from Qt) in QTableViews 0285 viewport->setUpdatesEnabled(false); 0286 update(); 0287 viewport->setUpdatesEnabled(true); 0288 0289 } else { 0290 update(); 0291 } 0292 } 0293 } 0294 0295 //____________________________________________________________________________________ 0296 void FrameShadow::paintEvent(QPaintEvent *event) 0297 { 0298 // this fixes shadows in frames that change frameStyle() after polish() 0299 if (QFrame *frame = qobject_cast<QFrame *>(parentWidget())) { 0300 if (frame->frameStyle() != (QFrame::StyledPanel | QFrame::Sunken)) { 0301 return; 0302 } 0303 } 0304 0305 const QRect parentRect(parentWidget()->contentsRect().translated(mapFromParent(QPoint(0, 0)))); 0306 const QRect rect(parentRect.adjusted(_margins.left(), _margins.top(), _margins.right(), _margins.bottom())); 0307 0308 // render 0309 QPainter painter(this); 0310 painter.setClipRegion(event->region()); 0311 painter.setRenderHint(QPainter::Antialiasing); 0312 0313 const QColor outline(_helper.frameOutlineColor(palette(), _mouseOver, _hasFocus, _opacity, _mode)); 0314 painter.setCompositionMode(QPainter::CompositionMode_SourceOver); 0315 _helper.renderFrame(&painter, rect, QColor(), outline); 0316 } 0317 0318 //____________________________________________________________________________________ 0319 QWidget *FrameShadow::viewport() const 0320 { 0321 if (!parentWidget()) { 0322 return nullptr; 0323 } else if (QAbstractScrollArea *widget = qobject_cast<QAbstractScrollArea *>(parentWidget())) { 0324 return widget->viewport(); 0325 0326 } else { 0327 return nullptr; 0328 } 0329 } 0330 0331 }