File indexing completed on 2024-11-10 04:57:58
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 2009 Lucas Murray <lmurray@undefinedfire.com> 0006 SPDX-FileCopyrightText: 2012 Martin Gräßlin <mgraesslin@kde.org> 0007 0008 SPDX-License-Identifier: GPL-2.0-or-later 0009 */ 0010 #include "virtualdesktops.h" 0011 #include "input.h" 0012 #include "wayland/plasmavirtualdesktop.h" 0013 // KDE 0014 #include <KConfigGroup> 0015 #include <KGlobalAccel> 0016 #include <KLocalizedString> 0017 #include <NETWM> 0018 0019 // Qt 0020 #include <QAction> 0021 #include <QDebug> 0022 #include <QUuid> 0023 0024 #include <algorithm> 0025 0026 namespace KWin 0027 { 0028 0029 static bool s_loadingDesktopSettings = false; 0030 static const double GESTURE_SWITCH_THRESHOLD = .25; 0031 0032 static QString generateDesktopId() 0033 { 0034 return QUuid::createUuid().toString(QUuid::WithoutBraces); 0035 } 0036 0037 VirtualDesktop::VirtualDesktop(QObject *parent) 0038 : QObject(parent) 0039 { 0040 } 0041 0042 VirtualDesktop::~VirtualDesktop() 0043 { 0044 Q_EMIT aboutToBeDestroyed(); 0045 } 0046 0047 void VirtualDesktopManager::setVirtualDesktopManagement(PlasmaVirtualDesktopManagementInterface *management) 0048 { 0049 Q_ASSERT(!m_virtualDesktopManagement); 0050 m_virtualDesktopManagement = management; 0051 0052 auto createPlasmaVirtualDesktop = [this](VirtualDesktop *desktop) { 0053 PlasmaVirtualDesktopInterface *pvd = m_virtualDesktopManagement->createDesktop(desktop->id(), desktop->x11DesktopNumber() - 1); 0054 pvd->setName(desktop->name()); 0055 pvd->sendDone(); 0056 0057 connect(desktop, &VirtualDesktop::nameChanged, pvd, [desktop, pvd]() { 0058 pvd->setName(desktop->name()); 0059 pvd->sendDone(); 0060 }); 0061 connect(pvd, &PlasmaVirtualDesktopInterface::activateRequested, this, [this, desktop]() { 0062 setCurrent(desktop); 0063 }); 0064 }; 0065 0066 connect(this, &VirtualDesktopManager::desktopAdded, m_virtualDesktopManagement, createPlasmaVirtualDesktop); 0067 0068 connect(this, &VirtualDesktopManager::rowsChanged, m_virtualDesktopManagement, [this](uint rows) { 0069 m_virtualDesktopManagement->setRows(rows); 0070 m_virtualDesktopManagement->sendDone(); 0071 }); 0072 0073 // handle removed: from VirtualDesktopManager to the wayland interface 0074 connect(this, &VirtualDesktopManager::desktopRemoved, m_virtualDesktopManagement, [this](VirtualDesktop *desktop) { 0075 m_virtualDesktopManagement->removeDesktop(desktop->id()); 0076 }); 0077 0078 // create a new desktop when the client asks to 0079 connect(m_virtualDesktopManagement, &PlasmaVirtualDesktopManagementInterface::desktopCreateRequested, this, [this](const QString &name, quint32 position) { 0080 createVirtualDesktop(position, name); 0081 }); 0082 0083 // remove when the client asks to 0084 connect(m_virtualDesktopManagement, &PlasmaVirtualDesktopManagementInterface::desktopRemoveRequested, this, [this](const QString &id) { 0085 // here there can be some nice kauthorized check? 0086 // remove only from VirtualDesktopManager, the other connections will remove it from m_virtualDesktopManagement as well 0087 removeVirtualDesktop(id); 0088 }); 0089 0090 connect(this, &VirtualDesktopManager::currentChanged, m_virtualDesktopManagement, [this]() { 0091 const QList<PlasmaVirtualDesktopInterface *> deskIfaces = m_virtualDesktopManagement->desktops(); 0092 for (auto *deskInt : deskIfaces) { 0093 if (deskInt->id() == currentDesktop()->id()) { 0094 deskInt->setActive(true); 0095 } else { 0096 deskInt->setActive(false); 0097 } 0098 } 0099 }); 0100 0101 std::for_each(m_desktops.constBegin(), m_desktops.constEnd(), createPlasmaVirtualDesktop); 0102 0103 m_virtualDesktopManagement->setRows(rows()); 0104 m_virtualDesktopManagement->sendDone(); 0105 } 0106 0107 void VirtualDesktop::setId(const QString &id) 0108 { 0109 Q_ASSERT(m_id.isEmpty()); 0110 m_id = id; 0111 } 0112 0113 void VirtualDesktop::setX11DesktopNumber(uint number) 0114 { 0115 // x11DesktopNumber can be changed now 0116 if (static_cast<uint>(m_x11DesktopNumber) == number) { 0117 return; 0118 } 0119 0120 m_x11DesktopNumber = number; 0121 0122 if (m_x11DesktopNumber != 0) { 0123 Q_EMIT x11DesktopNumberChanged(); 0124 } 0125 } 0126 0127 void VirtualDesktop::setName(const QString &name) 0128 { 0129 if (m_name == name) { 0130 return; 0131 } 0132 m_name = name; 0133 Q_EMIT nameChanged(); 0134 } 0135 0136 VirtualDesktopGrid::VirtualDesktopGrid() 0137 : m_size(1, 2) // Default to tow rows 0138 , m_grid(QList<QList<VirtualDesktop *>>{QList<VirtualDesktop *>{}, QList<VirtualDesktop *>{}}) 0139 { 0140 } 0141 0142 VirtualDesktopGrid::~VirtualDesktopGrid() = default; 0143 0144 void VirtualDesktopGrid::update(const QSize &size, const QList<VirtualDesktop *> &desktops) 0145 { 0146 // Set private variables 0147 m_size = size; 0148 const uint width = size.width(); 0149 const uint height = size.height(); 0150 0151 m_grid.clear(); 0152 auto it = desktops.begin(); 0153 auto end = desktops.end(); 0154 for (uint y = 0; y < height; ++y) { 0155 QList<VirtualDesktop *> row; 0156 for (uint x = 0; x < width && it != end; ++x) { 0157 row << *it; 0158 it++; 0159 } 0160 m_grid << row; 0161 } 0162 } 0163 0164 QPoint VirtualDesktopGrid::gridCoords(uint id) const 0165 { 0166 return gridCoords(VirtualDesktopManager::self()->desktopForX11Id(id)); 0167 } 0168 0169 QPoint VirtualDesktopGrid::gridCoords(VirtualDesktop *vd) const 0170 { 0171 for (int y = 0; y < m_grid.count(); ++y) { 0172 const auto &row = m_grid.at(y); 0173 for (int x = 0; x < row.count(); ++x) { 0174 if (row.at(x) == vd) { 0175 return QPoint(x, y); 0176 } 0177 } 0178 } 0179 return QPoint(-1, -1); 0180 } 0181 0182 VirtualDesktop *VirtualDesktopGrid::at(const QPoint &coords) const 0183 { 0184 if (coords.y() >= m_grid.count()) { 0185 return nullptr; 0186 } 0187 const auto &row = m_grid.at(coords.y()); 0188 if (coords.x() >= row.count()) { 0189 return nullptr; 0190 } 0191 return row.at(coords.x()); 0192 } 0193 0194 KWIN_SINGLETON_FACTORY_VARIABLE(VirtualDesktopManager, s_manager) 0195 0196 VirtualDesktopManager::VirtualDesktopManager(QObject *parent) 0197 : QObject(parent) 0198 , m_navigationWrapsAround(false) 0199 , m_rootInfo(nullptr) 0200 , m_swipeGestureReleasedY(new QAction(this)) 0201 , m_swipeGestureReleasedX(new QAction(this)) 0202 { 0203 } 0204 0205 VirtualDesktopManager::~VirtualDesktopManager() 0206 { 0207 s_manager = nullptr; 0208 } 0209 0210 void VirtualDesktopManager::setRootInfo(NETRootInfo *info) 0211 { 0212 m_rootInfo = info; 0213 0214 // Nothing will be connected to rootInfo 0215 if (m_rootInfo) { 0216 updateRootInfo(); 0217 m_rootInfo->setCurrentDesktop(currentDesktop()->x11DesktopNumber()); 0218 for (auto *vd : std::as_const(m_desktops)) { 0219 m_rootInfo->setDesktopName(vd->x11DesktopNumber(), vd->name().toUtf8().data()); 0220 } 0221 } 0222 } 0223 0224 VirtualDesktop *VirtualDesktopManager::inDirection(VirtualDesktop *desktop, Direction direction, bool wrap) 0225 { 0226 switch (direction) { 0227 case Direction::Up: 0228 return above(desktop, wrap); 0229 case Direction::Down: 0230 return below(desktop, wrap); 0231 case Direction::Right: 0232 return toRight(desktop, wrap); 0233 case Direction::Left: 0234 return toLeft(desktop, wrap); 0235 case Direction::Next: 0236 return next(desktop, wrap); 0237 case Direction::Previous: 0238 return previous(desktop, wrap); 0239 } 0240 Q_UNREACHABLE(); 0241 } 0242 0243 uint VirtualDesktopManager::inDirection(uint desktop, Direction direction, bool wrap) 0244 { 0245 return inDirection(desktopForX11Id(desktop), direction, wrap)->x11DesktopNumber(); 0246 } 0247 0248 void VirtualDesktopManager::moveTo(Direction direction, bool wrap) 0249 { 0250 setCurrent(inDirection(nullptr, direction, wrap)); 0251 } 0252 0253 VirtualDesktop *VirtualDesktopManager::above(VirtualDesktop *desktop, bool wrap) const 0254 { 0255 Q_ASSERT(m_current); 0256 if (!desktop) { 0257 desktop = m_current; 0258 } 0259 QPoint coords = m_grid.gridCoords(desktop); 0260 Q_ASSERT(coords.x() >= 0); 0261 while (true) { 0262 coords.ry()--; 0263 if (coords.y() < 0) { 0264 if (wrap) { 0265 coords.setY(m_grid.height() - 1); 0266 } else { 0267 return desktop; // Already at the top-most desktop 0268 } 0269 } 0270 if (VirtualDesktop *vd = m_grid.at(coords)) { 0271 return vd; 0272 } 0273 } 0274 return nullptr; 0275 } 0276 0277 VirtualDesktop *VirtualDesktopManager::toRight(VirtualDesktop *desktop, bool wrap) const 0278 { 0279 Q_ASSERT(m_current); 0280 if (!desktop) { 0281 desktop = m_current; 0282 } 0283 QPoint coords = m_grid.gridCoords(desktop); 0284 Q_ASSERT(coords.x() >= 0); 0285 while (true) { 0286 coords.rx()++; 0287 if (coords.x() >= m_grid.width()) { 0288 if (wrap) { 0289 coords.setX(0); 0290 } else { 0291 return desktop; // Already at the right-most desktop 0292 } 0293 } 0294 if (VirtualDesktop *vd = m_grid.at(coords)) { 0295 return vd; 0296 } 0297 } 0298 return nullptr; 0299 } 0300 0301 VirtualDesktop *VirtualDesktopManager::below(VirtualDesktop *desktop, bool wrap) const 0302 { 0303 Q_ASSERT(m_current); 0304 if (!desktop) { 0305 desktop = m_current; 0306 } 0307 QPoint coords = m_grid.gridCoords(desktop); 0308 Q_ASSERT(coords.x() >= 0); 0309 while (true) { 0310 coords.ry()++; 0311 if (coords.y() >= m_grid.height()) { 0312 if (wrap) { 0313 coords.setY(0); 0314 } else { 0315 // Already at the bottom-most desktop 0316 return desktop; 0317 } 0318 } 0319 if (VirtualDesktop *vd = m_grid.at(coords)) { 0320 return vd; 0321 } 0322 } 0323 return nullptr; 0324 } 0325 0326 VirtualDesktop *VirtualDesktopManager::toLeft(VirtualDesktop *desktop, bool wrap) const 0327 { 0328 Q_ASSERT(m_current); 0329 if (!desktop) { 0330 desktop = m_current; 0331 } 0332 QPoint coords = m_grid.gridCoords(desktop); 0333 Q_ASSERT(coords.x() >= 0); 0334 while (true) { 0335 coords.rx()--; 0336 if (coords.x() < 0) { 0337 if (wrap) { 0338 coords.setX(m_grid.width() - 1); 0339 } else { 0340 return desktop; // Already at the left-most desktop 0341 } 0342 } 0343 if (VirtualDesktop *vd = m_grid.at(coords)) { 0344 return vd; 0345 } 0346 } 0347 return nullptr; 0348 } 0349 0350 VirtualDesktop *VirtualDesktopManager::next(VirtualDesktop *desktop, bool wrap) const 0351 { 0352 Q_ASSERT(m_current); 0353 if (!desktop) { 0354 desktop = m_current; 0355 } 0356 auto it = std::find(m_desktops.begin(), m_desktops.end(), desktop); 0357 Q_ASSERT(it != m_desktops.end()); 0358 it++; 0359 if (it == m_desktops.end()) { 0360 if (wrap) { 0361 return m_desktops.first(); 0362 } else { 0363 return desktop; 0364 } 0365 } 0366 return *it; 0367 } 0368 0369 VirtualDesktop *VirtualDesktopManager::previous(VirtualDesktop *desktop, bool wrap) const 0370 { 0371 Q_ASSERT(m_current); 0372 if (!desktop) { 0373 desktop = m_current; 0374 } 0375 auto it = std::find(m_desktops.begin(), m_desktops.end(), desktop); 0376 Q_ASSERT(it != m_desktops.end()); 0377 if (it == m_desktops.begin()) { 0378 if (wrap) { 0379 return m_desktops.last(); 0380 } else { 0381 return desktop; 0382 } 0383 } 0384 it--; 0385 return *it; 0386 } 0387 0388 VirtualDesktop *VirtualDesktopManager::desktopForX11Id(uint id) const 0389 { 0390 if (id == 0 || id > count()) { 0391 return nullptr; 0392 } 0393 return m_desktops.at(id - 1); 0394 } 0395 0396 VirtualDesktop *VirtualDesktopManager::desktopForId(const QString &id) const 0397 { 0398 auto desk = std::find_if( 0399 m_desktops.constBegin(), 0400 m_desktops.constEnd(), 0401 [id](const VirtualDesktop *desk) { 0402 return desk->id() == id; 0403 }); 0404 0405 if (desk != m_desktops.constEnd()) { 0406 return *desk; 0407 } 0408 0409 return nullptr; 0410 } 0411 0412 VirtualDesktop *VirtualDesktopManager::createVirtualDesktop(uint position, const QString &name) 0413 { 0414 // too many, can't insert new ones 0415 if ((uint)m_desktops.count() == VirtualDesktopManager::maximum()) { 0416 return nullptr; 0417 } 0418 0419 position = std::clamp(position, 0u, static_cast<uint>(m_desktops.count())); 0420 0421 QString desktopName = name; 0422 if (desktopName.isEmpty()) { 0423 desktopName = defaultName(position + 1); 0424 } 0425 0426 auto *vd = new VirtualDesktop(this); 0427 vd->setX11DesktopNumber(position + 1); 0428 vd->setId(generateDesktopId()); 0429 vd->setName(desktopName); 0430 0431 connect(vd, &VirtualDesktop::nameChanged, this, [this, vd]() { 0432 if (m_rootInfo) { 0433 m_rootInfo->setDesktopName(vd->x11DesktopNumber(), vd->name().toUtf8().data()); 0434 } 0435 }); 0436 0437 if (m_rootInfo) { 0438 m_rootInfo->setDesktopName(vd->x11DesktopNumber(), vd->name().toUtf8().data()); 0439 } 0440 0441 m_desktops.insert(position, vd); 0442 0443 // update the id of displaced desktops 0444 for (uint i = position + 1; i < (uint)m_desktops.count(); ++i) { 0445 m_desktops[i]->setX11DesktopNumber(i + 1); 0446 if (m_rootInfo) { 0447 m_rootInfo->setDesktopName(i + 1, m_desktops[i]->name().toUtf8().data()); 0448 } 0449 } 0450 0451 save(); 0452 0453 updateLayout(); 0454 updateRootInfo(); 0455 Q_EMIT desktopAdded(vd); 0456 Q_EMIT countChanged(m_desktops.count() - 1, m_desktops.count()); 0457 return vd; 0458 } 0459 0460 void VirtualDesktopManager::removeVirtualDesktop(const QString &id) 0461 { 0462 auto desktop = desktopForId(id); 0463 if (desktop) { 0464 removeVirtualDesktop(desktop); 0465 } 0466 } 0467 0468 void VirtualDesktopManager::removeVirtualDesktop(VirtualDesktop *desktop) 0469 { 0470 // don't end up without any desktop 0471 if (m_desktops.count() == 1) { 0472 return; 0473 } 0474 0475 const int i = m_desktops.indexOf(desktop); 0476 m_desktops.remove(i); 0477 0478 for (int j = i; j < m_desktops.count(); ++j) { 0479 m_desktops[j]->setX11DesktopNumber(j + 1); 0480 if (m_rootInfo) { 0481 m_rootInfo->setDesktopName(j + 1, m_desktops[j]->name().toUtf8().data()); 0482 } 0483 } 0484 0485 if (m_current == desktop) { 0486 m_current = (i < m_desktops.count()) ? m_desktops.at(i) : m_desktops.constLast(); 0487 Q_EMIT currentChanged(desktop, m_current); 0488 } 0489 0490 updateLayout(); 0491 updateRootInfo(); 0492 save(); 0493 0494 Q_EMIT desktopRemoved(desktop); 0495 Q_EMIT countChanged(m_desktops.count() + 1, m_desktops.count()); 0496 0497 desktop->deleteLater(); 0498 } 0499 0500 uint VirtualDesktopManager::current() const 0501 { 0502 return m_current ? m_current->x11DesktopNumber() : 0; 0503 } 0504 0505 VirtualDesktop *VirtualDesktopManager::currentDesktop() const 0506 { 0507 return m_current; 0508 } 0509 0510 bool VirtualDesktopManager::setCurrent(uint newDesktop) 0511 { 0512 if (newDesktop < 1 || newDesktop > count()) { 0513 return false; 0514 } 0515 auto d = desktopForX11Id(newDesktop); 0516 Q_ASSERT(d); 0517 return setCurrent(d); 0518 } 0519 0520 bool VirtualDesktopManager::setCurrent(VirtualDesktop *newDesktop) 0521 { 0522 Q_ASSERT(newDesktop); 0523 if (m_current == newDesktop) { 0524 return false; 0525 } 0526 VirtualDesktop *oldDesktop = currentDesktop(); 0527 m_current = newDesktop; 0528 Q_EMIT currentChanged(oldDesktop, newDesktop); 0529 return true; 0530 } 0531 0532 void VirtualDesktopManager::setCount(uint count) 0533 { 0534 count = std::clamp<uint>(count, 1, VirtualDesktopManager::maximum()); 0535 if (count == uint(m_desktops.count())) { 0536 // nothing to change 0537 return; 0538 } 0539 QList<VirtualDesktop *> newDesktops; 0540 const uint oldCount = m_desktops.count(); 0541 // this explicit check makes it more readable 0542 if ((uint)m_desktops.count() > count) { 0543 const auto desktopsToRemove = m_desktops.mid(count); 0544 m_desktops.resize(count); 0545 if (m_current && desktopsToRemove.contains(m_current)) { 0546 VirtualDesktop *oldCurrent = m_current; 0547 m_current = m_desktops.last(); 0548 Q_EMIT currentChanged(oldCurrent, m_current); 0549 } 0550 for (auto desktop : desktopsToRemove) { 0551 Q_EMIT desktopRemoved(desktop); 0552 desktop->deleteLater(); 0553 } 0554 } else { 0555 while (uint(m_desktops.count()) < count) { 0556 auto vd = new VirtualDesktop(this); 0557 const int x11Number = m_desktops.count() + 1; 0558 vd->setX11DesktopNumber(x11Number); 0559 vd->setName(defaultName(x11Number)); 0560 if (!s_loadingDesktopSettings) { 0561 vd->setId(generateDesktopId()); 0562 } 0563 m_desktops << vd; 0564 newDesktops << vd; 0565 connect(vd, &VirtualDesktop::nameChanged, this, [this, vd]() { 0566 if (m_rootInfo) { 0567 m_rootInfo->setDesktopName(vd->x11DesktopNumber(), vd->name().toUtf8().data()); 0568 } 0569 }); 0570 if (m_rootInfo) { 0571 m_rootInfo->setDesktopName(vd->x11DesktopNumber(), vd->name().toUtf8().data()); 0572 } 0573 } 0574 } 0575 0576 if (!m_current) { 0577 m_current = m_desktops.at(0); 0578 } 0579 0580 updateLayout(); 0581 updateRootInfo(); 0582 0583 if (!s_loadingDesktopSettings) { 0584 save(); 0585 } 0586 for (auto vd : std::as_const(newDesktops)) { 0587 Q_EMIT desktopAdded(vd); 0588 } 0589 Q_EMIT countChanged(oldCount, m_desktops.count()); 0590 } 0591 0592 uint VirtualDesktopManager::rows() const 0593 { 0594 return m_rows; 0595 } 0596 0597 void VirtualDesktopManager::setRows(uint rows) 0598 { 0599 if (rows == 0 || rows > count() || rows == m_rows) { 0600 return; 0601 } 0602 0603 m_rows = rows; 0604 0605 updateLayout(); 0606 updateRootInfo(); 0607 } 0608 0609 void VirtualDesktopManager::updateRootInfo() 0610 { 0611 if (m_rootInfo) { 0612 const int n = count(); 0613 m_rootInfo->setNumberOfDesktops(n); 0614 NETPoint *viewports = new NETPoint[n]; 0615 m_rootInfo->setDesktopViewport(n, *viewports); 0616 delete[] viewports; 0617 m_rootInfo->setDesktopLayout(NET::OrientationHorizontal, m_grid.width(), m_grid.height(), NET::DesktopLayoutCornerTopLeft); 0618 } 0619 } 0620 0621 void VirtualDesktopManager::updateLayout() 0622 { 0623 m_rows = std::min(m_rows, count()); 0624 0625 int columns = count() / m_rows; 0626 if (count() % m_rows > 0) { 0627 columns++; 0628 } 0629 0630 m_grid.update(QSize(columns, m_rows), m_desktops); 0631 0632 Q_EMIT layoutChanged(columns, m_rows); 0633 Q_EMIT rowsChanged(m_rows); 0634 } 0635 0636 void VirtualDesktopManager::load() 0637 { 0638 s_loadingDesktopSettings = true; 0639 if (!m_config) { 0640 return; 0641 } 0642 0643 KConfigGroup group(m_config, QStringLiteral("Desktops")); 0644 const int n = group.readEntry("Number", 1); 0645 setCount(n); 0646 0647 for (int i = 1; i <= n; i++) { 0648 QString s = group.readEntry(QStringLiteral("Name_%1").arg(i), i18n("Desktop %1", i)); 0649 if (m_rootInfo) { 0650 m_rootInfo->setDesktopName(i, s.toUtf8().data()); 0651 } 0652 m_desktops[i - 1]->setName(s); 0653 0654 const QString sId = group.readEntry(QStringLiteral("Id_%1").arg(i), QString()); 0655 0656 if (m_desktops[i - 1]->id().isEmpty()) { 0657 m_desktops[i - 1]->setId(sId.isEmpty() ? generateDesktopId() : sId); 0658 } else { 0659 Q_ASSERT(sId.isEmpty() || m_desktops[i - 1]->id() == sId); 0660 } 0661 0662 // TODO: update desktop focus chain, why? 0663 // m_desktopFocusChain.value()[i-1] = i; 0664 } 0665 0666 int rows = group.readEntry<int>("Rows", 2); 0667 m_rows = std::clamp(rows, 1, n); 0668 0669 s_loadingDesktopSettings = false; 0670 } 0671 0672 void VirtualDesktopManager::save() 0673 { 0674 if (s_loadingDesktopSettings) { 0675 return; 0676 } 0677 if (!m_config) { 0678 return; 0679 } 0680 KConfigGroup group(m_config, QStringLiteral("Desktops")); 0681 0682 for (int i = count() + 1; group.hasKey(QStringLiteral("Id_%1").arg(i)); i++) { 0683 group.deleteEntry(QStringLiteral("Id_%1").arg(i)); 0684 group.deleteEntry(QStringLiteral("Name_%1").arg(i)); 0685 } 0686 0687 group.writeEntry("Number", count()); 0688 for (VirtualDesktop *desktop : std::as_const(m_desktops)) { 0689 const uint position = desktop->x11DesktopNumber(); 0690 0691 QString s = desktop->name(); 0692 const QString defaultvalue = defaultName(position); 0693 if (s.isEmpty()) { 0694 s = defaultvalue; 0695 if (m_rootInfo) { 0696 m_rootInfo->setDesktopName(position, s.toUtf8().data()); 0697 } 0698 } 0699 0700 if (s != defaultvalue) { 0701 group.writeEntry(QStringLiteral("Name_%1").arg(position), s); 0702 } else { 0703 QString currentvalue = group.readEntry(QStringLiteral("Name_%1").arg(position), QString()); 0704 if (currentvalue != defaultvalue) { 0705 group.deleteEntry(QStringLiteral("Name_%1").arg(position)); 0706 } 0707 } 0708 group.writeEntry(QStringLiteral("Id_%1").arg(position), desktop->id()); 0709 } 0710 0711 group.writeEntry("Rows", m_rows); 0712 0713 // Save to disk 0714 group.sync(); 0715 } 0716 0717 QString VirtualDesktopManager::defaultName(int desktop) const 0718 { 0719 return i18n("Desktop %1", desktop); 0720 } 0721 0722 void VirtualDesktopManager::initShortcuts() 0723 { 0724 initSwitchToShortcuts(); 0725 0726 addAction(QStringLiteral("Switch to Next Desktop"), i18n("Switch to Next Desktop"), QKeySequence(), &VirtualDesktopManager::slotNext); 0727 addAction(QStringLiteral("Switch to Previous Desktop"), i18n("Switch to Previous Desktop"), QKeySequence(), &VirtualDesktopManager::slotPrevious); 0728 0729 // shortcuts 0730 addAction(QStringLiteral("Switch One Desktop to the Right"), i18n("Switch One Desktop to the Right"), QKeySequence(Qt::CTRL | Qt::META | Qt::Key_Right), &VirtualDesktopManager::slotRight); 0731 addAction(QStringLiteral("Switch One Desktop to the Left"), i18n("Switch One Desktop to the Left"), QKeySequence(Qt::CTRL | Qt::META | Qt::Key_Left), &VirtualDesktopManager::slotLeft); 0732 addAction(QStringLiteral("Switch One Desktop Up"), i18n("Switch One Desktop Up"), QKeySequence(Qt::CTRL | Qt::META | Qt::Key_Up), &VirtualDesktopManager::slotUp); 0733 addAction(QStringLiteral("Switch One Desktop Down"), i18n("Switch One Desktop Down"), QKeySequence(Qt::CTRL | Qt::META | Qt::Key_Down), &VirtualDesktopManager::slotDown); 0734 0735 // Gestures 0736 // These connections decide which desktop to end on after gesture ends 0737 connect(m_swipeGestureReleasedX.get(), &QAction::triggered, this, &VirtualDesktopManager::gestureReleasedX); 0738 connect(m_swipeGestureReleasedY.get(), &QAction::triggered, this, &VirtualDesktopManager::gestureReleasedY); 0739 0740 const auto left = [this](qreal cb) { 0741 if (grid().width() > 1) { 0742 m_currentDesktopOffset.setX(cb); 0743 Q_EMIT currentChanging(currentDesktop(), m_currentDesktopOffset); 0744 } 0745 }; 0746 const auto right = [this](qreal cb) { 0747 if (grid().width() > 1) { 0748 m_currentDesktopOffset.setX(-cb); 0749 Q_EMIT currentChanging(currentDesktop(), m_currentDesktopOffset); 0750 } 0751 }; 0752 input()->registerTouchpadSwipeShortcut(SwipeDirection::Left, 3, m_swipeGestureReleasedX.get(), left); 0753 input()->registerTouchpadSwipeShortcut(SwipeDirection::Right, 3, m_swipeGestureReleasedX.get(), right); 0754 input()->registerTouchpadSwipeShortcut(SwipeDirection::Left, 4, m_swipeGestureReleasedX.get(), left); 0755 input()->registerTouchpadSwipeShortcut(SwipeDirection::Right, 4, m_swipeGestureReleasedX.get(), right); 0756 input()->registerTouchpadSwipeShortcut(SwipeDirection::Down, 3, m_swipeGestureReleasedY.get(), [this](qreal cb) { 0757 if (grid().height() > 1) { 0758 m_currentDesktopOffset.setY(-cb); 0759 Q_EMIT currentChanging(currentDesktop(), m_currentDesktopOffset); 0760 } 0761 }); 0762 input()->registerTouchpadSwipeShortcut(SwipeDirection::Up, 3, m_swipeGestureReleasedY.get(), [this](qreal cb) { 0763 if (grid().height() > 1) { 0764 m_currentDesktopOffset.setY(cb); 0765 Q_EMIT currentChanging(currentDesktop(), m_currentDesktopOffset); 0766 } 0767 }); 0768 input()->registerTouchscreenSwipeShortcut(SwipeDirection::Left, 3, m_swipeGestureReleasedX.get(), left); 0769 input()->registerTouchscreenSwipeShortcut(SwipeDirection::Right, 3, m_swipeGestureReleasedX.get(), right); 0770 0771 // axis events 0772 input()->registerAxisShortcut(Qt::MetaModifier | Qt::AltModifier, PointerAxisDown, 0773 findChild<QAction *>(QStringLiteral("Switch to Next Desktop"))); 0774 input()->registerAxisShortcut(Qt::MetaModifier | Qt::AltModifier, PointerAxisUp, 0775 findChild<QAction *>(QStringLiteral("Switch to Previous Desktop"))); 0776 } 0777 0778 void VirtualDesktopManager::gestureReleasedY() 0779 { 0780 // Note that if desktop wrapping is disabled and there's no desktop above or below, 0781 // above() and below() will return the current desktop. 0782 VirtualDesktop *target = m_current; 0783 if (m_currentDesktopOffset.y() <= -GESTURE_SWITCH_THRESHOLD) { 0784 target = above(m_current, isNavigationWrappingAround()); 0785 } else if (m_currentDesktopOffset.y() >= GESTURE_SWITCH_THRESHOLD) { 0786 target = below(m_current, isNavigationWrappingAround()); 0787 } 0788 0789 // If the current desktop has not changed, consider that the gesture has been canceled. 0790 if (m_current != target) { 0791 setCurrent(target); 0792 } else { 0793 Q_EMIT currentChangingCancelled(); 0794 } 0795 m_currentDesktopOffset = QPointF(0, 0); 0796 } 0797 0798 void VirtualDesktopManager::gestureReleasedX() 0799 { 0800 // Note that if desktop wrapping is disabled and there's no desktop to left or right, 0801 // toLeft() and toRight() will return the current desktop. 0802 VirtualDesktop *target = m_current; 0803 if (m_currentDesktopOffset.x() <= -GESTURE_SWITCH_THRESHOLD) { 0804 target = toLeft(m_current, isNavigationWrappingAround()); 0805 } else if (m_currentDesktopOffset.x() >= GESTURE_SWITCH_THRESHOLD) { 0806 target = toRight(m_current, isNavigationWrappingAround()); 0807 } 0808 0809 // If the current desktop has not changed, consider that the gesture has been canceled. 0810 if (m_current != target) { 0811 setCurrent(target); 0812 } else { 0813 Q_EMIT currentChangingCancelled(); 0814 } 0815 m_currentDesktopOffset = QPointF(0, 0); 0816 } 0817 0818 void VirtualDesktopManager::initSwitchToShortcuts() 0819 { 0820 const QString toDesktop = QStringLiteral("Switch to Desktop %1"); 0821 const KLocalizedString toDesktopLabel = ki18n("Switch to Desktop %1"); 0822 addAction(toDesktop, toDesktopLabel, 1, QKeySequence(Qt::CTRL | Qt::Key_F1), &VirtualDesktopManager::slotSwitchTo); 0823 addAction(toDesktop, toDesktopLabel, 2, QKeySequence(Qt::CTRL | Qt::Key_F2), &VirtualDesktopManager::slotSwitchTo); 0824 addAction(toDesktop, toDesktopLabel, 3, QKeySequence(Qt::CTRL | Qt::Key_F3), &VirtualDesktopManager::slotSwitchTo); 0825 addAction(toDesktop, toDesktopLabel, 4, QKeySequence(Qt::CTRL | Qt::Key_F4), &VirtualDesktopManager::slotSwitchTo); 0826 0827 for (uint i = 5; i <= maximum(); ++i) { 0828 addAction(toDesktop, toDesktopLabel, i, QKeySequence(), &VirtualDesktopManager::slotSwitchTo); 0829 } 0830 } 0831 0832 QAction *VirtualDesktopManager::addAction(const QString &name, const KLocalizedString &label, uint value, const QKeySequence &key, void (VirtualDesktopManager::*slot)()) 0833 { 0834 QAction *a = new QAction(this); 0835 a->setProperty("componentName", QStringLiteral("kwin")); 0836 a->setObjectName(name.arg(value)); 0837 a->setText(label.subs(value).toString()); 0838 a->setData(value); 0839 KGlobalAccel::setGlobalShortcut(a, key); 0840 connect(a, &QAction::triggered, this, slot); 0841 return a; 0842 } 0843 0844 QAction *VirtualDesktopManager::addAction(const QString &name, const QString &label, const QKeySequence &key, void (VirtualDesktopManager::*slot)()) 0845 { 0846 QAction *a = new QAction(this); 0847 a->setProperty("componentName", QStringLiteral("kwin")); 0848 a->setObjectName(name); 0849 a->setText(label); 0850 KGlobalAccel::setGlobalShortcut(a, key); 0851 connect(a, &QAction::triggered, this, slot); 0852 return a; 0853 } 0854 0855 void VirtualDesktopManager::slotSwitchTo() 0856 { 0857 QAction *act = qobject_cast<QAction *>(sender()); 0858 if (!act) { 0859 return; 0860 } 0861 bool ok = false; 0862 const uint i = act->data().toUInt(&ok); 0863 if (!ok) { 0864 return; 0865 } 0866 setCurrent(i); 0867 } 0868 0869 void VirtualDesktopManager::setNavigationWrappingAround(bool enabled) 0870 { 0871 if (enabled == m_navigationWrapsAround) { 0872 return; 0873 } 0874 m_navigationWrapsAround = enabled; 0875 Q_EMIT navigationWrappingAroundChanged(); 0876 } 0877 0878 void VirtualDesktopManager::slotDown() 0879 { 0880 moveTo(Direction::Down, isNavigationWrappingAround()); 0881 } 0882 0883 void VirtualDesktopManager::slotLeft() 0884 { 0885 moveTo(Direction::Left, isNavigationWrappingAround()); 0886 } 0887 0888 void VirtualDesktopManager::slotPrevious() 0889 { 0890 moveTo(Direction::Previous, isNavigationWrappingAround()); 0891 } 0892 0893 void VirtualDesktopManager::slotNext() 0894 { 0895 moveTo(Direction::Next, isNavigationWrappingAround()); 0896 } 0897 0898 void VirtualDesktopManager::slotRight() 0899 { 0900 moveTo(Direction::Right, isNavigationWrappingAround()); 0901 } 0902 0903 void VirtualDesktopManager::slotUp() 0904 { 0905 moveTo(Direction::Up, isNavigationWrappingAround()); 0906 } 0907 0908 } // KWin 0909 0910 #include "moc_virtualdesktops.cpp"