diff --git a/containments/desktop/package/contents/ui/AppletAppearance.qml b/containments/desktop/package/contents/ui/AppletAppearance.qml index d47b8d7d4..41b42cb87 100644 --- a/containments/desktop/package/contents/ui/AppletAppearance.qml +++ b/containments/desktop/package/contents/ui/AppletAppearance.qml @@ -1,613 +1,614 @@ /* * Copyright 2011-2013 Sebastian Kügler * Copyright 2011 Marco Martin * Copyright 2014 David Edmundson * Copyright 2015 Eike Hein * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 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 Library General Public License for more details * * You should have received a copy of the GNU Library 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. */ import QtQuick 2.0 import QtQuick.Layouts 1.1 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.extras 2.0 as PlasmaExtras import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons import org.kde.plasma.plasmoid 2.0 Item { id: appletItem property int handleWidth: iconSize property int minimumHandleHeight: 7 * (root.iconSize + 7) + margins.top + margins.bottom property int handleHeight: (height < minimumHandleHeight) ? minimumHandleHeight : height property string category - property bool showAppletHandle: false + property bool showAppletHandle: temporaryShowAppletHandle || toolBox.open + property bool temporaryShowAppletHandle: false property real controlsOpacity: (plasmoid.immutable || !showAppletHandle) ? 0 : 1 property string backgroundHints: "NoBackground" property bool hasBackground: false property bool handleMerged: (height > minimumHandleHeight && !appletHandle.forceFloating) property bool animationsEnabled: false property bool floating: true // turns off layoutManagment space handling for appletItem property alias innerEndHeight: mouseListener.endHeight property alias innerEndWidth: mouseListener.endWidth property alias innerHeight: mouseListener.height property alias innerWidth: mouseListener.width property int minimumWidth: Math.max(root.layoutManager.cellSize.width, appletContainer.minimumWidth + margins.left + margins.right); property int minimumHeight: Math.max(root.layoutManager.cellSize.height, appletContainer.minimumHeight + margins.top + margins.bottom); property int maximumWidth: appletContainer.maximumWidth + margins.left + margins.right; property int maximumHeight: appletContainer.maximumHeight + margins.top + margins.bottom; property alias applet: appletContainer.applet property Item contents: appletContainer property alias margins: plasmoidBackground.margins property alias imagePath: plasmoidBackground.imagePath visible: false QtObject { id: d property real lastX: 0 property real lastY: 0 } onMinimumWidthChanged: { if (width < minimumWidth) { releasePosition(); width = minimumWidth; positionItem(); if (showAppletHandle && !handleMerged) appletHandle.positionHandle(); } } onMinimumHeightChanged: { if (height < minimumHeight) { releasePosition(); height = minimumHeight; positionItem(); if (showAppletHandle && !handleMerged) appletHandle.positionHandle(); } } onMaximumWidthChanged: { if (width > maximumWidth) { releasePosition(); width = maximumWidth; positionItem(); if (showAppletHandle && !handleMerged) appletHandle.positionHandle(); } } onMaximumHeightChanged: { if (height > maximumHeight) { releasePosition(); height = maximumHeight; positionItem(); if (showAppletHandle && !handleMerged) appletHandle.positionHandle(); } } onHeightChanged: { if (height > maximumHeight) innerEndHeight = maximumHeight; else if (height < minimumHeight) innerEndHeight = minimumHeight; else innerEndHeight = height; } onWidthChanged: { if (width > maximumWidth) innerEndWidth = maximumWidth; else if (width < minimumWidth) innerEndWidth = minimumWidth; else innerEndWidth = width; } onXChanged: { if (animationsEnabled) { animationsEnabled = false; mouseListener.x += d.lastX - x; animationsEnabled = true; } mouseListener.x = mouseListener.endX = (width - innerEndWidth)/2; d.lastX = x; } onYChanged: { if (animationsEnabled) { animationsEnabled = false; mouseListener.y += d.lastY - y; animationsEnabled = true; } mouseListener.y = mouseListener.endY = (height - innerEndHeight)/2; d.lastY = y; } // use this function to position appletItem instead of root.layoutManager.positionItem(appletItem) function positionItem() { if (floating) return; root.layoutManager.positionItem(appletItem); root.layoutManager.saveItem(appletItem); } // use this function to free appletItem position instead of // root.layoutManager.setSpaceAvailable(appletItem.x, appletItem.y, appletItem.width, appletItem.height, true) function releasePosition() { if (!floating) root.layoutManager.setSpaceAvailable(x, y, width, height, true); } function updateBackgroundHints() { hasBackground = (applet.backgroundHints != "NoBackground"); if (applet.backgroundHints == 1) { appletItem.imagePath = "widgets/background"; backgroundHints = "StandardBackground"; } else if (applet.backgroundHints == 2) { appletItem.imagePath = "widgets/translucentbackground" backgroundHints = "TranslucentBackground"; } else if (applet.backgroundHints == 0) { appletItem.imagePath = "" backgroundHints = "NoBackground"; } else { backgroundHints = "DefaultBackground"; appletItem.imagePath = "widgets/background"; } //print("Backgroundhints changed: " + appletItem.imagePath); } KQuickControlsAddons.MouseEventListener { id: mouseListener // used as inner applet (which would not be sized over or under size limits ) // centered in appletItem.onXChanged and onYChanged due to allow animations on X and Y property int pressX: -1 property int pressY: -1 // for animations property int endHeight: minimumHeight property int endWidth: minimumWidth property int endX: 0 property int endY: 0 height: endHeight width: endWidth z: 10 hoverEnabled: true onPressed: { pressX = mouse.x; pressY = mouse.y; } onPressAndHold: { if (!plasmoid.immutable && plasmoid.configuration.pressToMove) { if (!dragMouseArea.dragging && !root.isDrag(pressX, pressY, mouse.x, mouse.y)) { - showAppletHandle = true; + temporaryShowAppletHandle = true; dragMouseArea.dragging = true; eventGenerator.sendGrabEventRecursive(appletItem, KQuickControlsAddons.EventGenerator.UngrabMouse); eventGenerator.sendGrabEvent(dragMouseArea, KQuickControlsAddons.EventGenerator.GrabMouse); eventGenerator.sendMouseEvent(dragMouseArea, KQuickControlsAddons.EventGenerator.MouseButtonPress, mouse.x, mouse.y, Qt.LeftButton, Qt.LeftButton, 0); } } pressX = -1; pressY = -1; } onContainsMouseChanged: { animationsEnabled = true; if (!plasmoid.immutable && (!plasmoid.configuration.pressToMove || !containsMouse)) { hoverTracker.restart(); } } Timer { id: hoverTracker repeat: false interval: root.handleDelay onTriggered: { if (mouseListener.containsMouse || (appletHandle.item && (appletHandle.item.containsMouse || appletHandle.item.pressed))) { if (!plasmoid.configuration.pressToMove) { - showAppletHandle = true; + temporaryShowAppletHandle = true; } } else if (!dragMouseArea.dragging) { - showAppletHandle = false; + temporaryShowAppletHandle = false; } } } Item { anchors { left: parent.left; top: parent.top; bottom: parent.bottom; } width: parent.width+handleWidth; z: mouseListener.z + 4 PlasmaCore.FrameSvgItem { id: plasmoidBackground visible: backgroundHints != PlasmaCore.Types.NoBackground imagePath: "widgets/background" anchors { left: parent.left; top: parent.top; bottom: parent.bottom; } width: parent.width - _handleWidth smooth: true property real _handleWidth: (showAppletHandle && handleMerged) ? 0 : handleWidth Behavior on _handleWidth { enabled: animationsEnabled NumberAnimation { duration: units.longDuration easing.type: Easing.InOutQuad } } } Connections { target: plasmoid onImmutableChanged: { // print(" TB dragMouseArea.visible: " + plasmoid.immutable) dragMouseArea.visible = !plasmoid.immutable; - showAppletHandle = false; + temporaryShowAppletHandle = false; } onAppletRemoved: { // print("Applet removed Applet-" + applet.id) if (applet.id == appletItem.applet.id) { // print("Destroying Applet-" + applet.id) root.layoutManager.saveRotation(appletItem); appletItem.releasePosition(); //applet.action("remove").trigger(); //appletItem.destroy() appletItem.destroy(); } } } Connections { target: applet onBackgroundHintsChanged: { // print("plasmoid.backgroundHintsChanged"); updateBackgroundHints(); } } MouseArea { id: dragMouseArea anchors.fill: parent z: appletContainer.z - 2 visible: !plasmoid.immutable property int zoffset: 1000 drag.target: appletItem property bool dragging: false // Set by mouseListener.onPressAndHold -- drag.active only becomes true on movement. function endDrag() { appletItem.z = appletItem.z - zoffset; repositionTimer.running = false; placeHolderPaint.opacity = 0; animationsEnabled = true; appletItem.floating = false; appletItem.positionItem(); root.layoutManager.save(); dragging = false; } onDraggingChanged: { cursorShape = dragging ? Qt.DragMoveCursor : Qt.ArrowCursor; } onPressed: { appletItem.z = appletItem.z + zoffset; animationsEnabled = false; mouse.accepted = true; appletItem.releasePosition(); appletItem.floating = true; placeHolder.syncWithItem(appletItem); placeHolderPaint.opacity = root.haloOpacity; } onPositionChanged: { var pos = mapToItem(root.parent, mouse.x, mouse.y); var newCont = plasmoid.containmentAt(pos.x, pos.y); if (newCont && newCont != plasmoid) { var newPos = newCont.mapFromApplet(plasmoid, pos.x, pos.y); newCont.addApplet(appletItem.applet, newPos.x, newPos.y); placeHolderPaint.opacity = 0; } else { placeHolder.syncWithItem(appletItem); } } onCanceled: { endDrag(); appletHandle.positionHandle(); } onReleased: { endDrag(); appletHandle.positionHandle(); } } Item { id: appletContainer anchors { fill: parent leftMargin: plasmoidBackground.margins.left rightMargin: plasmoidBackground.margins.right + handleWidth topMargin: plasmoidBackground.margins.top bottomMargin: plasmoidBackground.margins.bottom } z: mouseListener.z+1 property QtObject applet readonly property int maxInt: 1000000 // dirty hack to convert js number to qml int property var minimumSize: { var size; if (applet && applet.Layout) { var layout = applet.Layout size = { 'width': layout.minimumWidth, 'height': layout.minimumHeight }; } else { size = { 'width': 0, 'height': 0 }; } return size; } property var maximumSize: { var size; if (applet && applet.Layout) { var layout = applet.Layout size = { 'width': layout.maximumWidth, 'height': layout.maximumHeight }; } else { size = { 'width': Number.POSITIVE_INFINITY, 'height': Number.POSITIVE_INFINITY }; } if (size.width > maxInt) size.width = maxInt; if (size.height > maxInt) size.height = maxInt; return size; } onMinimumSizeChanged: { minimumHeight = minimumSize.height if (minimumHeight > maximumSize.height) maximumHeight = minimumHeight else if (maximumHeight !== maximumSize.height) maximumHeight = maximumSize.height minimumWidth = minimumSize.width if (minimumWidth > maximumSize.width) maximumWidth = minimumWidth else if (maximumWidth !== maximumSize.width) maximumWidth = maximumSize.width } onMaximumSizeChanged: { maximumHeight = maximumSize.height if (maximumHeight < minimumSize.height) minimumHeight = maximumHeight else if (minimumHeight !== minimumSize.height) minimumHeight = minimumSize.height maximumWidth = maximumSize.width if (maximumWidth < minimumSize.width) minimumWidth = maximumWidth else if (minimumWidth !== minimumSize.width) minimumWidth = minimumSize.width } property int minimumWidth: 0 property int minimumHeight: 0 property int maximumWidth: maxInt property int maximumHeight: maxInt function appletDestroyed() { // print("Applet DESTROYED."); appletItem.releasePosition(); applet.action("remove").trigger(); appletItem.destroy() } onAppletChanged: { if (!applet) { return; } applet.parent = appletContainer; applet.anchors.fill = appletContainer; updateBackgroundHints(); } Connections { target: appletHandle.item onRemoveApplet: { killAnim.running = true; } } Behavior on opacity { NumberAnimation { duration: units.longDuration easing.type: Easing.InOutQuad } } SequentialAnimation { id: killAnim PlasmaExtras.DisappearAnimation { targetItem: appletItem } ScriptAction { script: { appletItem.scale = 1; appletContainer.appletDestroyed(); } } } Loader { id: busyLoader anchors.centerIn: parent z: appletContainer.z + 1 active: applet && applet.busy source: "BusyOverlay.qml" asynchronous: true } Component.onCompleted: PlasmaExtras.AppearAnimation { targetItem: appletItem } } Loader { id: appletHandle z: appletContainer.z + 1 property bool forceFloating : false anchors { verticalCenter: parent.verticalCenter right: plasmoidBackground.right } Connections { target: appletItem onShowAppletHandleChanged: { if (appletItem.showAppletHandle) { appletItem.z += dragMouseArea.zoffset; appletHandle.positionHandle(); if (appletHandle.source == "") { appletHandle.source = "AppletHandle.qml"; } } else { appletItem.z -= dragMouseArea.zoffset; } } onHandleMergedChanged: { if (appletItem.handleMerged) { appletHandle.anchors.verticalCenterOffset = 0; } else { appletHandle.positionHandle(); } } } Connections { target: appletHandle.item onMoveFinished: { appletHandle.positionHandle(); } } function positionHandle() { // Don't show handle outside of desktop var available = plasmoid.availableScreenRect; var x = Math.min(Math.max(0, appletItem.x + mouseListener.endX), available.width - appletItem.width + mouseListener.endX); var y = Math.min(Math.max(0, appletItem.y + mouseListener.endY), available.height - appletItem.height + mouseListener.endY); var verticalCenter = (y + mouseListener.endHeight / 2); var topOutside = (verticalCenter - minimumHandleHeight / 2); var bottomOutside = verticalCenter + minimumHandleHeight / 2 - available.height; if (bottomOutside > 0) { anchors.verticalCenterOffset = -bottomOutside; } else if (topOutside < 0) { anchors.verticalCenterOffset = -topOutside; } else { anchors.verticalCenterOffset = 0; } var rightOutside = x + mouseListener.endWidth + handleWidth - available.width; appletHandle.anchors.rightMargin = appletItem.margins.right + Math.max(0, rightOutside); appletHandle.forceFloating = rightOutside > 0; } } } Behavior on x { enabled: animationsEnabled NumberAnimation { duration: units.longDuration easing.type: Easing.InOutQuad } } Behavior on y { enabled: animationsEnabled NumberAnimation { duration: units.longDuration easing.type: Easing.InOutQuad } } Behavior on width { enabled: animationsEnabled NumberAnimation { id: widthAnimation duration: units.longDuration easing.type: Easing.InOutQuad } } Behavior on height { enabled: animationsEnabled NumberAnimation { id: heightAnimation duration: units.longDuration easing.type: Easing.InOutQuad } } } Behavior on controlsOpacity { NumberAnimation { duration: units.longDuration easing.type: Easing.InOutQuad } } Component.onCompleted: { floating = false; layoutTimer.running = true layoutTimer.restart() visible = false } } diff --git a/containments/desktop/package/contents/ui/main.qml b/containments/desktop/package/contents/ui/main.qml index 3646d6e61..0efdc75bb 100644 --- a/containments/desktop/package/contents/ui/main.qml +++ b/containments/desktop/package/contents/ui/main.qml @@ -1,580 +1,583 @@ /*************************************************************************** * Copyright (C) 2011-2013 Sebastian Kügler * * Copyright (C) 2011 Marco Martin * * Copyright (C) 2014-2015 by Eike Hein * * * * 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 . * ***************************************************************************/ import QtQuick 2.4 import QtQuick.Layouts 1.1 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 import org.kde.draganddrop 2.0 as DragDrop import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons import org.kde.private.desktopcontainment.desktop 0.1 as Desktop import "LayoutManager.js" as LayoutManager import "FolderTools.js" as FolderTools FolderViewDropArea { id: root objectName: isFolder ? "folder" : "desktop" width: isPopup ? undefined : preferredWidth(false) // for the initial size when placed on the desktop Layout.minimumWidth: preferredWidth(true) Layout.preferredWidth: isPopup ? preferredWidth(false) : 0 // for the popup size to change at runtime when view mode changes Plasmoid.switchWidth: preferredWidth(true) height: isPopup ? undefined : preferredHeight(false) Layout.minimumHeight: preferredHeight(true) Layout.preferredHeight: isPopup ? preferredHeight(false) : 0 Plasmoid.switchHeight: preferredHeight(true) LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft LayoutMirroring.childrenInherit: true property bool isFolder: (plasmoid.pluginName == "org.kde.plasma.folder") property bool isContainment: ("containmentType" in plasmoid) property bool isPopup: (plasmoid.location != PlasmaCore.Types.Floating) property bool useListViewMode: isPopup && plasmoid.configuration.viewMode === 0 property Component appletAppearanceComponent property Item toolBox property var layoutManager: LayoutManager property int handleDelay: 800 property real haloOpacity: 0.5 property int iconSize: units.iconSizes.small property int iconWidth: iconSize property int iconHeight: iconWidth readonly property int hoverActivateDelay: 750 // Magic number that matches Dolphin's auto-expand folders delay. preventStealing: true // Plasmoid.title is set by a Binding {} in FolderViewLayer Plasmoid.toolTipSubText: "" Plasmoid.icon: (!plasmoid.configuration.useCustomIcon && folderViewLayer.ready) ? folderViewLayer.view.model.iconName : plasmoid.configuration.icon Plasmoid.compactRepresentation: (isFolder && !isContainment) ? compactRepresentation : null Plasmoid.associatedApplicationUrls: folderViewLayer.ready ? folderViewLayer.model.resolvedUrl : null onIconHeightChanged: updateGridSize() anchors { leftMargin: (isContainment && plasmoid.availableScreenRect) ? plasmoid.availableScreenRect.x : 0 topMargin: (isContainment && plasmoid.availableScreenRect) ? plasmoid.availableScreenRect.y : 0 // Don't apply the right margin if the folderView is in column mode and not overflowing. // In this way, the last column remains droppable even if a small part of the icon is behind a panel. rightMargin: folderViewLayer.ready && (folderViewLayer.view.overflowing || folderViewLayer.view.flow == GridView.FlowLeftToRight || folderViewLayer.view.layoutDirection == Qt.RightToLeft) && (isContainment && plasmoid.availableScreenRect) && parent ? parent.width - (plasmoid.availableScreenRect.x + plasmoid.availableScreenRect.width) : 0 // Same mechanism as the right margin but applied here to the bottom when the folderView is in row mode. bottomMargin: folderViewLayer.ready && (folderViewLayer.view.overflowing || folderViewLayer.view.flow == GridView.FlowTopToBottom) && (isContainment && plasmoid.availableScreenRect) && parent ? parent.height - (plasmoid.availableScreenRect.y + plasmoid.availableScreenRect.height) : 0 } Behavior on anchors.topMargin { NumberAnimation { duration: units.longDuration; easing.type: Easing.InOutQuad } } Behavior on anchors.leftMargin { NumberAnimation { duration: units.longDuration; easing.type: Easing.InOutQuad } } Behavior on anchors.rightMargin { NumberAnimation { duration: units.longDuration; easing.type: Easing.InOutQuad } } Behavior on anchors.bottomMargin { NumberAnimation { duration: units.longDuration; easing.type: Easing.InOutQuad } } function updateGridSize() { LayoutManager.cellSize.width = root.iconWidth + toolBoxSvg.elementSize("left").width + toolBoxSvg.elementSize("right").width LayoutManager.cellSize.height = root.iconHeight + toolBoxSvg.elementSize("top").height + toolBoxSvg.elementSize("bottom").height; LayoutManager.defaultAppletSize.width = LayoutManager.cellSize.width * 6; LayoutManager.defaultAppletSize.height = LayoutManager.cellSize.height * 6; layoutTimer.restart(); } function addLauncher(desktopUrl) { if (!isFolder) { return; } folderViewLayer.view.linkHere(desktopUrl); } function addApplet(applet, x, y) { if (!appletAppearanceComponent) { appletAppearanceComponent = Qt.createComponent("AppletAppearance.qml"); } if (appletAppearanceComponent.status !== Component.Ready) { console.warn("Error loading AppletAppearance.qml:", appletAppearanceComponent.errorString()); return; } var category = "Applet-" + applet.id; var container = appletAppearanceComponent.createObject(resultsFlow, { category: category }); applet.parent = container applet.visible = true; var config = LayoutManager.itemsConfig[category]; // We have it in the config. if (config !== undefined && config.width !== undefined && config.height !== undefined && config.width > 0 && config.height > 0) { container.width = config.width; container.height = config.height; // We have a default. } else if (applet.width > 0 && applet.height > 0) { container.width = applet.width; container.height = applet.height; // The container needs to be bigger than applet of margins factor. if (applet.backgroundHints != PlasmaCore.Types.NoBackground) { container.width += container.margins.left + container.margins.right; container.height += container.margins.top + container.margins.bottom; } // Give up, assign the global default. } else { container.width = LayoutManager.defaultAppletSize.width; container.height = LayoutManager.defaultAppletSize.height; } container.applet = applet; // Coordinated passed by param? if ( x >= 0 && y >= 0) { if (x + container.width > root.width) { x = root.width - container.width - 10; } if (y + container.height > root.height) { x = root.height - container.height; } // On applet undo or via scripting, the applet position will be saved // in applet's scene coordinates so remap it to resultsflow's coordinates. var pos = root.parent.mapToItem(resultsFlow, x, y); container.x = pos.x; container.y = pos.y; // To be sure it's restored at the same position, take margins into account // if there is a background. if (applet.backgroundHints != PlasmaCore.Types.NoBackground) { container.x -= container.margins.left; container.y -= container.margins.top; } // Coordinates stored? } else if (config !== undefined && config.x !== undefined && config.y !== undefined && config.x >= 0 && config.y >= 0) { container.x = config.x; container.y = config.y; } // Rotation stored and significant? if (config !== undefined && config.rotation !== undefined && (config.rotation > 5 || config.rotation < -5)) { container.rotation = config.rotation; } else { LayoutManager.restoreRotation(container); } LayoutManager.itemGroups[container.category] = container; if (container.x >= 0 && container.y >= 0) { LayoutManager.positionItem(container); } } function preferredWidth(minimum) { if (isContainment || !folderViewLayer.ready) { return -1; } else if (useListViewMode) { return (minimum ? folderViewLayer.view.cellHeight * 4 : units.gridUnit * 16); } return (folderViewLayer.view.cellWidth * (minimum ? 1 : 3)) + (units.largeSpacing * 2); } function preferredHeight(minimum) { if (isContainment || !folderViewLayer.ready) { return -1; } else if (useListViewMode) { var height = (folderViewLayer.view.cellHeight * (minimum ? 1 : 15)) + units.smallSpacing; } else { var height = (folderViewLayer.view.cellHeight * (minimum ? 1 : 2)) + units.largeSpacing } if (plasmoid.configuration.labelMode != 0) { height += folderViewLayer.item.labelHeight; } return height; } function isDrag(fromX, fromY, toX, toY) { var length = Math.abs(fromX - toX) + Math.abs(fromY - toY); return length >= Qt.styleHints.startDragDistance; } onDragEnter: { if (isContainment && plasmoid.immutable && !(isFolder && FolderTools.isFileDrag(event))) { event.ignore(); } } onDragMove: { // TODO: We should reject drag moves onto file items that don't accept drops // (cf. QAbstractItemModel::flags() here, but DeclarativeDropArea currently // is currently incapable of rejecting drag events. // Trigger autoscroll. if (isFolder && FolderTools.isFileDrag(event)) { handleDragMove(folderViewLayer.view, mapToItem(folderViewLayer.view, event.x, event.y)); } else if (isContainment) { placeHolder.width = LayoutManager.defaultAppletSize.width; placeHolder.height = LayoutManager.defaultAppletSize.height; placeHolder.minimumWidth = placeHolder.minimumHeight = 0; placeHolder.x = event.x - placeHolder.width / 2; placeHolder.y = event.y - placeHolder.width / 2; LayoutManager.positionItem(placeHolder); LayoutManager.setSpaceAvailable(placeHolder.x, placeHolder.y, placeHolder.width, placeHolder.height, true); placeHolderPaint.opacity = root.haloOpacity; } } onDragLeave: { // Cancel autoscroll. if (isFolder) { handleDragEnd(folderViewLayer.view); } if (isContainment) { placeHolderPaint.opacity = 0; } } onDrop: { if (isFolder && FolderTools.isFileDrag(event)) { handleDragEnd(folderViewLayer.view); folderViewLayer.view.drop(root, event, mapToItem(folderViewLayer.view, event.x, event.y)); } else if (isContainment) { placeHolderPaint.opacity = 0; var pos = root.parent.mapFromItem(resultsFlow, event.x - placeHolder.width / 2, event.y - placeHolder.height / 2); plasmoid.processMimeData(event.mimeData, pos.x, pos.y); event.accept(event.proposedAction); } } Component { id: compactRepresentation CompactRepresentation { folderView: folderViewLayer.view } } Connections { target: plasmoid ignoreUnknownSignals: true onAppletAdded: { addApplet(applet, x, y); // Clean any eventual invalid chunks in the config. LayoutManager.save(); } onAppletRemoved: { // Clean any eventual invalid chunks in the config. LayoutManager.removeApplet(applet); LayoutManager.save(); } onImmutableChanged: { if (!plasmoid.immutable) { pressToMoveHelp.show(); } } onAvailableScreenRegionChanged: layoutTimer.restart(); } Connections { target: plasmoid.configuration onPressToMoveChanged: { if (plasmoid.configuration.pressToMove && plasmoid.configuration.pressToMoveHelp && !plasmoid.immutable) { pressToMoveHelp.show(); } } } Binding { target: toolBox property: "visible" value: plasmoid.configuration.showToolbox } Desktop.InfoNotification { id: pressToMoveHelp enabled: plasmoid.configuration.pressToMove && plasmoid.configuration.pressToMoveHelp iconName: "plasma" titleText: i18n("Widgets unlocked") text: i18n("You can press and hold widgets to move them and reveal their handles.") acknowledgeActionText: i18n("Got it") onAcknowledged: { plasmoid.configuration.pressToMoveHelp = false; } } PlasmaCore.FrameSvgItem { id : highlightItemSvg visible: false imagePath: isPopup ? "widgets/viewitem" : "" prefix: "hover" } PlasmaCore.FrameSvgItem { id : listItemSvg visible: false imagePath: isPopup ? "widgets/viewitem" : "" prefix: "normal" } PlasmaCore.Svg { id: toolBoxSvg imagePath: "widgets/toolbox" property int rightBorder: elementSize("right").width property int topBorder: elementSize("top").height property int bottomBorder: elementSize("bottom").height property int leftBorder: elementSize("left").width } PlasmaCore.Svg { id: configIconsSvg imagePath: "widgets/configuration-icons" } KQuickControlsAddons.EventGenerator { id: eventGenerator } MouseArea { // unfocus any plasmoid when clicking empty desktop area anchors.fill: parent onPressed: { root.forceActiveFocus() mouse.accepted = false // Bug 351277 + if (toolBox && toolBox.open) { + toolBox.open = false; + } } } Loader { id: folderViewLayer anchors.fill: parent property bool ready: status == Loader.Ready property Item view: item ? item.view : null property QtObject model: item ? item.model : null focus: true active: isFolder asynchronous: false source: "FolderViewLayer.qml" onFocusChanged: { if (!focus && model) { model.clearSelection(); } } Connections { target: folderViewLayer.view onPressed: { folderViewLayer.focus = true; } } } Item { id: resultsFlow anchors.fill: parent anchors { top: parent.top topMargin: 5 horizontalCenter: parent.horizontalCenter } visible: isContainment enabled: isContainment // This is just for event compression when a lot of boxes are created one after the other. Timer { id: layoutTimer repeat: false running: false interval: 100 onTriggered: { LayoutManager.resetPositions() for (var i=0; i 0 Behavior on opacity { NumberAnimation { duration: units.longDuration easing.type: Easing.InOutQuad } } } } Component.onCompleted: { if (!isContainment) { return; } plasmoid.action("configure").text = i18n("Configure Desktop"); // WORKAROUND: that's the only place where we can inject a sensible size. // if root has width defined, it will override the value we set before // the component completes root.width = plasmoid.width; LayoutManager.resultsFlow = resultsFlow; LayoutManager.plasmoid = plasmoid; updateGridSize(); LayoutManager.restore(); for (var i = 0; i < plasmoid.applets.length; ++i) { var applet = plasmoid.applets[i]; addApplet(applet, -1, -1); } // Clean any eventual invalid chunks in the config. LayoutManager.save(); } } diff --git a/toolboxes/desktoptoolbox/contents/ui/ToolBoxButton.qml b/toolboxes/desktoptoolbox/contents/ui/ToolBoxButton.qml index 2612ddf60..2db76f861 100644 --- a/toolboxes/desktoptoolbox/contents/ui/ToolBoxButton.qml +++ b/toolboxes/desktoptoolbox/contents/ui/ToolBoxButton.qml @@ -1,311 +1,310 @@ /*************************************************************************** * Copyright 2012,2015 by Sebastian Kügler * * Copyright 2015 by Kai Uwe Broulik * * * * 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 . * ***************************************************************************/ import QtQuick 2.4 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons import org.kde.plasma.plasmoid 2.0 Item { id: toolBoxButton property string text: main.Plasmoid.activityName property bool isCorner: !buttonMouse.dragging && ((state == "topleft") || (state == "topright") || (state == "bottomright") || (state == "bottomleft")) property bool isHorizontal: (state != "left" && state != "right") rotation: switch(state) { case "left": return -90; case "right": return 90; default: return 0; } transform: Translate { x: state == "left" ? Math.round(-width/2 + height/2) : state == "right" ? + Math.round(width/2 - height/2) : 0 Behavior on x { NumberAnimation { duration: units.shortDuration * 3; easing.type: Easing.InOutExpo; } } } transformOrigin: Item.Center Behavior on rotation { NumberAnimation { duration: units.shortDuration * 3; easing.type: Easing.InOutExpo; } enabled: visible } Behavior on x { NumberAnimation { duration: units.shortDuration * 3; easing.type: Easing.InOutExpo; } enabled: visible } Behavior on y { NumberAnimation { duration: units.shortDuration * 3; easing.type: Easing.InOutExpo; } enabled: visible } clip: backgroundFrameWidthAnimation.running width: backgroundFrame.width + backgroundFrame.width % 2 height: backgroundFrame.height + backgroundFrame.height % 2 //x and y default to 0, so top left would be correct //If the position is anything else it will updated via onXChanged during intialisation state: "topleft" onXChanged: stateTimer.restart() onYChanged: stateTimer.restart() Timer { id: stateTimer interval: 100 onTriggered: updateState() } function updateState() { var container = main; //print(" w: " + container.width +"x"+container.height+" : "+x+"/"+y+" tbw: " + toolBoxButton.width); var x = toolBoxButton.x - main.x; var y = toolBoxButton.y - main.y; var cornerSnap = iconWidth if (x < cornerSnap && y < cornerSnap) { toolBoxButton.state = "topleft"; } else if (container.width - x - buttonLayout.width < cornerSnap && y < cornerSnap) { toolBoxButton.state = "topright"; } else if (container.width - x - buttonLayout.width < cornerSnap && container.height - y - buttonLayout.width < cornerSnap) { toolBoxButton.state = "bottomright"; } else if (x < cornerSnap && container.height - y - buttonLayout.width < cornerSnap) { toolBoxButton.state = "bottomleft"; //top diagonal half } else if (x > (y * (container.width/container.height))) { //Top edge if (container.width - x > y ) { toolBoxButton.state = "top"; //right edge } else { //toolBoxButton.transformOrigin = Item.BottomRight toolBoxButton.state = "right"; } //bottom diagonal half } else { //Left edge if (container.height - y > x ) { //toolBoxButton.transformOrigin = Item.TopLeft toolBoxButton.state = "left"; //Bottom edge } else { toolBoxButton.state = "bottom"; } } if (!buttonMouse.pressed) { main.placeToolBox(toolBoxButton.state); } } PlasmaCore.FrameSvgItem { id: backgroundFrame anchors { left: parent.left top: parent.top } imagePath: "widgets/translucentbackground" opacity: buttonMouse.containsMouse || (toolBoxLoader.item && toolBoxLoader.item.visible) ? 1.0 : 0.4 width: Math.round((isCorner ? buttonLayout.height : buttonLayout.width) + margins.horizontal) height: Math.round(buttonLayout.height + margins.vertical) Behavior on width { NumberAnimation { id: backgroundFrameWidthAnimation duration: units.longDuration; easing.type: Easing.InOutQuad; } } Behavior on opacity { NumberAnimation { duration: units.longDuration; } } } Row { id: buttonLayout anchors.centerIn: parent height: Math.max(toolBoxIcon.height, fontMetrics.height) spacing: units.smallSpacing Behavior on x { NumberAnimation { duration: units.longDuration; easing.type: Easing.InOutQuad; } } PlasmaCore.SvgItem { id: toolBoxIcon svg: PlasmaCore.Svg { id: iconSvg imagePath: "widgets/configuration-icons" onRepaintNeeded: toolBoxIcon.elementId = iconSvg.hasElement("menu") ? "menu" : "configure" } elementId: iconSvg.hasElement("menu") ? "menu" : "configure" anchors.verticalCenter: parent.verticalCenter width: iconSize height: iconSize opacity: buttonMouse.containsMouse || (toolBoxLoader.item && toolBoxLoader.item.visible) ? 1 : 0.5 rotation: isHorizontal ? 0 : -90; transformOrigin: Item.Center Behavior on opacity { NumberAnimation { duration: units.longDuration; easing.type: Easing.InOutExpo; } } } PlasmaComponents.Label { id: activityName anchors.verticalCenter: parent.verticalCenter opacity: isCorner ? 0 : 1 text: toolBoxButton.text visible: opacity Behavior on opacity { //only have this animation when going from hidden -> shown enabled: activityName.opacity == 0 SequentialAnimation { //pause to allow the toolbox frame to resize //otherwise we see the text overflow the box //whilst that animates PauseAnimation { duration: units.longDuration } NumberAnimation { duration: units.shortDuration easing.type: Easing.InOutExpo } } } } FontMetrics { id: fontMetrics font: activityName.font } } MouseArea { id: buttonMouse property QtObject container: main property int pressedX property int pressedY property bool dragging: false anchors { fill: parent margins: -10 } drag { target: main.Plasmoid.immutable ? undefined : toolBoxButton minimumX: 0 maximumX: container.width - toolBoxIcon.width minimumY: 0 maximumY: container.height - toolBoxIcon.height } hoverEnabled: true onPressed: { pressedX = toolBoxButton.x pressedY = toolBoxButton.y } onPositionChanged: { if (pressed && (Math.abs(toolBoxButton.x - pressedX) > iconSize || Math.abs(toolBoxButton.y - pressedY) > iconSize)) { dragging = true; } } onClicked: { - toolBoxLoader.active = true // the dialog auto-closes on losing focus - toolBoxLoader.item.visible = !main.dialogWasVisible + main.open = !main.dialogWasVisible plasmoid.focus = true; } onReleased: { main.Plasmoid.configuration.ToolBoxButtonState = toolBoxButton.state; main.Plasmoid.configuration.ToolBoxButtonX = toolBoxButton.x; main.Plasmoid.configuration.ToolBoxButtonY = toolBoxButton.y; //print("Saved coordinates for ToolBox in config: " + toolBoxButton.x + ", " +toolBoxButton.x); if (dragging) { main.placeToolBox(); } dragging = false; stateTimer.stop(); updateState(); } onCanceled: dragging = false; } states: [ State { name: "topleft" }, State { name: "top" }, State { name: "topright" }, State { name: "right" }, State { name: "bottomright" }, State { name: "bottom" }, State { name: "bottomleft" }, State { name: "topleft" }, State { name: "left" } ] } diff --git a/toolboxes/desktoptoolbox/contents/ui/ToolBoxItem.qml b/toolboxes/desktoptoolbox/contents/ui/ToolBoxItem.qml index 77722c229..c0ca5c1ea 100644 --- a/toolboxes/desktoptoolbox/contents/ui/ToolBoxItem.qml +++ b/toolboxes/desktoptoolbox/contents/ui/ToolBoxItem.qml @@ -1,158 +1,158 @@ /*************************************************************************** * Copyright 2012 by Sebastian Kügler * * Copyright 2015 by Kai Uwe Broulik * * * * 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 . * ***************************************************************************/ import QtQuick 2.0 import QtQuick.Layouts 1.1 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents ListView { id: menu width: units.gridUnit * 14 height: contentHeight currentIndex: -1 focus: true keyNavigationWraps: true onVisibleChanged: currentIndex = -1 // needs to be on released, otherwise Dashboard hides because it already gained focus // because of the dialog closing right on the key *press* event Keys.onReleased: { if (event.key === Qt.Key_Escape) { - toolBoxLoader.item.visible = false + main.open = false event.accepted = true } } Keys.onPressed: { if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { if (currentIndex >= 0) { if (model[currentIndex].operation) { performOperation(model[currentIndex].operation) } else { model[currentIndex].trigger() } } - toolBoxLoader.item.visible = false + main.open = false event.accepted = true } } function performOperation(what) { var service = dataEngine.serviceForSource("PowerDevil"); var operation = service.operationDescription(what); return service.startOperationCall(operation); } highlightMoveDuration: 0 highlightResizeDuration: 0 highlight: PlasmaComponents.Highlight { } PlasmaCore.DataSource { id: dataEngine engine: "powermanagement" connectedSources: ["PowerDevil", "Sleep States"] } model: { var model = [] var actions = plasmoid.actions for (var i = 0, j = actions.length; i < j; ++i) { var action = actions[i] if (action && action.visible && action.text !== "") { model.push(action) } } if (dataEngine.data["Sleep States"].LockScreen) { model.push({ text: i18nd("plasma_toolbox_org.kde.desktoptoolbox", "Lock Screen"), icon: "system-lock-screen", visible: true, enabled: true, operation: "lockScreen" }) } if (dataEngine.data["Sleep States"].Logout) { model.push({ text: i18nd("plasma_toolbox_org.kde.desktoptoolbox", "Leave"), icon: "system-log-out", visible: true, enabled: true, operation: "requestShutDown" // cannot put function() into a model :( }) } return model } delegate: MouseArea { width: menu.width height: labelRow.implicitHeight + units.smallSpacing * 2 hoverEnabled: true enabled: modelData.enabled opacity: modelData.enabled ? 1 : 0.5 onEntered: menu.currentIndex = index onExited: menu.currentIndex = -1 onClicked: { - toolBoxLoader.item.visible = false + main.open = false if (modelData.operation) { performOperation(modelData.operation) } else { modelData.trigger() } } Accessible.role: Accessible.MenuItem Accessible.name: textLabel.text RowLayout { id: labelRow anchors { left: parent.left right: parent.right margins: units.smallSpacing verticalCenter: parent.verticalCenter } spacing: units.smallSpacing PlasmaCore.IconItem { implicitWidth: units.iconSizes.smallMedium implicitHeight: implicitWidth source: modelData.icon Accessible.ignored: true } PlasmaComponents.Label { id: textLabel Layout.fillWidth: true text: modelData.text.replace("&", "") // hack to get rid of keyboard accelerator hints elide: Text.ElideRight Accessible.ignored: true } } } } diff --git a/toolboxes/desktoptoolbox/contents/ui/ToolBoxRoot.qml b/toolboxes/desktoptoolbox/contents/ui/ToolBoxRoot.qml index c23472d8c..52ce97c79 100644 --- a/toolboxes/desktoptoolbox/contents/ui/ToolBoxRoot.qml +++ b/toolboxes/desktoptoolbox/contents/ui/ToolBoxRoot.qml @@ -1,170 +1,202 @@ /* * Copyright 2011 Sebastian Kügler * Copyright 2011 Marco Martin * Copyright 2015 Kai Uwe Broulik * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 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 Library General Public License for more details * * You should have received a copy of the GNU Library 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. */ import QtQuick 2.2 +import QtQuick.Window 2.2 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons import org.kde.plasma.extras 2.0 as PlasmaExtras import org.kde.plasma.plasmoid 2.0 Item { id: main objectName: "org.kde.desktoptoolbox" z: 999 anchors.fill: parent Connections { target: plasmoid onAvailableScreenRegionChanged: placeToolBoxTimer.restart(); } + //FIXME: this timer shouldn't exist, but unfortunately when the focus passes + //from the desktop to the dialog or vice versa, the event is not atomic + //and ends up with neither of those having focus, hiding the dialog when + //it shouldn't + Timer { + id: hideDialogTimer + interval: 0 + //NOTE: it's checking activeFocusItem instead of active as active doesn't correctly signal its change + property bool desktopOrDialogFocus: main.Window.activeFocusItem != null || (toolBoxLoader.item && toolBoxLoader.item.activeFocusItem != null) + onDesktopOrDialogFocusChanged: { + if (!desktopOrDialogFocus) { + hideDialogTimer.restart(); + } + + } + onTriggered: { + if (!desktopOrDialogFocus) { + open = false; + } + } + } + signal minimumWidthChanged signal minimumHeightChanged signal maximumWidthChanged signal maximumHeightChanged signal preferredWidthChanged signal preferredHeightChanged property int iconSize: units.iconSizes.small property int iconWidth: units.iconSizes.smallMedium property int iconHeight: iconWidth property bool dialogWasVisible: false + property bool open: false + onOpenChanged: { + if (open) { + toolBoxLoader.active = true; + toolBoxLoader.item.visible = true; + } else { + toolBoxLoader.item.visible = false; + } + } onWidthChanged: placeToolBoxTimer.restart(); onHeightChanged: placeToolBoxTimer.restart(); LayoutMirroring.enabled: (Qt.application.layoutDirection === Qt.RightToLeft) LayoutMirroring.childrenInherit: true onVisibleChanged: { if (!visible && toolBoxLoader.item) { toolBoxLoader.item.visible = false } } ToolBoxButton { id: toolBoxButton visible: false Component.onCompleted: { placeToolBox(plasmoid.configuration.ToolBoxButtonState); toolBoxButton.visible = true } Behavior on x { XAnimator { duration: units.longDuration; easing.type: Easing.InOutQuad } // avoid the toolbox sliding over the screen on startup // placeToolBox is initially called before the toolBoxButton is visible // so those Behaviors won't fire on startup enabled: toolBoxButton.visible } Behavior on y { YAnimator { duration: units.longDuration; easing.type: Easing.InOutQuad } enabled: toolBoxButton.visible } } Timer { id: placeToolBoxTimer interval: 100 repeat: false running: false onTriggered: { placeToolBox(plasmoid.configuration.ToolBoxButtonState); } } Loader { id: toolBoxLoader active: false sourceComponent: PlasmaCore.Dialog { flags: Qt.WindowStaysOnTopHint location: PlasmaCore.Types.Floating visualParent: toolBoxButton - hideOnWindowDeactivate: true + // hideOnWindowDeactivate: true mainItem: ToolBoxItem { id: dialog LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft LayoutMirroring.childrenInherit: true Timer { id: visibleTimer interval: 300 onTriggered: main.dialogWasVisible = dialog.visible } } onVisibleChanged: visibleTimer.restart(); } } function placeToolBox(ts) { var tx = Plasmoid.configuration.ToolBoxButtonX var ty = Plasmoid.configuration.ToolBoxButtonY var pos; switch (ts) { case "top": ty = main.y; pos = plasmoid.adjustToAvailableScreenRegion(tx, ty, toolBoxButton.width, toolBoxButton.height); break; case "left": tx = main.x; pos = plasmoid.adjustToAvailableScreenRegion(tx, ty, toolBoxButton.width, toolBoxButton.height); break; case "right": tx = main.width + main.x - toolBoxButton.width; pos = plasmoid.adjustToAvailableScreenRegion(tx, ty, toolBoxButton.width, toolBoxButton.height); break; case "bottom": ty = main.height + main.y - toolBoxButton.height; pos = plasmoid.adjustToAvailableScreenRegion(tx, ty, toolBoxButton.width, toolBoxButton.height); break; case "topleft": tx = main.x; ty = main.y; pos = plasmoid.adjustToAvailableScreenRegion(tx, ty, toolBoxButton.height, toolBoxButton.height); break; case "topright": tx = main.width + main.x - toolBoxButton.height; ty = main.y; pos = plasmoid.adjustToAvailableScreenRegion(tx, ty, toolBoxButton.height, toolBoxButton.height); break; case "bottomleft": tx = main.x; ty = main.height + main.y - toolBoxButton.height; pos = plasmoid.adjustToAvailableScreenRegion(tx, ty, toolBoxButton.height, toolBoxButton.height); break; case "bottomright": default: tx = main.width + main.x - toolBoxButton.height; ty = main.height + main.y - toolBoxButton.height; pos = plasmoid.adjustToAvailableScreenRegion(tx, ty, toolBoxButton.height, toolBoxButton.height); break; } //print("XXXY Setting toolbox to: " + ts + " " + tx + "x" + ty + " screen: " + main.width+ "x" + main.height+""); toolBoxButton.x = pos.x; toolBoxButton.y = pos.y; } }