1/*
2    SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
3
4    SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7import QtQuick 2.0
8import org.kde.plasma.components 2.0 as PlasmaComponents
9import org.kde.plasma.core 2.0 as PlasmaCore
10import org.kde.plasma.configuration 2.0
11import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons
12
13
14PlasmaCore.FrameSvgItem {
15    id: root
16
17    //Those properties get updated by PanelConfiguration.qml whenever a value in the panel changes
18    property alias offset: offsetHandle.value
19    property alias minimumLength: minimumLengthHandle.value
20    property alias maximumLength: maximumLengthHandle.value
21
22    imagePath: "widgets/containment-controls"
23    state: "BottomEdge"
24    implicitWidth: offsetHandle.width + minimumLengthHandle.width
25    implicitHeight: offsetHandle.height + minimumLengthHandle.height
26
27    onMinimumLengthChanged: leftMinimumLengthHandle.value = minimumLength
28    onMaximumLengthChanged: leftMaximumLengthHandle.value = maximumLength
29
30    /* As offset and length have a different meaning in all alignments, the panel shifts on alignment change.
31     * This could result in wrong panel positions (e.g. panel shifted over monitor border).
32     * The fancy version would be a recalculation of all values, so that the panel stays at it's current position,
33     * but this would be error prone and complicated. As the panel alignment is rarely changed, it's not worth it.
34     * The more easy approach is just setting the panel offset to zero. This makes sure the panel has a valid position and size.
35     */
36    Connections {
37        target: panel
38        function onAlignmentChanged() {
39            offset = 0
40        }
41    }
42
43    Component.onCompleted: {
44        offsetHandle.value = panel.offset
45        minimumLengthHandle.value = panel.minimumLength
46        maximumLengthHandle.value = panel.maximumLength
47        leftMinimumLengthHandle.value = panel.minimumLength
48        leftMaximumLengthHandle.value = panel.maximumLength
49    }
50
51    PlasmaCore.Svg {
52        id: containmentControlsSvg
53        imagePath: "widgets/containment-controls"
54    }
55    PlasmaCore.SvgItem {
56        id: centerMark
57        svg: containmentControlsSvg
58        elementId: dialogRoot.vertical ? "vertical-centerindicator" : "horizontal-centerindicator"
59        visible: panel.alignment === Qt.AlignCenter
60        width: dialogRoot.vertical ? parent.width : naturalSize.width
61        height: dialogRoot.vertical ? naturalSize.height : parent.height
62        anchors.centerIn: parent
63    }
64
65    SliderHandle {
66        id: offsetHandle
67        graphicElementName: "offsetslider"
68        onValueChanged: panel.offset = value
69        property int position: (dialogRoot.vertical) ? y : x
70        /* The maximum/minimumPosition values are needed to prevent the user from moving a panel with
71         * center alignment to the left and then drag the position handle to the left.
72         * This would make the panel to go off the monitor:
73         * |<-        V        ->                         |
74         * |  ->      |      <-                           |
75         *            ^move this slider to the left
76         */
77        minimumPosition: {
78            var size = dialogRoot.vertical ? height : width
79            switch(panel.alignment){
80            case Qt.AlignLeft:
81                    return -size / 2
82            case Qt.AlignRight:
83                    return leftMaximumLengthHandle.value - size / 2
84            default:
85                    return panel.maximumLength / 2 - size / 2
86            }
87        }
88        //Needed for the same reason as above
89        maximumPosition: {
90            var size = dialogRoot.vertical ? height : width
91            var dialogRootSize = dialogRoot.vertical ? dialogRoot.height : dialogRoot.width
92            switch(panel.alignment){
93            case Qt.AlignLeft:
94                    return dialogRootSize - maximumLengthHandle.value - size / 2
95            case Qt.AlignRight:
96                    return dialogRootSize - size / 2
97            default:
98                    return dialogRootSize - panel.maximumLength / 2 - size / 2
99            }
100        }
101    }
102
103    /* The maximumPosition value for the right handles and the minimumPosition value for the left handles are
104     * needed to prevent the user from moving a panel with center alignment to the left (right) and then pull one of the
105     * right (left) sliders to the right (left).
106     * Because the left and right sliders are coupled, this would make the left (right) sliders to go off the monitor.
107     *
108     * |<-        V        ->                         |
109     * |   ->     |     <-                            |
110     *                      ^move this slider to the right
111     *
112     * The other max/min Position values just set a minimum panel size
113     */
114
115    SliderHandle {
116        id: minimumLengthHandle
117        alignment: panel.alignment | Qt.AlignLeft
118        visible: panel.alignment !== Qt.AlignRight
119        offset: panel.offset
120        graphicElementName: "minslider"
121        onValueChanged: panel.minimumLength = value
122        minimumPosition: offsetHandle.position + PlasmaCore.Units.gridUnit * 3
123        maximumPosition: {
124            var dialogRootSize = dialogRoot.vertical ? dialogRoot.height : dialogRoot.width
125            var size = dialogRoot.vertical ? height : width
126            panel.alignment === Qt.AlignCenter ? Math.min(dialogRootSize - size/2, dialogRootSize + offset * 2 - size/2) : dialogRootSize - size/2
127        }
128    }
129
130    SliderHandle {
131        id: maximumLengthHandle
132        alignment: panel.alignment | Qt.AlignLeft
133        visible: panel.alignment !== Qt.AlignRight
134        offset: panel.offset
135        graphicElementName: "maxslider"
136        onValueChanged: panel.maximumLength = value
137        minimumPosition: offsetHandle.position + PlasmaCore.Units.gridUnit * 3
138        maximumPosition: {
139            var dialogRootSize = dialogRoot.vertical ? dialogRoot.height : dialogRoot.width
140            var size = dialogRoot.vertical ? height : width
141            panel.alignment === Qt.AlignCenter ? Math.min(dialogRootSize - size/2, dialogRootSize + offset * 2 - size/2) : dialogRootSize - size/2
142        }
143    }
144    SliderHandle {
145        id: leftMinimumLengthHandle
146        alignment: panel.alignment | Qt.AlignRight
147        visible: panel.alignment !== Qt.AlignLeft
148        offset: panel.offset
149        graphicElementName: "minslider"
150        onValueChanged: panel.minimumLength = value
151        maximumPosition: offsetHandle.position - PlasmaCore.Units.gridUnit * 3
152        minimumPosition: {
153            var size = dialogRoot.vertical ? height : width
154            panel.alignment === Qt.AlignCenter ? Math.max(-size/2, offset*2 - size/2) : -size/2
155        }
156    }
157    SliderHandle {
158        id: leftMaximumLengthHandle
159        alignment: panel.alignment | Qt.AlignRight
160        visible: panel.alignment !== Qt.AlignLeft
161        offset: panel.offset
162        graphicElementName: "maxslider"
163        onValueChanged: panel.maximumLength = value
164        maximumPosition: offsetHandle.position - PlasmaCore.Units.gridUnit * 3
165        minimumPosition: {
166            var size = dialogRoot.vertical ? height : width
167            panel.alignment === Qt.AlignCenter ? Math.max(-size/2, offset*2 - size/2) : -size/2
168        }
169    }
170
171    states: [
172        State {
173            name: "TopEdge"
174            PropertyChanges {
175                target: root
176                prefix: "north"
177                height: root.implicitHeight
178            }
179            AnchorChanges {
180                target: root
181                anchors {
182                    top: root.parent.top
183                    bottom: undefined
184                    left: root.parent.left
185                    right: root.parent.right
186                }
187            }
188            AnchorChanges {
189                target: offsetHandle
190                anchors {
191                    top: undefined
192                    bottom: root.bottom
193                    left: undefined
194                    right: undefined
195                }
196            }
197            AnchorChanges {
198                target: minimumLengthHandle
199                anchors {
200                    top: root.top
201                    bottom: undefined
202                    left: undefined
203                    right: undefined
204                }
205            }
206            AnchorChanges {
207                target: maximumLengthHandle
208                anchors {
209                    top: undefined
210                    bottom: root.bottom
211                    left: undefined
212                    right: undefined
213                }
214            }
215            AnchorChanges {
216                target: leftMinimumLengthHandle
217                anchors {
218                    top: root.top
219                    bottom: undefined
220                    left: undefined
221                    right: undefined
222                }
223            }
224            AnchorChanges {
225                target: leftMaximumLengthHandle
226                anchors {
227                    top: undefined
228                    bottom: root.bottom
229                    left: undefined
230                    right: undefined
231                }
232            }
233        },
234        State {
235            name: "BottomEdge"
236            PropertyChanges {
237                target: root
238                prefix: "south"
239                height: root.implicitHeight
240            }
241            AnchorChanges {
242                target: root
243                anchors {
244                    top: undefined
245                    bottom: root.parent.bottom
246                    left: root.parent.left
247                    right: root.parent.right
248                }
249            }
250            AnchorChanges {
251                target: offsetHandle
252                anchors {
253                    top: root.top
254                    bottom: undefined
255                    left: undefined
256                    right: undefined
257                }
258            }
259            AnchorChanges {
260                target: minimumLengthHandle
261                anchors {
262                    top: undefined
263                    bottom: root.bottom
264                    left: undefined
265                    right: undefined
266                }
267            }
268            AnchorChanges {
269                target: maximumLengthHandle
270                anchors {
271                    top: root.top
272                    bottom: undefined
273                    left: undefined
274                    right: undefined
275                }
276            }
277            AnchorChanges {
278                target: leftMinimumLengthHandle
279                anchors {
280                    top: undefined
281                    bottom: root.bottom
282                    left: undefined
283                    right: undefined
284                }
285            }
286            AnchorChanges {
287                target: leftMaximumLengthHandle
288                anchors {
289                    top: root.top
290                    bottom: undefined
291                    left: undefined
292                    right: undefined
293                }
294            }
295        },
296        State {
297            name: "LeftEdge"
298            PropertyChanges {
299                target: root
300                prefix: "west"
301                width: root.implicitWidth
302            }
303            AnchorChanges {
304                target: root
305                anchors {
306                    top: root.parent.top
307                    bottom: root.parent.bottom
308                    left: root.parent.left
309                    right: undefined
310                }
311            }
312            AnchorChanges {
313                target: offsetHandle
314                anchors {
315                    top: undefined
316                    bottom: undefined
317                    left: undefined
318                    right: root.right
319                }
320            }
321            AnchorChanges {
322                target: minimumLengthHandle
323                anchors {
324                    top: undefined
325                    bottom: undefined
326                    left: root.left
327                    right: undefined
328                }
329            }
330            AnchorChanges {
331                target: maximumLengthHandle
332                anchors {
333                    top: undefined
334                    bottom: undefined
335                    left: undefined
336                    right: root.right
337                }
338            }
339            AnchorChanges {
340                target: leftMinimumLengthHandle
341                anchors {
342                    top: undefined
343                    bottom: undefined
344                    left: root.left
345                    right: undefined
346                }
347            }
348            AnchorChanges {
349                target: leftMaximumLengthHandle
350                anchors {
351                    top: undefined
352                    bottom: undefined
353                    left: undefined
354                    right: root.right
355                }
356            }
357        },
358        State {
359            name: "RightEdge"
360            PropertyChanges {
361                target: root
362                prefix: "east"
363                width: root.implicitWidth
364            }
365            AnchorChanges {
366                target: root
367                anchors {
368                    top: root.parent.top
369                    bottom: root.parent.bottom
370                    left: undefined
371                    right: root.parent.right
372                }
373            }
374            AnchorChanges {
375                target: offsetHandle
376                anchors {
377                    top: undefined
378                    bottom: undefined
379                    left: parent.left
380                    right: undefined
381                }
382            }
383            AnchorChanges {
384                target: minimumLengthHandle
385                anchors {
386                    top: undefined
387                    bottom: undefined
388                    left: undefined
389                    right: parent.right
390                }
391            }
392            AnchorChanges {
393                target: maximumLengthHandle
394                anchors {
395                    top: undefined
396                    bottom: undefined
397                    left: parent.left
398                    right: undefined
399                }
400            }
401            AnchorChanges {
402                target: leftMinimumLengthHandle
403                anchors {
404                    top: undefined
405                    bottom: undefined
406                    left: undefined
407                    right: parent.right
408                }
409            }
410            AnchorChanges {
411                target: leftMaximumLengthHandle
412                anchors {
413                    top: undefined
414                    bottom: undefined
415                    left: parent.left
416                    right: undefined
417                }
418            }
419        }
420    ]
421}
422