rott/kf_sources/XInterface/Classes/GUIListBase.uc
2026-07-14 20:27:09 +07:00

646 lines
17 KiB
Ucode

// ====================================================================
// Class: UT2K4UI.GUIListBase
//
// Abstract GUIList list box component.
//
// Written by Joe Wilcox
// Made abstract by Jack Porter
// (c) 2002, Epic Games, Inc. All Rights Reserved
// ====================================================================
class GUIListBase extends GUIComponent
Native
Abstract;
// if _RO_
// else
//#exec OBJ LOAD FILE=InterfaceContent.utx
// end if _RO_
cpptext
{
protected:
virtual void DrawItem(UCanvas* Canvas, INT Item, FLOAT X, FLOAT Y, FLOAT W, FLOAT HT, UBOOL bSelected, UBOOL bPending) {}
public:
}
var() bool bSorted; // Should we sort this list
var() bool bHotTrack; // Use the Mouse X/Y to always hightlight something
var() bool bHotTrackSound; // Whether to make the mouse over sound when hottracking
var() bool bDrawSelectionBorder; // Should we draw a selection border around the selected item
var() bool bVisibleWhenEmpty; // List is still drawn when there are no items in it.
var() bool bNotify; // Used to abort OnChange notification in list
var() bool bInitializeList; // If true, set index to 0 when adding first item
var() bool bMultiSelect; // allow multiple selections (where implemented)
var() bool bAllowEmptyItems;
var() noexport GUIScrollBarBase MyScrollBar;
// Styles
var(Style) string SelectedStyleName; // Name of the style to use for the selected item
var(Style) string SectionStyleName; // Name of the style to use for header items
var(Style) string OutlineStyleName;
var(Style) noexport GUIStyles SelectedStyle;
var(Style) noexport GUIStyles SectionStyle;
var(Style) noexport GUIStyles OutlineStyle; // Used for outlining a pending drag-n-drop
var() ETextAlign SectionJustification;
var() Material SelectedImage; // Image to use when displaying
var() color SelectedBKColor; // Color for a selection background
var() noexport editconst int Top,Index; // Pointers in to the list
var() noexport editconst const int ItemsPerPage; // # of items per Page. Is set natively
var() noexport editconst const float ItemHeight; // Size of each row. Subclass should set in PreDraw.
var() noexport editconst const float ItemWidth; // Width of each row.. Subclass should set in PreDraw.
var() noexport editconst int ItemCount; // # of items in this list
// Drag-n-drop
// You must set these to enable drag-n-drop
var() noexport editconst array<int> SelectedItems;
var() noexport editconst int LastSelected; // Last selected item
var() noexport editconst int LastPressX, LastPressY; // Last position of mouse press
var() noexport editconst int DropIndex; // Indicates the insertion position for the drag-n-drop operation
var() noexport editconst int MightRemove; // Indicates an item that will be de-selected unless a drag operation begins
var() noexport editconstarray array<GUIComponent> LinkedObjects; // Objects state is changed based on whether this list has a valid index
// Not yet implemented (drag-n-drop)
// This will eventually allow you to auto-scroll a list while holding the cursor
// on the top or bottom item of the target list when moving items
delegate OnScrollBy(GUIComponent Sender);
// Owner-draw.
delegate OnDrawItem(Canvas Canvas, int Item, float X, float Y, float W, float HT, bool bSelected, bool bPending);
delegate OnAdjustTop(GUIComponent Sender);
// Called when hot tracking, and a new item is highlighted
delegate OnTrack(GUIComponent Sender, int LastIndex);
delegate CheckLinkedObjects( GUIListBase List )
{
if ( IsValid() )
EnableLinkedObjects();
else DisableLinkedObjects();
}
function InitComponent(GUIController MyController, GUIComponent MyOwner)
{
Super.InitComponent(MyController,MyOwner);
if (SectionStyleName != "" && SectionStyle == None)
SectionStyle = MyController.GetStyle(SectionStyleName,FontScale);
if (SelectedStyleName != "" && SelectedStyle == None)
SelectedStyle = MyController.GetStyle(SelectedStyleName,FontScale);
if (OutlineStyleName != "" && OutlineStyle == None)
OutlineStyle = MyController.GetStyle(OutlineStyleName,FontScale);
}
function Sort(); // Add in a bit
function int SilentSetIndex(int NewIndex)
{
local int i;
bNotify = False;
i = SetIndex(NewIndex);
bNotify = True;
return i;
}
// Should be subclassed
event int CalculateIndex(optional bool bRequireValidIndex) { return -1; }
function int SetIndex(int NewIndex)
{
if ( !IsValidIndex(NewIndex) )
Index = -1;
else
Index = NewIndex;
if ( index >= 0 && ItemsPerPage > 0 )
{
if ( Index < Top )
SetTopItem(Index);
else if ( Index >= Top + ItemsPerPage )
SetTopItem(Index - ItemsPerPage + 1);
else if ( bNotify )
CheckLinkedObjects(Self);
}
else
{
if ( bNotify )
CheckLinkedObjects(Self);
if ( Top >= ItemCount )
Home();
}
IndexChanged(self);
return Index;
}
function IndexChanged(GUIComponent Sender)
{
if ( bNotify )
OnChange(Sender);
LastSelected = Index;
}
function ClearPendingElements()
{
// log(Name@"ClearPendingElements()");
if (bRepeatClick)
{
bRepeatClick = False;
return;
}
SelectedItems.Remove(0, SelectedItems.Length);
DropIndex = -1;
}
function Clear()
{
Top = 0;
ItemCount=0;
SetIndex(-1);
MyScrollBar.AlignThumb();
}
function MakeVisible(float Perc)
{
SetTopItem(int((ItemCount-ItemsPerPage) * Perc));
}
function SetTopItem(int Item)
{
//log("GUIListBase::SetTopItem"@Item@"ItemsPerPage:"$ItemsPerPage);
Top = Item;
if (Top+ItemsPerPage>=ItemCount)
Top = ItemCount - ItemsPerPage;
if (Top<0)
Top=0;
if ( bNotify )
CheckLinkedObjects(Self);
OnAdjustTop(Self);
if ( MyScrollBar != None )
MyScrollBar.AlignThumb();
}
function int AddLinkObject( GUIComponent NewObj, optional bool bNoCheck )
{
local int i;
if ( NewObj != None )
{
if ( !bNoCheck )
{
for (i = 0; i < LinkedObjects.Length; i++)
if ( LinkedObjects[i] == NewObj )
return i;
}
i = LinkedObjects.Length;
LinkedObjects[i] = NewObj;
return i;
}
return -1;
}
function InitLinkObjects( array<GUIComponent> NewObj, optional bool bNoCheck )
{
local int i;
if ( !bNoCheck )
{
for (i = NewObj.Length - 1; i >= 0; i--)
if ( NewObj[i] == None )
NewObj.Remove(i,1);
}
LinkedObjects = NewObj;
if ( bNotify )
CheckLinkedObjects(Self);
}
function EnableLinkedObjects()
{
local int i;
for (i = 0; i < LinkedObjects.Length; i++)
if ( LinkedObjects[i] != None )
EnableComponent(LinkedObjects[i]);
}
function DisableLinkedObjects()
{
local int i;
for (i = 0; i < LinkedObjects.Length; i++)
if ( LinkedObjects[i] != None )
DisableComponent(LinkedObjects[i]);
}
function bool IsValid()
{
if (Index < 0 || Index >= ItemCount)
return false;
return true;
}
function bool IsValidIndex( int i )
{
if ( i < 0 || i >= ItemCount )
return false;
return true;
}
event string AdditionalDebugString()
{
return " SelectedItems:"@ SelectedItems.Length;
}
function string GetItemAtIndex( int idx ) { return ""; }
// Specify true for bGuarantee to receive the selected item if there are no "pending" items
function array<string> GetPendingItems(optional bool bGuarantee)
{
local int i;
local array<string> Items;
if ( (DropState == DRP_Source && Controller.DropSource == Self) || bGuarantee )
{
for ( i = 0; i < SelectedItems.Length; i++ )
if ( IsValidIndex(SelectedItems[i]) )
Items[Items.Length] = GetItemAtIndex(SelectedItems[i]);
if ( Items.Length == 0 && IsValid() )
Items[0] = GetItemAtIndex(Index);
}
return Items;
}
function bool InternalOnKeyEvent( out byte Key, out byte KeyState, float Delta )
{
local int i;
local Interactions.EInputKey iKey;
if ( ItemsPerPage == 0 || ItemsPerPage == 0 ) return false;
iKey = EInputKey(Key);
if ( KeyState == 3 && ikey == IK_MouseWheelUp ) { WheelUp(); return true; }
if ( KeyState == 3 && ikey == IK_MouseWheelDown ) { WheelDown(); return true; }
if ( KeyState != 1 ) return false;
switch ( iKey )
{
case IK_Up:
if ( Up() )
return true;
break;
case IK_Down:
if ( !Controller.ShiftPressed && Down() )
return true;
break;
case IK_Left:
if ( MoveLeft() )
return true;
break;
case IK_Right:
if ( MoveRight() )
return true;
break;
case IK_Home:
Home();
return true;
case IK_End:
End();
return true;
case IK_PageUp:
PgUp();
return true;
case IK_PageDown:
PgDn();
return true;
case IK_A:
if ( Controller.CtrlPressed && bMultiSelect )
{
SelectedItems.Length = ItemCount;
for ( i = 0; i < ItemCount; i++ )
SelectedItems[i] = i;
return true;
}
}
return false;
}
function bool Up() { return false; }
function bool Down() { return false; }
function bool MoveRight() { return false; }
function bool MoveLeft() { return false; }
function WheelUp();
function WheelDown();
function PgUp();
function PgDn();
function Home();
function End();
// We've received a mouse press in the drop source
function InternalOnMousePressed(GUIComponent Sender, bool IsRepeat)
{
local int i, j, k, NewIndex;
if ( !IsInClientBounds() || ItemsPerPage == 0 )
return;
// If not holding down the mouse
if (!IsRepeat && ItemCount > 0)
{
// log(Name@"InternalOnMousePressed");
NewIndex = CalculateIndex(True);
if ( NewIndex == -1 )
return;
LastPressX = Controller.MouseX;
LastPressY = Controller.MouseY;
// If we had an Item selected, go ahead and add it to the drag-n-drop list
if ( Controller.CtrlPressed && bMultiSelect && SelectedItems.Length == 0 && NewIndex != Index )
SelectedItems[SelectedItems.Length] = Index;
// If shift is pressed, do shift selection
if ( Controller.ShiftPressed && IsMultiSelect() )
{
if ( LastSelected == -1 )
LastSelected = 0;
// If not pressing Ctrl, clear out the SelectedItems array
if ( !Controller.CtrlPressed )
for ( j = SelectedItems.Length - 1; j >= 0; j-- )
if ( SelectedItems[j] != Index )
SelectedItems.Remove(j,1);
for ( j = Min(LastSelected, NewIndex); j <= Max(LastSelected, NewIndex); j++ )
{
for ( k = 0; k < SelectedItems.Length; k++ )
{
if ( j == SelectedItems[k] )
break;
if ( j < SelectedItems[k] )
{
SelectedItems.Insert(k, 1);
SelectedItems[k] = j;
break;
}
}
if ( k == SelectedItems.Length )
SelectedItems[k] = j;
}
return;
}
else
{
LastSelected = NewIndex;
if ( IsMultiSelect() )
Index = NewIndex;
}
// Find out if the this index is already in our selected list
for (i = 0; i < SelectedItems.Length; i++)
if (SelectedItems[i] == NewIndex)
break;
// If it was found, remove it (allows toggling an Item's selectedness)
// don't remove it immediately, but set MightRemove. This way, if the user drags
// the selected Item, instead of releasing the mouse, we won't deselect it.
if (i < SelectedItems.Length)
MightRemove = i;
else if (Controller.CtrlPressed && bMultiSelect)
{
for ( i = 0; i < SelectedItems.Length; i++ )
if ( NewIndex < SelectedItems[i] )
{
SelectedItems.Insert(i, 1);
SelectedItems[i] = NewIndex;
break;
}
if ( i == SelectedItems.Length )
SelectedItems[i] = NewIndex;
}
else if (SelectedItems.Length > 0 && MightRemove == -1)
SelectedItems.Remove(0, SelectedItems.Length);
}
}
// Called on both the source and target when mouse is released.
function InternalOnMouseRelease(GUIComponent Sender)
{
// log(Name@"InternalOnMouseRelease Sender:"$Sender.Name);
if ( MightRemove >= 0 && MightRemove < SelectedItems.Length )
SelectedItems.Remove(MightRemove, 1);
MightRemove = -1;
// Stop here if we aren't doing a multi-selection
if ( !IsMultiSelect() )
ClearPendingElements();
}
// Called when mouse is pressed and user begins to move mouse while items are selected.
function bool InternalOnBeginDrag(GUIComponent Sender)
{
if ( Controller == None )
return false;
// Must move the mouse more than 3 pixels in order to begin a drag operation
// to account for accidentally moving the mouse a little while clicking
if ( (Abs(LastPressX - Controller.MouseX) < 3) && (Abs(LastPressY - Controller.MouseY) < 3) )
return false;
// log(Name@"InternalOnBeginDrag");
MightRemove = -1;
if ( SelectedItems.Length == 0 )
CheckDragSelect();
// Assign the offset that native rendering will use to draw the hovering selection
UpdateOffset(ClientBounds[0], ClientBounds[1], ClientBounds[2], ClientBounds[3]);
SetOutlineAlpha(128);
return true;
}
// Find out if we were clicking on any Item - if so, add it to SelectedItems
function CheckDragSelect()
{
local int i;
i = CalculateIndex(True);
if ( i < 0 )
return;
SetIndex(i);
SelectedItems[SelectedItems.Length] = i;
}
function bool InternalOnRightClick( GUIComponent Sender )
{
if ( bDropSource && bMultiSelect && SelectedItems.Length > 0 )
Controller.bIgnoreNextRelease = True;
return true;
}
// Called on the drop source when when an Item has been dropped. bAccepted tells it whether
// the operation was successful or not.
// Should be subclassed
function InternalOnEndDrag(GUIComponent Accepting, bool bAccepted);
// Called on the drop target when the mouse is released - Sender is always DropTarget
// Should be subclassed
function bool InternalOnDragDrop(GUIComponent Sender)
{
return false;
}
// Called on whatever component the data is being dragged over
function InternalOnDragOver(GUIComponent Sender)
{
local int NewIndex;
if ( Controller == None )
return;
if (Controller.DropTarget == Self)
{
NewIndex = CalculateIndex(True);
if (NewIndex == -1)
{
DropIndex = -1;
return;
}
// Remove any items that were selected in this list
if ( Controller.DropSource != Self && SelectedItems.Length > 0 )
SelectedItems.Remove(0, SelectedItems.Length);
DropIndex = NewIndex;
}
}
function InternalOnDragEnter(GUIComponent Sender)
{
/*
Uncomment to add special mouse cursor
MouseCursorIndex = -- "OK to drop here" mouse cursor -- ;
*/
SetOutlineAlpha(255);
}
function InternalOnDragLeave(GUIComponent Sender)
{
/* Uncomment to add special mouse cursor
MouseCursorIndex = -- "Cannot drop here" mouse cursor --;
*/
SetOutlineAlpha(128);
if (DropIndex >= 0)
DropIndex = -1;
}
event bool IsMultiSelect()
{
if ( Controller == None )
return false;
return bDropSource && bMultiSelect && DropState != DRP_Source && (Controller.CtrlPressed || Controller.ShiftPressed) && OnMultiSelect(Self);
}
function SetOutlineAlpha(int NewAlpha)
{
local int i;
if (OutlineStyle == None)
return;
for (i = 0; i < 5; i++)
OutlineStyle.ImgColors[i].A = NewAlpha;
}
defaultproperties
{
bAcceptsInput=true
FontScale=FNS_Small
StyleName="NoBackground"
SelectedStyleName="ListSelection"
SelectedBKColor=(R=255,G=255,B=200,A=255)
SectionJustification=TXTA_Left
Top=0
Index=-1
ItemsPerPage=0
bTabStop=true
bVisibleWhenEmpty=false
bBoundToParent=false
bScaleToParent=false
bDrawSelectionBorder=True
bNotify=True
bInitializeList=True
bMultiSelect=true
bHotTrackSound=True
OutlineStyleName="ItemOutline"
OnBeginDrag=InternalOnBeginDrag
OnEndDrag=InternalOnEndDrag
OnDragDrop=InternalOnDragDrop
OnDragEnter=InternalOnDragEnter
OnDragLeave=InternalOnDragLeave
OnDragOver=InternalOnDragOver
OnRightClick=InternalOnRightClick
OnKeyEvent=InternalOnKeyEvent
OnMousePressed=InternalOnMousePressed
OnMouseRelease=InternalOnMouseRelease
LastSelected=-1
MightRemove=-1
DropIndex=-1
SectionStyleName="ListSection"
}