KWin
Loading...
Searching...
No Matches
focuschain.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2012 Martin Gräßlin <mgraesslin@kde.org>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
9#include "focuschain.h"
10#include "window.h"
11#include "workspace.h"
12
13namespace KWin
14{
15
17{
18 for (auto it = m_desktopFocusChains.begin();
19 it != m_desktopFocusChains.end();
20 ++it) {
21 it.value().removeAll(window);
22 }
23 m_mostRecentlyUsed.removeAll(window);
24}
25
27{
28 m_desktopFocusChains.insert(desktop, Chain());
29}
30
32{
33 if (m_currentDesktop == desktop) {
34 m_currentDesktop = nullptr;
35 }
36 m_desktopFocusChains.remove(desktop);
37}
38
40{
41 return getForActivation(desktop, workspace()->activeOutput());
42}
43
45{
46 auto it = m_desktopFocusChains.constFind(desktop);
47 if (it == m_desktopFocusChains.constEnd()) {
48 return nullptr;
49 }
50 const auto &chain = it.value();
51 for (int i = chain.size() - 1; i >= 0; --i) {
52 auto tmp = chain.at(i);
53 // TODO: move the check into Window
54 if (!tmp->isShade() && tmp->isShown() && tmp->isOnCurrentActivity()
55 && (!m_separateScreenFocus || tmp->output() == output)) {
56 return tmp;
57 }
58 }
59 return nullptr;
60}
61
63{
64 if (!window->wantsTabFocus()) {
65 // Doesn't want tab focus, remove
66 remove(window);
67 return;
68 }
69
70 if (window->isOnAllDesktops()) {
71 // Now on all desktops, add it to focus chains it is not already in
72 for (auto it = m_desktopFocusChains.begin();
73 it != m_desktopFocusChains.end();
74 ++it) {
75 auto &chain = it.value();
76 // Making first/last works only on current desktop, don't affect all desktops
77 if (it.key() == m_currentDesktop
78 && (change == MakeFirst || change == MakeLast)) {
79 if (change == MakeFirst) {
80 makeFirstInChain(window, chain);
81 } else {
82 makeLastInChain(window, chain);
83 }
84 } else {
85 insertWindowIntoChain(window, chain);
86 }
87 }
88 } else {
89 // Now only on desktop, remove it anywhere else
90 for (auto it = m_desktopFocusChains.begin();
91 it != m_desktopFocusChains.end();
92 ++it) {
93 auto &chain = it.value();
94 if (window->isOnDesktop(it.key())) {
95 updateWindowInChain(window, change, chain);
96 } else {
97 chain.removeAll(window);
98 }
99 }
100 }
101
102 // add for most recently used chain
103 updateWindowInChain(window, change, m_mostRecentlyUsed);
104}
105
106void FocusChain::updateWindowInChain(Window *window, FocusChain::Change change, Chain &chain)
107{
108 if (change == MakeFirst) {
109 makeFirstInChain(window, chain);
110 } else if (change == MakeLast) {
111 makeLastInChain(window, chain);
112 } else {
113 insertWindowIntoChain(window, chain);
114 }
115}
116
117void FocusChain::insertWindowIntoChain(Window *window, Chain &chain)
118{
119 Q_ASSERT(!window->isDeleted());
120 if (chain.contains(window)) {
121 return;
122 }
123 if (m_activeWindow && m_activeWindow != window && !chain.empty() && chain.last() == m_activeWindow) {
124 // Add it after the active window
125 chain.insert(chain.size() - 1, window);
126 } else {
127 // Otherwise add as the first one
128 chain.append(window);
129 }
130}
131
133{
134 Q_ASSERT(!window->isDeleted());
135 if (!window->wantsTabFocus()) {
136 return;
137 }
138
139 for (auto it = m_desktopFocusChains.begin();
140 it != m_desktopFocusChains.end();
141 ++it) {
142 if (!window->isOnDesktop(it.key())) {
143 continue;
144 }
145 moveAfterWindowInChain(window, reference, it.value());
146 }
147 moveAfterWindowInChain(window, reference, m_mostRecentlyUsed);
148}
149
150void FocusChain::moveAfterWindowInChain(Window *window, Window *reference, Chain &chain)
151{
152 Q_ASSERT(!window->isDeleted());
153 if (!chain.contains(reference)) {
154 return;
155 }
156 if (Window::belongToSameApplication(reference, window)) {
157 chain.removeAll(window);
158 chain.insert(chain.indexOf(reference), window);
159 } else {
160 chain.removeAll(window);
161 for (int i = chain.size() - 1; i >= 0; --i) {
162 if (Window::belongToSameApplication(reference, chain.at(i))) {
163 chain.insert(i, window);
164 break;
165 }
166 }
167 }
168}
169
171{
172 if (m_mostRecentlyUsed.isEmpty()) {
173 return nullptr;
174 }
175 return m_mostRecentlyUsed.first();
176}
177
179{
180 if (m_mostRecentlyUsed.isEmpty()) {
181 return nullptr;
182 }
183 const int index = m_mostRecentlyUsed.indexOf(reference);
184 if (index == -1) {
185 return m_mostRecentlyUsed.first();
186 }
187 if (index == 0) {
188 return m_mostRecentlyUsed.last();
189 }
190 return m_mostRecentlyUsed.at(index - 1);
191}
192
193// copied from activation.cpp
195{
196 return c != prev && !c->isShade() && c->isShown() && c->isOnCurrentDesktop() && c->isOnCurrentActivity() && (!m_separateScreenFocus || c->isOnOutput(prev ? prev->output() : workspace()->activeOutput()));
197}
198
200{
201 auto it = m_desktopFocusChains.constFind(desktop);
202 if (it == m_desktopFocusChains.constEnd()) {
203 return nullptr;
204 }
205 const auto &chain = it.value();
206 for (int i = chain.size() - 1; i >= 0; --i) {
207 auto window = chain.at(i);
208 if (isUsableFocusCandidate(window, reference)) {
209 return window;
210 }
211 }
212 return nullptr;
213}
214
215void FocusChain::makeFirstInChain(Window *window, Chain &chain)
216{
217 Q_ASSERT(!window->isDeleted());
218 chain.removeAll(window);
219 chain.append(window);
220}
221
222void FocusChain::makeLastInChain(Window *window, Chain &chain)
223{
224 Q_ASSERT(!window->isDeleted());
225 chain.removeAll(window);
226 chain.prepend(window);
227}
228
229bool FocusChain::contains(Window *window, VirtualDesktop *desktop) const
230{
231 auto it = m_desktopFocusChains.constFind(desktop);
232 if (it == m_desktopFocusChains.constEnd()) {
233 return false;
234 }
235 return it.value().contains(window);
236}
237
238} // namespace
239
240#include "moc_focuschain.cpp"
Window * getForActivation(VirtualDesktop *desktop) const
Finds the best Window to become the new active Window in the focus chain for the given virtual deskto...
void removeDesktop(VirtualDesktop *desktop)
void moveAfterWindow(Window *window, Window *reference)
Moves window behind the reference Window in all focus chains.
void remove(KWin::Window *window)
Removes window from all focus chains.
void update(Window *window, Change change)
Updates the position of the window according to the requested change in the focus chain.
void addDesktop(VirtualDesktop *desktop)
Window * nextForDesktop(Window *reference, VirtualDesktop *desktop) const
Queries the focus chain for desktop for the next Window in relation to the given reference Window.
bool contains(Window *window) const
Checks whether the most recently used focus chain contains the given window.
Definition focuschain.h:209
bool isUsableFocusCandidate(Window *window, Window *prev) const
Window * firstMostRecentlyUsed() const
Returns the first Window in the most recently used focus chain. First Window in this case means reall...
Window * nextMostRecentlyUsed(Window *reference) const
Queries the most recently used focus chain for the next Window after the given reference Window.
bool isShade() const
Definition window.h:1034
bool isShown() const
Definition window.cpp:4232
bool wantsTabFocus() const
Definition window.cpp:697
bool isOnCurrentActivity() const
Definition window.cpp:3097
static bool belongToSameApplication(const Window *c1, const Window *c2, SameApplicationChecks checks=SameApplicationChecks())
Definition window.cpp:426
bool isOnAllDesktops() const
Definition window.h:2032
bool isOnCurrentDesktop() const
Definition window.cpp:848
bool isOnDesktop(VirtualDesktop *desktop) const
Definition window.cpp:843
KWin::Output * output
Definition window.h:111
bool isOnOutput(Output *output) const
Definition window.cpp:254
bool isDeleted() const
Definition window.cpp:540
Output * activeOutput() const
Workspace * workspace()
Definition workspace.h:830