Skip to content

#6780 — Interactive Scene Strip Gizmos in the Dope Sheet - #6813

Open
Draise14 wants to merge 66 commits into
masterfrom
6780-3d-sequencer---fix-timeline-gizmos-to-retime-shots
Open

Draise14 wants to merge 66 commits into
masterfrom
6780-3d-sequencer---fix-timeline-gizmos-to-retime-shots

Conversation

@Draise14

Copy link
Copy Markdown
Collaborator

Summary

This PR introduces a full set of interactive scene-strip gizmos rendered in the Dope Sheet editor, providing direct manipulation of scene strips on the master sequencer timeline. The gizmos replace the legacy 3D Sequencer addon's overlay-only display with native C++ gizmo controls, bringing parity between the sequencer and the dope sheet for scene-strip editing workflows.

The feature is sync-agnostic — it works with the built-in Blender scene-time sync, the legacy 3D Sequencer addon sync, or with no sync at all. A single "Scene Strip Gizmo" toggle in the Dope Sheet overlays controls all gizmo visibility.

bforartists_6t7euXojPt bforartists_L9Bb0Fxv8h

Key Features

Strip Range Manipulation

  • Green/Red retime handles (left/right) to extend or trim the strip's start/end edges, with live hold-frame extension and neighbor push/pull
  • Move bar (top middle zone) to translate the strip's content window (scene range) while the sequencer position stays locked
  • Slip bar (bottom middle zone) to move the strip in the sequencer timeline, with optional vertical channel stepping
  • Master-timeline scrub area below the bar for playhead scrubbing from the dope sheet

Range Management Toggles (dope sheet overlays)

  • Set Preview Range — the gizmo writes the strip scene's preview range (psfra/pefra) to match the strip extent
  • Set Frame Range (Clamp) — the gizmo clamps the strip scene's render range (sfra/efra) to the strip's visible extent, with optional Lead In/Lead Out padding
  • Both toggles compose: enabled together, both ranges follow the strip edits simultaneously

Layered Strip Display

  • Show All Strips — opt-in display of every scene strip on the master timeline (not just those referencing the active scene), drawn as rounded colored chips above the bar with automatic lane stacking
  • Show Strip Names / Show Scene Names — label toggles for strip and scene identification, drawn inside the chips and on the bar
  • Strips Opacity — transparency control for the layered strip indicators
  • Strip colors match the VSE color tags and scene-strip theme color

No-Strip Fallback Mode (§5.4)

  • When no scene strip exists for the active scene, the gizmo falls back to editing the active scene's own frame ranges directly — usable as a plain range-setting tool on any scene with no sequencer configuration required

Sync Scene Strip Operator

  • ACTION_OT_scene_strip_sync_from_range — one-shot, opt-in reconciliation of the strip's geometry with the strip scene's render range (the inverse of the Clamp mode), available as a refresh button in the Timeline header
  • Explicit cure for the desync that occurs when the user changes the scene range from the timeline — the gizmo then reflects the updated range and subsequent drags do not "snap"

Bidirectional Playhead Sync

  • With sync enabled, scrubbing the dope sheet playhead drives the master timeline playhead bidirectionally while inside the strip's range
  • Deferred timeline switching — scrubbing past a strip's edge does not switch the active timeline until the mouse is released; a ghost highlight previews the landing target
  • Ghost chips on the layered strip display highlight the deferred switch target

Overlap Mode Feedback

  • During a move drag that causes overlap, the gizmo bar shows a colored ring matching the sequencer's overlap mode (green = expand, sky blue = shuffle, red = overwrite)
  • Overlap is resolved on release using the master timeline's configured overlap mode

Undo and Cancel

  • Full undo support for all gizmo operations (OPTYPE_UNDO)
  • Exact Esc-cancel restores all strip state including scene range, preview range, anim offsets, and the SCER_PRV_RANGE flag

Files Changed

File Description
action_gizmo_scene_strip.cc Main feature file (~2700 lines) — gizmo types, modal operator (ACTION_OT_scene_strip_timing), strip editing helpers, sync-from-range operator (ACTION_OT_scene_strip_sync_from_range), scrub operator (ACTION_OT_scene_strip_scrub)
action_intern.hh Internal declarations for the new operators and widget types
action_ops.cc Operator registration
space_action.cc Area initialization with gizmo defaults
CMakeLists.txt Added bf::sequencer dependency for SEQ headers
anim_draw.cc Dope sheet strip range shading (sync-agnostic resolver integration)
anim_ops.cc Deferred timeline switch hookup in the scrub operator
ED_anim_api.hh Exported ANIM_scene_strip_master_get() for dual-store strip resolution
ED_sequencer.hh Exported scrub-defer API
sequencer_edit.cc Deferred timeline switching implementation
DNA_action_types.h New overlay flag enum values (ADS_SHOW_SCENE_STRIP_GIZMOS, ADS_SHOW_USE_PREVIEW_RANGE, ADS_SHOW_SCENE_STRIP_ALL, ADS_SHOW_SCENE_STRIP_SCENE_NAME, ADS_SHOW_SCENE_STRIP_STRIP_NAME, ADS_SHOW_CLAMP_TO_SCENE_STRIP) and all_strips_opacity / clamp_lead_in / clamp_lead_out fields
rna_space.cc RNA property definitions for the new overlay toggles
versioning_503.cc Versioning defaults for the new overlay flags
versioning_defaults.cc Startup defaults
space_dopesheet.py Overlay UI panel with all toggle controls
space_time.py Timeline header "Sync Scene Strip" button
core.py Updated get_master_scene() to honor the workspace pin; get_dopesheet_preview_range() shared with C gizmos
overlay.py Legacy overlay superseded by the built-in C gizmos
ui.py UI merged with the built-in overlay toggle

