Warning, /plasma/plasma-workspace/components/containmentlayoutmanager/qml/BasicAppletContainer.qml is written in an unsupported language. File is not indexed.

0001 /*
0002     SPDX-FileCopyrightText: 2019 Marco Martin <mart@kde.org>
0003     SPDX-FileCopyrightText: 2022 ivan tkachenko <me@ratijas.tk>
0004     SPDX-FileCopyrightText: 2022 Niccolò Venerandi <niccolo@venerandi.com>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 import QtQuick 2.15
0010 import QtQuick.Layouts 1.15
0011 import QtQuick.Window 2.15
0012 import Qt5Compat.GraphicalEffects
0013 
0014 import org.kde.plasma.plasmoid 2.0
0015 import org.kde.plasma.core as PlasmaCore
0016 import org.kde.ksvg 1.0 as KSvg
0017 import org.kde.plasma.components 3.0 as PlasmaComponents
0018 import org.kde.plasma.private.containmentlayoutmanager 1.0 as ContainmentLayoutManager
0019 import org.kde.kirigami 2.11 as Kirigami
0020 
0021 ContainmentLayoutManager.AppletContainer {
0022     id: appletContainer
0023     editModeCondition: Plasmoid.immutable
0024         ? ContainmentLayoutManager.ItemContainer.Manual
0025         : ContainmentLayoutManager.ItemContainer.AfterPressAndHold
0026 
0027     Kirigami.Theme.inherit: false
0028     Kirigami.Theme.colorSet: (applet.plasmoid.effectiveBackgroundHints & PlasmaCore.Types.ShadowBackground)
0029         && !(applet.plasmoid.effectiveBackgroundHints & PlasmaCore.Types.StandardBackground)
0030         && !(applet.plasmoid.effectiveBackgroundHints & PlasmaCore.Types.TranslucentBackground)
0031             ? Kirigami.Theme.Complementary
0032             : Kirigami.Theme.Window
0033 
0034     onFocusChanged: {
0035         if (!focus && !dragActive) {
0036             editMode = false;
0037         }
0038     }
0039     Layout.minimumWidth: {
0040         if (!applet) {
0041             return leftPadding + rightPadding;
0042         }
0043 
0044         if (applet.preferredRepresentation !== applet.fullRepresentation
0045             && applet.compactRepresentationItem
0046         ) {
0047             return applet.compactRepresentationItem.Layout.minimumWidth + leftPadding + rightPadding;
0048         } else {
0049             return applet.Layout.minimumWidth + leftPadding + rightPadding;
0050         }
0051     }
0052     Layout.minimumHeight: {
0053         if (!applet) {
0054             return topPadding + bottomPadding;
0055         }
0056 
0057         if (applet.preferredRepresentation !== applet.fullRepresentation
0058             && applet.compactRepresentationItem
0059         ) {
0060             return applet.compactRepresentationItem.Layout.minimumHeight + topPadding + bottomPadding;
0061         } else {
0062             return applet.Layout.minimumHeight + topPadding + bottomPadding;
0063         }
0064     }
0065 
0066     Layout.preferredWidth: Math.max(applet.Layout.minimumWidth, applet.Layout.preferredWidth)
0067     Layout.preferredHeight: Math.max(applet.Layout.minimumHeight, applet.Layout.preferredHeight)
0068 
0069     Layout.maximumWidth: applet.Layout.maximumWidth
0070     Layout.maximumHeight: applet.Layout.maximumHeight
0071 
0072     leftPadding: background.margins.left
0073     topPadding: background.margins.top
0074     rightPadding: background.margins.right
0075     bottomPadding: background.margins.bottom
0076 
0077     // render via a layer if we're at an angle
0078     // resize handles are rendered outside this item, so also disable when they're showing to avoid clipping
0079     layer.enabled: (rotation % 90 !== 0) && !(configOverlayItem && configOverlayItem.visible)
0080     layer.smooth: true
0081 
0082     initialSize.width: applet.switchWidth + leftPadding + rightPadding
0083     initialSize.height: applet.switchHeight + topPadding + bottomPadding
0084 
0085     background: KSvg.FrameSvgItem {
0086         id: background
0087 
0088         property bool blurEnabled: false
0089         property Item maskItem: null
0090 
0091         prefix: blurEnabled ? "blurred" : ""
0092 
0093         imagePath: {
0094             if (!appletContainer.applet) {
0095                 return "";
0096             }
0097             if (appletContainer.applet.plasmoid.effectiveBackgroundHints & PlasmaCore.Types.TranslucentBackground) {
0098                 return "widgets/translucentbackground";
0099             } else if (appletContainer.applet.plasmoid.effectiveBackgroundHints & PlasmaCore.Types.StandardBackground) {
0100                 return "widgets/background";
0101             } else {
0102                 return "";
0103             }
0104         }
0105 
0106         function bindBlurEnabled() {
0107             // bind to api and hints automatically, refresh non-observable prefix manually
0108             blurEnabled = Qt.binding(() =>
0109                    GraphicsInfo.api !== GraphicsInfo.Software
0110                 && (appletContainer.applet.plasmoid.effectiveBackgroundHints & PlasmaCore.Types.StandardBackground)
0111                 && hasElementPrefix("blurred")
0112             );
0113         }
0114 
0115         Component.onCompleted: bindBlurEnabled()
0116         onRepaintNeeded: bindBlurEnabled()
0117 
0118         onBlurEnabledChanged: {
0119             if (blurEnabled) {
0120                 if (maskItem === null) {
0121                     maskItem = maskComponent.createObject(this);
0122                 }
0123             } else {
0124                 if (maskItem !== null) {
0125                     maskItem.destroy();
0126                     maskItem = null;
0127                 }
0128             }
0129         }
0130 
0131         DropShadow {
0132             anchors {
0133                 fill: parent
0134                 leftMargin: appletContainer.leftPadding
0135                 topMargin: appletContainer.topPadding
0136                 rightMargin: appletContainer.rightPadding
0137                 bottomMargin: appletContainer.bottomPadding
0138             }
0139             z: -1
0140             horizontalOffset: 0
0141             verticalOffset: 1
0142 
0143             radius: 4
0144             samples: 9
0145             spread: 0.35
0146 
0147             color: Qt.rgba(0, 0, 0, 0.5)
0148             opacity: 1
0149 
0150             source: appletContainer.applet && appletContainer.applet.plasmoid.effectiveBackgroundHints & PlasmaCore.Types.ShadowBackground
0151                 ? appletContainer.applet : null
0152             visible: source !== null
0153         }
0154     }
0155 
0156     Component {
0157         id: maskComponent
0158 
0159         OpacityMask {
0160             id: mask
0161 
0162             readonly property rect appletContainerScreenRect: {
0163                 const win = appletContainer.Window.window;
0164                 let sceneSize = Qt.size(appletContainer.width, appletContainer.height)
0165                 if (win) {
0166                     sceneSize = Qt.size(win.width, win.height)
0167                 }
0168                 const position = appletContainer.Kirigami.ScenePosition;
0169                 return clipRect(
0170                     boundsForTransformedRect(
0171                         Qt.rect(
0172                             position.x,
0173                             position.y,
0174                             appletContainer.width,
0175                             appletContainer.height),
0176                         appletContainer.rotation,
0177                         appletContainer.scale),
0178                     sceneSize);
0179             }
0180 
0181             /** Apply geometry transformations, and return a bounding rectangle for a resulting shape. */
0182             // Note: It's basically a custom QMatrix::mapRect implementation, and for
0183             // simplicity's sake should be replaced when/if mapRect becomes available in QML.
0184             function boundsForTransformedRect(rect: rect, angle: real, scale: real): rect {
0185                 if (angle === 0 && scale === 1) {
0186                     return rect; // hot path optimization
0187                 }
0188                 let cosa = Math.abs(Math.cos(angle * (Math.PI / 180))) * scale;
0189                 let sina = Math.abs(Math.sin(angle * (Math.PI / 180))) * scale;
0190                 let newSize = Qt.size(
0191                     rect.width * cosa + rect.height * sina,
0192                     rect.width * sina + rect.height * cosa);
0193                 return Qt.rect(
0194                     rect.left + (rect.width - newSize.width) / 2,
0195                     rect.top + (rect.height - newSize.height) / 2,
0196                     newSize.width,
0197                     newSize.height);
0198             }
0199 
0200             /** Clip given rectangle to the bounds of given size, assuming bounds position {0,0}.
0201              * This is a pure library function, similar to QRect::intersected,
0202              * which Qt should've exposed in QML stdlib.
0203              */
0204             function clipRect(rect: rect, bounds: size): rect {
0205                 return Qt.rect(
0206                     Math.max(0, Math.min(bounds.width, rect.x)),
0207                     Math.max(0, Math.min(bounds.height, rect.y)),
0208                     Math.max(0, rect.width
0209                                 + Math.min(0, rect.x)
0210                                 + Math.min(0, bounds.width - (rect.x + rect.width))),
0211                     Math.max(0, rect.height
0212                                 + Math.min(0, rect.y)
0213                                 + Math.min(0, bounds.height - (rect.y + rect.height))),
0214                 );
0215             }
0216 
0217             parent: appletContainer.layout.containmentItem
0218             x: appletContainerScreenRect.x
0219             y: appletContainerScreenRect.y
0220             width: appletContainerScreenRect.width
0221             height: appletContainerScreenRect.height
0222 
0223             z: -2
0224             maskSource: Item {
0225                 // optimized (clipped) blurred-mask
0226 
0227                 width: mask.appletContainerScreenRect.width
0228                 height: mask.appletContainerScreenRect.height
0229 
0230                 clip: true
0231 
0232                 KSvg.FrameSvgItem {
0233                     imagePath: "widgets/background"
0234                     prefix: "blurred-mask"
0235 
0236                     x: appletContainer.Kirigami.ScenePosition.x - mask.appletContainerScreenRect.x
0237                     y: appletContainer.Kirigami.ScenePosition.y - mask.appletContainerScreenRect.y
0238 
0239                     width: background.width
0240                     height: background.height
0241 
0242                     rotation: appletContainer.rotation
0243                     scale: appletContainer.scale
0244                 }
0245             }
0246 
0247             source: FastBlur {
0248                 width: mask.appletContainerScreenRect.width
0249                 height: mask.appletContainerScreenRect.height
0250 
0251                 radius: 128
0252 
0253                 source: ShaderEffectSource {
0254                     width: mask.appletContainerScreenRect.width
0255                     height: mask.appletContainerScreenRect.height
0256                     sourceRect: mask.appletContainerScreenRect
0257                     sourceItem: appletContainer.layout.containmentItem.wallpaper
0258                 }
0259             }
0260         }
0261     }
0262 
0263     busyIndicatorComponent: PlasmaComponents.BusyIndicator {
0264         anchors.centerIn: parent
0265         visible: applet.plasmoid.busy
0266         running: visible
0267     }
0268     configurationRequiredComponent: PlasmaComponents.Button {
0269         anchors.centerIn: parent
0270         text: i18n("Configure…")
0271         icon.name: "configure"
0272         visible: applet.plasmoid.configurationRequired
0273         onClicked: applet.plasmoid.internalAction("configure").trigger();
0274     }
0275 }