move scratchpad on a scratchpad windows inserts in head
Patch status: rejected
Patch by Philippe Virouleau
Long description:
Implement the behaviour described in #1035 : 'scratchpad show' cycles between windows, 'scratchpad move' on a scratchpad window makes it the next to be displayed by 'scratchpad show'.
To apply this patch, use:
curl http://cr.i3wm.org/patch/248/raw.patch | git am
b/include/data.h
23 |
@@ -585,7 +585,11 @@ struct Con { |
24 |
SCRATCHPAD_FRESH = 1, |
25 |
|
26 |
/* The user changed position/size of the scratchpad window. */ |
27 |
- SCRATCHPAD_CHANGED = 2 |
28 |
+ SCRATCHPAD_CHANGED = 2, |
29 |
+ |
30 |
+ /* A scratchpad window moving to scratchpad again through an |
31 |
+ * actual 'scratchpad move'. */ |
32 |
+ SCRATCHPAD_MOVE = 3 |
33 |
} scratchpad_state; |
34 |
|
35 |
/* The ID of this container before restarting. Necessary to correctly |
b/include/scratchpad.h
40 |
@@ -17,7 +17,7 @@ |
41 |
* Gets called upon the command 'move scratchpad'. |
42 |
* |
43 |
*/ |
44 |
-void scratchpad_move(Con *con); |
45 |
+void scratchpad_move(Con *con, bool from_show); |
46 |
|
47 |
/** |
48 |
* Either shows the top-most scratchpad window (con == NULL) or shows the |
b/src/commands.c
53 |
@@ -1786,7 +1786,7 @@ void cmd_move_scratchpad(I3_CMD) { |
54 |
|
55 |
TAILQ_FOREACH(current, &owindows, owindows) { |
56 |
DLOG("matching: %p / %s\n", current->con, current->con->name); |
57 |
- scratchpad_move(current->con); |
58 |
+ scratchpad_move(current->con, false); |
59 |
} |
60 |
|
61 |
cmd_output->needs_tree_render = true; |
b/src/con.c
66 |
@@ -138,7 +138,12 @@ void con_attach(Con *con, Con *parent, bool ignore_focus) { |
67 |
|
68 |
if (con->type == CT_FLOATING_CON) { |
69 |
DLOG("Inserting into floating containers\n"); |
70 |
- TAILQ_INSERT_TAIL(&(parent->floating_head), con, floating_windows); |
71 |
+ Con *floating; |
72 |
+ if ((floating = con_inside_floating(con)) |
73 |
+ && floating->scratchpad_state == SCRATCHPAD_MOVE) |
74 |
+ TAILQ_INSERT_HEAD(&(parent->floating_head), con, floating_windows); |
75 |
+ else |
76 |
+ TAILQ_INSERT_TAIL(&(parent->floating_head), con, floating_windows); |
77 |
} else { |
78 |
if (!ignore_focus) { |
79 |
/* Get the first tiling container in focus stack */ |
b/src/ipc.c
84 |
@@ -176,6 +176,9 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { |
85 |
case SCRATCHPAD_CHANGED: |
86 |
ystr("changed"); |
87 |
break; |
88 |
+ case SCRATCHPAD_MOVE: |
89 |
+ ystr("move"); |
90 |
+ break; |
91 |
} |
92 |
|
93 |
ystr("percent"); |
b/src/load_layout.c
98 |
@@ -254,6 +254,8 @@ static int json_string(void *ctx, const unsigned char *val, unsigned int len) { |
99 |
json_node->scratchpad_state = SCRATCHPAD_FRESH; |
100 |
else if (strcasecmp(buf, "changed") == 0) |
101 |
json_node->scratchpad_state = SCRATCHPAD_CHANGED; |
102 |
+ else if (strcasecmp(buf, "move") == 0) |
103 |
+ json_node->scratchpad_state = SCRATCHPAD_MOVE; |
104 |
free(buf); |
105 |
} |
106 |
} |
b/src/scratchpad.c
111 |
@@ -18,7 +18,7 @@ |
112 |
* Gets called upon the command 'move scratchpad'. |
113 |
* |
114 |
*/ |
115 |
-void scratchpad_move(Con *con) { |
116 |
+void scratchpad_move(Con *con, bool from_show) { |
117 |
if (con->type == CT_WORKSPACE) { |
118 |
LOG("'move scratchpad' used on a workspace \"%s\". Calling it " |
119 |
"recursively on all windows on this workspace.\n", con->name); |
120 |
@@ -26,7 +26,7 @@ void scratchpad_move(Con *con) { |
121 |
current = TAILQ_FIRST(&(con->focus_head)); |
122 |
while (current) { |
123 |
Con *next = TAILQ_NEXT(current, focused); |
124 |
- scratchpad_move(current); |
125 |
+ scratchpad_move(current, from_show); |
126 |
current = next; |
127 |
} |
128 |
return; |
129 |
@@ -57,10 +57,21 @@ void scratchpad_move(Con *con) { |
130 |
con = maybe_floating_con; |
131 |
} |
132 |
|
133 |
+ /* |
134 |
+ * If this is an actual 'scratchpad move' and the window is a scratchpad window, |
135 |
+ * set the scratchpad_state to SCRATCHPAD_MOVE so that it's moved to |
136 |
+ * top of the queue |
137 |
+ */ |
138 |
+ int tmp_state = con->scratchpad_state; |
139 |
+ if (!from_show && con->scratchpad_state != SCRATCHPAD_NONE) |
140 |
+ con->scratchpad_state = SCRATCHPAD_MOVE; |
141 |
+ |
142 |
/* 2: Send the window to the __i3_scratch workspace, mainting its |
143 |
* coordinates and not warping the pointer. */ |
144 |
con_move_to_workspace(con, __i3_scratch, true, true); |
145 |
|
146 |
+ con->scratchpad_state = tmp_state; |
147 |
+ |
148 |
/* 3: If this is the first time this window is used as a scratchpad, we set |
149 |
* the scratchpad_state to SCRATCHPAD_FRESH. The window will then be |
150 |
* adjusted in size according to what the user specifies. */ |
151 |
@@ -97,7 +108,7 @@ void scratchpad_show(Con *con) { |
152 |
(floating = con_inside_floating(focused)) && |
153 |
floating->scratchpad_state != SCRATCHPAD_NONE) { |
154 |
DLOG("Focused window is a scratchpad window, hiding it.\n"); |
155 |
- scratchpad_move(focused); |
156 |
+ scratchpad_move(focused, true); |
157 |
return; |
158 |
} |
159 |
|
160 |
@@ -165,7 +176,7 @@ void scratchpad_show(Con *con) { |
161 |
* it, otherwise we should move it to the active workspace. */ |
162 |
if (current == active) { |
163 |
DLOG("Window is a scratchpad window, hiding it.\n"); |
164 |
- scratchpad_move(con); |
165 |
+ scratchpad_move(con, true); |
166 |
return; |
167 |
} |
168 |
} |
b/testcases/t/185-scratchpad.t
173 |
@@ -398,10 +398,11 @@ sub verify_scratchpad_move_with_visible_scratch_con { |
174 |
# hide window 1 again |
175 |
cmd 'move scratchpad'; |
176 |
|
177 |
- # this should bring up window 2 |
178 |
+ # Since #1035 |
179 |
+ # this should bring up window 1 |
180 |
cmd "workspace $first"; |
181 |
cmd 'scratchpad show'; |
182 |
- is($x->input_focus, $window2->id, "showed the correct scratchpad window"); |
183 |
+ is($x->input_focus, $window1->id, "showed the correct scratchpad window"); |
184 |
} |
185 |
|
186 |
# let's clear the scratchpad first |
187 |
@@ -428,21 +429,91 @@ does_i3_live; |
188 |
# when another window on the same workspace has focus |
189 |
################################################################################ |
190 |
|
191 |
+sub test_scratchpad_show_moves_focus { |
192 |
+ my $ws = fresh_workspace; |
193 |
+ cmd "workspace $ws"; |
194 |
+ |
195 |
+ open_window; |
196 |
+ my $scratch = get_focused($ws); |
197 |
+ cmd 'move scratchpad'; |
198 |
+ cmd 'scratchpad show'; |
199 |
+ |
200 |
+ open_window; |
201 |
+ my $not_scratch = get_focused($ws); |
202 |
+ is(get_focused($ws), $not_scratch, 'not scratch window has focus'); |
203 |
+ |
204 |
+ cmd 'scratchpad show'; |
205 |
+ |
206 |
+ is(get_focused($ws), $scratch, 'scratchpad is focused'); |
207 |
+ |
208 |
+ #Kill the scratchpad window so that it doesn't interfere with other tests |
209 |
+ cmd 'kill'; |
210 |
+ |
211 |
+ is(get_focused($ws), $not_scratch, 'not scratch window has focus'); |
212 |
+} |
213 |
+ |
214 |
clear_scratchpad; |
215 |
-my $ws = fresh_workspace; |
216 |
+is (scalar @{get_ws('__i3_scratch')->{floating_nodes}}, 0, "scratchpad is empty"); |
217 |
+test_scratchpad_show_moves_focus; |
218 |
|
219 |
-open_window; |
220 |
-my $scratch = get_focused($ws); |
221 |
-cmd 'move scratchpad'; |
222 |
-cmd 'scratchpad show'; |
223 |
+################################################################################ |
224 |
+# 13bis: Test the following behaviour : |
225 |
+# With multiple windows in the scratchpad, 'scratchpad_show' cycles between |
226 |
+# them, and 'scratchpad move' on a scratchpad window makes it the next to be |
227 |
+# displayed with 'scratchpad show' |
228 |
+################################################################################ |
229 |
|
230 |
-open_window; |
231 |
-my $not_scratch = get_focused($ws); |
232 |
-is(get_focused($ws), $not_scratch, 'not scratch window has focus'); |
233 |
+sub test_scratchpad_move_to_head { |
234 |
|
235 |
-cmd 'scratchpad show'; |
236 |
+ my $ws = fresh_workspace; |
237 |
+ cmd "workspace $ws"; |
238 |
+ |
239 |
+ my $window1 = open_window; |
240 |
+ cmd 'move scratchpad'; |
241 |
+ |
242 |
+ my $window2 = open_window; |
243 |
+ cmd 'move scratchpad'; |
244 |
|
245 |
-is(get_focused($ws), $scratch, 'scratchpad is focused'); |
246 |
+ # this should bring up window 1 |
247 |
+ cmd 'scratchpad show'; |
248 |
+ |
249 |
+ is(scalar @{get_ws($ws)->{floating_nodes}}, 1, 'one floating node on ws'); |
250 |
+ is($x->input_focus, $window1->id, "showed the correct scratchpad window1"); |
251 |
+ |
252 |
+ #this should hide window 1 |
253 |
+ cmd 'scratchpad show'; |
254 |
+ |
255 |
+ is(scalar @{get_ws($ws)->{floating_nodes}}, 0, 'no floating node on ws'); |
256 |
+ |
257 |
+ #this should show window 2 |
258 |
+ cmd 'scratchpad show'; |
259 |
+ |
260 |
+ is(scalar @{get_ws($ws)->{floating_nodes}}, 1, 'one floating node on ws'); |
261 |
+ is($x->input_focus, $window2->id, "showed the correct scratchpad window2"); |
262 |
+ |
263 |
+ #this should hide window 2 and make it the next window to be displayed |
264 |
+ cmd 'move scratchpad'; |
265 |
+ |
266 |
+ |
267 |
+ is(scalar @{get_ws($ws)->{floating_nodes}}, 0, 'no floating node on ws'); |
268 |
+ |
269 |
+ #this should show window 2 (#1035) |
270 |
+ cmd 'scratchpad show'; |
271 |
+ |
272 |
+ is(scalar @{get_ws($ws)->{floating_nodes}}, 1, 'one floating node on ws'); |
273 |
+ is($x->input_focus, $window2->id, "showed the correct scratchpad window2"); |
274 |
+ |
275 |
+ #clean |
276 |
+ cmd 'kill'; |
277 |
+ cmd 'scratchpad show'; |
278 |
+ cmd 'kill'; |
279 |
+ |
280 |
+ |
281 |
+} |
282 |
+ |
283 |
+clear_scratchpad; |
284 |
+is (scalar @{get_ws('__i3_scratch')->{floating_nodes}}, 0, "scratchpad is empty"); |
285 |
+test_scratchpad_move_to_head; |
286 |
|
287 |
# TODO: make i3bar display *something* when a window on the scratchpad has the urgency hint |
288 |
|