// Columned list box (only for text lines). Class KFGUI_ColumnList extends KFGUI_List; struct FColumnItem { var() string Text; var() float Width; var transient bool bHidden; var transient int X,XSize; }; var() array<FColumnItem> Columns; var() class<KFGUI_ListItem> ListItemClass; var() int FontSize; var() color FocusedLineColor,SelectedLineColor; var KFGUI_ColumnTop ColumnComp; var Canvas.FontRenderInfo LineFontInfo; var int SelectedRowIndex; var int LastSortedColumn; var transient float TextHeight,ScalerSize,TextScaler; var transient int OldItemsPerFrame; var KFGUI_ListItem FirstItem,UnusedItem; var transient bool bListSizeDirty; var bool bLastSortedReverse; var() bool bShouldSortList; // Should sort any new items added to the list instantly. var() bool bCanSortColumn; // Allow user to sort columns. delegate OnSelectedRow( KFGUI_ListItem Item, int Row, bool bRight, bool bDblClick ); function KFGUI_ListItem AddLine( string Value, optional int iValue, optional string SortValue, optional int Index=-1 ) { local KFGUI_ListItem N,O; local int i; // Allocate list item object. if( UnusedItem!=None ) { N = UnusedItem; UnusedItem = N.Next; N.Next = None; } else N = new (None) ListItemClass; // Setup column text value. N.SetValue(Value,iValue,SortValue); // Insert into list. if( bShouldSortList && Index==-1 ) { N.Temp = N.GetSortStr(LastSortedColumn); if( ListCount==0 ) // No sorting needed yet. { N.Next = FirstItem; FirstItem = N; } else if( bLastSortedReverse ) { if( FirstItem.Temp<N.Temp ) { N.Next = FirstItem; FirstItem = N; } else { for( O=FirstItem; O!=None; O=O.Next ) { if( O.Next==None || O.Next.Temp<N.Temp ) { N.Next = O.Next; O.Next = N; break; } } } } else if( FirstItem.Temp>N.Temp ) { N.Next = FirstItem; FirstItem = N; } else { for( O=FirstItem; O!=None; O=O.Next ) { if( O.Next==None || O.Next.Temp>N.Temp ) { N.Next = O.Next; O.Next = N; break; } } } } else if( Index==-1 || Index>ListCount ) Index = ListCount; if( Index==0 ) { N.Next = FirstItem; FirstItem = N; } else { i = 0; for( O=FirstItem; O!=None; O=O.Next ) { if( (++i)==Index ) { N.Next = O.Next; O.Next = N; break; } } } UpdateListSize(); return N; } final function RemoveLine( KFGUI_ListItem I ) { local KFGUI_ListItem N; if( I.Index==-1 ) return; // Update selected row info. if( SelectedRowIndex==I.Index ) SelectedRowIndex = -1; else if( SelectedRowIndex>I.Index ) --SelectedRowIndex; // Remove from list. if( FirstItem==I ) FirstItem = I.Next; else { for( N=FirstItem; N!=None; N=N.Next ) if( N.Next==I ) { N.Next = I.Next; break; } } // Add to unused list. I.Next = UnusedItem; UnusedItem = I; I.Index = -1; UpdateListSize(); } final function EmptyList() { local KFGUI_ListItem N,I; for( I=FirstItem; I!=None; I=N ) { N = I.Next; // Add to unused list. I.Next = UnusedItem; UnusedItem = I; I.Index = -1; } FirstItem = None; UpdateListSize(); } final function KFGUI_ListItem GetFromIndex( int Index ) { local KFGUI_ListItem N; if( Index<0 || Index>=ListCount ) return None; for( N=FirstItem; N!=None; N=N.Next ) if( (Index--)==0 ) return N; return None; } function SortColumn( int Column, optional bool bReverse ) { local array<KFGUI_ListItem> List; local KFGUI_ListItem Sel,N,P; local int i; if( !bCanSortColumn || Column<0 || Column>=Columns.Length ) return; LastSortedColumn = Column; bLastSortedReverse = bReverse; bShouldSortList = true; // Allocate memory space first. List.Length = ListCount; List.Length = 0; // Grab current selected line. Sel = GetFromIndex(SelectedRowIndex); SelectedRowIndex = -1; // Slow, sort it all. for( N=FirstItem; N!=None; N=N.Next ) { N.Temp = N.GetSortStr(Column); if( bReverse ) { for( i=0; i<List.Length; ++i ) if( List[i].Temp<N.Temp ) break; } else { for( i=0; i<List.Length; ++i ) if( List[i].Temp>N.Temp ) break; } List.Insert(i,1); List[i] = N; } // Rebuild list. FirstItem = None; P = None; for( i=0; i<List.Length; ++i ) { N = List[i]; if( Sel==N ) SelectedRowIndex = i; N.Index = i; N.Next = None; if( P==None ) FirstItem = N; else P.Next = N; P = N; } } function ChangeListSize( int NewSize ); final function UpdateListSize() { local KFGUI_ListItem N; ListCount = 0; for( N=FirstItem; N!=None; N=N.Next ) N.Index = ListCount++; bListSizeDirty = true; } function InitMenu() { ListCount = 0; Super.InitMenu(); ColumnComp = KFGUI_ColumnTop(FindComponentID('Columns')); } final function DrawStrClipped( string S ) { Canvas.PushMaskRegion(Canvas.OrgX,Canvas.OrgY,Canvas.ClipX,Canvas.ClipY); Canvas.DrawText(S,,TextScaler,TextScaler,LineFontInfo); Canvas.PopMaskRegion(); } function DrawMenu() { local int i,n,j; local float Y,TextY,YClip,XOffset; local KFGUI_ListItem C; local bool bCheckMouse; Canvas.DrawColor = BackgroundColor; Canvas.SetPos(0.f,0.f); Owner.CurrentStyle.DrawWhiteBox(CompPos[2],CompPos[3]); // Mouse focused item check. bCheckMouse = bClickable && bFocused; FocusMouseItem = -1; if( bCheckMouse ) MouseYHit = Owner.MousePosition.Y - CompPos[1]; n = ScrollBar.CurrentScroll; i = 0; for( C=FirstItem; C!=None; C=C.Next ) if( (i++)==n ) break; Y = 0.f; TextY = (ItemHeight-TextHeight)*0.5f; XOffset = TextY*0.75; YClip = CompPos[1]+CompPos[3]; Canvas.SetDrawColor(250,250,250,255); for( i=0; (i<ListItemsPerPage && C!=None); ++i ) { // Check for mouse hit. if( bCheckMouse && FocusMouseItem==-1 ) { if( MouseYHit<ItemHeight ) FocusMouseItem = n; else MouseYHit-=ItemHeight; } // Draw selection background. if( SelectedRowIndex==n ) // Selected { Canvas.SetPos(0,Y); Canvas.DrawColor = SelectedLineColor; Owner.CurrentStyle.DrawWhiteBox(CompPos[2],ItemHeight); Canvas.SetDrawColor(250,250,250,255); } else if( FocusMouseItem==n ) // Focused { Canvas.SetPos(0,Y); Canvas.DrawColor = FocusedLineColor; Owner.CurrentStyle.DrawWhiteBox(CompPos[2],ItemHeight); Canvas.SetDrawColor(250,250,250,255); } // Draw columns of text for( j=0; j<Columns.Length; ++j ) if( !Columns[j].bHidden ) { Canvas.SetClip(Columns[j].X+Columns[j].XSize,YClip); Canvas.SetPos(Columns[j].X+XOffset,TextY); DrawStrClipped(C.GetDisplayStr(j)); } Y+=ItemHeight; TextY+=ItemHeight; ++n; C = C.Next; } } function PreDraw() { local byte j; local float XS,SpaceX; ComputeCoords(); // Check font to use. Canvas.Font = Owner.CurrentStyle.PickFont(Min(FontSize+Owner.CurrentStyle.DefaultFontSize,Owner.CurrentStyle.MaxFontScale),TextScaler); Canvas.TextSize("ABC",XS,TextHeight,TextScaler,TextScaler); for( j=0; j<4; ++j ) { ScrollBar.InputPos[j] = CompPos[j]; ColumnComp.InputPos[j] = CompPos[j]; } // Setup positioning. // First compute the width scrollbar. if( OldXSize!=InputPos[2] ) { OldXSize = InputPos[2]; ScalerSize = ScrollBar.GetWidth(); ScrollBar.XPosition = 1.f - ScalerSize; ColumnComp.XSize = ScrollBar.XPosition; } SpaceX = ScalerSize*CompPos[2]; CompPos[2] -= SpaceX; ScrollBar.InputPos[3] = CompPos[3]; // Draw columns. ColumnComp.YSize = (TextHeight*1.05) / CompPos[3]; ColumnComp.Canvas = Canvas; ColumnComp.PreDraw(); // Move down to give space for columns. CompPos[1] += ColumnComp.CompPos[3]; CompPos[3] -= ColumnComp.CompPos[3]; // Compute how many rows fit in with this setting. ItemHeight = TextHeight*1.025; ListItemsPerPage = CompPos[3]/ItemHeight; ItemHeight = CompPos[3]/ListItemsPerPage; if( OldItemsPerFrame!=ListItemsPerPage || bListSizeDirty ) { if( SelectedRowIndex>=ListCount ) SelectedRowIndex = -1; OldItemsPerFrame = ListItemsPerPage; bListSizeDirty = false; UpdateListVis(); } // Draw vertical scrollbar ScrollBar.Canvas = Canvas; ScrollBar.PreDraw(); // Draw self. Canvas.SetOrigin(CompPos[0],CompPos[1]); Canvas.SetClip(CompPos[0]+CompPos[2],CompPos[1]+CompPos[3]); DrawMenu(); // Reset scaling to allow mouse to capture input. CompPos[1] -= ColumnComp.CompPos[3]; CompPos[2] += SpaceX; CompPos[3] += ColumnComp.CompPos[3]; } function InternalClickedItem( int Index, bool bRight, int MouseX, int MouseY ) { SelectedRowIndex = Index; OnSelectedRow(GetFromIndex(Index),Index,bRight,false); } function InternalDblClickedItem( int Index, bool bRight, int MouseX, int MouseY ) { SelectedRowIndex = Index; OnSelectedRow(GetFromIndex(Index),Index,bRight,true); } defaultproperties { ListItemClass=class'KFGUI_ListItem' OnClickedItem=InternalClickedItem OnDblClickedItem=InternalDblClickedItem SelectedRowIndex=-1 bClickable=true FocusedLineColor=(R=64,G=3,B=48,A=255) SelectedLineColor=(R=84,G=26,B=128,A=255) bCanSortColumn=true Begin Object Class=KFGUI_ColumnTop Name=ColumnComps XPosition=0 YPosition=0 XSize=1 YSize=0.04 ID="Columns" End Object Components.Add(ColumnComps) LineFontInfo=(bClipText=true,bEnableShadow=false) }