// ==================================================================== // 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 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 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 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 GetPendingItems(optional bool bGuarantee) { local int i; local array 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" }