add double tap to switch between 2 most recent apps in swicher

also simplified openApp signature and fixed a typo in code
FEATURE: 486555
This commit is contained in:
Luis Büchi 2024-10-27 16:37:14 +01:00
parent d7ae1917af
commit 16192c5f71
5 changed files with 50 additions and 12 deletions

View file

@ -1,6 +1,6 @@
// SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org> // SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
// SPDX-FileCopyrightText: 2023 Devin Lin <devin@kde.org> // SPDX-FileCopyrightText: 2023 Devin Lin <devin@kde.org>
// SPDX-FileCopyrightText: 2024 Luis Büchi <luis.buechi@server23.cc> // SPDX-FileCopyrightText: 2024 Luis Büchi <luis.buechi@kdemail.net>
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "mobiletaskswitchereffect.h" #include "mobiletaskswitchereffect.h"
@ -20,6 +20,7 @@ namespace KWin
MobileTaskSwitcherState::MobileTaskSwitcherState(EffectTouchBorderState *effectState) MobileTaskSwitcherState::MobileTaskSwitcherState(EffectTouchBorderState *effectState)
: m_effectState{effectState} : m_effectState{effectState}
, m_doubleClickTimer{new QElapsedTimer{}}
{ {
connect(m_effectState, &EffectTouchBorderState::inProgressChanged, this, &MobileTaskSwitcherState::gestureInProgressChanged); connect(m_effectState, &EffectTouchBorderState::inProgressChanged, this, &MobileTaskSwitcherState::gestureInProgressChanged);
} }
@ -150,6 +151,11 @@ void MobileTaskSwitcherState::setInitialTaskIndex(int newTaskIndex)
} }
} }
void MobileTaskSwitcherState::restartDoubleClickTimer()
{
m_doubleClickTimer->restart();
}
void MobileTaskSwitcherState::calculateFilteredVelocity(qreal primaryDelta, qreal orthogonalDelta) void MobileTaskSwitcherState::calculateFilteredVelocity(qreal primaryDelta, qreal orthogonalDelta)
{ {
static qreal prevPrimaryDelta = 0; static qreal prevPrimaryDelta = 0;
@ -190,6 +196,15 @@ void MobileTaskSwitcherState::processTouchPositionChanged(qreal primaryDelta, qr
Q_EMIT touchPositionChanged(); Q_EMIT touchPositionChanged();
} }
qint64 MobileTaskSwitcherState::getElapsedTimeSinceStart()
{
if (m_doubleClickTimer->isValid())
{
return m_doubleClickTimer->elapsed();
}
return -1;
}
MobileTaskSwitcherEffect::MobileTaskSwitcherEffect() MobileTaskSwitcherEffect::MobileTaskSwitcherEffect()
: m_effectState{new EffectTouchBorderState(this)} : m_effectState{new EffectTouchBorderState(this)}
, m_taskSwitcherState{new MobileTaskSwitcherState(m_effectState)} , m_taskSwitcherState{new MobileTaskSwitcherState(m_effectState)}
@ -269,6 +284,7 @@ void MobileTaskSwitcherEffect::grabbedKeyboardEvent(QKeyEvent *keyEvent)
void MobileTaskSwitcherEffect::toggle() void MobileTaskSwitcherEffect::toggle()
{ {
if (!isRunning()) { if (!isRunning()) {
m_taskSwitcherState->restartDoubleClickTimer();
activate(); activate();
} else { } else {
deactivate(false); deactivate(false);

View file

@ -1,6 +1,6 @@
// SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org> // SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
// SPDX-FileCopyrightText: 2023 Devin Lin <devin@kde.org> // SPDX-FileCopyrightText: 2023 Devin Lin <devin@kde.org>
// SPDX-FileCopyrightText: 2024 Luis Büchi <luis.buechi@server23.cc> // SPDX-FileCopyrightText: 2024 Luis Büchi <luis.buechi@kdemail.net>
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
@ -48,6 +48,9 @@ class MobileTaskSwitcherState : public QObject
Q_PROPERTY(bool gestureInProgress READ gestureInProgress NOTIFY gestureInProgressChanged) Q_PROPERTY(bool gestureInProgress READ gestureInProgress NOTIFY gestureInProgressChanged)
Q_PROPERTY(Status status READ status WRITE setStatus NOTIFY statusChanged) Q_PROPERTY(Status status READ status WRITE setStatus NOTIFY statusChanged)
Q_PROPERTY(qint64 elapsedTimeSinceStart READ getElapsedTimeSinceStart)
Q_PROPERTY(qint64 doubleClickInterval READ getDoubleClickInterval) // is there a better way than to forward this?
public: public:
enum class Status { enum class Status {
// TODO! I could (should?) re-add the activating and deactivating states again to match EffectTogglableState. could help with/tie into // TODO! I could (should?) re-add the activating and deactivating states again to match EffectTogglableState. could help with/tie into
@ -97,6 +100,8 @@ public:
return m_initialTaskIndex; return m_initialTaskIndex;
} }
void restartDoubleClickTimer();
public Q_SLOTS: public Q_SLOTS:
void processTouchPositionChanged(qreal primaryPosition, qreal orthogonalPosition); void processTouchPositionChanged(qreal primaryPosition, qreal orthogonalPosition);
@ -130,6 +135,7 @@ private:
void clearVelocityFilter(); void clearVelocityFilter();
void calculateFilteredVelocity(qreal primaryPosition, qreal orthogonalPosition); void calculateFilteredVelocity(qreal primaryPosition, qreal orthogonalPosition);
qint64 getElapsedTimeSinceStart();
// velocities in (logical) pixels/msec // velocities in (logical) pixels/msec
QElapsedTimer m_frameTimer; QElapsedTimer m_frameTimer;
@ -149,6 +155,12 @@ private:
qreal m_yPosition = 0; qreal m_yPosition = 0;
bool m_wasInActiveTask; bool m_wasInActiveTask;
QElapsedTimer *m_doubleClickTimer;
qint64 getDoubleClickInterval() const
{
return qApp->doubleClickInterval();
}
}; };
class MobileTaskSwitcherEffect : public QuickSceneEffect class MobileTaskSwitcherEffect : public QuickSceneEffect

View file

@ -1,5 +1,6 @@
// SPDX-FileCopyrightText: 2015 Marco Martin <notmart@gmail.com> // SPDX-FileCopyrightText: 2015 Marco Martin <notmart@gmail.com>
// SPDX-FileCopyrightText: 2021-2023 Devin Lin <devin@kde.org> // SPDX-FileCopyrightText: 2021-2023 Devin Lin <devin@kde.org>
// SPDX-FileCopyrightText: 2024 Luis Büchi <luis.buechi@kdemail.net>
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick import QtQuick
@ -48,7 +49,7 @@ Item {
if (!ShellSettings.Settings.convergenceModeEnabled) { if (!ShellSettings.Settings.convergenceModeEnabled) {
delegate.window.setMaximize(true, true); delegate.window.setMaximize(true, true);
} }
taskSwitcherHelpers.openApp(model.index, delegate.window); taskSwitcherHelpers.openApp(model.index);
} }
function minimizeApp() { function minimizeApp() {

View file

@ -1,5 +1,6 @@
// SPDX-FileCopyrightText: 2015 Marco Martin <notmart@gmail.com> // SPDX-FileCopyrightText: 2015 Marco Martin <notmart@gmail.com>
// SPDX-FileCopyrightText: 2021-2024 Devin Lin <devin@kde.org> // SPDX-FileCopyrightText: 2021-2024 Devin Lin <devin@kde.org>
// SPDX-FileCopyrightText: 2024 Luis Büchi <luis.buechi@kdemail.net>
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick import QtQuick
@ -37,6 +38,7 @@ FocusScope {
property var taskSwitcherHelpers: TaskSwitcherHelpers { property var taskSwitcherHelpers: TaskSwitcherHelpers {
taskSwitcher: root taskSwitcher: root
stateClass: TaskSwitcherData.TaskSwitcherState stateClass: TaskSwitcherData.TaskSwitcherState
taskList: taskList
} }
MobileShell.HapticsEffect { MobileShell.HapticsEffect {
@ -308,8 +310,7 @@ FocusScope {
// if accidentally invoked, but can also be used to switch to an adjacent app and then open it // if accidentally invoked, but can also be used to switch to an adjacent app and then open it
function returnToApp() { function returnToApp() {
let newIndex = taskSwitcherHelpers.getNearestTaskIndex(); let newIndex = taskSwitcherHelpers.getNearestTaskIndex();
let appAtNewIndex = taskList.getTaskAt(newIndex).window; taskSwitcherHelpers.openApp(newIndex);
taskSwitcherHelpers.openApp(newIndex, appAtNewIndex);
} }
// diagonal quick switch gesture logic // diagonal quick switch gesture logic
@ -338,15 +339,14 @@ FocusScope {
} else { } else {
// flick to the left on the home screen, dismiss the gesture // flick to the left on the home screen, dismiss the gesture
taskSwitcherHelpers.close(); taskSwitcherHelpers.close();
retrun; return;
} }
} }
if (shouldSwitch) { if (shouldSwitch) {
if (!taskSwitcherHelpers.taskDrawerOpened && unmodifiedYposition < taskSwitcherHelpers.openedYPosition) { if (!taskSwitcherHelpers.taskDrawerOpened && unmodifiedYposition < taskSwitcherHelpers.openedYPosition) {
// if in a app, switch it to the new task when it is under the openedYPosition // if in a app, switch it to the new task when it is under the openedYPosition
taskList.setTaskOffsetValue(0, unmodifiedYposition < taskSwitcherHelpers.openedYPosition && taskSwitcherHelpers.notHomeScreenState); taskList.setTaskOffsetValue(0, unmodifiedYposition < taskSwitcherHelpers.openedYPosition && taskSwitcherHelpers.notHomeScreenState);
let appAtNewIndex = taskList.getTaskAt(newIndex).window; taskSwitcherHelpers.openApp(newIndex, Kirigami.Units.longDuration * 4, Easing.OutExpo);
taskSwitcherHelpers.openApp(newIndex, appAtNewIndex, Kirigami.Units.longDuration * 4, Easing.OutExpo);
} else { } else {
// if already in the task switcher or above the openedYPosition, only change the focus to the new task // if already in the task switcher or above the openedYPosition, only change the focus to the new task
taskSwitcherHelpers.animateGoToTaskIndex(newIndex); taskSwitcherHelpers.animateGoToTaskIndex(newIndex);
@ -380,7 +380,7 @@ FocusScope {
taskSwitcherHelpers.open(); taskSwitcherHelpers.open();
taskSwitcherHelpers.isInTaskScrubMode = false; taskSwitcherHelpers.isInTaskScrubMode = false;
} else { } else {
taskSwitcherHelpers.openApp(state.currentTaskIndex, taskList.getTaskAt(state.currentTaskIndex).window); taskSwitcherHelpers.openApp(state.currentTaskIndex);
} }
} else if (taskSwitcherHelpers.gestureState == TaskSwitcherHelpers.GestureStates.Undecided) { } else if (taskSwitcherHelpers.gestureState == TaskSwitcherHelpers.GestureStates.Undecided) {
if (taskSwitcherHelpers.taskDrawerOpened) { if (taskSwitcherHelpers.taskDrawerOpened) {
@ -499,8 +499,15 @@ FocusScope {
if (taskList.count === 0) { if (taskList.count === 0) {
root.hide(); root.hide();
} else { } else {
if (taskList.count > 1 &&
state.elapsedTimeSinceStart != -1 &&
state.elapsedTimeSinceStart < state.doubleClickInterval) {
taskSwitcherHelpers.openApp(1);
return;
}
const currentIndex = state.currentTaskIndex; const currentIndex = state.currentTaskIndex;
taskSwitcherHelpers.openApp(state.currentTaskIndex, taskList.getTaskAt(currentIndex).window); taskSwitcherHelpers.openApp(state.currentTaskIndex);
} }
} }
} }

View file

@ -1,4 +1,5 @@
// SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org> // SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
// SPDX-FileCopyrightText: 2024 Luis Büchi <luis.buechi@kdemail.net>
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick 2.15 import QtQuick 2.15
@ -19,6 +20,7 @@ QtObject {
required property var taskSwitcher required property var taskSwitcher
property var state: taskSwitcher.state property var state: taskSwitcher.state
required property var stateClass required property var stateClass
required property var taskList
// task switcher peek and pop setting for when it is toggled from the home screen // task switcher peek and pop setting for when it is toggled from the home screen
readonly property real peekOffsetValue: 1.85 readonly property real peekOffsetValue: 1.85
@ -217,14 +219,14 @@ QtObject {
closeFactorAnim.restart(); closeFactorAnim.restart();
} }
function openApp(index, window, duration = Kirigami.Units.shortDuration, horizontalEasing = Easing.OutBack) { function openApp(index, duration = Kirigami.Units.shortDuration, horizontalEasing = Easing.OutBack) {
// cancel any opening animations ongoing // cancel any opening animations ongoing
openAnim.stop(); openAnim.stop();
cancelAnimations(); cancelAnimations();
animateGoToTaskIndex(index, duration); animateGoToTaskIndex(index, duration);
openAppAnim.restart(); openAppAnim.restart();
KWinComponents.Workspace.activeWindow = window KWinComponents.Workspace.activeWindow = taskList.getTaskAt(index).window;
} }
// get the xPosition where the task will be centered on the screen // get the xPosition where the task will be centered on the screen