diff options
Diffstat (limited to 'sway/commands/layout.c')
-rw-r--r-- | sway/commands/layout.c | 185 |
1 files changed, 114 insertions, 71 deletions
diff --git a/sway/commands/layout.c b/sway/commands/layout.c index a06832de..44ce2970 100644 --- a/sway/commands/layout.c +++ b/sway/commands/layout.c | |||
@@ -4,21 +4,20 @@ | |||
4 | #include "sway/commands.h" | 4 | #include "sway/commands.h" |
5 | #include "sway/tree/arrange.h" | 5 | #include "sway/tree/arrange.h" |
6 | #include "sway/tree/container.h" | 6 | #include "sway/tree/container.h" |
7 | #include "sway/tree/workspace.h" | ||
7 | #include "log.h" | 8 | #include "log.h" |
8 | 9 | ||
9 | static bool parse_layout_string(char *s, enum sway_container_layout *ptr) { | 10 | static enum sway_container_layout parse_layout_string(char *s) { |
10 | if (strcasecmp(s, "splith") == 0) { | 11 | if (strcasecmp(s, "splith") == 0) { |
11 | *ptr = L_HORIZ; | 12 | return L_HORIZ; |
12 | } else if (strcasecmp(s, "splitv") == 0) { | 13 | } else if (strcasecmp(s, "splitv") == 0) { |
13 | *ptr = L_VERT; | 14 | return L_VERT; |
14 | } else if (strcasecmp(s, "tabbed") == 0) { | 15 | } else if (strcasecmp(s, "tabbed") == 0) { |
15 | *ptr = L_TABBED; | 16 | return L_TABBED; |
16 | } else if (strcasecmp(s, "stacking") == 0) { | 17 | } else if (strcasecmp(s, "stacking") == 0) { |
17 | *ptr = L_STACKED; | 18 | return L_STACKED; |
18 | } else { | ||
19 | return false; | ||
20 | } | 19 | } |
21 | return true; | 20 | return L_NONE; |
22 | } | 21 | } |
23 | 22 | ||
24 | static const char* expected_syntax = | 23 | static const char* expected_syntax = |
@@ -26,84 +25,128 @@ static const char* expected_syntax = | |||
26 | "'layout toggle [split|all]' or " | 25 | "'layout toggle [split|all]' or " |
27 | "'layout toggle [split|tabbed|stacking|splitv|splith] [split|tabbed|stacking|splitv|splith]...'"; | 26 | "'layout toggle [split|tabbed|stacking|splitv|splith] [split|tabbed|stacking|splitv|splith]...'"; |
28 | 27 | ||
28 | static enum sway_container_layout get_layout_toggle(int argc, char **argv, | ||
29 | enum sway_container_layout layout, | ||
30 | enum sway_container_layout prev_split_layout) { | ||
31 | // "layout toggle" | ||
32 | if (argc == 0) { | ||
33 | return layout == L_HORIZ ? L_VERT : L_HORIZ; | ||
34 | } | ||
35 | |||
36 | if (argc == 2) { | ||
37 | // "layout toggle split" (same as "layout toggle") | ||
38 | if (strcasecmp(argv[1], "split") == 0) { | ||
39 | return layout == L_HORIZ ? L_VERT : L_HORIZ; | ||
40 | } | ||
41 | // "layout toggle all" | ||
42 | if (strcasecmp(argv[1], "all") == 0) { | ||
43 | return layout == L_HORIZ ? L_VERT : | ||
44 | layout == L_VERT ? L_STACKED : | ||
45 | layout == L_STACKED ? L_TABBED : L_HORIZ; | ||
46 | } | ||
47 | return L_NONE; | ||
48 | } | ||
49 | |||
50 | enum sway_container_layout parsed; | ||
51 | int curr = 1; | ||
52 | for (; curr < argc; curr++) { | ||
53 | parsed = parse_layout_string(argv[curr]); | ||
54 | if (parsed == layout || (strcmp(argv[curr], "split") == 0 && | ||
55 | (layout == L_VERT || layout == L_HORIZ))) { | ||
56 | break; | ||
57 | } | ||
58 | } | ||
59 | for (int i = curr + 1; i != curr; ++i) { | ||
60 | // cycle round to find next valid layout | ||
61 | if (i >= argc) { | ||
62 | i = 1; | ||
63 | } | ||
64 | parsed = parse_layout_string(argv[i]); | ||
65 | if (parsed != L_NONE) { | ||
66 | return parsed; | ||
67 | } | ||
68 | if (strcmp(argv[i], "split") == 0) { | ||
69 | return layout == L_HORIZ ? L_VERT : | ||
70 | layout == L_VERT ? L_HORIZ : prev_split_layout; | ||
71 | } | ||
72 | // invalid layout strings are silently ignored | ||
73 | } | ||
74 | return L_NONE; | ||
75 | } | ||
76 | |||
77 | static enum sway_container_layout get_layout(int argc, char **argv, | ||
78 | enum sway_container_layout layout, | ||
79 | enum sway_container_layout prev_split_layout) { | ||
80 | // Check if assigned directly | ||
81 | enum sway_container_layout parsed = parse_layout_string(argv[0]); | ||
82 | if (parsed != L_NONE) { | ||
83 | return parsed; | ||
84 | } | ||
85 | |||
86 | if (strcasecmp(argv[0], "default") == 0) { | ||
87 | return prev_split_layout; | ||
88 | } | ||
89 | |||
90 | if (strcasecmp(argv[0], "toggle") == 0) { | ||
91 | return get_layout_toggle(argc, argv, layout, prev_split_layout); | ||
92 | } | ||
93 | |||
94 | return L_NONE; | ||
95 | } | ||
96 | |||
29 | struct cmd_results *cmd_layout(int argc, char **argv) { | 97 | struct cmd_results *cmd_layout(int argc, char **argv) { |
30 | struct cmd_results *error = NULL; | 98 | struct cmd_results *error = NULL; |
31 | if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) { | 99 | if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) { |
32 | return error; | 100 | return error; |
33 | } | 101 | } |
34 | struct sway_container *parent = config->handler_context.current_container; | 102 | struct sway_container *container = config->handler_context.container; |
103 | struct sway_workspace *workspace = config->handler_context.workspace; | ||
35 | 104 | ||
36 | if (container_is_floating(parent)) { | 105 | if (container && container_is_floating(container)) { |
37 | return cmd_results_new(CMD_FAILURE, "layout", | 106 | return cmd_results_new(CMD_FAILURE, "layout", |
38 | "Unable to change layout of floating windows"); | 107 | "Unable to change layout of floating windows"); |
39 | } | 108 | } |
40 | 109 | ||
41 | while (parent->type == C_VIEW) { | 110 | // Typically we change the layout of the current container, but if the |
42 | parent = parent->parent; | 111 | // current container is a view (it usually is) then we'll change the layout |
112 | // of the parent instead, as it doesn't make sense for views to have layout. | ||
113 | if (container && container->view) { | ||
114 | container = container->parent; | ||
43 | } | 115 | } |
44 | 116 | ||
45 | enum sway_container_layout prev = parent->layout; | 117 | // We could be working with a container OR a workspace. These are different |
46 | bool assigned_directly = parse_layout_string(argv[0], &parent->layout); | 118 | // structures, so we set up pointers to they layouts so we can refer them in |
47 | if (!assigned_directly) { | 119 | // an abstract way. |
48 | if (strcasecmp(argv[0], "default") == 0) { | 120 | enum sway_container_layout new_layout = L_NONE; |
49 | parent->layout = parent->prev_split_layout; | 121 | enum sway_container_layout old_layout = L_NONE; |
50 | } else if (strcasecmp(argv[0], "toggle") == 0) { | 122 | if (container) { |
51 | if (argc == 1) { | 123 | old_layout = container->layout; |
52 | parent->layout = | 124 | new_layout = get_layout(argc, argv, |
53 | parent->layout == L_STACKED ? L_TABBED : | 125 | container->layout, container->prev_split_layout); |
54 | parent->layout == L_TABBED ? parent->prev_split_layout : L_STACKED; | 126 | } else { |
55 | } else if (argc == 2) { | 127 | old_layout = workspace->layout; |
56 | if (strcasecmp(argv[1], "all") == 0) { | 128 | new_layout = get_layout(argc, argv, |
57 | parent->layout = | 129 | workspace->layout, workspace->prev_split_layout); |
58 | parent->layout == L_HORIZ ? L_VERT : | ||
59 | parent->layout == L_VERT ? L_STACKED : | ||
60 | parent->layout == L_STACKED ? L_TABBED : L_HORIZ; | ||
61 | } else if (strcasecmp(argv[1], "split") == 0) { | ||
62 | parent->layout = | ||
63 | parent->layout == L_HORIZ ? L_VERT : | ||
64 | parent->layout == L_VERT ? L_HORIZ : parent->prev_split_layout; | ||
65 | } else { | ||
66 | return cmd_results_new(CMD_INVALID, "layout", expected_syntax); | ||
67 | } | ||
68 | } else { | ||
69 | enum sway_container_layout parsed_layout; | ||
70 | int curr = 1; | ||
71 | for (; curr < argc; curr++) { | ||
72 | bool valid = parse_layout_string(argv[curr], &parsed_layout); | ||
73 | if ((valid && parsed_layout == parent->layout) || | ||
74 | (strcmp(argv[curr], "split") == 0 && | ||
75 | (parent->layout == L_VERT || parent->layout == L_HORIZ))) { | ||
76 | break; | ||
77 | } | ||
78 | } | ||
79 | for (int i = curr + 1; i != curr; ++i) { | ||
80 | // cycle round to find next valid layout | ||
81 | if (i >= argc) { | ||
82 | i = 1; | ||
83 | } | ||
84 | if (parse_layout_string(argv[i], &parent->layout)) { | ||
85 | break; | ||
86 | } else if (strcmp(argv[i], "split") == 0) { | ||
87 | parent->layout = | ||
88 | parent->layout == L_HORIZ ? L_VERT : | ||
89 | parent->layout == L_VERT ? L_HORIZ : parent->prev_split_layout; | ||
90 | break; | ||
91 | } // invalid layout strings are silently ignored | ||
92 | } | ||
93 | } | ||
94 | } else { | ||
95 | return cmd_results_new(CMD_INVALID, "layout", expected_syntax); | ||
96 | } | ||
97 | } | 130 | } |
98 | if (parent->layout == L_NONE) { | 131 | if (new_layout == L_NONE) { |
99 | parent->layout = container_get_default_layout(parent); | 132 | return cmd_results_new(CMD_INVALID, "layout", expected_syntax); |
100 | } | 133 | } |
101 | if (prev != parent->layout) { | 134 | if (new_layout != old_layout) { |
102 | if (prev != L_TABBED && prev != L_STACKED) { | 135 | if (container) { |
103 | parent->prev_split_layout = prev; | 136 | if (old_layout != L_TABBED && old_layout != L_STACKED) { |
137 | container->prev_split_layout = old_layout; | ||
138 | } | ||
139 | container->layout = new_layout; | ||
140 | container_update_representation(container); | ||
141 | arrange_container(container); | ||
142 | } else { | ||
143 | if (old_layout != L_TABBED && old_layout != L_STACKED) { | ||
144 | workspace->prev_split_layout = old_layout; | ||
145 | } | ||
146 | workspace->layout = new_layout; | ||
147 | workspace_update_representation(workspace); | ||
148 | arrange_workspace(workspace); | ||
104 | } | 149 | } |
105 | container_notify_subtree_changed(parent); | ||
106 | arrange_windows(parent->parent); | ||
107 | } | 150 | } |
108 | 151 | ||
109 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 152 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |