aerothemeplasma/Plasma Widgets/Task Manager/org.kde.plasma.seventasks/contents/ui/GroupDialog.qml
2022-04-19 22:04:22 +02:00

290 lines
8.8 KiB
QML

/*
SPDX-FileCopyrightText: 2012-2013 Eike Hein <hein@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
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
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 - (PlasmaCore.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: MouseHandler {
id: mouseHandler
target: taskList
handleWheelEvents: !scrollArea.overflowing
Timer {
id: focusActiveTaskTimer
property var targetIndex: null
interval: 0
repeat: false
onTriggered: {
// Now we can home in on the previously active task
// collected in groupDialog.onVisibleChanged.
if (targetIndex != null) {
for (var i = 0; i < groupRepeater.count; ++i) {
var task = groupRepeater.itemAt(i);
if (task.modelIndex() === targetIndex) {
selectTask(task);
return;
}
}
}
}
}
PlasmaExtras.ScrollArea {
id: scrollArea
anchors.fill: parent
readonly property bool overflowing: (viewport.height < contentItem.height)
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
/*ScrollBar.horizontal.visible: false;
ScrollBar.vertical.visible: false;*/
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);
}
}
TaskList {
id: taskList
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;
}
}
}
}
onAnimatingChanged: {
if (!animating) {
Qt.callLater(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;
}
onItemAdded: {
item.labelTextChanged.connect(updateSize);
Qt.callLater(updateSize)
}
onItemRemoved: {
if (groupDialog.visible && index > 0 && index == count) {
Qt.callLater(updateSize);
}
}
}
}
Component.onCompleted: {
flickableItem.boundsBehavior = Flickable.StopAtBounds;
}
}
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 (visible && visualParent) {
attachModel();
} else {
visible = false;
}
}
onVisibleChanged: {
if (visible && visualParent) {
_oldAppletStatus = plasmoid.status;
plasmoid.status = PlasmaCore.Types.RequiresAttentionStatus;
attachModel();
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) {
return;
}
if (!visualParent) {
visible = false;
return;
}
if (!visualParent.childCount) {
visible = false;
// 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 (!groupRepeater.aboutToPopulate || 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;
}
}
maxHeight = groupRepeater.count * (LayoutManager.verticalMargins() + Math.max(theme.mSize(theme.defaultFont).height, PlasmaCore.Units.iconSizes.medium));
maxWidth += LayoutManager.horizontalMargins() + PlasmaCore.Units.iconSizes.medium + 2 * PlasmaCore.Units.smallSpacing;
// Add horizontal space for scrollbar if needed.
// FIXME TODO HACK: Use actual scrollbar width instead of a good guess.
if (maxHeight > preferredHeight) {
maxWidth += (PlasmaCore.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)));
}
}
}