Shrinking on a per-block basis (#5818)

Co-authored-by: Orestis Floros <orestisflo@gmail.com>
This commit is contained in:
Alessandro Vinciguerra 2024-01-28 11:30:28 +01:00 committed by GitHub
parent f169624560
commit ca510e5e0f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 107 additions and 53 deletions

View File

@ -46,6 +46,9 @@ struct status_block {
i3String *full_text;
i3String *short_text;
bool use_short;
uint32_t render_length;
char *color;
char *background;
char *border;

View File

@ -65,8 +65,6 @@ struct i3_output {
surface_t statusline_buffer;
/* How much of statusline_buffer's horizontal space was used on last statusline render. */
int statusline_width;
/* Whether statusline block short texts where used on last statusline render. */
bool statusline_short_text;
/* The actual window on which we draw. */
surface_t bar;

View File

@ -8,6 +8,7 @@
*
*/
#include "common.h"
#include "queue.h"
#include "yajl_utils.h"
#include <ctype.h> /* isspace */
@ -21,6 +22,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/wait.h>
#include <unistd.h>
@ -354,11 +356,13 @@ static int stdin_end_map(void *context) {
i3string_set_markup(new_block->full_text, new_block->pango_markup);
new_block->use_short = false;
if (new_block->short_text != NULL) {
i3string_set_markup(new_block->short_text, new_block->pango_markup);
}
TAILQ_INSERT_TAIL(&statusline_buffer, new_block, blocks);
return 1;
}

View File

@ -147,7 +147,6 @@ static int outputs_start_map_cb(void *params_) {
new_output->visible = false;
new_output->ws = 0,
new_output->statusline_width = 0;
new_output->statusline_short_text = false;
memset(&new_output->rect, 0, sizeof(rect));
memset(&new_output->bar, 0, sizeof(surface_t));
memset(&new_output->buffer, 0, sizeof(surface_t));

View File

@ -12,6 +12,7 @@
#include <err.h>
#include <ev.h>
#include <i3/ipc.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
@ -188,48 +189,59 @@ static void draw_separator(i3_output *output, uint32_t x, struct status_block *b
}
}
static uint32_t predict_statusline_length(bool use_short_text) {
static void predict_block_length(struct status_block *block) {
i3String *text = block->full_text;
struct status_block_render_desc *render = &block->full_render;
if (block->use_short && block->short_text != NULL) {
text = block->short_text;
render = &block->short_render;
}
if (i3string_get_num_bytes(text) == 0) {
block->render_length = 0;
return;
}
render->width = predict_text_width(text);
if (block->border) {
render->width += logical_px(block->border_left + block->border_right);
}
/* Compute offset and append for text alignment in min_width. */
if (block->min_width <= render->width) {
render->x_offset = 0;
render->x_append = 0;
} else {
uint32_t padding_width = block->min_width - render->width;
switch (block->align) {
case ALIGN_LEFT:
render->x_append = padding_width;
break;
case ALIGN_RIGHT:
render->x_offset = padding_width;
break;
case ALIGN_CENTER:
render->x_offset = padding_width / 2;
render->x_append = padding_width / 2 + padding_width % 2;
break;
}
}
block->render_length = render->width + render->x_offset + render->x_append;
}
static uint32_t predict_statusline_length(void) {
uint32_t width = 0;
struct status_block *block;
TAILQ_FOREACH (block, &statusline_head, blocks) {
i3String *text = block->full_text;
struct status_block_render_desc *render = &block->full_render;
if (use_short_text && block->short_text != NULL) {
text = block->short_text;
render = &block->short_render;
}
if (i3string_get_num_bytes(text) == 0) {
predict_block_length(block);
uint32_t block_width = block->render_length;
if (block_width == 0) {
continue;
}
render->width = predict_text_width(text);
if (block->border) {
render->width += logical_px(block->border_left + block->border_right);
}
/* Compute offset and append for text alignment in min_width. */
if (block->min_width <= render->width) {
render->x_offset = 0;
render->x_append = 0;
} else {
uint32_t padding_width = block->min_width - render->width;
switch (block->align) {
case ALIGN_LEFT:
render->x_append = padding_width;
break;
case ALIGN_RIGHT:
render->x_offset = padding_width;
break;
case ALIGN_CENTER:
render->x_offset = padding_width / 2;
render->x_append = padding_width / 2 + padding_width % 2;
break;
}
}
width += render->width + render->x_offset + render->x_append;
width += block_width;
/* If this is not the last block, add some pixels for a separator. */
if (TAILQ_NEXT(block, blocks) != NULL) {
@ -240,10 +252,49 @@ static uint32_t predict_statusline_length(bool use_short_text) {
return width;
}
static uint32_t switch_block_to_short(struct status_block *block) {
/* Skip blocks that have no short form or are already in short form */
if (block->short_text == NULL || block->use_short) {
return 0;
}
uint32_t full = block->render_length;
block->use_short = true;
predict_block_length(block);
return full - block->render_length;
}
static uint32_t adjust_statusline_length(uint32_t max_length) {
uint32_t width = predict_statusline_length();
/* Progressively switch the blocks to short mode */
struct status_block *block;
TAILQ_FOREACH (block, &statusline_head, blocks) {
if (width < max_length) {
break;
}
width -= switch_block_to_short(block);
/* Provide support for representing a single logical block using multiple
* JSON blocks: if one block is shortened, ensure that all other blocks
* with the same name are also shortened such that the entire logical block uses
* the short form text. */
if (block->name) {
struct status_block *other;
TAILQ_FOREACH (other, &statusline_head, blocks) {
if (other->name && !strcmp(other->name, block->name)) {
width -= switch_block_to_short(other);
}
}
}
}
return width;
}
/*
* Redraws the statusline to the output's statusline_buffer
*/
static void draw_statusline(i3_output *output, uint32_t clip_left, bool use_focus_colors, bool use_short_text) {
static void draw_statusline(i3_output *output, uint32_t clip_left, bool use_focus_colors) {
struct status_block *block;
color_t bar_color = (use_focus_colors ? colors.focus_bar_bg : colors.bar_bg);
@ -261,7 +312,7 @@ static void draw_statusline(i3_output *output, uint32_t clip_left, bool use_focu
TAILQ_FOREACH (block, &statusline_head, blocks) {
i3String *text = block->full_text;
struct status_block_render_desc *render = &block->full_render;
if (use_short_text && block->short_text != NULL) {
if (block->use_short && block->short_text != NULL) {
text = block->short_text;
render = &block->short_render;
}
@ -466,7 +517,7 @@ static void child_handle_button(xcb_button_press_event_t *event, i3_output *outp
TAILQ_FOREACH (block, &statusline_head, blocks) {
i3String *text;
struct status_block_render_desc *render;
if (output->statusline_short_text && block->short_text != NULL) {
if (block->use_short && block->short_text != NULL) {
text = block->short_text;
render = &block->short_render;
} else {
@ -2055,9 +2106,6 @@ static void draw_button(surface_t *surface, color_t fg_color, color_t bg_color,
void draw_bars(bool unhide) {
DLOG("Drawing bars...\n");
uint32_t full_statusline_width = predict_statusline_length(false);
uint32_t short_statusline_width = predict_statusline_length(true);
i3_output *outputs_walk;
SLIST_FOREACH (outputs_walk, outputs, slist) {
int workspace_width = logical_px(config.padding.x);
@ -2132,27 +2180,28 @@ void draw_bars(bool unhide) {
uint32_t hoff = logical_px(((workspace_width > 0) + (tray_width > 0)) * sb_hoff_px);
uint32_t max_statusline_width = outputs_walk->rect.w - workspace_width - tray_width - hoff;
uint32_t clip_left = 0;
uint32_t statusline_width = full_statusline_width;
bool use_short_text = false;
/* Reset short mode between outputs */
struct status_block *block;
TAILQ_FOREACH (block, &statusline_head, blocks) {
block->use_short = false;
}
uint32_t statusline_width = adjust_statusline_length(max_statusline_width);
if (statusline_width > max_statusline_width) {
statusline_width = short_statusline_width;
use_short_text = true;
if (statusline_width > max_statusline_width) {
clip_left = statusline_width - max_statusline_width;
}
clip_left = statusline_width - max_statusline_width;
}
int16_t visible_statusline_width = MIN(statusline_width, max_statusline_width);
int x_dest = outputs_walk->rect.w - tray_width - logical_px((tray_width > 0) * sb_hoff_px) - visible_statusline_width;
x_dest -= logical_px(config.padding.width);
draw_statusline(outputs_walk, clip_left, use_focus_colors, use_short_text);
draw_statusline(outputs_walk, clip_left, use_focus_colors);
draw_util_copy_surface(&outputs_walk->statusline_buffer, &outputs_walk->buffer, 0, 0,
x_dest, 0, visible_statusline_width, (int16_t)bar_height);
outputs_walk->statusline_width = statusline_width;
outputs_walk->statusline_short_text = use_short_text;
}
}

View File

@ -0,0 +1 @@
i3bar: use short-form text on a per-block basis