diff --git a/applets/taskmanager/package/contents/ui/ContextMenu.qml b/applets/taskmanager/package/contents/ui/ContextMenu.qml --- a/applets/taskmanager/package/contents/ui/ContextMenu.qml +++ b/applets/taskmanager/package/contents/ui/ContextMenu.qml @@ -533,7 +533,9 @@ text: i18nc("Remove launcher button for application shown while it is not running", "Unpin") - onClicked: tasksModel.requestRemoveLauncher(get(atm.LauncherUrlWithoutIcon)); + onClicked: { + tasksModel.requestRemoveLauncher(get(atm.LauncherUrlWithoutIcon)); + } } diff --git a/applets/taskmanager/package/contents/ui/GroupDialog.qml b/applets/taskmanager/package/contents/ui/GroupDialog.qml --- a/applets/taskmanager/package/contents/ui/GroupDialog.qml +++ b/applets/taskmanager/package/contents/ui/GroupDialog.qml @@ -17,10 +17,11 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ -import QtQuick 2.0 +import QtQuick 2.7 import QtQuick.Window 2.2 import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.extras 2.0 as PlasmaExtras import org.kde.draganddrop 2.0 import "../code/layout.js" as LayoutManager @@ -36,63 +37,140 @@ property int preferredWidth: Screen.width / (3 * Screen.devicePixelRatio) property int preferredHeight: Screen.height / (2 * Screen.devicePixelRatio) + property int contentWidth: scrollArea.overflowing ? mainItem.width - (units.smallSpacing * 3) : mainItem.width + property TextMetrics textMetrics: TextMetrics {} + property alias overflowing: scrollArea.overflowing + property alias activeTask: focusActiveTaskTimer.targetIndex + + function selectTask(task) { + if (!task) { + return; + } + + task.forceActiveFocus(); + scrollArea.ensureItemVisible(task); + } + + mainItem: PlasmaExtras.ScrollArea { + id: scrollArea + + property bool overflowing: (viewport.height < contentItem.height) + + horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff + + function ensureItemVisible(item) { + var itemTop = item.y; + var itemBottom = (item.y + item.height); + + if (itemTop < flickableItem.contentY) { + flickableItem.contentY = itemTop; + } + + if ((itemBottom - flickableItem.contentY) > viewport.height) { + flickableItem.contentY = Math.abs(viewport.height - itemBottom); + } + } - mainItem: Item { MouseHandler { id: mouseHandler - anchors.fill: parent + width: parent.width + height: (groupRepeater.count * (LayoutManager.verticalMargins() + + Math.max(theme.mSize(theme.defaultFont).height, units.iconSizes.medium))) target: taskList - } + handleWheelEvents: !scrollArea.overflowing + + Timer { + id: focusActiveTaskTimer + + property var targetIndex: null - TaskList { - id: taskList + interval: 0 + repeat: false - anchors.fill: parent + onTriggered: { + // Now we can home in on the previously active task + // collected in groupDialog.onVisibleChanged. - Repeater { - id: groupRepeater + if (targetIndex != null) { + for (var i = 0; i < groupRepeater.count; ++i) { + var task = groupRepeater.itemAt(i); - function currentIndex() { - for (var i = 0; i < count; ++i) { - if (itemAt(i).activeFocus) { - return i; + if (task.modelIndex() == targetIndex) { + selectTask(task); + return; + } } } + } + } + + TaskList { + id: taskList + + anchors.fill: parent - return -1; + add: Transition { + // We trigger a null-interval timer in the first add + // transition after setting the model so onTriggered + // will run after the Flow has positioned items. + + ScriptAction { + script: { + if (groupRepeater.aboutToPopulate) { + focusActiveTaskTimer.restart(); + groupRepeater.aboutToPopulate = false; + } + } + } } - onCountChanged: updateSize() + Repeater { + id: groupRepeater + + property bool aboutToPopulate: false + + function currentIndex() { + for (var i = 0; i < count; ++i) { + if (itemAt(i).activeFocus) { + return i; + } + } + + return -1; + } + + onCountChanged: updateSize(); + } } } Keys.onUpPressed: { var currentIndex = groupRepeater.currentIndex(); // In doubt focus the first item if (currentIndex === -1) { - groupRepeater.itemAt(0).forceActiveFocus(); + selectTask(groupRepeater.itemAt(0)); return; } var previousIndex = currentIndex - 1; if (previousIndex < 0) { previousIndex = groupRepeater.count - 1; } - groupRepeater.itemAt(previousIndex).forceActiveFocus() + selectTask(groupRepeater.itemAt(previousIndex)); } Keys.onDownPressed: { var currentIndex = groupRepeater.currentIndex(); // In doubt focus the first item, also wrap around. if (currentIndex === -1 || currentIndex + 1 >= groupRepeater.count) { - groupRepeater.itemAt(0).forceActiveFocus(); + selectTask(groupRepeater.itemAt(0)); return; } - groupRepeater.itemAt(currentIndex + 1).forceActiveFocus(); + selectTask(groupRepeater.itemAt(currentIndex + 1)); } Keys.onEscapePressed: groupDialog.visible = false; @@ -119,6 +197,8 @@ if (visible && visualParent) { groupFilter.model = tasksModel; groupFilter.rootIndex = groupFilter.modelIndex(visualParent.itemIndex); + + groupRepeater.aboutToPopulate = true; groupRepeater.model = groupFilter; mainItem.forceActiveFocus(); @@ -147,11 +227,14 @@ for (var i = 0; i < taskList.children.length - 1; ++i) { task = taskList.children[i]; - if (task.textWidth > maxWidth) { - maxWidth = task.textWidth; + textMetrics.text = task.labelText; + var textWidth = textMetrics.boundingRect.width; + + if (textWidth > maxWidth) { + maxWidth = textWidth; } - task.textWidthChanged.connect(updateSize); + task.labelTextChanged.connect(updateSize); } maxWidth += LayoutManager.horizontalMargins() + units.iconSizes.medium + 2 * units.smallSpacing; diff --git a/applets/taskmanager/package/contents/ui/MouseHandler.qml b/applets/taskmanager/package/contents/ui/MouseHandler.qml --- a/applets/taskmanager/package/contents/ui/MouseHandler.qml +++ b/applets/taskmanager/package/contents/ui/MouseHandler.qml @@ -34,6 +34,7 @@ property bool moved: false property alias hoveredItem: dropHandler.hoveredItem + property alias handleWheelEvents: wheelHandler.active Timer { id: ignoreItemTimer @@ -159,9 +160,20 @@ id: wheelHandler anchors.fill: parent + + enabled: active && plasmoid.configuration.wheelEnabled + + property bool active: true property int wheelDelta: 0; - enabled: plasmoid.configuration.wheelEnabled - onWheel: wheelDelta = TaskTools.wheelActivateNextPrevTask(null, wheelDelta, wheel.angleDelta.y); + + onWheel: { + if (!active) { + wheel.accepted = false; + return; + } + + wheelDelta = TaskTools.wheelActivateNextPrevTask(null, wheelDelta, wheel.angleDelta.y); + } } } diff --git a/applets/taskmanager/package/contents/ui/Task.qml b/applets/taskmanager/package/contents/ui/Task.qml --- a/applets/taskmanager/package/contents/ui/Task.qml +++ b/applets/taskmanager/package/contents/ui/Task.qml @@ -31,7 +31,7 @@ MouseArea { id: task - width: groupDialog.mainItem.width + width: groupDialog.contentWidth height: Math.max(theme.mSize(theme.defaultFont).height, units.iconSizes.medium) + LayoutManager.verticalMargins() visible: false @@ -48,7 +48,7 @@ property bool isWindow: model.IsWindow === true property int childCount: model.ChildCount != undefined ? model.ChildCount : 0 property int previousChildCount: 0 - property alias textWidth: label.implicitWidth + property alias labelText: label.text property bool pressed: false property int pressX: -1 property int pressY: -1 @@ -85,6 +85,10 @@ } onChildCountChanged: { + if (containsMouse) { + groupDialog.activeTask = null; + } + if (childCount > previousChildCount) { tasksModel.requestPublishDelegateGeometry(modelIndex(), backend.globalRect(task), task); } @@ -163,7 +167,7 @@ } onWheel: { - if (plasmoid.configuration.wheelEnabled) { + if (plasmoid.configuration.wheelEnabled && (!inPopup || !groupDialog.overflowing)) { wheelDelta = TaskTools.wheelActivateNextPrevTask(task, wheelDelta, wheel.angleDelta.y); } else { wheel.accepted = false; diff --git a/applets/taskmanager/package/contents/ui/main.qml b/applets/taskmanager/package/contents/ui/main.qml --- a/applets/taskmanager/package/contents/ui/main.qml +++ b/applets/taskmanager/package/contents/ui/main.qml @@ -164,6 +164,18 @@ } } + Connections { + enabled: plasmoid.configuration.groupPopups + + target: tasksModel + + onActiveTaskChanged: { + if (tasksModel.activeTask.parent.valid) { + groupDialog.activeTask = tasksModel.activeTask; + } + } + } + TaskManager.VirtualDesktopInfo { id: virtualDesktopInfo }