SmartTableCell.h

The class a cell Widget Blueprint sits on, and the two stock cells.

Source/SmartTables/Public/SmartTableCell.h

One cell, for a column that needs more than text.

Recycled, never rebuilt. The table keeps a pool and hands cells to new rows as the list scrolls, so OnCellAssigned runs many times on one instance. Whatever it sets on one row it sets again on the next. Whatever the cell holds is dropped in OnCellReleased. An item kept past a release stays alive as long as the table, and a delegate handle left bound fires for a row this cell stopped drawing.

OnItemSet has the shape of IUserObjectListEntry, so a UListView entry is already this class. A cell whose widgets bind to a viewmodel needs neither. It names an ItemSetter and holds no graph at all.

Class
USmartTableCell
Extends
UUserWidget
Members
62
Blueprint nodes
8
Panel fields
2

Public functions 15

Cell 7

Find Cell AnimationFindCellAnimationpure node
UFUNCTION( BlueprintPure, Category = "Smart Tables|Cell" ) UWidgetAnimation * FindCellAnimation( FName AnimationName ) const

One animation of this Widget Blueprint, by name.

See USmartTableRowWidget::FindRowAnimation for why UMG has nothing of its own here.

Get Assign ReasonGetAssignReasonpure node
UFUNCTION( BlueprintPure, Category = "Smart Tables|Cell" ) ESmartTableAssignReason GetAssignReason() const

Why this cell was last given a row.

The same answer OnCellAssigned gets, kept here so a C++ cell can read it from NativeOnCellAssigned, which takes no arguments.

Get TableGetTablepure node
UFUNCTION( BlueprintPure, Category = "Smart Tables|Cell" ) USmartTable * GetTable() const

The table this cell draws in, where the styles and the columns are named. Null between a release and the next row, the same as GetModel, and null inside a release that teardown caused.

Get ModelGetModelpure node
UFUNCTION( BlueprintPure, Category = "Smart Tables|Cell" ) USmartTableModel * GetModel() const

The model this cell reads. Null between a release and the next row.

Get Row IndexGetRowIndexpure node
UFUNCTION( BlueprintPure, Category = "Smart Tables|Cell" ) int32 GetRowIndex() const

The row this cell draws right now, in natural space.

Get Column IdGetColumnIdpure node
UFUNCTION( BlueprintPure, Category = "Smart Tables|Cell" ) FName GetColumnId() const

The column this cell draws. NAME_None while it sits in the pool.

Get ItemGetItempure node
UFUNCTION( BlueprintPure, Category = "Smart Tables|Cell" ) UObject * GetItem() const

Null when the source has no items at all: a DataTable row, or a model that says it has none.

Drag and Drop 4

Make Cell Drag PayloadMakeCellDragPayloadoverride
UFUNCTION( BlueprintNativeEvent, Category = "Smart Tables|Drag and Drop" ) class USmartTableCellDragDropOp * MakeCellDragPayload()

What this cell gives over when a drag starts on it.

The default fills in the row, the column, the item and the table, then the drag visual and the grab point. An override carries more. With none, the drop reads USmartTableCellDragDropOp.

Only called while the table has bAllowCellDragDrop on.

Make Cell Drag VisualMakeCellDragVisualoverride
UFUNCTION( BlueprintNativeEvent, Category = "Smart Tables|Drag and Drop" ) UWidget * MakeCellDragVisual()

What follows the cursor during the drag. This same cell again, by default.

A second cell of the column's DragVisualClass is built, given the same row and column, and sized to what this one is drawing right now. Null carries no visual at all, which is all a drag that only moves data needs. A cell that was never laid out answers null too, since there is no size to copy.

A Smart Table Cell anywhere inside what this gives back is RELEASED when the drag ends, at any depth, whether the plugin built it or an override did. No pool takes a drag visual's cell back, so nothing else reaches its OnCellReleased.

Get Cell Drag RoleGetCellDragRolepure node
UFUNCTION( BlueprintPure, Category = "Smart Tables|Drag and Drop" ) ESmartTableCellDragRole GetCellDragRole() const

What this cell is to the drag going on right now.

On Cell Drag Role ChangedOnCellDragRoleChangedoverride
UFUNCTION( BlueprintNativeEvent, Category = "Smart Tables|Drag and Drop", meta = ( DisplayName = "On Cell Drag Role Changed" ) ) void OnCellDragRoleChanged( ESmartTableCellDragRole NewRole )

This cell is now the source of a drag, the thing under the pointer, or neither.

The table marks both already, as bMarkDragCells describes. This is for a cell doing something the marking can not: grow, change colour, play a sound, hide what it holds while that is on the move.

With bMarkDragCells left on, a cell that marks itself here carries two markings at once.

C++ only 4

AssignCell
void AssignCell( USmartTable * InTable, USmartTableModel * InModel, int32 InNaturalRow, FName InColumnId, ESmartTableAssignReason InReason )
ReleaseCell
void ReleaseCell()

Given back to the pool. The cell goes on existing and draws another row later. Its table may already be gone, since this also runs while the row it sits in is torn down.

HasItemSetter
bool HasItemSetter() const

