SmartTableColumnLayout.h

The column sums with no widget in them: where each edge lands, and where a moved column ends up.

Source/SmartTables/Public/SmartTableColumnLayout.h

The column width and order sums, with the widget taken out of them.

Nothing in here knows what an SListView or an SHeaderRow is. A width is worked out before any widget is built.

USmartTable is the way in. Its own member functions read the inputs off the named columns and the layout in force, call through to these, then write the answers back.

Types
6
Of which
1 enum, 1 namespace, 4 struct
Members
27

SmartTable::ColumnLayout namespace

The column width and order sums, with the widget taken out of them.

Nothing in here knows what an SListView or an SHeaderRow is. A width is worked out before any widget is built.

USmartTable is the way in. Its own member functions read the inputs off the named columns and the layout in force, call through to these, then write the answers back.

Public functions 11

ResolveWidths
SMARTTABLES_API TArray< FResolvedWidth > ResolveWidths( TArrayView< const FColumn > Columns, float TotalWidth, float MinWidth, bool bStretchLast )

The pixel width of every shown column, for the space the table has right now.

A column with a UserWidth takes it. A Fixed one takes its named pixels. The Fill columns split what is left by weight, and weights adding up to nothing share it equally instead. bStretchLast then widens the last entry to close any gap under the total.

Everything is floored at MinWidth, a chosen width too, so the fixed and chosen columns alone can run the total over TotalWidth. Columns arrive in drawn order with the hidden ones dropped, and TotalWidth at or below zero answers with nothing: a column at the floor for one frame is worse than no answer.

FindEdgeAt
SMARTTABLES_API FEdgeHit FindEdgeAt( TArrayView< const FColumn > ShownColumns, float LocalX, float GripWidth, float LeadingOffset )

Which column right edge, if any, the pointer is close enough to grab.

The walk adds widths left to right, and that running total is each column right edge, in the places the header draws them. LeadingOffset is whatever sits pinned left of the first column without being one, which is the row number gutter.

It gives up at the first column with no measured width. Past an unknown the total is a guess, and a grip on the wrong column is worse than no grip at all.

ResolveGrowDrag
SMARTTABLES_API FGrantedDrag ResolveGrowDrag( float StartDraggedWidth, float DesiredWidth, TArrayView< const float > StartRightWidths, float MinWidth )

Works a resize drag out against the columns to its right.

Every number is measured from where the drag began. Never from the widths in force this frame, and never from the space the table has to fill. Filling a window is what ResolveWidths is for.

The dragged column grows until everything right of it sits at MinWidth. The cost comes one column at a time, starting with the neighbour along. What the drag pushes on is what moves first.

WidthsToWrite
SMARTTABLES_API TArray< int32 > WidthsToWrite( TArrayView< const float > Targets, TArrayView< const float > Drawn, float Epsilon )

Which of the widths a drag answered for really moved, by index into Targets.

ResolveGrowDrag answers for every column right of the drag, and the first one or two take up the whole cost. The rest already sit at the width they are being set to. Only a column that really moved counts as chosen, and a chosen column is one ResolveWidths stops fitting to the window.

Drawn is what each column draws at now. A target with no drawn width beside it counts as moved, since an unknown is no match. Epsilon is how close two widths have to be to pass for the same one.

StretchLast
SMARTTABLES_API float StretchLast( float BaseWidth, float TotalWidth, float OthersTotal )

The width of the last column when the table is set to stretch it.

A floor and not an override. A last column dragged wider than the leftover keeps that width, and only one narrower than the gap is widened to close it.

HeaderWidthModeFor
SMARTTABLES_API EHeaderWidthMode HeaderWidthModeFor( const FSmartTableColumn & Column, bool bWidthsLive )

The mode a header column is built in. A column goes Manual only once the widths are live and it can be resized. Before that, and for a column that cannot be resized, it keeps its authored Fixed or Fill.

Manual reads its pixel width from an attribute on every frame. The header and every row follow it through a resize, and nothing is built again. Fixed writes its width into the cell once, when the cell is built. A Fixed column only moves when the header is built again, and that drops every row to nothing for a frame.

A Manual column takes its width from a width pass. Before the first pass has run, a column that fills would draw with no width at all.

MovedColumns
SMARTTABLES_API TArray< FName > MovedColumns( const TMap< FName, float > & Before, TArrayView< const FResolvedWidth > After, float Epsilon )

Which columns of a width pass moved since the last one, in the order After lists them.

