i3bar: implement custom workspace numbers config
Patch status: merged
Patch by Tony Crisci
Long description:
Implement the configuration option within the bar config directive for custom workspace numbers with the directive `strip_workspace_numbers yes`. This directive strips the workspace name of the number prefix and delimiter. When the workspace name consists only of the number, it will default to show the number. For example: * "2:5" -> "5" * "4:$" -> "$" * "8" -> "8" This allows customization of i3bar for alternate ordering of workspaces which has a legitimate use for alternate keyboard layouts such as Dvorak. fixes #1131
To apply this patch, use:
curl http://cr.i3wm.org/patch/542/raw.patch | git am
b/docs/userguide
43 |
@@ -1191,6 +1191,31 @@ bar { |
44 |
} |
45 |
------------------------ |
46 |
|
47 |
+=== Strip workspace numbers |
48 |
+ |
49 |
+Specifies whether workspace numbers should be displayed within the workspace |
50 |
+buttons. This is useful if you want to have a named workspace that stays in |
51 |
+order on the bar according to its number without displaying the number prefix. |
52 |
+ |
53 |
+When +strip_workspace_numbers+ is set to +yes+, any workspace that has a name of |
54 |
+the form "[n]:[NAME]" will display only the name. You could use this, for |
55 |
+instance, to display Roman numerals rather than digits by naming your |
56 |
+workspaces to "1:I", "2:II", "3:III", "4:IV", ... |
57 |
+ |
58 |
+The default is to display the full name within the workspace button. |
59 |
+ |
60 |
+*Syntax*: |
61 |
+---------------------------------- |
62 |
+strip_workspace_numbers <yes|no> |
63 |
+---------------------------------- |
64 |
+ |
65 |
+*Example*: |
66 |
+---------------------------- |
67 |
+bar { |
68 |
+ strip_workspace_numbers yes |
69 |
+} |
70 |
+---------------------------- |
71 |
+ |
72 |
=== Binding Mode indicator |
73 |
|
74 |
Specifies whether the current binding mode indicator should be shown or not. |
b/i3bar/include/config.h
79 |
@@ -27,6 +27,7 @@ typedef struct config_t { |
80 |
struct xcb_color_strings_t colors; |
81 |
bool disable_binding_mode_indicator; |
82 |
bool disable_ws; |
83 |
+ bool strip_ws_numbers; |
84 |
char *bar_id; |
85 |
char *command; |
86 |
char *fontname; |
b/i3bar/include/workspaces.h
91 |
@@ -31,7 +31,8 @@ void free_workspaces(void); |
92 |
|
93 |
struct i3_ws { |
94 |
int num; /* The internal number of the ws */ |
95 |
- i3String *name; /* The name of the ws */ |
96 |
+ char *canonical_name; /* The true name of the ws according to the ipc */ |
97 |
+ i3String *name; /* The name of the ws that is displayed on the bar */ |
98 |
int name_width; /* The rendered width of the name */ |
99 |
bool visible; /* If the ws is currently visible on an output */ |
100 |
bool focused; /* If the ws is currently focused */ |
b/i3bar/src/config.c
105 |
@@ -193,6 +193,12 @@ static int config_boolean_cb(void *params_, int val) { |
106 |
return 1; |
107 |
} |
108 |
|
109 |
+ if (!strcmp(cur_key, "strip_workspace_numbers")) { |
110 |
+ DLOG("strip_workspace_numbers = %d\n", val); |
111 |
+ config.strip_ws_numbers = val; |
112 |
+ return 1; |
113 |
+ } |
114 |
+ |
115 |
if (!strcmp(cur_key, "verbose")) { |
116 |
DLOG("verbose = %d\n", val); |
117 |
config.verbose = val; |
b/i3bar/src/workspaces.c
122 |
@@ -105,14 +105,38 @@ static int workspaces_string_cb(void *params_, const unsigned char *val, size_t |
123 |
char *output_name; |
124 |
|
125 |
if (!strcmp(params->cur_key, "name")) { |
126 |
- /* Save the name */ |
127 |
- params->workspaces_walk->name = i3string_from_utf8_with_length((const char *)val, len); |
128 |
+ const char *ws_name = (const char*)val; |
129 |
+ params->workspaces_walk->canonical_name = strndup(ws_name, len); |
130 |
+ |
131 |
+ if (config.strip_ws_numbers && params->workspaces_walk->num >= 0) { |
132 |
+ /* Special case: strip off the workspace number */ |
133 |
+ static char ws_num[10]; |
134 |
+ |
135 |
+ snprintf(ws_num, sizeof(ws_num), "%d", params->workspaces_walk->num); |
136 |
+ |
137 |
+ /* Calculate the length of the number str in the name */ |
138 |
+ int offset = strspn(ws_name, ws_num); |
139 |
+ |
140 |
+ /* Also strip off the conventional ws name delimiter */ |
141 |
+ if (offset && ws_name[offset] == ':') |
142 |
+ offset += 1; |
143 |
+ |
144 |
+ /* Offset may be equal to length, in which case display the number */ |
145 |
+ params->workspaces_walk->name = (offset < len |
146 |
+ ? i3string_from_utf8_with_length(ws_name + offset, len - offset) |
147 |
+ : i3string_from_utf8(ws_num)); |
148 |
+ |
149 |
+ } else { |
150 |
+ /* Default case: just save the name */ |
151 |
+ params->workspaces_walk->name = i3string_from_utf8_with_length(ws_name, len); |
152 |
+ } |
153 |
|
154 |
/* Save its rendered width */ |
155 |
params->workspaces_walk->name_width = |
156 |
predict_text_width(params->workspaces_walk->name); |
157 |
|
158 |
- DLOG("Got Workspace %s, name_width: %d, glyphs: %zu\n", |
159 |
+ DLOG("Got Workspace canonical: %s, name: '%s', name_width: %d, glyphs: %zu\n", |
160 |
+ params->workspaces_walk->canonical_name, |
161 |
i3string_as_utf8(params->workspaces_walk->name), |
162 |
params->workspaces_walk->name_width, |
163 |
i3string_get_num_glyphs(params->workspaces_walk->name)); |
164 |
@@ -246,6 +270,7 @@ void free_workspaces(void) { |
165 |
if (outputs_walk->workspaces != NULL && !TAILQ_EMPTY(outputs_walk->workspaces)) { |
166 |
TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) { |
167 |
I3STRING_FREE(ws_walk->name); |
168 |
+ FREE(ws_walk->canonical_name); |
169 |
} |
170 |
FREE_TAILQ(outputs_walk->workspaces, i3_ws); |
171 |
} |
b/i3bar/src/xcb.c
176 |
@@ -418,7 +418,7 @@ void handle_button(xcb_button_press_event_t *event) { |
177 |
* buffer, then we copy character by character. */ |
178 |
int num_quotes = 0; |
179 |
size_t namelen = 0; |
180 |
- const char *utf8_name = i3string_as_utf8(cur_ws->name); |
181 |
+ const char *utf8_name = cur_ws->canonical_name; |
182 |
for (const char *walk = utf8_name; *walk != '\0'; walk++) { |
183 |
if (*walk == '"') |
184 |
num_quotes++; |
b/include/config.h
189 |
@@ -267,6 +267,10 @@ struct Barconfig { |
190 |
* zero. */ |
191 |
bool hide_workspace_buttons; |
192 |
|
193 |
+ /** Strip workspace numbers? Configuration option is |
194 |
+ * 'strip_workspace_numbers yes'. */ |
195 |
+ bool strip_workspace_numbers; |
196 |
+ |
197 |
/** Hide mode button? Configuration option is 'binding_mode_indicator no' |
198 |
* but we invert the bool for the same reason as hide_workspace_buttons.*/ |
199 |
bool hide_binding_mode_indicator; |
b/include/config_directives.h
204 |
@@ -81,4 +81,5 @@ CFGFUN(bar_color_single, const char *colorclass, const char *color); |
205 |
CFGFUN(bar_status_command, const char *command); |
206 |
CFGFUN(bar_binding_mode_indicator, const char *value); |
207 |
CFGFUN(bar_workspace_buttons, const char *value); |
208 |
+CFGFUN(bar_strip_workspace_numbers, const char *value); |
209 |
CFGFUN(bar_finish); |
b/parser-specs/config.spec
214 |
@@ -358,6 +358,7 @@ state BAR: |
215 |
'font' -> BAR_FONT |
216 |
'binding_mode_indicator' -> BAR_BINDING_MODE_INDICATOR |
217 |
'workspace_buttons' -> BAR_WORKSPACE_BUTTONS |
218 |
+ 'strip_workspace_numbers' -> BAR_STRIP_WORKSPACE_NUMBERS |
219 |
'verbose' -> BAR_VERBOSE |
220 |
'colors' -> BAR_COLORS_BRACE |
221 |
'}' |
222 |
@@ -420,6 +421,10 @@ state BAR_WORKSPACE_BUTTONS: |
223 |
value = word |
224 |
-> call cfg_bar_workspace_buttons($value); BAR |
225 |
|
226 |
+state BAR_STRIP_WORKSPACE_NUMBERS: |
227 |
+ value = word |
228 |
+ -> call cfg_bar_strip_workspace_numbers($value); BAR |
229 |
+ |
230 |
state BAR_VERBOSE: |
231 |
value = word |
232 |
-> call cfg_bar_verbose($value); BAR |
b/src/config_directives.c
237 |
@@ -517,6 +517,10 @@ CFGFUN(bar_workspace_buttons, const char *value) { |
238 |
current_bar.hide_workspace_buttons = !eval_boolstr(value); |
239 |
} |
240 |
|
241 |
+CFGFUN(bar_strip_workspace_numbers, const char *value) { |
242 |
+ current_bar.strip_workspace_numbers = eval_boolstr(value); |
243 |
+} |
244 |
+ |
245 |
CFGFUN(bar_finish) { |
246 |
DLOG("\t new bar configuration finished, saving.\n"); |
247 |
/* Generate a unique ID for this bar if not already configured */ |
b/src/ipc.c
252 |
@@ -514,6 +514,9 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) { |
253 |
ystr("workspace_buttons"); |
254 |
y(bool, !config->hide_workspace_buttons); |
255 |
|
256 |
+ ystr("strip_workspace_numbers"); |
257 |
+ y(bool, config->strip_workspace_numbers); |
258 |
+ |
259 |
ystr("binding_mode_indicator"); |
260 |
y(bool, !config->hide_binding_mode_indicator); |
261 |
|
b/testcases/t/201-config-parser.t
266 |
@@ -627,7 +627,7 @@ EOT |
267 |
|
268 |
$expected = <<'EOT'; |
269 |
cfg_bar_output(LVDS-1) |
270 |
-ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'i3bar_command', 'status_command', 'socket_path', 'mode', 'hidden_state', 'id', 'modifier', 'position', 'output', 'tray_output', 'font', 'binding_mode_indicator', 'workspace_buttons', 'verbose', 'colors', '}' |
271 |
+ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'i3bar_command', 'status_command', 'socket_path', 'mode', 'hidden_state', 'id', 'modifier', 'position', 'output', 'tray_output', 'font', 'binding_mode_indicator', 'workspace_buttons', 'strip_workspace_numbers', 'verbose', 'colors', '}' |
272 |
ERROR: CONFIG: (in file <stdin>) |
273 |
ERROR: CONFIG: Line 1: bar { |
274 |
ERROR: CONFIG: Line 2: output LVDS-1 |