From 8c29e6151f10e0c54b92d8813588a6dd6ca16123 Mon Sep 17 00:00:00 2001
From: Ian C <ianc@noddybox.co.uk>
Date: Fri, 28 Apr 2006 23:23:01 +0000
Subject: Updated docs (removed extra about: lines)

---
 gfxmenu.mod/gfxmenu.bmx     |  372 ++---
 simplegui.mod/simplegui.bmx | 3325 +++++++++++++++++++++----------------------
 2 files changed, 1848 insertions(+), 1849 deletions(-)

diff --git a/gfxmenu.mod/gfxmenu.bmx b/gfxmenu.mod/gfxmenu.bmx
index bc51f9d..921ebc7 100644
--- a/gfxmenu.mod/gfxmenu.bmx
+++ b/gfxmenu.mod/gfxmenu.bmx
@@ -1,186 +1,186 @@
-Rem
-bbdoc: noddybox.gfxmenu
-EndRem
-Module noddybox.gfxmenu
-
-ModuleInfo "Framework: Simple Graphical Menu"
-ModuleInfo "Copyright: Public Domain"
-ModuleInfo "Author: Ian Cowburn"
-ModuleInfo "Version: $Revision$"
-
-' $Id$
-
-Strict
-Import brl.linkedlist
-Import brl.max2d
-
-Rem
-bbdoc: Allows a menu backdrop to be automatically processed.  Derive a class from this to use.
-EndRem
-Type TGfxMenuBackdrop Abstract
-	Rem
-	bbdoc: Create a menu backdrop element.
-	returns: The created elemnt.
-	about: Notice that this is a method, rather than the usual function creator.
-	EndRem
-	Method Create:TGfxMenuBackdrop() Abstract
-	Rem
-	bbdoc: Called by the menu to update the backdrop.
-	EndRem
-	Method Update() Abstract
-End Type
-
-Rem
-bbdoc: Defines a graphical menu.
-EndRem
-Type TGfxMenu
-	Field list:TList
-	Field bdrop:TList
-	Field mbdown:Int
-	Field r:Int
-	Field g:Int
-	Field b:Int
-	Field over_r:Int
-	Field over_g:Int
-	Field over_b:Int
-	Field fade:Int
-
-	Rem
-	bbdoc: Create a menu.
-	returns: The created menu.
-	about: @backdrop is the backdrop to create (null for no backdrop).  @num is the number of backdrop items to create.
-	about: SetColor() is called with @r, @g and @b when the mouse is not over an item, overwise SetColor() is
-	about: called with @over_r, @over_g and @over_b when the item is active.  @fade is the amount colours will alter as the
-	about: mouse hovers over buttons.  Set this to 255 to make colour changes instant.
-	EndRem
-	Function Create:TGfxMenu(r:Int=164, g:Int=164, b:Int=164, over_r:Int=255, over_g:Int=255, over_b:Int=255, fade:Int=5, backdrop:TGfxMenuBackdrop=Null, num:Int=0)
-		Local menu:TGfxMenu=New TGfxMenu
-		
-		menu.list=New TList
-		menu.bdrop=New TList
-		menu.mbdown=False
-		
-		menu.r=r
-		menu.g=g
-		menu.b=b
-		menu.over_r=over_r
-		menu.over_g=over_g
-		menu.over_b=over_b
-		menu.fade=fade
-		
-		If backdrop
-			For Local f:Int=0 Until num
-				menu.bdrop.AddLast(backdrop.Create())
-			Next
-		EndIf
-				
-		Return menu
-	End Function
-	
-	Rem
-	bbdoc: Adds a menu item.
-	returns: The created menu.
-	about: @x and @y are the position to draw the image at.  If @x is -1 then the image is centred.  @i is the image.
-	about: @id is the value returned from @Render() for this menu item.  Don't use -1 for this!
-	EndRem
-	Method Add(x:Int, y:Int, i:TImage, id:Int)
-		list.AddLast(TGfxMenuOpt.Create(x,y,i,id,r,g,b))
-	End Method
-	
-	Rem
-	bbdoc: Renders and updates the menu.
-	returns: The selected item, or -1 if nothing clicked.
-	about: Set @hide to True to hide the menu (the backdrop is still updated).  SetColor() may have changed when this routine exits.
-	EndRem
-	Method Render:Int(hide:Int)
-		For Local p:TGfxMenuBackdrop=EachIn bdrop
-			p.Update()
-		Next
-		
-		If hide
-			Return -1
-		EndIf
-		
-		Local in:Int=-1
-		Local mx:Int=MouseX()
-		Local my:Int=MouseY()
-		Local any:Int=False
-		
-		For Local opt:TGfxMenuOpt=EachIn list
-			If opt.InBox(mx,my)
-				opt.r=Towards(over_r,opt.r)
-				opt.g=Towards(over_g,opt.g)
-				opt.b=Towards(over_b,opt.b)
-				SetColor(opt.r,opt.g,opt.b)
-		
-				If MouseDown(1)
-					mbdown=True
-					DrawImage(opt.i,opt.x,opt.y+2)
-					any=True
-				Else
-					DrawImage(opt.i,opt.x,opt.y)
-					If mbdown
-						in=opt.id
-					EndIf
-					mbdown=False
-				EndIf
-			Else
-				opt.r=Towards(r,opt.r)
-				opt.g=Towards(g,opt.g)
-				opt.b=Towards(b,opt.b)
-				SetColor(opt.r,opt.g,opt.b)
-				DrawImage(opt.i,opt.x,opt.y)
-			EndIf
-		Next
-		
-		If Not any
-			mbdown=False
-		EndIf
-		
-		Return in
-	End Method
-	
-	Method Towards:Int(dest:Int, val:Int)
-		If val<dest
-			Return Min(dest,val+fade)
-		ElseIf val>dest
-			Return Max(dest,val-fade)
-		EndIf
-		
-		Return val
-	End Method
-End Type
-
-Private
-
-Type TGfxMenuOpt
-	Field x:Int
-	Field y:Int
-	Field i:TImage
-	Field id:Int
-	Field r:Int
-	Field g:Int
-	Field b:Int
-	
-	Function Create:TGfxMenuOpt(x:Int, y:Int, i:TImage, id:Int, r:Int, g:Int, b:Int)
-		Local o:TGfxMenuOpt=New TGfxMenuOpt
-		
-		If x=-1
-			x=(GraphicsWidth()-ImageWidth(i))/2
-		EndIf
-		
-		o.x=x
-		o.y=y
-		o.i=i
-		o.id=id
-		o.r=r
-		o.g=g
-		o.b=b
-		
-		Return o
-	End Function
-	
-	Method InBox:Int(mx:Int, my:Int)
-		Return mx>=x And my>=y And mx<=x+ImageWidth(i) And my<=y+ImageHeight(i)
-	End Method
-End Type
+Rem
+bbdoc: noddybox.gfxmenu
+EndRem
+Module noddybox.gfxmenu
+
+ModuleInfo "Framework: Simple Graphical Menu"
+ModuleInfo "Copyright: Public Domain"
+ModuleInfo "Author: Ian Cowburn"
+ModuleInfo "Version: $Revision$"
+
+' $Id$
+
+Strict
+Import brl.linkedlist
+Import brl.max2d
+
+Rem
+bbdoc: Allows a menu backdrop to be automatically processed.  Derive a class from this to use.
+EndRem
+Type TGfxMenuBackdrop Abstract
+	Rem
+	bbdoc: Create a menu backdrop element.
+	returns: The created elemnt.
+	about: Notice that this is a method, rather than the usual function creator.
+	EndRem
+	Method Create:TGfxMenuBackdrop() Abstract
+	Rem
+	bbdoc: Called by the menu to update the backdrop.
+	EndRem
+	Method Update() Abstract
+End Type
+
+Rem
+bbdoc: Defines a graphical menu.
+EndRem
+Type TGfxMenu
+	Field list:TList
+	Field bdrop:TList
+	Field mbdown:Int
+	Field r:Int
+	Field g:Int
+	Field b:Int
+	Field over_r:Int
+	Field over_g:Int
+	Field over_b:Int
+	Field fade:Int
+
+	Rem
+	bbdoc: Create a menu.
+	returns: The created menu.
+	about: @backdrop is the backdrop to create (null for no backdrop).  @num is the number of backdrop items to create.
+	SetColor() is called with @r, @g and @b when the mouse is not over an item, overwise SetColor() is
+	called with @over_r, @over_g and @over_b when the item is active.  @fade is the amount colours will alter as the
+	mouse hovers over buttons.  Set this to 255 to make colour changes instant.
+	EndRem
+	Function Create:TGfxMenu(r:Int=164, g:Int=164, b:Int=164, over_r:Int=255, over_g:Int=255, over_b:Int=255, fade:Int=5, backdrop:TGfxMenuBackdrop=Null, num:Int=0)
+		Local menu:TGfxMenu=New TGfxMenu
+		
+		menu.list=New TList
+		menu.bdrop=New TList
+		menu.mbdown=False
+		
+		menu.r=r
+		menu.g=g
+		menu.b=b
+		menu.over_r=over_r
+		menu.over_g=over_g
+		menu.over_b=over_b
+		menu.fade=fade
+		
+		If backdrop
+			For Local f:Int=0 Until num
+				menu.bdrop.AddLast(backdrop.Create())
+			Next
+		EndIf
+				
+		Return menu
+	End Function
+	
+	Rem
+	bbdoc: Adds a menu item.
+	returns: The created menu.
+	about: @x and @y are the position to draw the image at.  If @x is -1 then the image is centred.  @i is the image.
+	@id is the value returned from @Render() for this menu item.  Don't use -1 for this!
+	EndRem
+	Method Add(x:Int, y:Int, i:TImage, id:Int)
+		list.AddLast(TGfxMenuOpt.Create(x,y,i,id,r,g,b))
+	End Method
+	
+	Rem
+	bbdoc: Renders and updates the menu.
+	returns: The selected item, or -1 if nothing clicked.
+	about: Set @hide to True to hide the menu (the backdrop is still updated).  SetColor() may have changed when this routine exits.
+	EndRem
+	Method Render:Int(hide:Int)
+		For Local p:TGfxMenuBackdrop=EachIn bdrop
+			p.Update()
+		Next
+		
+		If hide
+			Return -1
+		EndIf
+		
+		Local in:Int=-1
+		Local mx:Int=MouseX()
+		Local my:Int=MouseY()
+		Local any:Int=False
+		
+		For Local opt:TGfxMenuOpt=EachIn list
+			If opt.InBox(mx,my)
+				opt.r=Towards(over_r,opt.r)
+				opt.g=Towards(over_g,opt.g)
+				opt.b=Towards(over_b,opt.b)
+				SetColor(opt.r,opt.g,opt.b)
+		
+				If MouseDown(1)
+					mbdown=True
+					DrawImage(opt.i,opt.x,opt.y+2)
+					any=True
+				Else
+					DrawImage(opt.i,opt.x,opt.y)
+					If mbdown
+						in=opt.id
+					EndIf
+					mbdown=False
+				EndIf
+			Else
+				opt.r=Towards(r,opt.r)
+				opt.g=Towards(g,opt.g)
+				opt.b=Towards(b,opt.b)
+				SetColor(opt.r,opt.g,opt.b)
+				DrawImage(opt.i,opt.x,opt.y)
+			EndIf
+		Next
+		
+		If Not any
+			mbdown=False
+		EndIf
+		
+		Return in
+	End Method
+	
+	Method Towards:Int(dest:Int, val:Int)
+		If val<dest
+			Return Min(dest,val+fade)
+		ElseIf val>dest
+			Return Max(dest,val-fade)
+		EndIf
+		
+		Return val
+	End Method
+End Type
+
+Private
+
+Type TGfxMenuOpt
+	Field x:Int
+	Field y:Int
+	Field i:TImage
+	Field id:Int
+	Field r:Int
+	Field g:Int
+	Field b:Int
+	
+	Function Create:TGfxMenuOpt(x:Int, y:Int, i:TImage, id:Int, r:Int, g:Int, b:Int)
+		Local o:TGfxMenuOpt=New TGfxMenuOpt
+		
+		If x=-1
+			x=(GraphicsWidth()-ImageWidth(i))/2
+		EndIf
+		
+		o.x=x
+		o.y=y
+		o.i=i
+		o.id=id
+		o.r=r
+		o.g=g
+		o.b=b
+		
+		Return o
+	End Function
+	
+	Method InBox:Int(mx:Int, my:Int)
+		Return mx>=x And my>=y And mx<=x+ImageWidth(i) And my<=y+ImageHeight(i)
+	End Method
+End Type
diff --git a/simplegui.mod/simplegui.bmx b/simplegui.mod/simplegui.bmx
index f49c8dc..1f2742a 100644
--- a/simplegui.mod/simplegui.bmx
+++ b/simplegui.mod/simplegui.bmx
@@ -1,1663 +1,1662 @@
-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<maxlen
-			If (mode & NUMERIC)
-				If k=Asc("-") And (Not (mode & POSITIVE)) And text.length=0
-					text:+Chr(k)
-				EndIf
-				
-				If k>=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.  <i>Note</i>: 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 i<num
-			top=i
-		EndIf
-	End Method
-	
-	Rem
-	bbdoc: Get the top row.
-	returns: The index of the item displayed in the first row of the widget.  -1 if no options are set.
-	EndRem
-	Method GetTopRow:Int()
-		If options<>Null
-			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<num
-			If i<top
-				top=i
-			ElseIf 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 sel<num
-				selected=sel
-				If callback<>Null
-					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+TGUIFont.font.MaxHeight()
-						SetColor(200,128,128)
-					Else
-						SetColor(128,128,128)
-					EndIf
-					DrawRect(x,ty,w,TGUIFont.font.MaxHeight())
-				Else
-					If mouse_over And MouseY()>=ty And MouseY()<ty+TGUIFont.font.MaxHeight() And f<num
-						SetColor(128,0,0)
-						DrawRect(x,ty,w,TGUIFont.font.MaxHeight())
-					EndIf
-				EndIf
-				
-				If f<num
-					Local li:TTextListItem=TTextListItem(options[f])
-					
-					If li<>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()<x+w/2
-			value=Min(value+change,maxval)
-		Else
-			value=Max(value-change,minval)
-		EndIf
-		
-		If value<>old And callback<>Null
-			callback(Self)
-		EndIf
-	End Method
-
-	Method Draw()
-		If mouse_over
-			If MouseX()<x+w/2
-				SetColor(128,128,128)
-				DrawRect(x,y,w/2,h)
-				SetColor(64,64,64)
-				DrawRect(x+w/2,y,w/2,h)
-			Else
-				SetColor(64,64,64)
-				DrawRect(x,y,w/2,h)
-				SetColor(128,128,128)
-				DrawRect(x+w/2,y,w/2,h)
-			EndIf
-		Else
-			SetColor(64,64,64)
-			DrawRect(x,y,w,h)
-		EndIf
-	
-		Draw3DBox(x+2,y+2,h-4,h-4,False,1)
-		Draw3DBox(x+w-h,y+2,h-4,h-4,False,1)
-
-		TGUIFont.font.Draw(value,x+w+2,y+1)
-	End Method
-End Type
-
-Rem
-bbdoc: A number up/down widget for ints
-EndRem
-Type TNumberInt Extends TWidget
-
-	Rem
-	bbdoc: The value
-	EndRem
-	Field value:Int
-	
-	Rem
-	bbdoc: The increment/decrement
-	EndRem
-	Field change:Int
-	
-	Rem
-	bbdoc: The maximum value
-	EndRem
-	Field maxval:Int
-	
-	Rem
-	bbdoc: The minimum value
-	EndRem
-	Field minval:Int
-	
-	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:TNumberInt(gui:TGUIHandler, x:Int, y:Int, callback(w:TWidget)=Null)
-		Local o:TNumberInt=New TNumberInt
-		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:Int=value
-
-		If MouseX()<x+w/2
-			value=Min(value+change,maxval)
-		Else
-			value=Max(value-change,minval)
-		EndIf
-		
-		If value<>old And callback<>Null
-			callback(Self)
-		EndIf
-	End Method
-
-	Method Draw()
-		If mouse_over
-			If MouseX()<x+w/2
-				SetColor(128,128,128)
-				DrawRect(x,y,w/2,h)
-				SetColor(64,64,64)
-				DrawRect(x+w/2,y,w/2,h)
-			Else
-				SetColor(64,64,64)
-				DrawRect(x,y,w/2,h)
-				SetColor(128,128,128)
-				DrawRect(x+w/2,y,w/2,h)
-			EndIf
-		Else
-			SetColor(64,64,64)
-			DrawRect(x,y,w,h)
-		EndIf
-
-		Draw3DBox(x+2,y+2,h-4,h-4,False,1)
-		Draw3DBox(x+w-h,y+2,h-4,h-4,False,1)
-
-		TGUIFont.font.Draw(value,x+w+2,y+1)
-	End Method
-End Type
-
-Rem
-bbdoc: A scrollbar widget
-EndRem
-Type TScrollbar Extends TWidget
-
-	Field horiz:Int
-	Field bc:Int
-	Field page:Double
-	Field maxval:Double
-	Field minval:Double
-	Field stepsize:Double
-	Field blocksize:Int
-	
-	Rem
-	bbdoc: Create a TVerticalScrollbar widget.
-	returns: The created widget.
-	about: @gui is the @TGUIHandler object managing this wiget. @x, @y, @w and @h are its position and size.
-	about: If @w is greater than height then an horizontal scrollbar is created, otherwise its vertical.  @callback is called when the scrollbar moves.
-	EndRem
-	Function Create:TScrollbar(gui:TGUIHandler, x:Int, y:Int, w:Int, h:Int, callback(w:TWidget)=Null)
-		Local o:TScrollbar=New TScrollbar
-		o.displayed=True
-		o.enabled=True
-		o.consumes=HAS_CLICK|HAS_DRAG
-		o.x=x
-		o.y=y
-		o.w=w
-		o.h=h
-		o.horiz=(w>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.  <i>Note</i>: 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 x<w.x+w.w And y<w.y+w.h And w.consumes
-				Return w
-			EndIf
-		Next
-		Return Null
-	End Method
-End Type
-
-
-Rem
-bbdoc: Displays a notification alert with the supplied string.
-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 GUINotify(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 button:TButton=TButton.Create(gui,x+5,by,w-10,TGUIFont.font.MaxHeight()*2,"OK",Null)
-	
-	Local click:TWidget=Null
-	
-	While click<>button
-		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
+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.
+	@maxlen is the maximum number of characters allowed.  @mode defines the valid characters that can be entered.
+	@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
+	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<maxlen
+			If (mode & NUMERIC)
+				If k=Asc("-") And (Not (mode & POSITIVE)) And text.length=0
+					text:+Chr(k)
+				EndIf
+				
+				If k>=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.
+	@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.
+	@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
+	@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.
+	@rows are the number of items to display at a time.
+	@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.  <i>Note</i>: If the objects passed are derived from TTextListItem then tabular output is generated.
+	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
+	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 i<num
+			top=i
+		EndIf
+	End Method
+	
+	Rem
+	bbdoc: Get the top row.
+	returns: The index of the item displayed in the first row of the widget.  -1 if no options are set.
+	EndRem
+	Method GetTopRow:Int()
+		If options<>Null
+			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<num
+			If i<top
+				top=i
+			ElseIf 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 sel<num
+				selected=sel
+				If callback<>Null
+					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+TGUIFont.font.MaxHeight()
+						SetColor(200,128,128)
+					Else
+						SetColor(128,128,128)
+					EndIf
+					DrawRect(x,ty,w,TGUIFont.font.MaxHeight())
+				Else
+					If mouse_over And MouseY()>=ty And MouseY()<ty+TGUIFont.font.MaxHeight() And f<num
+						SetColor(128,0,0)
+						DrawRect(x,ty,w,TGUIFont.font.MaxHeight())
+					EndIf
+				EndIf
+				
+				If f<num
+					Local li:TTextListItem=TTextListItem(options[f])
+					
+					If li<>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. @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()<x+w/2
+			value=Min(value+change,maxval)
+		Else
+			value=Max(value-change,minval)
+		EndIf
+		
+		If value<>old And callback<>Null
+			callback(Self)
+		EndIf
+	End Method
+
+	Method Draw()
+		If mouse_over
+			If MouseX()<x+w/2
+				SetColor(128,128,128)
+				DrawRect(x,y,w/2,h)
+				SetColor(64,64,64)
+				DrawRect(x+w/2,y,w/2,h)
+			Else
+				SetColor(64,64,64)
+				DrawRect(x,y,w/2,h)
+				SetColor(128,128,128)
+				DrawRect(x+w/2,y,w/2,h)
+			EndIf
+		Else
+			SetColor(64,64,64)
+			DrawRect(x,y,w,h)
+		EndIf
+	
+		Draw3DBox(x+2,y+2,h-4,h-4,False,1)
+		Draw3DBox(x+w-h,y+2,h-4,h-4,False,1)
+
+		TGUIFont.font.Draw(value,x+w+2,y+1)
+	End Method
+End Type
+
+Rem
+bbdoc: A number up/down widget for ints
+EndRem
+Type TNumberInt Extends TWidget
+
+	Rem
+	bbdoc: The value
+	EndRem
+	Field value:Int
+	
+	Rem
+	bbdoc: The increment/decrement
+	EndRem
+	Field change:Int
+	
+	Rem
+	bbdoc: The maximum value
+	EndRem
+	Field maxval:Int
+	
+	Rem
+	bbdoc: The minimum value
+	EndRem
+	Field minval:Int
+	
+	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.
+	@callback is called when the value changes.
+	EndRem
+	Function Create:TNumberInt(gui:TGUIHandler, x:Int, y:Int, callback(w:TWidget)=Null)
+		Local o:TNumberInt=New TNumberInt
+		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:Int=value
+
+		If MouseX()<x+w/2
+			value=Min(value+change,maxval)
+		Else
+			value=Max(value-change,minval)
+		EndIf
+		
+		If value<>old And callback<>Null
+			callback(Self)
+		EndIf
+	End Method
+
+	Method Draw()
+		If mouse_over
+			If MouseX()<x+w/2
+				SetColor(128,128,128)
+				DrawRect(x,y,w/2,h)
+				SetColor(64,64,64)
+				DrawRect(x+w/2,y,w/2,h)
+			Else
+				SetColor(64,64,64)
+				DrawRect(x,y,w/2,h)
+				SetColor(128,128,128)
+				DrawRect(x+w/2,y,w/2,h)
+			EndIf
+		Else
+			SetColor(64,64,64)
+			DrawRect(x,y,w,h)
+		EndIf
+
+		Draw3DBox(x+2,y+2,h-4,h-4,False,1)
+		Draw3DBox(x+w-h,y+2,h-4,h-4,False,1)
+
+		TGUIFont.font.Draw(value,x+w+2,y+1)
+	End Method
+End Type
+
+Rem
+bbdoc: A scrollbar widget
+EndRem
+Type TScrollbar Extends TWidget
+
+	Field horiz:Int
+	Field bc:Int
+	Field page:Double
+	Field maxval:Double
+	Field minval:Double
+	Field stepsize:Double
+	Field blocksize:Int
+	
+	Rem
+	bbdoc: Create a TVerticalScrollbar widget.
+	returns: The created widget.
+	about: @gui is the @TGUIHandler object managing this wiget. @x, @y, @w and @h are its position and size.
+	If @w is greater than height then an horizontal scrollbar is created, otherwise its vertical.  @callback is called when the scrollbar moves.
+	EndRem
+	Function Create:TScrollbar(gui:TGUIHandler, x:Int, y:Int, w:Int, h:Int, callback(w:TWidget)=Null)
+		Local o:TScrollbar=New TScrollbar
+		o.displayed=True
+		o.enabled=True
+		o.consumes=HAS_CLICK|HAS_DRAG
+		o.x=x
+		o.y=y
+		o.w=w
+		o.h=h
+		o.horiz=(w>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
+	is covered by the block in the scrollbar.  <i>Note</i>: 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 x<w.x+w.w And y<w.y+w.h And w.consumes
+				Return w
+			EndIf
+		Next
+		Return Null
+	End Method
+End Type
+
+
+Rem
+bbdoc: Displays a notification alert with the supplied string.
+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 GUINotify(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 button:TButton=TButton.Create(gui,x+5,by,w-10,TGUIFont.font.MaxHeight()*2,"OK",Null)
+	
+	Local click:TWidget=Null
+	
+	While click<>button
+		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.
+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.
+Set @save to true for a save dialog (the filename can be altered).
+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.
+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
-- 
cgit v1.2.3