Commit ae555600 authored by Marco Martin's avatar Marco Martin
Browse files

support global edit mode

Summary:
desktop and panel toolboxes are only visible in "edit mode"
edit mode can be triggered by
* long press in desktop
* context menu action
* dbus property (to be eventually used by systemsettings)
The desktop toolbox becomes a "toolbar" with just few selected actions
the presence of that toolbar both fullfills "long press should usually show some
contextual actions" and being a very visible hint that "we are now in edit mode"
toolbox is still movable around in case would obscure any important stuff where it's
by default

Depends on D24263
Depends on D24264
Closes T10402

Test Plan: ui works as planned, toolbox position is saved and restored correctly

Reviewers: #plasma, ngraham

Reviewed By: ngraham

Subscribers: davidedmundson, ngraham, GB_2, plasma-devel

Tags: #plasma

Maniphest Tasks: T10190, T11094, T10402, T8751

Differential Revision: https://phabricator.kde.org/D24265
parent 04e42646
......@@ -205,8 +205,12 @@ ContainmentLayoutManager.ConfigOverlayWithHandles {
Layout.fillHeight: true
Layout.fillWidth: true
cursorShape: Qt.DragMoveCursor
hoverEnabled: true
onPressed: appletsLayout.releaseSpace(overlay.itemContainer);
onPositionChanged: {
if (!pressed) {
return;
}
appletsLayout.showPlaceHolderForItem(overlay.itemContainer);
var dragPos = mapToItem(overlay.itemContainer, mouse.x, mouse.y);
overlay.itemContainer.userDrag(Qt.point(overlay.itemContainer.x, overlay.itemContainer.y), dragPos);
......
......@@ -37,6 +37,7 @@ FocusScope {
property QtObject model: dir
property Item rubberBand: null
property alias view: gridView
property alias isRootView: gridView.isRootView
property alias currentIndex: gridView.currentIndex
property alias url: dir.url
......@@ -175,16 +176,6 @@ FocusScope {
}
}
// Lower the toolBox when an item is hovered, so it doesn't interfere with
// its interaction (e.g. the selection button in the top left, cf. Bug 337060)
Binding {
target: toolBox
property: "z"
// 999 is the default "z" for desktop ToolBoxRoot
value: main.hoveredItem ? -100 : 999
when: toolBox
}
Binding {
target: plasmoid
property: "busy"
......
......@@ -329,7 +329,7 @@ FolderViewDropArea {
containment: plasmoid
editModeCondition: plasmoid.immutable
? ContainmentLayoutManager.AppletsLayout.Locked
: ContainmentLayoutManager.AppletsLayout.Manual
: ContainmentLayoutManager.AppletsLayout.AfterPressAndHold
// Sets the containment in edit mode when we go in edit mode as well
onEditModeChanged: plasmoid.editMode = editMode
......@@ -340,6 +340,8 @@ FolderViewDropArea {
cellWidth: units.iconSizes.small
cellHeight: cellWidth
eventManagerToFilter: folderViewLayer.item ? folderViewLayer.item.view.view : null
appletContainerComponent: ContainmentLayoutManager.BasicAppletContainer {
id: appletContainer
editModeCondition: plasmoid.immutable
......
......@@ -421,10 +421,10 @@ function checkLastSpacer() {
interval: 150
onTriggered: {
dndSpacer.parent = root;
currentLayout.x = (isHorizontal && toolBox && Qt.application.layoutDirection === Qt.RightToLeft && (plasmoid.editMode || plasmoid.userConfiguring)) ? toolBox.width : 0;
currentLayout.x = (isHorizontal && toolBox && Qt.application.layoutDirection === Qt.RightToLeft && plasmoid.editMode) ? toolBox.width : 0;
currentLayout.y = 0
currentLayout.width = root.width - (isHorizontal && toolBox && (plasmoid.editMode || plasmoid.userConfiguring) ? toolBox.width : 0)
currentLayout.height = root.height - (!isHorizontal && toolBox && (plasmoid.editMode || plasmoid.userConfiguring) ? toolBox.height : 0)
currentLayout.width = root.width - (isHorizontal && toolBox && plasmoid.editMode ? toolBox.width : 0)
currentLayout.height = root.height - (!isHorizontal && toolBox && plasmoid.editMode ? toolBox.height : 0)
currentLayout.isLayoutHorizontal = isHorizontal
}
}
......
......@@ -19,39 +19,38 @@
***************************************************************************/
import QtQuick 2.4
import QtQuick.Layouts 1.4
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.components 3.0 as PlasmaComponents3
import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons
import org.kde.plasma.plasmoid 2.0
Item {
id: toolBoxButton
property string text: main.Plasmoid.activityName === i18n("Default") ? i18n("Desktop Toolbox") : i18n("Desktop Toolbox — %1 Activity", 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 {
y: plasmoid.editMode ? 0
: state == "top" || state == "topcenter" ? -height
: state == "bottom" || state == "bottomcenter" ? height
: 0
Behavior on y {
NumberAnimation {
duration: units.shortDuration * 3;
easing.type: Easing.InOutExpo;
duration: units.longDuration
easing.type: Easing.InOutQuad
}
}
}
transformOrigin: Item.Center
opacity: plasmoid.editMode
Behavior on opacity {
OpacityAnimator {
duration: units.longDuration
easing.type: Easing.InOutQuad
}
enabled: visible
}
Behavior on rotation {
NumberAnimation {
duration: units.shortDuration * 3;
......@@ -75,20 +74,17 @@ Item {
enabled: visible
}
clip: backgroundFrameWidthAnimation.running
width: backgroundFrame.width + backgroundFrame.width % 2
height: backgroundFrame.height + backgroundFrame.height % 2
width: buttonLayout.width
height: buttonLayout.height
//x and y default to 0, so top left would be correct
//If the position is anything else it will updated via onXChanged during initialization
state: "topleft"
state: "topcenter"
onXChanged: stateTimer.restart()
onYChanged: stateTimer.restart()
Timer {
id: stateTimer
interval: 100
interval: 0
onTriggered: updateState()
}
function updateState() {
......@@ -100,31 +96,17 @@ Item {
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
//top
if (y + height / 2 < container.height / 2) {
if (Math.abs(container.width/2 - (x + width/2)) < units.gridUnit) {
toolBoxButton.state = "topcenter";
} else {
//toolBoxButton.transformOrigin = Item.BottomRight
toolBoxButton.state = "right";
toolBoxButton.state = "top";
}
//bottom diagonal half
//bottom
} else {
//Left edge
if (container.height - y > x ) {
//toolBoxButton.transformOrigin = Item.TopLeft
toolBoxButton.state = "left";
//Bottom edge
if (Math.abs(container.width/2 - (x + height/2)) < units.gridUnit) {
toolBoxButton.state = "bottomcenter";
} else {
toolBoxButton.state = "bottom";
}
......@@ -140,91 +122,15 @@ Item {
PlasmaCore.FrameSvgItem {
id: backgroundFrame
anchors {
left: parent.left
top: parent.top
fill: parent
leftMargin: -backgroundFrame.margins.left
topMargin: -backgroundFrame.margins.top
rightMargin: -backgroundFrame.margins.right
bottomMargin: -backgroundFrame.margins.bottom
}
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)
imagePath: "widgets/background"
width: Math.round(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 {
......@@ -233,19 +139,19 @@ Item {
property QtObject container: main
property int pressedX
property int pressedY
property int snapStartX
property bool snapX: false;
property bool dragging: false
anchors {
fill: parent
margins: -10
}
anchors.fill: parent
drag {
filterChildren: true
target: main.Plasmoid.immutable ? undefined : toolBoxButton
minimumX: 0
maximumX: container.width - toolBoxIcon.width
maximumX: container.width - toolBoxButton.width
minimumY: 0
maximumY: container.height - toolBoxIcon.height
maximumY: container.height
}
hoverEnabled: true
......@@ -259,13 +165,22 @@ Item {
Math.abs(toolBoxButton.y - pressedY) > iconSize)) {
dragging = true;
}
// Center snapping X
if (snapX && Math.abs(snapStartX - mouse.x) > units.gridUnit) {
toolBoxButton.anchors.horizontalCenter = undefined;
snapX = false;
} else if (!snapX && Math.abs(main.width/2 - (toolBoxButton.x + toolBoxButton.width/2)) < units.gridUnit) {
toolBoxButton.anchors.horizontalCenter = main.horizontalCenter;
snapStartX = mouse.x;
snapX = true;
}
}
onClicked: {
// the dialog auto-closes on losing focus
main.open = !main.dialogWasVisible
plasmoid.focus = true;
}
onReleased: {
toolBoxButton.anchors.horizontalCenter = undefined;
toolBoxButton.anchors.verticalCenter = undefined;
snapX = false;
main.Plasmoid.configuration.ToolBoxButtonState = toolBoxButton.state;
main.Plasmoid.configuration.ToolBoxButtonX = toolBoxButton.x;
main.Plasmoid.configuration.ToolBoxButtonY = toolBoxButton.y;
......@@ -278,32 +193,43 @@ Item {
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: "left"
RowLayout {
id: buttonLayout
anchors.centerIn: parent
spacing: units.smallSpacing
PlasmaComponents3.ToolButton {
property QtObject qAction: plasmoid.action("add widgets")
text: qAction.text
icon.name: "list-add"
icon.height: units.iconSizes.smallMedium
onClicked: qAction.trigger()
}
PlasmaComponents3.ToolButton {
property QtObject qAction: plasmoid.globalAction("manage activities")
text: qAction.text
icon.name: "activities"
icon.height: units.iconSizes.smallMedium
onClicked: qAction.trigger()
}
PlasmaComponents3.ToolButton {
property QtObject qAction: plasmoid.action("configure")
text: qAction.text
icon.name: "preferences-desktop-wallpaper"
icon.height: units.iconSizes.smallMedium
onClicked: qAction.trigger()
}
PlasmaComponents3.ToolButton {
icon.name: "window-close"
icon.height: units.iconSizes.smallMedium
Layout.preferredWidth: height
onClicked: plasmoid.editMode = false
PlasmaComponents3.ToolTip {
text: i18n("Close Edit Mode")
}
}
}
]
}
}
/***************************************************************************
* Copyright 2012 by Sebastian Kügler <sebas@kde.org> *
* Copyright 2015 by Kai Uwe Broulik <kde@privat.broulik.de> *
* *
* 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) {
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()
}
}
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: {
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
}
}
}
}
......@@ -40,51 +40,11 @@ Item {
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