Commit 07efee10 authored by Fushan Wen's avatar Fushan Wen 💬
Browse files

shell: allow to cycle through multiple panels

This adds `m_blockRestorePreviousWindow` to avoid restoring focus to the
panel, and replace QHash with QMap to save panel orders.

CCBUG: 453166
CCBUG: 352476
parent 1a8b6ba7
Pipeline #220438 passed with stage
in 9 minutes and 36 seconds
......@@ -31,7 +31,6 @@
#include <Plasma/Package>
#include <KWayland/Client/plasmashell.h>
#include <KWayland/Client/plasmawindowmanagement.h>
#include <KWayland/Client/surface.h>
#if HAVE_X11
......@@ -1406,28 +1405,14 @@ void PanelView::refreshStatus(Plasma::Types::ItemStatus status)
} else if (status == Plasma::Types::AcceptingInputStatus) {
setFlags(flags() & ~Qt::WindowDoesNotAcceptFocus);
#ifdef HAVE_X11
if (KWindowSystem::isPlatformX11()) {
m_previousWId = KWindowSystem::activeWindow();
}
KWindowSystem::forceActiveWindow(winId());
#endif
if (m_shellSurface) {
m_previousPlasmaWindow = m_corona->waylandPlasmaWindowManagementInterface()->activeWindow();
m_shellSurface->setPanelTakesFocus(true);
}
} else {
if (status == Plasma::Types::PassiveStatus) {
// Restores the previous focus
#ifdef HAVE_X11
if (KWindowSystem::isPlatformX11() && m_previousWId) {
KWindowSystem::forceActiveWindow(m_previousWId);
m_previousWId = 0;
}
#endif
if (m_previousPlasmaWindow) {
m_previousPlasmaWindow->requestActivate();
m_previousPlasmaWindow = nullptr;
}
m_corona->restorePreviousWindow();
}
restoreAutoHide();
......
......@@ -23,7 +23,6 @@ namespace KWayland
namespace Client
{
class PlasmaShellSurface;
class PlasmaWindow;
}
}
......@@ -287,11 +286,5 @@ private:
QPointer<QScreen> m_screenToFollow;
QMetaObject::Connection m_transientWindowVisibleWatcher;
// Used to restore the previous activated window after the panel loses focus
#ifdef HAVE_X11
WId m_previousWId = 0;
#endif
KWayland::Client::PlasmaWindow *m_previousPlasmaWindow = nullptr;
static const int STRUTSTIMERDELAY = 200;
};
......@@ -277,27 +277,7 @@ void ShellCorona::init()
QAction *cyclePanelFocusAction = actions()->addAction(QStringLiteral("cycle-panels"));
cyclePanelFocusAction->setText(i18n("Move keyboard focus between panels"));
KGlobalAccel::self()->setGlobalShortcut(cyclePanelFocusAction, Qt::META | Qt::ALT | Qt::Key_P);
connect(cyclePanelFocusAction, &QAction::triggered, this, [this]() {
if (m_panelViews.isEmpty()) {
return;
}
PanelView *activePanel = qobject_cast<PanelView *>(qGuiApp->focusWindow());
if (!activePanel) {
activePanel = m_panelViews.values().first();
}
if (activePanel->containment()->status() != Plasma::Types::AcceptingInputStatus) {
activePanel->containment()->setStatus(Plasma::Types::AcceptingInputStatus);
auto nextItem = activePanel->rootObject()->nextItemInFocusChain();
if (nextItem) {
nextItem->forceActiveFocus();
}
} else {
// Cancel focus
activePanel->containment()->setStatus(Plasma::Types::PassiveStatus);
}
});
connect(cyclePanelFocusAction, &QAction::triggered, this, &ShellCorona::slotCyclePanelFocus);
}
ShellCorona::~ShellCorona()
......@@ -914,6 +894,79 @@ void ShellCorona::requestApplicationConfigSync()
m_appConfigSyncTimer.start();
}
void ShellCorona::slotCyclePanelFocus()
{
if (m_panelViews.empty()) {
return;
}
PanelView *activePanel = qobject_cast<PanelView *>(qGuiApp->focusWindow());
if (!activePanel) {
// Activate the first panel and save the previous window
activePanel = m_panelViews.begin().value();
#if HAVE_X11
if (KWindowSystem::isPlatformX11()) {
m_previousWId = KWindowSystem::activeWindow();
}
#endif
if (m_waylandWindowManagement) {
m_previousPlasmaWindow = m_waylandWindowManagement->activeWindow();
}
}
auto setFocus = [](PanelView *view) {
view->containment()->setStatus(Plasma::Types::AcceptingInputStatus);
auto nextItem = view->rootObject()->nextItemInFocusChain();
if (nextItem) {
nextItem->forceActiveFocus();
} else {
view->containment()->setStatus(Plasma::Types::PassiveStatus);
}
};
if (activePanel->containment()->status() != Plasma::Types::AcceptingInputStatus) {
setFocus(activePanel);
} else {
// Cancel focus on the current panel
// Block focus on the panel if it's not the last panel
if (activePanel != m_panelViews.last()) {
m_blockRestorePreviousWindow = true;
}
activePanel->containment()->setStatus(Plasma::Types::PassiveStatus);
m_blockRestorePreviousWindow = false;
// More than one panel and the current panel is not the last panel,
// move focus to next panel.
if (activePanel != m_panelViews.last()) {
auto viewIt = std::find_if(m_panelViews.cbegin(), m_panelViews.cend(), [activePanel](const PanelView *panel) {
return activePanel == panel;
});
if (viewIt == m_panelViews.cend()) {
return;
}
// Skip destroyed panels
viewIt = std::next(viewIt);
while (viewIt != m_panelViews.cend()) {
if (!viewIt.value()->containment()->destroyed()) {
break;
}
viewIt = std::next(viewIt);
}
if (viewIt != m_panelViews.cend()) {
setFocus(viewIt.value());
} else {
restorePreviousWindow();
}
}
}
}
void ShellCorona::loadDefaultLayout()
{
// pre-startup scripts
......@@ -1114,7 +1167,7 @@ void ShellCorona::removeDesktop(DesktopView *desktopView)
}
Q_ASSERT(deskIt != m_desktopViewForScreen.end());
QMutableHashIterator<const Plasma::Containment *, PanelView *> it(m_panelViews);
QMutableMapIterator<const Plasma::Containment *, PanelView *> it(m_panelViews);
while (it.hasNext()) {
it.next();
PanelView *panelView = it.value();
......@@ -1137,6 +1190,25 @@ PanelView *ShellCorona::panelView(Plasma::Containment *containment) const
return m_panelViews.value(containment);
}
void ShellCorona::restorePreviousWindow()
{
if (m_blockRestorePreviousWindow) {
return;
}
#if HAVE_X11
if (KWindowSystem::isPlatformX11() && m_previousWId) {
KWindowSystem::forceActiveWindow(m_previousWId);
}
#endif
if (m_previousPlasmaWindow) {
m_previousPlasmaWindow->requestActivate();
}
m_previousWId = 0;
m_previousPlasmaWindow = nullptr;
}
///// SLOTS
QList<PanelView *> ShellCorona::panelsForScreen(QScreen *screen) const
......@@ -2198,11 +2270,6 @@ KWayland::Client::PlasmaShell *ShellCorona::waylandPlasmaShellInterface() const
return m_waylandPlasmaShell;
}
KWayland::Client::PlasmaWindowManagement *ShellCorona::waylandPlasmaWindowManagementInterface() const
{
return m_waylandWindowManagement;
}
ScreenPool *ShellCorona::screenPool() const
{
return m_screenPool;
......
......@@ -9,6 +9,7 @@
#pragma once
#include "config-X11.h"
#include "plasma/corona.h"
#include <QDBusContext>
......@@ -52,6 +53,8 @@ namespace KWayland
namespace Client
{
class PlasmaShell;
class PlasmaShellSurface;
class PlasmaWindow;
class PlasmaWindowManagement;
}
}
......@@ -87,6 +90,7 @@ public:
Q_INVOKABLE QStringList availableActivities() const;
PanelView *panelView(Plasma::Containment *containment) const;
void restorePreviousWindow();
// This one is a bit of an hack but are just for desktop scripting
void insertActivity(const QString &id, const QString &plugin);
......@@ -101,7 +105,6 @@ public:
Plasma::Containment *createContainmentForActivity(const QString &activity, int screenNum);
KWayland::Client::PlasmaShell *waylandPlasmaShellInterface() const;
KWayland::Client::PlasmaWindowManagement *waylandPlasmaWindowManagementInterface() const;
ScreenPool *screenPool() const;
......@@ -145,6 +148,11 @@ public Q_SLOTS:
*/
void requestApplicationConfigSync();
/**
* Cycle through all panels
*/
void slotCyclePanelFocus();
/**
* Sets the shell that the corona should display
*/
......@@ -263,7 +271,7 @@ private:
ScreenPool *m_screenPool;
QString m_shell;
KActivities::Controller *m_activityController;
QHash<const Plasma::Containment *, PanelView *> m_panelViews;
QMap<const Plasma::Containment *, PanelView *> m_panelViews;
// map from QScreen to desktop view
QHash<const QScreen *, DesktopView *> m_desktopViewForScreen;
QHash<const Plasma::Containment *, int> m_pendingScreenChanges;
......@@ -275,6 +283,13 @@ private:
std::unique_ptr<QMenu> m_addPanelsMenu;
KPackage::Package m_lookAndFeelPackage;
// Used to restore the previous activated window after the panel loses focus
KWayland::Client::PlasmaShellSurface *m_shellSurface = nullptr;
#if HAVE_X11
WId m_previousWId = 0;
#endif
bool m_blockRestorePreviousWindow = false;
QTimer m_waitingPanelsTimer;
QTimer m_appConfigSyncTimer;
#ifndef NDEBUG
......@@ -283,6 +298,7 @@ private:
KWayland::Client::PlasmaShell *m_waylandPlasmaShell;
// For getting the active window on Wayland
KWayland::Client::PlasmaWindowManagement *m_waylandWindowManagement = nullptr;
KWayland::Client::PlasmaWindow *m_previousPlasmaWindow = nullptr;
bool m_closingDown : 1;
QString m_testModeLayout;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment