diff options
author | Brian Ashworth <bosrsf04@gmail.com> | 2018-11-07 22:44:11 -0500 |
---|---|---|
committer | Brian Ashworth <bosrsf04@gmail.com> | 2018-11-07 22:44:11 -0500 |
commit | 9e8aa3953098adb6175c26aebd984a32a2beccb0 (patch) | |
tree | ac64adf9f2720ddbb2476810f6ec05ec2a85a4ae /sway/commands/gaps.c | |
parent | Add focus_follows_mouse always. (#3081) (diff) | |
download | sway-9e8aa3953098adb6175c26aebd984a32a2beccb0.tar.gz sway-9e8aa3953098adb6175c26aebd984a32a2beccb0.tar.zst sway-9e8aa3953098adb6175c26aebd984a32a2beccb0.zip |
Implement per side and per direction outer gaps
This introduces the following command extensions from `i3-gaps`:
* `gaps horizontal|vertical|top|right|bottom|left <amount>`
* `gaps horizontal|vertical|top|right|bottom|left all|current
set|plus|minus <amount>`
* `workspace <ws> gaps horizontal|vertical|top|right|bottom|left
<amount>`
`inner` and `outer` are also still available as options for all three
of the above commands. `outer` now acts as a shorthand to set/alter
all sides.
Additionally, this fixes two bugs with the prevention of invalid gap
configurations for workspace configs:
1. If outer gaps were not set and inner gaps were, the outer gaps
would be snapped to the negation of the inner gaps due to `INT_MIN`
being less than the negation. This took precedence over the default
outer gaps.
2. Similarly, if inner gaps were not set and outer gaps were, inner
gaps would be set to zero, which would take precedence over the
default inner gaps.
Fixing both of the above items also requires checking the gaps again
when creating a workspace since the default outer gaps can be smaller
than the negation of the workspace specific inner gaps.
Diffstat (limited to 'sway/commands/gaps.c')
-rw-r--r-- | sway/commands/gaps.c | 151 |
1 files changed, 108 insertions, 43 deletions
diff --git a/sway/commands/gaps.c b/sway/commands/gaps.c index 3f0ef155..faaeab37 100644 --- a/sway/commands/gaps.c +++ b/sway/commands/gaps.c | |||
@@ -16,73 +16,128 @@ enum gaps_op { | |||
16 | 16 | ||
17 | struct gaps_data { | 17 | struct gaps_data { |
18 | bool inner; | 18 | bool inner; |
19 | struct { | ||
20 | bool top; | ||
21 | bool right; | ||
22 | bool bottom; | ||
23 | bool left; | ||
24 | } outer; | ||
19 | enum gaps_op operation; | 25 | enum gaps_op operation; |
20 | int amount; | 26 | int amount; |
21 | }; | 27 | }; |
22 | 28 | ||
23 | // gaps inner|outer <px> | 29 | // Prevent negative outer gaps from moving windows out of the workspace. |
30 | static void prevent_invalid_outer_gaps(void) { | ||
31 | if (config->gaps_outer.top < -config->gaps_inner) { | ||
32 | config->gaps_outer.top = -config->gaps_inner; | ||
33 | } | ||
34 | if (config->gaps_outer.right < -config->gaps_inner) { | ||
35 | config->gaps_outer.right = -config->gaps_inner; | ||
36 | } | ||
37 | if (config->gaps_outer.bottom < -config->gaps_inner) { | ||
38 | config->gaps_outer.bottom = -config->gaps_inner; | ||
39 | } | ||
40 | if (config->gaps_outer.left < -config->gaps_inner) { | ||
41 | config->gaps_outer.left = -config->gaps_inner; | ||
42 | } | ||
43 | } | ||
44 | |||
45 | // gaps inner|outer|horizontal|vertical|top|right|bottom|left <px> | ||
46 | static const char *expected_defaults = | ||
47 | "'gaps inner|outer|horizontal|vertical|top|right|bottom|left <px>'"; | ||
24 | static struct cmd_results *gaps_set_defaults(int argc, char **argv) { | 48 | static struct cmd_results *gaps_set_defaults(int argc, char **argv) { |
25 | struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 2); | 49 | struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 2); |
26 | if (error) { | 50 | if (error) { |
27 | return error; | 51 | return error; |
28 | } | 52 | } |
29 | 53 | ||
30 | bool inner; | ||
31 | if (strcasecmp(argv[0], "inner") == 0) { | ||
32 | inner = true; | ||
33 | } else if (strcasecmp(argv[0], "outer") == 0) { | ||
34 | inner = false; | ||
35 | } else { | ||
36 | return cmd_results_new(CMD_INVALID, "gaps", | ||
37 | "Expected 'gaps inner|outer <px>'"); | ||
38 | } | ||
39 | |||
40 | char *end; | 54 | char *end; |
41 | int amount = strtol(argv[1], &end, 10); | 55 | int amount = strtol(argv[1], &end, 10); |
42 | if (strlen(end) && strcasecmp(end, "px") != 0) { | 56 | if (strlen(end) && strcasecmp(end, "px") != 0) { |
43 | return cmd_results_new(CMD_INVALID, "gaps", | 57 | return cmd_results_new(CMD_INVALID, "gaps", |
44 | "Expected 'gaps inner|outer <px>'"); | 58 | "Expected %s", expected_defaults); |
45 | } | 59 | } |
46 | if (inner) { | 60 | |
61 | bool valid = false; | ||
62 | if (!strcasecmp(argv[0], "inner")) { | ||
63 | valid = true; | ||
47 | config->gaps_inner = (amount >= 0) ? amount : 0; | 64 | config->gaps_inner = (amount >= 0) ? amount : 0; |
48 | } else { | 65 | } else { |
49 | config->gaps_outer = amount; | 66 | if (!strcasecmp(argv[0], "outer") || !strcasecmp(argv[0], "vertical") |
50 | } | 67 | || !strcasecmp(argv[0], "top")) { |
51 | 68 | valid = true; | |
52 | // Prevent negative outer gaps from moving windows out of the workspace. | 69 | config->gaps_outer.top = amount; |
53 | if (config->gaps_outer < -config->gaps_inner) { | 70 | } |
54 | config->gaps_outer = -config->gaps_inner; | 71 | if (!strcasecmp(argv[0], "outer") || !strcasecmp(argv[0], "horizontal") |
72 | || !strcasecmp(argv[0], "right")) { | ||
73 | valid = true; | ||
74 | config->gaps_outer.right = amount; | ||
75 | } | ||
76 | if (!strcasecmp(argv[0], "outer") || !strcasecmp(argv[0], "vertical") | ||
77 | || !strcasecmp(argv[0], "bottom")) { | ||
78 | valid = true; | ||
79 | config->gaps_outer.bottom = amount; | ||
80 | } | ||
81 | if (!strcasecmp(argv[0], "outer") || !strcasecmp(argv[0], "horizontal") | ||
82 | || !strcasecmp(argv[0], "left")) { | ||
83 | valid = true; | ||
84 | config->gaps_outer.left = amount; | ||
85 | } | ||
86 | } | ||
87 | if (!valid) { | ||
88 | return cmd_results_new(CMD_INVALID, "gaps", | ||
89 | "Expected %s", expected_defaults); | ||
55 | } | 90 | } |
56 | 91 | ||
92 | prevent_invalid_outer_gaps(); | ||
57 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 93 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
58 | } | 94 | } |
59 | 95 | ||
60 | static void configure_gaps(struct sway_workspace *ws, void *_data) { | 96 | static void apply_gaps_op(int *prop, enum gaps_op op, int amount) { |
61 | struct gaps_data *data = _data; | 97 | switch (op) { |
62 | int *prop = data->inner ? &ws->gaps_inner : &ws->gaps_outer; | ||
63 | |||
64 | switch (data->operation) { | ||
65 | case GAPS_OP_SET: | 98 | case GAPS_OP_SET: |
66 | *prop = data->amount; | 99 | *prop = amount; |
67 | break; | 100 | break; |
68 | case GAPS_OP_ADD: | 101 | case GAPS_OP_ADD: |
69 | *prop += data->amount; | 102 | *prop += amount; |
70 | break; | 103 | break; |
71 | case GAPS_OP_SUBTRACT: | 104 | case GAPS_OP_SUBTRACT: |
72 | *prop -= data->amount; | 105 | *prop -= amount; |
73 | break; | 106 | break; |
74 | } | 107 | } |
108 | } | ||
109 | |||
110 | static void configure_gaps(struct sway_workspace *ws, void *_data) { | ||
111 | // Apply operation to gaps | ||
112 | struct gaps_data *data = _data; | ||
113 | if (data->inner) { | ||
114 | apply_gaps_op(&ws->gaps_inner, data->operation, data->amount); | ||
115 | } | ||
116 | if (data->outer.top) { | ||
117 | apply_gaps_op(&(ws->gaps_outer.top), data->operation, data->amount); | ||
118 | } | ||
119 | if (data->outer.right) { | ||
120 | apply_gaps_op(&(ws->gaps_outer.right), data->operation, data->amount); | ||
121 | } | ||
122 | if (data->outer.bottom) { | ||
123 | apply_gaps_op(&(ws->gaps_outer.bottom), data->operation, data->amount); | ||
124 | } | ||
125 | if (data->outer.left) { | ||
126 | apply_gaps_op(&(ws->gaps_outer.left), data->operation, data->amount); | ||
127 | } | ||
128 | |||
75 | // Prevent invalid gaps configurations. | 129 | // Prevent invalid gaps configurations. |
76 | if (ws->gaps_inner < 0) { | 130 | if (ws->gaps_inner < 0) { |
77 | ws->gaps_inner = 0; | 131 | ws->gaps_inner = 0; |
78 | } | 132 | } |
79 | if (ws->gaps_outer < -ws->gaps_inner) { | 133 | prevent_invalid_outer_gaps(); |
80 | ws->gaps_outer = -ws->gaps_inner; | ||
81 | } | ||
82 | arrange_workspace(ws); | 134 | arrange_workspace(ws); |
83 | } | 135 | } |
84 | 136 | ||
85 | // gaps inner|outer current|all set|plus|minus <px> | 137 | // gaps inner|outer|horizontal|vertical|top|right|bottom|left current|all |
138 | // set|plus|minus <px> | ||
139 | static const char *expected_runtime = "'gaps inner|outer|horizontal|vertical|" | ||
140 | "top|right|bottom|left current|all set|plus|minus <px>'"; | ||
86 | static struct cmd_results *gaps_set_runtime(int argc, char **argv) { | 141 | static struct cmd_results *gaps_set_runtime(int argc, char **argv) { |
87 | struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 4); | 142 | struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 4); |
88 | if (error) { | 143 | if (error) { |
@@ -93,15 +148,24 @@ static struct cmd_results *gaps_set_runtime(int argc, char **argv) { | |||
93 | "Can't run this command while there's no outputs connected."); | 148 | "Can't run this command while there's no outputs connected."); |
94 | } | 149 | } |
95 | 150 | ||
96 | struct gaps_data data; | 151 | struct gaps_data data = {0}; |
97 | 152 | ||
98 | if (strcasecmp(argv[0], "inner") == 0) { | 153 | if (strcasecmp(argv[0], "inner") == 0) { |
99 | data.inner = true; | 154 | data.inner = true; |
100 | } else if (strcasecmp(argv[0], "outer") == 0) { | ||
101 | data.inner = false; | ||
102 | } else { | 155 | } else { |
156 | data.outer.top = !strcasecmp(argv[0], "outer") || | ||
157 | !strcasecmp(argv[0], "vertical") || !strcasecmp(argv[0], "top"); | ||
158 | data.outer.right = !strcasecmp(argv[0], "outer") || | ||
159 | !strcasecmp(argv[0], "horizontal") || !strcasecmp(argv[0], "right"); | ||
160 | data.outer.bottom = !strcasecmp(argv[0], "outer") || | ||
161 | !strcasecmp(argv[0], "vertical") || !strcasecmp(argv[0], "bottom"); | ||
162 | data.outer.left = !strcasecmp(argv[0], "outer") || | ||
163 | !strcasecmp(argv[0], "horizontal") || !strcasecmp(argv[0], "left"); | ||
164 | } | ||
165 | if (!data.inner && !data.outer.top && !data.outer.right && | ||
166 | !data.outer.bottom && !data.outer.left) { | ||
103 | return cmd_results_new(CMD_INVALID, "gaps", | 167 | return cmd_results_new(CMD_INVALID, "gaps", |
104 | "Expected 'gaps inner|outer current|all set|plus|minus <px>'"); | 168 | "Expected %s", expected_runtime); |
105 | } | 169 | } |
106 | 170 | ||
107 | bool all; | 171 | bool all; |
@@ -111,7 +175,7 @@ static struct cmd_results *gaps_set_runtime(int argc, char **argv) { | |||
111 | all = true; | 175 | all = true; |
112 | } else { | 176 | } else { |
113 | return cmd_results_new(CMD_INVALID, "gaps", | 177 | return cmd_results_new(CMD_INVALID, "gaps", |
114 | "Expected 'gaps inner|outer current|all set|plus|minus <px>'"); | 178 | "Expected %s", expected_runtime); |
115 | } | 179 | } |
116 | 180 | ||
117 | if (strcasecmp(argv[2], "set") == 0) { | 181 | if (strcasecmp(argv[2], "set") == 0) { |
@@ -122,14 +186,14 @@ static struct cmd_results *gaps_set_runtime(int argc, char **argv) { | |||
122 | data.operation = GAPS_OP_SUBTRACT; | 186 | data.operation = GAPS_OP_SUBTRACT; |
123 | } else { | 187 | } else { |
124 | return cmd_results_new(CMD_INVALID, "gaps", | 188 | return cmd_results_new(CMD_INVALID, "gaps", |
125 | "Expected 'gaps inner|outer current|all set|plus|minus <px>'"); | 189 | "Expected %s", expected_runtime); |
126 | } | 190 | } |
127 | 191 | ||
128 | char *end; | 192 | char *end; |
129 | data.amount = strtol(argv[3], &end, 10); | 193 | data.amount = strtol(argv[3], &end, 10); |
130 | if (strlen(end) && strcasecmp(end, "px") != 0) { | 194 | if (strlen(end) && strcasecmp(end, "px") != 0) { |
131 | return cmd_results_new(CMD_INVALID, "gaps", | 195 | return cmd_results_new(CMD_INVALID, "gaps", |
132 | "Expected 'gaps inner|outer current|all set|plus|minus <px>'"); | 196 | "Expected %s", expected_runtime); |
133 | } | 197 | } |
134 | 198 | ||
135 | if (all) { | 199 | if (all) { |
@@ -141,8 +205,10 @@ static struct cmd_results *gaps_set_runtime(int argc, char **argv) { | |||
141 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 205 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
142 | } | 206 | } |
143 | 207 | ||
144 | // gaps inner|outer <px> - sets defaults for workspaces | 208 | // gaps inner|outer|<dir>|<side> <px> - sets defaults for workspaces |
145 | // gaps inner|outer current|all set|plus|minus <px> - runtime only | 209 | // gaps inner|outer|<dir>|<side> current|all set|plus|minus <px> - runtime only |
210 | // <dir> = horizontal|vertical | ||
211 | // <side> = top|right|bottom|left | ||
146 | struct cmd_results *cmd_gaps(int argc, char **argv) { | 212 | struct cmd_results *cmd_gaps(int argc, char **argv) { |
147 | struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 2); | 213 | struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 2); |
148 | if (error) { | 214 | if (error) { |
@@ -159,9 +225,8 @@ struct cmd_results *cmd_gaps(int argc, char **argv) { | |||
159 | } | 225 | } |
160 | if (config_loading) { | 226 | if (config_loading) { |
161 | return cmd_results_new(CMD_INVALID, "gaps", | 227 | return cmd_results_new(CMD_INVALID, "gaps", |
162 | "Expected 'gaps inner|outer <px>'"); | 228 | "Expected %s", expected_defaults); |
163 | } | 229 | } |
164 | return cmd_results_new(CMD_INVALID, "gaps", | 230 | return cmd_results_new(CMD_INVALID, "gaps", |
165 | "Expected 'gaps inner|outer <px>' or " | 231 | "Expected %s or %s", expected_runtime, expected_defaults); |
166 | "'gaps inner|outer current|all set|plus|minus <px>'"); | ||
167 | } | 232 | } |