aerothemeplasma/plasma/plasmoids/org.kde.desktopcontainment/contents/ui/FolderItemDelegate.qml
2024-08-09 03:20:25 +02:00

517 lines
19 KiB
QML

/*
SPDX-FileCopyrightText: 2014-2015 Eike Hein <hein@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQuick 2.15
import QtQuick.Window 2.15
import Qt5Compat.GraphicalEffects
import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.core as PlasmaCore
import org.kde.plasma.extras as PlasmaExtras
import org.kde.plasma.components as PlasmaComponents
import org.kde.kirigami 2.20 as Kirigami
import org.kde.ksvg 1.0 as KSvg
import org.kde.kquickcontrolsaddons 2.0
Item {
id: main
property int index: model.index
property string name: model.blank ? "" : model.display
property string nameWrapped: model.blank ? "" : model.displayWrapped
property bool blank: model.blank
property bool isDir: loader.item ? loader.item.isDir : false
property QtObject popupDialog: loader.item ? loader.item.popupDialog : null
property Item iconArea: loader.item ? loader.item.iconArea : null
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
property Item frame: loader.item ? loader.item.frame : null
property Item toolTip: loader.item ? loader.item.toolTip : null
Accessible.name: name
Accessible.role: Accessible.Canvas
// This MouseArea exists to intercept press and hold; preventing edit mode
// from being triggered when pressing and holding on an icon (if there is one).
MouseArea {
anchors.fill: parent
visible: !main.blank
}
function openPopup() {
if (isDir) {
loader.item.openPopup();
}
}
function closePopup() {
if (popupDialog) {
popupDialog.requestDestroy();
loader.item.popupDialog = null;
}
}
Loader {
id: loader
// 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
visible: status === Loader.Ready
active: !model.blank
sourceComponent: delegateImplementation
asynchronous: true
}
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
property bool hovered: (main.GridView.view.hoveredItem === main)
property QtObject popupDialog: null
property Item iconArea: icon
property Item label: label
property Item labelArea: label
property Item actionsOverlay: actions
property Item hoverArea: toolTip
property Item frame: frameLoader
property Item toolTip: toolTip
property Item selectionButton: null
property Item popupButton: null
readonly property bool iconAndLabelsShouldlookSelected: impl.hovered
// When a drop happens, a new item is created, and is set to selected
// grabToImagebefore it gets the final width, making grabToImage fail because it's still 0x0
onSelectedChanged: Qt.callLater(updateDragImage)
function updateDragImage() {
if (selected && !blank) {
frameLoader.grabToImage(result => {
dir.addItemDragImage(positioner.map(index), main.x + frameLoader.x, main.y + frameLoader.y, frameLoader.width, frameLoader.height, result.image);
});
}
}
Connections {
target: model
function onSelectedChanged() {
if (dir.usedByContainment && model.selected) {
gridView.currentIndex = model.index;
}
}
}
onHoveredChanged: {
if (hovered) {
// In list view, it behaves more like a menu, and menus always activate their items on a single click
if (Plasmoid.configuration.selectionMarkers && (Qt.styleHints.singleClickActivation || root.useListViewMode)) {
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) {
if (popupDialog != null) {
closePopup();
}
if (selectionButton) {
selectionButton.destroy();
selectionButton = null;
}
if (popupButton) {
popupButton.destroy();
popupButton = null;
}
}
}
function openPopup() {
if (folderViewDialogComponent.status === Component.Ready) {
impl.popupDialog = folderViewDialogComponent.createObject(impl);
impl.popupDialog.visualParent = icon;
impl.popupDialog.url = model.linkDestinationUrl;
impl.popupDialog.visible = true;
}
}
PlasmaCore.ToolTipArea {
id: toolTip
active: (Plasmoid.configuration.toolTips || label.truncated)
&& popupDialog === null
&& !model.blank
interactive: false
location: root.useListViewMode ? (Plasmoid.location === PlasmaCore.Types.LeftEdge ? PlasmaCore.Types.LeftEdge : PlasmaCore.Types.RightEdge) : Plasmoid.location
z: 999
//anchors.fill: parent
MouseArea {
id: ma
anchors.fill: parent
hoverEnabled: true
propagateComposedEvents: true
onPositionChanged: (mouse) => {
if (containsMouse && !model.blank) {
if (toolTip.active) {
toolTip.textFormat = Text.RichText;
toolTip.mainText = model.display;
if (model.size !== undefined) {
toolTip.subText = model.type + "<br>" + "Size: " + model.size;
} else {
toolTip.subText = model.type;
}
}
main.GridView.view.hoveredItem = main;
}
mouse.accepted = false;
}
}
states: [
State { // icon view
when: !root.useListViewMode
AnchorChanges {
target: toolTip
anchors.horizontalCenter: parent.horizontalCenter
}
PropertyChanges {
target: toolTip
x: Kirigami.Units.smallSpacing
y: Kirigami.Units.smallSpacing
width: parent.width - Kirigami.Units.smallSpacing
height: parent.height - Kirigami.Units.smallSpacing
//width: Math.max(icon.paintedWidth, label.paintedWidth)
//height: (label.y + label.paintedHeight)
//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
}
}
]
}
Loader {
id: frameLoader
x: 0//root.useListViewMode ? 0 : Kirigami.Units.smallSpacing
y: root.useListViewMode ? 0 : Kirigami.Units.smallSpacing
property Item iconShadow: null
property string prefix: ""
sourceComponent: frameComponent
active: impl.iconAndLabelsShouldlookSelected || model.selected
asynchronous: true
width: {
if (root.useListViewMode) {
if (main.GridView.view.overflowing) {
return parent.width// - Kirigami.Units.smallSpacing;
} else {
return parent.width;
}
}
return parent.width// - (Kirigami.Units.smallSpacing * 2);
}
height: root.useListViewMode
? parent.height
// the smallSpacings are for padding
: icon.height + (Kirigami.Units.iconSizes.small * label.lineCount) + (Kirigami.Units.smallSpacing * 3)
Kirigami.Icon {
id: icon
z: 2
states: [
State { // icon view
when: !root.useListViewMode
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
}
}
]
anchors {
topMargin: Kirigami.Units.smallSpacing
leftMargin: Kirigami.Units.smallSpacing
}
width: root.useListViewMode ? main.GridView.view.iconSize : (parent.width - 2 * Kirigami.Units.smallSpacing)
height: main.GridView.view.iconSize
opacity: {
if (root.useListViewMode && selectionButton) {
return 0.3;
}
if (model.isHidden) {
return 0.6;
}
return 1.0;
}
animated: false
source: model.decoration
}
PlasmaComponents.Label {
id: label
z: 2 // So it's always above the highlight effect
states: [
State { // icon view
when: !root.useListViewMode
AnchorChanges {
target: label
anchors.top: icon.bottom
anchors.horizontalCenter: parent.horizontalCenter
}
PropertyChanges {
target: label
anchors.topMargin: Kirigami.Units.smallSpacing
width: parent.width - Kirigami.Units.smallSpacing
maximumLineCount: Plasmoid.configuration.textLines
horizontalAlignment: Text.AlignHCenter
}
},
State { // list view
when: root.useListViewMode
AnchorChanges {
target: label
anchors.left: icon.right
anchors.verticalCenter: parent.verticalCenter
}
PropertyChanges {
target: label
anchors.leftMargin: Kirigami.Units.smallSpacing * 2
anchors.rightMargin: Kirigami.Units.smallSpacing * 2
width: parent.width - icon.width - (Kirigami.Units.smallSpacing * 4)
maximumLineCount: 1
horizontalAlignment: Text.AlignLeft
}
}
]
color: {
if (Plasmoid.isContainment) {
// In this situation there's a shadow or a background rect, both of which are always black
return "white";
}
if (model.selected) {
return Kirigami.Theme.highlightedTextColor;
}
return Kirigami.Theme.textColor;
}
//renderShadow: false //(Plasmoid.isContainment && (!editor || editor.targetItem !== main)) && Plasmoid.configuration.textShadows
opacity: model.isHidden ? 0.6 : 1
text: main.nameWrapped
elide: Text.ElideRight
//font.italic: model.isLink
wrapMode: (maximumLineCount === 1) ? Text.NoWrap : Text.Wrap
horizontalAlignment: Text.AlignHCenter
layer.enabled: true
layer.effect: DropShadow {
anchors.fill: label
z: 1
horizontalOffset: 1
verticalOffset: 1
radius: 3.0
samples: radius * 2
spread: 0.435
color: "#F9080808"
opacity: model.isHidden ? 0.6 : 1
source: label
visible: (Plasmoid.isContainment && (!editor || editor.targetItem !== main)) && Plasmoid.configuration.textShadows
}
}
Component {
id: frameComponent
PlasmaExtras.Highlight {
// Workaround for a bug where the frameComponent does not
// get unloaded when items are dragged to a different
// place on the desktop.
visible: this === frameLoader.item
hovered: impl.iconAndLabelsShouldlookSelected
pressed: model.selected
active: Window.active
}
}
Component {
id: selectionButtonComponent
FolderItemActionButton {
element: model.selected ? "remove" : "add"
onClicked: {
dir.toggleSelected(positioner.map(index));
main.GridView.view.currentIndex = index;
}
}
}
Component {
id: popupButtonComponent
FolderItemActionButton {
visible: main.GridView.view.isRootView && (popupDialog == null)
element: "open"
onClicked: {
dir.setSelected(positioner.map(index));
main.GridView.view.currentIndex = index;
openPopup();
}
}
}
Component {
id: iconShadowComponent
DropShadow {
anchors.fill: icon
z: 1
verticalOffset: 1
radius: 5.0
samples: radius * 2 + 1
spread: 0.05
color: "black"
opacity: model.isHidden ? 0.3 : 0.6
visible: Plasmoid.configuration.iconShadows
source: icon
}
}
}
Column {
id: actions
visible: {
if (main.GridView.view.isRootView && root.containsDrag) {
return false;
}
if (!main.GridView.view.isRootView && main.GridView.view.dialog && main.GridView.view.dialog.containsDrag) {
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
}
Component.onCompleted: {
if (Plasmoid.isContainment && main.GridView.view.isRootView && root.GraphicsInfo.api === GraphicsInfo.OpenGL) {
frameLoader.iconShadow = iconShadowComponent.createObject(frameLoader);
}
}
}
}
}