diff --git a/src/plugins/overview/overvieweffect.cpp b/src/plugins/overview/overvieweffect.cpp index 65e07f3045..0117288425 100644 --- a/src/plugins/overview/overvieweffect.cpp +++ b/src/plugins/overview/overvieweffect.cpp @@ -18,28 +18,18 @@ namespace KWin { -OverviewEffect::OverviewEffect() - : m_shutdownTimer(new QTimer(this)) +TogglableState::TogglableState(QObject *effect) + : QObject(effect) { - m_shutdownTimer->setSingleShot(true); - connect(m_shutdownTimer, &QTimer::timeout, this, &OverviewEffect::realDeactivate); - - const QKeySequence defaultToggleShortcut = Qt::META | Qt::Key_W; - m_toggleAction = new QAction(this); - connect(m_toggleAction, &QAction::triggered, this, &OverviewEffect::toggle); - m_toggleAction->setObjectName(QStringLiteral("Overview")); - m_toggleAction->setText(i18n("Toggle Overview")); - KGlobalAccel::self()->setDefaultShortcut(m_toggleAction, {defaultToggleShortcut}); - KGlobalAccel::self()->setShortcut(m_toggleAction, {defaultToggleShortcut}); - m_toggleShortcut = KGlobalAccel::self()->shortcut(m_toggleAction); - m_activateAction = new QAction(this); connect(m_activateAction, &QAction::triggered, this, [this]() { if (m_status == Status::Activating) { if (m_partialActivationFactor > 0.5) { activate(); + Q_EMIT activated(); } else { - cancelPartialActivate(); + deactivate(); + Q_EMIT deactivated(); } } }); @@ -48,38 +38,239 @@ OverviewEffect::OverviewEffect() if (m_status == Status::Deactivating) { if (m_partialActivationFactor < 0.5) { deactivate(); + Q_EMIT deactivated(); } else { - cancelPartialDeactivate(); + activate(); + Q_EMIT activated(); } } }); + m_toggleAction = new QAction(this); + connect(m_toggleAction, &QAction::triggered, this, &TogglableState::toggle); +} +void TogglableState::activate() +{ + setStatus(Status::Active); + setInProgress(false); + setPartialActivationFactor(0.0); +} + +void TogglableState::setPartialActivationFactor(qreal factor) +{ + if (m_partialActivationFactor != factor) { + m_partialActivationFactor = factor; + Q_EMIT partialActivationFactorChanged(); + } +} + +void TogglableState::deactivate() +{ + setInProgress(false); + setPartialActivationFactor(0.0); +} + +bool TogglableState::inProgress() const +{ + return m_inProgress; +} + +void TogglableState::setInProgress(bool gesture) +{ + if (m_inProgress != gesture) { + m_inProgress = gesture; + Q_EMIT inProgressChanged(); + } +} + +void TogglableState::setStatus(Status status) +{ + if (m_status != status) { + m_status = status; + Q_EMIT statusChanged(status); + } +} + +void TogglableState::partialActivate(qreal factor) +{ + if (effects->isScreenLocked()) { + return; + } + + setStatus(Status::Activating); + setPartialActivationFactor(factor); + setInProgress(true); +} + +void TogglableState::partialDeactivate(qreal factor) +{ + setStatus(Status::Deactivating); + setPartialActivationFactor(1.0 - factor); + setInProgress(true); +} + +void TogglableState::toggle() +{ + if (m_status == Status::Inactive || m_partialActivationFactor > 0.5) { + activate(); + Q_EMIT activated(); + } else { + deactivate(); + Q_EMIT deactivated(); + } +} + +void TogglableState::setProgress(qreal progress) +{ + if (!effects->hasActiveFullScreenEffect() || effects->activeFullScreenEffect() == parent()) { + switch (m_status) { + case Status::Inactive: + case Status::Activating: + partialActivate(progress); + break; + } + } +} + +void TogglableState::setRegress(qreal regress) +{ + if (!effects->hasActiveFullScreenEffect() || effects->activeFullScreenEffect() == parent()) { + switch (m_status) { + case Status::Active: + case Status::Deactivating: + partialDeactivate(regress); + break; + } + } +} + +TogglableGesture::TogglableGesture(TogglableState *state) + : QObject(state) + , m_state(state) +{ +} + +static PinchDirection opposite(PinchDirection direction) +{ + switch (direction) { + case PinchDirection::Contracting: + return PinchDirection::Expanding; + case PinchDirection::Expanding: + return PinchDirection::Contracting; + } +} + +void TogglableGesture::addTouchpadGesture(PinchDirection direction, uint fingerCount) +{ auto progressCallback = [this](qreal progress) { - if (!effects->hasActiveFullScreenEffect() || effects->activeFullScreenEffect() == this) { - switch (m_status) { - case Status::Inactive: - case Status::Activating: - partialActivate(progress); - break; - } - } + m_state->setProgress(progress); }; - auto progressCallbackInv = [this](qreal progress) { - if (!effects->hasActiveFullScreenEffect() || effects->activeFullScreenEffect() == this) { - switch (m_status) { - case Status::Active: - case Status::Deactivating: - partialDeactivate(progress); - break; - } - } + m_state->setRegress(progress); }; - effects->registerTouchpadPinchShortcut(PinchDirection::Contracting, 4, m_activateAction, progressCallback); - effects->registerTouchpadPinchShortcut(PinchDirection::Expanding, 4, m_deactivateAction, progressCallbackInv); - effects->registerTouchscreenSwipeShortcut(SwipeDirection::Up, 3, m_activateAction, progressCallback); - effects->registerTouchscreenSwipeShortcut(SwipeDirection::Down, 3, m_deactivateAction, progressCallbackInv); + effects->registerTouchpadPinchShortcut(direction, 4, m_state->activateAction(), progressCallback); + effects->registerTouchpadPinchShortcut(opposite(direction), 4, m_state->deactivateAction(), progressCallbackInv); +} + +static SwipeDirection opposite(SwipeDirection direction) +{ + switch (direction) { + case SwipeDirection::Invalid: + return SwipeDirection::Invalid; + case SwipeDirection::Down: + return SwipeDirection::Up; + case SwipeDirection::Up: + return SwipeDirection::Down; + case SwipeDirection::Left: + return SwipeDirection::Right; + case SwipeDirection::Right: + return SwipeDirection::Left; + } +} + +void TogglableGesture::addTouchscreenSwipeGesture(SwipeDirection direction, uint fingerCount) +{ + auto progressCallback = [this](qreal progress) { + m_state->setProgress(progress); + }; + auto progressCallbackInv = [this](qreal progress) { + m_state->setRegress(progress); + }; + + effects->registerTouchscreenSwipeShortcut(direction, 3, m_state->activateAction(), progressCallback); + effects->registerTouchscreenSwipeShortcut(opposite(direction), 3, m_state->deactivateAction(), progressCallbackInv); +} + +TogglableTouchBorder::TogglableTouchBorder(TogglableState *state) + : QObject(state) + , m_state(state) +{ +} + +TogglableTouchBorder::~TogglableTouchBorder() +{ + for (const ElectricBorder &border : std::as_const(m_touchBorderActivate)) { + effects->unregisterTouchBorder(border, m_state->toggleAction()); + } +} + +void TogglableTouchBorder::setBorders(const QList &touchActivateBorders) +{ + for (const ElectricBorder &border : std::as_const(m_touchBorderActivate)) { + effects->unregisterTouchBorder(border, m_state->toggleAction()); + } + m_touchBorderActivate.clear(); + + for (const int &border : touchActivateBorders) { + m_touchBorderActivate.append(ElectricBorder(border)); + effects->registerRealtimeTouchBorder(ElectricBorder(border), m_state->toggleAction(), [this](ElectricBorder border, const QPointF &deltaProgress, const EffectScreen *screen) { + if (m_state->status() == TogglableState::Status::Active) { + return; + } + const int maxDelta = 500; // Arbitrary logical pixels value seems to behave better than scaledScreenSize + qreal progress = 0; + if (border == ElectricTop || border == ElectricBottom) { + progress = std::min(1.0, std::abs(deltaProgress.y()) / maxDelta); + } else { + progress = std::min(1.0, std::abs(deltaProgress.x()) / maxDelta); + } + m_state->setProgress(progress); + }); + } +} + +OverviewEffect::OverviewEffect() + : m_state(new TogglableState(this)) + , m_border(new TogglableTouchBorder(m_state)) + , m_shutdownTimer(new QTimer(this)) +{ + auto gesture = new TogglableGesture(m_state); + gesture->addTouchpadGesture(PinchDirection::Contracting, 4); + gesture->addTouchscreenSwipeGesture(SwipeDirection::Up, 3); + + connect(m_state, &TogglableState::activated, this, &OverviewEffect::activate); + connect(m_state, &TogglableState::deactivated, this, &OverviewEffect::deactivate); + connect(m_state, &TogglableState::inProgressChanged, this, &OverviewEffect::gestureInProgressChanged); + connect(m_state, &TogglableState::partialActivationFactorChanged, this, &OverviewEffect::partialActivationFactorChanged); + connect(m_state, &TogglableState::statusChanged, this, [this](TogglableState::Status status) { + if (status == TogglableState::Status::Activating) { + m_searchText = QString(); + } + setRunning(status != TogglableState::Status::Inactive); + }); + + m_shutdownTimer->setSingleShot(true); + connect(m_shutdownTimer, &QTimer::timeout, this, &OverviewEffect::realDeactivate); + + const QKeySequence defaultToggleShortcut = Qt::META | Qt::Key_W; + auto toggleAction = m_state->toggleAction(); + connect(toggleAction, &QAction::triggered, m_state, &TogglableState::toggle); + toggleAction->setObjectName(QStringLiteral("Overview")); + toggleAction->setText(i18n("Toggle Overview")); + KGlobalAccel::self()->setDefaultShortcut(toggleAction, {defaultToggleShortcut}); + KGlobalAccel::self()->setShortcut(toggleAction, {defaultToggleShortcut}); + m_toggleShortcut = KGlobalAccel::self()->shortcut(toggleAction); connect(effects, &EffectsHandler::screenAboutToLock, this, &OverviewEffect::realDeactivate); @@ -103,12 +294,7 @@ void OverviewEffect::reconfigure(ReconfigureFlags) effects->unreserveElectricBorder(border, this); } - for (const ElectricBorder &border : std::as_const(m_touchBorderActivate)) { - effects->unregisterTouchBorder(border, m_toggleAction); - } - m_borderActivate.clear(); - m_touchBorderActivate.clear(); const QList activateBorders = OverviewConfig::borderActivate(); for (const int &border : activateBorders) { @@ -116,21 +302,7 @@ void OverviewEffect::reconfigure(ReconfigureFlags) effects->reserveElectricBorder(ElectricBorder(border), this); } - const QList touchActivateBorders = OverviewConfig::touchBorderActivate(); - for (const int &border : touchActivateBorders) { - m_touchBorderActivate.append(ElectricBorder(border)); - effects->registerRealtimeTouchBorder(ElectricBorder(border), m_deactivateAction, [this](ElectricBorder border, const QPointF &deltaProgress, const EffectScreen *screen) { - if (m_status == Status::Active) { - return; - } - const int maxDelta = 500; // Arbitrary logical pixels value seems to behave better than scaledScreenSize - if (border == ElectricTop || border == ElectricBottom) { - partialActivate(std::min(1.0, std::abs(deltaProgress.y()) / maxDelta)); - } else { - partialActivate(std::min(1.0, std::abs(deltaProgress.x()) / maxDelta)); - } - }); - } + m_border->setBorders(OverviewConfig::touchBorderActivate()); } int OverviewEffect::animationDuration() const @@ -164,32 +336,6 @@ void OverviewEffect::setLayout(int layout) } } -qreal OverviewEffect::partialActivationFactor() const -{ - return m_partialActivationFactor; -} - -void OverviewEffect::setPartialActivationFactor(qreal factor) -{ - if (m_partialActivationFactor != factor) { - m_partialActivationFactor = factor; - Q_EMIT partialActivationFactorChanged(); - } -} - -bool OverviewEffect::gestureInProgress() const -{ - return m_gestureInProgress; -} - -void OverviewEffect::setGestureInProgress(bool gesture) -{ - if (m_gestureInProgress != gesture) { - m_gestureInProgress = gesture; - Q_EMIT gestureInProgressChanged(); - } -} - int OverviewEffect::requestedEffectChainPosition() const { return 70; @@ -198,56 +344,19 @@ int OverviewEffect::requestedEffectChainPosition() const bool OverviewEffect::borderActivated(ElectricBorder border) { if (m_borderActivate.contains(border)) { - toggle(); + m_state->toggle(); return true; } return false; } -void OverviewEffect::toggle() -{ - if (!isRunning() || m_partialActivationFactor > 0.5) { - activate(); - } else { - deactivate(); - } -} - void OverviewEffect::activate() { if (effects->isScreenLocked()) { return; } - m_status = Status::Active; - - setGestureInProgress(false); - setPartialActivationFactor(0.0); - - // This one should be the last. - m_searchText = QString(); - setRunning(true); -} - -void OverviewEffect::partialActivate(qreal factor) -{ - if (effects->isScreenLocked()) { - return; - } - - m_status = Status::Activating; - - setPartialActivationFactor(factor); - setGestureInProgress(true); - - // This one should be the last. - m_searchText = QString(); - setRunning(true); -} - -void OverviewEffect::cancelPartialActivate() -{ - deactivate(); + m_state->activate(); } void OverviewEffect::deactivate() @@ -260,34 +369,19 @@ void OverviewEffect::deactivate() } m_shutdownTimer->start(animationDuration()); - setGestureInProgress(false); - setPartialActivationFactor(0.0); -} - -void OverviewEffect::partialDeactivate(qreal factor) -{ - m_status = Status::Deactivating; - - setPartialActivationFactor(1.0 - factor); - setGestureInProgress(true); -} - -void OverviewEffect::cancelPartialDeactivate() -{ - activate(); + m_state->deactivate(); } void OverviewEffect::realDeactivate() { - setRunning(false); - m_status = Status::Inactive; + m_state->setStatus(TogglableState::Status::Inactive); } void OverviewEffect::grabbedKeyboardEvent(QKeyEvent *keyEvent) { if (m_toggleShortcut.contains(keyEvent->key() | keyEvent->modifiers())) { if (keyEvent->type() == QEvent::KeyPress) { - toggle(); + m_state->toggle(); } return; } diff --git a/src/plugins/overview/overvieweffect.h b/src/plugins/overview/overvieweffect.h index eb0b8515ef..8d66de158a 100644 --- a/src/plugins/overview/overvieweffect.h +++ b/src/plugins/overview/overvieweffect.h @@ -11,6 +11,104 @@ namespace KWin { +class TogglableState : public QObject +{ + Q_OBJECT +public: + enum class Status { + Inactive, + Activating, + Deactivating, + Active + }; + Q_ENUM(Status); + + TogglableState(QObject *parent); + + bool inProgress() const; + void setInProgress(bool gesture); + + qreal partialActivationFactor() const + { + return m_partialActivationFactor; + } + void setPartialActivationFactor(qreal factor); + + QAction *activateAction() const + { + return m_activateAction; + } + QAction *deactivateAction() const + { + return m_deactivateAction; + } + QAction *toggleAction() const + { + return m_toggleAction; + } + + void activate(); + void deactivate(); + void toggle(); + void setStatus(Status status); + Status status() const + { + return m_status; + } + +Q_SIGNALS: + void inProgressChanged(); + void partialActivationFactorChanged(); + void activated(); + void deactivated(); + void statusChanged(Status status); + +protected: + void setProgress(qreal progress); + + /// regress being the progress when on an active state + void setRegress(qreal regress); + +private: + void partialActivate(qreal factor); + void partialDeactivate(qreal factor); + + QAction *m_deactivateAction = nullptr; + QAction *m_activateAction = nullptr; + QAction *m_toggleAction = nullptr; + Status m_status = Status::Inactive; + bool m_inProgress = false; + qreal m_partialActivationFactor = 0; + + friend class TogglableGesture; + friend class TogglableTouchBorder; +}; + +class TogglableGesture : public QObject +{ +public: + TogglableGesture(TogglableState *state); + + void addTouchpadGesture(PinchDirection dir, uint fingerCount); + void addTouchscreenSwipeGesture(SwipeDirection direction, uint fingerCount); + +private: + TogglableState *const m_state; +}; + +class TogglableTouchBorder : public QObject +{ +public: + TogglableTouchBorder(TogglableState *state); + ~TogglableTouchBorder(); + + void setBorders(const QList &borders); + +private: + QList m_touchBorderActivate; + TogglableState *const m_state; +}; + class OverviewEffect : public QuickSceneEffect { Q_OBJECT @@ -23,12 +121,6 @@ class OverviewEffect : public QuickSceneEffect Q_PROPERTY(QString searchText MEMBER m_searchText NOTIFY searchTextChanged) public: - enum class Status { - Inactive, - Activating, - Deactivating, - Active - }; OverviewEffect(); ~OverviewEffect() override; @@ -40,11 +132,15 @@ public: int animationDuration() const; void setAnimationDuration(int duration); - qreal partialActivationFactor() const; - void setPartialActivationFactor(qreal factor); + qreal partialActivationFactor() const + { + return m_state->partialActivationFactor(); + } - bool gestureInProgress() const; - void setGestureInProgress(bool gesture); + bool gestureInProgress() const + { + return m_state->inProgress(); + } int requestedEffectChainPosition() const override; bool borderActivated(ElectricBorder border) override; @@ -61,29 +157,20 @@ Q_SIGNALS: public Q_SLOTS: void activate(); - void partialActivate(qreal factor); - void cancelPartialActivate(); - void partialDeactivate(qreal factor); - void cancelPartialDeactivate(); void deactivate(); - void toggle(); private: void realDeactivate(); + TogglableState *const m_state; + TogglableTouchBorder *const m_border; + QTimer *m_shutdownTimer; - QAction *m_toggleAction = nullptr; - QAction *m_deactivateAction = nullptr; - QAction *m_activateAction = nullptr; QList m_toggleShortcut; QList m_borderActivate; - QList m_touchBorderActivate; QString m_searchText; - Status m_status = Status::Inactive; - qreal m_partialActivationFactor = 0; int m_animationDuration = 400; int m_layout = 1; - bool m_gestureInProgress = false; }; } // namespace KWin