Read off the class default by the table, where a model with no items is a mistake for this cell.

SetCellDragRole
void SetCellDragRole( ESmartTableCellDragRole NewRole )

Set by the row as a drag moves over its cells, and never called from outside the table.

Protected functions 11

Cell 3

On Cell AssignedOnCellAssignedbp event
UFUNCTION( BlueprintImplementableEvent, Category = "Smart Tables|Cell", meta = ( DisplayName = "On Cell Assigned" ) ) void OnCellAssigned( ESmartTableAssignReason Reason )

Fires every time this cell is given a row, one it has drawn before too.

Reason is what sets the cases apart. A cell that plays an entrance whatever the reason says plays it on every scroll, since scrolling is what hands a pooled cell its next row.

On Cell ReleasedOnCellReleasedbp event
UFUNCTION( BlueprintImplementableEvent, Category = "Smart Tables|Cell", meta = ( DisplayName = "On Cell Released" ) ) void OnCellReleased()

Fires as the cell goes back to the pool, while the row it drew can still be read. Anything held is dropped here.

GetTable answers null when teardown caused the release, so nothing here may read a default back off the table. A cell takes its look when it is GIVEN a row, where the table is there by contract.

On Item SetOnItemSetbp event
UFUNCTION( BlueprintImplementableEvent, Category = "Smart Tables|Cell", meta = ( DisplayName = "On Item Set" ) ) void OnItemSet( UObject * Item )

The short form of the one above, for a cell that only needs the item.

C++ only 8

Get Item Setter OptionsGetItemSetterOptions
UFUNCTION() TArray< FName > GetItemSetterOptions() const

The functions of the classes below USmartTableCell that CanBeItemSetter allows, for the list in the Details panel. A Set function of a viewmodel context is there once the Widget Blueprint compiles.

NativeOnCellAssignedvirtual
virtual void NativeOnCellAssigned()
NativeOnCellReleasedvirtual
virtual void NativeOnCellReleased()
NativeOnMouseButtonDownoverride
virtual FReply NativeOnMouseButtonDown( const FGeometry & Geometry, const FPointerEvent & MouseEvent ) override

Drag and drop, which a cell only joins in while its table lets it.

This base starts drags and says when a drop happened. It moves nothing. The drop goes out on the table as OnCellDropped, which is where a game changes its own data.

NativeOnDragDetectedoverride
virtual void NativeOnDragDetected( const FGeometry & Geometry, const FPointerEvent & MouseEvent, UDragDropOperation *& OutOperation ) override
NativeOnDragOveroverride
virtual bool NativeOnDragOver( const FGeometry & Geometry, const FDragDropEvent & DragDropEvent, UDragDropOperation * InOperation ) override
NativeOnDragLeaveoverride
virtual void NativeOnDragLeave( const FDragDropEvent & DragDropEvent, UDragDropOperation * InOperation ) override
NativeOnDropoverride
virtual bool NativeOnDrop( const FGeometry & Geometry, const FDragDropEvent & DragDropEvent, UDragDropOperation * InOperation ) override

Private functions 5

PlayValueChanged
void PlayValueChanged( ESmartTableAssignReason Reason )

Plays ValueChangedAnimation, when the reason is one that should.

HandOverItem
void HandOverItem( UObject * Item )

Hands Item to the ItemSetter, unless it is the item handed last.

FindItemSetter
UFunction * FindItemSetter()

The ItemSetter, found on first use and held. Null when the class names none, and null when the one it names can not take an item.

IsFirstItemSetterWarning
bool IsFirstItemSetterWarning()

True the first time an ItemSetter mistake of this class comes up in this table. A cell with no table says every one.

ResolveFlash
UWidgetAnimation * ResolveFlash()

The held pair above, brought up to date. Not const, because it writes them.

Public variables 1

Value Changed AnimationValueChangedAnimationpanel
UPROPERTY( EditAnywhere, BlueprintReadWrite, Category = "Smart Tables|Cell", meta = ( ToolTip = "A Sequencer animation played when this row's values move, named Pulse by default. A refresh of everything never fires it." ) ) FName ValueChangedAnimation = TEXT( "Pulse" )

An animation played when the values of this row move, and at no other time.

A flash, or a quick change of colour. It starts from where the cell already sits, and nothing pins frame zero in front of it, so an animation whose first key is somewhere else jumps for one frame.

A refresh of everything arrives as Refreshed and plays nothing. Empty for a cell that holds still.

Protected variables 1

Item SetterItemSetterpanel
UPROPERTY( EditDefaultsOnly, Category = "Smart Tables|Cell", meta = ( GetOptions = "GetItemSetterOptions", ToolTip = "The function this cell hands its row's object to, each time it draws a row with a new one. Pick the Set function of a Manual viewmodel context, and the bindings of that context draw the cell." ) ) FName ItemSetter
Options from
GetItemSetterOptions

A function of this cell that takes the row's object, found by name.

Called with the item each time the cell is given a row whose item is a different object, before On Cell Assigned. Called with null as the cell goes back to the pool, after On Cell Released. A row whose item is the one handed last calls nothing, since the bindings a viewmodel feeds draw its changes.

