From c793bfda23cdae0ddcbf44bde3ff7da319edb20a Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Fri, 20 Jun 2025 03:31:16 +0200 Subject: [PATCH] UI: Area Maintenance Post Op Transitions While performance area maintenance operations (split, join, docking) some areas get highlighted and some are shown darkened to indicate they will be removed. This PR just adds transitions AFTER completion that eases out this highlighting. Make it less jarring and a little easier to follow what happens. Pull Request: https://projects.blender.org/blender/blender/pulls/140628 --- source/blender/editors/screen/screen_draw.cc | 79 +++++++++++++++++++ source/blender/editors/screen/screen_edit.cc | 11 +++ .../blender/editors/screen/screen_intern.hh | 12 +++ source/blender/editors/screen/screen_ops.cc | 42 ++++++++++ .../blender/windowmanager/intern/wm_draw.cc | 3 +- 5 files changed, 146 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/screen/screen_draw.cc b/source/blender/editors/screen/screen_draw.cc index 5ffb7ab32af..31b917483d5 100644 --- a/source/blender/editors/screen/screen_draw.cc +++ b/source/blender/editors/screen/screen_draw.cc @@ -22,6 +22,7 @@ #include "BLI_listbase.h" #include "BLI_math_vector.h" #include "BLI_rect.h" +#include "BLI_time.h" #include "BLT_translation.hh" @@ -667,3 +668,81 @@ void screen_draw_split_preview(ScrArea *area, const eScreenAxis dir_axis, const } UI_draw_roundbox_4fv(&rect, true, 0.0f, border); } + +struct AreaAnimateHighlightData { + wmWindow *win; + bScreen *screen; + rctf rect; + float inner[4]; + float outline[4]; + double start_time; + double end_time; + void *draw_callback; +}; + +static void area_animate_highlight_cb(const wmWindow * /*win*/, void *userdata) +{ + const AreaAnimateHighlightData *data = static_cast(userdata); + + double now = BLI_time_now_seconds(); + if (now > data->end_time) { + WM_draw_cb_exit(data->win, data->draw_callback); + MEM_freeN(const_cast(data)); + data = nullptr; + return; + } + + const float factor = pow((now - data->start_time) / (data->end_time - data->start_time), 2); + const bool do_inner = data->inner[3] > 0.0f; + const bool do_outline = data->outline[3] > 0.0f; + + float inner_color[4]; + if (do_inner) { + inner_color[0] = data->inner[0]; + inner_color[1] = data->inner[1]; + inner_color[2] = data->inner[2]; + inner_color[3] = (1.0f - factor) * data->inner[3]; + } + + float outline_color[4]; + if (do_outline) { + outline_color[0] = data->outline[0]; + outline_color[1] = data->outline[1]; + outline_color[2] = data->outline[2]; + outline_color[3] = (1.0f - factor) * data->outline[3]; + } + + UI_draw_roundbox_corner_set(UI_CNR_ALL); + UI_draw_roundbox_4fv_ex(&data->rect, + do_inner ? inner_color : nullptr, + nullptr, + 1.0f, + do_outline ? outline_color : nullptr, + U.pixelsize, + EDITORRADIUS); + + data->screen->do_refresh = true; +} + +void screen_animate_area_highlight(wmWindow *win, + bScreen *screen, + const rcti *rect, + float inner[4], + float outline[4], + float seconds) +{ + AreaAnimateHighlightData *data = MEM_callocN( + "screen_animate_area_highlight"); + data->win = win; + data->screen = screen; + BLI_rctf_rcti_copy(&data->rect, rect); + if (inner) { + copy_v4_v4(data->inner, inner); + } + if (outline) { + copy_v4_v4(data->outline, outline); + } + data->start_time = BLI_time_now_seconds(); + data->end_time = data->start_time + seconds; + data->draw_callback = WM_draw_cb_activate(win, area_animate_highlight_cb, data); +} diff --git a/source/blender/editors/screen/screen_edit.cc b/source/blender/editors/screen/screen_edit.cc index e5d2d6a872b..eadd34579c6 100644 --- a/source/blender/editors/screen/screen_edit.cc +++ b/source/blender/editors/screen/screen_edit.cc @@ -564,7 +564,18 @@ static bool screen_area_join_ex(bContext *C, if (close_all_remainders || offset1 < 0 || offset2 > 0) { /* Close both if trimming `sa1`. */ + float inner[4] = {0.0f, 0.0f, 0.0f, 0.7f}; + if (side1) { + rcti rect = {side1->v1->vec.x, side1->v3->vec.x, side1->v1->vec.y, side1->v3->vec.y}; + screen_animate_area_highlight( + CTX_wm_window(C), CTX_wm_screen(C), &rect, inner, nullptr, AREA_CLOSE_FADEOUT); + } screen_area_close(C, reports, screen, side1); + if (side2) { + rcti rect = {side2->v1->vec.x, side2->v3->vec.x, side2->v1->vec.y, side2->v3->vec.y}; + screen_animate_area_highlight( + CTX_wm_window(C), CTX_wm_screen(C), &rect, inner, nullptr, AREA_CLOSE_FADEOUT); + } screen_area_close(C, reports, screen, side2); } else { diff --git a/source/blender/editors/screen/screen_intern.hh b/source/blender/editors/screen/screen_intern.hh index 80fedac9cef..44d6db28e50 100644 --- a/source/blender/editors/screen/screen_intern.hh +++ b/source/blender/editors/screen/screen_intern.hh @@ -80,6 +80,11 @@ enum class AreaDockTarget { /* Less expansion needed for global edges. */ #define BORDERPADDING_GLOBAL (3.0f * UI_SCALE_FAC) +#define AREA_CLOSE_FADEOUT 0.22f /* seconds */ +#define AREA_DOCK_FADEOUT 0.20f /* seconds */ +#define AREA_JOIN_FADEOUT 0.15f /* seconds */ +#define AREA_SPLIT_FADEOUT 0.15f /* seconds */ + /* `area.cc` */ /** @@ -112,6 +117,13 @@ void screen_draw_move_highlight(const wmWindow *win, bScreen *screen, eScreenAxi void screen_draw_region_scale_highlight(ARegion *region); +void screen_animate_area_highlight(wmWindow *win, + bScreen *screen, + const rcti *rect, + float inner[4], + float outline[4], + float seconds); + /* `screen_edit.cc` */ /** diff --git a/source/blender/editors/screen/screen_ops.cc b/source/blender/editors/screen/screen_ops.cc index b4afff03054..efab96724c1 100644 --- a/source/blender/editors/screen/screen_ops.cc +++ b/source/blender/editors/screen/screen_ops.cc @@ -1583,6 +1583,10 @@ static wmOperatorStatus area_close_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + float inner[4] = {0.0f, 0.0f, 0.0f, 0.7f}; + screen_animate_area_highlight( + CTX_wm_window(C), CTX_wm_screen(C), &area->totrct, inner, nullptr, AREA_CLOSE_FADEOUT); + if (!screen_area_close(C, op->reports, screen, area)) { BKE_report(op->reports, RPT_ERROR, "Unable to close area"); return OPERATOR_CANCELLED; @@ -2597,6 +2601,14 @@ static wmOperatorStatus area_split_modal(bContext *C, wmOperator *op, const wmEv case LEFTMOUSE: if (sd->previewmode) { + float inner[4] = {1.0f, 1.0f, 1.0f, 0.1f}; + float outline[4] = {1.0f, 1.0f, 1.0f, 0.3f}; + screen_animate_area_highlight(CTX_wm_window(C), + CTX_wm_screen(C), + &sd->sarea->totrct, + inner, + outline, + AREA_SPLIT_FADEOUT); area_split_apply(C, op); area_split_exit(C, op); return OPERATOR_FINISHED; @@ -3819,6 +3831,22 @@ static bool area_join_apply(bContext *C, wmOperator *op) bScreen *screen = CTX_wm_screen(C); + /* Rect of the combined areas. */ + const bool vertical = SCREEN_DIR_IS_VERTICAL(jd->dir); + rcti combined{}; + combined.xmin = vertical ? std::max(jd->sa1->totrct.xmin, jd->sa2->totrct.xmin) : + std::min(jd->sa1->totrct.xmin, jd->sa2->totrct.xmin); + combined.xmax = vertical ? std::min(jd->sa1->totrct.xmax, jd->sa2->totrct.xmax) : + std::max(jd->sa1->totrct.xmax, jd->sa2->totrct.xmax); + combined.ymin = vertical ? std::min(jd->sa1->totrct.ymin, jd->sa2->totrct.ymin) : + std::max(jd->sa1->totrct.ymin, jd->sa2->totrct.ymin); + combined.ymax = vertical ? std::max(jd->sa1->totrct.ymax, jd->sa2->totrct.ymax) : + std::min(jd->sa1->totrct.ymax, jd->sa2->totrct.ymax); + float inner[4] = {1.0f, 1.0f, 1.0f, 0.1f}; + float outline[4] = {1.0f, 1.0f, 1.0f, 0.3f}; + screen_animate_area_highlight( + CTX_wm_window(C), screen, &combined, inner, outline, AREA_JOIN_FADEOUT); + if (!screen_area_join(C, op->reports, screen, jd->sa1, jd->sa2)) { return false; } @@ -3971,6 +3999,13 @@ void static area_docking_apply(bContext *C, wmOperator *op) return; } + float inner[4] = {1.0f, 1.0f, 1.0f, 0.15f}; + float outline[4] = {1.0f, 1.0f, 1.0f, 0.4f}; + jd->sa2->flag |= AREA_FLAG_REGION_SIZE_UPDATE; + ED_area_update_region_sizes(CTX_wm_manager(C), jd->win2, jd->sa2); + screen_animate_area_highlight( + jd->win2, CTX_wm_screen(C), &jd->sa2->totrct, inner, outline, AREA_DOCK_FADEOUT); + if (!aligned_neighbors || !screen_area_join(C, op->reports, CTX_wm_screen(C), jd->sa1, jd->sa2)) { ED_area_swapspace(C, jd->sa2, jd->sa1); @@ -3984,6 +4019,9 @@ void static area_docking_apply(bContext *C, wmOperator *op) WM_window_get_active_screen(jd->win2)->active_region = nullptr; } else { + float inner[4] = {0.0f, 0.0f, 0.0f, 0.7f}; + screen_animate_area_highlight( + jd->win1, CTX_wm_screen(C), &jd->sa1->totrct, inner, nullptr, AREA_CLOSE_FADEOUT); screen_area_close(C, op->reports, CTX_wm_screen(C), jd->sa1); } } @@ -4416,6 +4454,10 @@ static wmOperatorStatus area_join_modal(bContext *C, wmOperator *op, const wmEve else if (jd->sa1 && jd->sa1 == jd->sa2) { /* Same area so split. */ if (area_split_allowed(jd->sa1, jd->split_dir) && jd->split_fac > 0.0001) { + float inner[4] = {1.0f, 1.0f, 1.0f, 0.1f}; + float outline[4] = {1.0f, 1.0f, 1.0f, 0.3f}; + screen_animate_area_highlight( + jd->win1, CTX_wm_screen(C), &jd->sa1->totrct, inner, outline, AREA_SPLIT_FADEOUT); jd->sa2 = area_split(jd->win2, WM_window_get_active_screen(jd->win1), jd->sa1, diff --git a/source/blender/windowmanager/intern/wm_draw.cc b/source/blender/windowmanager/intern/wm_draw.cc index 4b525a55b83..9d5d506cb4c 100644 --- a/source/blender/windowmanager/intern/wm_draw.cc +++ b/source/blender/windowmanager/intern/wm_draw.cc @@ -632,7 +632,8 @@ void WM_draw_cb_exit(wmWindow *win, void *handle) static void wm_draw_callbacks(wmWindow *win) { - LISTBASE_FOREACH (WindowDrawCB *, wdc, &win->drawcalls) { + /* Allow callbacks to remove themselves. */ + LISTBASE_FOREACH_MUTABLE (WindowDrawCB *, wdc, &win->drawcalls) { wdc->draw(win, wdc->customdata); } }