diff --git a/src/animation/client.h b/src/animation/client.h index 3622891..184abf6 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -932,12 +932,35 @@ bool client_draw_frame(Client *c) { if (!c || !client_surface(c)->mapped) return false; + // Animate focus transitions (opacity + border color) if (c->isfullscreen) { client_set_opacity(c, 1); - } else if (c == selmon->sel && !c->animation.running) { - client_set_opacity(c, c->focused_opacity); - } else if (!c->animation.running) { - client_set_opacity(c, c->unfocused_opacity); + c->current_opacity = 1; + c->target_opacity = 1; + } else if (c->opacity_animation_frames > 0 && c->opacity_animation_passed < c->opacity_animation_frames) { + float linear_progress = (float)c->opacity_animation_passed / c->opacity_animation_frames; + float eased_progress = find_animation_curve_at(linear_progress, FOCUS); + + // Animate opacity + float opacity_start = (c->target_opacity == c->focused_opacity) ? c->unfocused_opacity : c->focused_opacity; + c->current_opacity = opacity_start + (c->target_opacity - opacity_start) * eased_progress; + client_set_opacity(c, c->current_opacity); + + // Animate border color + bool focusing = (c->target_border_color[0] == focuscolor[0]); + float *border_start = focusing ? bordercolor : focuscolor; + for (int i = 0; i < 4; i++) { + c->current_border_color[i] = border_start[i] + (c->target_border_color[i] - border_start[i]) * eased_progress; + } + client_set_border_color(c, c->current_border_color); + + c->opacity_animation_passed++; + } else { + // Animation complete or disabled - apply target values + c->current_opacity = c->target_opacity; + client_set_opacity(c, c->current_opacity); + memcpy(c->current_border_color, c->target_border_color, sizeof(c->current_border_color)); + client_set_border_color(c, c->current_border_color); } if (!c->need_output_flush) diff --git a/src/animation/common.h b/src/animation/common.h index 7739373..2da27de 100644 --- a/src/animation/common.h +++ b/src/animation/common.h @@ -9,6 +9,8 @@ struct dvec2 calculate_animation_curve_at(double t, int type) { animation_curve = animation_curve_tag; } else if (type == CLOSE) { animation_curve = animation_curve_close; + } else if (type == FOCUS) { + animation_curve = animation_curve_focus; } else { animation_curve = animation_curve_move; } @@ -28,6 +30,8 @@ void init_baked_points(void) { baked_points_tag = calloc(BAKED_POINTS_COUNT, sizeof(*baked_points_tag)); baked_points_close = calloc(BAKED_POINTS_COUNT, sizeof(*baked_points_close)); + baked_points_focus = + calloc(BAKED_POINTS_COUNT, sizeof(*baked_points_focus)); for (unsigned int i = 0; i < BAKED_POINTS_COUNT; i++) { baked_points_move[i] = calculate_animation_curve_at( @@ -45,6 +49,10 @@ void init_baked_points(void) { baked_points_close[i] = calculate_animation_curve_at( (double)i / (BAKED_POINTS_COUNT - 1), CLOSE); } + for (unsigned int i = 0; i < BAKED_POINTS_COUNT; i++) { + baked_points_focus[i] = calculate_animation_curve_at( + (double)i / (BAKED_POINTS_COUNT - 1), FOCUS); + } } double find_animation_curve_at(double t, int type) { @@ -61,6 +69,8 @@ double find_animation_curve_at(double t, int type) { baked_points = baked_points_tag; } else if (type == CLOSE) { baked_points = baked_points_close; + } else if (type == FOCUS) { + baked_points = baked_points_focus; } else { baked_points = baked_points_move; } diff --git a/src/config/parse_config.h b/src/config/parse_config.h index 258d70a..04bdfbd 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -176,10 +176,12 @@ typedef struct { uint32_t animation_duration_open; uint32_t animation_duration_tag; uint32_t animation_duration_close; + uint32_t animation_duration_focus; double animation_curve_move[4]; double animation_curve_open[4]; double animation_curve_tag[4]; double animation_curve_close[4]; + double animation_curve_focus[4]; int scroller_structs; float scroller_default_proportion; @@ -1113,6 +1115,8 @@ void parse_option(Config *config, char *key, char *value) { config->animation_duration_tag = atoi(value); } else if (strcmp(key, "animation_duration_close") == 0) { config->animation_duration_close = atoi(value); + } else if (strcmp(key, "animation_duration_focus") == 0) { + config->animation_duration_focus = atoi(value); } else if (strcmp(key, "animation_curve_move") == 0) { int num = parse_double_array(value, config->animation_curve_move, 4); if (num != 4) { @@ -1138,6 +1142,13 @@ void parse_option(Config *config, char *key, char *value) { "Error: Failed to parse animation_curve_close: %s\n", value); } + } else if (strcmp(key, "animation_curve_focus") == 0) { + int num = parse_double_array(value, config->animation_curve_focus, 4); + if (num != 4) { + fprintf(stderr, + "Error: Failed to parse animation_curve_focus: %s\n", + value); + } } else if (strcmp(key, "scroller_structs") == 0) { config->scroller_structs = atoi(value); } else if (strcmp(key, "scroller_default_proportion") == 0) { @@ -2527,6 +2538,8 @@ void override_config(void) { animation_duration_tag = CLAMP_INT(config.animation_duration_tag, 1, 50000); animation_duration_close = CLAMP_INT(config.animation_duration_close, 1, 50000); + animation_duration_focus = + CLAMP_INT(config.animation_duration_focus, 1, 50000); // 滚动布局设置 scroller_default_proportion = @@ -2640,6 +2653,8 @@ void override_config(void) { sizeof(animation_curve_tag)); memcpy(animation_curve_close, config.animation_curve_close, sizeof(animation_curve_close)); + memcpy(animation_curve_focus, config.animation_curve_focus, + sizeof(animation_curve_focus)); } void set_value_default() { @@ -2662,6 +2677,8 @@ void set_value_default() { animation_duration_tag; // Animation tag speed config.animation_duration_close = animation_duration_close; // Animation tag speed + config.animation_duration_focus = + animation_duration_focus; // Animation focus opacity speed /* appearance */ config.axis_bind_apply_timeout = @@ -2760,6 +2777,8 @@ void set_value_default() { sizeof(animation_curve_tag)); memcpy(config.animation_curve_close, animation_curve_close, sizeof(animation_curve_close)); + memcpy(config.animation_curve_focus, animation_curve_focus, + sizeof(animation_curve_focus)); memcpy(config.rootcolor, rootcolor, sizeof(rootcolor)); memcpy(config.bordercolor, bordercolor, sizeof(bordercolor)); diff --git a/src/config/preset.h b/src/config/preset.h index b7695f2..63ca5fc 100644 --- a/src/config/preset.h +++ b/src/config/preset.h @@ -25,10 +25,12 @@ uint32_t animation_duration_move = 500; // Animation move speed uint32_t animation_duration_open = 400; // Animation open speed uint32_t animation_duration_tag = 300; // Animation tag speed uint32_t animation_duration_close = 300; // Animation close speed +uint32_t animation_duration_focus = 400; // Animation focus opacity speed double animation_curve_move[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线 double animation_curve_open[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线 double animation_curve_tag[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线 double animation_curve_close[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线 +double animation_curve_focus[4] = {0.08, 0.82, 0.17, 1}; // 动画曲线 /* appearance */ unsigned int axis_bind_apply_timeout = 100; // 滚轮绑定动作的触发的时间间隔 diff --git a/src/mango.c b/src/mango.c index fb346f4..e17471f 100644 --- a/src/mango.c +++ b/src/mango.c @@ -157,7 +157,7 @@ enum { }; /* EWMH atoms */ #endif enum { UP, DOWN, LEFT, RIGHT, UNDIR }; /* smartmovewin */ -enum { NONE, OPEN, MOVE, CLOSE, TAG }; +enum { NONE, OPEN, MOVE, CLOSE, TAG, FOCUS }; enum { UNFOLD, FOLD, INVALIDFOLD }; enum { PREV, NEXT }; @@ -339,6 +339,12 @@ struct Client { int isunglobal; float focused_opacity; float unfocused_opacity; + float current_opacity; + float target_opacity; + unsigned int opacity_animation_frames; + unsigned int opacity_animation_passed; + float current_border_color[4]; + float target_border_color[4]; char oldmonname[128]; struct wlr_ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel; double master_mfact_per, master_inner_per, stack_innder_per; @@ -787,6 +793,7 @@ struct dvec2 *baked_points_move; struct dvec2 *baked_points_open; struct dvec2 *baked_points_tag; struct dvec2 *baked_points_close; +struct dvec2 *baked_points_focus; static struct wl_event_source *hide_source; static bool cursor_hidden = false; @@ -3036,7 +3043,13 @@ void focusclient(Client *c, int lift) { // change border color c->isurgent = 0; - setborder_color(c); + // Start border color animation to focused + memcpy(c->target_border_color, focuscolor, sizeof(c->target_border_color)); + + // Start opacity animation to focused + c->target_opacity = c->focused_opacity; + c->opacity_animation_frames = (animation_duration_focus * 60) / 1000; // 60fps + c->opacity_animation_passed = 0; } if (c && !c->iskilling && c->foreign_toplevel) @@ -3064,7 +3077,13 @@ void focusclient(Client *c, int lift) { * probably other clients */ } else if (w && !client_is_unmanaged(w) && (!c || !client_wants_focus(c))) { - setborder_color(w); + // Start border color animation to unfocused + memcpy(w->target_border_color, bordercolor, sizeof(w->target_border_color)); + + // Start opacity animation to unfocused + w->target_opacity = w->unfocused_opacity; + w->opacity_animation_frames = (animation_duration_focus * 60) / 1000; // 60fps + w->opacity_animation_passed = 0; client_activate_surface(old_keyboard_focus_surface, 0); } @@ -3420,6 +3439,12 @@ void init_client_properties(Client *c) { c->fake_no_border = false; c->focused_opacity = focused_opacity; c->unfocused_opacity = unfocused_opacity; + c->current_opacity = unfocused_opacity; + c->target_opacity = unfocused_opacity; + c->opacity_animation_frames = 0; + c->opacity_animation_passed = 0; + memcpy(c->current_border_color, bordercolor, sizeof(c->current_border_color)); + memcpy(c->target_border_color, bordercolor, sizeof(c->target_border_color)); c->nofadein = 0; c->nofadeout = 0; c->no_force_center = 0;