Commit 1062a6a2 authored by Eike Hein's avatar Eike Hein

Adapt to QQuickWindow hover event synthesis & optimize delegate

Summary:
As of Qt 5.9, QQuickWindow synthesizes a hover event once per frame.
This interferes with keyboard navigation when having the mouse
pointer above any item, because after navigating away with the arrow
keys, the next synthesized hover event will cause the item below the
pointer to become the current item again. This patch addresses this
by rejecting onPositionChanged with identical coordinates from the
last event.

It also optimizes the delegate by turning its root item from
MouseArea to Item and turning the per-delegate context menu instance
into a per-view one.

Reviewers: #plasma

Subscribers: plasma-devel

Tags: #plasma

Differential Revision: https://phabricator.kde.org/D9817
parent 76b112d8
......@@ -111,6 +111,8 @@ Kicker.DashboardWindow {
}
mainItem: MouseArea {
id: rootItem
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
......
......@@ -24,15 +24,12 @@ import org.kde.plasma.components 2.0 as PlasmaComponents
import "../code/tools.js" as Tools
MouseArea {
Item {
id: item
width: GridView.view.cellWidth
height: width
signal actionTriggered(string actionId, variant actionArgument)
signal aboutToShowActionMenu(variant actionMenu)
property bool showLabel: true
property int itemIndex: model.index
......@@ -40,48 +37,20 @@ MouseArea {
property url url: model.url != undefined ? model.url : ""
property variant icon: model.decoration != undefined ? model.decoration : ""
property var m: model
property bool pressed: false
property bool hasActionList: ((model.favoriteId != null)
|| (("hasActionList" in model) && (model.hasActionList == true)))
property Item view: GridView.view
property Item menu: actionMenu
Accessible.role: Accessible.MenuItem
Accessible.name: model.display
acceptedButtons: root.visible ? (Qt.LeftButton | Qt.RightButton) : Qt.NoButton
onPressed: {
if (mouse.buttons & Qt.RightButton) {
if (hasActionList) {
openActionMenu(item, mouse.x, mouse.y);
}
} else {
pressed = true;
}
}
onReleased: {
if (pressed && GridView.view.currentItem == item) {
hoverArea.hoverEnabled = false;
if ("trigger" in GridView.view.model) {
GridView.view.model.trigger(index, "", null);
root.toggle();
}
itemGrid.itemActivated(index, "", null);
}
pressed = false;
}
onAboutToShowActionMenu: {
function openActionMenu(x, y) {
var actionList = hasActionList ? model.actionList : [];
Tools.fillActionMenu(actionMenu, actionList, GridView.view.model.favoritesModel, model.favoriteId);
actionMenu.visualParent = item;
actionMenu.open(x, y);
}
onActionTriggered: {
function actionTriggered() {
var close = Tools.triggerAction(GridView.view.model, model.index, actionId, actionArgument);
if (close) {
......@@ -89,20 +58,6 @@ MouseArea {
}
}
function openActionMenu(visualParent, x, y) {
aboutToShowActionMenu(actionMenu);
actionMenu.visualParent = visualParent;
actionMenu.open(x, y);
}
ActionMenu {
id: actionMenu
onActionClicked: {
actionTriggered(actionId, actionArgument);
}
}
PlasmaCore.IconItem {
id: icon
......@@ -117,7 +72,7 @@ MouseArea {
colorGroup: PlasmaCore.Theme.ComplementaryColorGroup
animated: false
usesPlasmaTheme: view.usesPlasmaTheme
usesPlasmaTheme: GridView.view.usesPlasmaTheme
source: model.decoration
}
......
......@@ -40,9 +40,6 @@ FocusScope {
property bool showLabels: true
property alias usesPlasmaTheme: gridView.usesPlasmaTheme
property int pressX: -1
property int pressY: -1
property alias currentIndex: gridView.currentIndex
property alias currentItem: gridView.currentItem
property alias contentItem: gridView.contentItem
......@@ -107,6 +104,14 @@ FocusScope {
gridView.forceLayout();
}
ActionMenu {
id: actionMenu
onActionClicked: {
visualParent.actionTriggered(actionId, actionArgument);
}
}
DropArea {
id: dropArea
......@@ -166,248 +171,280 @@ FocusScope {
}
}
MouseEventListener {
id: hoverArea
anchors.fill: parent
Timer {
id: resetAnimationDurationTimer
hoverEnabled: true
interval: 120
repeat: false
onPressed: {
pressX = mouse.x;
pressY = mouse.y;
onTriggered: {
gridView.animationDuration = interval - 20;
}
}
onReleased: {
pressX = -1;
pressY = -1;
}
PlasmaExtras.ScrollArea {
id: scrollArea
onClicked: {
var cPos = mapToItem(gridView.contentItem, mouse.x, mouse.y);
var item = gridView.itemAt(cPos.x, cPos.y);
anchors.fill: parent
if (!item) {
root.toggle();
}
}
focus: true
onPositionChanged: {
var cPos = mapToItem(gridView.contentItem, mouse.x, mouse.y);
var item = gridView.itemAt(cPos.x, cPos.y);
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
if (!item) {
gridView.currentIndex = -1;
} else {
gridView.currentIndex = item.itemIndex;
itemGrid.focus = (currentIndex != -1)
GridView {
id: gridView
if (dragEnabled && pressX != -1 && dragHelper.isDrag(pressX, pressY, mouse.x, mouse.y)) {
if ("pluginName" in item.m) {
dragHelper.startDrag(kicker, item.url, item.icon,
"text/x-plasmoidservicename", item.m.pluginName);
} else {
dragHelper.startDrag(kicker, item.url, item.icon);
}
property bool usesPlasmaTheme: false
kicker.dragSource = item;
property int iconSize: units.iconSizes.huge
pressX = -1;
pressY = -1;
}
}
}
property bool animating: false
property int animationDuration: dropEnabled ? resetAnimationDurationTimer.interval : 0
onContainsMouseChanged: {
if (!containsMouse) {
gridView.currentIndex = -1;
pressX = -1;
pressY = -1;
}
}
focus: true
Connections {
target: root
currentIndex: -1
onVisibleChanged: {
if (root.visible) {
hoverArea.hoverEnabled = true;
}
}
}
move: Transition {
enabled: itemGrid.dropEnabled
Timer {
id: resetAnimationDurationTimer
SequentialAnimation {
PropertyAction { target: gridView; property: "animating"; value: true }
interval: 120
repeat: false
NumberAnimation {
duration: gridView.animationDuration
properties: "x, y"
easing.type: Easing.OutQuad
}
onTriggered: {
gridView.animationDuration = interval - 20;
PropertyAction { target: gridView; property: "animating"; value: false }
}
}
}
PlasmaExtras.ScrollArea {
id: scrollArea
moveDisplaced: Transition {
enabled: itemGrid.dropEnabled
anchors.fill: parent
SequentialAnimation {
PropertyAction { target: gridView; property: "animating"; value: true }
focus: true
NumberAnimation {
duration: gridView.animationDuration
properties: "x, y"
easing.type: Easing.OutQuad
}
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
PropertyAction { target: gridView; property: "animating"; value: false }
}
}
GridView {
id: gridView
keyNavigationWraps: false
boundsBehavior: Flickable.StopAtBounds
property bool usesPlasmaTheme: false
delegate: ItemGridDelegate {
showLabel: showLabels
}
highlight: Item {
property bool isDropPlaceHolder: "dropPlaceholderIndex" in model && currentIndex == model.dropPlaceholderIndex
property int iconSize: units.iconSizes.huge
PlasmaComponents.Highlight {
visible: gridView.currentItem && !isDropPlaceHolder
property bool animating: false
property int animationDuration: dropEnabled ? resetAnimationDurationTimer.interval : 0
anchors.fill: parent
}
focus: true
PlasmaCore.FrameSvgItem {
visible: gridView.currentItem && isDropPlaceHolder
currentIndex: -1
anchors.fill: parent
move: Transition {
enabled: itemGrid.dropEnabled
imagePath: "widgets/viewitem"
prefix: "selected"
SequentialAnimation {
PropertyAction { target: gridView; property: "animating"; value: true }
opacity: 0.5
NumberAnimation {
duration: gridView.animationDuration
properties: "x, y"
easing.type: Easing.OutQuad
PlasmaCore.IconItem {
anchors {
right: parent.right
rightMargin: parent.margins.right
bottom: parent.bottom
bottomMargin: parent.margins.bottom
}
PropertyAction { target: gridView; property: "animating"; value: false }
width: units.iconSizes.smallMedium
height: width
source: "list-add"
active: false
}
}
}
moveDisplaced: Transition {
enabled: itemGrid.dropEnabled
highlightFollowsCurrentItem: true
highlightMoveDuration: 0
SequentialAnimation {
PropertyAction { target: gridView; property: "animating"; value: true }
onCurrentIndexChanged: {
if (currentIndex != -1) {
focus = true;
}
}
NumberAnimation {
duration: gridView.animationDuration
properties: "x, y"
easing.type: Easing.OutQuad
}
onCountChanged: {
animationDuration = 0;
resetAnimationDurationTimer.start();
}
PropertyAction { target: gridView; property: "animating"; value: false }
}
onModelChanged: {
currentIndex = -1;
}
Keys.onLeftPressed: {
if (currentCol() != 0) {
event.accepted = true;
moveCurrentIndexLeft();
} else {
itemGrid.keyNavLeft();
}
}
keyNavigationWraps: false
boundsBehavior: Flickable.StopAtBounds
Keys.onRightPressed: {
var columns = Math.floor(width / cellWidth);
delegate: ItemGridDelegate {
showLabel: showLabels
if (currentCol() != columns - 1 && currentIndex != count -1) {
event.accepted = true;
moveCurrentIndexRight();
} else {
itemGrid.keyNavRight();
}
}
highlight: Item {
property bool isDropPlaceHolder: "dropPlaceholderIndex" in model && currentIndex == model.dropPlaceholderIndex
Keys.onUpPressed: {
if (currentRow() != 0) {
event.accepted = true;
moveCurrentIndexUp();
positionViewAtIndex(currentIndex, GridView.Contain);
} else {
itemGrid.keyNavUp();
}
}
PlasmaComponents.Highlight {
visible: gridView.currentItem && !isDropPlaceHolder
Keys.onDownPressed: {
if (currentRow() < itemGrid.lastRow()) {
// Fix moveCurrentIndexDown()'s lack of proper spatial nav down
// into partial columns.
event.accepted = true;
var columns = Math.floor(width / cellWidth);
var newIndex = currentIndex + columns;
currentIndex = Math.min(newIndex, count - 1);
positionViewAtIndex(currentIndex, GridView.Contain);
} else {
itemGrid.keyNavDown();
}
}
}
}
anchors.fill: parent
}
MouseArea {
id: hoverArea
PlasmaCore.FrameSvgItem {
visible: gridView.currentItem && isDropPlaceHolder
anchors.fill: parent
anchors.fill: parent
property int pressX: -1
property int pressY: -1
property int lastX: -1
property int lastY: -1
property Item pressedItem: null
imagePath: "widgets/viewitem"
prefix: "selected"
acceptedButtons: Qt.LeftButton | Qt.RightButton
opacity: 0.5
hoverEnabled: true
PlasmaCore.IconItem {
anchors {
right: parent.right
rightMargin: parent.margins.right
bottom: parent.bottom
bottomMargin: parent.margins.bottom
}
onPressed: {
pressX = mouse.x;
pressY = mouse.y;
width: units.iconSizes.smallMedium
height: width
mouse.accepted = true;
source: "list-add"
active: false
}
if (mouse.button == Qt.RightButton) {
if (gridView.currentItem) {
if (gridView.currentItem.hasActionList) {
var mapped = mapToItem(gridView.currentItem, mouse.x, mouse.y);
gridView.currentItem.openActionMenu(mapped.x, mapped.y);
}
} else {
var mapped = mapToItem(rootItem, mouse.x, mouse.y);
contextMenu.open(mapped.x, mapped.y);
}
} else {
pressedItem = gridView.currentItem;
}
}
highlightFollowsCurrentItem: true
highlightMoveDuration: 0
onReleased: {
mouse.accepted = true;
onCurrentIndexChanged: {
if (currentIndex != -1) {
focus = true;
}
if (gridView.currentItem && gridView.currentItem == pressedItem) {
if ("trigger" in gridView.model) {
gridView.model.trigger(pressedItem.itemIndex, "", null);
root.toggle();
}
onCountChanged: {
animationDuration = 0;
resetAnimationDurationTimer.start();
}
itemGrid.itemActivated(pressedItem.itemIndex, "", null);
} else if (!pressedItem && mouse.button == Qt.LeftButton) {
root.toggle();
}
onModelChanged: {
currentIndex = -1;
}
pressX = -1;
pressY = -1;
pressedItem = null;
}
Keys.onLeftPressed: {
if (currentCol() != 0) {
event.accepted = true;
moveCurrentIndexLeft();
} else {
itemGrid.keyNavLeft();
}
}
onPositionChanged: {
// Prevent hover event synthesis in QQuickWindow interfering
// with keyboard navigation by ignoring repeated events with
// identical coordinates. As the work done here would be re-
// dundant in any case, these are safe to ignore.
if (mouse.x == lastX && mouse.y == lastY) {
return;
}
Keys.onRightPressed: {
var columns = Math.floor(width / cellWidth);
lastX = mouse.x;
lastY = mouse.y;
if (currentCol() != columns - 1 && currentIndex != count -1) {
event.accepted = true;
moveCurrentIndexRight();
} else {
itemGrid.keyNavRight();
}
}
var cPos = mapToItem(gridView.contentItem, mouse.x, mouse.y);
var item = gridView.itemAt(cPos.x, cPos.y);
Keys.onUpPressed: {
if (currentRow() != 0) {
event.accepted = true;
moveCurrentIndexUp();
positionViewAtIndex(currentIndex, GridView.Contain);
} else {
itemGrid.keyNavUp();
}
}
if (!item) {
gridView.currentIndex = -1;
pressedItem = null;
} else {
gridView.currentIndex = item.itemIndex;
itemGrid.focus = (currentIndex != -1)
Keys.onDownPressed: {
if (currentRow() < itemGrid.lastRow()) {
// Fix moveCurrentIndexDown()'s lack of proper spatial nav down
// into partial columns.
event.accepted = true;
var columns = Math.floor(width / cellWidth);
var newIndex = currentIndex + columns;
currentIndex = Math.min(newIndex, count - 1);
positionViewAtIndex(currentIndex, GridView.Contain);
if (dragEnabled && pressX != -1 && dragHelper.isDrag(pressX, pressY, mouse.x, mouse.y)) {
if ("pluginName" in item.m) {
dragHelper.startDrag(kicker, item.url, item.icon,
"text/x-plasmoidservicename", item.m.pluginName);
} else {
itemGrid.keyNavDown();
dragHelper.startDrag(kicker, item.url, item.icon);
}
kicker.dragSource = item;
pressX = -1;
pressY = -1;
}
}
}
onContainsMouseChanged: {
if (!containsMouse) {
gridView.currentIndex = -1;
pressX = -1;
pressY = -1;
pressedItem = null;
}
}
}
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment