File: BasicAppletContainer.qml

package info (click to toggle)
plasma-workspace 4%3A5.27.5-2%2Bdeb12u2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 102,040 kB
  • sloc: cpp: 121,800; xml: 3,238; python: 645; perl: 586; sh: 254; javascript: 113; ruby: 62; makefile: 15; ansic: 13
file content (275 lines) | stat: -rw-r--r-- 10,434 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
/*
    SPDX-FileCopyrightText: 2019 Marco Martin <mart@kde.org>
    SPDX-FileCopyrightText: 2022 ivan tkachenko <me@ratijas.tk>
    SPDX-FileCopyrightText: 2022 Niccolò Venerandi <niccolo@venerandi.com>

    SPDX-License-Identifier: LGPL-2.0-or-later
*/

import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQuick.Window 2.15
import QtGraphicalEffects 1.0

import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 3.0 as PlasmaComponents
import org.kde.plasma.private.containmentlayoutmanager 1.0 as ContainmentLayoutManager
import org.kde.kirigami 2.11 as Kirigami

ContainmentLayoutManager.AppletContainer {
    id: appletContainer
    editModeCondition: Plasmoid.immutable
        ? ContainmentLayoutManager.ItemContainer.Manual
        : ContainmentLayoutManager.ItemContainer.AfterPressAndHold

    Kirigami.Theme.inherit: false
    Kirigami.Theme.colorSet: (applet.effectiveBackgroundHints & PlasmaCore.Types.ShadowBackground)
        && !(applet.effectiveBackgroundHints & PlasmaCore.Types.StandardBackground)
        && !(applet.effectiveBackgroundHints & PlasmaCore.Types.TranslucentBackground)
            ? Kirigami.Theme.Complementary
            : Kirigami.Theme.Window

    PlasmaCore.ColorScope.inherit: false
    PlasmaCore.ColorScope.colorGroup: Kirigami.Theme.colorSet === Kirigami.Theme.Complementary
        ? PlasmaCore.Theme.ComplementaryColorGroup
        : PlasmaCore.Theme.NormalColorGroup

    onFocusChanged: {
        if (!focus && !dragActive) {
            editMode = false;
        }
    }
    Layout.minimumWidth: {
        if (!applet) {
            return leftPadding + rightPadding;
        }

        if (applet.preferredRepresentation !== applet.fullRepresentation
            && applet.compactRepresentationItem
        ) {
            return applet.compactRepresentationItem.Layout.minimumWidth + leftPadding + rightPadding;
        } else {
            return applet.Layout.minimumWidth + leftPadding + rightPadding;
        }
    }
    Layout.minimumHeight: {
        if (!applet) {
            return topPadding + bottomPadding;
        }

        if (applet.preferredRepresentation !== applet.fullRepresentation
            && applet.compactRepresentationItem
        ) {
            return applet.compactRepresentationItem.Layout.minimumHeight + topPadding + bottomPadding;
        } else {
            return applet.Layout.minimumHeight + topPadding + bottomPadding;
        }
    }

    Layout.preferredWidth: Math.max(applet.Layout.minimumWidth, applet.Layout.preferredWidth)
    Layout.preferredHeight: Math.max(applet.Layout.minimumHeight, applet.Layout.preferredHeight)

    Layout.maximumWidth: applet.Layout.maximumWidth
    Layout.maximumHeight: applet.Layout.maximumHeight

    leftPadding: background.margins.left
    topPadding: background.margins.top
    rightPadding: background.margins.right
    bottomPadding: background.margins.bottom

    // render via a layer if we're at an angle
    // resize handles are rendered outside this item, so also disable when they're showing to avoid clipping
    layer.enabled: (rotation % 90 !== 0) && !(configOverlayItem && configOverlayItem.visible)
    layer.smooth: true

    initialSize.width: applet.switchWidth + leftPadding + rightPadding
    initialSize.height: applet.switchHeight + topPadding + bottomPadding

    background: PlasmaCore.FrameSvgItem {
        id: background

        property bool blurEnabled: false
        property Item maskItem: null

        prefix: blurEnabled ? "blurred" : ""

        imagePath: {
            if (!appletContainer.applet) {
                return "";
            }
            if (appletContainer.applet.effectiveBackgroundHints & PlasmaCore.Types.TranslucentBackground) {
                return "widgets/translucentbackground";
            } else if (appletContainer.applet.effectiveBackgroundHints & PlasmaCore.Types.StandardBackground) {
                return "widgets/background";
            } else {
                return "";
            }
        }

        function bindBlurEnabled() {
            // bind to api and hints automatically, refresh non-observable prefix manually
            blurEnabled = Qt.binding(() =>
                   GraphicsInfo.api !== GraphicsInfo.Software
                && (appletContainer.applet.effectiveBackgroundHints & PlasmaCore.Types.StandardBackground)
                && hasElementPrefix("blurred")
            );
        }

        Component.onCompleted: bindBlurEnabled()
        onRepaintNeeded: bindBlurEnabled()

        onBlurEnabledChanged: {
            if (blurEnabled) {
                if (maskItem === null) {
                    maskItem = maskComponent.createObject(this);
                }
            } else {
                if (maskItem !== null) {
                    maskItem.destroy();
                    maskItem = null;
                }
            }
        }

        DropShadow {
            anchors {
                fill: parent
                leftMargin: appletContainer.leftPadding
                topMargin: appletContainer.topPadding
                rightMargin: appletContainer.rightPadding
                bottomMargin: appletContainer.bottomPadding
            }
            z: -1
            horizontalOffset: 0
            verticalOffset: 1

            radius: 4
            samples: 9
            spread: 0.35

            color: Qt.rgba(0, 0, 0, 0.5)
            opacity: 1

            source: appletContainer.applet && appletContainer.applet.effectiveBackgroundHints & PlasmaCore.Types.ShadowBackground
                ? appletContainer.applet : null
            visible: source !== null
        }
    }

    Component {
        id: maskComponent

        OpacityMask {
            id: mask

            readonly property rect appletContainerScreenRect: {
                const scene = appletContainer.Window.window;
                const position = appletContainer.Kirigami.ScenePosition;
                return clipRect(
                    boundsForTransformedRect(
                        Qt.rect(
                            position.x,
                            position.y,
                            appletContainer.width,
                            appletContainer.height),
                        appletContainer.rotation,
                        appletContainer.scale),
                    Qt.size(scene.width, scene.height));
            }

            /** Apply geometry transformations, and return a bounding rectangle for a resulting shape. */
            // Note: It's basically a custom QMatrix::mapRect implementation, and for
            // simplicity's sake should be replaced when/if mapRect becomes available in QML.
            function boundsForTransformedRect(rect: rect, angle: real, scale: real): rect {
                if (angle === 0 && scale === 1) {
                    return rect; // hot path optimization
                }
                let cosa = Math.abs(Math.cos(angle * (Math.PI / 180))) * scale;
                let sina = Math.abs(Math.sin(angle * (Math.PI / 180))) * scale;
                let newSize = Qt.size(
                    rect.width * cosa + rect.height * sina,
                    rect.width * sina + rect.height * cosa);
                return Qt.rect(
                    rect.left + (rect.width - newSize.width) / 2,
                    rect.top + (rect.height - newSize.height) / 2,
                    newSize.width,
                    newSize.height);
            }

            /** Clip given rectangle to the bounds of given size, assuming bounds position {0,0}.
             * This is a pure library function, similar to QRect::intersected,
             * which Qt should've exposed in QML stdlib.
             */
            function clipRect(rect: rect, bounds: size): rect {
                return Qt.rect(
                    Math.max(0, Math.min(bounds.width, rect.x)),
                    Math.max(0, Math.min(bounds.height, rect.y)),
                    Math.max(0, rect.width
                                + Math.min(0, rect.x)
                                + Math.min(0, bounds.width - (rect.x + rect.width))),
                    Math.max(0, rect.height
                                + Math.min(0, rect.y)
                                + Math.min(0, bounds.height - (rect.y + rect.height))),
                );
            }

            parent: appletContainer.Plasmoid.self
            x: appletContainerScreenRect.x
            y: appletContainerScreenRect.y
            width: appletContainerScreenRect.width
            height: appletContainerScreenRect.height

            z: -2
            maskSource: Item {
                // optimized (clipped) blurred-mask

                width: mask.appletContainerScreenRect.width
                height: mask.appletContainerScreenRect.height

                clip: true

                PlasmaCore.FrameSvgItem {
                    imagePath: "widgets/background"
                    prefix: "blurred-mask"

                    x: appletContainer.Kirigami.ScenePosition.x - mask.appletContainerScreenRect.x
                    y: appletContainer.Kirigami.ScenePosition.y - mask.appletContainerScreenRect.y

                    width: background.width
                    height: background.height

                    rotation: appletContainer.rotation
                    scale: appletContainer.scale
                }
            }

            source: FastBlur {
                width: mask.appletContainerScreenRect.width
                height: mask.appletContainerScreenRect.height

                radius: 128

                source: ShaderEffectSource {
                    width: mask.appletContainerScreenRect.width
                    height: mask.appletContainerScreenRect.height
                    sourceRect: mask.appletContainerScreenRect
                    sourceItem: appletContainer.Plasmoid.wallpaper
                }
            }
        }
    }

    busyIndicatorComponent: PlasmaComponents.BusyIndicator {
        anchors.centerIn: parent
        visible: applet.busy
        running: visible
    }
    configurationRequiredComponent: PlasmaComponents.Button {
        anchors.centerIn: parent
        text: i18n("Configure…")
        icon.name: "configure"
        visible: applet.configurationRequired
        onClicked: applet.action("configure").trigger();
    }
}