File indexing completed on 2024-11-10 04:56:44

0001 /*
0002     SPDX-FileCopyrightText: 2023 Aleix Pol Gonzalez <aleixpol@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "effect/effecttogglablestate.h"
0008 #include "effect/effecthandler.h"
0009 
0010 namespace KWin
0011 {
0012 
0013 EffectTogglableState::EffectTogglableState(Effect *effect)
0014     : QObject(effect)
0015     , m_deactivateAction(std::make_unique<QAction>())
0016     , m_activateAction(std::make_unique<QAction>())
0017     , m_toggleAction(std::make_unique<QAction>())
0018 {
0019     connect(m_activateAction.get(), &QAction::triggered, this, [this]() {
0020         if (m_status == Status::Activating) {
0021             if (m_partialActivationFactor > 0.5) {
0022                 activate();
0023                 Q_EMIT activated();
0024             } else {
0025                 deactivate();
0026                 Q_EMIT deactivated();
0027             }
0028         }
0029     });
0030     connect(m_deactivateAction.get(), &QAction::triggered, this, [this]() {
0031         if (m_status == Status::Deactivating) {
0032             if (m_partialActivationFactor < 0.5) {
0033                 deactivate();
0034                 Q_EMIT deactivated();
0035             } else {
0036                 activate();
0037                 Q_EMIT activated();
0038             }
0039         }
0040     });
0041     connect(m_toggleAction.get(), &QAction::triggered, this, &EffectTogglableState::toggle);
0042 }
0043 
0044 void EffectTogglableState::activate()
0045 {
0046     setInProgress(false);
0047     setPartialActivationFactor(1.0);
0048     setStatus(Status::Active);
0049 }
0050 
0051 void EffectTogglableState::setPartialActivationFactor(qreal factor)
0052 {
0053     if (m_partialActivationFactor != factor) {
0054         m_partialActivationFactor = factor;
0055         Q_EMIT partialActivationFactorChanged();
0056     }
0057 }
0058 
0059 void EffectTogglableState::deactivate()
0060 {
0061     setInProgress(false);
0062     setPartialActivationFactor(0.0);
0063     setStatus(Status::Inactive);
0064 }
0065 
0066 void EffectTogglableState::stop()
0067 {
0068     setInProgress(false);
0069     setPartialActivationFactor(0.0);
0070     setStatus(Status::Stopped);
0071 }
0072 
0073 bool EffectTogglableState::inProgress() const
0074 {
0075     return m_inProgress;
0076 }
0077 
0078 void EffectTogglableState::setInProgress(bool gesture)
0079 {
0080     if (m_inProgress != gesture) {
0081         m_inProgress = gesture;
0082         Q_EMIT inProgressChanged();
0083     }
0084 }
0085 
0086 void EffectTogglableState::setStatus(Status status)
0087 {
0088     if (m_status != status) {
0089         m_status = status;
0090         Q_EMIT statusChanged(status);
0091     }
0092 }
0093 
0094 void EffectTogglableState::partialActivate(qreal factor)
0095 {
0096     if (effects->isScreenLocked()) {
0097         return;
0098     }
0099 
0100     setStatus(Status::Activating);
0101     setInProgress(true);
0102     setPartialActivationFactor(factor);
0103 }
0104 
0105 void EffectTogglableState::partialDeactivate(qreal factor)
0106 {
0107     setStatus(Status::Deactivating);
0108     setInProgress(true);
0109     setPartialActivationFactor(1.0 - factor);
0110 }
0111 
0112 void EffectTogglableState::toggle()
0113 {
0114     if (m_status == Status::Inactive) {
0115         activate();
0116         Q_EMIT activated();
0117     } else {
0118         deactivate();
0119         Q_EMIT deactivated();
0120     }
0121 }
0122 
0123 void EffectTogglableState::setProgress(qreal progress)
0124 {
0125     if (m_status == Status::Stopped) {
0126         return;
0127     }
0128     if (!effects->hasActiveFullScreenEffect() || effects->activeFullScreenEffect() == parent()) {
0129         switch (m_status) {
0130         case Status::Inactive:
0131         case Status::Activating:
0132             partialActivate(progress);
0133             break;
0134         default:
0135             break;
0136         }
0137     }
0138 }
0139 
0140 void EffectTogglableState::setRegress(qreal regress)
0141 {
0142     if (m_status == Status::Stopped) {
0143         return;
0144     }
0145     if (!effects->hasActiveFullScreenEffect() || effects->activeFullScreenEffect() == parent()) {
0146         switch (m_status) {
0147         case Status::Active:
0148         case Status::Deactivating:
0149             partialDeactivate(regress);
0150             break;
0151         default:
0152             break;
0153         }
0154     }
0155 }
0156 
0157 EffectTogglableGesture::EffectTogglableGesture(EffectTogglableState *state)
0158     : QObject(state)
0159     , m_state(state)
0160 {
0161 }
0162 
0163 static PinchDirection opposite(PinchDirection direction)
0164 {
0165     switch (direction) {
0166     case PinchDirection::Contracting:
0167         return PinchDirection::Expanding;
0168     case PinchDirection::Expanding:
0169         return PinchDirection::Contracting;
0170     }
0171     return PinchDirection::Expanding;
0172 }
0173 
0174 static SwipeDirection opposite(SwipeDirection direction)
0175 {
0176     switch (direction) {
0177     case SwipeDirection::Invalid:
0178         return SwipeDirection::Invalid;
0179     case SwipeDirection::Down:
0180         return SwipeDirection::Up;
0181     case SwipeDirection::Up:
0182         return SwipeDirection::Down;
0183     case SwipeDirection::Left:
0184         return SwipeDirection::Right;
0185     case SwipeDirection::Right:
0186         return SwipeDirection::Left;
0187     }
0188     return SwipeDirection::Invalid;
0189 }
0190 
0191 std::function<void(qreal progress)> EffectTogglableState::progressCallback()
0192 {
0193     return [this](qreal progress) {
0194         setProgress(progress);
0195     };
0196 }
0197 
0198 std::function<void(qreal progress)> EffectTogglableState::regressCallback()
0199 {
0200     return [this](qreal progress) {
0201         setRegress(progress);
0202     };
0203 }
0204 
0205 void EffectTogglableGesture::addTouchpadPinchGesture(PinchDirection direction, uint fingerCount)
0206 {
0207     effects->registerTouchpadPinchShortcut(direction, fingerCount, m_state->activateAction(), m_state->progressCallback());
0208     effects->registerTouchpadPinchShortcut(opposite(direction), fingerCount, m_state->deactivateAction(), m_state->regressCallback());
0209 }
0210 
0211 void EffectTogglableGesture::addTouchpadSwipeGesture(SwipeDirection direction, uint fingerCount)
0212 {
0213     effects->registerTouchpadSwipeShortcut(direction, fingerCount, m_state->activateAction(), m_state->progressCallback());
0214     effects->registerTouchpadSwipeShortcut(opposite(direction), fingerCount, m_state->deactivateAction(), m_state->regressCallback());
0215 }
0216 
0217 void EffectTogglableGesture::addTouchscreenSwipeGesture(SwipeDirection direction, uint fingerCount)
0218 {
0219     effects->registerTouchscreenSwipeShortcut(direction, fingerCount, m_state->activateAction(), m_state->progressCallback());
0220     effects->registerTouchscreenSwipeShortcut(opposite(direction), fingerCount, m_state->deactivateAction(), m_state->regressCallback());
0221 }
0222 
0223 EffectTogglableTouchBorder::EffectTogglableTouchBorder(EffectTogglableState *state)
0224     : QObject(state)
0225     , m_state(state)
0226 {
0227 }
0228 
0229 EffectTogglableTouchBorder::~EffectTogglableTouchBorder()
0230 {
0231     for (const ElectricBorder &border : std::as_const(m_touchBorderActivate)) {
0232         effects->unregisterTouchBorder(border, m_state->activateAction());
0233     }
0234 }
0235 
0236 void EffectTogglableTouchBorder::setBorders(const QList<int> &touchActivateBorders)
0237 {
0238     for (const ElectricBorder &border : std::as_const(m_touchBorderActivate)) {
0239         effects->unregisterTouchBorder(border, m_state->activateAction());
0240     }
0241     m_touchBorderActivate.clear();
0242 
0243     for (const int &border : touchActivateBorders) {
0244         m_touchBorderActivate.append(ElectricBorder(border));
0245         effects->registerRealtimeTouchBorder(ElectricBorder(border), m_state->activateAction(), [this](ElectricBorder border, const QPointF &deltaProgress, const Output *screen) {
0246             if (m_state->status() == EffectTogglableState::Status::Active) {
0247                 return;
0248             }
0249             const int maxDelta = 500; // Arbitrary logical pixels value seems to behave better than scaledScreenSize
0250             qreal progress = 0;
0251             if (border == ElectricTop || border == ElectricBottom) {
0252                 progress = std::min(1.0, std::abs(deltaProgress.y()) / maxDelta);
0253             } else {
0254                 progress = std::min(1.0, std::abs(deltaProgress.x()) / maxDelta);
0255             }
0256             m_state->setProgress(progress);
0257         });
0258     }
0259 }
0260 
0261 }
0262 
0263 #include "moc_effecttogglablestate.cpp"