A Manual viewmodel context adds a Set function for itself to the Widget Blueprint, and that function is what this names. It is found by name, so the plugin links no MVVM module.

An item of a class the setter does not take is handed over as null, and warned about once per class.

Private variables 11

Table
TWeakObjectPtr< USmartTable > Table

Weak. The table owns the pool that owns this cell, so a strong pointer back would be a ring.

ModelModel
UPROPERTY( Transient ) TObjectPtr< USmartTableModel > Model
Transient
Never saved.

Strong, because a cell outliving its model would read freed rows.

NaturalRow
int32 NaturalRow = INDEX_NONE
ColumnId
FName ColumnId
DragRole

Cleared on release with everything else, or a pooled cell would draw the marking of the last drag on a row that had nothing to do with it.

AssignReason

Starts at the case that has to stay quiet, so a cell read before it ever drew can not be taken for one that just arrived.

bWantsItem
TOptional< bool > bWantsItem

Whether a graph on this class answers On Item Set, worked out the first time a row arrives. Unset until then, since the answer belongs to the class and a cell keeps the class it was built as.

ResolvedItemSetter
TOptional< TWeakObjectPtr< UFunction > > ResolvedItemSetter

Unset until FindItemSetter first runs, for the reason bWantsItem is.

Handed ItemHandedItem
UPROPERTY( Transient ) TObjectPtr< UObject > HandedItem
Transient
Never saved.

What the ItemSetter was last handed. Strong, like Model, and dropped on release.

Resolved FlashResolvedFlash
UPROPERTY( Transient ) TObjectPtr< UWidgetAnimation > ResolvedFlash
Transient
Never saved.

The animation named above, found once and held.

FindWidgetAnimation walks the class's property chain. A live feed asks for this one on every cell whose value moves, so that walk is what a table that flashes pays per change.

The name is the whole key. A widget's class never changes under it, and a graph built again in the editor gives new widgets instead of changing these.

NAME_None in the pair here means the name has never been asked. That is also the answer for a widget naming no animation, and both want the same nothing back.

ResolvedFlashFor
FName ResolvedFlashFor

USmartTableEditableCell class

The cell a column gets when its values are typed and not just read.

The model answers GetCellEditor and nothing here guesses: a text box, the same box aligned right for a number, or a checkbox. None draws the value as plain text, so a column of enums in an editable table stays readable instead of turning into a box that should not be typed into.

A value is written on Enter or on losing focus, never per character. A model that sorted again on every key would move the row out from under the cursor mid word. A refused value goes back to what the model still holds, with a Verbose line and no other mark. A redraw of its own row keeps the typing, and rows arriving or leaving above it end the edit with nothing written.

Protected functions 3

NativeOnCellAssignedoverride
virtual void NativeOnCellAssigned() override
NativeOnCellReleasedoverride
virtual void NativeOnCellReleased() override
RebuildWidgetoverride
virtual TSharedRef< SWidget > RebuildWidget() override

Private functions 7

ShowModelValue
void ShowModelValue()

Puts what the model holds into the editor this cell names, and shows that one. It never asks which is on screen already.

ShowNothing
void ShowNothing()

Clears all three editors and shows the read-only one.

Two cases reach it. A cell going back to the pool clears what it drew. A cell already in the pool can still be asked for a value, since Slate commits an edit on a focus change that can arrive long after the row scrolled away.

IsEditOpen
bool IsEditOpen() const

Whether any FSlateUser holds the focus in the text box, which is an edit being typed.

EndOpenEdit
void EndOpenEdit( const TCHAR * Why )

Takes the focus off the text box, for whichever FSlateUser holds it, and the typing goes with it. Nothing is written.

For a cell given a different row, where a commit would write the typing into the wrong row, and for a row the model offers no box on now. Why goes into the log line.

CommitText
void CommitText( const FText & Value, ETextCommit::Type Cause )
IsCellChecked
bool IsCellChecked( const FText & Drawn ) const

Whether the toggle draws checked. A numeric sort key answers it whatever words draw the row, and the drawn word is the fallback for a model with no numeric key here.

CommitToggle
void CommitToggle( ECheckBoxState State )

Private variables 5

Switcher
TSharedPtr< class SWidgetSwitcher > Switcher

All three editors are built once and swapped by index. Building the Slate again per row would throw away the pooling, and a cell rebuilt while focused would lose the edit in it.

TextBox
TSharedPtr< class SEditableTextBox > TextBox
CheckBox
TSharedPtr< class SCheckBox > CheckBox
ReadOnlyText
TSharedPtr< class STextBlock > ReadOnlyText

USmartTableTextCell class

The cell every column gets when it names none.

It draws the model text in the table's cell text style, and takes the per cell colour from the model where the model gives one. A text column needs no asset and no Widget Blueprint.

Protected functions 2

NativeOnCellAssignedoverride
virtual void NativeOnCellAssigned() override
RebuildWidgetoverride
virtual TSharedRef< SWidget > RebuildWidget() override

Private variables 1

TextBlock
TSharedPtr< class STextBlock > TextBlock

Next: SmartTableRowWidget.h