Make "Thumbnail Grid" the default Task Switcher and make it live here

Per https://invent.kde.org/plasma/plasma-desktop/-/issues/53, we're
making an overhauled version of the Thumbnail Grid Task Switcher the
default one in Plasma 6.

Currently the default Task Switcher is specified as the "Breeze" Task
Switcher which isn't ideal since it doesn't live in this repo, and it's
possible to use KWin without Plasma, where it does live.

So as a part of making Thumbnail Grid the new default Task Switcher,
let's also move it here so that KWin's default Task Switcher is always
available.

This commit grabs the Thumbnail Grid Task Switcher verbatim from where
it currently lives in the kdeplasma-addons-repo as of commit
54d16f44a56530854444b844536933a3107ef8a6.

BUG: 433034
FIXED-IN: 6.0
This commit is contained in:
Nate Graham 2023-05-08 12:58:37 +02:00
parent 805c899589
commit 3ca1fc800d
7 changed files with 367 additions and 3 deletions

View file

@ -296,6 +296,7 @@ if (KWIN_BUILD_TABBOX)
tabbox/x11_filter.cpp
)
target_link_libraries(kwin Qt::GuiPrivate)
add_subdirectory(tabbox/switchers)
endif()
qt_generate_dbus_interface(virtualkeyboard_dbus.h org.kde.kwin.VirtualKeyboard.xml OPTIONS -A)

View file

@ -318,7 +318,7 @@
<default>true</default>
</entry>
<entry name="LayoutName" type="String">
<default>thumbnails</default>
<default>thumbnail_grid</default>
</entry>
</group>
<group name="KDE">

View file

@ -0,0 +1,6 @@
# SPDX-FileCopyrightText: 2023 Nate Graham <nate@kde.org>
#
# SPDX-License-Identifier: BSD-3-Clause
set(TABBOX_DIR ${KDE_INSTALL_DATADIR}/kwin/tabbox)
install(DIRECTORY thumbnail_grid DESTINATION ${TABBOX_DIR})

View file

