diff --git a/containments/desktop/package/contents/ui/AppletAppearance.qml b/containments/desktop/package/contents/ui/AppletAppearance.qml index e10a51368..523da06d4 100644 --- a/containments/desktop/package/contents/ui/AppletAppearance.qml +++ b/containments/desktop/package/contents/ui/AppletAppearance.qml @@ -1,404 +1,449 @@ /* * 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: 6 * (root.iconSize + 6) + margins.top + margins.bottom property int handleHeight: (height < minimumHandleHeight) ? minimumHandleHeight : height property string category property bool showAppletHandle: false property real controlsOpacity: (plasmoid.immutable || !showAppletHandle) ? 0 : 1 property string backgroundHints: "NoBackground" property bool hasBackground: false - property bool handleMerged: (height > minimumHandleHeight) + property bool handleMerged: (height > minimumHandleHeight && !appletHandle.forceFloating) property bool animationsEnabled: false property int minimumWidth: Math.max(root.layoutManager.cellSize.width, appletContainer.minimumWidth + appletItem.contents.anchors.leftMargin + appletItem.contents.anchors.rightMargin) property int minimumHeight: Math.max(root.layoutManager.cellSize.height, appletContainer.minimumHeight + appletItem.contents.anchors.topMargin + appletItem.contents.anchors.bottomMargin) property alias applet: appletContainer.applet property Item contents: appletContainer property alias margins: plasmoidBackground.margins property alias imagePath: plasmoidBackground.imagePath visible: false onMinimumWidthChanged: appletItem.width = Math.max(minimumWidth, width); onMinimumHeightChanged: appletItem.height = Math.max(minimumHeight, height); 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); } Rectangle { color: Qt.rgba(0,0,0,0); border.width: 3; border.color: "white"; opacity: 0.5; visible: debug; anchors.fill: parent; } KQuickControlsAddons.MouseEventListener { id: mouseListener anchors { fill: parent } z: 10 hoverEnabled: true property int pressX: -1 property int pressY: -1 onPressed: { pressX = mouse.x; pressY = mouse.y; } onPressAndHold: { if (!plasmoid.immutable && plasmoid.configuration.pressToMove) { if (!dragMouseArea.dragging && !systemSettings.isDrag(pressX, pressY, mouse.x, mouse.y)) { showAppletHandle = 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; } } else if (!dragMouseArea.dragging) { showAppletHandle = 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: (showAppletHandle && handleMerged) ? parent.width : parent.width-handleWidth; smooth: true Behavior on width { 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; } onAppletRemoved: { // print("Applet removed Applet-" + applet.id) if (applet.id == appletItem.applet.id) { // print("Destroying Applet-" + applet.id) root.layoutManager.setSpaceAvailable(appletItem.x, appletItem.y, appletItem.width, appletItem.height, true) //applet.action("remove").trigger(); //appletItem.destroy() appletItem.destroy(); } } } Connections { target: applet onBusyChanged: { if (applet.busy) { busyLoader.source = "BusyOverlay.qml" } else if (busyLoader.item && typeof(busyLoader.item) != "undefined") { busyLoader.item.disappear(); } } 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; root.layoutManager.positionItem(appletItem); root.layoutManager.save(); dragging = false; } onDraggingChanged: { cursorShape = dragging ? Qt.DragMoveCursor : Qt.ArrowCursor; } onPressed: { appletItem.z = appletItem.z + zoffset; animationsEnabled = plasmoid.configuration.pressToMove ? true : false; mouse.accepted = true; var x = Math.round(appletItem.x / root.layoutManager.cellSize.width) * root.layoutManager.cellSize.width; var y = Math.round(appletItem.y / root.layoutManager.cellSize.height) * root.layoutManager.cellSize.height; root.layoutManager.setSpaceAvailable(x, y, appletItem.width, appletItem.height, 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() - onReleased: endDrag() + 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 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 int minimumWidth: minimumSize.width; property int minimumHeight: minimumSize.height; function appletDestroyed() { // print("Applet DESTROYED."); root.layoutManager.setSpaceAvailable(appletItem.x, appletItem.y, appletItem.width, appletItem.height, true) 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 } // Rectangle { color: "green"; opacity: 0.3; visible: debug; anchors.fill: parent; } Component.onCompleted: PlasmaExtras.AppearAnimation { targetItem: appletItem } } Loader { id: appletHandle z: appletContainer.z + 1 + property bool forceFloating : false anchors { verticalCenter: parent.verticalCenter right: plasmoidBackground.right - rightMargin: appletItem.margins.right } Connections { target: appletItem onShowAppletHandleChanged: { - if (appletItem.showAppletHandle && appletHandle.source == "") { - //print("Loading applethandle "); - appletHandle.source = "AppletHandle.qml"; + 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), available.width - appletItem.width); + var y = Math.min(Math.max(0, appletItem.y), available.height - appletItem.height); + var verticalCenter = (y + appletItem.height / 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 + appletItem.width + handleWidth - available.width; + appletHandle.anchors.rightMargin = appletItem.margins.right + Math.max(0, rightOutside); + appletHandle.forceFloating = rightOutside > 0; + } } // Rectangle { color: "orange"; opacity: 0.1; visible: debug; anchors.fill: parent; } } } Behavior on controlsOpacity { NumberAnimation { duration: units.longDuration easing.type: Easing.InOutQuad } } 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 } } Component.onCompleted: { layoutTimer.running = true layoutTimer.restart() visible = false // restore rotation } } diff --git a/containments/desktop/package/contents/ui/AppletHandle.qml b/containments/desktop/package/contents/ui/AppletHandle.qml index 980d45c77..f256de8aa 100644 --- a/containments/desktop/package/contents/ui/AppletHandle.qml +++ b/containments/desktop/package/contents/ui/AppletHandle.qml @@ -1,329 +1,331 @@ /* * Copyright 2011-2013 Sebastian Kügler * Copyright 2011 Marco Martin * Copyright 2014 David Edmundson * * 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.kquickcontrolsaddons 2.0 as KQuickControlsAddons import org.kde.plasma.components 2.0 as PlasmaComponents KQuickControlsAddons.MouseEventListener { id: appletHandle opacity: appletItem.controlsOpacity visible: opacity > 0 width: appletItem.handleWidth height: Math.max(appletItem.height - appletItem.margins.top - appletItem.margins.bottom, buttonColumn.implicitHeight) hoverEnabled: true onContainsMouseChanged: { if (!containsMouse && !pressed) { hoverTracker.restart() } } onReleased: { if (!containsMouse) { hoverTracker.restart() } } //z: dragMouseArea.z + 1 property int buttonMargin: 6 property int minimumHeight: 6 * (root.iconSize + buttonMargin) signal removeApplet + signal moveFinished transform: Translate { x: handleMerged ? 0 : controlsOpacity * appletHandle.width } PlasmaCore.FrameSvgItem { id: noBackgroundHandle visible: controlsOpacity > 0 z: plasmoidBackground.z - 10 anchors { top: parent.top bottom: parent.bottom left: parent.left right: parent.right // verticalCenter: parent.verticalCenter leftMargin: -margins.left topMargin: -margins.top rightMargin: -margins.right bottomMargin: -margins.bottom } smooth: true imagePath: (backgroundHints == "NoBackground" || !handleMerged) ? "widgets/background" : "" } PlasmaComponents.Label { id: toolTipDelegate width: contentWidth height: contentHeight property Item toolTip text: (toolTip != null) ? toolTip.mainText : "" } ColumnLayout { id: buttonColumn width: handleWidth anchors { top: parent.top bottom: parent.bottom right: parent.right } spacing: buttonMargin*2 ActionButton { svg: configIconsSvg elementId: "size-diagonal-tr2bl" iconSize: root.iconSize mainText: i18n("Resize") active: !resizeHandle.pressed MouseArea { id: resizeHandle anchors { fill: parent margins: -buttonMargin } property int startX property int startY onPressed: { parent.hideToolTip(); mouse.accepted = true animationsEnabled = false; startX = mouse.x; startY = mouse.y; root.layoutManager.setSpaceAvailable(appletItem.x, appletItem.y, appletItem.width, appletItem.height, true) appletContainer.clip = true } onPositionChanged: { appletItem.width = Math.max(appletItem.minimumWidth + appletHandle.width, appletItem.width + mouse.x-startX); var oldBottom = appletItem.y + appletItem.height; appletItem.height = Math.max(appletItem.minimumHeight, appletItem.height + startY-mouse.y) appletItem.y = oldBottom - appletItem.height; } onReleased: { animationsEnabled = true root.layoutManager.positionItem(appletItem) root.layoutManager.save() root.layoutManager.setSpaceAvailable(appletItem.x, appletItem.y, widthAnimation.to, heightAnimation.to, false) appletContainer.clip = false } // Rectangle { color: "blue"; opacity: 0.4; visible: debug; anchors.fill: parent; } } } ActionButton { id: rotateButton svg: configIconsSvg elementId: "rotate" mainText: i18n("Rotate") iconSize: root.iconSize action: (applet) ? applet.action("rotate") : null active: !rotateHandle.pressed Component.onCompleted: { if (action && typeof(action) != "undefined") { action.enabled = true } } MouseArea { id: rotateHandle anchors { fill: parent margins: -buttonMargin } property int startRotation property real startCenterRelativeAngle; function pointAngle(pos) { var r = Math.sqrt(pos.x * pos.x + pos.y * pos.y); var cosine = pos.x / r; if (pos.y >= 0) { return Math.acos(cosine) * (180/Math.PI); } else { return -Math.acos(cosine) * (180/Math.PI); } } function centerRelativePos(x, y) { var mousePos = appletItem.parent.mapFromItem(rotateButton, x, y); var centerPos = appletItem.parent.mapFromItem(appletItem, appletItem.width/2, appletItem.height/2); mousePos.x -= centerPos.x; mousePos.y -= centerPos.y; return mousePos; } onPressed: { parent.hideToolTip(); mouse.accepted = true animationsEnabled = false; startRotation = appletItem.rotation; startCenterRelativeAngle = pointAngle(centerRelativePos(mouse.x, mouse.y)); } onPositionChanged: { var rot = startRotation%360; var snap = 4; var newRotation = Math.round(pointAngle(centerRelativePos(mouse.x, mouse.y)) - startCenterRelativeAngle + startRotation); if (newRotation < 0) { newRotation = newRotation + 360; } else if (newRotation >= 360) { newRotation = newRotation % 360; } snapIt(0); snapIt(90); snapIt(180); snapIt(270); function snapIt(snapTo) { if (newRotation > (snapTo - snap) && newRotation < (snapTo + snap)) { newRotation = snapTo; } } //print("Start: " + startRotation + " new: " + newRotation); appletItem.rotation = newRotation; } onReleased: { // save rotation // print("saving..."); root.layoutManager.saveItem(appletItem); } // Rectangle { color: "red"; opacity: 0.6; visible: debug; anchors.fill: parent; } } } ActionButton { svg: configIconsSvg elementId: "configure" iconSize: root.iconSize visible: (action && typeof(action) != "undefined") ? action.enabled : false action: (applet) ? applet.action("configure") : null Component.onCompleted: { if (action && typeof(action) != "undefined") { action.enabled = true } } } ActionButton { svg: configIconsSvg elementId: "maximize" iconSize: root.iconSize visible: (action && typeof(action) != "undefined") ? action.enabled : false action: (applet) ? applet.action("run associated application") : null Component.onCompleted: { if (action && typeof(action) != "undefined") { action.enabled = true } } } //spacer MouseArea { id: dragMouseArea implicitHeight: root.iconSize * 2 Layout.fillWidth: true Layout.fillHeight: true property int zoffset: 1000 drag.target: appletItem cursorShape: Qt.DragMoveCursor onPressed: { appletItem.z = appletItem.z + zoffset; animationsEnabled = false mouse.accepted = true var x = Math.round(appletItem.x / root.layoutManager.cellSize.width) * root.layoutManager.cellSize.width var y = Math.round(appletItem.y / root.layoutManager.cellSize.height) * root.layoutManager.cellSize.height root.layoutManager.setSpaceAvailable(x, y, appletItem.width, appletItem.height, 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); } } onReleased: { appletItem.z = appletItem.z - zoffset; repositionTimer.running = false placeHolderPaint.opacity = 0 animationsEnabled = true root.layoutManager.positionItem(appletItem) root.layoutManager.save() + appletHandle.moveFinished() } } Item { Layout.minimumWidth: closeButton.width Layout.minimumHeight: closeButton.height ActionButton { id: closeButton svg: configIconsSvg elementId: "close" mainText: i18n("Remove") iconSize: root.iconSize visible: { if (!applet) { return false; } var a = applet.action("remove"); return (a && typeof(a) != "undefined") ? a.enabled : false; } // we don't set action, since we want to catch the button click, // animate, and then trigger the "remove" action // Triggering the action is handled in the appletItem, we just // emit a signal here to avoid the applet-gets-removed-before-we- // can-animate it race condition. onClicked: { appletHandle.removeApplet(); } Component.onCompleted: { var a = applet.action("remove"); if (a && typeof(a) != "undefined") { a.enabled = true } } } } } }