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.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: If true the widget is displayed and can be used. 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: True if this widget consumes events, False 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() mouse_over=True 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: 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.enabled=True o.consumes=False 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.enabled=True o.consumes=False 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: 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.enabled=True o.consumes=True 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 Method HandleClick() owner.SetFocus(Self) End Method Method HandleKey(k:Int) 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 text:+Chr(k) EndIf EndIf End Method Method Draw() Local s:String=text If owner.GetFocus()=Self 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.enabled=True o.consumes=True 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.enabled=True o.consumes=True 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.enabled=True o.consumes=True 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: A number up/down widget for floats EndRem Type TNumberFloat Extends TWidget Rem bbdoc: The value EndRem Field value:Float Rem bbdoc: The increment/decrement EndRem Field change:Float Rem bbdoc: The maximum value EndRem Field maxval:Float Rem bbdoc: The minimum value EndRem Field minval:Float 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:TNumberFloat(gui:TGUIHandler, x:Int, y:Int, callback(w:TWidget)=Null) Local o:TNumberFloat=New TNumberFloat o.enabled=True o.consumes=True 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:Float=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()0 Local w:TWidget=LocateWidget(x,y) For Local wid:TWidget=EachIn m_widgets If wid.enabled wid.Draw() 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 b 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 m_focus.HandleKey(k) 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 FlushMem 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 FlushMem 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() 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 KeyHit(KEY_MOUSERIGHT)=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 FlushMem Wend For f=0 Until options.length If button[f]=click Return f EndIf Next Return -1 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) 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 FlushMem Wend 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