FolderItemDelegate.qml 19.8 KB
Newer Older
1
/***************************************************************************
2
 *   Copyright (C) 2014-2015 by Eike Hein <hein@kde.org>                   *
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA .        *
 ***************************************************************************/

20
import QtQuick 2.8
21
import QtQuick.Window 2.2
22
23
import QtGraphicalEffects 1.0

24
25
26
27
import org.kde.plasma.plasmoid 2.0

import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 2.0 as PlasmaComponents
28
import org.kde.kquickcontrolsaddons 2.0
29
30
31
32
33

Item {
    id: main

    property int index: model.index
34
35
    property string name: model.blank ? "" : model.display
    property bool blank: model.blank
36
    property bool isDir: loader.item ? loader.item.isDir : false
37
    property QtObject popupDialog: loader.item ? loader.item.popupDialog : null
38
    property Item iconArea: loader.item ? loader.item.iconArea : null
39
40
41
42
    property Item label: loader.item ? loader.item.label : null
    property Item labelArea: loader.item ? loader.item.labelArea : null
    property Item actionsOverlay: loader.item ? loader.item.actionsOverlay : null
    property Item hoverArea: loader.item ? loader.item.hoverArea : null
43
    property Item frame: loader.item ? loader.item.frame : null
44
    property Item toolTip: loader.item ? loader.item.toolTip : null
45
46
    Accessible.name: name
    Accessible.role: Accessible.Canvas
47

48
49
50
51
52
53
    function openPopup() {
        if (isDir) {
            loader.item.openPopup();
        }
    }

54
55
56
57
58
59
60
    function closePopup() {
        if (popupDialog) {
            popupDialog.requestDestroy();
            loader.item.popupDialog = null;
        }
    }

61
62
    Loader {
        id: loader
63

64
65
66
67
68
69
70
        // On the desktop we pad our cellSize to avoid a gap at the right/bottom of the screen.
        // The padding per item is quite small and causes the delegate to be positioned on fractional pixels
        // leading to blurry rendering. The Loader is offset to account for this.
        x: -main.x % 1
        y: -main.y % 1
        width: parent.width
        height: parent.height
71

72
73
        visible: status === Loader.Ready

74
        active: !model.blank
75

76
        sourceComponent: delegateImplementation
77
78

        asynchronous: true
79
80
    }

81
82
83
84
85
86
87
88
89
90
91
    Component {
        id: delegateImplementation

        Item {
            id: impl

            anchors.fill: parent

            property bool blank: model.blank
            property bool selected: model.blank ? false : model.selected
            property bool isDir: model.blank ? false : model.isDir
Laurent Montel's avatar
Laurent Montel committed
92
            property bool hovered: (main.GridView.view.hoveredItem === main)
93
            property QtObject popupDialog: null
94
            property Item iconArea: icon
95
            property Item label: label
96
            property Item labelArea: frameLoader.textShadow || label
97
98
            property Item actionsOverlay: actions
            property Item hoverArea: toolTip
99
            property Item frame: frameLoader
100
            property Item toolTip: toolTip
101
            property Item selectionButton: null
102
            property Item popupButton: null
103

104
105
            onSelectedChanged: {
                if (selected && !blank) {
106
107
                    frameLoader.grabToImage(function(result) {
                        dir.addItemDragImage(positioner.map(index), main.x + frameLoader.x, main.y + frameLoader.y, frameLoader.width, frameLoader.height, result.image);
Eike Hein's avatar
Eike Hein committed
108
                    });
109
                }
110
111
            }

112
            onHoveredChanged: {
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
                if (hovered) {
                    if (plasmoid.configuration.selectionMarkers && Qt.styleHints.singleClickActivation) {
                        selectionButton = selectionButtonComponent.createObject(actions);
                    }

                    if (model.isDir) {
                        if (!main.GridView.view.isRootView || root.containsDrag) {
                            hoverActivateTimer.restart();
                        }

                        if (plasmoid.configuration.popups && !root.useListViewMode) {
                            popupButton = popupButtonComponent.createObject(actions);
                        }
                    }
                } else if (!hovered) {
128
                    if (popupDialog != null) {
129
                        closePopup();
130
                    }
131
132
133
134
135
136
137
138
139
140
141

                    if (selectionButton) {
                        selectionButton.destroy();
                        selectionButton = null;
                    }

                    if (popupButton) {
                        popupButton.destroy();
                        popupButton = null;
                    }
                }
142
            }
143

144
            function openPopup() {
145
146
                if (folderViewDialogComponent.status == Component.Ready) {
                    impl.popupDialog = folderViewDialogComponent.createObject(impl);
147
                    impl.popupDialog.visualParent = icon;
148
                    impl.popupDialog.url = model.linkDestinationUrl;
149
150
151
                    impl.popupDialog.visible = true;
                }
            }
152

153
154
            PlasmaCore.ToolTipArea {
                id: toolTip
155

156
                active: (plasmoid.configuration.toolTips && popupDialog == null && !model.blank)
157
                interactive: false
Laurent Montel's avatar
Laurent Montel committed
158
                location: root.useListViewMode ? (plasmoid.location === PlasmaCore.Types.LeftEdge ? PlasmaCore.Types.LeftEdge : PlasmaCore.Types.RightEdge) : plasmoid.location
159

160
161
                onContainsMouseChanged:  {
                    if (containsMouse && !model.blank) {
162
163
164
165
166
167
168
169
170
                        if (toolTip.active) {
                            toolTip.icon = model.decoration;
                            toolTip.mainText = model.display;

                            if (model.size !== undefined) {
                                    toolTip.subText = model.type + "\n" + model.size;
                            } else {
                                toolTip.subText = model.type;
                            }
Eike Hein's avatar
Eike Hein committed
171
172
                        }

173
174
175
                        main.GridView.view.hoveredItem = main;
                    }
                }
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

                states: [
                    State { // icon view
                        when: !root.useListViewMode

                        AnchorChanges {
                            target: toolTip
                            anchors.horizontalCenter: parent.horizontalCenter
                        }

                        PropertyChanges {
                            target: toolTip
                            y: frameLoader.y + icon.y
                            width: Math.max(icon.paintedWidth, label.paintedWidth)
                            height: (label.y + label.paintedHeight) - y
                        }
                    },
                    State { // list view
                        when: root.useListViewMode

                        AnchorChanges {
                            target: toolTip
                            anchors.horizontalCenter: undefined
                        }

                        PropertyChanges {
                            target: toolTip
                            x: frameLoader.x
                            y: frameLoader.y
                            width: frameLoader.width
                            height: frameLoader.height
                        }
                    }
                ]
210
211
            }

212
213
            Loader {
                id: frameLoader
214

215
216
                x: root.useListViewMode ? 0 : units.smallSpacing
                y: root.useListViewMode ? 0 : units.smallSpacing
217

218
                property Item textShadow: null
219
                property Item iconShadow: null
220
221
222
223
224
225
                property string prefix: ""

                sourceComponent: frameComponent
                active: state !== ""
                asynchronous: true

226
                width: {
227
                    if (root.useListViewMode) {
228
229
230
231
232
233
234
235
236
237
238
                        if (main.GridView.view.overflowing) {
                            return parent.width - units.smallSpacing;
                        } else {
                            return parent.width;
                        }
                    }

                    return parent.width - (units.smallSpacing * 2);
                }

                height: {
239
                    if (root.useListViewMode) {
240
241
242
                        return parent.height;
                    }

243
244
245
246
247
248
                    // Note: frameLoader.y = units.smallSpacing (acts as top margin)
                    return (units.smallSpacing // icon.anchors.topMargin (acts as top padding)
                        + icon.height
                        + units.smallSpacing // label.anchors.topMargin (acts as spacing between icon and label)
                        + (label.lineCount * theme.mSize(theme.defaultFont).height)
                        + units.smallSpacing); // leftover (acts as bottom padding)
249
                }
250

251
252
                PlasmaCore.IconItem {
                    id: icon
253

254
                    z: 2
255

256
257
258
                    states: [
                        State { // icon view
                            when: !root.useListViewMode
259

260
261
262
263
264
265
266
267
268
269
270
271
272
                            AnchorChanges {
                                target: icon
                                anchors.top: parent.top
                                anchors.horizontalCenter: parent.horizontalCenter
                            }
                        },
                        State { // list view
                            when: root.useListViewMode

                            AnchorChanges {
                                target: icon
                                anchors.left: parent.left
                                anchors.verticalCenter: parent.verticalCenter
273
                            }
274
                        }
275
                    ]
276

277
                    anchors {
278
                        topMargin: units.smallSpacing
279
                        leftMargin: units.smallSpacing
280
                    }
Eike Hein's avatar
Eike Hein committed
281

282
283
                    width: root.useListViewMode ? main.GridView.view.iconSize : (parent.width - 2 * units.smallSpacing)
                    height: main.GridView.view.iconSize
284

285
286
287
288
289
290
291
292
293
294
295
                    opacity: {
                        if (root.useListViewMode && selectionButton) {
                            return 0.3;
                        }

                        if (model.isHidden) {
                            return 0.6;
                        }

                        return 1.0;
                    }
296

297
298
                    animated: false
                    usesPlasmaTheme: false
299

Eike Hein's avatar
Eike Hein committed
300
301
                    smooth: true

302
303
304
                    source: model.decoration
                    overlays: model.overlays
                }
305

306
307
                PlasmaComponents.Label {
                    id: label
308

309
                    z: 2 // So we can position a textShadowComponent below if needed.
310

311
312
313
                    states: [
                        State { // icon view
                            when: !root.useListViewMode
314

315
316
317
318
319
320
321
                            AnchorChanges {
                                target: label
                                anchors.top: icon.bottom
                                anchors.horizontalCenter: parent.horizontalCenter
                            }
                            PropertyChanges {
                                target: label
322
                                anchors.topMargin: units.smallSpacing
Fabian Vogt's avatar
Fabian Vogt committed
323
                                width: Math.round(Math.min(label.implicitWidth + units.smallSpacing, parent.width - units.smallSpacing))
324
325
326
327
328
329
330
331
332
333
334
                                maximumLineCount: plasmoid.configuration.textLines
                                horizontalAlignment: Text.AlignHCenter
                            }
                        },
                        State { // list view
                            when: root.useListViewMode

                            AnchorChanges {
                                target: label
                                anchors.left: icon.right
                                anchors.verticalCenter: parent.verticalCenter
335
                            }
336
337
338
339
340
341
342
343
344
345
                            PropertyChanges {
                                target: label
                                anchors.leftMargin: units.smallSpacing * 2
                                anchors.rightMargin: units.smallSpacing * 2
                                width: parent.width - icon.width - (units.smallSpacing * 4)
                                maximumLineCount: 1
                                horizontalAlignment: Text.AlignLeft
                            }
                        }
                    ]
346

347
                    height: undefined // Unset PlasmaComponents.Label's default.
348

349
                    textFormat: Text.PlainText
350

Eike Hein's avatar
Eike Hein committed
351
                    wrapMode: (maximumLineCount == 1) ? Text.NoWrap : Text.Wrap
352
                    elide: Text.ElideRight
353

354
355
356
357
358
359
360
361
362
                    color: {
                        if (frameLoader.textShadow && frameLoader.textShadow.visible) {
                            return "#fff";
                        } else if (model.selected) {
                            return PlasmaCore.ColorScope.highlightedTextColor;
                        } else {
                            return PlasmaCore.ColorScope.textColor
                        }
                    }
363

364
365
                    opacity: model.isHidden ? 0.6 : 1

366
                    text: model.blank ? "" : model.display
367

368
                    font.italic: model.isLink
369

370
                    visible: {
Kai Uwe Broulik's avatar
Kai Uwe Broulik committed
371
                        if (editor && editor.targetItem === main) {
372
373
374
375
376
377
378
379
380
381
                            return false;
                        }

                        // DropShadow renders the Label already.
                        if (frameLoader.textShadow && frameLoader.textShadow.visible) {
                            return false;
                        }

                        return true;
                    }
382
                }
383

384
385
386
387
388
389
390
                Component {
                    id: frameComponent

                    PlasmaCore.FrameSvgItem {
                        prefix: frameLoader.prefix

                        imagePath: "widgets/viewitem"
391
392
393

                        // Use inactive highlight effect when something else has focus
                        opacity: Window.active ? 1 : 0.4
394
395
                    }
                }
396

397
398
                Component {
                    id: selectionButtonComponent
399

400
                    FolderItemActionButton {
401
                        element: model.selected ? "remove" : "add"
402

403
404
405
406
                        onClicked: {
                            dir.toggleSelected(positioner.map(index))
                            main.GridView.view.currentIndex = index
                        }
407
                    }
408
                }
409
410
411
412

                Component {
                    id: popupButtonComponent

413
                    FolderItemActionButton {
414
                        visible: main.GridView.view.isRootView && (popupDialog == null)
415
416
417

                        element: "open"

418
419
                        onClicked: {
                            dir.setSelected(positioner.map(index))
420
                            main.GridView.view.currentIndex = index
421
422
                            openPopup();
                        }
423
424
425
                    }
                }

426
427
428
429
430
431
432
433
434
435
                Component {
                    id: iconShadowComponent

                    DropShadow {
                        anchors.fill: icon

                        z: 1

                        verticalOffset: 1

436
437
                        radius: Math.round(5 * units.devicePixelRatio)
                        samples: radius * 2 + 1
438
439
440
441
442
443
444
445
446
447
448
449
                        spread: 0.05

                        color: "black"

                        opacity: model.isHidden ? 0.3 : 0.6

                        source: icon

                        visible: !editor || editor.targetItem != main
                    }
                }

450
451
452
453
454
455
456
457
                Component {
                    id: textShadowComponent

                    DropShadow {
                        anchors.fill: label

                        z: 1

458
459
                        horizontalOffset: 1
                        verticalOffset: 1
460

461
462
                        radius: Math.round(4 * units.devicePixelRatio)
                        samples: radius * 2 + 1
463
                        spread: 0.35
464
465
466

                        color: "black"

467
468
                        opacity: model.isHidden ? 0.6 : 1

469
                        source: label
470

471
                        visible: !editor || editor.targetItem != main
472
                    }
473
                }
474
475
476
477
478
479
480

                states: [
                    State {
                        name: "selected"
                        when: model.selected

                        PropertyChanges {
481
                            target: frameLoader
482
483
484
485
486
                            prefix: "selected"
                        }
                    },
                    State {
                        name: "hover"
Eike Hein's avatar
Eike Hein committed
487
                        when: hovered && !model.selected && plasmoid.configuration.iconHoverEffect
488
489

                        PropertyChanges {
490
                            target: frameLoader
491
492
493
494
495
                            prefix: "hover"
                        }
                    },
                    State {
                        name: "selected+hover"
Eike Hein's avatar
Eike Hein committed
496
                        when: hovered && model.selected && plasmoid.configuration.iconHoverEffect
497
498

                        PropertyChanges {
499
                            target: frameLoader
500
501
502
503
                            prefix: "selected+hover"
                        }
                    }
                ]
504
            }
505

506
507
508
509
510
511
512
513
            Column {
                id: actions

                visible: {
                    if (main.GridView.view.isRootView && root.containsDrag) {
                        return false;
                    }

Eike Hein's avatar
Eike Hein committed
514
                    if (!main.GridView.view.isRootView && main.GridView.view.dialog.containsDrag) {
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
                        return false;
                    }

                    if (popupDialog) {
                        return false;
                    }

                    return true;
                }

                anchors {
                    left: frameLoader.left
                    top: frameLoader.top
                    leftMargin: root.useListViewMode ? (icon.x + (icon.width / 2)) - (width / 2) : 0
                    topMargin: root.useListViewMode ? (icon.y + (icon.height / 2)) - (height / 2)  : 0
                }

                width: implicitWidth
                height: implicitHeight
            }

536
            Component.onCompleted: {
537
                if (root.isContainment && main.GridView.view.isRootView && root.GraphicsInfo.api === GraphicsInfo.OpenGL) {
538
                    frameLoader.textShadow = textShadowComponent.createObject(frameLoader);
539
                    frameLoader.iconShadow = iconShadowComponent.createObject(frameLoader);
540
541
                }
            }
542
        }
543
544
    }
}