feat: add smooth focus transition animation for opacity and border

This adds animated transitions when switching focus between windows.
Both window opacity and border color now fade smoothly using cubic
bezier easing instead of changing instantly.

Implementation:
- Added animation_duration_focus config option (default 400ms)
- Added animation_curve_focus for cubic bezier easing curve
- Window opacity and border color animate together when focus changes
- Uses existing animation infrastructure (baked bezier points)

The feature is backwards compatible and can be disabled by setting
animation_duration_focus=0 in config file.

Changes affect 5 files with minimal additions to keep code clean.
This commit is contained in:
Szymon Rączka
2025-10-31 22:33:38 +01:00
parent 5bb149f84e
commit 8f706688de
5 changed files with 86 additions and 7 deletions

View File

@@ -178,10 +178,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;
@@ -1132,6 +1134,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) {
@@ -1157,6 +1161,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) {
@@ -2595,6 +2606,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 =
@@ -2727,6 +2740,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() {
@@ -2749,6 +2764,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 =
@@ -2866,6 +2883,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));