@ -0,0 +1,225 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2020 Chris Holland <zrenfire@gmail.com>
SPDX-FileCopyrightText: 2023 Nate Graham <nate@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQuick 2.0
import QtQuick.Layouts 1.1
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 3.0 as PlasmaComponents3
import org.kde.kwin 3.0 as KWin
import org.kde.kirigami 2.20 as Kirigami
KWin.TabBoxSwitcher {
id: tabBox
currentIndex: thumbnailGridView.currentIndex
PlasmaCore.Dialog {
location: PlasmaCore.Types.Floating
visible: tabBox.visible
flags: Qt.X11BypassWindowManagerHint
x: tabBox.screenGeometry.x + tabBox.screenGeometry.width * 0.5 - dialogMainItem.width * 0.5
y: tabBox.screenGeometry.y + tabBox.screenGeometry.height * 0.5 - dialogMainItem.height * 0.5
mainItem: Item {
id: dialogMainItem
focus: true
property int maxWidth: tabBox.screenGeometry.width * 0.9
property int maxHeight: tabBox.screenGeometry.height * 0.7
property real screenFactor: tabBox.screenGeometry.width / tabBox.screenGeometry.height
property int maxGridColumnsByWidth: Math.floor(maxWidth / thumbnailGridView.cellWidth)
property int gridColumns: { // Simple greedy algorithm
// respect screenGeometry
const c = Math.min(thumbnailGridView.count, maxGridColumnsByWidth);
const residue = thumbnailGridView.count % c;
if (residue == 0) {
return c;
}
// start greedy recursion
return columnCountRecursion(c, c, c - residue);
}
property int gridRows: Math.ceil(thumbnailGridView.count / gridColumns)
property int optimalWidth: thumbnailGridView.cellWidth * gridColumns
property int optimalHeight: thumbnailGridView.cellHeight * gridRows
width: Math.min(Math.max(thumbnailGridView.cellWidth, optimalWidth), maxWidth)
height: Math.min(Math.max(thumbnailGridView.cellHeight, optimalHeight), maxHeight)
clip: true
// Step for greedy algorithm
function columnCountRecursion(prevC, prevBestC, prevDiff) {
const c = prevC - 1;
// don't increase vertical extent more than horizontal
// and don't exceed maxHeight
if (prevC * prevC <= thumbnailGridView.count + prevDiff ||
maxHeight < Math.ceil(thumbnailGridView.count / c) * thumbnailGridView.cellHeight) {
return prevBestC;
}
const residue = thumbnailGridView.count % c;
// halts algorithm at some point
if (residue == 0) {
return c;
}
// empty slots
const diff = c - residue;
// compare it to previous count of empty slots
if (diff < prevDiff) {
return columnCountRecursion(c, c, diff);
} else if (diff == prevDiff) {
// when it's the same try again, we'll stop early enough thanks to the landscape mode condition
return columnCountRecursion(c, prevBestC, diff);
}
// when we've found a local minimum choose this one (greedy)
return columnCountRecursion(c, prevBestC, diff);
}
// Just to get the margin sizes
PlasmaCore.FrameSvgItem {
id: hoverItem
imagePath: "widgets/viewitem"
prefix: "hover"
visible: false
}
GridView {
id: thumbnailGridView
anchors.fill: parent
model: tabBox.model
readonly property int iconSize: PlasmaCore.Units.iconSizes.huge
readonly property int captionRowHeight: PlasmaCore.Units.gridUnit * 2
readonly property int columnSpacing: PlasmaCore.Units.largeSpacing
readonly property int thumbnailWidth: PlasmaCore.Units.gridUnit * 16
readonly property int thumbnailHeight: thumbnailWidth * (1.0/dialogMainItem.screenFactor)
cellWidth: hoverItem.margins.left + thumbnailWidth + hoverItem.margins.right
cellHeight: hoverItem.margins.top + captionRowHeight + thumbnailHeight + hoverItem.margins.bottom
keyNavigationWraps: true
highlightMoveDuration: 0
delegate: Item {
id: thumbnailGridItem
width: thumbnailGridView.cellWidth
height: thumbnailGridView.cellHeight
readonly property bool isCurrentItem: GridView.isCurrentItem
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: {
thumbnailGridItem.select();
}
}
function select() {
thumbnailGridView.currentIndex = index;
thumbnailGridView.currentIndexChanged(thumbnailGridView.currentIndex);
}
ColumnLayout {
id: columnLayout
z: 0
spacing: thumbnailGridView.columnSpacing
anchors.fill: parent
anchors.leftMargin: hoverItem.margins.left * 2
anchors.topMargin: hoverItem.margins.top * 2
anchors.rightMargin: hoverItem.margins.right * 2
anchors.bottomMargin: hoverItem.margins.bottom * 2
// KWin.WindowThumbnail needs a container
// otherwise it will be drawn the same size as the parent ColumnLayout
Item {
Layout.fillWidth: true
Layout.fillHeight: true
KWin.WindowThumbnail {
anchors.fill: parent
wId: windowId
}
PlasmaCore.IconItem {
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.bottom
anchors.verticalCenterOffset: Math.round(-columnLayout.spacing/2)
width: thumbnailGridView.iconSize
height: thumbnailGridView.iconSize
source: model.icon
usesPlasmaTheme: false
}
PlasmaComponents3.Button {
id: closeButton
anchors {
right: parent.right
top: parent.top
}
visible: model.closeable && typeof tabBox.model.close !== 'undefined' &&
(mouseArea.containsMouse
|| closeButton.hovered
|| thumbnailGridItem.isCurrentItem
|| Kirigami.Settings.tabletMode
|| Kirigami.Settings.hasTransientTouchInput
)
icon.name: 'window-close-symbolic'
onClicked: {
tabBox.model.close(index);
}
}
}
PlasmaComponents3.Label {
Layout.fillWidth: true
text: model.caption
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
textFormat: Text.PlainText
elide: Text.ElideRight
}
}
} // GridView.delegate
highlight: PlasmaCore.FrameSvgItem {
imagePath: "widgets/viewitem"
prefix: "hover"
}
Connections {
target: tabBox
function onCurrentIndexChanged() {
thumbnailGridView.currentIndex = tabBox.currentIndex;
}
}
} // GridView
Keys.onPressed: {
if (event.key == Qt.Key_Left) {
thumbnailGridView.moveCurrentIndexLeft();
} else if (event.key == Qt.Key_Right) {
thumbnailGridView.moveCurrentIndexRight();
} else if (event.key == Qt.Key_Up) {
thumbnailGridView.moveCurrentIndexUp();
} else if (event.key == Qt.Key_Down) {
thumbnailGridView.moveCurrentIndexDown();
} else {
return;
}
thumbnailGridView.currentIndexChanged(thumbnailGridView.currentIndex);
}
} // Dialog.mainItem
} // Dialog
}

View file

