File indexing completed on 2024-12-22 04:31:07
0001 /* 0002 * Copyright (C) 2021 CutefishOS Team. 0003 * 0004 * Author: revenmartin <revenmartin@gmail.com> 0005 * 0006 * This program is free software: you can redistribute it and/or modify 0007 * it under the terms of the GNU General Public License as published by 0008 * the Free Software Foundation, either version 3 of the License, or 0009 * any later version. 0010 * 0011 * This program 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 "windowshadow.h" 0021 #include "boxshadowrenderer.h" 0022 #include <QDebug> 0023 #include <QCoreApplication> 0024 #include <QDBusInterface> 0025 0026 #include <KWindowSystem> 0027 #include <KWindowEffects> 0028 0029 enum { 0030 ShadowNone, 0031 ShadowSmall, 0032 ShadowMedium, 0033 ShadowLarge, 0034 ShadowVeryLarge 0035 }; 0036 0037 const CompositeShadowParams s_shadowParams[] = { 0038 // None 0039 CompositeShadowParams(), 0040 // Small 0041 CompositeShadowParams( 0042 QPoint(0, 3), 0043 ShadowParams(QPoint(0, 0), 16, 0.26), 0044 ShadowParams(QPoint(0, -2), 8, 0.16)), 0045 // Medium 0046 CompositeShadowParams( 0047 QPoint(0, 4), 0048 ShadowParams(QPoint(0, 0), 20, 0.24), 0049 ShadowParams(QPoint(0, -2), 10, 0.14)), 0050 // Large 0051 CompositeShadowParams( 0052 QPoint(0, 5), 0053 ShadowParams(QPoint(0, 0), 24, 0.22), 0054 ShadowParams(QPoint(0, -3), 12, 0.12)), 0055 // Very Large 0056 CompositeShadowParams( 0057 QPoint(0, 6), 0058 ShadowParams(QPoint(0, 0), 36, 0.12), 0059 ShadowParams(QPoint(0, -3), 20, 0.05)) 0060 }; 0061 0062 WindowShadow::WindowShadow(QObject *parent) noexcept 0063 : QObject(parent) 0064 , m_view(nullptr) 0065 , m_shadow(new KWindowShadow(this)) 0066 { 0067 0068 } 0069 0070 WindowShadow::~WindowShadow() 0071 { 0072 m_shadow->destroy(); 0073 } 0074 0075 CompositeShadowParams WindowShadow::lookupShadowParams(int shadowSizeEnum) 0076 { 0077 switch (shadowSizeEnum) { 0078 case ShadowNone: 0079 return s_shadowParams[0]; 0080 case ShadowSmall: 0081 return s_shadowParams[1]; 0082 case ShadowMedium: 0083 return s_shadowParams[2]; 0084 case ShadowLarge: 0085 return s_shadowParams[3]; 0086 case ShadowVeryLarge: 0087 return s_shadowParams[4]; 0088 default: 0089 // Fallback to the Large size. 0090 return s_shadowParams[3]; 0091 } 0092 } 0093 0094 void WindowShadow::classBegin() 0095 { 0096 m_shadowTiles = this->shadowTiles(); 0097 0098 QVector<KWindowShadowTile::Ptr> tiles = { 0099 createTile(m_shadowTiles.pixmap(1)), 0100 createTile(m_shadowTiles.pixmap(2)), 0101 createTile(m_shadowTiles.pixmap(5)), 0102 createTile(m_shadowTiles.pixmap(8)), 0103 createTile(m_shadowTiles.pixmap(7)), 0104 createTile(m_shadowTiles.pixmap(6)), 0105 createTile(m_shadowTiles.pixmap(3)), 0106 createTile(m_shadowTiles.pixmap(0)) 0107 }; 0108 0109 m_tile = tiles; 0110 } 0111 0112 void WindowShadow::componentComplete() 0113 { 0114 configureTiles(); 0115 } 0116 0117 void WindowShadow::setView(QWindow *view) 0118 { 0119 if (view != m_view) { 0120 m_view = view; 0121 Q_EMIT viewChanged(); 0122 configureTiles(); 0123 0124 connect(m_view, &QWindow::visibleChanged, this, &WindowShadow::onViewVisibleChanged); 0125 } 0126 } 0127 0128 QWindow *WindowShadow::view() const 0129 { 0130 return m_view; 0131 } 0132 0133 void WindowShadow::setGeometry(const QRect &rect) 0134 { 0135 if (rect != m_rect) { 0136 m_rect = rect; 0137 Q_EMIT geometryChanged(); 0138 configureTiles(); 0139 } 0140 } 0141 0142 QRect WindowShadow::geometry() const 0143 { 0144 return m_rect; 0145 } 0146 0147 void WindowShadow::setRadius(qreal value) 0148 { 0149 if (m_radius != value) { 0150 m_radius = value; 0151 Q_EMIT radiusChanged(); 0152 0153 this->classBegin(); 0154 0155 configureTiles(); 0156 } 0157 } 0158 0159 qreal WindowShadow::strength() const 0160 { 0161 return m_strength; 0162 } 0163 0164 void WindowShadow::setStrength(qreal strength) 0165 { 0166 if (m_strength != strength) { 0167 m_strength = strength; 0168 0169 this->classBegin(); 0170 configureTiles(); 0171 0172 Q_EMIT strengthChanged(); 0173 } 0174 } 0175 0176 void WindowShadow::onViewVisibleChanged(bool visible) 0177 { 0178 if (visible && m_view) { 0179 configureTiles(); 0180 } 0181 } 0182 0183 void WindowShadow::configureTiles() 0184 { 0185 //only for cask 0186 // if(qEnvironmentVariableIsSet("XDG_CURRENT_DESKTOP") && qEnvironmentVariable("XDG_CURRENT_DESKTOP") == "Cask") 0187 // { 0188 auto chromeInterface = new QDBusInterface ("org.cask.Server", 0189 "/Chrome", 0190 "org.cask.Chrome", 0191 QDBusConnection::sessionBus(), this); 0192 qDebug() << "TRYING TO HOOK TO THE CASKSERVER" << qApp->desktopFileName() << qApp->desktopFileName(); 0193 0194 if(chromeInterface->isValid()) 0195 { 0196 qDebug() << "TRYING TO HOOK TO THE CASKSERVER IS VAL:ID"; 0197 0198 chromeInterface->call("dropShadow", static_cast<int>(m_radius), qApp->desktopFileName()); 0199 }else 0200 { 0201 qDebug() << "COULD NTO HOOK TO THE CASKSERVER"; 0202 } 0203 0204 // return; 0205 // } 0206 0207 0208 //only for plasma 0209 m_shadow->destroy(); 0210 0211 if (!m_view) 0212 return; 0213 0214 m_shadow->setWindow(m_view); 0215 m_shadow->setTopTile(m_tile[0]); 0216 m_shadow->setTopRightTile(m_tile[1]); 0217 m_shadow->setRightTile(m_tile[2]); 0218 m_shadow->setBottomRightTile(m_tile[3]); 0219 m_shadow->setBottomTile(m_tile[4]); 0220 m_shadow->setBottomLeftTile(m_tile[5]); 0221 m_shadow->setLeftTile(m_tile[6]); 0222 m_shadow->setTopLeftTile(m_tile[7]); 0223 m_shadow->setPadding(shadowMargins(m_shadowTiles)); 0224 m_shadow->create(); 0225 } 0226 0227 KWindowShadowTile::Ptr WindowShadow::createTile(const QPixmap& source) 0228 { 0229 KWindowShadowTile::Ptr tile = KWindowShadowTile::Ptr::create(); 0230 tile->setImage(source.toImage()); 0231 return tile; 0232 } 0233 0234 TileSet WindowShadow::shadowTiles() 0235 { 0236 const qreal frameRadius = m_radius; 0237 const CompositeShadowParams params = lookupShadowParams(ShadowVeryLarge); 0238 0239 if (params.isNone()) 0240 return TileSet(); 0241 0242 auto withOpacity = [](const QColor &color, qreal opacity) -> QColor { 0243 QColor c(color); 0244 c.setAlphaF(opacity); 0245 return c; 0246 }; 0247 0248 const QColor color = Qt::black; 0249 const qreal strength = m_strength; 0250 0251 const QSize boxSize = BoxShadowRenderer::calculateMinimumBoxSize(params.shadow1.radius) 0252 .expandedTo(BoxShadowRenderer::calculateMinimumBoxSize(params.shadow2.radius)); 0253 0254 const qreal dpr = qApp->devicePixelRatio(); 0255 0256 BoxShadowRenderer shadowRenderer; 0257 shadowRenderer.setBorderRadius(frameRadius); 0258 shadowRenderer.setBoxSize(boxSize); 0259 shadowRenderer.setDevicePixelRatio(dpr); 0260 0261 shadowRenderer.addShadow(params.shadow1.offset, params.shadow1.radius, 0262 withOpacity(color, params.shadow1.opacity * strength)); 0263 shadowRenderer.addShadow(params.shadow2.offset, params.shadow2.radius, 0264 withOpacity(color, params.shadow2.opacity * strength)); 0265 0266 QImage shadowTexture = shadowRenderer.render(); 0267 0268 const QRect outerRect(QPoint(0, 0), shadowTexture.size() / dpr); 0269 0270 QRect boxRect(QPoint(0, 0), boxSize); 0271 boxRect.moveCenter(outerRect.center()); 0272 0273 // Mask out inner rect. 0274 QPainter painter(&shadowTexture); 0275 painter.setRenderHint(QPainter::Antialiasing); 0276 0277 int Shadow_Overlap = 3; 0278 const QMargins margins = QMargins( 0279 boxRect.left() - outerRect.left() - Shadow_Overlap - params.offset.x(), 0280 boxRect.top() - outerRect.top() - Shadow_Overlap - params.offset.y(), 0281 outerRect.right() - boxRect.right() - Shadow_Overlap + params.offset.x(), 0282 outerRect.bottom() - boxRect.bottom() - Shadow_Overlap + params.offset.y()); 0283 0284 painter.setPen(Qt::NoPen); 0285 painter.setBrush(Qt::black); 0286 painter.setCompositionMode(QPainter::CompositionMode_DestinationOut); 0287 painter.drawRoundedRect( 0288 outerRect - margins, 0289 frameRadius, 0290 frameRadius); 0291 0292 // We're done. 0293 painter.end(); 0294 0295 const QPoint innerRectTopLeft = outerRect.center(); 0296 TileSet tiles = TileSet( 0297 QPixmap::fromImage(shadowTexture), 0298 innerRectTopLeft.x(), 0299 innerRectTopLeft.y(), 0300 1, 1); 0301 0302 return tiles; 0303 } 0304 0305 QMargins WindowShadow::shadowMargins(TileSet shadowTiles) const 0306 { 0307 const CompositeShadowParams params = lookupShadowParams(ShadowVeryLarge); 0308 if (params.isNone()) 0309 return QMargins(); 0310 0311 const QSize boxSize = BoxShadowRenderer::calculateMinimumBoxSize(params.shadow1.radius) 0312 .expandedTo(BoxShadowRenderer::calculateMinimumBoxSize(params.shadow2.radius)); 0313 0314 const QSize shadowSize = BoxShadowRenderer::calculateMinimumShadowTextureSize(boxSize, params.shadow1.radius, params.shadow1.offset) 0315 .expandedTo(BoxShadowRenderer::calculateMinimumShadowTextureSize(boxSize, params.shadow2.radius, params.shadow2.offset)); 0316 0317 const QRect shadowRect(QPoint(0, 0), shadowSize); 0318 0319 QRect boxRect(QPoint(0, 0), boxSize); 0320 boxRect.moveCenter(shadowRect.center()); 0321 0322 int Shadow_Overlap = 4; 0323 QMargins margins( 0324 boxRect.left() - shadowRect.left() - Shadow_Overlap - params.offset.x(), 0325 boxRect.top() - shadowRect.top() - Shadow_Overlap - params.offset.y(), 0326 shadowRect.right() - boxRect.right() - Shadow_Overlap + params.offset.x(), 0327 shadowRect.bottom() - boxRect.bottom() - Shadow_Overlap + params.offset.y()); 0328 0329 margins *= shadowTiles.pixmap(0).devicePixelRatio(); 0330 0331 return margins; 0332 }