diff options
author | Ryan Dwyer <ryandwyer1@gmail.com> | 2018-07-13 18:06:11 +1000 |
---|---|---|
committer | Ryan Dwyer <ryandwyer1@gmail.com> | 2018-07-14 10:00:39 +1000 |
commit | 5940682f404a0c6bcf35ff838f2799d47d7c9955 (patch) | |
tree | d8c6e43105b8e84dc8061272081a33c5c0dbd7bb /sway/commands/resize.c | |
parent | Implement resize command for floating views (diff) | |
download | sway-5940682f404a0c6bcf35ff838f2799d47d7c9955.tar.gz sway-5940682f404a0c6bcf35ff838f2799d47d7c9955.tar.zst sway-5940682f404a0c6bcf35ff838f2799d47d7c9955.zip |
Implement resize grow|shrink <direction> <amount> or <amount>
Diffstat (limited to 'sway/commands/resize.c')
-rw-r--r-- | sway/commands/resize.c | 329 |
1 files changed, 220 insertions, 109 deletions
diff --git a/sway/commands/resize.c b/sway/commands/resize.c index afb369c2..3f243cf7 100644 --- a/sway/commands/resize.c +++ b/sway/commands/resize.c | |||
@@ -29,6 +29,11 @@ enum resize_axis { | |||
29 | RESIZE_AXIS_INVALID, | 29 | RESIZE_AXIS_INVALID, |
30 | }; | 30 | }; |
31 | 31 | ||
32 | struct resize_amount { | ||
33 | int amount; | ||
34 | enum resize_unit unit; | ||
35 | }; | ||
36 | |||
32 | static enum resize_unit parse_resize_unit(const char *unit) { | 37 | static enum resize_unit parse_resize_unit(const char *unit) { |
33 | if (strcasecmp(unit, "px") == 0) { | 38 | if (strcasecmp(unit, "px") == 0) { |
34 | return RESIZE_UNIT_PX; | 39 | return RESIZE_UNIT_PX; |
@@ -42,6 +47,30 @@ static enum resize_unit parse_resize_unit(const char *unit) { | |||
42 | return RESIZE_UNIT_INVALID; | 47 | return RESIZE_UNIT_INVALID; |
43 | } | 48 | } |
44 | 49 | ||
50 | // Parse arguments such as "10", "10px" or "10 px". | ||
51 | // Returns the number of arguments consumed. | ||
52 | static int parse_resize_amount(int argc, char **argv, | ||
53 | struct resize_amount *amount) { | ||
54 | char *err; | ||
55 | amount->amount = (int)strtol(argv[0], &err, 10); | ||
56 | if (*err) { | ||
57 | // e.g. 10px | ||
58 | amount->unit = parse_resize_unit(err); | ||
59 | return 1; | ||
60 | } | ||
61 | if (argc == 1) { | ||
62 | amount->unit = RESIZE_UNIT_DEFAULT; | ||
63 | return 1; | ||
64 | } | ||
65 | // Try the second argument | ||
66 | amount->unit = parse_resize_unit(argv[1]); | ||
67 | if (amount->unit == RESIZE_UNIT_INVALID) { | ||
68 | amount->unit = RESIZE_UNIT_DEFAULT; | ||
69 | return 1; | ||
70 | } | ||
71 | return 2; | ||
72 | } | ||
73 | |||
45 | static enum resize_axis parse_resize_axis(const char *axis) { | 74 | static enum resize_axis parse_resize_axis(const char *axis) { |
46 | if (strcasecmp(axis, "width") == 0 || strcasecmp(axis, "horizontal") == 0) { | 75 | if (strcasecmp(axis, "width") == 0 || strcasecmp(axis, "horizontal") == 0) { |
47 | return RESIZE_AXIS_HORIZONTAL; | 76 | return RESIZE_AXIS_HORIZONTAL; |
@@ -202,36 +231,39 @@ static void resize_tiled(int amount, enum resize_axis axis) { | |||
202 | arrange_and_commit(parent->parent); | 231 | arrange_and_commit(parent->parent); |
203 | } | 232 | } |
204 | 233 | ||
205 | static void resize_floating(int amount, enum resize_axis axis) { | 234 | /** |
235 | * Implement `resize <grow|shrink>` for a floating container. | ||
236 | */ | ||
237 | static struct cmd_results *resize_adjust_floating(enum resize_axis axis, | ||
238 | struct resize_amount *amount) { | ||
206 | struct sway_container *con = config->handler_context.current_container; | 239 | struct sway_container *con = config->handler_context.current_container; |
207 | int grow_x = 0, grow_y = 0; | 240 | int grow_x = 0, grow_y = 0; |
208 | int grow_width = 0, grow_height = 0; | 241 | int grow_width = 0, grow_height = 0; |
209 | switch (axis) { | 242 | switch (axis) { |
210 | case RESIZE_AXIS_HORIZONTAL: | 243 | case RESIZE_AXIS_HORIZONTAL: |
211 | grow_x = -amount / 2; | 244 | grow_x = -amount->amount / 2; |
212 | grow_width = amount; | 245 | grow_width = amount->amount; |
213 | break; | 246 | break; |
214 | case RESIZE_AXIS_VERTICAL: | 247 | case RESIZE_AXIS_VERTICAL: |
215 | grow_y = -amount / 2; | 248 | grow_y = -amount->amount / 2; |
216 | grow_height = amount; | 249 | grow_height = amount->amount; |
217 | break; | 250 | break; |
218 | case RESIZE_AXIS_UP: | 251 | case RESIZE_AXIS_UP: |
219 | grow_y = -amount; | 252 | grow_y = -amount->amount; |
220 | grow_height = amount; | 253 | grow_height = amount->amount; |
221 | break; | 254 | break; |
222 | case RESIZE_AXIS_LEFT: | 255 | case RESIZE_AXIS_LEFT: |
223 | grow_x = -amount; | 256 | grow_x = -amount->amount; |
224 | grow_width = amount; | 257 | grow_width = amount->amount; |
225 | break; | 258 | break; |
226 | case RESIZE_AXIS_DOWN: | 259 | case RESIZE_AXIS_DOWN: |
227 | grow_height = amount; | 260 | grow_height = amount->amount; |
228 | break; | 261 | break; |
229 | case RESIZE_AXIS_RIGHT: | 262 | case RESIZE_AXIS_RIGHT: |
230 | grow_width = amount; | 263 | grow_width = amount->amount; |
231 | break; | 264 | break; |
232 | case RESIZE_AXIS_INVALID: | 265 | case RESIZE_AXIS_INVALID: |
233 | sway_assert(false, "Didn't expect RESIZE_AXIS_INVALID"); | 266 | return cmd_results_new(CMD_INVALID, "resize", "Invalid axis/direction"); |
234 | return; | ||
235 | } | 267 | } |
236 | con->x += grow_x; | 268 | con->x += grow_x; |
237 | con->y += grow_y; | 269 | con->y += grow_y; |
@@ -247,80 +279,64 @@ static void resize_floating(int amount, enum resize_axis axis) { | |||
247 | } | 279 | } |
248 | 280 | ||
249 | arrange_and_commit(con); | 281 | arrange_and_commit(con); |
282 | |||
283 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
250 | } | 284 | } |
251 | 285 | ||
252 | static void resize(int amount, enum resize_axis axis, enum resize_unit unit) { | 286 | /** |
287 | * Implement `resize <grow|shrink>` for a tiled container. | ||
288 | */ | ||
289 | static struct cmd_results *resize_adjust_tiled(enum resize_axis axis, | ||
290 | struct resize_amount *amount) { | ||
253 | struct sway_container *current = config->handler_context.current_container; | 291 | struct sway_container *current = config->handler_context.current_container; |
254 | if (container_is_floating(current)) { | ||
255 | return resize_floating(amount, axis); | ||
256 | } | ||
257 | 292 | ||
258 | if (unit == RESIZE_UNIT_DEFAULT) { | 293 | if (amount->unit == RESIZE_UNIT_DEFAULT) { |
259 | unit = RESIZE_UNIT_PPT; | 294 | amount->unit = RESIZE_UNIT_PPT; |
260 | } | 295 | } |
261 | if (unit == RESIZE_UNIT_PPT) { | 296 | if (amount->unit == RESIZE_UNIT_PPT) { |
262 | float pct = amount / 100.0f; | 297 | float pct = amount->amount / 100.0f; |
298 | // TODO: Make left/right/up/down resize in that direction? | ||
263 | switch (axis) { | 299 | switch (axis) { |
300 | case RESIZE_AXIS_LEFT: | ||
301 | case RESIZE_AXIS_RIGHT: | ||
264 | case RESIZE_AXIS_HORIZONTAL: | 302 | case RESIZE_AXIS_HORIZONTAL: |
265 | amount = (float)current->width * pct; | 303 | amount->amount = (float)current->width * pct; |
266 | break; | 304 | break; |
305 | case RESIZE_AXIS_UP: | ||
306 | case RESIZE_AXIS_DOWN: | ||
267 | case RESIZE_AXIS_VERTICAL: | 307 | case RESIZE_AXIS_VERTICAL: |
268 | amount = (float)current->height * pct; | 308 | amount->amount = (float)current->height * pct; |
269 | break; | 309 | break; |
270 | default: | 310 | case RESIZE_AXIS_INVALID: |
271 | sway_assert(0, "invalid resize axis"); | 311 | return cmd_results_new(CMD_INVALID, "resize", |
272 | return; | 312 | "Invalid resize axis/direction"); |
273 | } | 313 | } |
274 | } | 314 | } |
275 | 315 | ||
276 | return resize_tiled(amount, axis); | 316 | resize_tiled(amount->amount, axis); |
317 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
277 | } | 318 | } |
278 | 319 | ||
279 | // resize set <width> [px|ppt] <height> [px|ppt] | 320 | /** |
280 | static struct cmd_results *cmd_resize_set(int argc, char **argv) { | 321 | * Implement `resize set` for a tiled container. |
281 | struct cmd_results *error; | 322 | */ |
282 | if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) { | 323 | static struct cmd_results *resize_set_tiled(struct sway_container *con, |
283 | return error; | 324 | struct resize_amount *width, struct resize_amount *height) { |
284 | } | 325 | return cmd_results_new(CMD_INVALID, "resize", |
285 | struct sway_container *con = config->handler_context.current_container; | 326 | "'resize set' is not implemented for tiled views"); |
286 | if (!container_is_floating(con)) { | 327 | } |
287 | return cmd_results_new(CMD_INVALID, "resize", | ||
288 | "resize set is not currently supported for tiled containers"); | ||
289 | } | ||
290 | const char *usage = "Expected 'resize set <width> <height>'"; | ||
291 | |||
292 | char *err; | ||
293 | size_t width = (int)strtol(*argv, &err, 10); | ||
294 | if (*err) { | ||
295 | // e.g. `resize set width 500px` | ||
296 | enum resize_unit unit = parse_resize_unit(err); | ||
297 | if (unit == RESIZE_UNIT_INVALID) { | ||
298 | return cmd_results_new(CMD_INVALID, "resize", usage); | ||
299 | } | ||
300 | } | ||
301 | --argc; ++argv; | ||
302 | if (parse_resize_unit(argv[0]) != RESIZE_UNIT_INVALID) { | ||
303 | --argc; ++argv; | ||
304 | } | ||
305 | |||
306 | if (!argc) { | ||
307 | return cmd_results_new(CMD_INVALID, "resize", usage); | ||
308 | } | ||
309 | size_t height = (int)strtol(*argv, &err, 10); | ||
310 | if (*err) { | ||
311 | // e.g. `resize set height 500px` | ||
312 | enum resize_unit unit = parse_resize_unit(err); | ||
313 | if (unit == RESIZE_UNIT_INVALID) { | ||
314 | return cmd_results_new(CMD_INVALID, "resize", usage); | ||
315 | } | ||
316 | } | ||
317 | 328 | ||
318 | int grow_width = width - con->width; | 329 | /** |
319 | int grow_height = height - con->height; | 330 | * Implement `resize set` for a floating container. |
331 | */ | ||
332 | static struct cmd_results *resize_set_floating(struct sway_container *con, | ||
333 | struct resize_amount *width, struct resize_amount *height) { | ||
334 | int grow_width = width->amount - con->width; | ||
335 | int grow_height = height->amount - con->height; | ||
320 | con->x -= grow_width / 2; | 336 | con->x -= grow_width / 2; |
321 | con->y -= grow_height / 2; | 337 | con->y -= grow_height / 2; |
322 | con->width = width; | 338 | con->width = width->amount; |
323 | con->height = height; | 339 | con->height = height->amount; |
324 | 340 | ||
325 | if (con->type == C_VIEW) { | 341 | if (con->type == C_VIEW) { |
326 | struct sway_view *view = con->sway_view; | 342 | struct sway_view *view = con->sway_view; |
@@ -335,73 +351,168 @@ static struct cmd_results *cmd_resize_set(int argc, char **argv) { | |||
335 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 351 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
336 | } | 352 | } |
337 | 353 | ||
338 | struct cmd_results *cmd_resize(int argc, char **argv) { | 354 | /** |
339 | struct sway_container *current = config->handler_context.current_container; | 355 | * resize set <args> |
340 | if (!current) { | 356 | * |
341 | return cmd_results_new(CMD_INVALID, "resize", "Cannot resize nothing"); | 357 | * args: <width> [px|ppt] <height> [px|ppt] |
342 | } | 358 | */ |
343 | if (current->type != C_VIEW && current->type != C_CONTAINER) { | 359 | static struct cmd_results *cmd_resize_set(int argc, char **argv) { |
344 | return cmd_results_new(CMD_INVALID, "resize", | ||
345 | "Can only resize views/containers"); | ||
346 | } | ||
347 | |||
348 | struct cmd_results *error; | 360 | struct cmd_results *error; |
349 | if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) { | 361 | if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) { |
350 | return error; | 362 | return error; |
351 | } | 363 | } |
364 | const char *usage = "Expected 'resize set <width> <height>'"; | ||
352 | 365 | ||
353 | if (strcasecmp(argv[0], "set") == 0) { | 366 | // Width |
354 | return cmd_resize_set(argc - 1, &argv[1]); | 367 | struct resize_amount width; |
368 | int num_consumed_args = parse_resize_amount(argc, argv, &width); | ||
369 | argc -= num_consumed_args; | ||
370 | argv += num_consumed_args; | ||
371 | if (width.unit == RESIZE_UNIT_INVALID) { | ||
372 | return cmd_results_new(CMD_INVALID, "resize", usage); | ||
373 | } | ||
374 | if (!argc) { | ||
375 | return cmd_results_new(CMD_INVALID, "resize", usage); | ||
355 | } | 376 | } |
356 | 377 | ||
357 | const char *usage = "Expected 'resize <shrink|grow> " | 378 | // Height |
358 | "<width|height|up|down|left|right> [<amount>] [px|ppt]'"; | 379 | struct resize_amount height; |
359 | 380 | num_consumed_args = parse_resize_amount(argc, argv, &height); | |
360 | int multiplier = 0; | 381 | argc -= num_consumed_args; |
361 | if (strcasecmp(*argv, "grow") == 0) { | 382 | argv += num_consumed_args; |
362 | multiplier = 1; | 383 | if (height.unit == RESIZE_UNIT_INVALID) { |
363 | } else if (strcasecmp(*argv, "shrink") == 0) { | ||
364 | multiplier = -1; | ||
365 | } else { | ||
366 | return cmd_results_new(CMD_INVALID, "resize", usage); | 384 | return cmd_results_new(CMD_INVALID, "resize", usage); |
367 | } | 385 | } |
368 | --argc; ++argv; | ||
369 | 386 | ||
387 | // If 0, don't resize that dimension | ||
388 | struct sway_container *con = config->handler_context.current_container; | ||
389 | if (width.amount <= 0) { | ||
390 | width.amount = con->width; | ||
391 | } | ||
392 | if (height.amount <= 0) { | ||
393 | height.amount = con->height; | ||
394 | } | ||
395 | |||
396 | if (container_is_floating(con)) { | ||
397 | return resize_set_floating(con, &width, &height); | ||
398 | } | ||
399 | return resize_set_tiled(con, &width, &height); | ||
400 | } | ||
401 | |||
402 | /** | ||
403 | * resize <grow|shrink> <args> | ||
404 | * | ||
405 | * args: <direction> | ||
406 | * args: <direction> <amount> <unit> | ||
407 | * args: <direction> <amount> <unit> or <amount> <other_unit> | ||
408 | */ | ||
409 | static struct cmd_results *cmd_resize_adjust(int argc, char **argv, | ||
410 | int multiplier) { | ||
411 | const char *usage = "Expected 'resize grow|shrink <direction> " | ||
412 | "[<amount> px|ppt [or <amount> px|ppt]]'"; | ||
370 | enum resize_axis axis = parse_resize_axis(*argv); | 413 | enum resize_axis axis = parse_resize_axis(*argv); |
371 | if (axis == RESIZE_AXIS_INVALID) { | 414 | if (axis == RESIZE_AXIS_INVALID) { |
372 | return cmd_results_new(CMD_INVALID, "resize", usage); | 415 | return cmd_results_new(CMD_INVALID, "resize", usage); |
373 | } | 416 | } |
374 | --argc; ++argv; | 417 | --argc; ++argv; |
375 | 418 | ||
376 | int amount = 10; // Default amount | 419 | // First amount |
377 | enum resize_unit unit = RESIZE_UNIT_DEFAULT; | 420 | struct resize_amount first_amount; |
378 | |||
379 | if (argc) { | 421 | if (argc) { |
380 | char *err; | 422 | int num_consumed_args = parse_resize_amount(argc, argv, &first_amount); |
381 | amount = (int)strtol(*argv, &err, 10); | 423 | argc -= num_consumed_args; |
382 | if (*err) { | 424 | argv += num_consumed_args; |
383 | // e.g. `resize grow width 10px` | 425 | if (first_amount.unit == RESIZE_UNIT_INVALID) { |
384 | unit = parse_resize_unit(err); | 426 | return cmd_results_new(CMD_INVALID, "resize", usage); |
385 | if (unit == RESIZE_UNIT_INVALID) { | ||
386 | return cmd_results_new(CMD_INVALID, "resize", usage); | ||
387 | } | ||
388 | } | 427 | } |
389 | --argc; ++argv; | 428 | } else { |
429 | first_amount.amount = 10; | ||
430 | first_amount.unit = RESIZE_UNIT_DEFAULT; | ||
390 | } | 431 | } |
391 | 432 | ||
433 | // "or" | ||
392 | if (argc) { | 434 | if (argc) { |
393 | unit = parse_resize_unit(*argv); | 435 | if (strcmp(*argv, "or") != 0) { |
394 | if (unit == RESIZE_UNIT_INVALID) { | ||
395 | return cmd_results_new(CMD_INVALID, "resize", usage); | 436 | return cmd_results_new(CMD_INVALID, "resize", usage); |
396 | } | 437 | } |
397 | --argc; ++argv; | 438 | --argc; ++argv; |
398 | } | 439 | } |
399 | 440 | ||
441 | // Second amount | ||
442 | struct resize_amount second_amount; | ||
400 | if (argc) { | 443 | if (argc) { |
401 | // Provied too many args, the bastard | 444 | int num_consumed_args = parse_resize_amount(argc, argv, &second_amount); |
402 | return cmd_results_new(CMD_INVALID, "resize", usage); | 445 | argc -= num_consumed_args; |
446 | argv += num_consumed_args; | ||
447 | if (second_amount.unit == RESIZE_UNIT_INVALID) { | ||
448 | return cmd_results_new(CMD_INVALID, "resize", usage); | ||
449 | } | ||
450 | } else { | ||
451 | second_amount.unit = RESIZE_UNIT_INVALID; | ||
403 | } | 452 | } |
404 | 453 | ||
405 | resize(amount * multiplier, axis, unit); | 454 | first_amount.amount *= multiplier; |
406 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 455 | second_amount.amount *= multiplier; |
456 | |||
457 | struct sway_container *con = config->handler_context.current_container; | ||
458 | if (container_is_floating(con)) { | ||
459 | // Floating containers can only resize in px. Choose an amount which | ||
460 | // uses px, with fallback to an amount that specified no unit. | ||
461 | if (first_amount.unit == RESIZE_UNIT_PX) { | ||
462 | return resize_adjust_floating(axis, &first_amount); | ||
463 | } else if (second_amount.unit == RESIZE_UNIT_PX) { | ||
464 | return resize_adjust_floating(axis, &second_amount); | ||
465 | } else if (first_amount.unit == RESIZE_UNIT_DEFAULT) { | ||
466 | return resize_adjust_floating(axis, &first_amount); | ||
467 | } else if (second_amount.unit == RESIZE_UNIT_DEFAULT) { | ||
468 | return resize_adjust_floating(axis, &second_amount); | ||
469 | } else { | ||
470 | return cmd_results_new(CMD_INVALID, "resize", | ||
471 | "Floating containers cannot use ppt measurements"); | ||
472 | } | ||
473 | } | ||
474 | |||
475 | // For tiling, prefer ppt -> default -> px | ||
476 | if (first_amount.unit == RESIZE_UNIT_PPT) { | ||
477 | return resize_adjust_tiled(axis, &first_amount); | ||
478 | } else if (second_amount.unit == RESIZE_UNIT_PPT) { | ||
479 | return resize_adjust_tiled(axis, &second_amount); | ||
480 | } else if (first_amount.unit == RESIZE_UNIT_DEFAULT) { | ||
481 | return resize_adjust_tiled(axis, &first_amount); | ||
482 | } else if (second_amount.unit == RESIZE_UNIT_DEFAULT) { | ||
483 | return resize_adjust_tiled(axis, &second_amount); | ||
484 | } else { | ||
485 | return resize_adjust_tiled(axis, &first_amount); | ||
486 | } | ||
487 | } | ||
488 | |||
489 | struct cmd_results *cmd_resize(int argc, char **argv) { | ||
490 | struct sway_container *current = config->handler_context.current_container; | ||
491 | if (!current) { | ||
492 | return cmd_results_new(CMD_INVALID, "resize", "Cannot resize nothing"); | ||
493 | } | ||
494 | if (current->type != C_VIEW && current->type != C_CONTAINER) { | ||
495 | return cmd_results_new(CMD_INVALID, "resize", | ||
496 | "Can only resize views/containers"); | ||
497 | } | ||
498 | |||
499 | struct cmd_results *error; | ||
500 | if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) { | ||
501 | return error; | ||
502 | } | ||
503 | |||
504 | if (strcasecmp(argv[0], "set") == 0) { | ||
505 | return cmd_resize_set(argc - 1, &argv[1]); | ||
506 | } | ||
507 | if (strcasecmp(argv[0], "grow") == 0) { | ||
508 | return cmd_resize_adjust(argc - 1, &argv[1], 1); | ||
509 | } | ||
510 | if (strcasecmp(argv[0], "shrink") == 0) { | ||
511 | return cmd_resize_adjust(argc - 1, &argv[1], -1); | ||
512 | } | ||
513 | |||
514 | const char *usage = "Expected 'resize <shrink|grow> " | ||
515 | "<width|height|up|down|left|right> [<amount>] [px|ppt]'"; | ||
516 | |||
517 | return cmd_results_new(CMD_INVALID, "resize", usage); | ||
407 | } | 518 | } |