[WIP]tweak-experiment-experimental #119805

Draft
Richard Antalik wants to merge 46 commits from iss/blender:tweak-experiment-experimental into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
30 changed files with 837 additions and 217 deletions

View File

@ -8408,26 +8408,38 @@ def km_3d_view_tool_sculpt_gpencil_select_lasso(params):
# ------------------------------------------------------------------------------
# Tool System (Sequencer, Generic)
def km_sequencer_editor_tool_generic_select(params, *, fallback):
def km_sequencer_editor_tool_generic_select_timeline_rcs(params, fallback):
return [
("sequencer.select", {"type": 'LEFTMOUSE', "value": 'PRESS'},
{"properties": [("handles_only", True), ("wait_to_deselect_others", True)]}),
*_template_items_change_frame(params),
# Frame change can be cancelled if click happens on strip handle. In such case move the handle.
("transform.seq_slide", {"type": 'LEFTMOUSE', "value": 'PRESS'},
{"properties": [("view2d_edge_pan", True)]}),
]
def km_sequencer_editor_tool_generic_select_timeline_lcs(params, fallback):
return [
*_template_items_tool_select(
params, "sequencer.select", "sequencer.cursor_set", cursor_prioritize=True, fallback=fallback),
]
def km_sequencer_editor_tool_generic_select_timeline(params, *, fallback):
return (
_fallback_id("Sequencer Tool: Tweak", fallback),
_fallback_id("Sequencer Timeline Tool: Tweak", fallback),
{"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'},
{"items": [
*([] if (fallback and (params.select_mouse == 'RIGHTMOUSE')) else _template_items_tool_select(
params, "sequencer.select", "sequencer.cursor_set", cursor_prioritize=True, fallback=fallback)),
*([] if params.use_fallback_tool_select_handled else
_template_sequencer_preview_select(
type=params.select_mouse, value=params.select_mouse_value, legacy=params.legacy)),
# Ignored for preview.
*_template_items_change_frame(params),
*(km_sequencer_editor_tool_generic_select_timeline_rcs(params, fallback) if (params.select_mouse == 'RIGHTMOUSE')
else km_sequencer_editor_tool_generic_select_timeline_lcs(params, fallback)),
]},
)
def km_sequencer_editor_tool_generic_select_box(params, *, fallback):
def km_sequencer_editor_tool_generic_select_box_timeline(params, *, fallback):
return (
_fallback_id("Sequencer Tool: Select Box", fallback),
_fallback_id("Sequencer Timeline Tool: Select Box", fallback),
{"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'},
{"items": [
# Don't use `tool_maybe_tweak_event`, see comment for this slot.
@ -8436,7 +8448,6 @@ def km_sequencer_editor_tool_generic_select_box(params, *, fallback):
**(params.select_tweak_event if (fallback and params.use_fallback_tool_select_mouse) else
params.tool_tweak_event),
properties=[("tweak", params.select_mouse == 'LEFTMOUSE')])),
# RMB select can already set the frame, match the tweak tool.
# Ignored for preview.
*(_template_items_change_frame(params)
@ -8445,6 +8456,36 @@ def km_sequencer_editor_tool_generic_select_box(params, *, fallback):
)
def km_sequencer_editor_tool_generic_select_preview(params, *, fallback):
return (
_fallback_id("Sequencer Preview Tool: Tweak", fallback),
{"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'},
{"items": [
*([] if (fallback and (params.select_mouse == 'RIGHTMOUSE')) else _template_items_tool_select(
params, "sequencer.select", "sequencer.cursor_set", cursor_prioritize=True, fallback=fallback)),
*([] if params.use_fallback_tool_select_handled else
_template_sequencer_preview_select(
type=params.select_mouse, value=params.select_mouse_value, legacy=params.legacy)),
]},
)
def km_sequencer_editor_tool_generic_select_box_preview(params, *, fallback):
return (
_fallback_id("Sequencer Preview Tool: Select Box", fallback),
{"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'},
{"items": [
# Don't use `tool_maybe_tweak_event`, see comment for this slot.
*([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple(
"sequencer.select_box",
**(params.select_tweak_event if (fallback and params.use_fallback_tool_select_mouse) else
params.tool_tweak_event),
properties=[("tweak", params.select_mouse == 'LEFTMOUSE')])),
]},
)
def km_sequencer_editor_tool_generic_cursor(params):
return (
"Sequencer Tool: Cursor",
@ -8793,8 +8834,10 @@ def generate_keymaps(params=None):
km_3d_view_tool_sculpt_gpencil_select_box(params),
km_3d_view_tool_sculpt_gpencil_select_circle(params),
km_3d_view_tool_sculpt_gpencil_select_lasso(params),
*(km_sequencer_editor_tool_generic_select(params, fallback=fallback) for fallback in (False, True)),
*(km_sequencer_editor_tool_generic_select_box(params, fallback=fallback) for fallback in (False, True)),
*(km_sequencer_editor_tool_generic_select_timeline(params, fallback=fallback) for fallback in (False, True)),
*(km_sequencer_editor_tool_generic_select_box_timeline(params, fallback=fallback) for fallback in (False, True)),
*(km_sequencer_editor_tool_generic_select_preview(params, fallback=fallback) for fallback in (False, True)),
*(km_sequencer_editor_tool_generic_select_box_preview(params, fallback=fallback) for fallback in (False, True)),
km_sequencer_editor_tool_generic_cursor(params),
km_sequencer_editor_tool_blade(params),
km_sequencer_editor_tool_sample(params),

View File

@ -317,6 +317,7 @@ class SEQUENCER_PT_sequencer_overlay(Panel):
layout.prop(overlay_settings, "show_strip_offset", text="Offsets")
layout.prop(overlay_settings, "show_fcurves", text="F-Curves")
layout.prop(overlay_settings, "show_strip_handles", text="Handles")
layout.prop(overlay_settings, "show_strip_retiming", text="Retiming")
layout.prop(overlay_settings, "show_thumbnails", text="Thumbnails")
layout.prop(overlay_settings, "show_grid", text="Grid")

View File

@ -2669,17 +2669,17 @@ class _defs_sequencer_generic:
class _defs_sequencer_select:
@ToolDef.from_fn
def select():
def select_timeline():
return dict(
idname="builtin.select",
label="Tweak",
icon="ops.generic.select",
widget=None,
keymap="Sequencer Tool: Tweak",
keymap="Sequencer Timeline Tool: Tweak",
)
@ToolDef.from_fn
def box():
def box_timeline():
def draw_settings(_context, layout, tool):
props = tool.operator_properties("sequencer.select_box")
row = layout.row()
@ -2690,7 +2690,33 @@ class _defs_sequencer_select:
label="Select Box",
icon="ops.generic.select_box",
widget=None,
keymap="Sequencer Tool: Select Box",
keymap="Sequencer Timeline Tool: Select Box",
draw_settings=draw_settings,
)
@ToolDef.from_fn
def select_preview():
return dict(
idname="builtin.select",
label="Tweak",
icon="ops.generic.select",
widget=None,
keymap="Sequencer Preview Tool: Tweak",
)
@ToolDef.from_fn
def box_preview():
def draw_settings(_context, layout, tool):
props = tool.operator_properties("sequencer.select_box")
row = layout.row()
row.use_property_split = False
row.prop(props, "mode", text="", expand=True, icon_only=True)
return dict(
idname="builtin.select_box",
label="Select Box",
icon="ops.generic.select_box",
widget=None,
keymap="Sequencer Preview Tool: Select Box",
draw_settings=draw_settings,
)
@ -3274,13 +3300,6 @@ class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel):
yield from cls._tools.items()
# Private tool lists for convenient reuse in `_tools`.
_tools_select = (
(
_defs_sequencer_select.select,
_defs_sequencer_select.box,
),
)
_tools_annotate = (
(
_defs_annotate.scribble,
@ -3297,7 +3316,10 @@ class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel):
None: [
],
'PREVIEW': [
*_tools_select,
(
_defs_sequencer_select.select_preview,
_defs_sequencer_select.box_preview,
),
_defs_sequencer_generic.cursor,
None,
_defs_sequencer_generic.translate,
@ -3309,12 +3331,13 @@ class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel):
*_tools_annotate,
],
'SEQUENCER': [
*_tools_select,
(
_defs_sequencer_select.select_timeline,
_defs_sequencer_select.box_timeline,
),
_defs_sequencer_generic.blade,
],
'SEQUENCER_PREVIEW': [
*_tools_select,
None,
*_tools_annotate,
None,
_defs_sequencer_generic.blade,

View File

@ -533,6 +533,17 @@ class USERPREF_PT_edit_node_editor(EditingPanel, CenterAlignMixIn, Panel):
layout.prop(edit, "node_preview_resolution", text="Preview Resolution")
class USERPREF_PT_edit_sequence_editor(EditingPanel, CenterAlignMixIn, Panel):
bl_label = "Video Sequencer"
bl_options = {'DEFAULT_CLOSED'}
def draw_centered(self, context, layout):
prefs = context.preferences
edit = prefs.edit
layout.prop(edit, "use_sequencer_simplified_tweaking")
class USERPREF_PT_edit_misc(EditingPanel, CenterAlignMixIn, Panel):
bl_label = "Miscellaneous"
bl_options = {'DEFAULT_CLOSED'}
@ -2777,6 +2788,7 @@ classes = (
USERPREF_PT_edit_gpencil,
USERPREF_PT_edit_text_editor,
USERPREF_PT_edit_node_editor,
USERPREF_PT_edit_sequence_editor,
USERPREF_PT_edit_misc,
USERPREF_PT_animation_timeline,

View File

@ -29,7 +29,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 9
#define BLENDER_FILE_SUBVERSION 10
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and cancel loading the file, showing a warning to

View File

@ -930,6 +930,10 @@ void blo_do_versions_userdef(UserDef *userdef)
}
}
if (!USER_VERSION_ATLEAST(402, 10)) {
userdef->sequencer_editor_flag |= USER_SEQ_ED_SIMPLE_TWEAKING;
}
/**
* Always bump subversion in BKE_blender_version.h when adding versioning
* code here, and wrap it inside a USER_VERSION_ATLEAST check.

View File

@ -222,6 +222,22 @@ static bool use_sequencer_snapping(bContext *C)
(snap_flag & SEQ_SNAP_CURRENT_FRAME_TO_STRIPS);
}
static bool sequencer_skip_for_handle_tweak(const bContext *C, const wmEvent *event)
{
ARegion *region = CTX_wm_region(C);
const Scene *scene = CTX_data_scene(C);
const View2D *v2d = UI_view2d_fromcontext(C);
float mouse_co[2];
UI_view2d_region_to_view(v2d, event->mval[0], event->mval[1], &mouse_co[0], &mouse_co[1]);
Sequence *seq1, *seq2;
int side;
ED_sequencer_handle_selection_refine(scene, v2d, mouse_co, &seq1, &seq2, &side);
return side != SEQ_SIDE_NONE;
}
/* Modal Operator init */
static int change_frame_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
@ -230,6 +246,9 @@ static int change_frame_invoke(bContext *C, wmOperator *op, const wmEvent *event
if (CTX_wm_space_seq(C) != nullptr && region->regiontype == RGN_TYPE_PREVIEW) {
return OPERATOR_CANCELLED;
}
if (sequencer_skip_for_handle_tweak(C, event)) {
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
}
/* Change to frame that mouse is over before adding modal handler,
* as user could click on a single frame (jump to frame) as well as

View File

@ -8,10 +8,13 @@
#pragma once
#include "BLI_vector_set.hh"
struct Scene;
struct Sequence;
struct SpaceSeq;
struct bContext;
struct View2D;
void ED_sequencer_select_sequence_single(Scene *scene, Sequence *seq, bool deselect_all);
/**
@ -45,4 +48,28 @@ void ED_operatormacros_sequencer();
Sequence *ED_sequencer_special_preview_get();
void ED_sequencer_special_preview_set(bContext *C, const int mval[2]);
void ED_sequencer_special_preview_clear();
bool sequencer_retiming_mode_is_active(const bContext *C);
bool sequencer_retiming_mode_is_active(const Scene *scene);
/**
* Returns true if strip can be selected, false otherwise.
*
* r_seq1 first strip to be selected. Never nullptr if function returns true
* r_seq2 second strip to be selected.
* r_side which handle is selected. This further clarifies result if seq2 is nullptr.
*/
bool ED_sequencer_handle_selection_refine(const struct Scene *scene,
const View2D *v2d,
float mouse_co[2],
struct Sequence **r_seq1,
struct Sequence **r_seq2,
int *r_side);
/**
* Returns collection with selected strips presented to user. If operation is done in preview,
* collection is limited to selected presented strips, that can produce image output at current
* frame.
*
* \param C: context
* \return collection of strips (`Sequence`)
*/
blender::VectorSet<Sequence *> ED_sequencer_selected_strips_from_context(bContext *C);
bool ED_sequencer_can_select_handle(const Scene *scene, const Sequence *seq, const View2D *v2d);

View File

@ -1682,7 +1682,7 @@ static int sequencer_delete_exec(bContext *C, wmOperator *op)
SEQ_prefetch_stop(scene);
for (Sequence *seq : selected_strips_from_context(C)) {
for (Sequence *seq : ED_sequencer_selected_strips_from_context(C)) {
SEQ_edit_flag_for_removal(scene, seqbasep, seq);
if (delete_data) {
sequencer_delete_strip_data(C, seq);
@ -1712,7 +1712,7 @@ static int sequencer_delete_invoke(bContext *C, wmOperator *op, const wmEvent *e
}
}
if (sequencer_retiming_mode_is_active(C)) {
if (sequencer_retiming_mode_is_active(scene)) {
RNA_boolean_set(op->ptr, "use_retiming_mode", true);
}

View File

@ -146,7 +146,7 @@ void channel_draw_context_init(const bContext *C,
/* `sequencer_edit.cc` */
void seq_rectf(const Scene *scene, Sequence *seq, rctf *rectf);
void seq_rectf(const Scene *scene, const Sequence *seq, rctf *rectf);
Sequence *find_neighboring_sequence(Scene *scene, Sequence *test, int lr, int sel);
void recurs_sel_seq(Sequence *seq_meta);
int seq_effect_find_selected(Scene *scene,
@ -178,16 +178,6 @@ bool sequencer_view_strips_poll(bContext *C);
*/
blender::VectorSet<Sequence *> all_strips_from_context(bContext *C);
/**
* Returns collection with selected strips presented to user. If operation is done in preview,
* collection is limited to selected presented strips, that can produce image output at current
* frame.
*
* \param C: context
* \return collection of strips (`Sequence`)
*/
blender::VectorSet<Sequence *> selected_strips_from_context(bContext *C);
/* Externals. */
extern EnumPropertyItem sequencer_prop_effect_types[];
@ -335,12 +325,16 @@ int sequencer_retiming_box_select_exec(bContext *C, wmOperator *op);
/* `sequencer_retiming_draw.cc` */
void sequencer_draw_retiming(const bContext *C, SeqQuadsBatch *quads);
blender::Vector<Sequence *> sequencer_visible_strips_get(const bContext *C);
SeqRetimingKey *try_to_realize_virtual_key(const bContext *C, Sequence *seq, const int mval[2]);
SeqRetimingKey *retiming_mousover_key_get(const bContext *C, const int mval[2], Sequence **r_seq);
int left_fake_key_frame_get(const bContext *C, const Sequence *seq);
int right_fake_key_frame_get(const bContext *C, const Sequence *seq);
bool retiming_keys_are_visible(const bContext *C);
bool retiming_keys_are_visible(const SpaceSeq *sseq);
rctf retiming_keys_box_get(const Scene *scene, const View2D *v2d, const Sequence *seq);
/* `sequencer_timeline_draw.cc` */
blender::Vector<Sequence *> sequencer_visible_strips_get(const bContext *C);
blender::Vector<Sequence *> sequencer_visible_strips_get(const Scene *scene, const View2D *v2d);
/* `sequencer_clipboard.cc` */
int sequencer_clipboard_copy_exec(bContext *C, wmOperator *op);

View File

@ -39,9 +39,8 @@
using blender::MutableSpan;
bool sequencer_retiming_mode_is_active(const bContext *C)
bool sequencer_retiming_mode_is_active(const Scene *scene)
{
const Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene);
if (ed == nullptr) {
return false;
@ -90,7 +89,7 @@ static int sequencer_retiming_data_show_exec(bContext *C, wmOperator * /* op */)
return OPERATOR_CANCELLED;
}
if (sequencer_retiming_mode_is_active(C)) {
if (sequencer_retiming_mode_is_active(scene)) {
sequencer_retiming_data_hide_all(ed->seqbasep);
}
else if (SEQ_retiming_data_is_editable(seq_act)) {
@ -259,7 +258,7 @@ static int sequencer_retiming_key_add_exec(bContext *C, wmOperator *op)
}
int ret_val;
blender::VectorSet<Sequence *> strips = selected_strips_from_context(C);
blender::VectorSet<Sequence *> strips = ED_sequencer_selected_strips_from_context(C);
if (!strips.is_empty()) {
ret_val = retiming_key_add_from_selection(C, op, strips, timeline_frame);
}
@ -345,7 +344,7 @@ static bool freeze_frame_add_from_strip_selection(bContext *C,
const int duration)
{
Scene *scene = CTX_data_scene(C);
blender::VectorSet<Sequence *> strips = selected_strips_from_context(C);
blender::VectorSet<Sequence *> strips = ED_sequencer_selected_strips_from_context(C);
const int timeline_frame = BKE_scene_frame_get(scene);
bool success = false;
@ -382,7 +381,7 @@ static int sequencer_retiming_freeze_frame_add_exec(bContext *C, wmOperator *op)
duration = RNA_int_get(op->ptr, "duration");
}
if (sequencer_retiming_mode_is_active(C)) {
if (sequencer_retiming_mode_is_active(scene)) {
success = freeze_frame_add_from_retiming_selection(C, op, duration);
}
else {
@ -485,7 +484,7 @@ static int sequencer_retiming_transition_add_exec(bContext *C, wmOperator *op)
duration = RNA_int_get(op->ptr, "duration");
}
if (sequencer_retiming_mode_is_active(C)) {
if (sequencer_retiming_mode_is_active(scene)) {
success = transition_add_from_retiming_selection(C, op, duration);
}
else {
@ -541,9 +540,11 @@ static SeqRetimingKey *ensure_left_and_right_keys(const bContext *C, Sequence *s
/* Return speed of existing segment or strip. Assume 1 element is selected. */
static float strip_speed_get(bContext *C, const wmOperator * /* op */)
{
Scene *scene = CTX_data_scene(C);
/* Strip mode. */
if (!sequencer_retiming_mode_is_active(C)) {
blender::VectorSet<Sequence *> strips = selected_strips_from_context(C);
if (!sequencer_retiming_mode_is_active(scene)) {
blender::VectorSet<Sequence *> strips = ED_sequencer_selected_strips_from_context(C);
if (strips.size() == 1) {
Sequence *seq = strips[0];
SeqRetimingKey *key = ensure_left_and_right_keys(C, seq);
@ -551,7 +552,6 @@ static float strip_speed_get(bContext *C, const wmOperator * /* op */)
}
}
Scene *scene = CTX_data_scene(C);
blender::Map selection = SEQ_retiming_selection_get(SEQ_editing_get(scene));
/* Retiming mode. */
if (selection.size() == 1) {
@ -566,7 +566,7 @@ static float strip_speed_get(bContext *C, const wmOperator * /* op */)
static int strip_speed_set_exec(bContext *C, const wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
blender::VectorSet<Sequence *> strips = selected_strips_from_context(C);
blender::VectorSet<Sequence *> strips = ED_sequencer_selected_strips_from_context(C);
for (Sequence *seq : strips) {
SeqRetimingKey *key = ensure_left_and_right_keys(C, seq);
@ -613,7 +613,7 @@ static int sequencer_retiming_segment_speed_set_exec(bContext *C, wmOperator *op
const Scene *scene = CTX_data_scene(C);
/* Strip mode. */
if (!sequencer_retiming_mode_is_active(C)) {
if (!sequencer_retiming_mode_is_active(scene)) {
return strip_speed_set_exec(C, op);
}

View File

@ -42,9 +42,8 @@
#define KEY_SIZE (10 * U.pixelsize)
#define KEY_CENTER (UI_view2d_view_to_region_y(v2d, strip_y_rescale(seq, 0.0f)) + 4 + KEY_SIZE / 2)
bool retiming_keys_are_visible(const bContext *C)
bool retiming_keys_are_visible(const SpaceSeq *sseq)
{
const SpaceSeq *sseq = CTX_wm_space_seq(C);
return (sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_STRIP_RETIMING) != 0;
}
@ -76,67 +75,34 @@ static float pixels_to_view_height(const bContext *C, const float height)
return height / scale_y;
}
static float strip_start_screenspace_get(const bContext *C, const Sequence *seq)
static float strip_start_screenspace_get(const Scene *scene,
const View2D *v2d,
const Sequence *seq)
{
const View2D *v2d = UI_view2d_fromcontext(C);
const Scene *scene = CTX_data_scene(C);
return UI_view2d_view_to_region_x(v2d, SEQ_time_left_handle_frame_get(scene, seq));
}
static float strip_end_screenspace_get(const bContext *C, const Sequence *seq)
static float strip_end_screenspace_get(const Scene *scene, const View2D *v2d, const Sequence *seq)
{
const View2D *v2d = UI_view2d_fromcontext(C);
const Scene *scene = CTX_data_scene(C);
return UI_view2d_view_to_region_x(v2d, SEQ_time_right_handle_frame_get(scene, seq));
}
static rctf strip_box_get(const bContext *C, const Sequence *seq)
static rctf strip_box_get(const Scene *scene, const View2D *v2d, const Sequence *seq)
{
const View2D *v2d = UI_view2d_fromcontext(C);
rctf rect;
rect.xmin = strip_start_screenspace_get(C, seq);
rect.xmax = strip_end_screenspace_get(C, seq);
rect.xmin = strip_start_screenspace_get(scene, v2d, seq);
rect.xmax = strip_end_screenspace_get(scene, v2d, seq);
rect.ymin = UI_view2d_view_to_region_y(v2d, strip_y_rescale(seq, 0));
rect.ymax = UI_view2d_view_to_region_y(v2d, strip_y_rescale(seq, 1));
return rect;
}
blender::Vector<Sequence *> sequencer_visible_strips_get(const bContext *C)
{
const View2D *v2d = UI_view2d_fromcontext(C);
const Scene *scene = CTX_data_scene(C);
const Editing *ed = SEQ_editing_get(CTX_data_scene(C));
blender::Vector<Sequence *> strips;
LISTBASE_FOREACH (Sequence *, seq, ed->seqbasep) {
if (min_ii(SEQ_time_left_handle_frame_get(scene, seq), SEQ_time_start_frame_get(seq)) >
v2d->cur.xmax)
{
continue;
}
if (max_ii(SEQ_time_right_handle_frame_get(scene, seq),
SEQ_time_content_end_frame_get(scene, seq)) < v2d->cur.xmin)
{
continue;
}
if (seq->machine + 1.0f < v2d->cur.ymin) {
continue;
}
if (seq->machine > v2d->cur.ymax) {
continue;
}
strips.append(seq);
}
return strips;
}
/** Size in pixels. */
#define RETIME_KEY_MOUSEOVER_THRESHOLD (16.0f * UI_SCALE_FAC)
static rctf keys_box_get(const bContext *C, const Sequence *seq)
rctf retiming_keys_box_get(const Scene *scene, const View2D *v2d, const Sequence *seq)
{
const View2D *v2d = UI_view2d_fromcontext(C);
rctf rect = strip_box_get(C, seq);
rctf rect = strip_box_get(scene, v2d, seq);
rect.ymax = KEY_CENTER + KEY_SIZE / 2;
rect.ymin = KEY_CENTER - KEY_SIZE / 2;
return rect;
@ -163,7 +129,7 @@ static bool retiming_fake_key_is_clicked(const bContext *C,
{
const View2D *v2d = UI_view2d_fromcontext(C);
rctf box = keys_box_get(C, seq);
rctf box = retiming_keys_box_get(CTX_data_scene(C), v2d, seq);
if (!BLI_rctf_isect_pt(&box, mval[0], mval[1])) {
return false;
}
@ -231,8 +197,10 @@ static SeqRetimingKey *mouse_over_key_get_from_strip(const bContext *C,
SeqRetimingKey *retiming_mousover_key_get(const bContext *C, const int mval[2], Sequence **r_seq)
{
const Scene *scene = CTX_data_scene(C);
const View2D *v2d = UI_view2d_fromcontext(C);
for (Sequence *seq : sequencer_visible_strips_get(C)) {
rctf box = keys_box_get(C, seq);
rctf box = retiming_keys_box_get(scene, v2d, seq);
if (!BLI_rctf_isect_pt(&box, mval[0], mval[1])) {
continue;
}
@ -267,7 +235,7 @@ static void retime_key_draw(const bContext *C,
const float key_x = key_x_get(scene, seq, key);
const View2D *v2d = UI_view2d_fromcontext(C);
const rctf strip_box = strip_box_get(C, seq);
const rctf strip_box = strip_box_get(scene, v2d, seq);
if (!BLI_rctf_isect_x(&strip_box, UI_view2d_view_to_region_x(v2d, key_x))) {
return; /* Key out of the strip bounds. */
}
@ -405,7 +373,7 @@ static void retime_keys_draw(const bContext *C, SeqQuadsBatch *quads)
return;
}
if (!retiming_keys_are_visible(C)) {
if (!retiming_keys_are_visible(CTX_wm_space_seq(C))) {
return;
}
@ -573,7 +541,7 @@ static void retime_speed_draw(const bContext *C)
return;
}
if (!retiming_keys_are_visible(C)) {
if (!retiming_keys_are_visible(CTX_wm_space_seq(C))) {
return;
}

View File

@ -29,6 +29,7 @@
#include "RNA_define.hh"
#include "SEQ_channels.hh"
#include "SEQ_effects.hh"
#include "SEQ_iterator.hh"
#include "SEQ_relations.hh"
#include "SEQ_retiming.hh"
@ -68,9 +69,9 @@ blender::VectorSet<Sequence *> all_strips_from_context(bContext *C)
return SEQ_query_all_strips(seqbase);
}
blender::VectorSet<Sequence *> selected_strips_from_context(bContext *C)
blender::VectorSet<Sequence *> ED_sequencer_selected_strips_from_context(bContext *C)
{
Scene *scene = CTX_data_scene(C);
const Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene);
ListBase *seqbase = SEQ_active_seqbase_get(ed);
ListBase *channels = SEQ_channels_displayed_get(ed);
@ -247,7 +248,7 @@ void ED_sequencer_select_sequence_single(Scene *scene, Sequence *seq, bool desel
recurs_sel_seq(seq);
}
void seq_rectf(const Scene *scene, Sequence *seq, rctf *rect)
void seq_rectf(const Scene *scene, const Sequence *seq, rctf *rect)
{
rect->xmin = SEQ_time_left_handle_frame_get(scene, seq);
rect->xmax = SEQ_time_right_handle_frame_get(scene, seq);
@ -327,7 +328,7 @@ Sequence *find_nearest_seq(const Scene *scene, const View2D *v2d, const int mval
if (SEQ_transform_sequence_can_be_translated(seq)) {
/* Clamp handles to defined size in pixel space. */
handsize = 2.0f * sequence_handle_size_get_clamped(scene, seq, pixelx);
handsize = 4.0f * sequence_handle_size_get_clamped(scene, seq, pixelx);
displen = float(abs(SEQ_time_left_handle_frame_get(scene, seq) -
SEQ_time_right_handle_frame_get(scene, seq)));
@ -447,7 +448,7 @@ static int sequencer_de_select_all_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
if (sequencer_retiming_mode_is_active(C) && retiming_keys_are_visible(C)) {
if (sequencer_retiming_mode_is_active(scene) && retiming_keys_are_visible(CTX_wm_space_seq(C))) {
return sequencer_retiming_select_all_exec(C, op);
}
@ -822,13 +823,26 @@ static Sequence *seq_select_seq_from_preview(
return seq_select;
}
static bool element_already_selected(const Sequence *seq, const int handle_clicked)
static bool element_already_selected(const Sequence *seq1,
const Sequence *seq2,
int handle_clicked)
{
const bool handle_already_selected = ((handle_clicked == SEQ_SIDE_LEFT) &&
(seq->flag & SEQ_LEFTSEL)) ||
((handle_clicked == SEQ_SIDE_RIGHT) &&
(seq->flag & SEQ_RIGHTSEL));
return ((seq->flag & SELECT) && handle_clicked == SEQ_SIDE_NONE) || handle_already_selected;
if (seq1 == nullptr) {
return false;
}
const bool seq1_already_selected = ((seq1->flag & SELECT) != 0);
if (seq2 == nullptr) {
const bool handle_already_selected = (seq1->flag & handle_clicked) != 0 ||
handle_clicked == SEQ_SIDE_NONE;
return seq1_already_selected && handle_already_selected;
}
const bool seq2_already_selected = ((seq2->flag & SELECT) != 0);
const int seq1_handle = seq1->flag & (SEQ_RIGHTSEL | SEQ_LEFTSEL);
const int seq2_handle = seq2->flag & (SEQ_RIGHTSEL | SEQ_LEFTSEL);
/* Handles must be selected in XOR fashion, with `seq1` matching `handle_clicked`. */
const bool both_handles_selected = seq1_handle == handle_clicked && seq2_handle != 0 &&
seq1_handle != seq2_handle;
return seq1_already_selected && seq2_already_selected && both_handles_selected;
}
static void sequencer_select_strip_impl(const Editing *ed,
@ -885,15 +899,194 @@ static void sequencer_select_strip_impl(const Editing *ed,
}
}
/* Similar to `sequence_handle_size_get_clamped()` but allows for larger clickable area. */
static float clickable_handle_size_get(const Scene *scene, const Sequence *seq, const View2D *v2d)
{
const float pixelx = 1 / UI_view2d_scale_get_x(v2d);
const float strip_len = SEQ_time_strip_length_get(scene, seq);
return min_ff(15.0f * pixelx * U.pixelsize, strip_len / 4);
}
bool ED_sequencer_can_select_handle(const Scene *scene, const Sequence *seq, const View2D *v2d)
{
int min_len = 25 * U.pixelsize;
if ((U.sequencer_editor_flag & USER_SEQ_ED_SIMPLE_TWEAKING) == 0) {
min_len = 15 * U.pixelsize;
}
const float pixelx = 1 / UI_view2d_scale_get_x(v2d);
const int strip_len = SEQ_time_right_handle_frame_get(scene, seq) -
SEQ_time_left_handle_frame_get(scene, seq);
if (strip_len / pixelx < min_len) {
return false;
}
return true;
}
static void strip_clickable_areas_get(const Scene *scene,
const Sequence *seq,
const View2D *v2d,
rctf *r_body,
rctf *r_left_handle,
rctf *r_right_handle)
{
seq_rectf(scene, seq, r_body);
memcpy(r_left_handle, r_body, sizeof(*r_left_handle));
memcpy(r_right_handle, r_body, sizeof(*r_right_handle));
const float handsize = clickable_handle_size_get(scene, seq, v2d);
BLI_rctf_pad(r_left_handle, handsize / 3, 0.0f);
BLI_rctf_pad(r_right_handle, handsize / 3, 0.0f);
r_left_handle->xmax = r_body->xmin + handsize;
r_right_handle->xmin = r_body->xmax - handsize;
BLI_rctf_pad(r_body, -handsize, 0.0f);
}
static rctf strip_clickable_area_get(const Scene *scene, const View2D *v2d, const Sequence *seq)
{
rctf body, left, right;
strip_clickable_areas_get(scene, seq, v2d, &body, &left, &right);
BLI_rctf_union(&body, &left);
BLI_rctf_union(&body, &right);
return body;
}
static float strip_to_frame_distance(const Scene *scene,
const View2D *v2d,
const Sequence *seq,
float timeline_frame)
{
rctf body, left, right;
strip_clickable_areas_get(scene, seq, v2d, &body, &left, &right);
return BLI_rctf_length_x(&body, timeline_frame);
}
/* Get strips that can be selected by click. */
static blender::Vector<Sequence *> mouseover_strips_sorted_get(const Scene *scene,
const View2D *v2d,
float mouse_co[2])
{
blender::Vector<Sequence *> strips = sequencer_visible_strips_get(scene, v2d);
const Editing *ed = SEQ_editing_get(scene);
strips.remove_if([&](Sequence *seq) {
rctf body = strip_clickable_area_get(scene, v2d, seq);
if (!BLI_rctf_isect_pt_v(&body, mouse_co)) {
return true;
}
if (SEQ_transform_is_locked(ed->displayed_channels, seq)) {
return true;
}
return false;
});
/* It may be better to sort strips, as there can be very small strip in set, that may not be
* removed by previous conditions. `std::sort` has issues with this container though. */
if (strips.size() > 1 && strip_to_frame_distance(scene, v2d, strips[0], mouse_co[0]) <
strip_to_frame_distance(scene, v2d, strips[1], mouse_co[0]))
{
SWAP(Sequence *, strips[0], strips[1]);
}
BLI_assert(strips.size() <= 2);
return strips;
}
static bool strips_are_adjacent(const Scene *scene, const Sequence *seq1, const Sequence *seq2)
{
const int s1_left = SEQ_time_left_handle_frame_get(scene, seq1);
const int s1_right = SEQ_time_right_handle_frame_get(scene, seq1);
const int s2_left = SEQ_time_left_handle_frame_get(scene, seq2);
const int s2_right = SEQ_time_right_handle_frame_get(scene, seq2);
return s1_right == s2_left || s1_left == s2_right;
}
static int handle_selection_refine(const Scene *scene,
const Sequence *seq,
const View2D *v2d,
float mouse_co[2])
{
rctf body, left, right;
strip_clickable_areas_get(scene, seq, v2d, &body, &left, &right);
if (!ED_sequencer_can_select_handle(scene, seq, v2d)) {
return SEQ_SIDE_NONE;
}
if (BLI_rctf_isect_pt_v(&left, mouse_co)) {
return SEQ_SIDE_LEFT;
}
if (BLI_rctf_isect_pt_v(&right, mouse_co)) {
return SEQ_SIDE_RIGHT;
}
return SEQ_SIDE_NONE;
}
static bool both_handles_are_selected(const Scene *scene,
const Sequence *seq1,
const Sequence *seq2,
int seq1_side,
const View2D *v2d,
float mouse_co[2])
{
if ((U.sequencer_editor_flag & USER_SEQ_ED_SIMPLE_TWEAKING) == 0) {
return false;
}
if (seq1_side == SEQ_SIDE_NONE) {
return false;
}
if (!strips_are_adjacent(scene, seq1, seq2)) {
return false;
}
const int seq2_side = handle_selection_refine(scene, seq2, v2d, mouse_co);
if (seq1_side == SEQ_SIDE_RIGHT && seq2_side != SEQ_SIDE_LEFT) {
return false;
}
else if (seq1_side == SEQ_SIDE_LEFT && seq2_side != SEQ_SIDE_RIGHT) {
return false;
}
return true;
}
bool ED_sequencer_handle_selection_refine(const Scene *scene,
const View2D *v2d,
float mouse_co[2],
Sequence **r_seq1,
Sequence **r_seq2,
int *r_side)
{
blender::Vector<Sequence *> strips = mouseover_strips_sorted_get(scene, v2d, mouse_co);
*r_seq1 = *r_seq2 = nullptr;
if (strips.size() == 0) {
return false;
}
*r_seq1 = strips[0];
*r_side = handle_selection_refine(scene, *r_seq1, v2d, mouse_co);
if (strips.size() == 2 &&
both_handles_are_selected(scene, *r_seq1, strips[1], *r_side, v2d, mouse_co))
{
*r_seq2 = strips[1];
}
return true;
}
static bool use_retiming_mode(const bContext *C, const Sequence *seq_key_test)
{
return seq_key_test && SEQ_retiming_data_is_editable(seq_key_test) &&
!sequencer_retiming_mode_is_active(C) && retiming_keys_are_visible(C);
!sequencer_retiming_mode_is_active(CTX_data_scene(C)) &&
retiming_keys_are_visible(CTX_wm_space_seq(C));
}
int sequencer_select_exec(bContext *C, wmOperator *op)
{
View2D *v2d = UI_view2d_fromcontext(C);
const View2D *v2d = UI_view2d_fromcontext(C);
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene);
ARegion *region = CTX_wm_region(C);
@ -912,7 +1105,7 @@ int sequencer_select_exec(bContext *C, wmOperator *op)
}
}
if (sequencer_retiming_mode_is_active(C) && retiming_keys_are_visible(C)) {
if (sequencer_retiming_mode_is_active(scene) && retiming_keys_are_visible(CTX_wm_space_seq(C))) {
return sequencer_retiming_key_select_exec(C, op);
}
@ -925,21 +1118,30 @@ int sequencer_select_exec(bContext *C, wmOperator *op)
int mval[2];
mval[0] = RNA_int_get(op->ptr, "mouse_x");
mval[1] = RNA_int_get(op->ptr, "mouse_y");
float mouse_co[2];
UI_view2d_region_to_view(v2d, mval[0], mval[1], &mouse_co[0], &mouse_co[1]);
int handle_clicked = SEQ_SIDE_NONE;
Sequence *seq = nullptr;
Sequence *seq2 = nullptr;
if (region->regiontype == RGN_TYPE_PREVIEW) {
seq = seq_select_seq_from_preview(C, mval, toggle, extend, center);
}
else {
seq = find_nearest_seq(scene, v2d, mval, &handle_clicked);
ED_sequencer_handle_selection_refine(scene, v2d, mouse_co, &seq, &seq2, &handle_clicked);
}
if (RNA_boolean_get(op->ptr, "handles_only") && handle_clicked == SEQ_SIDE_NONE) {
return OPERATOR_CANCELLED;
}
/* NOTE: `side_of_frame` and `linked_time` functionality is designed to be shared on one
* keymap, therefore both properties can be true at the same time. */
Sequence *seq_key_test = nullptr;
SeqRetimingKey *key = retiming_mousover_key_get(C, mval, &seq_key_test);
/* NOTE: `side_of_frame` and `linked_time` functionality is designed to be shared on one keymap,
* therefore both properties can be true at the same time. */
/* NOTE: `side_of_frame` and `linked_time` functionality is designed to be shared on one
* keymap, therefore both properties can be true at the same time. */
if (seq && RNA_boolean_get(op->ptr, "linked_time")) {
if (use_retiming_mode(C, seq_key_test)) {
return sequencer_retiming_select_linked_time(C, op);
@ -981,7 +1183,9 @@ int sequencer_select_exec(bContext *C, wmOperator *op)
/* Clicking on already selected element falls on modal operation.
* All strips are deselected on mouse button release unless extend mode is used. */
if (seq && element_already_selected(seq, handle_clicked) && wait_to_deselect_others && !toggle) {
if (element_already_selected(seq, seq2, handle_clicked) && wait_to_deselect_others && !toggle &&
!RNA_boolean_get(op->ptr, "handles_only"))
{
return OPERATOR_RUNNING_MODAL;
}
@ -1020,6 +1224,11 @@ int sequencer_select_exec(bContext *C, wmOperator *op)
/* Do actual selection. */
sequencer_select_strip_impl(ed, seq, handle_clicked, extend, deselect, toggle);
if (seq2 != nullptr) {
/* Invert handle selection for second strip */
int seq2_handle_clicked = (handle_clicked == SEQ_LEFTSEL) ? SEQ_SIDE_RIGHT : SEQ_SIDE_LEFT;
sequencer_select_strip_impl(ed, seq2, seq2_handle_clicked, extend, deselect, toggle);
}
sequencer_select_do_updates(C, scene);
sequencer_select_set_active(scene, seq);
@ -1086,6 +1295,11 @@ void SEQUENCER_OT_select(wmOperatorType *ot)
"Side of Frame",
"Select all strips on same side of the current frame as the mouse cursor");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
/* Used for handle tweaking. */
prop = RNA_def_boolean(ot->srna, "handles_only", false, "Handles Only", "Select handles only");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
RNA_def_property_flag(prop, PROP_HIDDEN);
}
/** \} */
@ -1665,7 +1879,7 @@ static int sequencer_box_select_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
if (sequencer_retiming_mode_is_active(C) && retiming_keys_are_visible(C)) {
if (sequencer_retiming_mode_is_active(scene) && retiming_keys_are_visible(CTX_wm_space_seq(C))) {
return sequencer_retiming_box_select_exec(C, op);
}
@ -1700,7 +1914,7 @@ static int sequencer_box_select_exec(bContext *C, wmOperator *op)
if (handles) {
/* Get the handles draw size. */
float pixelx = BLI_rctf_size_x(&v2d->cur) / BLI_rcti_size_x(&v2d->mask);
float handsize = sequence_handle_size_get_clamped(scene, seq, pixelx);
float handsize = sequence_handle_size_get_clamped(scene, seq, pixelx) * 4;
/* Right handle. */
if (rectf.xmax > (SEQ_time_right_handle_frame_get(scene, seq) - handsize)) {
@ -1755,20 +1969,24 @@ static int sequencer_box_select_exec(bContext *C, wmOperator *op)
static int sequencer_box_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
Scene *scene = CTX_data_scene(C);
View2D *v2d = &CTX_wm_region(C)->v2d;
const View2D *v2d = UI_view2d_fromcontext(C);
ARegion *region = CTX_wm_region(C);
if (region->regiontype == RGN_TYPE_PREVIEW && !sequencer_view_preview_only_poll(C)) {
return OPERATOR_CANCELLED;
}
const bool tweak = RNA_boolean_get(op->ptr, "tweak");
if (tweak) {
int hand_dummy;
if (RNA_boolean_get(op->ptr, "tweak")) {
int mval[2];
float mouse_co[2];
WM_event_drag_start_mval(event, region, mval);
Sequence *seq = find_nearest_seq(scene, v2d, mval, &hand_dummy);
UI_view2d_region_to_view(v2d, mval[0], mval[1], &mouse_co[0], &mouse_co[1]);
int handle_clicked = SEQ_SIDE_NONE;
Sequence *seq = nullptr;
Sequence *seq2 = nullptr;
ED_sequencer_handle_selection_refine(scene, v2d, mouse_co, &seq, &seq2, &handle_clicked);
if (seq != nullptr) {
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
}

View File

@ -62,9 +62,6 @@
#include "sequencer_intern.hh"
#include "sequencer_quads_batch.hh"
#define SEQ_LEFTHANDLE 1
#define SEQ_RIGHTHANDLE 2
#define SEQ_HANDLE_SIZE 8.0f
#define MUTE_ALPHA 120
struct StripDrawContext {
@ -99,6 +96,38 @@ struct TimelineDrawContext {
SeqQuadsBatch *quads;
};
blender::Vector<Sequence *> sequencer_visible_strips_get(const bContext *C)
{
return sequencer_visible_strips_get(CTX_data_scene(C), UI_view2d_fromcontext(C));
}
blender::Vector<Sequence *> sequencer_visible_strips_get(const Scene *scene, const View2D *v2d)
{
const Editing *ed = SEQ_editing_get(scene);
blender::Vector<Sequence *> strips;
LISTBASE_FOREACH (Sequence *, seq, ed->seqbasep) {
if (min_ii(SEQ_time_left_handle_frame_get(scene, seq), SEQ_time_start_frame_get(seq)) >
v2d->cur.xmax)
{
continue;
}
if (max_ii(SEQ_time_right_handle_frame_get(scene, seq),
SEQ_time_content_end_frame_get(scene, seq)) < v2d->cur.xmin)
{
continue;
}
if (seq->machine + 1.0f < v2d->cur.ymin) {
continue;
}
if (seq->machine > v2d->cur.ymax) {
continue;
}
strips.append(seq);
}
return strips;
}
static TimelineDrawContext timeline_draw_context_get(const bContext *C)
{
TimelineDrawContext ctx;
@ -649,9 +678,53 @@ static void drawmeta_contents(TimelineDrawContext *timeline_ctx, const StripDraw
}
}
static void draw_handle_transform_text(const TimelineDrawContext *timeline_ctx,
const StripDrawContext *strip_ctx,
const short direction)
{
/* Draw numbers for start and end of the strip next to its handles. */
if (strip_ctx->strip_is_too_small || (strip_ctx->seq->flag & SELECT) == 0) {
return;
}
if ((strip_ctx->seq->flag & direction) == 0 && (G.moving & G_TRANSFORM_SEQ) == 0) {
return;
}
char numstr[64];
BLF_set_default();
/* Calculate if strip is wide enough for showing the labels. */
size_t numstr_len = SNPRINTF_RLEN(
numstr, "%d%d", int(strip_ctx->left_handle), int(strip_ctx->right_handle));
const float tot_width = BLF_width(BLF_default(), numstr, numstr_len);
if (strip_ctx->strip_length / timeline_ctx->pixelx < 20 + tot_width) {
return;
}
const uchar col[4] = {255, 255, 255, 255};
const float text_margin = 1.2f * strip_ctx->handle_width;
const float text_y = strip_ctx->bottom + 0.09f;
float text_x = strip_ctx->left_handle;
if (direction == SEQ_LEFTSEL) {
numstr_len = SNPRINTF_RLEN(numstr, "%d", int(strip_ctx->left_handle));
text_x += text_margin;
}
else {
numstr_len = SNPRINTF_RLEN(numstr, "%d", int(strip_ctx->right_handle - 1));
text_x = strip_ctx->right_handle -
(text_margin + timeline_ctx->pixelx * BLF_width(BLF_default(), numstr, numstr_len));
}
UI_view2d_text_cache_add(timeline_ctx->v2d, text_x, text_y, numstr, numstr_len, col);
}
float sequence_handle_size_get_clamped(const Scene *scene, Sequence *seq, const float pixelx)
{
const float maxhandle = (pixelx * SEQ_HANDLE_SIZE) * U.pixelsize;
const bool use_thin_handle = (U.sequencer_editor_flag & USER_SEQ_ED_SIMPLE_TWEAKING) != 0;
const float handle_size = use_thin_handle ? 5.0f : 8.0f;
const float maxhandle = (pixelx * handle_size) * U.pixelsize;
/* Ensure that handle is not wider, than quarter of strip. */
return min_ff(maxhandle,
@ -660,87 +733,52 @@ float sequence_handle_size_get_clamped(const Scene *scene, Sequence *seq, const
4.0f));
}
/* Draw a handle, on left or right side of strip. */
static void draw_seq_handle(TimelineDrawContext *timeline_ctx,
static void draw_seq_handle(const TimelineDrawContext *timeline_ctx,
const StripDrawContext *strip_ctx,
const short direction)
{
Sequence *seq = strip_ctx->seq;
const bool show_handles = (timeline_ctx->sseq->timeline_overlay.flag &
SEQ_TIMELINE_SHOW_HANDLES) != 0;
const bool strip_selected = (seq->flag & SELECT) != 0;
const bool handle_selected = (seq->flag & direction) != 0;
if ((!strip_selected || !handle_selected) && !show_handles) {
return;
}
if (SEQ_transform_is_locked(timeline_ctx->channels, seq)) {
return;
}
uint whichsel = 0;
uchar col[4];
/* Set up co-ordinates and dimensions for either left or right handle. */
rctf handle = {0, 0, strip_ctx->bottom, strip_ctx->top};
if (direction == SEQ_LEFTHANDLE) {
handle.xmin = strip_ctx->left_handle;
handle.xmax = strip_ctx->left_handle + strip_ctx->handle_width;
whichsel = SEQ_LEFTSEL;
if ((seq->type & SEQ_TYPE_EFFECT) && SEQ_effect_get_num_inputs(seq->type) > 0) {
return;
}
else if (direction == SEQ_RIGHTHANDLE) {
handle.xmin = strip_ctx->right_handle - strip_ctx->handle_width;
handle.xmax = strip_ctx->right_handle;
whichsel = SEQ_RIGHTSEL;
}
if (!(seq->type & SEQ_TYPE_EFFECT) || SEQ_effect_get_num_inputs(seq->type) == 0) {
if (seq->flag & whichsel) {
if (strip_ctx->is_active_strip) {
UI_GetThemeColor3ubv(TH_SEQ_ACTIVE, col);
}
else {
UI_GetThemeColor3ubv(TH_SEQ_SELECTED, col);
/* Make handles slightly brighter than the outlines. */
UI_GetColorPtrShade3ubv(col, col, 50);
}
col[3] = 255;
}
else {
col[0] = col[1] = col[2] = 0;
col[3] = 50;
}
timeline_ctx->quads->add_quad(handle.xmin, handle.ymin, handle.xmax, handle.ymax, col);
}
/* Draw numbers for start and end of the strip next to its handles. */
if (!strip_ctx->can_draw_strip_content ||
!(((seq->flag & SELECT) && (G.moving & G_TRANSFORM_SEQ)) || (seq->flag & whichsel)))
{
if (!ED_sequencer_can_select_handle(timeline_ctx->scene, seq, timeline_ctx->v2d)) {
return;
}
char numstr[64];
size_t numstr_len;
const int fontid = BLF_default();
BLF_set_default();
/* Calculate if strip is wide enough for showing the labels. */
numstr_len = SNPRINTF_RLEN(
numstr, "%d%d", int(strip_ctx->left_handle), int(strip_ctx->right_handle));
float tot_width = BLF_width(fontid, numstr, numstr_len);
if (strip_ctx->strip_length / timeline_ctx->pixelx > 20 + tot_width) {
col[0] = col[1] = col[2] = col[3] = 255;
float text_margin = 1.2f * strip_ctx->handle_width;
float text_x = strip_ctx->left_handle;
const float text_y = strip_ctx->bottom + 0.09f;
if (direction == SEQ_LEFTHANDLE) {
numstr_len = SNPRINTF_RLEN(numstr, "%d", int(strip_ctx->left_handle));
text_x += text_margin;
}
else {
numstr_len = SNPRINTF_RLEN(numstr, "%d", int(strip_ctx->right_handle - 1));
text_x = strip_ctx->right_handle -
(text_margin + timeline_ctx->pixelx * BLF_width(fontid, numstr, numstr_len));
}
UI_view2d_text_cache_add(timeline_ctx->v2d, text_x, text_y, numstr, numstr_len, col);
uchar col[4];
if (strip_selected && handle_selected && seq == SEQ_select_active_get(timeline_ctx->scene)) {
UI_GetThemeColor4ubv(TH_SEQ_ACTIVE, col);
}
else if (strip_selected && handle_selected) {
UI_GetThemeColor4ubv(TH_SEQ_SELECTED, col);
}
else {
col[0] = col[1] = col[2] = 0;
col[3] = 50;
}
rctf handle = {0, 0, strip_ctx->bottom, strip_ctx->top};
if (direction == SEQ_LEFTSEL) {
handle.xmin = strip_ctx->left_handle;
handle.xmax = strip_ctx->left_handle + strip_ctx->handle_width;
}
else if (direction == SEQ_RIGHTSEL) {
handle.xmin = strip_ctx->right_handle - strip_ctx->handle_width;
handle.xmax = strip_ctx->right_handle;
}
timeline_ctx->quads->add_quad(handle.xmin, handle.ymin, handle.xmax, handle.ymax, col);
}
static void draw_seq_outline(TimelineDrawContext *timeline_ctx, const StripDrawContext *strip_ctx)
@ -1463,8 +1501,10 @@ static void draw_seq_strips(TimelineDrawContext *timeline_ctx,
draw_effect_inputs_highlight(timeline_ctx, &strip_ctx);
draw_multicam_highlight(timeline_ctx, &strip_ctx);
draw_seq_solo_highlight(timeline_ctx, &strip_ctx);
draw_seq_handle(timeline_ctx, &strip_ctx, SEQ_LEFTHANDLE);
draw_seq_handle(timeline_ctx, &strip_ctx, SEQ_RIGHTHANDLE);
draw_seq_handle(timeline_ctx, &strip_ctx, SEQ_LEFTSEL);
draw_seq_handle(timeline_ctx, &strip_ctx, SEQ_RIGHTSEL);
draw_handle_transform_text(timeline_ctx, &strip_ctx, SEQ_LEFTSEL);
draw_handle_transform_text(timeline_ctx, &strip_ctx, SEQ_RIGHTSEL);
draw_seq_outline(timeline_ctx, &strip_ctx);
}
timeline_ctx->quads->draw();

View File

@ -27,6 +27,7 @@
/* For menu, popup, icons, etc. */
#include "ED_anim_api.hh"
#include "ED_screen.hh"
#include "ED_sequencer.hh"
#include "ED_time_scrub_ui.hh"
#include "ED_util_imbuf.hh"
@ -338,7 +339,7 @@ static int sequencer_view_selected_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
ARegion *region = CTX_wm_region(C);
blender::VectorSet strips = selected_strips_from_context(C);
blender::VectorSet strips = ED_sequencer_selected_strips_from_context(C);
View2D *v2d = UI_view2d_fromcontext(C);
rctf cur_new = v2d->cur;

View File

@ -30,6 +30,7 @@
#include "ED_markers.hh"
#include "ED_screen.hh"
#include "ED_sequencer.hh"
#include "ED_space_api.hh"
#include "ED_time_scrub_ui.hh"
#include "ED_transform.hh"
@ -39,6 +40,7 @@
#include "WM_api.hh"
#include "WM_message.hh"
#include "SEQ_retiming.hh"
#include "SEQ_sequencer.hh"
#include "SEQ_time.hh"
#include "SEQ_transform.hh"
@ -646,6 +648,93 @@ static void sequencer_main_region_message_subscribe(const wmRegionMessageSubscri
}
}
static bool mouseover_retiming_key(const Scene *scene,
const Sequence *seq,
const View2D *v2d,
const ScrArea *area,
float mouse_co_region[2])
{
const SpaceSeq *sseq = static_cast<SpaceSeq *>(area->spacedata.first);
if (!SEQ_retiming_data_is_editable(seq) || !retiming_keys_are_visible(sseq)) {
return false;
}
rctf retiming_keys_box = retiming_keys_box_get(scene, v2d, seq);
return BLI_rctf_isect_pt_v(&retiming_keys_box, mouse_co_region);
}
static void sequencer_main_cursor(wmWindow *win, ScrArea *area, ARegion *region)
{
int wmcursor = WM_CURSOR_DEFAULT;
const bToolRef *tref = area->runtime.tool;
if (!STREQ(tref->idname, "builtin.select")) {
WM_cursor_set(win, wmcursor);
return;
}
rcti scrub_rect = region->winrct;
scrub_rect.ymin = scrub_rect.ymax - UI_TIME_SCRUB_MARGIN_Y;
if (BLI_rcti_isect_pt_v(&scrub_rect, win->eventstate->xy)) {
WM_cursor_set(win, wmcursor);
return;
}
if ((U.sequencer_editor_flag & USER_SEQ_ED_SIMPLE_TWEAKING) == 0) {
WM_cursor_set(win, wmcursor);
return;
}
float mouse_co_region[2] = {float(win->eventstate->xy[0] - region->winrct.xmin),
float(win->eventstate->xy[1] - region->winrct.ymin)};
float mouse_co_view[2];
UI_view2d_region_to_view(
&region->v2d, mouse_co_region[0], mouse_co_region[1], &mouse_co_view[0], &mouse_co_view[1]);
const Scene *scene = win->scene;
const Editing *ed = SEQ_editing_get(scene);
if (ed == NULL) {
WM_cursor_set(win, wmcursor);
return;
}
int side;
Sequence *seq1, *seq2;
ED_sequencer_handle_selection_refine(scene, &region->v2d, mouse_co_view, &seq1, &seq2, &side);
if (seq1 == nullptr) {
WM_cursor_set(win, wmcursor);
return;
}
if (mouseover_retiming_key(scene, seq1, &region->v2d, area, mouse_co_region)) {
WM_cursor_set(win, wmcursor);
return;
}
const View2D *v2d = &region->v2d;
const float scale_y = UI_view2d_scale_get_y(v2d);
if (!ED_sequencer_can_select_handle(scene, seq1, v2d) || scale_y < 16 * U.pixelsize) {
WM_cursor_set(win, wmcursor);
return;
}
if (seq1 != nullptr && seq2 != nullptr) {
wmcursor = WM_CURSOR_BOTH_HANDLES;
}
else if (side == SEQ_SIDE_LEFT) {
wmcursor = WM_CURSOR_LEFT_HANDLE;
}
else if (side == SEQ_SIDE_RIGHT) {
wmcursor = WM_CURSOR_RIGHT_HANDLE;
}
WM_cursor_set(win, wmcursor);
}
/* *********************** header region ************************ */
/* Add handlers, stuff you only do once or on area/region changes. */
static void sequencer_header_region_init(wmWindowManager * /*wm*/, ARegion *region)
@ -1015,6 +1104,9 @@ void ED_spacetype_sequencer()
art->message_subscribe = sequencer_main_region_message_subscribe;
art->keymapflag = ED_KEYMAP_TOOL | ED_KEYMAP_GIZMO | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES |
ED_KEYMAP_ANIMATION;
art->cursor = sequencer_main_cursor;
art->event_cursor = true;
art->clip_gizmo_events_by_ui = true;
BLI_addhead(&st->regiontypes, art);
/* Preview. */

View File

@ -150,6 +150,9 @@ enum eTFlag {
/** Special flag for when the transform code is called after keys have been duplicated. */
T_DUPLICATED_KEYFRAMES = 1 << 26,
/** Handle tweaking with #USER_SEQ_ED_SIMPLE_TWEAKING. */
T_HANDLE_TWEAK = 1 << 27
};
ENUM_OPERATORS(eTFlag, T_DUPLICATED_KEYFRAMES);

View File

@ -934,7 +934,7 @@ static TransConvertTypeInfo *convert_type_get(const TransInfo *t, Object **r_obj
if (t->options & CTX_SEQUENCER_IMAGE) {
return &TransConvertType_SequencerImage;
}
if (sequencer_retiming_mode_is_active(t->context)) {
if (sequencer_retiming_mode_is_active(t->scene)) {
return &TransConvertType_SequencerRetiming;
}
return &TransConvertType_Sequencer;

View File

@ -696,6 +696,14 @@ static void special_aftertrans_update__sequencer(bContext * /*C*/, TransInfo *t)
&t->scene->markers, t->scene, TFM_TIME_EXTEND, t->values_final[0], t->frame_side);
}
}
if ((t->flag & T_HANDLE_TWEAK) != 0) {
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
blender::VectorSet<Sequence *> strips = seq_transform_collection_from_transdata(tc);
for (Sequence *seq : strips) {
seq->flag &= ~(SEQ_LEFTSEL | SEQ_RIGHTSEL);
}
}
}
void transform_convert_sequencer_channel_clamp(TransInfo *t, float r_val[2])

View File

@ -10,6 +10,8 @@
#include <cstdlib>
#include "DNA_screen_types.h"
#include "DNA_sequence_types.h"
#include "DNA_space_types.h"
#include "BKE_context.hh"
@ -22,6 +24,11 @@
#include "transform.hh"
#include "transform_mode.hh"
#include "ED_sequencer.hh"
#include "SEQ_sequencer.hh"
#include "SEQ_time.hh"
#include "MEM_guardedalloc.h"
using namespace blender;
@ -288,6 +295,62 @@ static void calcSpringFactor(MouseInput *mi)
}
}
static int transform_seq_slide_strip_cursor_get(const Sequence *seq)
{
if ((seq->flag & SEQ_LEFTSEL) != 0) {
return WM_CURSOR_LEFT_HANDLE;
}
if ((seq->flag & SEQ_RIGHTSEL) != 0) {
return WM_CURSOR_RIGHT_HANDLE;
}
return WM_CURSOR_NSEW_SCROLL;
}
static int transform_seq_slide_cursor_get(TransInfo *t)
{
if ((U.sequencer_editor_flag & USER_SEQ_ED_SIMPLE_TWEAKING) == 0) {
return WM_CURSOR_NSEW_SCROLL;
}
const Scene *scene = t->scene;
blender::VectorSet<Sequence *> strips = ED_sequencer_selected_strips_from_context(t->context);
if (strips.size() == 1) {
t->flag |= T_HANDLE_TWEAK;
return transform_seq_slide_strip_cursor_get(strips[0]);
}
if (strips.size() == 2) {
Sequence *seq1 = strips[0];
Sequence *seq2 = strips[1];
if (SEQ_time_left_handle_frame_get(scene, seq1) > SEQ_time_left_handle_frame_get(scene, seq2))
{
SWAP(Sequence *, seq1, seq2);
}
if (seq1->machine != seq2->machine) {
return WM_CURSOR_NSEW_SCROLL;
}
const Scene *scene = t->scene;
if (SEQ_time_right_handle_frame_get(scene, seq1) !=
SEQ_time_left_handle_frame_get(scene, seq2))
{
return WM_CURSOR_NSEW_SCROLL;
}
const int cursor1 = transform_seq_slide_strip_cursor_get(seq1);
const int cursor2 = transform_seq_slide_strip_cursor_get(seq2);
if (cursor1 == WM_CURSOR_RIGHT_HANDLE && cursor2 == WM_CURSOR_LEFT_HANDLE) {
t->flag |= T_HANDLE_TWEAK;
return WM_CURSOR_BOTH_HANDLES;
}
}
return WM_CURSOR_NSEW_SCROLL;
}
void initMouseInputMode(TransInfo *t, MouseInput *mi, MouseInputMode mode)
{
/* In case we allocate a new value. */
@ -383,6 +446,11 @@ void initMouseInputMode(TransInfo *t, MouseInput *mi, MouseInputMode mode)
t->flag |= T_MODAL_CURSOR_SET;
WM_cursor_modal_set(win, WM_CURSOR_NSEW_SCROLL);
}
/* On ly use special cursor, when tweaking strips with mouse. */
if (t->mode == TFM_SEQ_SLIDE && (t->launch_event == 1 || t->launch_event == 3)) {
WM_cursor_modal_set(win, transform_seq_slide_cursor_get(t));
}
break;
case HLP_SPRING:
case HLP_ANGLE:

View File

@ -1168,7 +1168,7 @@ void transform_mode_init(TransInfo *t, wmOperator *op, const int mode)
t->mode = eTfmMode(mode);
t->mode_info = mode_info_get(t, mode);
if (t->spacetype == SPACE_SEQ && sequencer_retiming_mode_is_active(t->context)) {
if (t->spacetype == SPACE_SEQ && sequencer_retiming_mode_is_active(t->scene)) {
t->mode_info = &TransMode_translate;
}

View File

@ -634,6 +634,7 @@ typedef enum eSpaceSeq_SequencerTimelineOverlay_Flag {
SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG = (1 << 3),
SEQ_TIMELINE_SHOW_STRIP_RETIMING = (1 << 4),
SEQ_TIMELINE_SHOW_FCURVES = (1 << 5),
SEQ_TIMELINE_SHOW_HANDLES = (1 << 6),
/** Draw all wave-forms. */
SEQ_TIMELINE_ALL_WAVEFORMS = (1 << 7),
/** Draw no wave-forms. */

View File

@ -1025,7 +1025,7 @@ typedef struct UserDef {
/** Pie menu distance from center before a direction is set. */
short pie_menu_threshold;
short _pad6[2];
int sequencer_editor_flag; /* eUserpref_SeqEditorFlags */
char factor_display_type;
@ -1511,6 +1511,10 @@ typedef enum eUserpref_SeqProxySetup {
USER_SEQ_PROXY_SETUP_AUTOMATIC = 1,
} eUserpref_SeqProxySetup;
typedef enum eUserpref_SeqEditorFlags {
USER_SEQ_ED_SIMPLE_TWEAKING = (1 << 0),
} eUserpref_SeqEditorFlags;
/* Locale Ids. Auto will try to get local from OS. Our default is English though. */
/** #UserDef.language */
enum {

View File

@ -5877,6 +5877,11 @@ static void rna_def_space_sequencer_timeline_overlay(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, nullptr, "flag", SEQ_TIMELINE_SHOW_STRIP_RETIMING);
RNA_def_property_ui_text(prop, "Show Retiming Keys", "Display retiming keys on top of strips");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, nullptr);
prop = RNA_def_property(srna, "show_strip_handles", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "flag", SEQ_TIMELINE_SHOW_HANDLES);
RNA_def_property_ui_text(prop, "Show Strip Handles", "Display strip handles when unselected");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, nullptr);
}
static void rna_def_space_sequencer(BlenderRNA *brna)

View File

@ -5572,6 +5572,13 @@ static void rna_def_userdef_edit(BlenderRNA *brna)
RNA_def_property_array(prop, 3);
RNA_def_property_ui_text(prop, "Sculpt/Paint Overlay Color", "Color of texture overlay");
/* VSE */
prop = RNA_def_property(srna, "use_sequencer_simplified_tweaking", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(
prop, nullptr, "sequencer_editor_flag", USER_SEQ_ED_SIMPLE_TWEAKING);
RNA_def_property_ui_text(
prop, "Tweak Handles", "Allows dragging handles without selecting them first");
/* duplication linking */
prop = RNA_def_property(srna, "use_duplicate_mesh", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "dupflag", USER_DUP_MESH);

View File

@ -11,6 +11,6 @@
struct Scene;
struct Sequence;
Sequence *SEQ_select_active_get(Scene *scene);
Sequence *SEQ_select_active_get(const Scene *scene);
bool SEQ_select_active_get_pair(Scene *scene, Sequence **r_seq_act, Sequence **r_seq_other);
void SEQ_select_active_set(Scene *scene, Sequence *seq);

View File

@ -25,8 +25,8 @@ struct SequencerToolSettings;
enum {
SEQ_SIDE_MOUSE = -1,
SEQ_SIDE_NONE = 0,
SEQ_SIDE_LEFT,
SEQ_SIDE_RIGHT,
SEQ_SIDE_LEFT = (1 << 1), /* Same as SEQ_LEFTSEL. */
SEQ_SIDE_RIGHT = (1 << 2), /*Same as SEQ_RIGHTSEL. */
SEQ_SIDE_BOTH,
SEQ_SIDE_NO_CHANGE,
};

View File

@ -14,7 +14,7 @@
#include "SEQ_select.hh"
#include "SEQ_sequencer.hh"
Sequence *SEQ_select_active_get(Scene *scene)
Sequence *SEQ_select_active_get(const Scene *scene)
{
const Editing *ed = SEQ_editing_get(scene);

View File

@ -1181,5 +1181,83 @@ void wm_init_cursor_data()
BlenderCursor[WM_CURSOR_PICK_AREA] = &PickAreaCursor;
END_CURSOR_BLOCK;
/********************** Right handle cursor ***********************/
BEGIN_CURSOR_BLOCK;
static char right_handle_bitmap[] = {
0x00, 0x00, 0xfc, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0,
0x11, 0xc0, 0x31, 0xc0, 0x71, 0xc0, 0x71, 0xc0, 0x31, 0xc0, 0x11,
0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xfc, 0x01, 0x00, 0x00,
};
static char right_handle_mask[] = {
0xfc, 0x03, 0xfc, 0x03, 0xfc, 0x03, 0xe0, 0x0b, 0xe0, 0x1b, 0xe0,
0x3b, 0xe0, 0x7b, 0xe0, 0xfb, 0xe0, 0xfb, 0xe0, 0x7b, 0xe0, 0x3b,
0xe0, 0x1b, 0xe0, 0x0b, 0xfc, 0x03, 0xfc, 0x03, 0xfc, 0x03,
};
static BCursor RightHandleCursor = {
right_handle_bitmap,
right_handle_mask,
7,
7,
false,
};
BlenderCursor[WM_CURSOR_RIGHT_HANDLE] = &RightHandleCursor;
END_CURSOR_BLOCK;
/********************** Left handle cursor ***********************/
BEGIN_CURSOR_BLOCK;
static char left_handle_bitmap[] = {
0x00, 0x00, 0x80, 0x3f, 0x80, 0x03, 0x80, 0x03, 0x80, 0x03, 0x88,
0x03, 0x8c, 0x03, 0x8e, 0x03, 0x8e, 0x03, 0x8c, 0x03, 0x88, 0x03,
0x80, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x3f, 0x00, 0x00,
};
static char left_handle_mask[] = {
0xc0, 0x3f, 0xc0, 0x3f, 0xc0, 0x3f, 0xc0, 0x07, 0xd8, 0x07, 0xdc,
0x07, 0xde, 0x07, 0xdf, 0x07, 0xdf, 0x07, 0xde, 0x07, 0xdc, 0x07,
0xd8, 0x07, 0xc0, 0x07, 0xc0, 0x3f, 0xc0, 0x3f, 0xc0, 0x3f,
};
static BCursor LeftHandleCursor = {
left_handle_bitmap,
left_handle_mask,
7,
7,
false,
};
BlenderCursor[WM_CURSOR_LEFT_HANDLE] = &LeftHandleCursor;
END_CURSOR_BLOCK;
/********************** both handles cursor ***********************/
BEGIN_CURSOR_BLOCK;
static char both_handles_bitmap[] = {
0x00, 0x00, 0x3f, 0xfc, 0x38, 0x1c, 0x38, 0x1c, 0x38, 0x1c, 0x38,
0x1c, 0x38, 0x1c, 0x38, 0x1c, 0x38, 0x1c, 0x38, 0x1c, 0x38, 0x1c,
0x38, 0x1c, 0x38, 0x1c, 0x38, 0x1c, 0x3f, 0xfc, 0x00, 0x00,
};
static char both_handles_mask[] = {
0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x7c, 0x3e, 0x7c, 0x3e, 0x7c,
0x3e, 0x7c, 0x3e, 0x7c, 0x3e, 0x7c, 0x3e, 0x7c, 0x3e, 0x7c, 0x3e,
0x7c, 0x3e, 0x7c, 0x3e, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe,
};
static BCursor BothHandlesCursor = {
both_handles_bitmap,
both_handles_mask,
7,
7,
false,
};
BlenderCursor[WM_CURSOR_BOTH_HANDLES] = &BothHandlesCursor;
END_CURSOR_BLOCK;
/********************** Put the cursors in the array ***********************/
}

View File

@ -57,6 +57,10 @@ enum WMCursorType {
WM_CURSOR_PICK_AREA,
WM_CURSOR_LEFT_HANDLE,
WM_CURSOR_RIGHT_HANDLE,
WM_CURSOR_BOTH_HANDLES,
/* --- ALWAYS LAST ----- */
WM_CURSOR_NUM,
};