diff --git a/applets/taskmanager/package/contents/code/tools.js b/applets/taskmanager/package/contents/code/tools.js --- a/applets/taskmanager/package/contents/code/tools.js +++ b/applets/taskmanager/package/contents/code/tools.js @@ -104,7 +104,7 @@ groupDialog.visible = false; } else { groupDialog.visualParent = task; - groupDialog.visible = true; + groupDialog.open(); } } else { if (model.IsMinimized === true) { 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,11 @@ text: i18nc("Remove launcher button for application shown while it is not running", "Unpin") - onClicked: tasksModel.requestRemoveLauncher(get(atm.LauncherUrlWithoutIcon)); + onClicked: { + console.log("unpin request", modelIndex, get(atm.LauncherUrlWithoutIcon)); + + 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 @@ -21,6 +21,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,150 @@ 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 var textMetrics: null + property alias overflowing: scrollArea.overflowing + + function open() { + // Collect active task before opening the dialog. As the dialog + // gains focus window focus changes, so we can't do it later. + focusActiveTaskTimer.targetIndex = tasksModel.activeTask; + + visible = true; + } + + 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); + + console.log(itemTop, itemBottom); + + 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 - TaskList { - id: taskList + property var targetIndex: null - anchors.fill: parent + interval: 0 + repeat: false - Repeater { - id: groupRepeater + onTriggered: { + // Now we can home in on the previously active task + // collected in groupDialog.onVisibleChanged. - function currentIndex() { - for (var i = 0; i < count; ++i) { - if (itemAt(i).activeFocus) { - return i; + if (targetIndex != null) { + for (var i = 0; i < groupRepeater.count; ++i) { + var task = groupRepeater.itemAt(i); + + if (task.modelIndex() == targetIndex) { + task.forceActiveFocus(); + scrollArea.ensureItemVisible(task); + return; + } } } + } + } + + TaskList { + id: taskList - return -1; + width: parent.width + height: parent.height + + 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(); + var task = groupRepeater.itemAt(0); + task.forceActiveFocus(); + scrollArea.ensureItemVisible(task); return; } var previousIndex = currentIndex - 1; if (previousIndex < 0) { previousIndex = groupRepeater.count - 1; } - groupRepeater.itemAt(previousIndex).forceActiveFocus() + var task = groupRepeater.itemAt(previousIndex); + task.forceActiveFocus(); + scrollArea.ensureItemVisible(task); } 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(); + var task = groupRepeater.itemAt(0); + task.forceActiveFocus(); + scrollArea.ensureItemVisible(task); return; } - groupRepeater.itemAt(currentIndex + 1).forceActiveFocus(); + var task = groupRepeater.itemAt(currentIndex + 1); + task.forceActiveFocus(); + scrollArea.ensureItemVisible(task); } Keys.onEscapePressed: groupDialog.visible = false; @@ -119,6 +207,8 @@ if (visible && visualParent) { groupFilter.model = tasksModel; groupFilter.rootIndex = groupFilter.modelIndex(visualParent.itemIndex); + + groupRepeater.aboutToPopulate = true; groupRepeater.model = groupFilter; mainItem.forceActiveFocus(); @@ -147,18 +237,30 @@ 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; + + for (var i = 0; i < taskList.children.length - 1; ++i) { + taskList.children[i] = maxWidth; + } + maxHeight = groupRepeater.count * (LayoutManager.verticalMargins() + Math.max(theme.mSize(theme.defaultFont).height, units.iconSizes.medium)); mainItem.height = Math.min(preferredHeight, maxHeight); mainItem.width = Math.min(preferredWidth, (tasks.vertical ? Math.max(maxWidth, tasks.width) : Math.min(maxWidth, tasks.width))); } } + + Component.onCompleted: { + textMetrics = Qt.createQmlObject('import QtQuick 2.7; TextMetrics {}', groupDialog); + } } 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 @@ -163,7 +163,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;