diff --git a/applets/taskmanager/package/contents/ui/GroupDialog.qml b/applets/taskmanager/package/contents/ui/GroupDialog.qml index 4b1fee505..08d2550f0 100644 --- a/applets/taskmanager/package/contents/ui/GroupDialog.qml +++ b/applets/taskmanager/package/contents/ui/GroupDialog.qml @@ -1,248 +1,283 @@ /*************************************************************************** * Copyright (C) 2012-2013 by Eike Hein * * * * 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.4 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 PlasmaCore.Dialog { id: groupDialog visible: false type: PlasmaCore.Dialog.PopupMenu flags: Qt.WindowStaysOnTopHint hideOnWindowDeactivate: true location: plasmoid.location - 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 {} + readonly property int preferredWidth: Screen.width / (3 * Screen.devicePixelRatio) + readonly property int preferredHeight: Screen.height / (2 * Screen.devicePixelRatio) + readonly property int contentWidth: scrollArea.overflowing ? mainItem.width - (units.smallSpacing * 3) : mainItem.width + readonly property TextMetrics textMetrics: TextMetrics {} property alias overflowing: scrollArea.overflowing property alias activeTask: focusActiveTaskTimer.targetIndex + property var _oldAppletStatus: PlasmaCore.Types.UnknownStatus function selectTask(task) { if (!task) { return; } task.forceActiveFocus(); scrollArea.ensureItemVisible(task); } - mainItem: PlasmaExtras.ScrollArea { - id: scrollArea + mainItem: MouseHandler { + id: mouseHandler - property bool overflowing: (viewport.height < contentItem.height) + target: taskList + handleWheelEvents: !scrollArea.overflowing - horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff + Timer { + id: focusActiveTaskTimer - function ensureItemVisible(item) { - var itemTop = item.y; - var itemBottom = (item.y + item.height); + property var targetIndex: null - if (itemTop < flickableItem.contentY) { - flickableItem.contentY = itemTop; - } + interval: 0 + repeat: false - if ((itemBottom - flickableItem.contentY) > viewport.height) { - flickableItem.contentY = Math.abs(viewport.height - itemBottom); - } - } + onTriggered: { + // Now we can home in on the previously active task + // collected in groupDialog.onVisibleChanged. - MouseHandler { - id: mouseHandler + if (targetIndex != null) { + for (var i = 0; i < groupRepeater.count; ++i) { + var task = groupRepeater.itemAt(i); - width: parent.width - height: (groupRepeater.count * (LayoutManager.verticalMargins() - + Math.max(theme.mSize(theme.defaultFont).height, units.iconSizes.medium))) + if (task.modelIndex() == targetIndex) { + selectTask(task); + return; + } + } + } + } + } - target: taskList - handleWheelEvents: !scrollArea.overflowing + PlasmaExtras.ScrollArea { + id: scrollArea - Timer { - id: focusActiveTaskTimer + anchors.fill: parent - property var targetIndex: null + readonly property bool overflowing: (viewport.height < contentItem.height) - interval: 0 - repeat: false + horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff - onTriggered: { - // Now we can home in on the previously active task - // collected in groupDialog.onVisibleChanged. + function ensureItemVisible(item) { + var itemTop = item.y; + var itemBottom = (item.y + item.height); - if (targetIndex != null) { - for (var i = 0; i < groupRepeater.count; ++i) { - var task = groupRepeater.itemAt(i); + if (itemTop < flickableItem.contentY) { + flickableItem.contentY = itemTop; + } - if (task.modelIndex() == targetIndex) { - selectTask(task); - return; - } - } - } + if ((itemBottom - flickableItem.contentY) > viewport.height) { + flickableItem.contentY = Math.abs(viewport.height - itemBottom); } } TaskList { id: taskList - anchors.fill: parent + width: parent.width 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; } } } } 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 last item, so we start at the bottom when user // initially presses up. if (currentIndex === -1) { selectTask(groupRepeater.itemAt(groupRepeater.count - 1)); return; } var previousIndex = currentIndex - 1; if (previousIndex < 0) { previousIndex = groupRepeater.count - 1; } 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) { selectTask(groupRepeater.itemAt(0)); return; } selectTask(groupRepeater.itemAt(currentIndex + 1)); } Keys.onEscapePressed: groupDialog.visible = false; } data: [ VisualDataModel { id: groupFilter delegate: Task { visible: true inPopup: true } } ] onVisualParentChanged: { - if (!visualParent) { + if (visible && visualParent) { + attachModel(); + } else { visible = false; } } onVisibleChanged: { if (visible && visualParent) { - groupFilter.model = tasksModel; - groupFilter.rootIndex = groupFilter.modelIndex(visualParent.itemIndex); + _oldAppletStatus = plasmoid.status; + plasmoid.status = PlasmaCore.Types.RequiresAttentionStatus; - groupRepeater.aboutToPopulate = true; - groupRepeater.model = groupFilter; + attachModel(); - mainItem.forceActiveFocus(); + groupDialog.requestActivate(); + mouseHandler.forceActiveFocus(); } else { + plasmoid.status = _oldAppletStatus; visualParent = null; groupRepeater.model = undefined; groupFilter.model = undefined; groupFilter.rootIndex = undefined; } } + function attachModel() { + if (!visualParent) { + return; + } + + if (!groupFilter.model) { + groupFilter.model = tasksModel; + } + + groupRepeater.aboutToPopulate = true; + + groupFilter.rootIndex = tasksModel.makeModelIndex(visualParent.itemIndex); + + if (!groupRepeater.model) { + groupRepeater.model = groupFilter; + } + } + function updateSize() { - if (!visible || !visualParent) { + if (!visible) { + return; + } + + if (!visualParent) { + visible = false; return; } - if (!groupRepeater.count) { + if (!visualParent.childCount) { visible = false; - } else { + // Setting VisualDataModel.rootIndex drops groupRepeater.count to 0 + // before the actual row count. updateSize is therefore invoked twice; + // only update size once the repeater count matches the model role. + } else if (visualParent.childCount == groupRepeater.count) { var task; var maxWidth = 0; var maxHeight = 0; backend.cancelHighlightWindows(); for (var i = 0; i < taskList.children.length - 1; ++i) { task = taskList.children[i]; textMetrics.text = task.labelText; var textWidth = textMetrics.boundingRect.width; if (textWidth > maxWidth) { maxWidth = textWidth; } task.labelTextChanged.connect(updateSize); } - maxWidth += LayoutManager.horizontalMargins() + units.iconSizes.medium + 2 * units.smallSpacing; maxHeight = groupRepeater.count * (LayoutManager.verticalMargins() + Math.max(theme.mSize(theme.defaultFont).height, units.iconSizes.medium)); + maxWidth += LayoutManager.horizontalMargins() + units.iconSizes.medium + 2 * units.smallSpacing; + + // Add horizontal space for scrollbar if needed. + // FIXME TODO HACK: Use actuall scrollbar width instead of a good guess. + if (maxHeight > preferredHeight) { + maxWidth += (units.smallSpacing * 3); + } + mainItem.height = Math.min(preferredHeight, maxHeight); mainItem.width = Math.min(preferredWidth, (tasks.vertical ? Math.max(maxWidth, tasks.width) : Math.min(maxWidth, tasks.width))); } } }