mirror of
https://github.com/FelixKratz/SketchyBar
synced 2025-02-17 04:58:28 +00:00
add bar background clipping
Implements the features discussed in #274. It allows the clipping of the bar background to reveal what is underneath. The clipping shape is based on the background dimensions & corner radius, and the clipping mask is a float (0.f = no clipping, 1.f = fully clipped). The background.clip property is fully animatable, and can be applied to items and brackets, but not to the bar itself or to text items. Floating point number properties can now be animated using the ANIMATE_FLOAT macro. Clipping should not affect performance when disabled, aside from an additional loop in the bar_draw function. When enabled, there are at the moment quite a few preventable bar redraws since the bar_needs_refresh is naive and does not check if the bar actually needs a refresh (i.e. the clipping value has changed, the item has moved or its dimensions were changed). A less aggressive change detection may be implemented but would require a bit of refactoring to the update functions.
This commit is contained in:
parent
32d85ffe9b
commit
f3d51570f2
10 changed files with 125 additions and 15 deletions
|
@ -29,6 +29,7 @@ void animation_setup(struct animation* animation, void* target, animator_functio
|
|||
animation->update_function = update_function;
|
||||
animation->target = target;
|
||||
animation->seperate_bytes = false;
|
||||
animation->as_float = false;
|
||||
|
||||
if (interp_function == INTERP_FUNCTION_TANH) {
|
||||
animation->interp_function = &function_tanh;
|
||||
|
@ -71,8 +72,10 @@ static bool animation_update(struct animation* animation) {
|
|||
unsigned char byte_val = (1. - slider) * byte_i + slider * byte_f;
|
||||
*((unsigned char*)&value + i) = byte_val;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else if (animation->as_float) {
|
||||
*((float*)&value) = (1. - slider) * *(float*)&animation->initial_value
|
||||
+ slider * *(float*)&animation->final_value;
|
||||
} else {
|
||||
value = (1. - slider) * animation->initial_value
|
||||
+ slider * animation->final_value;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,28 @@ extern struct bar_manager g_bar_manager;
|
|||
} \
|
||||
}
|
||||
|
||||
#define ANIMATE_FLOAT(f, o, p, t) \
|
||||
{\
|
||||
if (g_bar_manager.animator.duration > 0) { \
|
||||
animator_cancel_locked(&g_bar_manager.animator, (void*)o, (bool (*)(void*, int))&f); \
|
||||
struct animation* animation = animation_create(); \
|
||||
float initial_value = p; \
|
||||
float final_value = t; \
|
||||
animation_setup(animation, \
|
||||
(void*)o, \
|
||||
(bool (*)(void*, int))&f, \
|
||||
*(int*)&initial_value, \
|
||||
*(int*)&final_value, \
|
||||
g_bar_manager.animator.duration, \
|
||||
g_bar_manager.animator.interp_function ); \
|
||||
animation->as_float = true; \
|
||||
animator_add(&g_bar_manager.animator, animation); \
|
||||
} else { \
|
||||
needs_refresh = animator_cancel(&g_bar_manager.animator, (void*)o, (bool (*)(void*, int))&f); \
|
||||
needs_refresh |= f(o, t); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define ANIMATE_BYTES(f, o, p, t) \
|
||||
{\
|
||||
if (g_bar_manager.animator.duration > 0) { \
|
||||
|
@ -59,6 +81,7 @@ typedef ANIMATION_FUNCTION(animation_function);
|
|||
|
||||
struct animation {
|
||||
bool seperate_bytes;
|
||||
bool as_float;
|
||||
bool locked;
|
||||
|
||||
uint32_t duration;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
void background_init(struct background* background) {
|
||||
background->enabled = false;
|
||||
background->clip = 0.f;
|
||||
background->overrides_height = false;
|
||||
|
||||
background->bounds.size.height = 25;
|
||||
|
@ -36,6 +37,13 @@ static bool background_set_enabled(struct background* background, bool enabled)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool background_set_clip(struct background* background, float clip) {
|
||||
if (background->clip == clip) return false;
|
||||
background->clip = clip;
|
||||
background_set_enabled(background, clip > 0.f);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool background_set_color(struct background* background, uint32_t color) {
|
||||
struct rgba_color target_color = rgba_color_from_hex(color);
|
||||
if (background->color.r == target_color.r
|
||||
|
@ -95,6 +103,20 @@ void background_calculate_bounds(struct background* background, uint32_t x, uint
|
|||
image_calculate_bounds(&background->image, x, y);
|
||||
}
|
||||
|
||||
void background_clip(struct background* background, CGPoint bar_item_origin, CGPoint bar_window_origin, CGContextRef context) {
|
||||
if (!background->enabled) return;
|
||||
if (background->clip == 0.f) return;
|
||||
CGRect region = {{0, 0}, background->bounds.size};
|
||||
region.origin.x = bar_item_origin.x
|
||||
- bar_window_origin.x
|
||||
+ background->bounds.origin.x,
|
||||
region.origin.y = bar_item_origin.y
|
||||
- bar_window_origin.y
|
||||
+ background->bounds.origin.y
|
||||
+ background->y_offset;
|
||||
clip_rect(context, region, background->clip, background->corner_radius);
|
||||
}
|
||||
|
||||
void background_draw(struct background* background, CGContextRef context) {
|
||||
if (!background->enabled) return;
|
||||
CGRect background_bounds = background->bounds;
|
||||
|
@ -133,6 +155,7 @@ void background_destroy(struct background* background) {
|
|||
|
||||
void background_serialize(struct background* background, char* indent, FILE* rsp, bool detailed) {
|
||||
fprintf(rsp, "%s\"drawing\": \"%s\",\n"
|
||||
"%s\"clip\": \"%f\",\n"
|
||||
"%s\"color\": \"0x%x\",\n"
|
||||
"%s\"border_color\": \"0x%x\",\n"
|
||||
"%s\"border_width\": %u,\n"
|
||||
|
@ -142,6 +165,7 @@ void background_serialize(struct background* background, char* indent, FILE* rsp
|
|||
"%s\"padding_right\": %d,\n"
|
||||
"%s\"y_offset\": %d,\n",
|
||||
indent, format_bool(background->enabled),
|
||||
indent, background->clip,
|
||||
indent, hex_from_rgba_color(background->color),
|
||||
indent, hex_from_rgba_color(background->border_color),
|
||||
indent, background->border_width,
|
||||
|
@ -171,7 +195,13 @@ bool background_parse_sub_domain(struct background* background, FILE* rsp, struc
|
|||
return background_set_enabled(background,
|
||||
evaluate_boolean_state(get_token(&message),
|
||||
background->enabled));
|
||||
else if (token_equals(property, PROPERTY_HEIGHT)) {
|
||||
else if (token_equals(property, PROPERTY_CLIP)) {
|
||||
struct token token = get_token(&message);
|
||||
ANIMATE_FLOAT(background_set_clip,
|
||||
background,
|
||||
background->clip,
|
||||
token_to_float(token));
|
||||
} else if (token_equals(property, PROPERTY_HEIGHT)) {
|
||||
struct token token = get_token(&message);
|
||||
ANIMATE(background_set_height,
|
||||
background,
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
struct background {
|
||||
bool enabled;
|
||||
float clip;
|
||||
bool overrides_height;
|
||||
|
||||
int padding_left;
|
||||
|
@ -23,6 +24,7 @@ void background_calculate_bounds(struct background* background, uint32_t x, uint
|
|||
|
||||
bool background_set_height(struct background* background, uint32_t height);
|
||||
|
||||
void background_clip(struct background* background, CGPoint bar_item_origin, CGPoint bar_window_origin, CGContextRef context);
|
||||
void background_draw(struct background* background, CGContextRef context);
|
||||
|
||||
void background_clear_pointers(struct background* background);
|
||||
|
|
14
src/bar.c
14
src/bar.c
|
@ -113,6 +113,10 @@ void bar_order_item_windows(struct bar* bar) {
|
|||
void bar_draw(struct bar* bar) {
|
||||
if (bar->sid < 1 || bar->adid < 1) return;
|
||||
|
||||
for (int i = 0; i < g_bar_manager.bar_item_count; i++) {
|
||||
g_bar_manager.bar_needs_update |= g_bar_manager.bar_items[i]->bar_needs_update;
|
||||
}
|
||||
|
||||
if (g_bar_manager.bar_needs_update) {
|
||||
draw_rect(bar->window.context,
|
||||
bar->window.frame,
|
||||
|
@ -125,15 +129,15 @@ void bar_draw(struct bar* bar) {
|
|||
if (g_bar_manager.background.image.enabled) {
|
||||
image_draw(&g_bar_manager.background.image, bar->window.context);
|
||||
}
|
||||
|
||||
CGContextFlush(bar->window.context);
|
||||
}
|
||||
|
||||
for (int i = 0; i < g_bar_manager.bar_item_count; i++) {
|
||||
struct bar_item* bar_item = g_bar_manager.bar_items[i];
|
||||
struct window* window = bar_item_get_window(bar_item, bar->adid);
|
||||
|
||||
if (!bar_draws_item(bar, bar_item)
|
||||
if (bar_draws_item(bar, bar_item)) {
|
||||
background_clip(&bar_item->background, window->origin, bar->window.origin, bar->window.context);
|
||||
} else if (!bar_draws_item(bar, bar_item)
|
||||
|| (bar_item->type == BAR_COMPONENT_GROUP
|
||||
&& !bar_draws_item(bar, group_get_first_member(bar_item->group)))){
|
||||
|
||||
|
@ -162,6 +166,10 @@ void bar_draw(struct bar* bar) {
|
|||
bar_item_draw(bar_item, window->context);
|
||||
CGContextFlush(window->context);
|
||||
}
|
||||
|
||||
if (g_bar_manager.bar_needs_update) {
|
||||
CGContextFlush(bar->window.context);
|
||||
}
|
||||
}
|
||||
|
||||
static void bar_calculate_bounds_top_bottom(struct bar* bar) {
|
||||
|
|
|
@ -77,6 +77,7 @@ void bar_item_inherit_from_item(struct bar_item* bar_item, struct bar_item* ance
|
|||
|
||||
void bar_item_init(struct bar_item* bar_item, struct bar_item* default_item) {
|
||||
bar_item->needs_update = true;
|
||||
bar_item->bar_needs_update = true;
|
||||
bar_item->lazy = true;
|
||||
bar_item->drawing = true;
|
||||
bar_item->updates = true;
|
||||
|
@ -239,6 +240,16 @@ void bar_item_needs_update(struct bar_item* bar_item) {
|
|||
bar_item->needs_update = true;
|
||||
}
|
||||
|
||||
void bar_item_bar_needs_update(struct bar_item* bar_item, bool force) {
|
||||
if (bar_item->group) {
|
||||
struct bar_item* first_member = group_get_first_member(bar_item->group);
|
||||
if (first_member && first_member != bar_item)
|
||||
bar_item_bar_needs_update(first_member, force);
|
||||
}
|
||||
|
||||
bar_item->bar_needs_update = force || bar_item->background.clip > 0.f;
|
||||
}
|
||||
|
||||
void bar_item_set_name(struct bar_item* bar_item, char* name) {
|
||||
if (!name) return;
|
||||
|
||||
|
@ -787,49 +798,54 @@ void bar_item_serialize(struct bar_item* bar_item, FILE* rsp) {
|
|||
|
||||
void bar_item_parse_set_message(struct bar_item* bar_item, char* message, FILE* rsp) {
|
||||
bool needs_refresh = false;
|
||||
bool bar_needs_refresh = false;
|
||||
struct token property = get_token(&message);
|
||||
|
||||
struct key_value_pair key_value_pair = get_key_value_pair(property.text,'.');
|
||||
if (key_value_pair.key && key_value_pair.value) {
|
||||
struct token subdom = { key_value_pair.key, strlen(key_value_pair.key) };
|
||||
struct token entry = { key_value_pair.value, strlen(key_value_pair.value)};
|
||||
if (token_equals(subdom, SUB_DOMAIN_ICON))
|
||||
if (token_equals(subdom, SUB_DOMAIN_ICON)) {
|
||||
needs_refresh = text_parse_sub_domain(&bar_item->icon,
|
||||
rsp,
|
||||
entry,
|
||||
message );
|
||||
bar_needs_refresh = needs_refresh;
|
||||
|
||||
else if (token_equals(subdom, SUB_DOMAIN_LABEL))
|
||||
} else if (token_equals(subdom, SUB_DOMAIN_LABEL)) {
|
||||
needs_refresh = text_parse_sub_domain(&bar_item->label,
|
||||
rsp,
|
||||
entry,
|
||||
message );
|
||||
bar_needs_refresh = needs_refresh;
|
||||
|
||||
else if (token_equals(subdom, SUB_DOMAIN_BACKGROUND))
|
||||
} else if (token_equals(subdom, SUB_DOMAIN_BACKGROUND)) {
|
||||
needs_refresh = background_parse_sub_domain(&bar_item->background,
|
||||
rsp,
|
||||
entry,
|
||||
message );
|
||||
if (needs_refresh)
|
||||
bar_item_bar_needs_update(bar_item, true);
|
||||
|
||||
else if (token_equals(subdom, SUB_DOMAIN_GRAPH))
|
||||
} else if (token_equals(subdom, SUB_DOMAIN_GRAPH)) {
|
||||
needs_refresh = graph_parse_sub_domain(&bar_item->graph,
|
||||
rsp,
|
||||
entry,
|
||||
message );
|
||||
|
||||
else if (token_equals(subdom, SUB_DOMAIN_POPUP))
|
||||
} else if (token_equals(subdom, SUB_DOMAIN_POPUP)) {
|
||||
needs_refresh = popup_parse_sub_domain(&bar_item->popup,
|
||||
rsp,
|
||||
entry,
|
||||
message );
|
||||
|
||||
else if (token_equals(subdom, SUB_DOMAIN_ALIAS))
|
||||
} else if (token_equals(subdom, SUB_DOMAIN_ALIAS)) {
|
||||
needs_refresh = alias_parse_sub_domain(&bar_item->alias,
|
||||
rsp,
|
||||
entry,
|
||||
message );
|
||||
|
||||
else {
|
||||
} else {
|
||||
respond(rsp, "[!] Item (%s): Invalid subdomain '%s'\n", bar_item->name, subdom.text);
|
||||
}
|
||||
}
|
||||
|
@ -837,11 +853,13 @@ void bar_item_parse_set_message(struct bar_item* bar_item, char* message, FILE*
|
|||
needs_refresh = text_set_string(&bar_item->icon,
|
||||
token_to_string(get_token(&message)),
|
||||
false );
|
||||
bar_needs_refresh = needs_refresh;
|
||||
|
||||
} else if (token_equals(property, PROPERTY_LABEL)) {
|
||||
needs_refresh = text_set_string(&bar_item->label,
|
||||
token_to_string(get_token(&message)),
|
||||
false );
|
||||
bar_needs_refresh = needs_refresh;
|
||||
|
||||
} else if (token_equals(property, PROPERTY_UPDATES)) {
|
||||
struct token token = get_token(&message);
|
||||
|
@ -857,6 +875,7 @@ void bar_item_parse_set_message(struct bar_item* bar_item, char* message, FILE*
|
|||
needs_refresh = bar_item_set_drawing(bar_item,
|
||||
evaluate_boolean_state(get_token(&message),
|
||||
bar_item->drawing ));
|
||||
bar_needs_refresh = needs_refresh;
|
||||
} else if (token_equals(property, PROPERTY_WIDTH)) {
|
||||
struct token token = get_token(&message);
|
||||
if (token_equals(token, ARGUMENT_DYNAMIC)) {
|
||||
|
@ -887,6 +906,7 @@ void bar_item_parse_set_message(struct bar_item* bar_item, char* message, FILE*
|
|||
+ bar_item->background.padding_right)),
|
||||
token_to_int(token) );
|
||||
}
|
||||
bar_needs_refresh = needs_refresh;
|
||||
} else if (token_equals(property, PROPERTY_SCRIPT)) {
|
||||
bar_item_set_script(bar_item, token_to_string(get_token(&message)));
|
||||
} else if (token_equals(property, PROPERTY_CLICK_SCRIPT)) {
|
||||
|
@ -914,11 +934,13 @@ void bar_item_parse_set_message(struct bar_item* bar_item, char* message, FILE*
|
|||
}
|
||||
}
|
||||
needs_refresh = true;
|
||||
bar_needs_refresh = true;
|
||||
} else if (token_equals(property, PROPERTY_ALIGN)) {
|
||||
struct token position = get_token(&message);
|
||||
if (bar_item->align != position.text[0]) {
|
||||
bar_item->align = position.text[0];
|
||||
needs_refresh = true;
|
||||
bar_needs_refresh = true;
|
||||
}
|
||||
} else if (token_equals(property, PROPERTY_ASSOCIATED_SPACE)) {
|
||||
struct token token = get_token(&message);
|
||||
|
@ -936,6 +958,7 @@ void bar_item_parse_set_message(struct bar_item* bar_item, char* message, FILE*
|
|||
free(list);
|
||||
}
|
||||
needs_refresh = (prev != bar_item->associated_space);
|
||||
bar_needs_refresh = needs_refresh;
|
||||
} else if (token_equals(property, PROPERTY_ASSOCIATED_DISPLAY)) {
|
||||
struct token token = get_token(&message);
|
||||
uint32_t prev = bar_item->associated_display;
|
||||
|
@ -958,12 +981,14 @@ void bar_item_parse_set_message(struct bar_item* bar_item, char* message, FILE*
|
|||
free(list);
|
||||
}
|
||||
needs_refresh = (prev != bar_item->associated_display);
|
||||
bar_needs_refresh = needs_refresh;
|
||||
} else if (token_equals(property, PROPERTY_YOFFSET)) {
|
||||
struct token token = get_token(&message);
|
||||
ANIMATE(bar_item_set_yoffset,
|
||||
bar_item,
|
||||
bar_item->y_offset,
|
||||
token_to_int(token) );
|
||||
bar_needs_refresh = needs_refresh;
|
||||
|
||||
} else if (token_equals(property, PROPERTY_BLUR_RADIUS)) {
|
||||
struct token token = get_token(&message);
|
||||
|
@ -976,6 +1001,7 @@ void bar_item_parse_set_message(struct bar_item* bar_item, char* message, FILE*
|
|||
bar_item->ignore_association = evaluate_boolean_state(get_token(&message),
|
||||
bar_item->ignore_association);
|
||||
needs_refresh = true;
|
||||
bar_needs_refresh = true;
|
||||
} else if (token_equals(property, COMMAND_DEFAULT_RESET)) {
|
||||
bar_item_init(&g_bar_manager.default_item, NULL);
|
||||
} else if (token_equals(property, PROPERTY_EVENT_PORT)) {
|
||||
|
@ -987,6 +1013,7 @@ void bar_item_parse_set_message(struct bar_item* bar_item, char* message, FILE*
|
|||
}
|
||||
|
||||
if (needs_refresh) bar_item_needs_update(bar_item);
|
||||
if (bar_needs_refresh) bar_item_bar_needs_update(bar_item, false);
|
||||
}
|
||||
|
||||
void bar_item_parse_subscribe_message(struct bar_item* bar_item, char* message, FILE* rsp) {
|
||||
|
|
|
@ -16,6 +16,7 @@ struct bar_item {
|
|||
// Update Modifiers
|
||||
uint32_t counter;
|
||||
bool needs_update;
|
||||
bool bar_needs_update;
|
||||
bool updates;
|
||||
bool updates_only_when_shown;
|
||||
bool lazy;
|
||||
|
@ -106,6 +107,7 @@ bool bar_item_set_width(struct bar_item* bar_item, int width);
|
|||
uint32_t bar_item_get_length(struct bar_item* bar_item, bool ignore_override);
|
||||
uint32_t bar_item_get_height(struct bar_item* bar_item);
|
||||
|
||||
void bar_item_bar_needs_update(struct bar_item* bar_item, bool force);
|
||||
void bar_item_needs_update(struct bar_item* bar_item);
|
||||
|
||||
void bar_item_on_click(struct bar_item* bar_item, uint32_t type, uint32_t modifier);
|
||||
|
|
|
@ -313,8 +313,9 @@ static bool handle_domain_bar(FILE *rsp, struct token domain, char *message) {
|
|||
struct token position = get_token(&message);
|
||||
if (position.length > 0)
|
||||
needs_refresh = bar_manager_set_position(&g_bar_manager, position.text[0]);
|
||||
}
|
||||
else if (token_equals(command, PROPERTY_HEIGHT)) {
|
||||
} else if (token_equals(command, PROPERTY_CLIP)) {
|
||||
respond(rsp, "[!] Bar: Invalid property 'clip'\n");
|
||||
} else if (token_equals(command, PROPERTY_HEIGHT)) {
|
||||
struct token token = get_token(&message);
|
||||
ANIMATE(bar_manager_set_bar_height,
|
||||
&g_bar_manager,
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
#define PROPERTY_LINE_WIDTH "line_width"
|
||||
#define PROPERTY_BLUR_RADIUS "blur_radius"
|
||||
#define PROPERTY_DRAWING "drawing"
|
||||
#define PROPERTY_CLIP "clip"
|
||||
#define PROPERTY_DISTANCE "distance"
|
||||
#define PROPERTY_ANGLE "angle"
|
||||
#define PROPERTY_SCALE "scale"
|
||||
|
|
|
@ -345,6 +345,19 @@ static inline void draw_rect(CGContextRef context, CGRect region, struct rgba_co
|
|||
CFRelease(path);
|
||||
}
|
||||
|
||||
static inline void clip_rect(CGContextRef context, CGRect region, float clip, uint32_t corner_radius) {
|
||||
CGMutablePathRef path = CGPathCreateMutable();
|
||||
if (corner_radius > region.size.height / 2.f || corner_radius > region.size.width / 2.f)
|
||||
corner_radius = region.size.height > region.size.width ? region.size.width / 2.f : region.size.height / 2.f;
|
||||
CGPathAddRoundedRect(path, NULL, region, corner_radius, corner_radius);
|
||||
CGContextSetBlendMode(context, kCGBlendModeDestinationOut);
|
||||
CGContextSetRGBFillColor(context, 0.f, 0.f, 0.f, clip);
|
||||
CGContextAddPath(context, path);
|
||||
CGContextDrawPath(context, kCGPathFillStroke);
|
||||
CGContextSetBlendMode(context, kCGBlendModeNormal);
|
||||
CFRelease(path);
|
||||
}
|
||||
|
||||
static inline CGRect cgrect_mirror_y(CGRect rect, float y) {
|
||||
CGRect mirrored_rect = rect;
|
||||
mirrored_rect.origin.y = 2*y - rect.origin.y;
|
||||
|
|
Loading…
Add table
Reference in a new issue