File indexing completed on 2024-04-14 15:37:21
0001 /* 0002 * Copyright 2018 Michail Vourlakos <mvourlakos@gmail.com> 0003 * 0004 * This file is part of Latte-Dock 0005 * 0006 * Latte-Dock is free software; you can redistribute it and/or 0007 * modify it under the terms of the GNU General Public License as 0008 * published by the Free Software Foundation; either version 2 of 0009 * the License, or (at your option) any later version. 0010 * 0011 * Latte-Dock is distributed in the hope that it will be useful, 0012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0014 * GNU General Public License for more details. 0015 * 0016 * You should have received a copy of the GNU General Public License 0017 * along with this program. If not, see <http://www.gnu.org/licenses/>. 0018 */ 0019 0020 #include "effects.h" 0021 0022 // local 0023 #include "panelshadows_p.h" 0024 #include "view.h" 0025 #include "settings/primaryconfigview.h" 0026 #include "../../liblatte2/types.h" 0027 0028 // Qt 0029 #include <QRegion> 0030 0031 // KDE 0032 #include <KWindowEffects> 0033 #include <KWindowSystem> 0034 0035 namespace Latte { 0036 namespace ViewPart { 0037 0038 Effects::Effects(Latte::View *parent) 0039 : QObject(parent), 0040 m_view(parent) 0041 { 0042 init(); 0043 } 0044 0045 Effects::~Effects() 0046 { 0047 } 0048 0049 void Effects::init() 0050 { 0051 connect(this, &Effects::backgroundOpacityChanged, this, &Effects::updateEffects); 0052 connect(this, &Effects::drawEffectsChanged, this, &Effects::updateEffects); 0053 connect(this, &Effects::rectChanged, this, &Effects::updateEffects); 0054 connect(this, &Effects::settingsMaskSubtractedChanged, this, &Effects::updateMask); 0055 0056 connect(this, &Effects::drawShadowsChanged, this, [&]() { 0057 if (m_view->behaveAsPlasmaPanel()) { 0058 updateEnabledBorders(); 0059 } 0060 }); 0061 0062 connect(m_view, &Latte::View::alignmentChanged, this, &Effects::updateEnabledBorders); 0063 connect(m_view, &Latte::View::behaveAsPlasmaPanelChanged, this, &Effects::updateEffects); 0064 connect(m_view, &Latte::View::behaveAsPlasmaPanelChanged, this, &Effects::updateShadows); 0065 connect(m_view, &Latte::View::configWindowGeometryChanged, this, &Effects::updateMask); 0066 0067 connect(&m_theme, &Plasma::Theme::themeChanged, this, [&]() { 0068 auto background = m_background; 0069 m_background = new Plasma::FrameSvg(this); 0070 0071 if (background) { 0072 background->deleteLater(); 0073 } 0074 0075 if (m_background->imagePath() != "widgets/panel-background") { 0076 m_background->setImagePath(QStringLiteral("widgets/panel-background")); 0077 } 0078 0079 updateBackgroundContrastValues(); 0080 updateEffects(); 0081 }); 0082 } 0083 0084 bool Effects::animationsBlocked() const 0085 { 0086 return m_animationsBlocked; 0087 } 0088 0089 void Effects::setAnimationsBlocked(bool blocked) 0090 { 0091 if (m_animationsBlocked == blocked) { 0092 return; 0093 } 0094 0095 m_animationsBlocked = blocked; 0096 emit animationsBlockedChanged(); 0097 } 0098 0099 bool Effects::drawShadows() const 0100 { 0101 return m_drawShadows; 0102 } 0103 0104 void Effects::setDrawShadows(bool draw) 0105 { 0106 if (m_drawShadows == draw) { 0107 return; 0108 } 0109 0110 m_drawShadows = draw; 0111 0112 if (m_view->behaveAsPlasmaPanel() && m_drawShadows) { 0113 PanelShadows::self()->addWindow(m_view, m_enabledBorders); 0114 } else { 0115 PanelShadows::self()->removeWindow(m_view); 0116 } 0117 0118 emit drawShadowsChanged(); 0119 } 0120 0121 bool Effects::drawEffects() const 0122 { 0123 return m_drawEffects; 0124 } 0125 0126 void Effects::setDrawEffects(bool draw) 0127 { 0128 if (m_drawEffects == draw) { 0129 return; 0130 } 0131 0132 m_drawEffects = draw; 0133 0134 emit drawEffectsChanged(); 0135 } 0136 0137 bool Effects::forceDrawCenteredBorders() const 0138 { 0139 return m_forceDrawCenteredBorders; 0140 } 0141 0142 void Effects::setForceDrawCenteredBorders(bool draw) 0143 { 0144 if (m_forceDrawCenteredBorders == draw) { 0145 return; 0146 } 0147 0148 m_forceDrawCenteredBorders = draw; 0149 } 0150 0151 int Effects::backgroundOpacity() const 0152 { 0153 return m_backgroundOpacity; 0154 } 0155 0156 void Effects::setBackgroundOpacity(int opacity) 0157 { 0158 if (m_backgroundOpacity == opacity) { 0159 return; 0160 } 0161 0162 m_backgroundOpacity = opacity; 0163 0164 updateBackgroundContrastValues(); 0165 emit backgroundOpacityChanged(); 0166 } 0167 0168 int Effects::editShadow() const 0169 { 0170 return m_editShadow; 0171 } 0172 0173 void Effects::setEditShadow(int shadow) 0174 { 0175 if (m_editShadow == shadow) { 0176 return; 0177 } 0178 0179 m_editShadow = shadow; 0180 emit editShadowChanged(); 0181 } 0182 0183 int Effects::innerShadow() const 0184 { 0185 return m_innerShadow; 0186 } 0187 0188 void Effects::setInnerShadow(int shadow) 0189 { 0190 if (m_innerShadow == shadow) 0191 return; 0192 0193 m_innerShadow = shadow; 0194 0195 emit innerShadowChanged(); 0196 } 0197 0198 bool Effects::settingsMaskSubtracted() const 0199 { 0200 return m_settingsMaskSubtracted; 0201 } 0202 0203 void Effects::setSettingsMaskSubtracted(bool enabled) 0204 { 0205 if (m_settingsMaskSubtracted == enabled) { 0206 return; 0207 } 0208 0209 m_settingsMaskSubtracted = enabled; 0210 0211 emit settingsMaskSubtractedChanged(); 0212 } 0213 0214 QRegion Effects::subtrackedMaskFromWindow(QRegion initialRegion, QQuickView *window) 0215 { 0216 QRegion subtractedMask = initialRegion; 0217 0218 int start; 0219 int length; 0220 0221 if (m_view->formFactor() == Plasma::Types::Horizontal) { 0222 if (KWindowSystem::isPlatformX11()) { 0223 start = window->x(); 0224 length = window->width(); 0225 } else { 0226 start = m_view->x(); 0227 length = m_view->width(); 0228 } 0229 } else { 0230 if (KWindowSystem::isPlatformX11()) { 0231 start = window->y(); 0232 length = window->height(); 0233 } else { 0234 start = m_view->y(); 0235 length = m_view->height(); 0236 } 0237 } 0238 0239 if (m_settingsMaskSubtracted && window) { 0240 QRect windowMask; 0241 //! we need to subtrack the mask areas that overlap with underlying window 0242 switch (m_view->location()) { 0243 case Plasma::Types::TopEdge: 0244 windowMask.setTopLeft(QPoint(start - m_view->x(), m_mask.y() + m_mask.height() - m_editShadow)); 0245 windowMask.setSize(QSize(length, m_editShadow)); 0246 break; 0247 0248 case Plasma::Types::LeftEdge: 0249 windowMask.setTopLeft(QPoint(m_mask.right() + 1 - m_editShadow, start - m_view->y())); 0250 windowMask.setSize(QSize(m_editShadow, length)); 0251 break; 0252 0253 case Plasma::Types::RightEdge: 0254 windowMask.setTopLeft(QPoint(m_mask.x(), start - m_view->y())); 0255 windowMask.setSize(QSize(m_editShadow, length)); 0256 break; 0257 0258 case Plasma::Types::BottomEdge: 0259 windowMask.setTopLeft(QPoint(start - m_view->x(), m_mask.y())); 0260 windowMask.setSize(QSize(length, m_editShadow)); 0261 break; 0262 0263 default: 0264 break; 0265 } 0266 0267 subtractedMask = subtractedMask.subtracted(windowMask); 0268 } 0269 0270 return subtractedMask; 0271 } 0272 0273 QRegion Effects::subtractedMask() 0274 { 0275 QRegion subMask = m_mask; 0276 0277 if (m_settingsMaskSubtracted && m_view->configView()) { 0278 subMask = subtrackedMaskFromWindow(subMask, m_view->configView()); 0279 0280 ViewPart::PrimaryConfigView *primaryConfig = qobject_cast<ViewPart::PrimaryConfigView *>(m_view->configView()); 0281 0282 if (primaryConfig && m_view->formFactor() == Plasma::Types::Horizontal && primaryConfig->secondaryWindow()) { 0283 subMask = subtrackedMaskFromWindow(subMask, primaryConfig->secondaryWindow()); 0284 } 0285 } 0286 0287 return subMask; 0288 } 0289 0290 QRect Effects::rect() const 0291 { 0292 return m_rect; 0293 } 0294 0295 void Effects::setRect(QRect area) 0296 { 0297 if (m_rect == area) { 0298 return; 0299 } 0300 0301 m_rect = area; 0302 0303 emit rectChanged(); 0304 } 0305 0306 QRect Effects::mask() const 0307 { 0308 return m_mask; 0309 } 0310 0311 void Effects::setMask(QRect area) 0312 { 0313 if (m_mask == area) 0314 return; 0315 0316 m_mask = area; 0317 updateMask(); 0318 0319 // qDebug() << "dock mask set:" << m_mask; 0320 emit maskChanged(); 0321 } 0322 0323 void Effects::forceMaskRedraw() 0324 { 0325 if (m_background) { 0326 delete m_background; 0327 } 0328 0329 m_background = new Plasma::FrameSvg(this); 0330 m_background->setImagePath(QStringLiteral("widgets/panel-background")); 0331 m_background->setEnabledBorders(m_enabledBorders); 0332 0333 updateMask(); 0334 } 0335 0336 void Effects::updateMask() 0337 { 0338 if (KWindowSystem::compositingActive()) { 0339 if (m_view->behaveAsPlasmaPanel()) { 0340 m_view->setMask(QRect()); 0341 } else { 0342 m_view->setMask(subtractedMask()); 0343 } 0344 } else { 0345 //! this is used when compositing is disabled and provides 0346 //! the correct way for the mask to be painted in order for 0347 //! rounded corners to be shown correctly 0348 //! the enabledBorders check was added because there was cases 0349 //! that the mask region wasn't calculated correctly after location changes 0350 if (!m_background) { 0351 if (m_background && m_background->enabledBorders() != m_enabledBorders) { 0352 delete m_background; 0353 } 0354 0355 m_background = new Plasma::FrameSvg(this); 0356 } 0357 0358 if (m_background->imagePath() != "widgets/panel-background") { 0359 m_background->setImagePath(QStringLiteral("widgets/panel-background")); 0360 } 0361 0362 m_background->setEnabledBorders(m_enabledBorders); 0363 m_background->resizeFrame(m_mask.size()); 0364 QRegion fixedMask = m_background->mask(); 0365 fixedMask.translate(m_mask.x(), m_mask.y()); 0366 0367 //! fix for KF5.32 that return empty QRegion's for the mask 0368 if (fixedMask.isEmpty()) { 0369 fixedMask = QRegion(m_mask); 0370 } 0371 0372 m_view->setMask(fixedMask); 0373 } 0374 } 0375 0376 void Effects::clearShadows() 0377 { 0378 PanelShadows::self()->removeWindow(m_view); 0379 } 0380 0381 void Effects::updateShadows() 0382 { 0383 if (m_view->behaveAsPlasmaPanel() && drawShadows()) { 0384 PanelShadows::self()->addWindow(m_view, enabledBorders()); 0385 } else { 0386 PanelShadows::self()->removeWindow(m_view); 0387 } 0388 } 0389 0390 void Effects::updateEffects() 0391 { 0392 //! Don't apply any effect before the wayland surface is created under wayland 0393 //! https://bugs.kde.org/show_bug.cgi?id=392890 0394 if (KWindowSystem::isPlatformWayland() && !m_view->surface()) { 0395 return; 0396 } 0397 0398 bool clearEffects{true}; 0399 0400 if (m_drawEffects) { 0401 if (!m_view->behaveAsPlasmaPanel()) { 0402 if (!m_rect.isNull() && !m_rect.isEmpty()) { 0403 //! this is used when compositing is disabled and provides 0404 //! the correct way for the mask to be painted in order for 0405 //! rounded corners to be shown correctly 0406 if (!m_background) { 0407 m_background = new Plasma::FrameSvg(this); 0408 } 0409 0410 if (m_background->imagePath() != "widgets/panel-background") { 0411 m_background->setImagePath(QStringLiteral("widgets/panel-background")); 0412 } 0413 0414 m_background->setEnabledBorders(m_enabledBorders); 0415 m_background->resizeFrame(m_rect.size()); 0416 QRegion fixedMask = m_background->mask(); 0417 fixedMask.translate(m_rect.x(), m_rect.y()); 0418 0419 if (!fixedMask.isEmpty()) { 0420 clearEffects = false; 0421 KWindowEffects::enableBlurBehind(m_view->winId(), true, fixedMask); 0422 KWindowEffects::enableBackgroundContrast(m_view->winId(), 0423 m_theme.backgroundContrastEnabled(), 0424 m_backEffectContrast, 0425 m_backEffectIntesity, 0426 m_backEffectSaturation, 0427 fixedMask); 0428 } 0429 } 0430 } else { 0431 //! BEHAVEASPLASMAPANEL case 0432 clearEffects = false; 0433 KWindowEffects::enableBlurBehind(m_view->winId(), true); 0434 KWindowEffects::enableBackgroundContrast(m_view->winId(), 0435 m_theme.backgroundContrastEnabled(), 0436 m_backEffectContrast, 0437 m_backEffectIntesity, 0438 m_backEffectSaturation); 0439 } 0440 } 0441 0442 if (clearEffects) { 0443 KWindowEffects::enableBlurBehind(m_view->winId(), false); 0444 KWindowEffects::enableBackgroundContrast(m_view->winId(), false); 0445 } 0446 } 0447 0448 //!BEGIN draw panel shadows outside the dock window 0449 Plasma::FrameSvg::EnabledBorders Effects::enabledBorders() const 0450 { 0451 return m_enabledBorders; 0452 } 0453 0454 qreal Effects::currentMidValue(const qreal &max, const qreal &factor, const qreal &min) const 0455 { 0456 if (max==min || factor==0) { 0457 return min; 0458 } 0459 0460 qreal space = 0; 0461 qreal distance = 0; 0462 0463 if (max<min) { 0464 space = min-max; 0465 distance = factor*space; 0466 return 1-distance; 0467 } else { 0468 space = max-min; 0469 distance = factor*space; 0470 return 1+distance; 0471 } 0472 } 0473 0474 void Effects::updateBackgroundContrastValues() 0475 { 0476 if (!m_theme.backgroundContrastEnabled()) { 0477 m_backEffectContrast = 1; 0478 m_backEffectIntesity = 1; 0479 m_backEffectSaturation = 1; 0480 return; 0481 } 0482 0483 const qreal factor = (qreal)m_backgroundOpacity / (qreal)100; 0484 m_backEffectContrast = currentMidValue(m_theme.backgroundContrast(), factor, 1); 0485 m_backEffectIntesity = currentMidValue(m_theme.backgroundIntensity(), factor, 1); 0486 m_backEffectSaturation = currentMidValue(m_theme.backgroundSaturation(), factor, 1); 0487 } 0488 0489 void Effects::updateEnabledBorders() 0490 { 0491 if (!m_view->screen()) { 0492 return; 0493 } 0494 0495 Plasma::FrameSvg::EnabledBorders borders = Plasma::FrameSvg::AllBorders; 0496 0497 switch (m_view->location()) { 0498 case Plasma::Types::TopEdge: 0499 borders &= ~Plasma::FrameSvg::TopBorder; 0500 break; 0501 0502 case Plasma::Types::LeftEdge: 0503 borders &= ~Plasma::FrameSvg::LeftBorder; 0504 break; 0505 0506 case Plasma::Types::RightEdge: 0507 borders &= ~Plasma::FrameSvg::RightBorder; 0508 break; 0509 0510 case Plasma::Types::BottomEdge: 0511 borders &= ~Plasma::FrameSvg::BottomBorder; 0512 break; 0513 0514 default: 0515 break; 0516 } 0517 0518 if ((m_view->location() == Plasma::Types::LeftEdge || m_view->location() == Plasma::Types::RightEdge)) { 0519 if (m_view->maxLength() == 1 && m_view->alignment() == Latte::Types::Justify && !m_forceDrawCenteredBorders) { 0520 borders &= ~Plasma::FrameSvg::TopBorder; 0521 borders &= ~Plasma::FrameSvg::BottomBorder; 0522 } 0523 0524 if (m_view->alignment() == Latte::Types::Top && !m_forceDrawCenteredBorders && m_view->offset() == 0) { 0525 borders &= ~Plasma::FrameSvg::TopBorder; 0526 } 0527 0528 if (m_view->alignment() == Latte::Types::Bottom && !m_forceDrawCenteredBorders && m_view->offset() == 0) { 0529 borders &= ~Plasma::FrameSvg::BottomBorder; 0530 } 0531 } 0532 0533 if (m_view->location() == Plasma::Types::TopEdge || m_view->location() == Plasma::Types::BottomEdge) { 0534 if (m_view->maxLength() == 1 && m_view->alignment() == Latte::Types::Justify) { 0535 borders &= ~Plasma::FrameSvg::LeftBorder; 0536 borders &= ~Plasma::FrameSvg::RightBorder; 0537 } 0538 0539 if (m_view->alignment() == Latte::Types::Left && m_view->offset() == 0) { 0540 borders &= ~Plasma::FrameSvg::LeftBorder; 0541 } 0542 0543 if (m_view->alignment() == Latte::Types::Right && m_view->offset() == 0) { 0544 borders &= ~Plasma::FrameSvg::RightBorder; 0545 } 0546 } 0547 0548 if (m_enabledBorders != borders) { 0549 m_enabledBorders = borders; 0550 emit enabledBordersChanged(); 0551 } 0552 0553 if (!m_view->behaveAsPlasmaPanel() || !m_drawShadows) { 0554 PanelShadows::self()->removeWindow(m_view); 0555 } else { 0556 PanelShadows::self()->setEnabledBorders(m_view, borders); 0557 } 0558 } 0559 //!END draw panel shadows outside the dock window 0560 0561 } 0562 }