/* SPDX-FileCopyrightText: 2021 Vlad Zahorodnii SPDX-License-Identifier: GPL-2.0-or-later */ #include "overvieweffect.h" #include "effect/effecthandler.h" #include "overviewconfig.h" #include #include #include #include #include #include using namespace std::chrono_literals; namespace KWin { OverviewEffect::OverviewEffect() // manages the transition between inactive -> overview : m_overviewState(new EffectTogglableState(this)) // manages the transition between overview -> grid , m_transitionState(new EffectTogglableState(this)) // manages the transition betwee inactive -> overview , m_gridState(new EffectTogglableState(this)) , m_border(new EffectTogglableTouchBorder(m_overviewState)) , m_gridBorder(new EffectTogglableTouchBorder(m_gridState)) , m_shutdownTimer(new QTimer(this)) { auto gesture = new EffectTogglableGesture(m_overviewState); gesture->addTouchpadSwipeGesture(SwipeDirection::Up, 4); gesture->addTouchscreenSwipeGesture(SwipeDirection::Up, 3); auto transitionGesture = new EffectTogglableGesture(m_transitionState); transitionGesture->addTouchpadSwipeGesture(SwipeDirection::Up, 4); transitionGesture->addTouchscreenSwipeGesture(SwipeDirection::Up, 3); m_transitionState->stop(); auto gridGesture = new EffectTogglableGesture(m_gridState); gridGesture->addTouchpadSwipeGesture(SwipeDirection::Down, 4); gridGesture->addTouchscreenSwipeGesture(SwipeDirection::Down, 3); connect(m_overviewState, &EffectTogglableState::inProgressChanged, this, &OverviewEffect::overviewGestureInProgressChanged); connect(m_overviewState, &EffectTogglableState::partialActivationFactorChanged, this, &OverviewEffect::overviewPartialActivationFactorChanged); connect(m_overviewState, &EffectTogglableState::statusChanged, this, [this](EffectTogglableState::Status status) { if (status == EffectTogglableState::Status::Activating || status == EffectTogglableState::Status::Active) { m_searchText = QString(); setRunning(true); m_gridState->stop(); } if (status == EffectTogglableState::Status::Active) { m_transitionState->deactivate(); } if (status == EffectTogglableState::Status::Inactive || status == EffectTogglableState::Status::Deactivating) { m_transitionState->stop(); } if (status == EffectTogglableState::Status::Inactive) { m_gridState->deactivate(); deactivate(); } }); connect(m_transitionState, &EffectTogglableState::statusChanged, this, [this](EffectTogglableState::Status status) { if (status == EffectTogglableState::Status::Activating || status == EffectTogglableState::Status::Active) { m_overviewState->stop(); } if (status == EffectTogglableState::Status::Inactive) { m_overviewState->activate(); } if (status == EffectTogglableState::Status::Active) { m_gridState->activate(); } if (status == EffectTogglableState::Status::Inactive || status == EffectTogglableState::Status::Deactivating) { m_gridState->stop(); } }); connect(m_gridState, &EffectTogglableState::statusChanged, this, [this](EffectTogglableState::Status status) { if (status == EffectTogglableState::Status::Activating || status == EffectTogglableState::Status::Active) { m_searchText = QString(); setRunning(true); m_overviewState->stop(); } if (status == EffectTogglableState::Status::Inactive) { m_overviewState->deactivate(); deactivate(); } if (status == EffectTogglableState::Status::Active) { m_transitionState->activate(); } if (status == EffectTogglableState::Status::Inactive || status == EffectTogglableState::Status::Deactivating) { m_transitionState->stop(); } }); connect(m_transitionState, &EffectTogglableState::inProgressChanged, this, &OverviewEffect::transitionGestureInProgressChanged); connect(m_transitionState, &EffectTogglableState::partialActivationFactorChanged, this, &OverviewEffect::transitionPartialActivationFactorChanged); connect(m_gridState, &EffectTogglableState::inProgressChanged, this, &OverviewEffect::gridGestureInProgressChanged); connect(m_gridState, &EffectTogglableState::partialActivationFactorChanged, this, &OverviewEffect::gridPartialActivationFactorChanged); connect(effects, &EffectsHandler::desktopChanging, this, [this](VirtualDesktop *old, QPointF desktopOffset, EffectWindow *with) { m_desktopOffset = desktopOffset; Q_EMIT desktopOffsetChanged(); }); connect(effects, &EffectsHandler::desktopChanged, this, [this](VirtualDesktop *old, VirtualDesktop *current, EffectWindow *with) { m_desktopOffset = QPointF(0, 0); Q_EMIT desktopOffsetChanged(); }); connect(effects, &EffectsHandler::desktopChangingCancelled, this, [this]() { m_desktopOffset = QPointF(0, 0); Q_EMIT desktopOffsetChanged(); }); m_shutdownTimer->setSingleShot(true); connect(m_shutdownTimer, &QTimer::timeout, this, &OverviewEffect::realDeactivate); auto cycleAction = new QAction(this); connect(cycleAction, &QAction::triggered, this, &OverviewEffect::cycle); cycleAction->setObjectName(QStringLiteral("Cycle Overview")); cycleAction->setText(i18nc("@action Grid View and Overview are the name of KWin effects", "Cycle through Overview and Grid View")); KGlobalAccel::self()->setDefaultShortcut(cycleAction, {}); KGlobalAccel::self()->setShortcut(cycleAction, {}); m_cycleShortcut = KGlobalAccel::self()->shortcut(cycleAction); auto reverseCycleAction = new QAction(this); connect(reverseCycleAction, &QAction::triggered, this, &OverviewEffect::reverseCycle); reverseCycleAction->setObjectName(QStringLiteral("Cycle Overview Opposite")); reverseCycleAction->setText(i18nc("@action Grid View and Overview are the name of KWin effects", "Cycle through Grid View and Overview")); KGlobalAccel::self()->setDefaultShortcut(reverseCycleAction, {}); KGlobalAccel::self()->setShortcut(reverseCycleAction, {}); m_reverseCycleShortcut = KGlobalAccel::self()->shortcut(reverseCycleAction); const QKeySequence defaultOverviewShortcut = Qt::META | Qt::Key_W; auto overviewAction = m_overviewState->toggleAction(); overviewAction->setObjectName(QStringLiteral("Overview")); overviewAction->setText(i18nc("@action Overview is the name of a Kwin effect", "Toggle Overview")); KGlobalAccel::self()->setDefaultShortcut(overviewAction, {defaultOverviewShortcut}); KGlobalAccel::self()->setShortcut(overviewAction, {defaultOverviewShortcut}); m_overviewShortcut = KGlobalAccel::self()->shortcut(overviewAction); const QKeySequence defaultGridShortcut = Qt::META | Qt::Key_G; auto gridAction = m_gridState->toggleAction(); gridAction->setObjectName(QStringLiteral("Grid View")); gridAction->setText(i18nc("@action Grid view is the name of a Kwin effect", "Toggle Grid View")); KGlobalAccel::self()->setDefaultShortcut(gridAction, {defaultGridShortcut}); KGlobalAccel::self()->setShortcut(gridAction, {defaultGridShortcut}); m_gridShortcut = KGlobalAccel::self()->shortcut(gridAction); connect(effects, &EffectsHandler::screenAboutToLock, this, &OverviewEffect::realDeactivate); OverviewConfig::instance(effects->config()); reconfigure(ReconfigureAll); auto delegate = new QQmlComponent(effects->qmlEngine(), this); connect(delegate, &QQmlComponent::statusChanged, this, [delegate]() { if (delegate->isError()) { qWarning() << "Failed to load overview:" << delegate->errorString(); } }); delegate->loadUrl(QUrl(QStringLiteral("qrc:/overview/qml/main.qml")), QQmlComponent::Asynchronous); setDelegate(delegate); } OverviewEffect::~OverviewEffect() { } void OverviewEffect::reconfigure(ReconfigureFlags) { OverviewConfig::self()->read(); setAnimationDuration(animationTime(300ms)); setFilterWindows(OverviewConfig::filterWindows()); for (const ElectricBorder &border : std::as_const(m_borderActivate)) { effects->unreserveElectricBorder(border, this); } for (const ElectricBorder &border : std::as_const(m_gridBorderActivate)) { effects->unreserveElectricBorder(border, this); } m_borderActivate.clear(); m_gridBorderActivate.clear(); const QList activateBorders = OverviewConfig::borderActivate(); for (const int &border : activateBorders) { m_borderActivate.append(ElectricBorder(border)); effects->reserveElectricBorder(ElectricBorder(border), this); } const QList gridActivateBorders = OverviewConfig::gridBorderActivate(); for (const int &border : gridActivateBorders) { m_gridBorderActivate.append(ElectricBorder(border)); effects->reserveElectricBorder(ElectricBorder(border), this); } m_border->setBorders(OverviewConfig::touchBorderActivate()); m_gridBorder->setBorders(OverviewConfig::gridTouchBorderActivate()); } int OverviewEffect::animationDuration() const { return m_animationDuration; } void OverviewEffect::setAnimationDuration(int duration) { if (m_animationDuration != duration) { m_animationDuration = duration; Q_EMIT animationDurationChanged(); } } bool OverviewEffect::filterWindows() const { return m_filterWindows; } void OverviewEffect::setFilterWindows(bool filterWindows) { if (m_filterWindows != filterWindows) { m_filterWindows = filterWindows; Q_EMIT filterWindowsChanged(); } } qreal OverviewEffect::overviewPartialActivationFactor() const { return m_overviewState->partialActivationFactor(); } bool OverviewEffect::overviewGestureInProgress() const { return m_overviewState->inProgress(); } qreal OverviewEffect::transitionPartialActivationFactor() const { return m_transitionState->partialActivationFactor(); } bool OverviewEffect::transitionGestureInProgress() const { return m_transitionState->inProgress(); } qreal OverviewEffect::gridPartialActivationFactor() const { return m_gridState->partialActivationFactor(); } bool OverviewEffect::gridGestureInProgress() const { return m_gridState->inProgress(); } QPointF OverviewEffect::desktopOffset() const { return m_desktopOffset; } bool OverviewEffect::ignoreMinimized() const { return OverviewConfig::ignoreMinimized(); } bool OverviewEffect::organizedGrid() const { return OverviewConfig::organizedGrid(); } int OverviewEffect::requestedEffectChainPosition() const { return 70; } bool OverviewEffect::borderActivated(ElectricBorder border) { if (m_borderActivate.contains(border)) { m_overviewState->toggle(); return true; } else if (m_gridBorderActivate.contains(border)) { m_gridState->toggle(); return true; } return false; } void OverviewEffect::activate() { if (effects->isScreenLocked()) { return; } m_overviewState->activate(); } void OverviewEffect::deactivate() { const auto screens = effects->screens(); m_shutdownTimer->start(animationDuration()); m_overviewState->deactivate(); } void OverviewEffect::realDeactivate() { if (m_overviewState->status() == EffectTogglableState::Status::Inactive) { setRunning(false); } } void OverviewEffect::cycle() { if (m_overviewState->status() == EffectTogglableState::Status::Inactive) { m_overviewState->activate(); } else if (m_transitionState->status() == EffectTogglableState::Status::Inactive) { m_transitionState->activate(); } else if (m_gridState->status() == EffectTogglableState::Status::Active) { m_overviewState->deactivate(); } } void OverviewEffect::reverseCycle() { if (m_overviewState->status() == EffectTogglableState::Status::Active) { m_overviewState->deactivate(); } else if (m_transitionState->status() == EffectTogglableState::Status::Active) { m_transitionState->deactivate(); } else if (m_gridState->status() == EffectTogglableState::Status::Inactive) { m_gridState->activate(); } } void OverviewEffect::grabbedKeyboardEvent(QKeyEvent *keyEvent) { if (!effects->waylandDisplay()) { if (m_cycleShortcut.contains(keyEvent->key() | keyEvent->modifiers())) { if (keyEvent->type() == QEvent::KeyPress) { cycle(); } return; } if (m_reverseCycleShortcut.contains(keyEvent->key() | keyEvent->modifiers())) { if (keyEvent->type() == QEvent::KeyPress) { reverseCycle(); } return; } if (m_overviewShortcut.contains(keyEvent->key() | keyEvent->modifiers())) { if (keyEvent->type() == QEvent::KeyPress) { m_overviewState->toggle(); } return; } if (m_gridShortcut.contains(keyEvent->key() | keyEvent->modifiers())) { if (keyEvent->type() == QEvent::KeyPress) { m_gridState->toggle(); } return; } } QuickSceneEffect::grabbedKeyboardEvent(keyEvent); } void OverviewEffect::swapDesktops(VirtualDesktop *from, VirtualDesktop *to) { QList fromList; QList toList; for (auto *w : effects->stackingOrder()) { if (!w->isNormalWindow() || !w->isOnCurrentActivity()) { continue; } if (w->isOnDesktop(from)) { fromList << w; } else if (w->isOnDesktop(to)) { toList << w; } } for (auto *w : fromList) { effects->windowToDesktops(w, {to}); } for (auto *w : toList) { effects->windowToDesktops(w, {from}); } } } // namespace KWin #include "moc_overvieweffect.cpp"