What to Document

  • User-facing overlay toggles in the Dope Sheet overlays panel:
    • "Scene Strip Gizmo" — master enable/disable
    • "Set Preview Range" — preview range follow behavior
    • "Set Frame Range" with "Lead In"/"Lead Out" — render range clamp behavior
    • "Show All Strips", "Show Strip Names", "Show Scene Names", "Strips Opacity" — display options
  • Gizmo interaction zones and their behavior:
    • Green handle (left) — retime start
    • Red handle (right) — retime end
    • Top bar (move) — slide the range window
    • Bottom bar (slip) — move the strip in the sequencer
    • Scrub area — master timeline scrubbing
  • Composition of toggles: how "Set Preview Range" and "Set Frame Range" interact when both are enabled
  • No-strip fallback mode: the gizmo works on any scene, even without a sequencer setup
  • "Sync Scene Strip" button in the Timeline header — opt-in reconciliation
  • Deferred playhead switching: scrubbing past a strip's edge doesn't switch timelines until release; ghost highlight previews the landing target
  • Overlap mode feedback: colored ring indicating how the sequencer will resolve collisions on release
  • Reference to 6780-gizmo-parity-plan.md for the full design spec and UX model

Draise14 and others added 30 commits September 4, 2026 00:51
Restore the dopesheet scene strip gizmos (they became decorative when the
node editor minimap split the 2D gizmo draw step): gizmo-groups without an
explicit 2D draw step (all Python gizmo-groups) now default to the 2D tools
step, restoring hover highlighting, grabbing and interaction, and fixing the
out-of-bounds drawstep indexing.

Make the gizmos fully functional again:
- Green/red handles extend/squeeze the strip start/end in the master timeline
- Strip body: larger top bar moves the strip, smaller bottom bar slips content
- Overlay sits snug on the marker row instead of floating above it
- Shot scene preview range and scene frame range sync (each with a toggle in
  the sequencer sync settings), also working with the built-in scene sync
…addon #6780

Interactive scene strip gizmos (green/red retime handles, move/slip bars and
master-timeline scrub) are now implemented in C as an ACTION gizmo group,
driven by the existing scene-time sync. Includes a new dope-sheet overlays
toggle (show_scene_strip_gizmos) and native modal operators with undo that
use the sequencer transform helpers. The addon's overlay/gizmos defer to the
C implementation when its toggle is enabled, so the feature works out of the
box without the 3D Sequencer addon.

Generated with Codebuff 🤖
Co-Authored-By: Codebuff <noreply@codebuff.com>
#6780

Annotate the additions to shared/upstream files with bfa markers so the
changes can be identified and re-applied when rebasing or merging against
upstream Blender. The new action_gizmo_scene_strip.cc file itself carries
the 'Bforartists exclusive feature' header.

Generated with Codebuff 🤖
Co-Authored-By: Codebuff <noreply@codebuff.com>
Now it's in the built-in mode and doesn't need the addon to work.
… move, minimap style #6780

The gizmos now update the strip's scene timing out of the box. The dope-sheet
overlays gain Use Preview/Scene Frame Range toggles (default on) that keep the
scene strip's internal start/end frames in sync when retiming, and a single
Show Scene Strip toggle replaces the dead duplicate row (range shading rides
on it). The move gizmo resolves collisions through the sequencer's own overlap
engine per the user's overlap mode - expand (green), shuffle (sky blue),
overwrite (red) - with a mode-colored bump ring while dragging, never jumping
lanes. Visuals are redone minimap-style with a clear hover highlight and a
divider between the slip (top) and move (bottom) zones, and tooltips/status
describe each drag outcome.

Generated with Codebuff 🤖
Co-Authored-By: Codebuff <noreply@codebuff.com>
…equencer---fix-timeline-gizmos-to-retime-shots

# Conflicts:
#	scripts/addons_core/bfa_3Dsequencer/scene/ops.py
#	scripts/addons_core/bfa_3Dsequencer/sequence/ops.py
#6780

