Warning, /plasma/kwin/src/tabbox/switchers/thumbnail_grid/contents/ui/main.qml is written in an unsupported language. File is not indexed.

0001 /*
0002  KWin - the KDE window manager
0003  This file is part of the KDE project.
0004 
0005  SPDX-FileCopyrightText: 2020 Chris Holland <zrenfire@gmail.com>
0006  SPDX-FileCopyrightText: 2023 Nate Graham <nate@kde.org>
0007 
0008  SPDX-License-Identifier: GPL-2.0-or-later
0009  */
0010 
0011 import QtQuick
0012 import QtQuick.Layouts 1.1
0013 import org.kde.plasma.core as PlasmaCore
0014 import org.kde.ksvg 1.0 as KSvg
0015 import org.kde.plasma.components 3.0 as PlasmaComponents3
0016 import org.kde.kwin 3.0 as KWin
0017 import org.kde.kirigami 2.20 as Kirigami
0018 
0019 KWin.TabBoxSwitcher {
0020     id: tabBox
0021 
0022     Instantiator {
0023         active: tabBox.visible
0024         delegate: PlasmaCore.Dialog {
0025             location: PlasmaCore.Types.Floating
0026             visible: true
0027             flags: Qt.X11BypassWindowManagerHint
0028             x: tabBox.screenGeometry.x + tabBox.screenGeometry.width * 0.5 - dialogMainItem.width * 0.5
0029             y: tabBox.screenGeometry.y + tabBox.screenGeometry.height * 0.5 - dialogMainItem.height * 0.5
0030 
0031             mainItem: FocusScope {
0032                 id: dialogMainItem
0033 
0034                 focus: true
0035 
0036                 property int maxWidth: tabBox.screenGeometry.width * 0.9
0037                 property int maxHeight: tabBox.screenGeometry.height * 0.7
0038                 property real screenFactor: tabBox.screenGeometry.width / tabBox.screenGeometry.height
0039                 property int maxGridColumnsByWidth: Math.floor(maxWidth / thumbnailGridView.cellWidth)
0040 
0041                 property int gridColumns: {         // Simple greedy algorithm
0042                     // respect screenGeometry
0043                     const c = Math.min(thumbnailGridView.count, maxGridColumnsByWidth);
0044                     const residue = thumbnailGridView.count % c;
0045                     if (residue == 0) {
0046                         return c;
0047                     }
0048                     // start greedy recursion
0049                     return columnCountRecursion(c, c, c - residue);
0050                 }
0051 
0052                 property int gridRows: Math.ceil(thumbnailGridView.count / gridColumns)
0053                 property int optimalWidth: thumbnailGridView.cellWidth * gridColumns
0054                 property int optimalHeight: thumbnailGridView.cellHeight * gridRows
0055                 width: Math.min(Math.max(thumbnailGridView.cellWidth, optimalWidth), maxWidth)
0056                 height: Math.min(Math.max(thumbnailGridView.cellHeight, optimalHeight), maxHeight)
0057 
0058                 clip: true
0059 
0060                 // Step for greedy algorithm
0061                 function columnCountRecursion(prevC, prevBestC, prevDiff) {
0062                     const c = prevC - 1;
0063 
0064                     // don't increase vertical extent more than horizontal
0065                     // and don't exceed maxHeight
0066                     if (prevC * prevC <= thumbnailGridView.count + prevDiff ||
0067                             maxHeight < Math.ceil(thumbnailGridView.count / c) * thumbnailGridView.cellHeight) {
0068                         return prevBestC;
0069                     }
0070                     const residue = thumbnailGridView.count % c;
0071                     // halts algorithm at some point
0072                     if (residue == 0) {
0073                         return c;
0074                     }
0075                     // empty slots
0076                     const diff = c - residue;
0077 
0078                     // compare it to previous count of empty slots
0079                     if (diff < prevDiff) {
0080                         return columnCountRecursion(c, c, diff);
0081                     } else if (diff == prevDiff) {
0082                         // when it's the same try again, we'll stop early enough thanks to the landscape mode condition
0083                         return columnCountRecursion(c, prevBestC, diff);
0084                     }
0085                     // when we've found a local minimum choose this one (greedy)
0086                     return columnCountRecursion(c, prevBestC, diff);
0087                 }
0088 
0089                 // Just to get the margin sizes
0090                 KSvg.FrameSvgItem {
0091                     id: hoverItem
0092                     imagePath: "widgets/viewitem"
0093                     prefix: "hover"
0094                     visible: false
0095                 }
0096 
0097                 GridView {
0098                     id: thumbnailGridView
0099                     anchors.fill: parent
0100                     focus: true
0101                     model: tabBox.model
0102                     currentIndex: tabBox.currentIndex
0103 
0104                     readonly property int iconSize: Kirigami.Units.iconSizes.huge
0105                     readonly property int captionRowHeight: Kirigami.Units.gridUnit * 2
0106                     readonly property int columnSpacing: Kirigami.Units.gridUnit
0107                     readonly property int thumbnailWidth: Kirigami.Units.gridUnit * 16
0108                     readonly property int thumbnailHeight: thumbnailWidth * (1.0/dialogMainItem.screenFactor)
0109                     cellWidth: hoverItem.margins.left + thumbnailWidth + hoverItem.margins.right
0110                     cellHeight: hoverItem.margins.top + captionRowHeight + thumbnailHeight + hoverItem.margins.bottom
0111 
0112                     keyNavigationWraps: true
0113                     highlightMoveDuration: 0
0114 
0115                     delegate: MouseArea {
0116                         id: thumbnailGridItem
0117                         width: thumbnailGridView.cellWidth
0118                         height: thumbnailGridView.cellHeight
0119                         focus: GridView.isCurrentItem
0120                         hoverEnabled: true
0121 
0122                         Accessible.name: model.caption
0123                         Accessible.role: Accessible.ListItem
0124 
0125                         onClicked: {
0126                             tabBox.currentIndex = index;
0127                         }
0128 
0129                         ColumnLayout {
0130                             id: columnLayout
0131                             z: 0
0132                             spacing: thumbnailGridView.columnSpacing
0133                             anchors.fill: parent
0134                             anchors.leftMargin: hoverItem.margins.left * 2
0135                             anchors.topMargin: hoverItem.margins.top * 2
0136                             anchors.rightMargin: hoverItem.margins.right * 2
0137                             anchors.bottomMargin: hoverItem.margins.bottom * 2
0138 
0139 
0140                             // KWin.WindowThumbnail needs a container
0141                             // otherwise it will be drawn the same size as the parent ColumnLayout
0142                             Item {
0143                                 Layout.fillWidth: true
0144                                 Layout.fillHeight: true
0145 
0146                                 KWin.WindowThumbnail {
0147                                     anchors.fill: parent
0148                                     wId: windowId
0149                                 }
0150 
0151                                 Kirigami.Icon {
0152                                     anchors.horizontalCenter: parent.horizontalCenter
0153                                     anchors.verticalCenter: parent.bottom
0154                                     anchors.verticalCenterOffset: Math.round(-thumbnailGridView.iconSize / 4)
0155                                     width: thumbnailGridView.iconSize
0156                                     height: thumbnailGridView.iconSize
0157 
0158                                     source: model.icon
0159                                 }
0160 
0161                                 PlasmaComponents3.ToolButton {
0162                                     id: closeButton
0163                                     anchors {
0164                                         right: parent.right
0165                                         top: parent.top
0166                                         // Deliberately touch the inner edges of the frame
0167                                         rightMargin: -columnLayout.anchors.rightMargin
0168                                         topMargin: -columnLayout.anchors.topMargin
0169                                     }
0170                                     visible: model.closeable && typeof tabBox.model.close !== 'undefined' &&
0171                                             (thumbnailGridItem.containsMouse
0172                                             || closeButton.hovered
0173                                             || thumbnailGridItem.focus
0174                                             || Kirigami.Settings.tabletMode
0175                                             || Kirigami.Settings.hasTransientTouchInput
0176                                             )
0177                                     icon.name: 'window-close-symbolic'
0178                                     onClicked: {
0179                                         tabBox.model.close(index);
0180                                     }
0181                                 }
0182                             }
0183 
0184                             PlasmaComponents3.Label {
0185                                 Layout.fillWidth: true
0186                                 text: model.caption
0187                                 font.weight: thumbnailGridItem.focus ? Font.Bold : Font.Normal
0188                                 horizontalAlignment: Text.AlignHCenter
0189                                 verticalAlignment: Text.AlignVCenter
0190                                 textFormat: Text.PlainText
0191                                 elide: Text.ElideRight
0192                             }
0193                         }
0194                     } // GridView.delegate
0195 
0196                     highlight: KSvg.FrameSvgItem {
0197                         imagePath: "widgets/viewitem"
0198                         prefix: "hover"
0199                     }
0200 
0201                     onCurrentIndexChanged: tabBox.currentIndex = thumbnailGridView.currentIndex;
0202                 } // GridView
0203 
0204                 Kirigami.PlaceholderMessage {
0205                     anchors.centerIn: parent
0206                     width: parent.width - Kirigami.Units.largeSpacing * 2
0207                     icon.source: "edit-none"
0208                     text: i18ndc("kwin", "@info:placeholder no entries in the task switcher", "No open windows")
0209                     visible: thumbnailGridView.count === 0
0210                 }
0211 
0212                 Keys.onPressed: {
0213                     if (event.key == Qt.Key_Left) {
0214                         thumbnailGridView.moveCurrentIndexLeft();
0215                     } else if (event.key == Qt.Key_Right) {
0216                         thumbnailGridView.moveCurrentIndexRight();
0217                     } else if (event.key == Qt.Key_Up) {
0218                         thumbnailGridView.moveCurrentIndexUp();
0219                     } else if (event.key == Qt.Key_Down) {
0220                         thumbnailGridView.moveCurrentIndexDown();
0221                     } else {
0222                         return;
0223                     }
0224 
0225                     thumbnailGridView.currentIndexChanged(thumbnailGridView.currentIndex);
0226                 }
0227             } // Dialog.mainItem
0228         } // Dialog
0229     } // Instantiator
0230 }