overview: Split out the state managing code into a separate class

This way it can be used by other effects eventually.
This commit is contained in:
Aleix Pol 2023-05-07 21:35:08 +02:00 committed by Aleix Pol Gonzalez
parent 4c3d3053ab
commit bb8e5458a7
2 changed files with 343 additions and 162 deletions

View file

@ -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<int> &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<int> activateBorders = OverviewConfig::borderActivate();
for (const int &border : activateBorders) {
@ -116,21 +302,7 @@ void OverviewEffect::reconfigure(ReconfigureFlags)
effects->reserveElectricBorder(ElectricBorder(border), this);
}
const QList<int> 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;
}

View file

@ -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<int> &borders);
private:
QList<ElectricBorder> 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<QKeySequence> m_toggleShortcut;
QList<ElectricBorder> m_borderActivate;
QList<ElectricBorder> 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