A column moved when Before holds no width for it, or one further than Epsilon from its new width.

AutoScrollStep
SMARTTABLES_API float AutoScrollStep( float CursorX, float ViewportWidth, float EdgeZone, float Step )

How far a resize drag pans the table this frame, from where the cursor sits in the viewport.

Step while the cursor is within EdgeZone of the right edge, the negative of Step within EdgeZone of the left edge, and nothing anywhere else. The right edge wins in a viewport narrow enough for the two zones to meet.

MergeOrder
SMARTTABLES_API TArray< FName > MergeOrder( TArrayView< const FName > AuthoredIds, TArrayView< const FName > RequestedIds )

Folds a requested order into the authored one.

The requested ids lead, in the order asked for. Any that names no column is dropped, and a column named twice is placed once. Every column the request never names follows them, in the order it was authored, so a stored layout need not name them all.

LandingIndex
SMARTTABLES_API int32 LandingIndex( TArrayView< const FName > Order, int32 From, int32 Delta, TFunctionRef< bool( FName ) > IsShown )

Where the column at From lands after Delta steps along Order, as an index into the whole of Order.

Only a shown column counts as a step. Delta is places on screen, since that is what Move Left and Move Right are bound to. A hidden column between two shown ones would otherwise take the move and leave the header drawn as it was. Hidden columns keep their places, and only the moved one goes anywhere.

A Delta past the last shown column that way lands on that last one. INDEX_NONE when no shown column lies that way at all, and for a Delta of zero.

ColumnAt
SMARTTABLES_API FName ColumnAt( TArrayView< const FColumn > ShownColumns, float LocalX, float LeadingOffset )

Which column holds LocalX, or NAME_None. A column holds its left edge and not its right, and the walk gives up at the first column with no measured width, exactly as FindEdgeAt does.

FColumn struct

One column, flattened to the numbers these functions work on.

Public variables 6

ColumnId
FName ColumnId

The name of this column, the same one the table and the model use.

Sizing

Which of the two things AuthoredWidth means.

AuthoredWidth
float AuthoredWidth = 1.0f

Pixels in Fixed, and a share of the leftover in Fill. Nothing in the type keeps the two apart.

A Fill share is usually 1.0. Handed back as a pixel count it draws a column one pixel wide.

CurrentWidth
float CurrentWidth = 0.0f

The pixel width in force. Zero is not measured yet, and never a column zero pixels wide. The hit tests stop at the first zero they reach.

bResizable
bool bResizable = true

A column that cannot be resized is no grip target. It still counts in every total.

UserWidth
TOptional< float > UserWidth

A width that was chosen by a drag, SetColumnWidth or SizeColumnToContent. ResolveWidths gives the column what it names here, floored at MinWidth, instead of working a width out for it.

Unset leaves the column to be sized. Optional and not a float, since zero is spoken for already: CurrentWidth uses it for not measured yet.

FGrantedDrag struct

What a resize drag really got, and what that cost the columns to its right.

Public variables 3

Granted
float Granted = 0.0f

The width the dragged column really gets. Clamped to what the columns to its right can give up before they all sit at MinWidth.

RightWidths
TArray< float > RightWidths

The width every column right of the drag should now have. All of them, and not only the ones that moved.

Worked out from the snapshot on every call, never stepped on from the last frame. A drag that goes out and comes back lands on the snapshot again, and the neighbour is put back where it was.

bNegotiated
bool bNegotiated = false

False when a column on the right has no measured width, which is the one thing that stops the sums. Granted is then the ask, floored at MinWidth.

RightWidths comes back as the input, and none of it is written down. Those widths are unknown and not wrong, and one stored as a chosen width stays stuck wherever the guess put it.

EHeaderWidthMode enum

Which SHeaderRow sizing mode a header column is built in.

Values 3

ManualManual
Manual
FixedFixed
Fixed
FillFill
Fill

FResolvedWidth struct

The answer for one column out of ResolveWidths. Every shown column comes back exactly once, so the whole result can be set against what was written the time before.

Public variables 2

ColumnId
FName ColumnId
Width
float Width = 0.0f

Pixels, for the space the table has this frame.

FEdgeHit struct

Where FindEdgeAt landed, if anywhere.

Public variables 2

ColumnId
FName ColumnId

The column whose right edge was hit, or NAME_None when the pointer is on no grip.

Left
float Left = 0.0f

Where that column starts, in the same space LocalX was given in.

Next: SmartTableBinding.h