673 lines
14 KiB
Ucode
673 lines
14 KiB
Ucode
// ====================================================================
|
|
// (C) 2002, Epic Games
|
|
// ====================================================================
|
|
|
|
class GUIVertImageList extends GUIVertList
|
|
Native;
|
|
|
|
|
|
cpptext
|
|
{
|
|
void PreDraw(UCanvas* Canvas);
|
|
void Draw(UCanvas* Canvas);
|
|
void DrawItem( UCanvas* Canvas, INT Item, FLOAT X, FLOAT Y, FLOAT XL, FLOAT YL, UBOOL bSelected, UBOOL bPending );
|
|
}
|
|
|
|
var() eCellStyle CellStyle;
|
|
|
|
var() float ImageScale; // Scale value for the images
|
|
|
|
var() int NoVisibleRows; // How many rows of visible images are there
|
|
var() int NoVisibleCols; // How many cols of visible images are there
|
|
|
|
var() int HorzBorder, VertBorder; // How much white space
|
|
|
|
var() editconstarray editconst array<ImageListElem> Elements;
|
|
var() editconstarray editconst array<ImageListElem> SelectedElements;
|
|
|
|
var() material LockedMat;
|
|
|
|
event int CalculateIndex( optional bool bRequireValidIndex )
|
|
{
|
|
local int i;
|
|
local int HitCol, HitRow;
|
|
|
|
HitCol = (Controller.MouseX - ClientBounds[0]) / ItemWidth;
|
|
HitRow = (Controller.MouseY - ClientBounds[1]) / ItemHeight;
|
|
|
|
i = Top + HitCol + (HitRow * NoVisibleCols);
|
|
if ( (i < 0 || i >= ItemCount) && bRequireValidIndex )
|
|
return -1;
|
|
|
|
return i;
|
|
}
|
|
|
|
function bool InternalOnClick(GUIComponent Sender)
|
|
{
|
|
local int NewIndex;
|
|
|
|
if ( !IsInClientBounds() || ItemsPerPage==0 )
|
|
return false;
|
|
|
|
NewIndex = CalculateIndex();
|
|
if ( !IsValidIndex(NewIndex) )
|
|
return false;
|
|
|
|
SetIndex(NewIndex);
|
|
return true;
|
|
}
|
|
|
|
function int SetIndex(int NewIndex)
|
|
{
|
|
if (Elements[NewIndex].Locked==1)
|
|
return Index;
|
|
|
|
return super.SetIndex(NewIndex);
|
|
}
|
|
|
|
function bool Up()
|
|
{
|
|
local int TargetIndex;
|
|
if ( Index - NoVisibleCols < 0 )
|
|
return true;
|
|
|
|
TargetIndex = Index-NoVisibleCols;
|
|
if ( Elements[TargetIndex].Locked==1 )
|
|
{
|
|
TargetIndex = TargetIndex-NoVisibleCols;
|
|
if ( TargetIndex<0 || Elements[TargetIndex].Locked==1 )
|
|
return true;
|
|
}
|
|
|
|
SetIndex(TargetIndex);
|
|
|
|
if ( MyScrollBar != None )
|
|
MyScrollBar.AlignThumb();
|
|
return true;
|
|
}
|
|
|
|
function bool Down()
|
|
{
|
|
local int TargetIndex;
|
|
|
|
if ( Index + NoVisibleCols >= ItemCount )
|
|
return true;
|
|
|
|
TargetIndex = Index + NoVisibleCols;
|
|
if ( Elements[TargetIndex].Locked==1 )
|
|
{
|
|
TargetIndex = TargetIndex + NoVisibleCols;
|
|
if ( TargetIndex >= ItemCount || Elements[TargetIndex].Locked==1 )
|
|
return true;
|
|
}
|
|
|
|
SetIndex(TargetIndex);
|
|
if ( MyScrollBar != None )
|
|
MyScrollBar.AlignThumb();
|
|
|
|
return true;
|
|
|
|
}
|
|
function bool MoveRight()
|
|
{
|
|
local int TargetIndex;
|
|
|
|
|
|
TargetIndex = ( Index + 1 );
|
|
if ( TargetIndex % NoVisibleCols == 0 )
|
|
return true;
|
|
|
|
if (Elements[TargetIndex].Locked==1)
|
|
{
|
|
TargetIndex++;
|
|
if ( (TargetIndex % NoVisibleCols == 0 ) || (Elements[TargetIndex].Locked==1) )
|
|
return true;
|
|
}
|
|
|
|
if ( TargetIndex < ItemCount )
|
|
SetIndex(TargetIndex);
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
function bool MoveLeft()
|
|
{
|
|
local int TargetIndex;
|
|
|
|
if ( Index % NoVisibleCols == 0 )
|
|
return true;
|
|
|
|
TargetIndex = Index - 1;
|
|
if (Elements[TargetIndex].Locked==1)
|
|
{
|
|
TargetIndex--;
|
|
if (TargetIndex<0 || TargetIndex%NoVisibleCols==NoVisibleCols-1 || Elements[TargetIndex].Locked==1)
|
|
return true;
|
|
}
|
|
if ( Index > 0 )
|
|
SetIndex(TargetIndex);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
function Home()
|
|
{
|
|
local int i;
|
|
if ( ItemCount < 2 )
|
|
return;
|
|
|
|
for (i=0;i<Index;i++)
|
|
if (Elements[i].Locked!=1)
|
|
{
|
|
SetIndex(i);
|
|
if ( MyScrollBar != None )
|
|
MyScrollBar.AlignThumb();
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
function End()
|
|
{
|
|
local int i;
|
|
if ( ItemCount < 2 )
|
|
return;
|
|
|
|
for (i=ItemCount-1;i>0;i--)
|
|
if (Elements[i].Locked!=1)
|
|
{
|
|
SetIndex( i );
|
|
if ( MyScrollBar != None )
|
|
MyScrollBar.AlignThumb();
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
function PgUp()
|
|
{
|
|
local int newtop;
|
|
if (Top > 0)
|
|
{
|
|
NewTop = max(0,Top-NoVisibleRows);
|
|
SetTopItem(NewTop);
|
|
if ( MyScrollBar != None )
|
|
MyScrollBar.AlignThumb();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
function PgDn()
|
|
{
|
|
local int newtop;
|
|
if ( Top < (ItemCount / NoVisibleCols) - NoVisibleRows)
|
|
{
|
|
NewTop = Max(0, Top+NoVisibleRows);
|
|
SetTopItem(NewTop);
|
|
|
|
if ( MyScrollBar != None )
|
|
MyScrollBar.AlignThumb();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
function MakeVisible(float Perc)
|
|
{
|
|
local float MaxTop, ModResult;
|
|
local int NewTop, Change;
|
|
|
|
MaxTop = ItemCount - ItemsPerPage;
|
|
ModResult = MaxTop % NoVisibleCols;
|
|
if ( ModResult > 0 )
|
|
MaxTop = (MaxTop - ModResult) + NoVisibleCols;
|
|
|
|
// NewItem is number of hidden items * Perc we want to show
|
|
// Round off result before adjusting top to prevent constant flipping back and forth between two items
|
|
// or if we're attempting to view the last row
|
|
NewTop = Round(MaxTop * Perc);
|
|
Change = Abs(Top - NewTop);
|
|
if ( Change < NoVisibleCols && Perc < 1.0)
|
|
return;
|
|
|
|
SetTopItem(NewTop);
|
|
}
|
|
|
|
function SetTopItem(int Item)
|
|
{
|
|
local int ModResult;
|
|
|
|
// clamp new top to prevent displaying space below last items of list
|
|
Item = Clamp(Item, 0, ItemCount - 1);
|
|
|
|
// Adjust the result to fall on the first column - otherwise, elements might get shifted to a different column
|
|
ModResult = Item % NoVisibleCols;
|
|
if ( ModResult > 0 )
|
|
{
|
|
// If we had leftover items, we'll need another row
|
|
if ( Item > Top )
|
|
Item += NoVisibleCols;
|
|
|
|
Item -= ModResult;
|
|
}
|
|
|
|
// But if we went too far (more than itemcount + numcolumns), then back it up
|
|
while ( (Item + ItemsPerPage > ItemCount + NoVisibleCols) && Item >= 0)
|
|
Item -= NoVisibleCols;
|
|
|
|
Top = Max( 0, Item );
|
|
OnAdjustTop(Self);
|
|
}
|
|
|
|
function Add(Material Image, optional int Item, optional int Locked)
|
|
{
|
|
local int i;
|
|
|
|
if ( Image == None && !bAllowEmptyItems )
|
|
return;
|
|
|
|
i = Elements.Length;
|
|
Elements.Length = i + 1;
|
|
Elements[i].Image = Image;
|
|
Elements[i].Item = Item;
|
|
Elements[i].Locked = Locked;
|
|
ItemCount = Elements.Length;
|
|
|
|
if (ItemCount == 1 && bInitializeList)
|
|
SetIndex(0);
|
|
else CheckLinkedObjects(Self);
|
|
|
|
if (MyScrollBar != None)
|
|
MyScrollBar.AlignThumb();
|
|
}
|
|
|
|
function AddImage( ImageListElem NewElem, optional int Locked)
|
|
{
|
|
Add(NewElem.Image, NewElem.Item, Locked);
|
|
}
|
|
|
|
function Replace(int i, Material NewImage, optional int NewItem, optional int Locked)
|
|
{
|
|
if ( !IsValidIndex(i) )
|
|
Add(NewImage, NewItem);
|
|
else
|
|
{
|
|
if ( !bAllowEmptyItems && NewImage == None )
|
|
return;
|
|
|
|
Elements[i].Item = NewItem;
|
|
Elements[i].Image = NewImage;
|
|
Elements[i].Locked = Locked;
|
|
SetIndex(Index);
|
|
if ( MyScrollBar != None )
|
|
MyScrollBar.AlignThumb();
|
|
}
|
|
}
|
|
|
|
function Insert(int i, Material NewImage, optional int NewItem, optional int Locked)
|
|
{
|
|
if ( !IsValidIndex(i) )
|
|
Add(NewImage,NewItem);
|
|
else
|
|
{
|
|
if ( !bAllowEmptyItems && NewImage == None )
|
|
return;
|
|
|
|
Elements.Insert(i,1);
|
|
Elements[i].Item=NewItem;
|
|
Elements[i].Image = NewImage;
|
|
Elements[i].Locked = Locked;
|
|
ItemCount = Elements.Length;
|
|
|
|
SetIndex(Index);
|
|
|
|
if (MyScrollBar != None)
|
|
MyScrollBar.AlignThumb();
|
|
}
|
|
}
|
|
|
|
function InsertElement( int i, ImageListElem NewElem, optional int Locked )
|
|
{
|
|
Insert( i, NewElem.Image, NewElem.Item, Locked );
|
|
}
|
|
|
|
event Swap(int IndexA, int IndexB)
|
|
{
|
|
local ImageListElem elem;
|
|
|
|
if ( IsValidIndex(IndexA) && IsValidIndex(IndexB) )
|
|
{
|
|
elem = Elements[IndexA];
|
|
Elements[IndexA] = Elements[IndexB];
|
|
Elements[IndexB] = elem;
|
|
}
|
|
}
|
|
|
|
function LoadFrom(GUIVertImageList Source, optional bool bClearFirst)
|
|
{
|
|
local int i, item;
|
|
local Material mat;
|
|
local int l;
|
|
|
|
if (bClearfirst)
|
|
Clear();
|
|
|
|
for ( i = 0; i < Source.ItemCount; i++ )
|
|
{
|
|
Source.GetAtIndex(i,mat,item,l);
|
|
Add(mat,item,l);
|
|
}
|
|
}
|
|
|
|
function int Remove(int i, optional int Count)
|
|
{
|
|
Count = Max( Count, 1 );
|
|
if (!IsValidIndex(i))
|
|
return Index;
|
|
|
|
Elements.Remove(i, Min(Count, Elements.Length - i));
|
|
ItemCount = Elements.Length;
|
|
|
|
// In case we now have an invalid index
|
|
SetIndex(Index);
|
|
if (MyScrollBar != None)
|
|
MyScrollBar.AlignThumb();
|
|
|
|
return Index;
|
|
}
|
|
|
|
function int RemoveSilent(int i, optional int Count)
|
|
{
|
|
bNotify = False;
|
|
i = Remove(i, Count);
|
|
bNotify = True;
|
|
return i;
|
|
}
|
|
|
|
function int RemoveElement(ImageListElem Elem, optional int Count)
|
|
{
|
|
local int i;
|
|
|
|
Count = Max( Count, 1 );
|
|
for ( i = 0; i < Elements.Length; i++ )
|
|
{
|
|
if ( Elements[i] == Elem )
|
|
{
|
|
Elements.Remove(i, Min(Count, Elements.Length - i));
|
|
break;
|
|
}
|
|
}
|
|
|
|
ItemCount = Elements.Length;
|
|
|
|
// In case we now have an invalid index
|
|
SetIndex(Index);
|
|
|
|
if ( MyScrollBar != None )
|
|
MyScrollBar.AlignThumb();
|
|
|
|
return Index;
|
|
}
|
|
|
|
function Clear()
|
|
{
|
|
if (ItemCount == 0)
|
|
return;
|
|
|
|
Elements.Remove(0,Elements.Length);
|
|
Super.Clear();
|
|
}
|
|
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
// Query Functions
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
|
|
function Material Get(optional bool bGuarantee)
|
|
{
|
|
if ( !IsValid() )
|
|
{
|
|
if (bGuarantee && ItemCount > 0)
|
|
return Elements[0].Image;
|
|
|
|
return None;
|
|
}
|
|
|
|
return Elements[Index].Image;
|
|
}
|
|
|
|
function int GetItem()
|
|
{
|
|
if ( !IsValid() )
|
|
return -1;
|
|
|
|
return Elements[Index].Item;
|
|
}
|
|
|
|
// Arbitrary list Item
|
|
function Material GetImageAtIndex(int i)
|
|
{
|
|
if (!IsValidIndex(i))
|
|
return None;
|
|
|
|
return Elements[i].Image;
|
|
}
|
|
|
|
function string GetItemAtIndex(int i)
|
|
{
|
|
return string(GetItemIntAtIndex(i));
|
|
}
|
|
|
|
function int GetItemIntAtIndex(int i)
|
|
{
|
|
if (!IsValidIndex(i))
|
|
return -1;
|
|
|
|
return Elements[i].Item;
|
|
}
|
|
|
|
function GetAtIndex(int i, out Material Image, out int Item, out int Locked)
|
|
{
|
|
if (!IsValidIndex(i))
|
|
return;
|
|
|
|
Image = Elements[i].Image;
|
|
Item = Elements[i].Item;
|
|
Locked = Elements[i].Locked;
|
|
}
|
|
|
|
function bool IndexLocked(int i)
|
|
{
|
|
return Elements[i].Locked==1;
|
|
}
|
|
|
|
function bool IsLocked()
|
|
{
|
|
return Elements[Index].Locked==1;
|
|
}
|
|
|
|
|
|
function ClearPendingElements()
|
|
{
|
|
Super.ClearPendingElements();
|
|
SelectedElements.Remove(0, SelectedElements.Length);
|
|
}
|
|
|
|
function array<ImageListElem> GetPendingElements(optional bool bGuarantee)
|
|
{
|
|
local int i;
|
|
|
|
if ( (DropState == DRP_Source && Controller.DropSource == Self) || bGuarantee )
|
|
{
|
|
if ( SelectedElements.Length == 0 )
|
|
{
|
|
for (i = 0; i < SelectedItems.Length; i++)
|
|
if (IsValidIndex(SelectedItems[i]))
|
|
SelectedElements[SelectedElements.Length] = Elements[SelectedItems[i]];
|
|
|
|
if ( SelectedElements.Length == 0 && IsValid() )
|
|
{
|
|
SelectedElements.Length = SelectedElements.Length + 1;
|
|
GetAtIndex(Index, SelectedElements[0].Image, SelectedElements[0].Item, SelectedElements[0].Locked);
|
|
}
|
|
}
|
|
|
|
return SelectedElements;
|
|
}
|
|
}
|
|
|
|
//##############################################################################
|
|
//
|
|
// Assignment functions
|
|
//
|
|
|
|
function SetImageAtIndex(int i, Material NewImage)
|
|
{
|
|
if ( !IsValidIndex(i) )
|
|
return;
|
|
|
|
Elements[i].Image = NewImage;
|
|
}
|
|
|
|
function SetItemAtIndex(int i, int NewItem)
|
|
{
|
|
if (!IsValidIndex(i))
|
|
return;
|
|
|
|
Elements[i].Item = NewItem;
|
|
}
|
|
|
|
function RemoveImage( Material Image )
|
|
{
|
|
local int i;
|
|
|
|
i = FindImage(Image);
|
|
if ( IsValidIndex(i) )
|
|
Remove(i);
|
|
}
|
|
|
|
function RemoveItem(int Item)
|
|
{
|
|
local int i;
|
|
|
|
for ( i = Elements.Length -1; i >= 0; i-- )
|
|
if ( Elements[i].Item == Item )
|
|
Remove(i);
|
|
|
|
ItemCount = Elements.Length;
|
|
SetIndex(Index);
|
|
if (MyScrollBar != None)
|
|
MyScrollBar.AlignThumb();
|
|
}
|
|
|
|
|
|
//##############################################################################
|
|
//
|
|
// Search functions
|
|
//
|
|
|
|
function int FindImage( Material Image )
|
|
{
|
|
local int i;
|
|
|
|
|
|
if ( Image == None && !bAllowEmptyItems )
|
|
return -1;
|
|
|
|
for ( i = 0; i < Elements.Length; i++ )
|
|
if ( Elements[i].Image == Image )
|
|
return i;
|
|
|
|
return -1;
|
|
}
|
|
|
|
function int FindItem( int Item )
|
|
{
|
|
local int i;
|
|
|
|
for ( i = 0; i < Elements.Length; i++ )
|
|
if ( Elements[i].Item == Item )
|
|
return i;
|
|
|
|
return -1;
|
|
}
|
|
|
|
// Called on the drop source when when an Item has been dropped. bAccepted tells it whether
|
|
// the operation was successful or not.
|
|
function InternalOnEndDrag(GUIComponent Accepting, bool bAccepted)
|
|
{
|
|
local int i;
|
|
|
|
// log(Name@"InternalOnEndDrag Accepting:"$Accepting@"bAccepted:"$bAccepted);
|
|
if (bAccepted && Accepting != None)
|
|
{
|
|
GetPendingElements();
|
|
if ( Accepting != Self )
|
|
{
|
|
for ( i = 0; i < SelectedElements.Length; i++ )
|
|
RemoveElement(SelectedElements[i]);
|
|
}
|
|
|
|
bRepeatClick = False;
|
|
}
|
|
|
|
// Simulate repeat click if the operation was a failure to prevent InternalOnMouseRelease from clearing
|
|
// the SelectedItems array
|
|
// This way we don't lose the items we clicked on
|
|
if (Accepting == None)
|
|
bRepeatClick = True;
|
|
|
|
SetOutlineAlpha(255);
|
|
if ( bNotify )
|
|
CheckLinkedObjects(Self);
|
|
}
|
|
|
|
// Called on the drop target when the mouse is released - Sender is always DropTarget
|
|
function bool InternalOnDragDrop(GUIComponent Sender)
|
|
{
|
|
local array<ImageListElem> NewItem;
|
|
local int i;
|
|
// log(Name@"InternalOnDragDrop Sender:"$ Sender);
|
|
|
|
if (Controller.DropTarget == Self)
|
|
{
|
|
if (Controller.DropSource != None && GUIVertImageList(Controller.DropSource) != None)
|
|
{
|
|
NewItem = GUIVertImageList(Controller.DropSource).GetPendingElements();
|
|
|
|
// Special case for drag-n-drop between the same list.
|
|
if ( Controller.DropSource == Self )
|
|
{
|
|
for ( i = NewItem.Length - 1; i >= 0; i-- )
|
|
RemoveElement(NewItem[i]);
|
|
}
|
|
|
|
if ( !IsValidIndex(DropIndex) )
|
|
DropIndex = ItemCount;
|
|
|
|
for (i = NewItem.Length - 1; i >= 0; i--)
|
|
Insert(DropIndex, NewItem[i].Image, NewItem[i].Item);
|
|
|
|
SetIndex(DropIndex);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
defaultproperties
|
|
{
|
|
OnClick=InternalOnClick
|
|
CellStyle=Cell_FixedSize
|
|
ImageScale=1.0
|
|
StyleName=""
|
|
HorzBorder=5
|
|
VertBorder=5
|
|
NoVisibleRows=4
|
|
NoVisibleCols=3
|
|
}
|