mirror of
https://github.com/FelixKratz/SketchyBar
synced 2024-11-26 21:30:17 +00:00
Merge pull request #276 from izeau/background-clip
Add bar background clipping
This commit is contained in:
commit
b4499156f5
14 changed files with 253 additions and 30 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;
|
||||
|
|
110
src/background.c
110
src/background.c
|
@ -7,6 +7,9 @@
|
|||
|
||||
void background_init(struct background* background) {
|
||||
background->enabled = false;
|
||||
background->clip = 0.f;
|
||||
background->clips = NULL;
|
||||
background->num_clips = 0;
|
||||
background->overrides_height = false;
|
||||
|
||||
background->bounds.size.height = 25;
|
||||
|
@ -30,9 +33,34 @@ bool background_set_height(struct background* background, uint32_t height) {
|
|||
return true;
|
||||
}
|
||||
|
||||
static void background_reset_clip(struct background* background) {
|
||||
for (uint32_t i = 0; i < background->num_clips; i++)
|
||||
free(background->clips[i]);
|
||||
|
||||
if (background->clips) free(background->clips);
|
||||
background->clips = NULL;
|
||||
background->num_clips = 0;
|
||||
}
|
||||
|
||||
static bool background_set_enabled(struct background* background, bool enabled) {
|
||||
if (background->enabled == enabled) return false;
|
||||
|
||||
if (background_clips_bar(background)) {
|
||||
background_reset_clip(background);
|
||||
g_bar_manager.bar_needs_update = true;
|
||||
}
|
||||
|
||||
background->enabled = enabled;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool background_set_clip(struct background* background, float clip) {
|
||||
if (background->clip == clip) return false;
|
||||
background->clip = clip;
|
||||
g_bar_manager.bar_needs_update = true;
|
||||
g_bar_manager.might_need_clipping = true;
|
||||
if (clip > 0.f) background_set_enabled(background, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -87,9 +115,68 @@ static bool background_set_yoffset(struct background* background, int offset) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void background_calculate_bounds(struct background* background, uint32_t x, uint32_t y) {
|
||||
bool background_clip_needs_update(struct background* background, struct bar* bar) {
|
||||
if (background->clip == 0.f || !background->enabled) return false;
|
||||
struct background* clip = background_get_clip(background, bar->adid);
|
||||
if (!CGRectEqualToRect(background->bounds, clip->bounds)) return true;
|
||||
if (background->corner_radius != clip->corner_radius) return true;
|
||||
if (background->y_offset != clip->y_offset) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void background_update_clip(struct background* background, struct background* clip) {
|
||||
memcpy(clip, background, sizeof(struct background));
|
||||
background_clear_pointers(clip);
|
||||
}
|
||||
|
||||
struct background* background_get_clip(struct background* background, uint32_t adid) {
|
||||
if (adid < 1) return NULL;
|
||||
if (background->num_clips < adid) {
|
||||
background->clips = (struct background**) realloc(background->clips,
|
||||
sizeof(struct background*)*adid);
|
||||
memset(background->clips + background->num_clips,
|
||||
0,
|
||||
sizeof(struct background*) * (adid - background->num_clips));
|
||||
|
||||
background->num_clips = adid;
|
||||
}
|
||||
|
||||
if (!background->clips[adid - 1]) {
|
||||
struct background* clip = malloc(sizeof(struct background));
|
||||
background->clips[adid - 1] = clip;
|
||||
background_init(clip);
|
||||
background_update_clip(background, clip);
|
||||
clip->bounds.origin = g_nirvana;
|
||||
}
|
||||
|
||||
return background->clips[adid - 1];
|
||||
}
|
||||
|
||||
bool background_clips_bar(struct background* background) {
|
||||
return background->enabled && (background->clip > 0.f);
|
||||
}
|
||||
|
||||
void background_clip_bar(struct background* background, int offset, struct bar* bar) {
|
||||
if (!background_clips_bar(background)) return;
|
||||
|
||||
struct background* clip = background_get_clip(background, bar->adid);
|
||||
background_update_clip(background, clip);
|
||||
|
||||
CGRect background_bounds = background->bounds;
|
||||
background_bounds.origin.x += offset;
|
||||
background_bounds.origin.y += background->y_offset;
|
||||
|
||||
clip_rect(bar->window.context,
|
||||
background_bounds,
|
||||
background->clip,
|
||||
background->corner_radius);
|
||||
}
|
||||
|
||||
void background_calculate_bounds(struct background* background, uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
|
||||
background->bounds.origin.x = x;
|
||||
background->bounds.origin.y = y - background->bounds.size.height / 2;
|
||||
background->bounds.size.width = width;
|
||||
background->bounds.size.height = height;
|
||||
|
||||
if (background->image.enabled)
|
||||
image_calculate_bounds(&background->image, x, y);
|
||||
|
@ -123,10 +210,17 @@ void background_draw(struct background* background, CGContextRef context) {
|
|||
}
|
||||
|
||||
void background_clear_pointers(struct background* background) {
|
||||
background->clips = NULL;
|
||||
background->num_clips = 0;
|
||||
image_clear_pointers(&background->image);
|
||||
}
|
||||
|
||||
void background_destroy(struct background* background) {
|
||||
for (uint32_t i = 0; i < background->num_clips; i++)
|
||||
free(background->clips[i]);
|
||||
|
||||
if (background->clips) free(background->clips);
|
||||
|
||||
image_destroy(&background->image);
|
||||
background_clear_pointers(background);
|
||||
}
|
||||
|
@ -140,7 +234,8 @@ void background_serialize(struct background* background, char* indent, FILE* rsp
|
|||
"%s\"corner_radius\": %u,\n"
|
||||
"%s\"padding_left\": %d,\n"
|
||||
"%s\"padding_right\": %d,\n"
|
||||
"%s\"y_offset\": %d,\n",
|
||||
"%s\"y_offset\": %d,\n"
|
||||
"%s\"clip\": %f,\n",
|
||||
indent, format_bool(background->enabled),
|
||||
indent, hex_from_rgba_color(background->color),
|
||||
indent, hex_from_rgba_color(background->border_color),
|
||||
|
@ -149,7 +244,8 @@ void background_serialize(struct background* background, char* indent, FILE* rsp
|
|||
indent, background->corner_radius,
|
||||
indent, background->padding_left,
|
||||
indent, background->padding_right,
|
||||
indent, background->y_offset );
|
||||
indent, background->y_offset,
|
||||
indent, background->clip );
|
||||
|
||||
char deeper_indent[strlen(indent) + 2];
|
||||
snprintf(deeper_indent, strlen(indent) + 2, "%s\t", indent);
|
||||
|
@ -171,7 +267,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,
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
#pragma once
|
||||
#include "image.h"
|
||||
|
||||
struct bar;
|
||||
|
||||
struct background {
|
||||
bool enabled;
|
||||
float clip;
|
||||
bool overrides_height;
|
||||
|
||||
int padding_left;
|
||||
|
@ -16,14 +19,21 @@ struct background {
|
|||
struct shadow shadow;
|
||||
struct rgba_color color;
|
||||
struct rgba_color border_color;
|
||||
|
||||
struct background** clips;
|
||||
uint32_t num_clips;
|
||||
};
|
||||
|
||||
void background_init(struct background* background);
|
||||
void background_calculate_bounds(struct background* background, uint32_t x, uint32_t y);
|
||||
void background_calculate_bounds(struct background* background, uint32_t x, uint32_t y, uint32_t width, uint32_t height);
|
||||
|
||||
bool background_set_height(struct background* background, uint32_t height);
|
||||
|
||||
void background_draw(struct background* background, CGContextRef context);
|
||||
struct background* background_get_clip(struct background* background, uint32_t adid);
|
||||
void background_clip_bar(struct background* background, int offset, struct bar* bar);
|
||||
bool background_clip_needs_update(struct background* background, struct bar* bar);
|
||||
bool background_clips_bar(struct background* background);
|
||||
|
||||
void background_clear_pointers(struct background* background);
|
||||
void background_destroy(struct background* background);
|
||||
|
|
43
src/bar.c
43
src/bar.c
|
@ -110,9 +110,41 @@ void bar_order_item_windows(struct bar* bar) {
|
|||
}
|
||||
}
|
||||
|
||||
static void bar_check_for_clip_updates(struct bar* bar) {
|
||||
if (!g_bar_manager.bar_needs_update) {
|
||||
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);
|
||||
|
||||
bool clips_bar = bar_item_clips_bar(bar_item);
|
||||
if (!clips_bar || (!bar_draws_item(bar, bar_item)
|
||||
|| (bar_item->type == BAR_COMPONENT_GROUP
|
||||
&& !bar_draws_item(bar, group_get_first_member(bar_item->group))))) {
|
||||
|
||||
if (clips_bar && !CGPointEqualToPoint(window->origin, g_nirvana)) {
|
||||
g_bar_manager.bar_needs_update = true;
|
||||
break;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
g_bar_manager.bar_needs_update
|
||||
|= window->needs_move
|
||||
|| window->needs_resize
|
||||
|| bar_item_clip_needs_update_for_bar(bar_item, bar);
|
||||
|
||||
if (g_bar_manager.bar_needs_update) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bar_draw(struct bar* bar) {
|
||||
if (bar->sid < 1 || bar->adid < 1) return;
|
||||
|
||||
if (g_bar_manager.might_need_clipping)
|
||||
bar_check_for_clip_updates(bar);
|
||||
|
||||
if (g_bar_manager.bar_needs_update) {
|
||||
draw_rect(bar->window.context,
|
||||
bar->window.frame,
|
||||
|
@ -125,8 +157,6 @@ 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++) {
|
||||
|
@ -150,6 +180,12 @@ void bar_draw(struct bar* bar) {
|
|||
if (bar_item->popup.drawing && bar->adid == g_bar_manager.active_adid)
|
||||
popup_draw(&bar_item->popup);
|
||||
|
||||
if (g_bar_manager.bar_needs_update) {
|
||||
bar_item_clip_bar(bar_item,
|
||||
window->origin.x - bar->window.origin.x,
|
||||
bar );
|
||||
}
|
||||
|
||||
if (!window_apply_frame(window) && !bar_item->needs_update) continue;
|
||||
|
||||
if (bar_item->update_mask & UPDATE_MOUSE_ENTERED
|
||||
|
@ -162,6 +198,9 @@ 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) {
|
||||
|
|
|
@ -582,20 +582,43 @@ uint32_t bar_item_calculate_bounds(struct bar_item* bar_item, uint32_t bar_heigh
|
|||
}
|
||||
|
||||
if (bar_item->background.enabled) {
|
||||
bar_item->background.bounds.size.height = bar_item->background.overrides_height
|
||||
? bar_item->background.bounds.size.height
|
||||
: (bar_height
|
||||
- (g_bar_manager.background.border_width + 1));
|
||||
uint32_t height = bar_item->background.overrides_height
|
||||
? bar_item->background.bounds.size.height
|
||||
: (bar_height
|
||||
- (g_bar_manager.background.border_width + 1));
|
||||
|
||||
bar_item->background.bounds.size.width = bar_item_length;
|
||||
background_calculate_bounds(&bar_item->background,
|
||||
x,
|
||||
content_y + bar_item->y_offset );
|
||||
content_y + bar_item->y_offset,
|
||||
bar_item_length,
|
||||
height );
|
||||
}
|
||||
|
||||
return bar_item_length;
|
||||
}
|
||||
|
||||
bool bar_item_clip_needs_update_for_bar(struct bar_item* bar_item, struct bar* bar) {
|
||||
bool needs_update = false;
|
||||
|
||||
needs_update |= background_clip_needs_update(&bar_item->background, bar)
|
||||
| background_clip_needs_update(&bar_item->icon.background, bar)
|
||||
| background_clip_needs_update(&bar_item->label.background, bar);
|
||||
|
||||
return needs_update;
|
||||
}
|
||||
|
||||
bool bar_item_clips_bar(struct bar_item* bar_item) {
|
||||
return background_clips_bar(&bar_item->background)
|
||||
|| background_clips_bar(&bar_item->icon.background)
|
||||
|| background_clips_bar(&bar_item->label.background);
|
||||
}
|
||||
|
||||
void bar_item_clip_bar(struct bar_item* bar_item, int offset, struct bar* bar) {
|
||||
background_clip_bar(&bar_item->background, offset, bar);
|
||||
background_clip_bar(&bar_item->icon.background, offset, bar);
|
||||
background_clip_bar(&bar_item->label.background, offset, bar);
|
||||
}
|
||||
|
||||
void bar_item_draw(struct bar_item* bar_item, CGContextRef context) {
|
||||
background_draw(&bar_item->background, context);
|
||||
if (bar_item->type == BAR_COMPONENT_GROUP) return;
|
||||
|
@ -829,9 +852,8 @@ void bar_item_parse_set_message(struct bar_item* bar_item, char* message, FILE*
|
|||
entry,
|
||||
message );
|
||||
|
||||
else {
|
||||
else
|
||||
respond(rsp, "[!] Item (%s): Invalid subdomain '%s'\n", bar_item->name, subdom.text);
|
||||
}
|
||||
}
|
||||
else if (token_equals(property, PROPERTY_ICON)) {
|
||||
needs_refresh = text_set_string(&bar_item->icon,
|
||||
|
|
|
@ -106,6 +106,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);
|
||||
|
@ -119,6 +120,9 @@ CGRect bar_item_construct_bounding_rect(struct bar_item* bar_item);
|
|||
CGPoint bar_item_calculate_shadow_offsets(struct bar_item* bar_item);
|
||||
uint32_t bar_item_calculate_bounds(struct bar_item* bar_item, uint32_t bar_height, uint32_t x, uint32_t y);
|
||||
void bar_item_draw(struct bar_item* bar_item, CGContextRef context);
|
||||
bool bar_item_clip_needs_update_for_bar(struct bar_item* bar_item, struct bar* bar);
|
||||
void bar_item_clip_bar(struct bar_item* bar_item, int offset, struct bar* bar);
|
||||
bool bar_item_clips_bar(struct bar_item* bar_item);
|
||||
|
||||
void bar_item_change_space(struct bar_item* bar_item, uint64_t dsid, uint32_t adid);
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ void bar_manager_init(struct bar_manager* bar_manager) {
|
|||
bar_manager->notch_width = 200;
|
||||
bar_manager->notch_offset = 0;
|
||||
bar_manager->active_adid = display_arrangement(display_active_display_id());
|
||||
bar_manager->might_need_clipping = false;
|
||||
|
||||
background_init(&bar_manager->background);
|
||||
bar_manager->background.bounds.size.height = 25;
|
||||
|
|
|
@ -18,6 +18,7 @@ struct bar_manager {
|
|||
bool font_smoothing;
|
||||
bool any_bar_hidden;
|
||||
bool needs_ordering;
|
||||
bool might_need_clipping;
|
||||
bool bar_needs_update;
|
||||
bool bar_needs_resize;
|
||||
|
||||
|
|
11
src/group.c
11
src/group.c
|
@ -96,12 +96,11 @@ void group_destroy(struct group* group) {
|
|||
}
|
||||
|
||||
void group_calculate_bounds(struct group* group, struct bar* bar, uint32_t x, uint32_t y, bool rtl) {
|
||||
background_calculate_bounds(&group->members[0]->background, x, y);
|
||||
group->members[0]->background.bounds.size.width = group_get_length(group, bar);
|
||||
group->members[0]->background.bounds.origin.x = x;
|
||||
group->members[0]->background.bounds.origin.y = y
|
||||
- group->members[0]->background.bounds.size.height / 2
|
||||
+ group->members[0]->y_offset;
|
||||
background_calculate_bounds(&group->members[0]->background,
|
||||
x,
|
||||
y + group->members[0]->y_offset,
|
||||
group_get_length(group, bar),
|
||||
group->members[0]->background.bounds.size.height);
|
||||
}
|
||||
|
||||
void group_draw(struct group* group, CGContextRef context) {
|
||||
|
|
|
@ -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;
|
||||
|
|
14
src/text.c
14
src/text.c
|
@ -253,11 +253,15 @@ void text_calculate_bounds(struct text* text, uint32_t x, uint32_t y) {
|
|||
- text->line.descent) / 2));
|
||||
|
||||
if (text->background.enabled) {
|
||||
text->background.bounds.size.width = text_get_length(text, false);
|
||||
text->background.bounds.size.height = text->background.overrides_height
|
||||
? text->background.bounds.size.height
|
||||
: text->bounds.size.height;
|
||||
background_calculate_bounds(&text->background, x, y);
|
||||
uint32_t height = text->background.overrides_height
|
||||
? text->background.bounds.size.height
|
||||
: text->bounds.size.height;
|
||||
|
||||
background_calculate_bounds(&text->background,
|
||||
x,
|
||||
y,
|
||||
text_get_length(text, false),
|
||||
height );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue