Commit 376ee357 authored by Marco Martin's avatar Marco Martin
Browse files

Make windowview replace present windows

* give windowview the ability to show windows from current desktop, all
desktops or current class
* invokable from shortcuts screen edges or gestures
* add a search field to quickly filter
* current present windows effect still present but only internal as is used by desktop grid, which should eventually be ported as well

This can be done either from windowview or overview, tough note that some of the duplication from overview added in windowview is necessary regardless, as WindowHeap, the shared qml part needs some properties exposed from the c++ part.
Implementation wise I'm ok for it being in either place, but i think the functionality of present windows needs to be preserved, otherwise would be a completely unacceptable regression, namely:

* Behavior of what now are the ctrl+f7,f10 and f11 global shortcuts, showing windows of current app (invokable by shoortcut instead of having to use the pointer), windows of current desktop (the only thing overview does atm) or all desktops
* filter on typing, as opposed to invoking krunner. main use case of present windows is quickly switching, and filtering is the most helpful feature, some people do like krunner instead, but is completely out of place for my use case, and i suspect for many other users as well
* also clicking on a taskbar group should be possible to filter
* the view that opens by clicking on a taskbar group should follow the same layout strategy used elsewhere and currently being an effect used only there and not accessible form the list can't even be configured
parent 2c514ac5
Pipeline #172189 passed with stage
in 13 minutes and 14 seconds
......@@ -70,6 +70,6 @@
},
"X-KDE-ConfigModule": "kwin_presentwindows_config",
"org.kde.kwin.effect": {
"video": "https://files.kde.org/plasma/kwin/effect-videos/present_windows.mp4"
"internal": true
}
}
......@@ -52,9 +52,6 @@ PresentWindowsEffect::PresentWindowsEffect()
, m_lastPresentTime(std::chrono::milliseconds::zero())
, m_filterFrame(nullptr)
, m_closeView(nullptr)
, m_exposeAction(new QAction(this))
, m_exposeAllAction(new QAction(this))
, m_exposeClassAction(new QAction(this))
{
initConfig<PresentWindowsConfig>();
......@@ -66,43 +63,6 @@ PresentWindowsEffect::PresentWindowsEffect()
announceSupportProperties();
connect(effects, &EffectsHandler::xcbConnectionChanged, this, announceSupportProperties);
QAction *exposeAction = m_exposeAction;
exposeAction->setObjectName(QStringLiteral("Expose"));
exposeAction->setText(i18n("Toggle Present Windows (Current desktop)"));
KGlobalAccel::self()->setDefaultShortcut(exposeAction, QList<QKeySequence>() << (Qt::CTRL | Qt::Key_F9));
KGlobalAccel::self()->setShortcut(exposeAction, QList<QKeySequence>() << (Qt::CTRL | Qt::Key_F9));
shortcut = KGlobalAccel::self()->shortcut(exposeAction);
effects->registerGlobalShortcut(Qt::CTRL | Qt::Key_F9, exposeAction);
connect(exposeAction, &QAction::triggered, this, &PresentWindowsEffect::toggleActive);
QAction *exposeAllAction = m_exposeAllAction;
exposeAllAction->setObjectName(QStringLiteral("ExposeAll"));
exposeAllAction->setText(i18n("Toggle Present Windows (All desktops)"));
KGlobalAccel::self()->setDefaultShortcut(exposeAllAction, QList<QKeySequence>() << (Qt::CTRL | Qt::Key_F10) << Qt::Key_LaunchC);
KGlobalAccel::self()->setShortcut(exposeAllAction, QList<QKeySequence>() << (Qt::CTRL | Qt::Key_F10) << Qt::Key_LaunchC);
shortcutAll = KGlobalAccel::self()->shortcut(exposeAllAction);
effects->registerGlobalShortcut(Qt::CTRL + Qt::Key_F10, exposeAllAction);
connect(exposeAllAction, &QAction::triggered, this, &PresentWindowsEffect::toggleActiveAllDesktops);
effects->registerRealtimeTouchpadSwipeShortcut(SwipeDirection::Down, 4, new QAction(this), [this](qreal progress) {
m_mode = ModeAllDesktops;
if (progress > .2 && !m_activated) {
setActive(true);
} else if (progress <= .2 && m_activated) {
setActive(false);
}
});
QAction *exposeClassAction = m_exposeClassAction;
exposeClassAction->setObjectName(QStringLiteral("ExposeClass"));
exposeClassAction->setText(i18n("Toggle Present Windows (Window class)"));
KGlobalAccel::self()->setDefaultShortcut(exposeClassAction, QList<QKeySequence>() << (Qt::CTRL | Qt::Key_F7));
KGlobalAccel::self()->setShortcut(exposeClassAction, QList<QKeySequence>() << (Qt::CTRL | Qt::Key_F7));
effects->registerGlobalShortcut(Qt::CTRL | Qt::Key_F7, exposeClassAction);
connect(exposeClassAction, &QAction::triggered, this, &PresentWindowsEffect::toggleActiveClass);
shortcutClass = KGlobalAccel::self()->shortcut(exposeClassAction);
connect(KGlobalAccel::self(), &KGlobalAccel::globalShortcutChanged, this, &PresentWindowsEffect::globalShortcutChanged);
reconfigure(ReconfigureAll);
connect(effects, &EffectsHandler::windowAdded, this, &PresentWindowsEffect::slotWindowAdded);
connect(effects, &EffectsHandler::windowClosed, this, &PresentWindowsEffect::slotWindowClosed);
......@@ -132,30 +92,6 @@ PresentWindowsEffect::~PresentWindowsEffect()
void PresentWindowsEffect::reconfigure(ReconfigureFlags)
{
PresentWindowsConfig::self()->read();
for (ElectricBorder border : qAsConst(m_borderActivate)) {
effects->unreserveElectricBorder(border, this);
}
for (ElectricBorder border : qAsConst(m_borderActivateAll)) {
effects->unreserveElectricBorder(border, this);
}
m_borderActivate.clear();
m_borderActivateAll.clear();
const auto borderActivate = PresentWindowsConfig::borderActivate();
for (int i : borderActivate) {
m_borderActivate.append(ElectricBorder(i));
effects->reserveElectricBorder(ElectricBorder(i), this);
}
const auto activateAll = PresentWindowsConfig::borderActivateAll();
for (int i : activateAll) {
m_borderActivateAll.append(ElectricBorder(i));
effects->reserveElectricBorder(ElectricBorder(i), this);
}
const auto activateClass = PresentWindowsConfig::borderActivateClass();
for (int i : activateClass) {
m_borderActivateClass.append(ElectricBorder(i));
effects->reserveElectricBorder(ElectricBorder(i), this);
}
m_layoutMode = PresentWindowsConfig::layoutMode();
m_showCaptions = PresentWindowsConfig::drawWindowCaptions();
......@@ -178,25 +114,6 @@ void PresentWindowsEffect::reconfigure(ReconfigureFlags)
m_leftButtonDesktop = (DesktopMouseAction)PresentWindowsConfig::leftButtonDesktop();
m_middleButtonDesktop = (DesktopMouseAction)PresentWindowsConfig::middleButtonDesktop();
m_rightButtonDesktop = (DesktopMouseAction)PresentWindowsConfig::rightButtonDesktop();
// touch screen edges
const QVector<ElectricBorder> relevantBorders{ElectricLeft, ElectricTop, ElectricRight, ElectricBottom};
for (auto e : relevantBorders) {
effects->unregisterTouchBorder(e, m_exposeAction);
effects->unregisterTouchBorder(e, m_exposeAllAction);
effects->unregisterTouchBorder(e, m_exposeClassAction);
}
auto touchEdge = [&relevantBorders](const QList<int> touchBorders, QAction *action) {
for (int i : touchBorders) {
if (!relevantBorders.contains(ElectricBorder(i))) {
continue;
}
effects->registerTouchBorder(ElectricBorder(i), action);
}
};
touchEdge(PresentWindowsConfig::touchBorderActivate(), m_exposeAction);
touchEdge(PresentWindowsConfig::touchBorderActivateAll(), m_exposeAllAction);
touchEdge(PresentWindowsConfig::touchBorderActivateClass(), m_exposeClassAction);
}
void *PresentWindowsEffect::proxy()
......@@ -583,35 +500,6 @@ void PresentWindowsEffect::slotWindowFrameGeometryChanged(EffectWindow *w, const
rearrangeWindows();
}
bool PresentWindowsEffect::borderActivated(ElectricBorder border)
{
int mode = 0;
if (m_borderActivate.contains(border)) {
mode |= 1;
} else if (m_borderActivateAll.contains(border)) {
mode |= 2;
} else if (m_borderActivateClass.contains(border)) {
mode |= 4;
}
if (!mode) {
return false;
}
if (effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this) {
return true;
}
if (mode & 1) {
toggleActive();
} else if (mode & 2) {
toggleActiveAllDesktops();
} else if (mode & 4) {
toggleActiveClass();
}
return true;
}
void PresentWindowsEffect::windowInputMouseEvent(QEvent *e)
{
QMouseEvent *me = dynamic_cast<QMouseEvent *>(e);
......@@ -820,21 +708,6 @@ void PresentWindowsEffect::mouseActionDesktop(DesktopMouseAction &action)
void PresentWindowsEffect::grabbedKeyboardEvent(QKeyEvent *e)
{
if (e->type() == QEvent::KeyPress) {
// check for global shortcuts
// HACK: keyboard grab disables the global shortcuts so we have to check for global shortcut (bug 156155)
if (m_mode == ModeCurrentDesktop && shortcut.contains(e->key() | e->modifiers())) {
toggleActive();
return;
}
if (m_mode == ModeAllDesktops && shortcutAll.contains(e->key() | e->modifiers())) {
toggleActiveAllDesktops();
return;
}
if (m_mode == ModeWindowClass && shortcutClass.contains(e->key() | e->modifiers())) {
toggleActiveClass();
return;
}
switch (e->key()) {
// Wrap only if not auto-repeating
case Qt::Key_Left:
......@@ -2132,20 +2005,6 @@ EffectWindow *PresentWindowsEffect::findFirstWindow() const
return topLeft;
}
void PresentWindowsEffect::globalShortcutChanged(QAction *action, const QKeySequence &seq)
{
if (action->objectName() == QStringLiteral("Expose")) {
shortcut.clear();
shortcut.append(seq);
} else if (action->objectName() == QStringLiteral("ExposeAll")) {
shortcutAll.clear();
shortcutAll.append(seq);
} else if (action->objectName() == QStringLiteral("ExposeClass")) {
shortcutClass.clear();
shortcutClass.append(seq);
}
}
bool PresentWindowsEffect::isActive() const
{
return (m_activated || m_motionManager.managingWindows()) && !effects->isScreenLocked();
......
......@@ -100,7 +100,6 @@ public:
void paintWindow(EffectWindow *w, int mask, QRegion region, WindowPaintData &data) override;
// User interaction
bool borderActivated(ElectricBorder border) override;
void windowInputMouseEvent(QEvent *e) override;
void grabbedKeyboardEvent(QKeyEvent *e) override;
bool isActive() const override;
......@@ -218,9 +217,6 @@ public Q_SLOTS:
}
void toggleActiveClass();
// slots for global shortcut changed
// needed to toggle the effect
void globalShortcutChanged(QAction *action, const QKeySequence &seq);
// EffectsHandler
void slotWindowAdded(KWin::EffectWindow *w);
void slotWindowClosed(KWin::EffectWindow *w);
......@@ -282,9 +278,6 @@ private:
friend class PresentWindowsEffectProxy;
// User configuration settings
QList<ElectricBorder> m_borderActivate;
QList<ElectricBorder> m_borderActivateAll;
QList<ElectricBorder> m_borderActivateClass;
int m_layoutMode;
bool m_showCaptions;
bool m_showIcons;
......@@ -321,11 +314,6 @@ private:
EffectFrame *m_filterFrame;
QString m_windowFilter;
// Shortcut - needed to toggle the effect
QList<QKeySequence> shortcut;
QList<QKeySequence> shortcutAll;
QList<QKeySequence> shortcutClass;
// Atoms
// Present windows for all windows of given desktop
// -1 for all desktops
......@@ -348,10 +336,6 @@ private:
qint32 id = 0;
bool active = false;
} m_touch;
QAction *m_exposeAction;
QAction *m_exposeAllAction;
QAction *m_exposeClassAction;
};
} // namespace
......
......@@ -47,13 +47,5 @@
<entry name="RightButtonDesktop" type="Int">
<default>0</default>
</entry>
<entry name="BorderActivate" type="IntList" />
<entry name="BorderActivateAll" type="IntList">
<default code="true">QList&lt;int&gt;() &lt;&lt; int(ElectricTopLeft)</default>
</entry>
<entry name="BorderActivateClass" type="IntList" />
<entry name="TouchBorderActivate" type="IntList" />
<entry name="TouchBorderActivateAll" type="IntList"/>
<entry name="TouchBorderActivateClass" type="IntList" />
</group>
</kcfg>
......@@ -29,12 +29,19 @@ FocusScope {
property bool dragEnabled: true
property real padding: 0
property var showOnly: []
property string activeClass
readonly property alias count: windowsRepeater.count
required property bool organized
readonly property bool effectiveOrganized: expoLayout.ready && organized
signal activated()
function activateIndex(index) {
KWinComponents.Workspace.activeClient = windowsRepeater.itemAt(index).client;
activated();
}
ExpoLayout {
id: expoLayout
x: heap.padding
......@@ -54,7 +61,25 @@ FocusScope {
readonly property bool selected: heap.selectedIndex == index
readonly property bool hidden: {
return heap.showOnly.length && heap.showOnly.indexOf(client.internalId) == -1;
if (heap.showOnly === "activeClass") {
return heap.activeClass !== String(thumb.client.resourceName); // thumb.client.resourceName is not an actual String as comes from a QByteArray so === would fail
} else {
return heap.showOnly.length && heap.showOnly.indexOf(client.internalId) == -1;
}
}
Component.onCompleted: {
if (thumb.client.active) {
heap.activeClass = thumb.client.resourceName;
}
}
Connections {
target: thumb.client
function onActiveChanged() {
if (thumb.client.active) {
heap.activeClass = thumb.client.resourceName;
}
}
}
state: {
......
......@@ -2,18 +2,26 @@
#
# SPDX-License-Identifier: BSD-3-Clause
if (KWIN_BUILD_KCMS)
add_subdirectory(kcm)
endif()
set(windowview_SOURCES
main.cpp
windowvieweffect.cpp
)
kconfig_add_kcfg_files(windowview_SOURCES
windowviewconfig.kcfgc
)
qt_add_dbus_adaptor(windowview_SOURCES org.kde.KWin.Effect.WindowView1.xml windowvieweffect.h KWin::WindowViewEffect)
kwin4_add_effect_module(kwin4_effect_windowview ${windowview_SOURCES})
target_link_libraries(kwin4_effect_windowview PRIVATE
kwineffects
KF5::ConfigGui
KF5::GlobalAccel
KF5::I18n
......
# SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
#
# SPDX-License-Identifier: BSD-3-Clause
set(kwin_windowview_config_SOURCES windowvieweffectkcm.cpp)
ki18n_wrap_ui(kwin_windowview_config_SOURCES windowvieweffectkcm.ui)
kconfig_add_kcfg_files(kwin_windowview_config_SOURCES ../windowviewconfig.kcfgc)
kwin_add_effect_config(kwin_windowview_config ${kwin_windowview_config_SOURCES})
target_link_libraries(kwin_windowview_config
KF5::ConfigWidgets
KF5::CoreAddons
KF5::GlobalAccel
KF5::I18n
KF5::XmlGui
KWinEffectsInterface
)
/*
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "windowvieweffectkcm.h"
#include <config-kwin.h>
#include "windowviewconfig.h"
#include <kwineffects_interface.h>
#include <KActionCollection>
#include <KGlobalAccel>
#include <KLocalizedString>
#include <KPluginFactory>
#include <QAction>
K_PLUGIN_CLASS(KWin::WindowViewEffectConfig)
namespace KWin
{
WindowViewEffectConfig::WindowViewEffectConfig(QWidget *parent, const QVariantList &args)
: KCModule(parent, args)
{
ui.setupUi(this);
WindowViewConfig::instance(KWIN_CONFIG);
addConfig(WindowViewConfig::self(), this);
auto actionCollection = new KActionCollection(this, QStringLiteral("kwin"));
actionCollection->setComponentDisplayName(i18n("KWin"));
actionCollection->setConfigGroup(QStringLiteral("windowview"));
actionCollection->setConfigGlobal(true);
const QKeySequence defaultToggleShortcut = Qt::CTRL | Qt::Key_F9;
QAction *toggleAction = actionCollection->addAction(QStringLiteral("Expose"));
toggleAction->setText(i18n("Toggle Present Windows (Current desktop)"));
toggleAction->setProperty("isConfigurationAction", true);
KGlobalAccel::self()->setDefaultShortcut(toggleAction, {defaultToggleShortcut});
KGlobalAccel::self()->setShortcut(toggleAction, {defaultToggleShortcut});
const QKeySequence defaultToggleShortcutAll = Qt::CTRL | Qt::Key_F10;
toggleAction = actionCollection->addAction(QStringLiteral("ExposeAll"));
toggleAction->setText(i18n("Toggle Present Windows (All desktops)"));
toggleAction->setProperty("isConfigurationAction", true);
KGlobalAccel::self()->setDefaultShortcut(toggleAction, {defaultToggleShortcutAll});
KGlobalAccel::self()->setShortcut(toggleAction, {defaultToggleShortcutAll});
const QKeySequence defaultToggleShortcutClass = Qt::CTRL | Qt::Key_F7;
toggleAction = actionCollection->addAction(QStringLiteral("ExposeClass"));
toggleAction->setText(i18n("Toggle Present Windows (Window class)"));
toggleAction->setProperty("isConfigurationAction", true);
KGlobalAccel::self()->setDefaultShortcut(toggleAction, {defaultToggleShortcutClass});
KGlobalAccel::self()->setShortcut(toggleAction, {defaultToggleShortcutClass});
ui.shortcutsEditor->addCollection(actionCollection);
connect(ui.shortcutsEditor, &KShortcutsEditor::keyChange, this, &WindowViewEffectConfig::markAsChanged);
}
WindowViewEffectConfig::~WindowViewEffectConfig()
{
// If save() is called, undo() has no effect.
ui.shortcutsEditor->undo();
}
void WindowViewEffectConfig::save()
{
KCModule::save();
ui.shortcutsEditor->save();
OrgKdeKwinEffectsInterface interface(QStringLiteral("org.kde.KWin"),
QStringLiteral("/Effects"),
QDBusConnection::sessionBus());
interface.reconfigureEffect(QStringLiteral("windowview"));
}
void WindowViewEffectConfig::defaults()
{
ui.shortcutsEditor->allDefault();
KCModule::defaults();
}
} // namespace KWin
#include "windowvieweffectkcm.moc"
/*
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <KCModule>
#include "ui_windowvieweffectkcm.h"
namespace KWin
{
class WindowViewEffectConfig : public KCModule
{
Q_OBJECT
public:
explicit WindowViewEffectConfig(QWidget *parent = nullptr, const QVariantList &args = QVariantList());
~WindowViewEffectConfig() override;
public Q_SLOTS:
void save() override;
void defaults() override;
private:
::Ui::WindowViewEffectConfig ui;
};
} // namespace KWin
<?xml version="1.0" encoding="UTF-8"?>
<!--
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
-->
<ui version="4.0">
<class>WindowViewEffectConfig</class>
<widget class="QWidget" name="WindowViewEffectConfig">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>455</width>
<height>177</height>
</rect>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_LayoutMode">
<property name="text">
<string>Layout mode:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="kcfg_LayoutMode">
<item>
<property name="text">
<string>Closest</string>
</property>
</item>
<item>
<property name="text">
<string>Natural</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="KShortcutsEditor" name="shortcutsEditor">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="actionTypes">
<enum>KShortcutsEditor::GlobalAction</enum>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="kcfg_BlurBackground"/>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KShortcutsEditor</class>
<extends>QWidget</extends>
<header>kshortcutseditor.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
{
"KPlugin": {
"Category": "Window Management",
"Description": "Helper effect for the task manager",
"Description[ar]": "تأثير مساعدة لمدير المهام",
"Description[az]": "Tapşırıq meneceri üçün köməkçi effekti",
"Description[ca@valencia]": "Efecte auxiliar per al gestor de tasques",
"Description[ca]": "Efecte auxiliar per al gestor de tasques",
"Description[en_GB]": "Helper effect for the task manager",
"Description[es]": "Efecto auxiliar para el gestor de tareas",
"Description[fr]": "Effet d'assistance pour le gestionnaire de tâches",
"Description[ia]": "Effecto adjutante per le gerente de carga",
"Description[it]": "Effetto di assistenza per il gestore delle applicazioni",
"Description[ko]": "작업 관리자용 도우미 효과",
"Description[nl]": "Effect van hulp voor de takenbeheerder",
"Description[pl]": "Efekt pomocniczy dla zarządzania zadaniami",
"Description[pt]": "Efeito auxiliar para o gestor de tarefas",
"Description[pt_BR]": "Efeito auxiliar para o gerenciador de tarefas",
"Description[sl]": "Učinki pomočnika za upravljalnika nalog",
"Description[sv]": "Hjälpeffekt för aktivitetshanteraren",
"Description[tr]": "Görev yöneticisi için yardımcı etkisi",
"Description[uk]": "Допоміжний ефект для керування завданнями",
"Description[vi]": "Hiệu ứng trợ giúp cho trình quản lí tác vụ",
"Description[x-test]": "xxHelper effect for the task managerxx",
"Description[zh_CN]": "任务管理器的辅助特效",
"Description": "Zoom out until all opened windows can be displayed side-by-side",
"Description[ar]": "بعِّد إلى أن يصير بالإمكان عرض كل النوافذ المفتوحة جبناً إلى جنب",
"Description[az]": "Bütün açıq pəncərələrin miniatürünü yan-yana göstərir",
"Description[ca@valencia]": "Redueix fins que totes les finestres obertes es poden mostrar una al costat de l'altra",
"Description[ca]": "Redueix fins que totes les finestres obertes es poden mostrar una al costat de l'altra",
"Description[cs]": "Oddálí plochu, aby byla vidět všechna okna",
"Description[de]": "Verkleinert Fenster auf der Arbeitsfläche, sodass sie alle nebeneinander sichtbar sind",
"Description[en_GB]": "Zoom out until all opened windows can be displayed side-by-side",
"Description[es]": "Reducir hasta que todas las ventanas abiertas se puedan mostrar una al lado de la otra",
"Description[fi]": "Loitontaa työpöytää kunnes avoimet ikkunat voi esittää rinnakkain",
"Description[fr]": "Faire un zoom arrière jusqu'à ce que toutes les fenêtres ouvertes puissent être affichées côte à côte",
"Description[hu]": "Kinagyítja a nézetet, hogy az összes megnyitott ablak áttekinthető legyen",
"Description[ia]": "Zoom foras usque omne fenestras aperite pote esser monstrate flanco a flanco",
"Description[id]": "Zoom keluar hingga semua window yang terbuka bisa ditampilkan sisi demi sisi",
"Description[it]": "Arretra per far vedere tutte le finestre aperte fianco a fianco",
"Description[ko]": "모든 열린 창을 축소시켜서 한 화면에 보이도록 합니다",
"Description[nl]": "Zoomt uit totdat alle geopende vensters zij-aan-zij kunnen worden getoond",
"Description[nn]": "Forminsk skjermflata heilt til alle dei