aboutsummaryrefslogtreecommitdiffstats
path: root/sway/layout.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/layout.c')
-rw-r--r--sway/layout.c344
1 files changed, 276 insertions, 68 deletions
diff --git a/sway/layout.c b/sway/layout.c
index 0b498937..3e550927 100644
--- a/sway/layout.c
+++ b/sway/layout.c
@@ -3,7 +3,6 @@
3#include <math.h> 3#include <math.h>
4#include <wlc/wlc.h> 4#include <wlc/wlc.h>
5#include "extensions.h" 5#include "extensions.h"
6#include "layout.h"
7#include "log.h" 6#include "log.h"
8#include "list.h" 7#include "list.h"
9#include "config.h" 8#include "config.h"
@@ -13,6 +12,7 @@
13#include "output.h" 12#include "output.h"
14#include "ipc-server.h" 13#include "ipc-server.h"
15#include "border.h" 14#include "border.h"
15#include "layout.h"
16 16
17swayc_t root_container; 17swayc_t root_container;
18list_t *scratchpad; 18list_t *scratchpad;
@@ -244,7 +244,9 @@ void move_container(swayc_t *container, enum movement_direction dir) {
244 while (true) { 244 while (true) {
245 sway_log(L_DEBUG, "container:%p, parent:%p, child %p,", 245 sway_log(L_DEBUG, "container:%p, parent:%p, child %p,",
246 container,parent,child); 246 container,parent,child);
247 if (parent->layout == layout) { 247 if (parent->layout == layout
248 || (parent->layout == L_TABBED && layout == L_HORIZ)
249 || (parent->layout == L_STACKED && layout == L_VERT)) {
248 int diff; 250 int diff;
249 // If it has ascended (parent has moved up), no container is removed 251 // If it has ascended (parent has moved up), no container is removed
250 // so insert it at index, or index+1. 252 // so insert it at index, or index+1.
@@ -264,9 +266,11 @@ void move_container(swayc_t *container, enum movement_direction dir) {
264 // Move container into sibling container 266 // Move container into sibling container
265 if (child->type == C_CONTAINER) { 267 if (child->type == C_CONTAINER) {
266 parent = child; 268 parent = child;
267 // Insert it in first/last if matching layout,otherwise 269 // Insert it in first/last if matching layout, otherwise
268 // inesrt it next to focused container 270 // inesrt it next to focused container
269 if (parent->layout == layout) { 271 if (parent->layout == layout
272 || (parent->layout == L_TABBED && layout == L_HORIZ)
273 || (parent->layout == L_STACKED && layout == L_VERT)) {
270 desired = (diff < 0) * parent->children->length; 274 desired = (diff < 0) * parent->children->length;
271 } else { 275 } else {
272 desired = index_child(child->focused); 276 desired = index_child(child->focused);
@@ -299,8 +303,6 @@ void move_container(swayc_t *container, enum movement_direction dir) {
299 child = parent; 303 child = parent;
300 parent = child->parent; 304 parent = child->parent;
301 } 305 }
302 // Dirty hack to fix a certain case
303 arrange_windows(parent, -1, -1);
304 arrange_windows(parent->parent, -1, -1); 306 arrange_windows(parent->parent, -1, -1);
305 set_focused_container_for(parent->parent, container); 307 set_focused_container_for(parent->parent, container);
306} 308}
@@ -380,16 +382,14 @@ static void adjust_border_geometry(swayc_t *c, struct wlc_geometry *g,
380 g->size.w += left + right; 382 g->size.w += left + right;
381 if (g->origin.x - left < 0) { 383 if (g->origin.x - left < 0) {
382 g->size.w += g->origin.x - left; 384 g->size.w += g->origin.x - left;
383 } 385 } else if (g->origin.x + g->size.w - right > res->w) {
384 else if (g->origin.x + g->size.w - right > res->w) {
385 g->size.w = res->w - g->origin.x + right; 386 g->size.w = res->w - g->origin.x + right;
386 } 387 }
387 388
388 g->size.h += top + bottom; 389 g->size.h += top + bottom;
389 if (g->origin.y - top < 0) { 390 if (g->origin.y - top < 0) {
390 g->size.h += g->origin.y - top; 391 g->size.h += g->origin.y - top;
391 } 392 } else if (g->origin.y + g->size.h - top > res->h) {
392 else if (g->origin.y + g->size.h - top > res->h) {
393 g->size.h = res->h - g->origin.y + top; 393 g->size.h = res->h - g->origin.y + top;
394 } 394 }
395 395
@@ -421,11 +421,11 @@ static void update_border_geometry_floating(swayc_t *c, struct wlc_geometry *geo
421 421
422 struct wlc_geometry title_bar = { 422 struct wlc_geometry title_bar = {
423 .origin = { 423 .origin = {
424 .x = g.origin.x, 424 .x = c->actual_geometry.origin.x - c->border_thickness,
425 .y = g.origin.y 425 .y = c->actual_geometry.origin.y - title_bar_height
426 }, 426 },
427 .size = { 427 .size = {
428 .w = g.size.w, 428 .w = c->actual_geometry.size.w + (2 * c->border_thickness),
429 .h = title_bar_height 429 .h = title_bar_height
430 } 430 }
431 }; 431 };
@@ -440,10 +440,42 @@ static void update_border_geometry_floating(swayc_t *c, struct wlc_geometry *geo
440 update_view_border(c); 440 update_view_border(c);
441} 441}
442 442
443void update_geometry(swayc_t *container) { 443void update_layout_geometry(swayc_t *parent, enum swayc_layouts prev_layout) {
444 if (container->type != C_VIEW) { 444 switch (parent->layout) {
445 return; 445 case L_TABBED:
446 case L_STACKED:
447 if (prev_layout != L_TABBED && prev_layout != L_STACKED) {
448 // cache current geometry for all non-float children
449 int i;
450 for (i = 0; i < parent->children->length; ++i) {
451 swayc_t *child = parent->children->items[i];
452 child->cached_geometry.origin.x = child->x;
453 child->cached_geometry.origin.y = child->y;
454 child->cached_geometry.size.w = child->width;
455 child->cached_geometry.size.h = child->height;
456 }
457 }
458 break;
459 default:
460 if (prev_layout == L_TABBED || prev_layout == L_STACKED) {
461 // recover cached geometry for all non-float children
462 int i;
463 for (i = 0; i < parent->children->length; ++i) {
464 swayc_t *child = parent->children->items[i];
465 // only recoverer cached geometry if non-zero
466 if (!wlc_geometry_equals(&child->cached_geometry, &wlc_geometry_zero)) {
467 child->x = child->cached_geometry.origin.x;
468 child->y = child->cached_geometry.origin.y;
469 child->width = child->cached_geometry.size.w;
470 child->height = child->cached_geometry.size.h;
471 }
472 }
473 }
474 break;
446 } 475 }
476}
477
478static int update_gap_geometry(swayc_t *container, struct wlc_geometry *g) {
447 swayc_t *ws = swayc_parent_by_type(container, C_WORKSPACE); 479 swayc_t *ws = swayc_parent_by_type(container, C_WORKSPACE);
448 swayc_t *op = ws->parent; 480 swayc_t *op = ws->parent;
449 int gap = container->is_floating ? 0 : swayc_gap(container); 481 int gap = container->is_floating ? 0 : swayc_gap(container);
@@ -453,16 +485,63 @@ void update_geometry(swayc_t *container) {
453 gap -= 1; 485 gap -= 1;
454 } 486 }
455 487
488 g->origin.x = container->x + gap/2 < op->width ? container->x + gap/2 : op->width-1;
489 g->origin.y = container->y + gap/2 < op->height ? container->y + gap/2 : op->height-1;
490 g->size.w = container->width > gap ? container->width - gap : 1;
491 g->size.h = container->height > gap ? container->height - gap : 1;
492
493 if ((!config->edge_gaps && gap > 0) || (config->smart_gaps && ws->children->length == 1)) {
494 // Remove gap against the workspace edges. Because a pixel is not
495 // divisable, depending on gap size and the number of siblings our view
496 // might be at the workspace edge without being exactly so (thus test
497 // with gap, and align correctly).
498 if (container->x - gap <= ws->x) {
499 g->origin.x = ws->x;
500 g->size.w = container->width - gap/2;
501 }
502 if (container->y - gap <= ws->y) {
503 g->origin.y = ws->y;
504 g->size.h = container->height - gap/2;
505 }
506 if (container->x + container->width + gap >= ws->x + ws->width) {
507 g->size.w = ws->x + ws->width - g->origin.x;
508 }
509 if (container->y + container->height + gap >= ws->y + ws->height) {
510 g->size.h = ws->y + ws->height - g->origin.y;
511 }
512 }
513
514 return gap;
515}
516
517void update_geometry(swayc_t *container) {
518 if (container->type != C_VIEW && container->type != C_CONTAINER) {
519 return;
520 }
521
522 swayc_t *ws = swayc_parent_by_type(container, C_WORKSPACE);
523 swayc_t *op = ws->parent;
524 swayc_t *parent = container->parent;
525
456 struct wlc_geometry geometry = { 526 struct wlc_geometry geometry = {
457 .origin = { 527 .origin = {
458 .x = container->x + gap/2 < op->width ? container->x + gap/2 : op->width-1, 528 .x = container->x < op->width ? container->x : op->width-1,
459 .y = container->y + gap/2 < op->height ? container->y + gap/2 : op->height-1 529 .y = container->y < op->height ? container->y : op->height-1
460 }, 530 },
461 .size = { 531 .size = {
462 .w = container->width > gap ? container->width - gap : 1, 532 .w = container->width,
463 .h = container->height > gap ? container->height - gap : 1, 533 .h = container->height,
464 } 534 }
465 }; 535 };
536
537 int gap = 0;
538
539 // apply inner gaps to non-tabbed/stacked containers
540 swayc_t *p = swayc_tabbed_stacked_parent(container);
541 if (p == NULL) {
542 gap = update_gap_geometry(container, &geometry);
543 }
544
466 if (swayc_is_fullscreen(container)) { 545 if (swayc_is_fullscreen(container)) {
467 swayc_t *output = swayc_parent_by_type(container, C_OUTPUT); 546 swayc_t *output = swayc_parent_by_type(container, C_OUTPUT);
468 const struct wlc_size *size = wlc_output_get_resolution(output->handle); 547 const struct wlc_size *size = wlc_output_get_resolution(output->handle);
@@ -473,28 +552,7 @@ void update_geometry(swayc_t *container) {
473 if (op->focused == ws) { 552 if (op->focused == ws) {
474 wlc_view_bring_to_front(container->handle); 553 wlc_view_bring_to_front(container->handle);
475 } 554 }
476 } else if ((!config->edge_gaps && gap > 0) || (config->smart_gaps && ws->children->length == 1)) {
477 // Remove gap against the workspace edges. Because a pixel is not
478 // divisable, depending on gap size and the number of siblings our view
479 // might be at the workspace edge without being exactly so (thus test
480 // with gap, and align correctly).
481 if (container->x - gap <= ws->x) {
482 geometry.origin.x = ws->x;
483 geometry.size.w = container->width - gap/2;
484 }
485 if (container->y - gap <= ws->y) {
486 geometry.origin.y = ws->y;
487 geometry.size.h = container->height - gap/2;
488 }
489 if (container->x + container->width + gap >= ws->x + ws->width) {
490 geometry.size.w = ws->x + ws->width - geometry.origin.x;
491 }
492 if (container->y + container->height + gap >= ws->y + ws->height) {
493 geometry.size.h = ws->y + ws->height - geometry.origin.y;
494 }
495 }
496 555
497 if (swayc_is_fullscreen(container)) {
498 container->border_geometry = wlc_geometry_zero; 556 container->border_geometry = wlc_geometry_zero;
499 container->title_bar_geometry = wlc_geometry_zero; 557 container->title_bar_geometry = wlc_geometry_zero;
500 } else if (container->is_floating) { // allocate border for floating window 558 } else if (container->is_floating) { // allocate border for floating window
@@ -533,42 +591,106 @@ void update_geometry(swayc_t *container) {
533 } 591 }
534 } 592 }
535 593
536 switch (container->border_type) { 594 int title_bar_height = config->font_height + 4; //borders + padding
537 case B_NONE: 595
538 break; 596 if (parent->layout == L_TABBED) {
539 case B_PIXEL: 597 int i, x = 0, w, l, r;
598 l = parent->children->length;
599 w = geometry.size.w / l;
600 r = geometry.size.w % l;
601 for (i = 0; i < parent->children->length; ++i) {
602 swayc_t *view = parent->children->items[i];
603 if (view == container) {
604 x = w * i;
605 if (i == l - 1) {
606 w += r;
607 }
608 break;
609 }
610 }
611
612 struct wlc_geometry title_bar = {
613 .origin = {
614 .x = container->border_geometry.origin.x + x,
615 .y = container->border_geometry.origin.y
616 },
617 .size = {
618 .w = w,
619 .h = title_bar_height
620 }
621 };
540 geometry.origin.x += border_left; 622 geometry.origin.x += border_left;
541 geometry.origin.y += border_top; 623 geometry.origin.y += title_bar.size.h;
542 geometry.size.w -= (border_left + border_right); 624 geometry.size.w -= (border_left + border_right);
543 geometry.size.h -= (border_top + border_bottom); 625 geometry.size.h -= (border_bottom + title_bar.size.h);
544 break; 626 container->title_bar_geometry = title_bar;
545 case B_NORMAL: 627 } else if (parent->layout == L_STACKED) {
546 { 628 int i, y;
547 struct wlc_geometry title_bar = { 629 for (i = 0; i < parent->children->length; ++i) {
548 .origin = { 630 swayc_t *view = parent->children->items[i];
549 .x = container->border_geometry.origin.x, 631 if (view == container) {
550 .y = container->border_geometry.origin.y 632 y = title_bar_height * i;
551 }, 633 }
552 .size = { 634 }
553 .w = container->border_geometry.size.w, 635
554 .h = config->font_height + 4 // borders + padding 636 struct wlc_geometry title_bar = {
555 } 637 .origin = {
556 }; 638 .x = container->border_geometry.origin.x,
639 .y = container->border_geometry.origin.y + y
640 },
641 .size = {
642 .w = container->border_geometry.size.w,
643 .h = title_bar_height
644 }
645 };
646 title_bar_height = title_bar_height * parent->children->length;
647 geometry.origin.x += border_left;
648 geometry.origin.y += title_bar_height;
649 geometry.size.w -= (border_left + border_right);
650 geometry.size.h -= (border_bottom + title_bar_height);
651 container->title_bar_geometry = title_bar;
652 } else {
653 switch (container->border_type) {
654 case B_NONE:
655 break;
656 case B_PIXEL:
557 geometry.origin.x += border_left; 657 geometry.origin.x += border_left;
558 geometry.origin.y += title_bar.size.h; 658 geometry.origin.y += border_top;
559 geometry.size.w -= (border_left + border_right); 659 geometry.size.w -= (border_left + border_right);
560 geometry.size.h -= (border_bottom + title_bar.size.h); 660 geometry.size.h -= (border_top + border_bottom);
561 container->title_bar_geometry = title_bar;
562 break; 661 break;
662 case B_NORMAL:
663 {
664 struct wlc_geometry title_bar = {
665 .origin = {
666 .x = container->border_geometry.origin.x,
667 .y = container->border_geometry.origin.y
668 },
669 .size = {
670 .w = container->border_geometry.size.w,
671 .h = title_bar_height
672 }
673 };
674 geometry.origin.x += border_left;
675 geometry.origin.y += title_bar.size.h;
676 geometry.size.w -= (border_left + border_right);
677 geometry.size.h -= (border_bottom + title_bar.size.h);
678 container->title_bar_geometry = title_bar;
679 break;
680 }
563 } 681 }
564 } 682 }
565 683
566 container->actual_geometry = geometry; 684 container->actual_geometry = geometry;
567 685
568 update_view_border(container); 686 if (container->type == C_VIEW) {
687 update_view_border(container);
688 }
569 } 689 }
570 690
571 wlc_view_set_geometry(container->handle, 0, &geometry); 691 if (container->type == C_VIEW) {
692 wlc_view_set_geometry(container->handle, 0, &geometry);
693 }
572} 694}
573 695
574static void arrange_windows_r(swayc_t *container, double width, double height) { 696static void arrange_windows_r(swayc_t *container, double width, double height) {
@@ -648,7 +770,7 @@ static void arrange_windows_r(swayc_t *container, double width, double height) {
648 height = container->height = height - gap * 2; 770 height = container->height = height - gap * 2;
649 sway_log(L_DEBUG, "Arranging workspace '%s' at %f, %f", container->name, container->x, container->y); 771 sway_log(L_DEBUG, "Arranging workspace '%s' at %f, %f", container->name, container->x, container->y);
650 } 772 }
651 // children are properly handled below 773 // children are properly handled below
652 break; 774 break;
653 case C_VIEW: 775 case C_VIEW:
654 { 776 {
@@ -664,6 +786,33 @@ static void arrange_windows_r(swayc_t *container, double width, double height) {
664 container->height = height; 786 container->height = height;
665 x = container->x; 787 x = container->x;
666 y = container->y; 788 y = container->y;
789
790 // add gaps to top level tapped/stacked container
791 if (container->parent->type == C_WORKSPACE &&
792 (container->layout == L_TABBED || container->layout == L_STACKED)) {
793 update_geometry(container);
794 width = container->border_geometry.size.w;
795 height = container->border_geometry.size.h;
796 x = container->border_geometry.origin.x;
797 y = container->border_geometry.origin.y;
798 }
799
800 // update container size if it's a child in a tabbed/stacked layout
801 if (swayc_tabbed_stacked_parent(container) != NULL) {
802 // Use parent actual_geometry as a base for calculating
803 // container geometry
804 container->width = container->parent->actual_geometry.size.w;
805 container->height = container->parent->actual_geometry.size.h;
806 container->x = container->parent->actual_geometry.origin.x;
807 container->y = container->parent->actual_geometry.origin.y;
808
809 update_geometry(container);
810 width = container->width = container->actual_geometry.size.w;
811 height = container->height = container->actual_geometry.size.h;
812 x = container->x = container->actual_geometry.origin.x;
813 y = container->y = container->actual_geometry.origin.y;
814 }
815
667 break; 816 break;
668 } 817 }
669 818
@@ -683,15 +832,22 @@ static void arrange_windows_r(swayc_t *container, double width, double height) {
683 } 832 }
684 scale += *old_width; 833 scale += *old_width;
685 } 834 }
835
686 // Resize windows 836 // Resize windows
687 if (scale > 0.1) { 837 if (scale > 0.1) {
688 scale = width / scale; 838 scale = width / scale;
689 sway_log(L_DEBUG, "Arranging %p horizontally", container); 839 sway_log(L_DEBUG, "Arranging %p horizontally", container);
840 swayc_t *focused = NULL;
690 for (i = 0; i < container->children->length; ++i) { 841 for (i = 0; i < container->children->length; ++i) {
691 swayc_t *child = container->children->items[i]; 842 swayc_t *child = container->children->items[i];
692 sway_log(L_DEBUG, "Calculating arrangement for %p:%d (will scale %f by %f)", child, child->type, width, scale); 843 sway_log(L_DEBUG, "Calculating arrangement for %p:%d (will scale %f by %f)", child, child->type, width, scale);
693 child->x = x; 844 child->x = x;
694 child->y = y; 845 child->y = y;
846
847 if (child == container->focused) {
848 focused = child;
849 }
850
695 if (i == container->children->length - 1) { 851 if (i == container->children->length - 1) {
696 double remaining_width = container->x + width - x; 852 double remaining_width = container->x + width - x;
697 arrange_windows_r(child, remaining_width, height); 853 arrange_windows_r(child, remaining_width, height);
@@ -700,6 +856,12 @@ static void arrange_windows_r(swayc_t *container, double width, double height) {
700 } 856 }
701 x += child->width; 857 x += child->width;
702 } 858 }
859
860 // update focused view border last because it may
861 // depend on the title bar geometry of its siblings.
862 if (focused && container->children->length > 1) {
863 update_view_border(focused);
864 }
703 } 865 }
704 break; 866 break;
705 case L_VERT: 867 case L_VERT:
@@ -719,11 +881,17 @@ static void arrange_windows_r(swayc_t *container, double width, double height) {
719 if (scale > 0.1) { 881 if (scale > 0.1) {
720 scale = height / scale; 882 scale = height / scale;
721 sway_log(L_DEBUG, "Arranging %p vertically", container); 883 sway_log(L_DEBUG, "Arranging %p vertically", container);
884 swayc_t *focused = NULL;
722 for (i = 0; i < container->children->length; ++i) { 885 for (i = 0; i < container->children->length; ++i) {
723 swayc_t *child = container->children->items[i]; 886 swayc_t *child = container->children->items[i];
724 sway_log(L_DEBUG, "Calculating arrangement for %p:%d (will scale %f by %f)", child, child->type, height, scale); 887 sway_log(L_DEBUG, "Calculating arrangement for %p:%d (will scale %f by %f)", child, child->type, height, scale);
725 child->x = x; 888 child->x = x;
726 child->y = y; 889 child->y = y;
890
891 if (child == container->focused) {
892 focused = child;
893 }
894
727 if (i == container->children->length - 1) { 895 if (i == container->children->length - 1) {
728 double remaining_height = container->y + height - y; 896 double remaining_height = container->y + height - y;
729 arrange_windows_r(child, width, remaining_height); 897 arrange_windows_r(child, width, remaining_height);
@@ -732,8 +900,34 @@ static void arrange_windows_r(swayc_t *container, double width, double height) {
732 } 900 }
733 y += child->height; 901 y += child->height;
734 } 902 }
903
904 // update focused view border last because it may
905 // depend on the title bar geometry of its siblings.
906 if (focused && container->children->length > 1) {
907 update_view_border(focused);
908 }
735 } 909 }
736 break; 910 break;
911 case L_TABBED:
912 case L_STACKED:
913 {
914 swayc_t *focused = NULL;
915 for (i = 0; i < container->children->length; ++i) {
916 swayc_t *child = container->children->items[i];
917 child->x = x;
918 child->y = y;
919 if (child == container->focused) {
920 focused = child;
921 } else {
922 arrange_windows_r(child, width, height);
923 }
924 }
925
926 if (focused) {
927 arrange_windows_r(focused, width, height);
928 }
929 break;
930 }
737 } 931 }
738 932
739 // Arrage floating layouts for workspaces last 933 // Arrage floating layouts for workspaces last
@@ -742,6 +936,8 @@ static void arrange_windows_r(swayc_t *container, double width, double height) {
742 swayc_t *view = container->floating->items[i]; 936 swayc_t *view = container->floating->items[i];
743 if (view->type == C_VIEW) { 937 if (view->type == C_VIEW) {
744 update_geometry(view); 938 update_geometry(view);
939 sway_log(L_DEBUG, "Set floating view to %.f x %.f @ %.f, %.f", view->width,
940 view->height, view->x, view->y);
745 if (swayc_is_fullscreen(view)) { 941 if (swayc_is_fullscreen(view)) {
746 wlc_view_bring_to_front(view->handle); 942 wlc_view_bring_to_front(view->handle);
747 } else if (!container->focused 943 } else if (!container->focused
@@ -840,12 +1036,12 @@ swayc_t *get_swayc_in_direction_under(swayc_t *container, enum movement_directio
840 return get_swayc_in_output_direction(output, dir); 1036 return get_swayc_in_output_direction(output, dir);
841 } else { 1037 } else {
842 if (dir == MOVE_LEFT || dir == MOVE_RIGHT) { 1038 if (dir == MOVE_LEFT || dir == MOVE_RIGHT) {
843 if (parent->layout == L_HORIZ) { 1039 if (parent->layout == L_HORIZ || parent->layout == L_TABBED) {
844 can_move = true; 1040 can_move = true;
845 diff = dir == MOVE_LEFT ? -1 : 1; 1041 diff = dir == MOVE_LEFT ? -1 : 1;
846 } 1042 }
847 } else { 1043 } else {
848 if (parent->layout == L_VERT) { 1044 if (parent->layout == L_VERT || parent->layout == L_STACKED) {
849 can_move = true; 1045 can_move = true;
850 diff = dir == MOVE_UP ? -1 : 1; 1046 diff = dir == MOVE_UP ? -1 : 1;
851 } 1047 }
@@ -900,3 +1096,15 @@ void recursive_resize(swayc_t *container, double amount, enum wlc_resize_edge ed
900 } 1096 }
901 } 1097 }
902} 1098}
1099
1100enum swayc_layouts default_layout(swayc_t *output) {
1101 if (config->default_layout != L_NONE) {
1102 return config->default_layout;
1103 } else if (config->default_orientation != L_NONE) {
1104 return config->default_orientation;
1105 } else if (output->width >= output->height) {
1106 return L_HORIZ;
1107 } else {
1108 return L_VERT;
1109 }
1110}