File indexing completed on 2024-04-28 05:30:16

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2012 Martin Gräßlin <mgraesslin@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 #pragma once
0010 // KWin
0011 #include "effect/globals.h"
0012 // Qt
0013 #include <QHash>
0014 #include <QObject>
0015 
0016 namespace KWin
0017 {
0018 // forward declarations
0019 class Window;
0020 class Output;
0021 class VirtualDesktop;
0022 
0023 /**
0024  * @brief Singleton class to handle the various focus chains.
0025  *
0026  * A focus chain is a list of Windows containing information on which Window should be activated.
0027  *
0028  * Internally this FocusChain holds multiple independent chains. There is one chain of most recently
0029  * used Windows which is primarily used by TabBox to build up the list of Windows for navigation.
0030  * The chains are organized as a normal QList of Windows with the most recently used Window being the
0031  * last item of the list, that is a LIFO like structure.
0032  *
0033  * In addition there is one chain for each virtual desktop which is used to determine which Window
0034  * should get activated when the user switches to another virtual desktop.
0035  *
0036  * Furthermore this class contains various helper methods for the two different kind of chains.
0037  */
0038 class FocusChain : public QObject
0039 {
0040     Q_OBJECT
0041 public:
0042     enum Change {
0043         MakeFirst,
0044         MakeLast,
0045         Update,
0046         MakeFirstMinimized = MakeFirst
0047     };
0048     explicit FocusChain() = default;
0049 
0050     /**
0051      * @brief Updates the position of the @p window according to the requested @p change in the
0052      * focus chain.
0053      *
0054      * This method affects both the most recently used focus chain and the per virtual desktop focus
0055      * chain.
0056      *
0057      * In case the window does no longer want to get focus, it is removed from all chains. In case
0058      * the window is on all virtual desktops it is ensured that it is present in each of the virtual
0059      * desktops focus chain. In case it's on exactly one virtual desktop it is ensured that it is only
0060      * in the focus chain for that virtual desktop.
0061      *
0062      * Depending on @p change the Window is inserted at different positions in the focus chain. In case
0063      * of @c MakeFirst it is moved to the first position of the chain, in case of
0064      * @c MakeLast it is moved to the last position of the chain. In all other cases it
0065      * depends on whether the @p window is the currently active Window. If it is the active Window it
0066      * becomes the first Window in the chain, otherwise it is inserted at the second position that is
0067      * directly after the currently active Window.
0068      *
0069      * @param window The Window which should be moved inside the chains.
0070      * @param change Where to move the Window
0071      * @return void
0072      */
0073     void update(Window *window, Change change);
0074     /**
0075      * @brief Moves @p window behind the @p reference Window in all focus chains.
0076      *
0077      * @param window The Window to move in the chains
0078      * @param reference The Window behind which the @p window should be moved
0079      * @return void
0080      */
0081     void moveAfterWindow(Window *window, Window *reference);
0082     /**
0083      * @brief Finds the best Window to become the new active Window in the focus chain for the given
0084      * virtual @p desktop.
0085      *
0086      * In case that separate screen focus is used only Windows on the current screen are considered.
0087      * If no Window for activation is found @c null is returned.
0088      *
0089      * @param desktop The virtual desktop to look for a Window for activation
0090      * @return :X11Window *The Window which could be activated or @c null if there is none.
0091      */
0092     Window *getForActivation(VirtualDesktop *desktop) const;
0093     /**
0094      * @brief Finds the best Window to become the new active Window in the focus chain for the given
0095      * virtual @p desktop on the given @p screen.
0096      *
0097      * This method makes only sense to use if separate screen focus is used. If separate screen focus
0098      * is disabled the @p screen is ignored.
0099      * If no Window for activation is found @c null is returned.
0100      *
0101      * @param desktop The virtual desktop to look for a Window for activation
0102      * @param output The screen to constrain the search on with separate screen focus
0103      * @return :X11Window *The Window which could be activated or @c null if there is none.
0104      */
0105     Window *getForActivation(VirtualDesktop *desktop, Output *output) const;
0106 
0107     /**
0108      * @brief Checks whether the most recently used focus chain contains the given @p window.
0109      *
0110      * Does not consider the per-desktop focus chains.
0111      * @param window The Window to look for.
0112      * @return bool @c true if the most recently used focus chain contains @p window, @c false otherwise.
0113      */
0114     bool contains(Window *window) const;
0115     /**
0116      * @brief Checks whether the focus chain for the given @p desktop contains the given @p window.
0117      *
0118      * Does not consider the most recently used focus chain.
0119      *
0120      * @param window The Window to look for.
0121      * @param desktop The virtual desktop whose focus chain should be used
0122      * @return bool @c true if the focus chain for @p desktop contains @p window, @c false otherwise.
0123      */
0124     bool contains(Window *window, VirtualDesktop *desktop) const;
0125     /**
0126      * @brief Queries the most recently used focus chain for the next Window after the given
0127      * @p reference Window.
0128      *
0129      * The navigation wraps around the borders of the chain. That is if the @p reference Window is
0130      * the last item of the focus chain, the first Window will be returned.
0131      *
0132      * If the @p reference Window cannot be found in the focus chain, the first element of the focus
0133      * chain is returned.
0134      *
0135      * @param reference The start point in the focus chain to search
0136      * @return :X11Window *The relatively next Window in the most recently used chain.
0137      */
0138     Window *nextMostRecentlyUsed(Window *reference) const;
0139     /**
0140      * @brief Queries the focus chain for @p desktop for the next Window in relation to the given
0141      * @p reference Window.
0142      *
0143      * The method finds the first usable Window which is not the @p reference Window. If no Window
0144      * can be found @c null is returned
0145      *
0146      * @param reference The reference Window which should not be returned
0147      * @param desktop The virtual desktop whose focus chain should be used
0148      * @return :X11Window *The next usable Window or @c null if none can be found.
0149      */
0150     Window *nextForDesktop(Window *reference, VirtualDesktop *desktop) const;
0151     /**
0152      * @brief Returns the first Window in the most recently used focus chain. First Window in this
0153      * case means really the first Window in the chain and not the most recently used Window.
0154      *
0155      * @return :X11Window *The first Window in the most recently used chain.
0156      */
0157     Window *firstMostRecentlyUsed() const;
0158 
0159     bool isUsableFocusCandidate(Window *window, Window *prev) const;
0160 
0161 public Q_SLOTS:
0162     /**
0163      * @brief Removes @p window from all focus chains.
0164      *
0165      * @param window The Window to remove from all focus chains.
0166      * @return void
0167      */
0168     void remove(KWin::Window *window);
0169     void setSeparateScreenFocus(bool enabled);
0170     void setActiveWindow(KWin::Window *window);
0171     void setCurrentDesktop(VirtualDesktop *desktop);
0172     void addDesktop(VirtualDesktop *desktop);
0173     void removeDesktop(VirtualDesktop *desktop);
0174 
0175 private:
0176     using Chain = QList<Window *>;
0177     /**
0178      * @brief Makes @p window the first Window in the given focus @p chain.
0179      *
0180      * This means the existing position of @p window is dropped and @p window is appended to the
0181      * @p chain which makes it the first item.
0182      *
0183      * @param window The Window to become the first in @p chain
0184      * @param chain The focus chain to operate on
0185      * @return void
0186      */
0187     void makeFirstInChain(Window *window, Chain &chain);
0188     /**
0189      * @brief Makes @p window the last Window in the given focus @p chain.
0190      *
0191      * This means the existing position of @p window is dropped and @p window is prepended to the
0192      * @p chain which makes it the last item.
0193      *
0194      * @param window The Window to become the last in @p chain
0195      * @param chain The focus chain to operate on
0196      * @return void
0197      */
0198     void makeLastInChain(Window *window, Chain &chain);
0199     void moveAfterWindowInChain(Window *window, Window *reference, Chain &chain);
0200     void updateWindowInChain(Window *window, Change change, Chain &chain);
0201     void insertWindowIntoChain(Window *window, Chain &chain);
0202     Chain m_mostRecentlyUsed;
0203     QHash<VirtualDesktop *, Chain> m_desktopFocusChains;
0204     bool m_separateScreenFocus = false;
0205     Window *m_activeWindow = nullptr;
0206     VirtualDesktop *m_currentDesktop = nullptr;
0207 };
0208 
0209 inline bool FocusChain::contains(Window *window) const
0210 {
0211     return m_mostRecentlyUsed.contains(window);
0212 }
0213 
0214 inline void FocusChain::setSeparateScreenFocus(bool enabled)
0215 {
0216     m_separateScreenFocus = enabled;
0217 }
0218 
0219 inline void FocusChain::setActiveWindow(Window *window)
0220 {
0221     m_activeWindow = window;
0222 }
0223 
0224 inline void FocusChain::setCurrentDesktop(VirtualDesktop *desktop)
0225 {
0226     m_currentDesktop = desktop;
0227 }
0228 
0229 } // namespace