Rem bbdoc: noddybox.simplegui EndRem Module noddybox.simplegui ModuleInfo "Framework: (Very) Simple GUI" ModuleInfo "Copyright: Public Domain" ModuleInfo "Author: Ian Cowburn" ModuleInfo "Version: $Revision$" ' $Id$ Strict Import brl.max2d Import brl.filesystem Import brl.basic Import noddybox.bitmapfont Rem bbdoc: Defines the @TBitmapFont to be used by the GUI. EndRem Type TGUIFont Rem bbdoc: The font to use. EndRem Global font:TBitmapFont End Type Rem bbdoc: The base widget type. EndRem Type TWidget Abstract Rem bbdoc: This widget consumes click events EndRem Const HAS_CLICK:Int=1 Rem bbdoc: This widget consumes drag events EndRem Const HAS_DRAG:Int=2 Rem bbdoc: This widget consumes key events EndRem Const HAS_KEY:Int=4 Rem bbdoc: If true the widget is displayed. EndRem Field displayed:Int Rem bbdoc: If true the widget can be interacted with. EndRem Field enabled:Int Rem bbdoc: The text displayed in the widget. EndRem Field text:String Rem bbdoc: The X co-ordinate of the widget. EndRem Field x:Int Rem bbdoc: The Y co-ordinate of the widget. EndRem Field y:Int Rem bbdoc: The width of the widget. EndRem Field w:Int Rem bbdoc: The height of the widget. EndRem Field h:Int Rem bbdoc: Callback to call when clicked. EndRem Field callback(w:TWidget) Rem bbdoc: True if the mouse is over this widget, False otherwise. EndRem Field mouse_over:Int Rem bbdoc: The @TGUIHandler that owns this widget. EndRem Field owner:TGUIHandler Rem bbdoc: The mask of consumed events, zero otherwise. EndRem Field consumes:Int Rem bbdoc: Override this accept keypresses EndRem Method HandleKey(k:Int) End Method Rem bbdoc: Override this for when the mouse enters. Call this parent version to handle @mouse_over too. EndRem Method MouseEnter() If displayed And enabled mouse_over=True EndIf End Method Rem bbdoc: Override this for when the mouse leaves. Call this parent version to handle @mouse_over too. EndRem Method MouseLeave() mouse_over=False End Method Rem bbdoc: Override to handle a mouse button press. EndRem Method HandleClick() End Method Rem bbdoc: Handles mouse dragging. about: @mx and @my are the changes in mouse co-ordinates since the last drag. EndRem Method HandleDrag(mx:Int, my:Int) End Method Rem bbdoc: Must be provided by a widget to draw itself. EndRem Method Draw() Abstract Rem bbdoc: Helper to draw a wireframe rectangle. EndRem Function DrawBox(x:Int, y:Int, w:Int, h:Int) DrawLine(x,y,x+w-1,y) DrawLine(x+w-1,y,x+w-1,y+h-1) DrawLine(x+w-1,y+h-1,x,y+h-1) DrawLine(x,y+h-1,x,y) End Function Rem bbdoc: Helper to draw a 3D rectangle. EndRem Function Draw3DBox(x:Int, y:Int, w:Int, h:Int, invert:Int, size:Int=2) Local f:Int SetColor(200,200,200) DrawRect(x,y,w,h) If invert SetColor(170,170,170) Else SetColor(230,230,230) EndIf For f=0 Until size DrawLine(x+f,y+f,x+w-1-f,y+f) DrawLine(x+w-1-f,y+f,x+w-1-f,y+h-1-f) Next If invert SetColor(230,230,230) Else SetColor(170,170,170) EndIf For f=0 Until size DrawLine(x+w-1-f,y+h-1-f,x+f,y+h-1-f) DrawLine(x+f,y+h-1-f,x+f,y+f) Next End Function End Type Rem bbdoc: The panel widget (simply displays itself as a white bordered black box) EndRem Type TPanel Extends TWidget Rem bbdoc: Create a TPanel widget. returns: The created widget. about: @gui is the @TGUIHandler object managing this wiget. @x, @y, @x and @h are its position and size. If @x or @y is -1 they are centered. EndRem Function Create:TPanel(gui:TGUIHandler,x:Int, y:Int, w:Int, h:Int) Local o:TPanel=New TPanel o.displayed=True o.enabled=True o.consumes=0 If x=-1 x=(GraphicsWidth()-w)/2 EndIf If y=-1 y=(GraphicsHeight()-h)/2 EndIf o.x=x o.y=y o.w=w o.h=h gui.Register(o) Return o End Function Method Draw() SetColor(0,0,0) DrawRect(x,y,w,h) SetColor(255,255,255) DrawBox(x,y,w,h) End Method End Type Rem bbdoc: The label widget (simply displays the supplied string) EndRem Type TLabel Extends TWidget Rem bbdoc: Create a TLabel widget. returns: The created widget. about: @gui is the @TGUIHandler object managing this wiget. @x and @y are its position and @text is the text to display in the label. EndRem Function Create:TLabel(gui:TGUIHandler,x:Int, y:Int, text:String) Local o:TLabel=New TLabel o.displayed=True o.enabled=True o.consumes=0 o.x=x o.y=y o.w=TGUIFont.font.TextWidth(text)+2 o.h=TGUIFont.font.MaxHeight()+1 o.text=text gui.Register(o) Return o End Function Method Draw() TGUIFont.font.Draw(text,x+1,y+1) End Method End Type Rem bbdoc: The text entry widget EndRem Type TText Extends TWidget Field maxlen:Int Field mode:Int Rem bbdoc: The text box handles any text EndRem Const NORMAL:Int=0 Rem bbdoc: The text box handles a number EndRem Const NUMERIC:Int=1 Rem bbdoc: The text box handles only integer numbers EndRem Const INTEGER:Int=2 Rem bbdoc: The text box handles only positive numbers EndRem Const POSITIVE:Int=4 Rem bbdoc: The text box handles a basic set of filename characters (alphanumerics, periods, spaces and underscores) EndRem Const FILENAME:Int=8 Rem bbdoc: The text box is readonly EndRem Const READONLY:Int=256 Rem bbdoc: Create a TText widget. returns: The created widget. about: @gui is the @TGUIHandler object managing this wiget. @x and @y are its position and @text is the initial text in the widget. about: @maxlen is the maximum number of characters allowed. @mode defines the valid characters that can be entered. about: @callback is called when RETURN is pressed in the text field. EndRem Function Create:TText(gui:TGUIHandler,x:Int, y:Int, text:String, maxlen:Int, mode:Int=0, callback(w:TWidget)=Null) Local o:TText=New TText o.displayed=True o.enabled=True o.consumes=HAS_CLICK|HAS_KEY o.x=x o.y=y o.maxlen=maxlen o.w=TGUIFont.font.MaxWidth()*(maxlen+1)+2 o.h=TGUIFont.font.MaxHeight()+2 o.text=text o.callback=callback o.mode=mode gui.Register(o) Return o End Function Rem bbdoc: Set the text to a floating point value. about: @v is the floating point value (a double to cover both types). This value will be placed in the text about: field with trailing zeros (and trailing dot if an integer) removed. EndRem Method SetFloat(v:Double) Local s:String=v If s.Find(".")<>-1 While s.length>1 And s[s.length-1]=Asc("0") s=s[..s.length-1] Wend While s.length>1 And s[s.length-1]=Asc(".") s=s[..s.length-1] Wend EndIf text=s End Method Method HandleClick() If Not(mode & READONLY) owner.SetFocus(Self) EndIf End Method Method HandleKey(k:Int) If (mode & READONLY) Return EndIf If k=27 text="" Else If k=8 If text.length>0 text=text[0..text.length-1] EndIf Else If k=13 If callback<>Null callback(Self) owner.SetFocus(Null) EndIf Else If k>31 And k<127 And text.length=Asc("0") And k<=Asc("9") text:+Chr(k) EndIf If k=Asc(".") And (Not (mode & INTEGER)) And text.Find(".")=-1 text:+Chr(k) EndIf Else If (mode & FILENAME) If (k>=Asc("a") And k<=Asc("z")) Or (k>=Asc("A") And k<=Asc("Z")) Or (k>=Asc("0") And k<=Asc("9")) Or k=Asc(" ") Or k=Asc(".") Or k=Asc("_") text:+Chr(k) EndIf Else text:+Chr(k) EndIf EndIf EndIf End Method Method Draw() Local s:String=text If owner.GetFocus()=Self And Not (mode & READONLY) SetColor(128,128,128) s:+"_" Else SetColor(64,64,64) EndIf DrawRect(x,y,w,h) TGUIFont.font.Draw(s,x+1,y+1) End Method End Type Rem bbdoc: The checkbox widget EndRem Type TCheckbox Extends TWidget Rem bbdoc: True if the box is checked, False otherwise. EndRem Field checked:Int Rem bbdoc: Create a TCheckox widget. returns: The created widget. about: @gui is the @TGUIHandler object managing this wiget. @x and @y are its position and @text is the text to display by the checkbox. about: @callback is called when the checkbox is toggled. EndRem Function Create:TCheckbox(gui:TGUIHandler, x:Int, y:Int, text:String, callback(w:TWidget)=Null) Local o:TCheckbox=New TCheckbox o.displayed=True o.enabled=True o.consumes=HAS_CLICK o.x=x o.y=y o.w=TGUIFont.font.TextWidth(" "+text)+4+TGUIFont.font.MaxHeight() o.h=TGUIFont.font.MaxHeight()+2 o.text=text o.callback=callback gui.Register(o) Return o End Function Method HandleClick() checked=Not checked If callback<>Null callback(Self) EndIf End Method Method Draw() If (mouse_over) SetColor(255,255,255) Else SetColor(200,200,200) EndIf DrawBox(x,y,TGUIFont.font.MaxHeight(),TGUIFont.font.MaxHeight()) If checked SetColor(255,100,100) DrawRect(x+1,y+1,TGUIFont.font.MaxHeight()-2,TGUIFont.font.MaxHeight()-2) EndIf TGUIFont.font.Draw(" "+text,x+2+TGUIFont.font.MaxHeight(),y+1) End Method End Type Rem bbdoc: The button widget EndRem Type TButton Extends TWidget Field ox:Int Field oy:Int Rem bbdoc: Create a TButton widget. returns: The created widget. about: @gui is the @TGUIHandler object managing this wiget. @x, @y, @w and @h are its position and size, and @text is the text to display in the button. about: @callback is called when the button is pressed. EndRem Function Create:TButton(gui:TGUIHandler, x:Int, y:Int, w:Int, h:Int, text:String, callback(w:TWidget)) Local o:TButton=New TButton o.displayed=True o.enabled=True o.consumes=HAS_CLICK o.x=x o.y=y o.w=w o.h=h o.text=text o.ox=x+w/2-TGUIFont.font.TextWidth(text)/2 o.oy=y+h/2-TGUIFont.font.TextHeight(text)/2 o.callback=callback gui.Register(o) Return o End Function Method HandleClick() If callback<>Null callback(Self) EndIf End Method Method Draw() If (mouse_over) SetColor(255,255,255) DrawBox(x,y,w,h) EndIf Draw3DBox(x+1,y+1,w-2,h-2,False,2) TGUIFont.font.Draw(text,ox,oy) End Method End Type Rem bbdoc: A drop down list type widget EndRem Type TButtonList Extends TWidget Field options:String[] Rem bbdoc: The selected item. EndRem Field selected:Int Rem bbdoc: Create a TButtonList widget. returns: The created widget. about: @gui is the @TGUIHandler object managing this wiget. @x and @y are its position. @options are the options for the list and about: @selected is the currently selected item. @callback is called when the selection changes. EndRem Function Create:TButtonList(gui:TGUIHandler, x:Int, y:Int, options:String[], selected:Int, callback(w:TWidget)) Local o:TButtonList=New TButtonList Local maxw:Int=0 o.displayed=True o.enabled=True o.consumes=HAS_CLICK o.x=x o.y=y o.options=options o.selected=selected o.text=options[selected] For Local s:String=EachIn options maxw=Max(maxw,TGUIFont.font.TextWidth(s)) Next o.w=maxw+TGUIFont.font.MaxHeight()+4 o.h=TGUIFont.font.MaxHeight()+2 o.callback=callback gui.Register(o) Return o End Function Method HandleClick() Local sel:Int=GUIMenu("Select value",options,x,y) If sel<>-1 selected=sel text=options[selected] If callback<>Null callback(Self) EndIf EndIf End Method Method Draw() If (mouse_over) SetColor(128,128,128) Else SetColor(64,64,64) EndIf DrawRect(x,y,w,h) Draw3DBox(x+2,y+2,h-4,h-4,False,1) TGUIFont.font.Draw(text,x+TGUIFont.font.MaxHeight()+3,y+1) End Method End Type Rem bbdoc: Optional type to use for TTextList about: If the objects stored as TTextList options are derived from this type then tabular lists can be created. EndRem Type TTextListItem Abstract Rem bbdoc: The number of columns. returns: The number of columns EndRem Method Columns:Int() Abstract Rem bbdoc: A column offset. returns: The column offset for column @i (numbered from zero). EndRem Method ColumnOffset:Int(i:Int) Abstract Rem bbdoc: Column data. returns: The column data for column @i (numbered from zero). EndRem Method ColumnData:String(i:Int) Abstract End Type Rem bbdoc: A widget that displays text items as a list. EndRem Type TTextList Extends TWidget Field options:Object[] Field selected:Int Field num:Int Field top:Int Field rows:Int Rem bbdoc: Create a TTextList widget. returns: The created widget. about: @gui is the @TGUIHandler object managing this wiget. @x, @y, and @w are its position and width. about: @rows are the number of items to display at a time. about: @callback is called when the selection changes. EndRem Function Create:TTextList(gui:TGUIHandler, x:Int, y:Int, w:Int, rows:Int, callback(w:TWidget)=Null) Local o:TTextList=New TTextList Local maxw:Int=0 o.displayed=True o.enabled=True o.consumes=HAS_CLICK o.x=x o.y=y o.options=Null o.selected=-1 o.num=0 o.top=0 o.rows=rows o.w=w o.h=TGUIFont.font.MaxHeight()*rows o.callback=callback gui.Register(o) Return o End Function Rem bbdoc: Sets the options to display in the list. about: @opts is an array of objects. Note: If the objects passed are derived from TTextListItem then tabular output is generated. about: If they are not derived, ToString() is called on the objects to display them, so you are not restricted to the type of object that about: can be passed. EndRem Method SetOptions(opts:Object[]) options=opts num=opts.length selected=-1 top=0 End Method Rem bbdoc: Gets the selected item's index. returns: The selected item's index, or -1 if nothing is selected. EndRem Method GetSelectedIndex:Int() Return selected End Method Rem bbdoc: Gets the selected item. returns: The selected item, or null if nothing is selected. EndRem Method GetSelectedItem:Object() If options<>Null And selected<>-1 Return options[selected] Else Return Null EndIf End Method Rem bbdoc: Set the top row. about: @i is the index of the item to display in the first row of the widget. EndRem Method SetTopRow(i:Int) If options<>Null And iNull Return top Else Return -1 EndIf End Method Rem bbdoc: Ensure an item is visible. about: @i is the index of the item that is to be made visible. EndRem Method EnsureVisible(i:Int) If options<>Null And i=top+rows top=Max(0,i-rows+1) EndIf EndIf End Method Method HandleClick() If options<>Null Local sel:Int=top+(MouseY()-y)/TGUIFont.font.MaxHeight() If selNull callback(Self) EndIf EndIf EndIf End Method Method Draw() SetColor(64,64,64) DrawRect(x,y,w,h) If options<>Null Local vx:Int Local vy:Int Local vw:Int Local vh:Int GetViewport(vx,vy,vw,vh) Local ty:Int=y For Local f:Int=top Until top+rows SetViewport(x,ty,w,TGUIFont.font.MaxHeight()) If f=selected If mouse_over And MouseY()>=ty And MouseY()=ty And MouseY()Null For Local f:Int=0 Until li.Columns() TGUIFont.font.Draw(li.ColumnData(f),x+li.ColumnOffset(f),ty) Next Else TGUIFont.font.Draw(options[f].ToString(),x,ty) EndIf EndIf ty:+TGUIFont.font.MaxHeight() Next SetViewport(vx,vy,vw,vh) EndIf End Method End Type Rem bbdoc: A number up/down widget for doubles EndRem Type TNumberDouble Extends TWidget Rem bbdoc: The value EndRem Field value:Double Rem bbdoc: The increment/decrement EndRem Field change:Double Rem bbdoc: The maximum value EndRem Field maxval:Double Rem bbdoc: The minimum value EndRem Field minval:Double Rem bbdoc: Create a TNumber widget. returns: The created widget. about: @gui is the @TGUIHandler object managing this wiget. @x and @y are its position. about: @callback is called when the value changes. EndRem Function Create:TNumberDouble(gui:TGUIHandler, x:Int, y:Int, callback(w:TWidget)=Null) Local o:TNumberDouble=New TNumberDouble o.displayed=True o.enabled=True o.consumes=HAS_CLICK o.value=0 o.minval=0 o.maxval=100 o.change=10 o.x=x o.y=y o.w=TGUIFont.font.MaxHeight()*2+8 o.h=TGUIFont.font.MaxHeight()+2 o.callback=callback gui.Register(o) Return o End Function Method HandleClick() Local old:Double=value If MouseX()old And callback<>Null callback(Self) EndIf End Method Method Draw() If mouse_over If MouseX()old And callback<>Null callback(Self) EndIf End Method Method Draw() If mouse_over If MouseX()h) o.SetBar(0,100,10) o.callback=callback gui.Register(o) Return o End Function Rem bbdoc: Set parameters for scroll bar about: @min_val and @max_val are the values at the top and bottom of the scroll bar. @page_size is the numbers between the two that about: is covered by the block in the scrollbar. Note: The maximum value of @value is @max_val minus @page_size. EndRem Method SetBar(min_val:Double, max_val:Double, page_size:Double) minval=min_val maxval=max_val page=page_size If horiz stepsize=(maxval-minval)/w blocksize=Min(w,Max(1,page/stepsize)) bc=x Else stepsize=(maxval-minval)/h blocksize=Min(h,Max(1,page/stepsize)) bc=y EndIf End Method Rem bbdoc: Get the current value of the scrollbar. returns: The current position of the scrollbar inbetween the minimum and maximum values. EndRem Method GetValue:Double() If horiz Return minval+(bc-x)*stepsize Else Return minval+(bc-y)*stepsize EndIf End Method Rem bbdoc: Sets the current value of the scrollbar. EndRem Method SetValue(val:Double) If horiz bc=Max(x,Min(x+w-blocksize,x+(val-minval)/stepsize)) Else bc=Max(y,Min(y+h-blocksize,y+(val-minval)/stepsize)) EndIf End Method Method HandleClick() Local old:Double=GetValue() If horiz bc=Max(x,Min(x+w-blocksize,MouseX())) Else bc=Max(y,Min(y+h-blocksize,MouseY())) EndIf If GetValue()<>old And callback<>Null callback(Self) EndIf End Method Method HandleDrag(mx:Int, my:Int) Local old:Double=GetValue() If horiz bc=Max(x,Min(x+w-blocksize,bc+mx)) Else bc=Max(y,Min(y+h-blocksize,bc+my)) EndIf If GetValue()<>old And callback<>Null callback(Self) EndIf End Method Method Draw() SetColor(64,64,64) DrawRect(x,y,w,h) If horiz Draw3DBox(bc,y,blocksize,h,False,1) Else Draw3DBox(x,bc,w,blocksize,False,1) EndIf End Method End Type Rem bbdoc: Handles the GUI. EndRem Type TGUIHandler ' These are private ' Field m_widgets:TList Field m_focus:TWidget Field m_over:TWidget Field m_clicked:TWidget Field m_dragging:Int Field m_dragx:Int Field m_dragy:Int Field m_dragobj:TWidget Rem bbdoc: Create a GUI Handler. returns: The GUI Hanbler. EndRem Function Create:TGUIHandler() Local o:TGUIHandler o=New TGUIHandler o.m_widgets=CreateList() o.m_focus=Null o.m_over=Null o.m_clicked=Null o.m_dragging=False o.m_dragx=0 o.m_dragy=0 o.m_dragobj=Null Return o End Function Rem bbdoc: Register a widget. about: Widgets in the library call this themselves when creating. EndRem Method Register(w:TWidget) m_widgets.AddLast(w) w.owner=Self End Method Rem bbdoc: Remove all widgets. EndRem Method Clear() m_widgets.Clear() End Method Rem bbdoc: Sets the enable state of all widgets. about: If @state is true then all widgets are enabled, otherwise all widgets are disabled. EndRem Method SetEnable(state:Int) For Local w:TWidget=EachIn m_widgets w.enabled=state Next End Method Rem bbdoc: Sets the displayed state of all widgets. about: If @state is true then all widgets are displayed, otherwise all widgets are hidden. EndRem Method SetDisplay(state:Int) For Local w:TWidget=EachIn m_widgets w.displayed=state Next End Method Rem bbdoc: Sets the keyboard focus to the supplied widget. Pass null for no focus. EndRem Method SetFocus(w:TWidget) m_focus=w End Method Rem bbdoc: Gets the keyboard focus. returns: The currently focused TWidget, or null for no focus. EndRem Method GetFocus:TWidget() Return m_focus End Method Rem bbdoc: Gets the last clicked widget. returns: The last clicked TWidget. Null is returned if this is called again before another widget is clicked. EndRem Method Clicked:TWidget() Local last:TWidget=m_clicked m_clicked=Null Return last End Method Rem bbdoc: Whether a widget is dragging returns: True if a widget is consuming a mouse drag. EndRem Method IsDragging:Int() Return m_dragging End Method Rem bbdoc: Perform the event loop and pass any necessary events. about: KeyHit(KEY_MOUSELEFT) and GetChar() are used to consume events for the interface. EndRem Method EventLoop() Local x:Int=MouseX() Local y:Int=MouseY() Local b:Int=MouseHit(1)>0 Local drag:Int=MouseDown(1) Local w:TWidget=LocateWidget(x,y) For Local wid:TWidget=EachIn m_widgets If wid.displayed If wid.enabled wid.Draw() Else SetAlpha(0.3) wid.Draw() SetAlpha(1) EndIf EndIf Next If w<>m_over If m_over<>Null m_over.MouseLeave() EndIf m_over=w If m_over<>Null m_over.MouseEnter() EndIf EndIf If w<>Null And w.enabled And w.consumes&TWidget.HAS_CLICK=TWidget.HAS_CLICK And b And Not m_dragging w.HandleClick() m_clicked=w EndIf Local k:Int=GetChar() If k<>0 And m_focus<>Null And m_focus.enabled And m_focus.consumes&TWidget.HAS_KEY=TWidget.HAS_KEY m_focus.HandleKey(k) EndIf If drag If m_dragging m_dragobj.HandleDrag(MouseX()-m_dragx,MouseY()-m_dragy) m_dragx=MouseX() m_dragy=MouseY() Else If w<>Null And w.consumes&TWidget.HAS_DRAG=TWidget.HAS_DRAG m_dragging=True m_dragx=MouseX() m_dragy=MouseY() m_dragobj=w EndIf EndIf Else m_dragging=False m_dragobj=Null EndIf End Method ' Private method ' Method LocateWidget:TWidget(x:Int,y:Int) For Local w:TWidget=EachIn m_widgets If x>=w.x And y>=w.y And xbutton Cls SetAlpha(0.5) DrawImage(back,0,0) SetAlpha(1) TWidget.Draw3DBox(x,y,w,h,False,2) gui.EventLoop() click=gui.Clicked() If i<>Null SetColor(255,255,255) DrawImage(i,MouseX(),MouseY()) EndIf Flip Wend End Function Rem bbdoc: Displays a yes/no alert with the supplied string. returns: True if Yes selected, otherwise False. about: The string @s can be split into multiple lines using the '|' character. If @i is not null then this image is drawn as a mouse cursor. EndRem Function GUIYesNo:Int(s:String, i:TImage=Null) Local back:TImage=CreateImage(GraphicsWidth(),GraphicsHeight(),1,0) GrabImage(back,0,0) Local txt:TSplitText=TSplitText.Create(s) Local w:Int=Max(txt.width+10,GraphicsWidth()/4) Local h:Int=txt.Height+TGUIFont.font.MaxHeight()*4 Local x:Int=GraphicsWidth()/2-w/2 Local y=GraphicsHeight()/2-h/2 Local by=y+h-TGUIFont.font.MaxHeight()*2.5 Local gui:TGUIHandler=TGUIHandler.Create() Local ty:Int=y+5 For Local t:String=EachIn txt.lines Local label:TLabel=TLabel.Create(gui,x+5,ty,t) ty:+TGUIFont.font.TextHeight(t) Next Local yes:TButton=TButton.Create(gui,x+5,by,w/2-10,TGUIFont.font.MaxHeight()*2,"Yes",Null) Local no:TButton=TButton.Create(gui,x+w/2+5,by,w/2-10,TGUIFont.font.MaxHeight()*2,"No",Null) Local click:TWidget=Null While click=Null Cls SetAlpha(0.5) DrawImage(back,0,0) SetAlpha(1) TWidget.Draw3DBox(x,y,w,h,False,2) gui.EventLoop() click=gui.Clicked() If i<>Null SetColor(255,255,255) DrawImage(i,MouseX(),MouseY()) EndIf Flip Wend Return click=yes End Function Rem bbdoc: Displays a pop-up menu. returns: The index of the selected option, -1 for none. about: @title is the menu title, @options the options to display and @x and @y specify its position. about: If @i is not null then this image is drawn as a mouse cursor. EndRem Function GUIMenu(title:String, options:String[], x:Int, y:Int, i:TImage=Null) Local f:Int Local back:TImage=CreateImage(GraphicsWidth(),GraphicsHeight(),1,0) GrabImage(back,0,0) Local st:Int=TGUIFont.font.MaxHeight()*2 Local h:Int=TGUIFont.font.MaxHeight()*3 Local w:Int=TGUIFont.font.TextWidth(title)+30 For f=0 Until options.length w=Max(w,TGUIFont.font.TextWidth(options[f])+30) h:+st Next Local gui:TGUIHandler=TGUIHandler.Create() x=Max(0,Min(x,GraphicsWidth()-w)) y=Max(0,Min(y,GraphicsHeight()-h)) Local label:TLabel=TLabel.Create(gui,x+5,y+2,title) Local button:TButton[]=New TButton[options.length] For f=0 Until options.length button[f]=TButton.Create(gui,x+5,y+TGUIFont.font.MaxHeight()*2+f*st,w-10,st-2,options[f],Null) Next Local click:TWidget=Null While click=Null And MouseHit(2)=0 Cls SetAlpha(0.5) DrawImage(back,0,0) SetAlpha(1) TWidget.Draw3DBox(x,y,w,h,False,2) gui.EventLoop() click=gui.Clicked() If i<>Null SetColor(255,255,255) DrawImage(i,MouseX(),MouseY()) EndIf Flip Wend For f=0 Until options.length If button[f]=click Return f EndIf Next Return -1 End Function Rem bbdoc: Displays a basic file selector. returns: The selected file, or null for none. about: @title is the title. @file is the initially selected file, and f set to null then no file is selected and the current directory displayed. about: Set @save to true for a save dialog (the filename can be altered). about: If @i is not null then this image is drawn as a mouse cursor. EndRem Function GUIFileSelect:String(title:String, file:String, save:Int, i:TImage=Null) FlushKeys() Local oldpwd:String=CurrentDir() Local dir:String Local fn:String Local sw:Int=GraphicsWidth() Local sh:Int=GraphicsHeight() Local fh:Int=TGUIFont.font.MaxHeight() Local fw:Int=TGUIFont.font.MaxWidth() Local rows:Int=(sh-fh*10)/fh If file<>Null And file<>"" dir=ExtractDir(file) If dir="" dir=oldpwd EndIf If FileType(file)=FILETYPE_FILE fn=StripDir(file) Else fn="" EndIf Else dir=oldpwd fn="" EndIf Local gui:TGUIHandler=TGUIHandler.Create() TPanel.Create(gui,0,0,sw,sh) TLabel.Create(gui,10,fh*0.5,title) Local list:TTextList=TTextList.Create(gui,20,fh*2,sw-40,rows) Local scroll:TScrollbar=TScrollbar.Create(gui,list.x+list.w+5,list.y,10,list.h) Local fntext:TText If save fntext=TText.Create(gui,20,sh-fh*6,fn,(sw-40)/fw,TText.FILENAME) Else fntext=TText.Create(gui,20,sh-fh*6,fn,(sw-40)/fw,TText.READONLY) EndIf Local bw:Int=TGUIFont.font.MaxWidth()*9 Local cancel:TButton=TButton.Create(gui,sw-bw-20,sh-fh*3.5,bw,fh*2.5,"Cancel",Null) Local ok:Tbutton=TButton.Create(gui,cancel.x-bw-10,cancel.y,bw,fh*2.5,"OK",Null) Local last:TDirEntry=Null ChangeDir(dir) LoadDirEnts(list,scroll,rows) Repeat Cls gui.EventLoop() TGUIFont.font.Draw(CurrentDir(),list.x,list.y+list.h+3) Local click:TWidget=gui.Clicked() If click=ok Local ret:String=CurrentDir()+"/"+fntext.text ChangeDir(oldpwd) FlushKeys() Return ret EndIf If click=cancel Or (KeyHit(KEY_ESCAPE) And gui.GetFocus()=Null) ChangeDir(oldpwd) FlushKeys() Return Null EndIf If list.GetSelectedItem()<>last last=TDirEntry(list.GetSelectedItem()) If last.is_dir ChangeDir(last.filename) LoadDirEnts(list,scroll,rows) fntext.text="" last=Null Else fntext.text=last.filename EndIf EndIf ok.enabled=(fntext.text<>"") list.SetTopRow(scroll.GetValue()) If i<>Null SetColor(255,255,255) DrawImage(i,MouseX(),MouseY()) EndIf Flip Forever End Function Rem bbdoc: Handles a TGUIHandler as if it's a modal dialog. returns: True if @ok is pressed, False if @cancel or the ESCAPE key is pressed. ESCAPE is only allowed if nothing has the text focus. about: @gui is the GUI handler and widgets to display as a dialog., @ok is the clickable widget that OK's the dialog, @cancel the widget that cancels it. about: If @i is not null then this image is drawn as a mouse cursor. EndRem Function GUIDialog(gui:TGUIHandler, ok:TWidget, cancel:TWidget, i:TImage=Null) FlushKeys() Local back:TImage=CreateImage(GraphicsWidth(),GraphicsHeight(),1,0) GrabImage(back,0,0) Local done:Int=False Local ok_pressed:Int Local click:TWidget While done=False Cls SetAlpha(0.5) DrawImage(back,0,0) SetAlpha(1) gui.EventLoop() click=gui.Clicked() If click=ok done=True ok_pressed=True EndIf If click=cancel Or (KeyHit(KEY_ESCAPE) And gui.GetFocus()=Null) done=True ok_pressed=False EndIf If i<>Null SetColor(255,255,255) DrawImage(i,MouseX(),MouseY()) EndIf Flip Wend FlushKeys() Return ok_pressed End Function Private Type TSplitText Field lines:TList Field width:Int Field height:Int Function Create:TSplitText(s:String) Local o:TSplitText=New TSplitText o.lines=CreateList() o.width=0 o.height=0 Local sub:String="" For Local f:Int=0 Until s.length Local c:Int=s[f] If c=Asc("|") o.lines.AddLast(sub) o.width=Max(o.width,TGUIFont.font.TextWidth(sub)) o.height:+TGUIFont.font.TextHeight(sub) sub="" Else sub:+Chr(c) EndIf Next If sub.length>0 o.lines.AddLast(sub) o.width=Max(o.width,TGUIFont.font.TextWidth(sub)) o.height:+TGUIFont.font.TextHeight(sub) EndIf Return o End Function EndType Type TDirEntry Extends TTextListItem Field path:String Field dir:String Field filename:String Field size:Int Field is_dir:Int Function Create:TDirEntry(path:String) Local o:TDirEntry=New TDirEntry o.path=path o.dir=ExtractDir(path) o.filename=StripDir(path) o.is_dir=(FileType(path)=FILETYPE_DIR) o.size=FileSize(path) Return o End Function Method Compare:Int(o:Object) Local d:TDirEntry=TDirEntry(o) If d=Null Return 0 EndIf If is_dir=d.is_dir Return filename.Compare(d.filename) Else If is_dir Return -1 Else Return 1 EndIf EndIf Return 0 End Method Method Columns:Int() Return 2 End Method Method ColumnOffset:Int(i:Int) If i=0 Return 0 Else Return GraphicsWidth()-TGUIFont.font.MaxWidth()*20 EndIf End Method Method ColumnData:String(i:Int) If i=0 Return filename Else If is_dir Return "DIR" Else Return size+" bytes" EndIf EndIf End Method End Type Function LoadDirEnts(list:TTextList, scroll:TScrollbar, rows:Int) Local d:TList=CreateList() For Local s:String=EachIn LoadDir(CurrentDir(),False) d.AddLast(TDirEntry.Create(CurrentDir()+"/"+s)) Next d.Sort() list.SetOptions(d.ToArray()) scroll.SetBar(0,Max(1,d.Count()-rows/2),1) End Function