1/*
2    SPDX-FileCopyrightText: 2011 Marco Martin <mart@kde.org>
3
4    SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7import QtQuick 2.7
8import QtQuick.Controls 2.5 as QQC2
9
10import org.kde.plasma.components 2.0 as PlasmaComponents
11import org.kde.plasma.core 2.0 as PlasmaCore
12import org.kde.plasma.extras 2.0 as PlasmaExtras
13import org.kde.kquickcontrolsaddons 2.0
14import org.kde.kwindowsystem 1.0
15
16import QtQuick.Window 2.1
17import QtQuick.Layouts 1.1
18
19import org.kde.plasma.private.shell 2.0
20
21Item {
22    id: main
23
24    width: Math.max(heading.paintedWidth, PlasmaCore.Units.iconSizes.enormous * 3 + PlasmaCore.Units.smallSpacing * 4 + PlasmaCore.Units.gridUnit * 2)
25    height: 800//Screen.height
26
27    opacity: draggingWidget ? 0.3 : 1
28
29    property QtObject containment
30
31    //external drop events can cause a raise event causing us to lose focus and
32    //therefore get deleted whilst we are still in a drag exec()
33    //this is a clue to the owning dialog that hideOnWindowDeactivate should be deleted
34    //See https://bugs.kde.org/show_bug.cgi?id=332733
35    property bool preventWindowHide: draggingWidget || categoriesDialog.status !== PlasmaComponents.DialogStatus.Closed
36                                  || getWidgetsDialog.status !== PlasmaComponents.DialogStatus.Closed
37
38    property bool outputOnly: draggingWidget
39
40    property Item categoryButton
41
42    property bool draggingWidget: false
43
44    signal closed()
45
46    onVisibleChanged: {
47        if (!visible) {
48            kwindowsystem.showingDesktop = false
49        }
50    }
51
52    Component.onCompleted: {
53        if (!root.widgetExplorer) {
54            root.widgetExplorer = widgetExplorerComponent.createObject(root)
55        }
56        root.widgetExplorer.containment = main.containment
57    }
58
59    Component.onDestruction: {
60        if (pendingUninstallTimer.running) {
61            // we're not being destroyed so at least reset the filters
62            widgetExplorer.widgetsModel.filterQuery = ""
63            widgetExplorer.widgetsModel.filterType = ""
64            widgetExplorer.widgetsModel.searchTerm = ""
65        } else {
66            root.widgetExplorer.destroy()
67            root.widgetExplorer = null
68        }
69    }
70
71    function addCurrentApplet() {
72        var pluginName = list.currentItem ? list.currentItem.pluginName : ""
73        if (pluginName) {
74            widgetExplorer.addApplet(pluginName)
75        }
76    }
77
78    KWindowSystem {
79        id: kwindowsystem
80    }
81
82    QQC2.Action {
83        shortcut: "Escape"
84        onTriggered: {
85            if (searchInput.length > 0) {
86                searchInput.text = ""
87            } else {
88                main.closed()
89            }
90        }
91    }
92
93    QQC2.Action {
94        shortcut: "Up"
95        onTriggered: list.currentIndex = (list.count + list.currentIndex - 1) % list.count
96    }
97
98    QQC2.Action {
99        shortcut: "Down"
100        onTriggered: list.currentIndex = (list.currentIndex + 1) % list.count
101    }
102
103    QQC2.Action {
104        shortcut: "Enter"
105        onTriggered: addCurrentApplet()
106    }
107    QQC2.Action {
108        shortcut: "Return"
109        onTriggered: addCurrentApplet()
110    }
111
112    Component {
113        id: widgetExplorerComponent
114
115        WidgetExplorer {
116            //view: desktop
117            onShouldClose: main.closed();
118        }
119    }
120
121    PlasmaComponents.ModelContextMenu {
122        id: categoriesDialog
123        visualParent: categoryButton
124        // model set on first invocation
125
126        onClicked: {
127            list.contentX = 0
128            list.contentY = 0
129            categoryButton.text = (model.filterData ? model.display : i18nd("plasma_shell_org.kde.plasma.desktop", "All Widgets"))
130            widgetExplorer.widgetsModel.filterQuery = model.filterData
131            widgetExplorer.widgetsModel.filterType = model.filterType
132        }
133    }
134
135    PlasmaComponents.ModelContextMenu {
136        id: getWidgetsDialog
137        visualParent: getWidgetsButton
138        placement: PlasmaCore.Types.TopPosedLeftAlignedPopup
139        // model set on first invocation
140        onClicked: model.trigger()
141    }
142    /*
143    PlasmaCore.Dialog {
144        id: tooltipDialog
145        property Item appletDelegate
146        location: PlasmaCore.Types.RightEdge //actually we want this to be the opposite location of the explorer itself
147
148        type: PlasmaCore.Dialog.Tooltip
149        flags:Qt.Window|Qt.WindowStaysOnTopHint|Qt.X11BypassWindowManagerHint
150
151        onAppletDelegateChanged: {
152            if (!appletDelegate) {
153                toolTipHideTimer.restart()
154                toolTipShowTimer.running = false
155            } else if (tooltipDialog.visible) {
156                tooltipDialog.visualParent = appletDelegate
157            } else {
158                tooltipDialog.visualParent = appletDelegate
159                toolTipShowTimer.restart()
160                toolTipHideTimer.running = false
161            }
162        }
163        mainItem: Tooltip { id: tooltipWidget }
164
165        Behavior on y {
166            NumberAnimation { duration: PlasmaCore.Units.longDuration }
167        }
168    }
169    Timer {
170        id: toolTipShowTimer
171        interval: 500
172        repeat: false
173        onTriggered: {
174            tooltipDialog.visible = true
175        }
176    }
177    Timer {
178        id: toolTipHideTimer
179        interval: 1000
180        repeat: false
181        onTriggered: tooltipDialog.visible = false
182    }
183    */
184
185
186    PlasmaExtras.PlasmoidHeading {
187        id: topArea
188        implicitWidth: header.implicitWidth
189        implicitHeight: header.implicitHeight
190        anchors {
191            top: parent.top
192            left: parent.left
193            right: parent.right
194        }
195
196        ColumnLayout {
197            id: header
198            anchors.fill: parent
199
200            RowLayout {
201                PlasmaExtras.Heading {
202                    id: heading
203                    level: 1
204                    text: i18nd("plasma_shell_org.kde.plasma.desktop", "Widgets")
205                    elide: Text.ElideRight
206
207                    Layout.fillWidth: true
208                }
209                PlasmaComponents.ToolButton {
210                    id: getWidgetsButton
211                    iconSource: "get-hot-new-stuff"
212                    text: i18nd("plasma_shell_org.kde.plasma.desktop", "Get New Widgets…")
213                    onClicked: {
214                        getWidgetsDialog.model = widgetExplorer.widgetsMenuActions
215                        getWidgetsDialog.openRelative()
216                    }
217                }
218                PlasmaComponents.ToolButton {
219                    id: closeButton
220                    iconSource: "window-close"
221                    onClicked: main.closed()
222                }
223            }
224
225            RowLayout {
226                PlasmaComponents.TextField {
227                    id: searchInput
228                    Layout.fillWidth: true
229                    clearButtonShown: true
230                    placeholderText: i18nd("plasma_shell_org.kde.plasma.desktop", "Search…")
231
232                    inputMethodHints: Qt.ImhNoPredictiveText
233
234                    onTextChanged: {
235                        list.positionViewAtBeginning()
236                        list.currentIndex = -1
237                        widgetExplorer.widgetsModel.searchTerm = text
238                    }
239
240                    Component.onCompleted: forceActiveFocus()
241                }
242                PlasmaComponents.ToolButton {
243                    id: categoryButton
244                    tooltip: i18nd("plasma_shell_org.kde.plasma.desktop", "Categories")
245                    text: i18nd("plasma_shell_org.kde.plasma.desktop", "All Widgets")
246                    iconSource: "view-filter"
247                    onClicked: {
248                        categoriesDialog.model = widgetExplorer.filterModel
249                        categoriesDialog.open(0, categoryButton.height)
250                    }
251                }
252            }
253
254            Item {
255                height: PlasmaCore.Units.smallSpacing
256            }
257        }
258    }
259
260    Timer {
261        id: setModelTimer
262        interval: 20
263        running: true
264        onTriggered: list.model = widgetExplorer.widgetsModel
265    }
266
267    PlasmaExtras.ScrollArea {
268        anchors {
269            top: topArea.bottom
270            left: parent.left
271            right: parent.right
272            bottom: parent.bottom
273        }
274
275        verticalScrollBarPolicy: Qt.ScrollBarAlwaysOn
276
277        // hide the flickering by fading in nicely
278        opacity: setModelTimer.running ? 0 : 1
279        Behavior on opacity {
280            OpacityAnimator {
281                duration: PlasmaCore.Units.longDuration
282                easing.type: Easing.InOutQuad
283            }
284        }
285
286        GridView {
287            id: list
288
289            // model set delayed by Timer above
290
291            activeFocusOnTab: true
292            keyNavigationWraps: true
293            cellWidth: Math.floor((width - PlasmaCore.Units.smallSpacing) / 3)
294            cellHeight: cellWidth + PlasmaCore.Units.gridUnit * 4 + PlasmaCore.Units.smallSpacing * 2
295
296            delegate: AppletDelegate {}
297            highlight: PlasmaComponents.Highlight {}
298            highlightMoveDuration: 0
299            //highlightResizeDuration: 0
300
301            //slide in to view from the left
302            add: Transition {
303                NumberAnimation {
304                    properties: "x"
305                    from: -list.width
306                    duration: PlasmaCore.Units.shortDuration
307                }
308            }
309
310            //slide out of view to the right
311            remove: Transition {
312                NumberAnimation {
313                    properties: "x"
314                    to: list.width
315                    duration: PlasmaCore.Units.shortDuration
316                }
317            }
318
319            //if we are adding other items into the view use the same animation as normal adding
320            //this makes everything slide in together
321            //if we make it move everything ends up weird
322            addDisplaced: list.add
323
324            //moved due to filtering
325            displaced: Transition {
326                NumberAnimation {
327                    properties: "x,y"
328                    duration: PlasmaCore.Units.shortDuration
329                }
330            }
331
332            PlasmaExtras.Heading {
333                anchors.fill: parent
334                anchors.margins: PlasmaCore.Units.largeSpacing
335                horizontalAlignment: Text.AlignHCenter
336                verticalAlignment: Text.AlignVCenter
337                wrapMode: Text.WordWrap
338                level: 2
339                text: searchInput.text.length > 0 ? i18n("No widgets matched the search terms") : i18n("No widgets available")
340                enabled: false
341                visible: list.count == 0
342            }
343        }
344    }
345}
346