@ -0,0 +1,132 @@
{
"KPackageStructure": "KWin/WindowSwitcher",
"KPlugin": {
"Authors": [
{
"Email": "zrenfire@gmail.com",
"Name": "Chris Holland",
"Name[ar]": "Chris Holland",
"Name[az]": "Chris Holland",
"Name[bg]": "Chris Holland",
"Name[ca@valencia]": "Chris Holland",
"Name[ca]": "Chris Holland",
"Name[cs]": "Chris Holland",
"Name[de]": "Chris Holland",
"Name[en_GB]": "Chris Holland",
"Name[eo]": "Chris Holland",
"Name[es]": "Chris Holland",
"Name[eu]": "Chris Holland",
"Name[fi]": "Chris Holland",
"Name[fr]": "Chris Holland",
"Name[gl]": "Chris Holland",
"Name[ia]": "Chris Holland",
"Name[id]": "Chris Holland",
"Name[is]": "Chris Holland",
"Name[it]": "Chris Holland",
"Name[ja]": "Chris Holland",
"Name[ka]": "Chris Holland",
"Name[ko]": "Chris Holland",
"Name[lt]": "Chris Holland",
"Name[nl]": "Chris Holland",
"Name[nn]": "Chris Holland",
"Name[pl]": "Chris Holland",
"Name[pt]": "Chris Holland",
"Name[pt_BR]": "Chris Holland",
"Name[ro]": "Chris Holland",
"Name[ru]": "Chris Holland",
"Name[sk]": "Chris Holland",
"Name[sl]": "Chris Holland",
"Name[sv]": "Chris Holland",
"Name[tr]": "Chris Holland",
"Name[uk]": "Chris Holland",
"Name[vi]": "Chris Holland",
"Name[x-test]": "xxChris Hollandxx",
"Name[zh_CN]": "Chris Holland",
"Name[zh_TW]": "Chris Holland"
}
],
"Description": "A window switcher layout using a small grid of thumbnails",
"Description[ar]": "مخطط مبدل نوافذ يستخدم شبكة صغيرة من المصغرات",
"Description[az]": "Kiçik miniatürlər toru istifadə edən pəncərə dəyişdirici maketi",
"Description[bg]": "Оформление на програмата за превключване на прозорци с използване на редица от миниатюри за представяне на прозорците",
"Description[ca@valencia]": "Una disposició del commutador de finestres que utilitza una quadrícula xicoteta de miniatures",
"Description[ca]": "Una disposició del commutador de finestres que utilitza una quadrícula petita de miniatures",
"Description[cs]": "Přepínač oken používající malou mřížku náhledů",
"Description[de]": "Ein Fensterwechsler-Layout, das ein Raster von kleinen Vorschaubildern verwendet",
"Description[en_GB]": "A window switcher layout using a small grid of thumbnails",
"Description[eo]": "Fenestra ŝaltilo-aranĝo uzante malgrandan kradon de bildetoj",
"Description[es]": "Un esquema de selección de ventanas que usa una pequeña cuadrícula de miniaturas",
"Description[eu]": "Koadro-txikien sareta txiki bat erabiltzen duen leiho-trukatzaile antolamendu bat",
"Description[fi]": "Ikkunavaihtoasettelu, joka käyttää pientä pienoiskuvaruudukkoa",
"Description[fr]": "Une disposition du sélecteur de fenêtres utilisant une petite grille de vignettes",
"Description[gl]": "Unha disposición do selector de xanelas que usa unha pequena grade de miniaturas",
"Description[ia]": "Un disposition de commutator de fenestra usante un parve grillia de miniaturas",
"Description[id]": "Sebuah tataletak pengalih jendela yang menggunakan sebuah kisi kecil ber-thumbnail",
"Description[it]": "Una disposizione del selettore delle finestre che utilizza una piccola griglia di miniature",
"Description[ja]": "サムネイルの小さいグリッドを使用するウィンドウスイッチャー",
"Description[ka]": "ფანჯრების განლაგების გადართვა მინიატურების პატარა ბადის ჩვენების საშუალებით",
"Description[ko]": "작은 축소판 격자를 사용하는 창 전환기 레이아웃",
"Description[lt]": "Langų perjungiklio išdėstymas, naudojantis mažą miniatiūrų tinklelį",
"Description[nl]": "Een indeling van de vensterwisselaar met een klein raster van miniaturen",
"Description[nn]": "Vindaugsbytar som viser eit rutenett med miniatyrbilete",
"Description[pl]": "Układ przełączania okien, używający małej siatki miniatur",
"Description[pt]": "Uma disposição de mudança de janelas que mostra uma pequena grelha de miniaturas",
"Description[pt_BR]": "Um layout do seletor de janelas que usa uma pequena grade de miniaturas",
"Description[ro]": "Aranjament pentru comutatorul de ferestre folosind o grilă mică de pictograme",
"Description[ru]": "Переключатель окон в виде сетки миниатюр окон",
"Description[sk]": "Rozloženie prepínača okien používajúce malú mriežku miniatúr",
"Description[sl]": "Razporeditev preklopa oken, ki uporablja malo mrežo predoglednih sličic",
"Description[sv]": "En layout för fönsterbyte som använder ett litet miniatyrbildsrutnät",
"Description[tr]": "Küçük görsellerin bir ızgarasını gösteren pencere değiştirici düzeni",
"Description[uk]": "Компонування засобу перемикання вікон з малою таблицею мініатюр поточного стану вікон",
"Description[vi]": "Bố cục trình chuyển cửa sổ dùng một lưới nhỏ các hình nhỏ",
"Description[x-test]": "xxA window switcher layout using a small grid of thumbnailsxx",
"Description[zh_CN]": "将窗口缩略图按小型表格显示的窗口切换器布局",
"Description[zh_TW]": "使用小縮圖格線的視窗切換器佈局",
"Icon": "preferences-system-windows-switcher-thumbnail-grid",
"Id": "thumbnail_grid",
"License": "GPL",
"Name": "Thumbnail Grid",
"Name[ar]": "شبكة مصغرات",
"Name[az]": "Miniatürlər cədvəli",
"Name[bg]": "Решетка с миниатюри",
"Name[ca@valencia]": "Quadrícula de miniatures",
"Name[ca]": "Quadrícula de miniatures",
"Name[cs]": "Mřížka náhledů",
"Name[de]": "Vorschaubilder-Raster",
"Name[en_GB]": "Thumbnail Grid",
"Name[eo]": "Bildeto-Krado",
"Name[es]": "Cuadrícula de miniaturas",
"Name[eu]": "Koadro txikien sareta",
"Name[fi]": "Pienoiskuvaruudukko",
"Name[fr]": "Grille de vignettes",
"Name[gl]": "Grade de miniaturas",
"Name[ia]": "Grillia de Miniatura",
"Name[id]": "Kisi Thumbnail",
"Name[is]": "Raðaðar smámyndir",
"Name[it]": "Griglia delle miniature",
"Name[ja]": "サムネイルグリッド",
"Name[ka]": "მინიატურის ბადე",
"Name[ko]": "축소판 격자",
"Name[lt]": "Miniatiūrų tinklelis",
"Name[nl]": "Raster voor miniaturen",
"Name[nn]": "Rutenett med miniatyrbilete",
"Name[pl]": "Siatka miniatur",
"Name[pt]": "Grelha de Miniaturas",
"Name[pt_BR]": "Grade de miniaturas",
"Name[ro]": "Grilă de miniaturi",
"Name[ru]": "Сетка миниатюр",
"Name[sk]": "Mriežka miniatúr",
"Name[sl]": "Mreža predoglednih sličic",
"Name[sv]": "Miniatyrbildsrutnät",
"Name[tr]": "Küçük Görsel Izgarası",
"Name[uk]": "Таблиця мініатюр",
"Name[vi]": "Lưới hình nhỏ",
"Name[x-test]": "xxThumbnail Gridxx",
"Name[zh_CN]": "缩略图网格",
"Name[zh_TW]": "縮圖格線",
"Version": "6"
},
"X-Plasma-API": "declarativeappletscript",
"X-Plasma-MainScript": "ui/main.qml"
}

View file

@ -279,7 +279,7 @@ public:
}
static QString defaultLayoutName()
{
return QStringLiteral("org.kde.breeze.desktop");
return QStringLiteral("thumbnail_grid");
}
private:

View file

@ -235,7 +235,7 @@ QObject *TabBoxHandlerPrivate::createSwitcherItem()
// load default
offers = KPackage::PackageLoader::self()->findPackages(type, folderName,
[](const KPluginMetaData &data) {
return data.pluginId().compare(QStringLiteral("compact"), Qt::CaseInsensitive) == 0;
return data.pluginId().compare(QStringLiteral("thumbnail_grid"), Qt::CaseInsensitive) == 0;
});
if (offers.isEmpty()) {
qCDebug(KWIN_TABBOX) << "could not find default window switcher layout";