' Copyright (c) 2006 Ian Cowburn ' ' Permission is hereby granted, free of charge, to any person obtaining a copy of ' this software and associated documentation files (the "Software"), to deal in ' the Software without restriction, including without limitation the rights to ' use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies ' of the Software, and to permit persons to whom the Software is furnished to do ' so, subject to the following conditions: ' ' The above copyright notice and this permission notice shall be included in all ' copies or substantial portions of the Software. ' ' THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ' IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ' FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ' AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ' LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ' OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ' SOFTWARE. ' ' $Id$ ' Rem bbdoc: noddybox.simplegui EndRem Module noddybox.simplegui ModuleInfo "Framework: (Very) Simple GUI" ModuleInfo "Copyright: Ian Cowburn -- released under the MIT License" ModuleInfo "Author: Ian Cowburn" ModuleInfo "Version: $Revision$" 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 TSGUIFont 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=TSGUIFont.font.TextWidth(text)+2 o.h=TSGUIFont.font.MaxHeight()+1 o.text=text gui.Register(o) Return o End Function Method Draw() TSGUIFont.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 READ_ONLY: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=TSGUIFont.font.MaxWidth()*(maxlen+1)+2 o.h=TSGUIFont.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 & READ_ONLY) owner.SetFocus(Self) EndIf End Method Method HandleKey(k:Int) If (mode & READ_ONLY) 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 & READ_ONLY) SetColor(128,128,128) s:+"_" Else SetColor(64,64,64) EndIf DrawRect(x,y,w,h) TSGUIFont.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=TSGUIFont.font.TextWidth(" "+text)+4+TSGUIFont.font.MaxHeight() o.h=TSGUIFont.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,TSGUIFont.font.MaxHeight(),TSGUIFont.font.MaxHeight()) If checked SetColor(255,100,100) DrawRect(x+1,y+1,TSGUIFont.font.MaxHeight()-2,TSGUIFont.font.MaxHeight()-2) EndIf TSGUIFont.font.Draw(" "+text,x+2+TSGUIFont.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-TSGUIFont.font.TextWidth(text)/2 o.oy=y+h/2-TSGUIFont.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) TSGUIFont.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,TSGUIFont.font.TextWidth(s)) Next o.w=maxw+TSGUIFont.font.MaxHeight()+4 o.h=TSGUIFont.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) TSGUIFont.font.Draw(text,x+TSGUIFont.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=TSGUIFont.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. 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 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)/TSGUIFont.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,TSGUIFont.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() TSGUIFont.font.Draw(li.ColumnData(f),x+li.ColumnOffset(f),ty) Next Else TSGUIFont.font.Draw(options[f].ToString(),x,ty) EndIf EndIf ty:+TSGUIFont.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=TSGUIFont.font.MaxHeight()*2+8 o.h=TSGUIFont.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 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+TSGUIFont.font.MaxHeight()*4 Local x:Int=GraphicsWidth()/2-w/2 Local y=GraphicsHeight()/2-h/2 Local by=y+h-TSGUIFont.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:+TSGUIFont.font.TextHeight(t) Next Local yes:TButton=TButton.Create(gui,x+5,by,w/2-10,TSGUIFont.font.MaxHeight()*2,"Yes",Null) Local no:TButton=TButton.Create(gui,x+w/2+5,by,w/2-10,TSGUIFont.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=TSGUIFont.font.MaxHeight()*2 Local h:Int=TSGUIFont.font.MaxHeight()*3 Local w:Int=TSGUIFont.font.TextWidth(title)+30 For f=0 Until options.length w=Max(w,TSGUIFont.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+TSGUIFont.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=TSGUIFont.font.MaxHeight() Local fw:Int=TSGUIFont.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.READ_ONLY) EndIf Local bw:Int=TSGUIFont.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() TSGUIFont.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,TSGUIFont.font.TextWidth(sub)) o.height:+TSGUIFont.font.TextHeight(sub) sub="" Else sub:+Chr(c) EndIf Next If sub.length>0 o.lines.AddLast(sub) o.width=Max(o.width,TSGUIFont.font.TextWidth(sub)) o.height:+TSGUIFont.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()-TSGUIFont.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