Folds in the draw-step intent from the fix-3d-sequencer branch: unflagged
gizmo-groups (Python gizmo templates, scene strip gizmos) belong in the
2D_UI pass so they draw on top like upstream's single 2D step. Their patch
as-is made every non-3D group visible in BOTH passes wherever an editor
draws them back to back (dope sheet, image, clip, node), drawing each
gizmo twice. Adapted instead: each non-3D group is now visible and mapped
to exactly one pass - 2D_TOOL flagged tool gizmos in the tools pass, 2D_UI
flagged or unflagged groups in the UI pass. The scene strip gizmo group is
now explicitly 2D_UI, and the node/image compositor-backdrop gizmo groups
are classified 2D_TOOL to keep drawing where they always did.

Generated with Codebuff 🤖
Co-Authored-By: Codebuff <noreply@codebuff.com>
The gizmo draw bound GPU_SHADER_3D_UNIFORM_COLOR, then rendered the bar
backdrop through ui::draw_roundbox_4fv_ex (a batch draw that leaves the
immediate-mode shader state bound), then bound the same imm program a
second time. immBindShader asserts imm->shader is null, so the double bind
aborted with WITH_ASSERT_ABORT and crashed the app at startup draw. End the
imm program before rebinding; the roundbox calls that follow later are
already followed by the final immUnbindProgram.

Generated with Codebuff 🤖
Co-Authored-By: Codebuff <noreply@codebuff.com>
…les and addon deprecations #6780

The scene strip gizmo round completes the retime feature:

- Fix the bar chip and hover ring geometry (swapped x/y rectangle
  arguments) so the outline encases the gizmo instead of drawing a
  stray box/rod at the region corner
- Slip now offsets both internal scene edges while keeping the master
  window fixed - it no longer stretches only the end edge
- Widen the move zone (50% of a taller 26px bar) so it is easy to grab;
  handles highlight white on hover and in accent color while grabbed;
  move/slip drags ring the bar in blue; remove the in-bar playhead
  hairline that read as an artifact
- Merge the addon and built-in "Scene Strip Gizmo" toggles into one
  shared switch and retire the addon fallback overlay, so disabling the
  gizmos truly hides them (C draw_prepare gating included)
- Unify the preview/scene range controls: the dope-sheet overlays popup
  ("Set Preview Range" / "Set Scene Range") is the single source of
  truth for both the C gizmo and the addon sync (duplicate Advanced
  Settings rows removed), and the gizmo no longer writes scene ranges
  when the toggles are off
- Replace the deprecated strip RNA properties (frame_start /
  frame_final_start / frame_offset_start / ...) with their BFA native
  names (content_start / left_handle / left_handle_offset / ...) across
  the 3D Sequencer addon, silencing the per-frame DeprecationWarning
  spam

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
…rames #6780

The "Set Scene Range" dope-sheet toggle previously only *extended* the
strip scene's frame range, so with a scene range larger than the strip
nothing ever visibly changed. Now, like the preview range, the strip
scene's start/end frames are set exactly to the strip's remapped span on
every gizmo drag. Because a scene strip's content length is derived live
from the scene range, the handles are re-pinned afterwards so the strip
edges stay exactly where the drag put them, and cancel restores the
original scene start frame as well. The addon sync (legacy path) mirrors
the same set-and-re-pin behavior with an equality guard.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
…o visual polish #6780

- "Set Preview Range" no longer forces preview mode on: it only updates the
  strip scene's preview start/end while preview range is already enabled on
  the scene (timeline Preview Range toggle), so that toggle can be turned off
- Scene range writes reverted to extend-only: for scene strips the content
  length is derived live from the scene range, so setting it exactly
  re-anchored the scene to the strip's trims and every slip/retime compounded
  the offset (slip rushed forward, start/end retimes jumped); the range now
  only ever grows to contain the strip, with the handles snapshotted before
  extending so edges track the mouse 1:1
- Visual polish: whiter top (slip) zone, verbatim strip-name label in the bar
  (draws name + 2, skipping the ID_SEQ "SQ" prefix the RNA getter strips),
  half-height layered-strip indicators moved before the roundbox ring/bump
  batches so no imm draw follows a stale bound shader on Vulkan (hard crash
  on hover/grab)
- Addon sync mirrors the preview-range gating and extend-only scene range

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
…ook and Show All Strips #6780

- Remove the "Set Scene Range" toggle and feature end-to-end: extend-only
  scene-range writes proved unreliable for scene strips (content length is
  derived live from the scene range, so set/extend re-anchored the strip's
  trims and made retimes jump). Deleted adapt_scene_range,
  update_scene_frame_range and the ADS_SHOW_USE_SCENE_RANGE flag from the C
  gizmo, DNA, RNA, the dope-sheet overlays popup and the addon sync/operator.
  "Set Preview Range" stays, still opt-in via the timeline Preview Range.
- Lower the gizmo bar and the layered strip row to sit snug on the marker
  row (2 px at 1x scale), hit-rects kept in lockstep with the draw.
