1# Copyright (c) 2011 Florian Mounier
2# Copyright (c) 2012, 2014-2015 Tycho Andersen
3# Copyright (c) 2013 Mattias Svala
4# Copyright (c) 2013 Craig Barnes
5# Copyright (c) 2014 ramnes
6# Copyright (c) 2014 Sean Vig
7# Copyright (c) 2014 Adi Sieker
8# Copyright (c) 2014 Chris Wesseling
9#
10# Permission is hereby granted, free of charge, to any person obtaining a copy
11# of this software and associated documentation files (the "Software"), to deal
12# in the Software without restriction, including without limitation the rights
13# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14# copies of the Software, and to permit persons to whom the Software is
15# furnished to do so, subject to the following conditions:
16#
17# The above copyright notice and this permission notice shall be included in
18# all copies or substantial portions of the Software.
19#
20# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26# SOFTWARE.
27
28import pytest
29
30import libqtile.config
31from libqtile import layout
32from libqtile.confreader import Config
33from test.layouts.layout_utils import assert_focus_path, assert_focused
34
35
36class StackConfig(Config):
37    auto_fullscreen = True
38    groups = [
39        libqtile.config.Group("a"),
40        libqtile.config.Group("b"),
41        libqtile.config.Group("c"),
42        libqtile.config.Group("d")
43    ]
44    layouts = [
45        layout.Stack(num_stacks=2),
46        layout.Stack(num_stacks=1),
47    ]
48    floating_layout = libqtile.resources.default_config.floating_layout
49    keys = []
50    mouse = []
51    screens = []
52    follow_mouse_focus = False
53
54
55stack_config = pytest.mark.parametrize("manager", [StackConfig], indirect=True)
56
57
58def _stacks(manager):
59    stacks = []
60    for i in manager.c.layout.info()["stacks"]:
61        windows = i["clients"]
62        current = i["current"]
63        stacks.append(windows[current:] + windows[:current])
64    return stacks
65
66
67@stack_config
68def test_stack_commands(manager):
69    assert manager.c.layout.info()["current_stack"] == 0
70    manager.test_window("one")
71    assert _stacks(manager) == [["one"], []]
72    assert manager.c.layout.info()["current_stack"] == 0
73    manager.test_window("two")
74    assert _stacks(manager) == [["one"], ["two"]]
75    assert manager.c.layout.info()["current_stack"] == 1
76    manager.test_window("three")
77    assert _stacks(manager) == [["one"], ["three", "two"]]
78    assert manager.c.layout.info()["current_stack"] == 1
79
80    manager.c.layout.delete()
81    assert _stacks(manager) == [["one", "three", "two"]]
82    info = manager.c.groups()["a"]
83    assert info["focus"] == "one"
84    manager.c.layout.delete()
85    assert len(_stacks(manager)) == 1
86
87    manager.c.layout.add()
88    assert _stacks(manager) == [["one", "three", "two"], []]
89
90    manager.c.layout.rotate()
91    assert _stacks(manager) == [[], ["one", "three", "two"]]
92
93
94@stack_config
95def test_stack_cmd_down(manager):
96    manager.c.layout.down()
97
98
99@stack_config
100def test_stack_addremove(manager):
101    one = manager.test_window("one")
102    manager.c.layout.next()
103    two = manager.test_window("two")
104    three = manager.test_window("three")
105    assert _stacks(manager) == [['one'], ['three', 'two']]
106    assert manager.c.layout.info()["current_stack"] == 1
107    manager.kill_window(three)
108    assert manager.c.layout.info()["current_stack"] == 1
109    manager.kill_window(two)
110    assert manager.c.layout.info()["current_stack"] == 0
111    manager.c.layout.next()
112    two = manager.test_window("two")
113    manager.c.layout.next()
114    assert manager.c.layout.info()["current_stack"] == 0
115    manager.kill_window(one)
116    assert manager.c.layout.info()["current_stack"] == 1
117
118
119@stack_config
120def test_stack_rotation(manager):
121    manager.c.layout.delete()
122    manager.test_window("one")
123    manager.test_window("two")
124    manager.test_window("three")
125    assert _stacks(manager) == [["three", "two", "one"]]
126    manager.c.layout.down()
127    assert _stacks(manager) == [["two", "one", "three"]]
128    manager.c.layout.up()
129    assert _stacks(manager) == [["three", "two", "one"]]
130    manager.c.layout.down()
131    manager.c.layout.down()
132    assert _stacks(manager) == [["one", "three", "two"]]
133
134
135@stack_config
136def test_stack_nextprev(manager):
137    manager.c.layout.add()
138    one = manager.test_window("one")
139    two = manager.test_window("two")
140    three = manager.test_window("three")
141
142    assert manager.c.groups()["a"]["focus"] == "three"
143    manager.c.layout.next()
144    assert manager.c.groups()["a"]["focus"] == "one"
145
146    manager.c.layout.previous()
147    assert manager.c.groups()["a"]["focus"] == "three"
148    manager.c.layout.previous()
149    assert manager.c.groups()["a"]["focus"] == "two"
150
151    manager.c.layout.next()
152    manager.c.layout.next()
153    manager.c.layout.next()
154    assert manager.c.groups()["a"]["focus"] == "two"
155
156    manager.kill_window(three)
157    manager.c.layout.next()
158    assert manager.c.groups()["a"]["focus"] == "one"
159    manager.c.layout.previous()
160    assert manager.c.groups()["a"]["focus"] == "two"
161    manager.c.layout.next()
162    manager.kill_window(two)
163    manager.c.layout.next()
164    assert manager.c.groups()["a"]["focus"] == "one"
165
166    manager.kill_window(one)
167    manager.c.layout.next()
168    assert manager.c.groups()["a"]["focus"] is None
169    manager.c.layout.previous()
170    assert manager.c.groups()["a"]["focus"] is None
171
172
173@stack_config
174def test_stack_window_removal(manager):
175    manager.c.layout.next()
176    manager.test_window("one")
177    two = manager.test_window("two")
178    manager.c.layout.down()
179    manager.kill_window(two)
180
181
182@stack_config
183def test_stack_split(manager):
184    manager.test_window("one")
185    manager.test_window("two")
186    manager.test_window("three")
187    stacks = manager.c.layout.info()["stacks"]
188    assert not stacks[1]["split"]
189    manager.c.layout.toggle_split()
190    stacks = manager.c.layout.info()["stacks"]
191    assert stacks[1]["split"]
192
193
194@stack_config
195def test_stack_shuffle(manager):
196    manager.c.next_layout()
197    manager.test_window("one")
198    manager.test_window("two")
199    manager.test_window("three")
200
201    stack = manager.c.layout.info()["stacks"][0]
202    assert stack["clients"][stack["current"]] == "three"
203    for i in range(5):
204        manager.c.layout.shuffle_up()
205        stack = manager.c.layout.info()["stacks"][0]
206        assert stack["clients"][stack["current"]] == "three"
207    for i in range(5):
208        manager.c.layout.shuffle_down()
209        stack = manager.c.layout.info()["stacks"][0]
210        assert stack["clients"][stack["current"]] == "three"
211
212
213@stack_config
214def test_stack_client_to(manager):
215    manager.test_window("one")
216    manager.test_window("two")
217    assert manager.c.layout.info()["stacks"][0]["clients"] == ["one"]
218    manager.c.layout.client_to_previous()
219    assert manager.c.layout.info()["stacks"][0]["clients"] == ["two", "one"]
220    manager.c.layout.client_to_previous()
221    assert manager.c.layout.info()["stacks"][0]["clients"] == ["one"]
222    assert manager.c.layout.info()["stacks"][1]["clients"] == ["two"]
223    manager.c.layout.client_to_next()
224    assert manager.c.layout.info()["stacks"][0]["clients"] == ["two", "one"]
225
226
227@stack_config
228def test_stack_info(manager):
229    manager.test_window("one")
230    assert manager.c.layout.info()["stacks"]
231
232
233@stack_config
234def test_stack_window_focus_cycle(manager):
235    # setup 3 tiled and two floating clients
236    manager.test_window("one")
237    manager.test_window("two")
238    manager.test_window("float1")
239    manager.c.window.toggle_floating()
240    manager.test_window("float2")
241    manager.c.window.toggle_floating()
242    manager.test_window("three")
243
244    # test preconditions, stack adds clients at pos of current
245    assert manager.c.layout.info()['clients'] == ['three', 'one', 'two']
246    # last added window has focus
247    assert_focused(manager, "three")
248
249    # assert window focus cycle, according to order in layout
250    assert_focus_path(manager, 'one', 'two', 'float1', 'float2', 'three')
251