When height is limited, it is defined by space occupied by strips, but at least channels 1 to 7 will be always visible. This allows it to easily overview timeline content by zooming out to maximum extent in Y axis and panning in X axis. More channels can be "created" on demand by moving strip to higher channel. When strip is removed and highest channel becomes empty, view will stay as is until it is moved down. Then new highest point is remembered and it is not possible to pan upwards until strip is moved to higher channel. Limiting takes into account height of scrubbing and markers area as well as scrollers. This means that when zoomed out to maximum extent, no strips are obstructed by fixed UI element. Fixes T57976 Reviewed By: Severin Differential Revision: https://developer.blender.org/D14263
250 lines
7.7 KiB
C
250 lines
7.7 KiB
C
/* SPDX-License-Identifier: GPL-2.0-or-later
|
|
* Copyright 2019 Blender Foundation. All rights reserved. */
|
|
|
|
/** \file
|
|
* \ingroup edanimation
|
|
*/
|
|
|
|
#include "BKE_context.h"
|
|
#include "BKE_scene.h"
|
|
|
|
#include "GPU_immediate.h"
|
|
#include "GPU_matrix.h"
|
|
#include "GPU_state.h"
|
|
|
|
#include "ED_time_scrub_ui.h"
|
|
|
|
#include "WM_api.h"
|
|
#include "WM_types.h"
|
|
|
|
#include "UI_interface.h"
|
|
#include "UI_interface_icons.h"
|
|
#include "UI_resources.h"
|
|
#include "UI_view2d.h"
|
|
|
|
#include "DNA_scene_types.h"
|
|
|
|
#include "BLI_math.h"
|
|
#include "BLI_rect.h"
|
|
#include "BLI_string.h"
|
|
#include "BLI_timecode.h"
|
|
|
|
#include "RNA_access.h"
|
|
#include "RNA_prototypes.h"
|
|
|
|
void ED_time_scrub_region_rect_get(const ARegion *region, rcti *rect)
|
|
{
|
|
rect->xmin = 0;
|
|
rect->xmax = region->winx;
|
|
rect->ymax = region->winy;
|
|
rect->ymin = rect->ymax - UI_TIME_SCRUB_MARGIN_Y;
|
|
}
|
|
|
|
static int get_centered_text_y(const rcti *rect)
|
|
{
|
|
return BLI_rcti_cent_y(rect) - UI_DPI_FAC * 4;
|
|
}
|
|
|
|
static void draw_background(const rcti *rect)
|
|
{
|
|
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
|
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
|
|
|
|
immUniformThemeColor(TH_TIME_SCRUB_BACKGROUND);
|
|
|
|
GPU_blend(GPU_BLEND_ALPHA);
|
|
|
|
immRectf(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax);
|
|
|
|
GPU_blend(GPU_BLEND_NONE);
|
|
|
|
immUnbindProgram();
|
|
}
|
|
|
|
static void get_current_time_str(
|
|
const Scene *scene, bool display_seconds, int frame, uint max_len, char *r_str)
|
|
{
|
|
if (display_seconds) {
|
|
BLI_timecode_string_from_time(r_str, max_len, 0, FRA2TIME(frame), FPS, U.timecode_style);
|
|
}
|
|
else {
|
|
BLI_snprintf(r_str, max_len, "%d", frame);
|
|
}
|
|
}
|
|
|
|
static void draw_current_frame(const Scene *scene,
|
|
bool display_seconds,
|
|
const View2D *v2d,
|
|
const rcti *scrub_region_rect,
|
|
int current_frame)
|
|
{
|
|
const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
|
|
int frame_x = UI_view2d_view_to_region_x(v2d, current_frame);
|
|
|
|
char frame_str[64];
|
|
get_current_time_str(scene, display_seconds, current_frame, sizeof(frame_str), frame_str);
|
|
float text_width = UI_fontstyle_string_width(fstyle, frame_str);
|
|
float box_width = MAX2(text_width + 8 * UI_DPI_FAC, 24 * UI_DPI_FAC);
|
|
float box_padding = 3 * UI_DPI_FAC;
|
|
const int line_outline = max_ii(1, round_fl_to_int(1 * UI_DPI_FAC));
|
|
|
|
float bg_color[4];
|
|
UI_GetThemeColorShade4fv(TH_CFRAME, -5, bg_color);
|
|
|
|
/* Draw vertical line from the bottom of the current frame box to the bottom of the screen. */
|
|
const float subframe_x = UI_view2d_view_to_region_x(v2d, BKE_scene_ctime_get(scene));
|
|
GPUVertFormat *format = immVertexFormat();
|
|
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
|
|
|
GPU_blend(GPU_BLEND_ALPHA);
|
|
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
|
|
|
|
/* Outline. */
|
|
immUniformThemeColorShadeAlpha(TH_BACK, -25, -100);
|
|
immRectf(pos,
|
|
subframe_x - (line_outline + U.pixelsize),
|
|
scrub_region_rect->ymax - box_padding,
|
|
subframe_x + (line_outline + U.pixelsize),
|
|
0.0f);
|
|
|
|
/* Line. */
|
|
immUniformThemeColor(TH_CFRAME);
|
|
immRectf(pos,
|
|
subframe_x - U.pixelsize,
|
|
scrub_region_rect->ymax - box_padding,
|
|
subframe_x + U.pixelsize,
|
|
0.0f);
|
|
immUnbindProgram();
|
|
GPU_blend(GPU_BLEND_NONE);
|
|
|
|
UI_draw_roundbox_corner_set(UI_CNR_ALL);
|
|
|
|
float outline_color[4];
|
|
UI_GetThemeColorShade4fv(TH_CFRAME, 5, outline_color);
|
|
|
|
UI_draw_roundbox_4fv_ex(
|
|
&(const rctf){
|
|
.xmin = frame_x - box_width / 2 + U.pixelsize / 2,
|
|
.xmax = frame_x + box_width / 2 + U.pixelsize / 2,
|
|
.ymin = scrub_region_rect->ymin + box_padding,
|
|
.ymax = scrub_region_rect->ymax - box_padding,
|
|
},
|
|
bg_color,
|
|
NULL,
|
|
1.0f,
|
|
outline_color,
|
|
U.pixelsize,
|
|
4 * UI_DPI_FAC);
|
|
|
|
uchar text_color[4];
|
|
UI_GetThemeColor4ubv(TH_HEADER_TEXT_HI, text_color);
|
|
UI_fontstyle_draw_simple(fstyle,
|
|
frame_x - text_width / 2 + U.pixelsize / 2,
|
|
get_centered_text_y(scrub_region_rect),
|
|
frame_str,
|
|
text_color);
|
|
}
|
|
|
|
void ED_time_scrub_draw_current_frame(const ARegion *region,
|
|
const Scene *scene,
|
|
bool display_seconds)
|
|
{
|
|
const View2D *v2d = ®ion->v2d;
|
|
GPU_matrix_push_projection();
|
|
wmOrtho2_region_pixelspace(region);
|
|
|
|
rcti scrub_region_rect;
|
|
ED_time_scrub_region_rect_get(region, &scrub_region_rect);
|
|
|
|
draw_current_frame(scene, display_seconds, v2d, &scrub_region_rect, scene->r.cfra);
|
|
GPU_matrix_pop_projection();
|
|
}
|
|
|
|
void ED_time_scrub_draw(const ARegion *region,
|
|
const Scene *scene,
|
|
bool display_seconds,
|
|
bool discrete_frames)
|
|
{
|
|
const View2D *v2d = ®ion->v2d;
|
|
|
|
GPU_matrix_push_projection();
|
|
wmOrtho2_region_pixelspace(region);
|
|
|
|
rcti scrub_region_rect;
|
|
ED_time_scrub_region_rect_get(region, &scrub_region_rect);
|
|
|
|
draw_background(&scrub_region_rect);
|
|
|
|
rcti numbers_rect = scrub_region_rect;
|
|
numbers_rect.ymin = get_centered_text_y(&scrub_region_rect) - 4 * UI_DPI_FAC;
|
|
if (discrete_frames) {
|
|
UI_view2d_draw_scale_x__discrete_frames_or_seconds(
|
|
region, v2d, &numbers_rect, scene, display_seconds, TH_TEXT);
|
|
}
|
|
else {
|
|
UI_view2d_draw_scale_x__frames_or_seconds(
|
|
region, v2d, &numbers_rect, scene, display_seconds, TH_TEXT);
|
|
}
|
|
|
|
GPU_matrix_pop_projection();
|
|
}
|
|
|
|
bool ED_time_scrub_event_in_region(const ARegion *region, const wmEvent *event)
|
|
{
|
|
rcti rect = region->winrct;
|
|
rect.ymin = rect.ymax - UI_TIME_SCRUB_MARGIN_Y;
|
|
return BLI_rcti_isect_pt_v(&rect, event->xy);
|
|
}
|
|
|
|
void ED_time_scrub_channel_search_draw(const bContext *C, ARegion *region, bDopeSheet *dopesheet)
|
|
{
|
|
GPU_matrix_push_projection();
|
|
wmOrtho2_region_pixelspace(region);
|
|
|
|
rcti rect;
|
|
rect.xmin = 0;
|
|
rect.xmax = region->winx;
|
|
rect.ymin = region->winy - UI_TIME_SCRUB_MARGIN_Y;
|
|
rect.ymax = region->winy;
|
|
|
|
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
|
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
|
|
immUniformThemeColor(TH_BACK);
|
|
immRectf(pos, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
|
|
immUnbindProgram();
|
|
|
|
PointerRNA ptr;
|
|
RNA_pointer_create(&CTX_wm_screen(C)->id, &RNA_DopeSheet, dopesheet, &ptr);
|
|
|
|
const uiStyle *style = UI_style_get_dpi();
|
|
const float padding_x = 2 * UI_DPI_FAC;
|
|
const float padding_y = UI_DPI_FAC;
|
|
|
|
uiBlock *block = UI_block_begin(C, region, __func__, UI_EMBOSS);
|
|
uiLayout *layout = UI_block_layout(block,
|
|
UI_LAYOUT_VERTICAL,
|
|
UI_LAYOUT_HEADER,
|
|
rect.xmin + padding_x,
|
|
rect.ymin + UI_UNIT_Y + padding_y,
|
|
BLI_rcti_size_x(&rect) - 2 * padding_x,
|
|
1,
|
|
0,
|
|
style);
|
|
uiLayoutSetScaleY(layout, (UI_UNIT_Y - padding_y) / UI_UNIT_Y);
|
|
UI_block_layout_set_current(block, layout);
|
|
UI_block_align_begin(block);
|
|
uiItemR(layout, &ptr, "filter_text", 0, "", ICON_NONE);
|
|
uiItemR(layout, &ptr, "use_filter_invert", 0, "", ICON_ARROW_LEFTRIGHT);
|
|
UI_block_align_end(block);
|
|
UI_block_layout_resolve(block, NULL, NULL);
|
|
|
|
/* Make sure the events are consumed from the search and don't reach other UI blocks since this
|
|
* is drawn on top of animation-channels. */
|
|
UI_block_flag_enable(block, UI_BLOCK_CLIP_EVENTS);
|
|
UI_block_bounds_set_normal(block, 0);
|
|
UI_block_end(C, block);
|
|
UI_block_draw(C, block);
|
|
|
|
GPU_matrix_pop_projection();
|
|
}
|