- Give the bar the same look as the sequencer scene strips: the strip's
  color tag color when set, else TH_SEQ_SCENE resolved under the sequencer
  theme (theme_store/theme_set swap, same as the VSE's color3ubv_from_seq),
  shaded for body/outline/zones inside the rounded chip, with the verbatim
  strip name and optionally the referenced scene name as the label.
- Layered strips behind the bar are drawn as tinted rounded chips with
  their own color and scene name. New opt-in overlay toggles in the
  dope-sheet Overlays popup: "Show All Strips" (draw every master-timeline
  scene strip, not just same-scene ones) and "Show Scene Names".
- 3D Sequencer addon: drop the use_scene_range property,
  update_scene_frame_range() and its retime-operator use;
  get_dopesheet_range_toggles() becomes get_dopesheet_preview_range().
… layered strip chips #6780

- Remove the "Set Scene Range" toggle and scene frame range writes
  end-to-end: extend-only writes proved unreliable for scene strips
  (content length derives live from the scene range, so retimes jumped).
  Deleted update_scene_frame_range/adapt_scene_range from the C gizmo, the
  ADS_SHOW_USE_SCENE_RANGE flag, the RNA property and popup row, and the
  addon sync/operator plumbing (get_dopesheet_range_toggles() becomes
  get_dopesheet_preview_range()). "Set Preview Range" stays, gated on the
  timeline Preview Range toggle.
- The gizmo bar and layered strips now wear the strip's sequencer color
  (color tag, else TH_SEQ_SCENE resolved under the sequencer theme like
  the VSE's color3ubv_from_seq) inside rounded chips with outline, snug
  against the marker row.
- Overlapping strips draw as full rounded chips stacking snugly in lanes
  above the bar, so layered/crossing shots stay readable.
- New dope-sheet overlay options: "Show All Strips", "Show Strip Names",
  "Show Scene Names" and an "All Strips Opacity" slider.

Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
The warning
C4100: 'master_scene': parámetro sin referencia at action_gizmo_scene_strip.cc(1131) — in slip_shot_content().

Root cause: When the "Set Scene Range" feature was removed, the adapt_scene_range(master_scene, ...) call was dropped from slip_shot_content()'s body, but its Scene *master_scene parameter was left behind — now unused.

The fix
Removed the dead master_scene parameter from slip_shot_content() and updated its single call site in scene_strip_timing_apply() (the GZ_PART_SLIP case):
…chips

1. Sync-agnostic gizmos — scene_strip_master_get() no longer requires the WORKSPACE_SYNC_SCENE_BFA flag. The gizmos now appear whenever a master sequencer timeline exists — the workspace's Sequencer Scene pointer (user-assignable, independent of sync) or the addon's master scene. The sync only drives the playhead; the gizmos no longer care. The popup's graying by use_scene_time_sync is removed, and the RNA tooltip now documents the sync-agnostic behavior.

2. "Open Sequencer" button — new ANIM_OT_switch_editors_to_sequencer operator (wm.switch_editor_to_sequencer), following the exact pattern of the existing switch-editors operators, placed at the top of the Overlays popup with a SEQUENCE icon. One click flips the area to the sequencer so the master timeline is always reachable.

3. Collapsible Scene Strip Gizmo section — the popup now uses the View3D overlays pattern you showed: indented_column(draw_body=...) with a left-aligned checkbox header row and a disclosure triangle — Scene Strip Gizmo ▾ expands to the indented, property-split sub-rows (Set Preview Range, Show All Strips, Show Strip Names, Show Scene Names, Strips Opacity), and collapses to a single ▸ row when toggled off.
…pen Sequencer #6780

- Remove the "Open Sequencer" button and operator from the dope-sheet
  overlays popup: it toggled the area type rather than opening a separate
  editor, and was redundant with the editor type switcher.
- Enable the scene strip gizmo overlays out of the box for new dope-sheet
  spaces (action_create), factory startup files (versioning_defaults) and
  existing files (versioning gate at 503,19 with subversion bump): the
  interactive gizmos, "Show All Strips" indicators and both strip and scene
  name labels on, indicator opacity defaulting to 0.5. The versioning only
  rewrites unset sentinel opacities (0.0/1.0) so user-tuned values survive.
- Redraw dope-sheets when scene strips are edited in the sequencer (name,
  color tag, retiming): the listener previously only acted on NA_SELECTED,
  so renames and recolors left the gizmo bar and labels stale until
  mouse-over.
…ever

  leaves the lane - seeks space at the start/end or bounces back.
- Overwrite: mark the dragged strip SEQ_SELECT during the drag so
  query_overwrite_targets() excludes it; previously the dragged strip was
  targeted and the fallback shuffled it away.
- Keep live StripRuntimeFlag::Overlap feedback during the drag.
Add a batch operator (sequencer.sync_scene_strip_ranges) to the Scene menu
that sets each scene strip's underlying scene start/end frame so they align
to the strip's visible extent in the sequencer timeline, with optional
lead-in/out padding asked in an operator dialog.

Per scene: target range is the union of the visible extents of every strip
sharing it, remapped through remap_frame_value and padded. Non-destructive:
strip positions (left/right handles) are never moved; after the scene-range
write (which can auto-derive a scene strip's content length), each strip's
content_start/channel/duration/handles are read back and restored so the
displayed content stays exactly where it is. Strips without a scene are
skipped and reported. Groundwork for the future "Clamp to Scene Strip" batch
operator.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
Hide the SEQUENCER_MT_shot "Play Master Scene" entry (wm.timeline_sync_play_master)
unless the 3D Sequencer addon's legacy sync mode is active. Built-in sync already
carries the playhead across master and shot scenes bidirectionally, so the manual
master-scene jump is redundant there and only invites confusion.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
Turn the dope-sheet scene strip gizmo's "Set Preview Range" mode on by default
in all three defaults paths: runtime-created spaces (action_create), factory
startup / new blend files (versioning_defaults) and existing files via the
503,19 versioning gate. The RNA default was already true, so only the DNA
flags needed aligning; the gizmo drives the scene's preview range out of the
box, while scene start/end writes stay opt-in.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
Add a "Sync" toggle to the sequencer header (workspace.use_scene_time_sync,
same as the timeline checkbox) that mirrors the 3D Sequencer addon's Sync
button for the built-in sync mode. It only renders while the addon is
disabled, so the two affordances never appear at once; with the addon on,
its button remains the single switch.

The dope-sheet's scene strip range shading (ANIM_draw_scene_strip_range)
now follows either sync flag - the addon's WORKSPACE_SYNC_SCENE_BFA or the
built-in WORKSPACE_SYNC_SCENE_TIME - so both switches light up the dope-sheet
identically.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
Make the scene-strip time sync seam bidirectional for the built-in sync
mode (WORKSPACE_SYNC_SCENE_TIME): scrubbing or playing the dope-sheet
playhead of a shot scene now also moves the sequencer playhead, matching
the legacy 3D Sequencer addon behavior without the addon.

A frame-pair direction cache decides per call which side the user moved:
only the master (sequencer) time changed runs the existing forward mapping
unchanged, only the shot time changed inverts the mapping over the scene
strips showing the active scene (clamping to the nearest strip edge in
lead-in/out territory), and a call with both sides unchanged bails out -
breaking potential feedback loops with the addon's own sync handlers. The
first call after startup or a scene switch only primes the cache.

get_sequencer_scene_for_time_sync() falls through to the workspace's
sequencer scene during playback started outside the sequencer (dope-sheet,
3D view) so the reverse mapping has a master timeline to drive.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
…e sync #6780

Remove the DNA_action_types.h include added with the bidirectional playhead
sync - nothing in sequencer_edit.cc references SpaceAction or the ADS_*
overlay flags, so the include was dead weight.

Also fix the reverse mapping's frame math: the committed code referenced
Scene.frame_start, but the Scene struct stores its frame range in
Scene.r.sfra/efra (frame_start is only the Python API name). Use
active_scene_pre->r.sfra so the shot-to-sequencer playhead inversion
compiles and matches the forward path's simple-scene mapping.
The clamp_scene_strip_range() used the strip's exact visible extent for
sfra/efra, which for a trimmed strip shrunk the scene range and removed
the trim on release. This had two symptoms with the "Clamp to Scene Strip"
toggle on:
- MOVE: the strip snapped to a new position on release (the trim removal
  re-anchored the strip's internal state).
- LEFT-extend backward: after the trim was removed the strip had nothing
  to consume, so the next backward drag hit the previous-strip room guard
  in adjust_shot_duration_left and appeared stuck.

Make the clamp extend-only and non-destructive: sfra only moves backward
(min with the visible start) and efra only moves forward (max with the
visible end), so the strip's trim and displayed content are never touched.
- MOVE now leaves the range unchanged (no snap).
- LEFT/RIGHT extension is preserved (the drag already extends the range).
- A trimmed strip keeps its trim, so backward extension keeps working.

The existing lockstep shift (sfra and strip->start move together with a
startofs compensation) keeps the remap constant, and the handles are
re-pinned after the write (Set-Scene-Range lesson) so the master position
stays fixed.

BFA (#6780): 3D Sequencer dope-sheet scene-strip gizmos.
The SEQUENCER_OT_sync_scene_strip_ranges operator set each scene's
frame_start/frame_end to the strip's visible extent but restored the
strip's content_start to its old value. Since the scene frame shown at a
master-timeline frame is `frame - content_start + frame_start`, moving
frame_start without shifting content_start shifted the strip's displayed
content by the frame_start delta - e.g. a strip showing scene 30-100 with
frame_start at 0 would snap to 30 but then display 60-130.

Fix: shift content_start in lockstep with frame_start (content_start +=
delta) and re-pin the handles, so the remap (displayed content) and the
strip's master position both stay constant while the scene range aligns to
the strip's visible extent (with lead-in/out padding).

BFA (#6780): 3D Sequencer scene strip frame range sync.
Why the green handle got stuck (both your cases)

The old extend path measured free room after consuming trimmed content, so the room was under-counted by the trim amount — any strip with a left neighbor ended up with zero room even with hundreds of free frames. That's your case 2 (stuck at 50) and case 1's stall at 0.
It also had no path to go below frame 0 at all: the scene start was hard-floored at 0 in the drag and again in the release clamp, while the red side has no such ceiling on its end.
The fix

adjust_shot_duration_left() now works like adjust_shot_duration_right(): the strips before yours on the same channel are collected by a new strips_before_same_channel() helper (mirror of the existing strips_after_same_channel()), and they move with the start edge — pushed left when you extend, pulled right when you shrink. No more invisible wall at the neighbor or at frame 0.
The extension is applied in one servo step per mouse move (trim first, then scene-start extension), with the end edge captured before the start moves so it stays pinned — the end-edge creep you saw ("drags everything left") is gone.
Bounds are Blender's standard MINAFRAME (-1048574), same as every other scene range, not 0.
Esc/right-click cancel for left drags now restores from a snapshot (strip fields + scene sfra + pushed strips), so cancel is exact instead of best-effort.
The release-time "Clamp to Scene Strip" SET now floors at MINAFRAME instead of 0, so your lead-in can land in negative frames — matching the red side, which never clamped its end.
Can dig to the start and end properly, with lead in and out
Draise14 and others added 27 commits September 10, 2026 19:00
….4 leftover)

The slip mechanism moves the strip's anim_startofs/anim_endofs when the
preview toggle is off (seq::time_slip_strip path), but those fields were
not part of the cancel snapshot - Esc restored the strip's position and
the scene/preview ranges yet left a cancelled slip's anim offsets
shifted, so the dope-sheet bar stayed displaced after the undo.

The design item was in the §2.4 servo-hygiene list since Round 5; the
fields are now captured at invoke and restored before content_length_set
re-derives the length.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
…hout sync (§5.2)

Two wrap-up fixes:

- MOVE (range window) now runs the release SET clamp with the retime
  handles and the strip mover. The clamp-mode drag translates sfra/efra
  1:1, preserving whatever length the range had at invoke; when that
  length is stale (the range was edited from the timeline first), the
  release snap is what settles it to the strip's visible extent - the
  same one-time alignment the green/red handles apply on their first
  drag. Previously only the ends and the strip mover snapped, so a
  desynced range stayed desynced after moving the window.

- ANIM_draw_scene_strip_range (the dope-sheet strip-range shading) now
  resolves the strip through ANIM_scene_strip_master_get - the same
  dual-store, sync-agnostic resolver the gizmos use, exported from
  ED_anim_api.hh - instead of requiring the sync flags and
  workspace->sequencer_scene. The overlays now draw as soon as a master
  timeline holds the active scene's strip, with sync never initialized.

The shading keeps its give_frame_index mapping (scene-range-based, per
the §2.7 decision in the plan doc).

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
…claration

MSVC builds failed with C4430/C2143: the new ANIM_scene_strip_master_get
declaration used Strip without the type being known to translation units
that include only ED_anim_api.hh (gcc accepted it via the DNA include
chain, MSVC did not). Add a struct Strip forward declaration above it.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
Opt-in, one-way reconciliation of the scene strip's geometry with the strip
scene's render range (sfra/efra). When the user changes the scene range from
the timeline, the dope-sheet gizmo (which draws the strip's extent) keeps its
old width; this operator adopts the new range into the strip so the gizmo
reflects it and subsequent drags do not snap.

- ACTION_OT_scene_strip_sync_from_range: the inverse of clamp_scene_strip_range
  - adopts the render range only (preview range is an output of the strip)
  - left edge via adjust_shot_duration_left, right edge via
    adjust_shot_duration_right, both with clamp=false so sfra/efra are never
    written (the range is already the target, so the clamp cannot loop)
  - preserves the strip's content (anim_startofs/anim_endofs); the trim
    (startofs/endofs) absorbs the change
  - neighbor push/pull is uniform by construction, no overlap can arise
  - poll greys out while the timing gizmo is modal and when the strip's
    visible extent already equals the range
  - one undo step + ND_SEQUENCER / ND_FRAME_RANGE notifiers
- Timeline header button (FILE_REFRESH icon) next to the frame range controls,
  enabled only while the dope-sheet scene-strip gizmo is on; also reachable as
  a searchable operator
… addon is off

The 2.8 dual-store master scan probes the legacy 3D Sequencer addon's
sync settings on every gizmo poll/draw call. When the bfa_3Dsequencer
addon is disabled, its WindowManager.master_scene property is not
registered, and RNA_pointer_get printed a warning for each probe. Look
the property up with RNA_struct_find_property first - a silent query -
and bail when it does not exist.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
…lback (§5.3)

Two follow-ups to §5.2, plus a compile fix.

- Clamp-mode MOVE (range window) now aligns the scene range to the strip's
  visible extent once, on the first movement of the drag, instead of on
  release. The drag previously translated sfra/efra 1:1 with whatever length
  the range had at invoke and then jumped to the strip's extent when released
  ("updates, then snaps"). Initializing up front makes the length consistent
  from the first pixel and removes the release jump, while keeping the mode's
  SET-clamp contract. MOVE is removed from the release clamp; the invoke
  snapshot is untouched, so Esc still restores exactly.

- ANIM_scene_strip_master_get() gains a last-resort scan of bmain->scenes, so
  the gizmo and the dope-sheet range shading draw for the active scene when
  neither master store is set (workspace->sequencer_scene is optional; the
  addon's master_scene is transient and never saved). The "Scene Strip Gizmo"
  toggle alone is now enough to reveal the overlay - no Sync required. The
  shared scene_strip_for_active_scene() helper backs both named stores and the
  scan.

- Fix two stale scene_strip_master_get() call sites in the §2.3
  ACTION_OT_scene_strip_sync_from_range operator (poll and exec), which
  referenced the pre-rename symbol and would not compile.
The no-strip fallback for the scene-strip gizmo declared its pointer to
the render range as `SceneRange`, which is not a declared type in this
translation unit (MSVC C2065). It is Blender's `RenderData`; the field
accesses (psfra/pefra/sfra/efra) already match it.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
…rlay

Move the frame-range controls (Set Preview Range, Set Frame Range, Lead In/Out) above the layered-strip display options so the range-related toggles read together, and add a separator before the display options.
…ical drag

The bottom middle bar (strip mover) now moves the strip between
sequencer channels with vertical mouse motion (one bar-height per
channel step, up = higher channels), while horizontal motion keeps
driving the timeline position as before. Channel changes get the same
live overlap feedback as horizontal moves (red outline), and the
release path already resolves collisions via the sequencer's overlap
mode. Esc restores the original channel exactly, from a new
invoke-time snapshot. The retime handles and the range-window bar keep
channels locked, and the no-strip fallback is unaffected.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
…n documents

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
…h toggle ranges

Two fixes from the finishing-touches review:

Overlap resolve (§5.6): the VSE overlap helpers skip every SEQ_SELECT
strip, so any user-selected neighbor survived resolve_move_overlap and
the strip stayed overlapping in the sequencer while the gizmo drew the
resolve effect. The resolve now temporarily clears the selection of all
other strips and uses the generic transform_handle_overlap for all
modes (the shuffle-only bypass regressed the lane shuffle); a same-lane
time shuffle remains the last resort. The preview-range seeding in the
no-strip fallback now snapshots first, so Esc can undo the seeding.

Fallback dual-range (§5.5): with no scene strip, the gizmo previously
edited exactly one range pair. The toggles now mirror the strip-mode
coupling table: Clamp Scene Range edits the render range, Set Preview
Range edits the preview range, both toggles move both ranges in
lockstep, neither defaults to the render range. Set Preview Range
seeds a preview range from the render range on first use (undoable on
Esc).

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
…t strip edges

Dragging the dopesheet playhead beyond the scene strip's range with sync
on made the playhead glitch and bounce back: the reverse sync clamped
the master playhead to the nearest strip edge, and the next forward
re-derivation mapped that clamped value back to a shot frame inside the
strip, fighting the drag. The reverse mapping now follows the linear
shot->master formula regardless of coverage (nearest strip preferred
when several show the scene, integer math, result bounded at
MINAFRAME/MAXFRAME), keeping shot and master playheads in lockstep past
the strip so the drag stays smooth.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
Re-pinning the sequencer to a scene without scene strips did not "go
back": the gizmo resolver's candidate list let the legacy 3D Sequencer
store win with the previously pinned scene, so the gizmos kept editing
the old master instead of falling back to the active scene's own
ranges. The pinned scene now decides: when set, only it is probed and
a pin without a matching strip yields the fallback mode; the legacy
store and file scan only apply when nothing is pinned. The Python sync
system mirrors the same authority in get_master_scene(), and "Set
Pinned Scene as the Synchronization Timeline" with no pin now clears
the stored master, giving the sync system the same explicit "go back".

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
…f displacing neighbors

Extending or trimming with the green/red handles hard-pushed every
same-channel neighbor along (extend pushed them away, trim pulled them
back), regardless of the sequencer's overlap mode. Like a VSE edge
drag, the handles now never displace neighbors: extending into one
overlaps it with the live red outline and the release path resolves the
collision with the sequencer's overlap mode (expand/shuffle/overwrite);
shrinking just opens a gap. The finish path now runs the overlap
resolution for the retime handles when the final geometry collides
(also covering the clamp-mode lead shift). The neighbor-displacement
cancel bookkeeping stays but becomes a no-op, keeping the snapshot
machinery intact.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
… checklist

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
…over

Blender's region mval[1] grows upward like the sequencer's channel
numbering, so the subtraction had the drag inverted: dragging up pulled
the strip to lower tracks and dragging down to higher ones. Adding the
vertical delta instead aligns the drag with the cursor.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
…se release

Scrubbing the dopesheet playhead past the current scene strip used to switch
the active timeline live: in the master fallback the forward sync also rewrote
the master playhead from the strip mapping, which fought the reverse mapping
and made the playhead oscillate on empty lanes. Full-sequencer control belongs
in the sequencer, and a mid-drag scene swap is unpredictable UX.

While a dopesheet scrub is held down the sync seam no longer switches scenes
or rewrites the master playhead: inside the drag's timeline the shot playhead
stays authoritative (bidirectional sync unchanged), and past it the would-be
switch target (the next scene strip, or the master fallback on an empty lane)
is recorded. The dopesheet draws a ghost highlight of the target range plus a
landing line, and the mouse release applies the switch; Esc drops it. The
sequencer's own playhead keeps live switching.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
… MIN2/MAX2 include)

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
…AX2 unavailable in this header set)

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
…chips

The ghost highlight never showed because shot-scene scrubs run through the
reverse-sync branch, which returned before the forward path's defer block
could record a target. The reverse branch now records the target as well:
the top-most scene strip under the master playhead, or the master fallback
when the playhead is past every strip of the active scene.

The highlight itself moves onto the layered ghost chips the dopesheet
already draws: the target strip's chip brightens to near-white while the
scrub is held, and the background tint is reduced to the master-fallback
case plus the vertical landing line in both cases.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
…ight

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
…trims

Dragging the range-window bar with Clamp off shifted the strip through the
sequencer timeline: the branch wrote the master-side trims (startofs/endofs,
where left_handle = start + startofs), so both handles slid with the drag.

The content window lives in anim_startofs/anim_endofs - the pair the
dopesheet mapping (give_frame_index + sfra + anim_startofs) and the derived
content length read - so slipping those keeps the master handles exactly
where they were while the displayed scene-time range slides. The applied
move is clamped so neither anim offset goes negative: the scene range is
the window's only stop, the same bound a VSE slip has against the media
length. Clamp mode is unchanged (it translates sfra/efra, which for scene
strips is the same content window).

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
The ghost highlight flickered while dragging past the strip edge because
the two sync directions recorded the target with different rules: the
forward block checked the strip under the master playhead, while the
reverse branch measured the distance to same-scene strips only - so
consecutive mouse ticks could disagree and alternate the target on/off.

Both directions now share one recording rule: back inside the drag's
timeline clears the target (and re-anchors the mapping strip), anywhere
else records it - the top-most strip of another scene, or the master
fallback on an empty lane - and it stays until release applies it or the
drag returns. The chip highlight and the master-fallback tint are also
halved so the cue reads as a hint rather than a flash.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
…h target

Dragging the playhead toward the empty master timeline showed almost
nothing: the full-width tint ran at the same half strength as the other
cues. The fallback tint is now the strongest cue in the set - a clearly
visible tint over the whole view topped with bright edge lines - since
there is no chip to highlight in this direction.

Also fixes a gap in the same path: the draw bailed out entirely when the
drag started in the fallback timeline (no drag strip), so a master-to-strip
switch had no cue at all. Master frames now map 1:1 when there is no drag
strip, which is exact - the fallback view IS the master timeline.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
… MOVE

The top middle gizmo (range window) with Clamp off wrote the strip's
anim_startofs/anim_endofs, bounded so neither offset went negative. For a
scene strip with a full-range window both offsets are 0, so the bounds
collapsed to [0, 0] and every mousemove clamped the move to zero - the bar
detected the offset but never moved.

For a scene strip the content window IS the scene frame range [sfra, efra]
(there is no off-screen media to slip into), so the non-clamp MOVE now
translates sfra/efra like the clamp branch. The derived content length and
visible extent are unchanged, so the strip's master handles stay locked -
the dopesheet range slides while the strip does not move in the sequencer.
The servo delta now reads the applied displacement from the scene range in
both middle-bar branches.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
Every gizmo frame write was bounded at MINAFRAME (-1048574), ignoring the
user preference. All floors now go through a shared helper that returns 0
when "Allow Negative Frames" is off (USER_NONEGFRAMES, the default - the
same convention FRAMENUMBER_MIN_CLAMP applies to the playhead) and
MINAFRAME when it is enabled.

Covered: green/red handle extension (the trim and hold-frame extension
are now bounded by their SUM so the master start edge cannot cross the
floor), the strip mover (move_shot stops the left edge at the floor),
the range window translate, the no-strip fallback range edits, the
preview-range translates, the release clamp's new sfra, and the master
start-frame follow on a clamped leftward move.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
@Draise14 Draise14 linked an issue Sep 14, 2026 that may be closed by this pull request
@github-project-automation github-project-automation Bot moved this to 🟥 Todo in Workboard Sep 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: 🟥 Todo

Development

Successfully merging this pull request may close these issues.

3D Sequencer - Fix timeline gizmos to retime shots

2 participants