diff --git a/docs/userguide b/docs/userguide index da5d9873..401d5ff2 100644 --- a/docs/userguide +++ b/docs/userguide @@ -585,6 +585,16 @@ workspace_layout default|stacking|tabbed workspace_layout tabbed --------------------- +=== Window title alignment + +This option determines the window title's text alignment. +Default is +left+ + +*Syntax*: +--------------------------------------------- +title_align left|center|right +--------------------------------------------- + === Default border style for new windows This option determines which border style new windows will have. The default is diff --git a/include/config_directives.h b/include/config_directives.h index 4a20a1f5..72b59ea2 100644 --- a/include/config_directives.h +++ b/include/config_directives.h @@ -56,6 +56,7 @@ CFGFUN(disable_randr15, const char *value); CFGFUN(fake_outputs, const char *outputs); CFGFUN(force_display_urgency_hint, const long duration_ms); CFGFUN(focus_on_window_activation, const char *mode); +CFGFUN(title_align, const char *alignment); CFGFUN(show_marks, const char *value); CFGFUN(hide_edge_borders, const char *borders); CFGFUN(assign_output, const char *output); diff --git a/include/configuration.h b/include/configuration.h index 3eccca4c..a1e9cc80 100644 --- a/include/configuration.h +++ b/include/configuration.h @@ -201,6 +201,13 @@ struct Config { * decoration. Marks starting with a "_" will be ignored either way. */ bool show_marks; + /** Title alignment options. */ + enum { + ALIGN_LEFT, + ALIGN_CENTER, + ALIGN_RIGHT + } title_align; + /** The default border style for new windows. */ border_style_t default_border; diff --git a/parser-specs/config.spec b/parser-specs/config.spec index 5cdb7c32..43181c59 100644 --- a/parser-specs/config.spec +++ b/parser-specs/config.spec @@ -45,6 +45,7 @@ state INITIAL: 'fake_outputs', 'fake-outputs' -> FAKE_OUTPUTS 'force_display_urgency_hint' -> FORCE_DISPLAY_URGENCY_HINT 'focus_on_window_activation' -> FOCUS_ON_WINDOW_ACTIVATION + 'title_align' -> TITLE_ALIGN 'show_marks' -> SHOW_MARKS 'workspace' -> WORKSPACE 'ipc_socket', 'ipc-socket' -> IPC_SOCKET @@ -248,6 +249,11 @@ state FORCE_DISPLAY_URGENCY_HINT: duration_ms = number -> FORCE_DISPLAY_URGENCY_HINT_MS +# title_align [left|center|right] +state TITLE_ALIGN: + alignment = 'left', 'center', 'right' + -> call cfg_title_align($alignment) + # show_marks state SHOW_MARKS: value = word diff --git a/src/config_directives.c b/src/config_directives.c index 5c85197f..0b01d54a 100644 --- a/src/config_directives.c +++ b/src/config_directives.c @@ -318,6 +318,18 @@ CFGFUN(focus_on_window_activation, const char *mode) { DLOG("Set new focus_on_window_activation mode = %i.\n", config.focus_on_window_activation); } +CFGFUN(title_align, const char *alignment) { + if (strcmp(alignment, "left") == 0) { + config.title_align = ALIGN_LEFT; + } else if (strcmp(alignment, "center") == 0) { + config.title_align = ALIGN_CENTER; + } else if (strcmp(alignment, "right") == 0) { + config.title_align = ALIGN_RIGHT; + } else { + assert(false); + } +} + CFGFUN(show_marks, const char *value) { config.show_marks = eval_boolstr(value); } diff --git a/src/x.c b/src/x.c index 1f54db83..ceffd30a 100644 --- a/src/x.c +++ b/src/x.c @@ -602,6 +602,8 @@ void x_draw_decoration(Con *con) { goto after_title; } + const int title_padding = logical_px(2); + const int deco_width = (int)con->deco_rect.width; int mark_width = 0; if (config.show_marks && !TAILQ_EMPTY(&(con->marks_head))) { char *formatted_mark = sstrdup(""); @@ -623,12 +625,17 @@ void x_draw_decoration(Con *con) { i3String *mark = i3string_from_utf8(formatted_mark); mark_width = predict_text_width(mark); + int mark_offset_x = (config.title_align == ALIGN_RIGHT) + ? title_padding + : deco_width - mark_width - title_padding; + draw_util_text(mark, &(parent->frame_buffer), p->color->text, p->color->background, - con->deco_rect.x + con->deco_rect.width - mark_width - logical_px(2), + con->deco_rect.x + mark_offset_x, con->deco_rect.y + text_offset_y, mark_width); - I3STRING_FREE(mark); + + mark_width += title_padding; } FREE(formatted_mark); @@ -639,11 +646,33 @@ void x_draw_decoration(Con *con) { goto copy_pixmaps; } + int title_offset_x; + switch (config.title_align) { + case ALIGN_LEFT: + /* (pad)[text ](pad)[mark + its pad) */ + title_offset_x = title_padding; + break; + case ALIGN_CENTER: + /* (pad)[ text ](pad)[mark + its pad) + * To center the text inside its allocated space, the surface + * between the brackets, we use the formula + * (surface_width - predict_text_width) / 2 + * where surface_width = deco_width - 2 * pad - mark_width + * so, offset = pad + (surface_width - predict_text_width) / 2 = + * = … = (deco_width - mark_width - predict_text_width) / 2 */ + title_offset_x = max(title_padding, (deco_width - mark_width - predict_text_width(title)) / 2); + break; + case ALIGN_RIGHT: + /* [mark + its pad](pad)[ text](pad) */ + title_offset_x = max(title_padding + mark_width, deco_width - title_padding - predict_text_width(title)); + break; + } + draw_util_text(title, &(parent->frame_buffer), p->color->text, p->color->background, - con->deco_rect.x + logical_px(2), + con->deco_rect.x + title_offset_x, con->deco_rect.y + text_offset_y, - con->deco_rect.width - mark_width - 2 * logical_px(2)); + deco_width - mark_width - 2 * title_padding); if (con->title_format != NULL) { I3STRING_FREE(title); diff --git a/testcases/t/201-config-parser.t b/testcases/t/201-config-parser.t index a58f33c1..c6ce22eb 100644 --- a/testcases/t/201-config-parser.t +++ b/testcases/t/201-config-parser.t @@ -497,6 +497,7 @@ my $expected_all_tokens = "ERROR: CONFIG: Expected one of these tokens: , ' fake-outputs force_display_urgency_hint focus_on_window_activation + title_align show_marks workspace ipc_socket