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 }