Line Grid Canvas Panel 2.4 : Master Follower Grid Synchronization

The master/follower system lets multiple canvases represent one shared navigable domain without turning them into one giant widget.
A Notes grid can be the master. A ruler grid and a velocity grid can follow it. When the master changes its supported navigation state, the same public function is called on every compatible follower.
This keeps the grids visually aligned while allowing each canvas to retain its own rendering, children, and domain-specific behavior.

How it works

Each canvas can be in one of two roles:

  • A master owns a runtime list of follower grids.
  • A follower has one runtime master grid.
  • A master can have many followers.
  • A follower cannot follow more than one master at a time.
  • Cycles are rejected, so a follower can never eventually point back to itself.

The links use weak references. They do not keep widgets alive, are not editor-authored relationships, and are cleared during load or duplication. This is intentionally a runtime setup system.

Use these base functions:

				
					NotesGrid->AddFollowerGrid(RulerGrid);
NotesGrid->AddFollowerGrid(VelocityGrid);

// Later, if required:
NotesGrid->RemoveFollowerGrid(VelocityGrid);
RulerGrid->DetachFromMasterGrid();
				
			

AddFollowerGrid(...) immediately applies the master’s current supported state to the new follower. It is the right place to set up the relationship from a UMG Construct/OnInitialized path, after the participating widgets exist.

What every LineGridCanvas follower receives

The base canvas synchronizes navigation behavior that has the same meaning across ordinary line-grid canvases.

When the master calls one of these functions, each follower receives the same call:

  • SetScrollingEnabled
  • SetZoomingEnabled
  • SetLogicalSpan
  • ZoomNormalized
  • ScrollNormalized
  • ScrollToLogicalUnit
  • ScrollToGeometryPosition

So, for example:

				
					NotesGrid->SetLogicalSpan(FFloatInterval(-25.0f, 50.0f), Orient_Horizontal);
				
			

updates the master and then calls the same span function on its followers.

When a follower is first attached, the master also applies its current horizontal and vertical navigation snapshot:

  • scrolling enabled state;
  • zooming enabled state;
  • logical span;
  • normalized zoom;
  • normalized scroll position.

This means a ruler added after the Notes grid has already been zoomed and scrolled starts aligned rather than waiting for the next user interaction.

Extending the system

Further specialized canvases can extend follower synchronization without changing the base relationship.
The pattern is:

				
					void UMyGridCanvas::SynchronizeFollowerGrid(UAxLineGridCanvasPanel* InFollowerGrid)
{
    UMyGridCanvas* MatchingFollower = Cast<UMyGridCanvas>(InFollowerGrid);

    if (MatchingFollower)
    {
        MatchingFollower->SetMySharedDomainSetting(GetMySharedDomainSetting());
    }

    Super::SynchronizeFollowerGrid(InFollowerGrid);
}
				
			

For a new shared mutation, add a public setter on the specialized canvas, update its own state, then cast compatible followers and call that same setter on them.
That keeps propagation explicit and domain-safe: shared timeline state moves together, while each panel remains free to own what makes it distinct.

Example: MediaGrid followers

UAxMediaGridCanvas extends the base relationship for followers that are also media grids.

A compatible media follower receives:

  • SetPlaybackPosition
  • SetPlayheadPositionMode
  • SetPlayheadViewportAnchor

This is useful when several timeline views should track the same playhead and fixed-viewport playback behavior.

The following remain local:

  • playback LineGrid ID;
  • whether the cursor is drawn;
  • cursor brush, width, and draw layer.

That distinction matters: two panels may follow the same playback position but intentionally draw very different playheads—or none at all.

Example 2: MusicalGrid followers

UAxMusicalGridCanvas extends the relationship again for followers that are also musical grids.

A compatible musical follower receives:

  • SetTimeSignature
  • SetTPQ
  • SetTimeGridOrientation
  • SetDefaultBarLineSpacing
  • SetBeatsLogicalSpan
  • SetGridQuantization

This is the useful shared time-domain layer for a piano roll, bar ruler, and velocity lane.

For example:

				
					NotesGrid->SetBeatsLogicalSpan(FFloatInterval(-25.0f, 50.0f));
NotesGrid->SetGridQuantization(EAxMusicalTimeDivision::OneQuarterBeat);
				
			

updates compatible musical followers through their public musical APIs. The follower therefore rebuilds its Bars/Beats relationship correctly rather than having another canvas write into its internal variables.

SetDefaultBarLineSpacing and GetDefaultBarLineSpacing were also added so bar spacing can be changed through the same function-based path.

These remain local to each musical canvas:

  • pitch enablement and pitch span;
  • pitch LineGrid ID and pitch spacing;
  • grid density;
  • visual styling;
  • movement-snap policy.

A velocity lane may share musical time with a piano roll while having entirely different vertical-axis meaning. The system deliberately does not force those settings to match.