retro text box array

Post your FreeBASIC source, examples, tips and tricks here. Please don’t post code without including an explanation.
Post Reply
BasicCoder2
Posts: 3906
Joined: Jan 01, 2009 7:03
Location: Australia

retro text box array

Post by BasicCoder2 »

This uses a version of my retro text box code to demo its use in an array.
By retro I mean it is written the way I wrote code with QBASIC and only uses the standard font.
One day I might start encapsulating a control's software as a class :)

The text can be left/right or centered for each text box.
If the text in the selected text box is larger than the width of the text box its full contents are displayed on the bottom bar.

Code: Select all

screenres 640,480,32
color rgb(0,0,0),rgb(200,200,240):cls

dim shared as integer mx,my,mb

' GLOBAL VARIABLES USED BY TEXTBOX ROUTINES DrawTextBox and EditTextBox
dim shared as integer cursor

type TextBox
    as integer x
    as integer y
    as integer w
    as integer h
    as string  t    'text
    as integer a    'active=1 
    as integer j    'justify left = 0 /center = 1/right = 2
    as integer posX 'retains info when text larger than width of text box
end type

cursor = 0   

sub drawTextBox(tb as textBox)
    dim as integer shiftX
    screenlock()
    
    '*******************  THIS DRAWS THE BOX ***************************
    'clear rectangle
    line (tb.x,tb.y)-(tb.x+tb.w+8,tb.y+tb.h),rgb(255,255,255),bf
    'draw border
    line (tb.x,tb.y)-(tb.x+tb.w+8,tb.y+tb.h),rgb(127,127,127),b
    line (tb.x+1,tb.y+1)-(tb.x+tb.w+7,tb.y+tb.h-1),rgb(127,127,127),b
    '*******************************************************************
    
    if tb.a = 1 then
        'keep cursor within the box
        if cursor > tb.posX+tb.w/8 then
            tb.posX = tb.posX + 1
        end if
    
        if cursor < tb.posX+1 then
            if tb.posX>0 then
                tb.posX = tb.posX - 1
            end if
        end if
    end if

    if len(tb.t)*8 > tb.w or tb.j = 0 then
        draw string (tb.x+4,tb.y+4),mid(tb.t,tb.posX+1,tb.w\8)    
        if tb.a = 1 then
            line (tb.x+cursor*8+4-tb.posX*8,tb.y+2)-(tb.x+cursor*8+5-tb.posX*8,tb.y + 14),rgb(10,10,10),bf  'draw cursor
        end if
    else
        if tb.j = 1 then shiftX = tb.w\2-len(tb.t)*8/2
        if tb.j = 2 then shiftX = tb.w-len(tb.t)*8
        draw string (tb.x+4+shiftX,tb.y+4),tb.t
        if tb.a = 1 then
            line (tb.x+cursor*8+4+shiftX,tb.y+2)-(tb.x+cursor*8+5+shiftX,tb.y + 14),rgb(10,10,10),bf  'draw cursor
        end if
    end if
    
    screenunlock()

end sub

function editTextBox(tb as TextBox) as integer 

    dim as string key
    dim as integer ascKey

    cursor =(mx - tb.x)\8+tb.posX
    if cursor > len(tb.t) then
        cursor = len(tb.t)
    end if
    while mb=1
        getmouse mx,my,,mb
    wend

    tb.a = 1 'activate
    while inkey<>"":wend  'clear buffer
    drawTextBox(tb) 'show cursor
    do
        getmouse mx,my,,mb
        if mb=1 then
            if  mx>tb.x and mx<tb.x+tb.w and my>tb.y and my<tb.y+tb.h then
                cursor =(mx - tb.x)\8+tb.posX
                if cursor > len(tb.t) then
                    cursor = len(tb.t)
                end if
                while mb=1
                    getmouse mx,my,,mb
                wend
            end if
            drawTextBox(tb)  ' to show cursor at new position
        end if
        
        key = inkey
        if key<>"" then
            if len(key)>1 then
                ascKey = asc(right(key,1))
                if ascKey = 75 then  'CURSOR LEFT
                    if cursor > 0 then
                        cursor = cursor -1 
                    end if
                end if
                if ascKey = 77 then  'CURSOR RIGHT
                    if cursor < len(tb.t) then
                        cursor = cursor + 1
                    end if
                end if
                if ascKey = 83 then 'DELETE
                    tb.t = left(tb.t,cursor) + right(tb.t,len(tb.t)-cursor-1)
                end if
            else
                ascKey = asc(key)
                if ascKey =8 then
                    if cursor > 0 then  'BACKSPACE
                        tb.t = left(tb.t,cursor-1) + mid(tb.t,cursor+1,len(tb.t)-cursor)
                        cursor = cursor - 1
                    end if
                else
                    if ascKey<>9 and ascKey<>27 and ascKey<>13 then  'TAB, ESC, ENTER
                        tb.t = left(tb.t,cursor) + key + right(tb.t,len(tb.t)-cursor)
                        cursor = cursor + 1
                    end if
                end if
            end if
            drawTextBox(tb)
        end if
        sleep 2
    
    loop until asc(key)=13 or ascKey=9 or ascKey=27 or mb=1 'ENTER, TAB, ESC

    tb.a = 0 'deactivate
    drawTextBox(tb)  'this draws contents without cursor
    
    return ascKey
    
end function


'=====================================================================
'===============    USE OF TEXTBOX EXAMPLE    ========================
'=====================================================================

'global variables
dim shared as integer event     'flag button down on a text box
dim shared as integer retKey    'exit char returned from editTextBox
dim shared as integer col,row   'column and row of current textbox being edited

dim shared as textBox tb1(5,20)  'create a 3 x 20 textbox array
'initialize 5 x 20 array of text boxes
for j as integer = 0 to 19
    for i as integer = 0 to 4
        tb1(i,j).x = i*100+50          
        tb1(i,j).y = j*18+50
        tb1(i,j).w = 11*8   'wide enough for 10 characters 8 pixels wide
        tb1(i,j).h = 16
        tb1(i,j).t = ""
        tb1(i,j).a = 0      
        tb1(i,j).j = 0
    next i
next j


sub update()
    screenlock
    cls
    for j as integer = 0 to 19
        for i as integer = 0 to 4
            drawTextBox(tb1(i,j))
        next i
    next j
    draw string (50,10),"USE MOUSE KEY TO SELECT A TEXT BOX"
    draw string (50,20),"HIT ESC KEY TO EXIT PROGRAM"
    
    'display content of current textbox
    line (0,460)-(639,479),rgb(100,100,200),bf
    draw string (20,479-12),tb1(col,row).t

    screenunlock
end sub

'     --- MAIN ---

update()  'show the text boxes to start with

do
    getmouse mx,my,,mb

    'test if left mouse button down event over a text box
    event = -1
    for j as integer = 0 to 19
        for i as integer = 0 to 4
            if mb=1 and mx>tb1(i,j).x and mx<tb1(i,j).x+tb1(i,j).w and my>tb1(i,j).y and my<tb1(i,j).y+tb1(i,j).h then
                col = i
                row = j
                event = 1
            end if
        next i
    next j
    
    'mouse down on text box event?
    if event = 1 then
        update()
        retKey = editTextBox(tb1(col,row))
        update()
    end if

    sleep 2
    
loop until multikey(&H01)
 
Last edited by BasicCoder2 on Aug 09, 2015 2:21, edited 1 time in total.
sancho2
Posts: 547
Joined: May 17, 2015 6:41

Re: retro text box array

Post by sancho2 »

Thanks for the code demo, BasicCoder2.
I had been preparing to code exactly this. I logged on today and saw your code and I tried it out. I didn't fully read your comments however and I went ahead and built a class of it. Now I see that you might be doing that as well.

My version is incomplete. Two things I am going to add are a flexible border, and replacement of graphics under the textbox after the user is done with it. I did add a simple textbox erasing routine (TextBoxCLS) that clears the textbox from screen.
I also added a collection class to deal with the array of textboxes. It is incomplete but enough to demonstrate the textbox class.

Here is my version of your retro textbox as a class.

Code: Select all

' ---------------------------------------------------------------------------------------
' TextBox as a Class.
' 	This is BasicCoder2's retro textbox implemented as a class. 
' ---------------------------------------------------------------------------------------
ScreenRes 640,480,32
color rgb(0,0,0),rgb(200,200,240):cls

dim shared as integer mx,my,mb

type TextBox
	Public:
		As integer x
		As integer y
		As integer w
		As integer h
		As string  t    'text
		As integer a    'active=1
		As integer j    'justify left = 0 /center = 1/right = 2
		As integer posX 'retains info when text larger than width of text box
		Declare Constructor (x As Integer, y As Integer, w As Integer, h As Integer, _
					t As String, a As Integer, j As Integer) 
		Declare Sub DrawTextBox(cursor As Integer = 0)
		Declare Function EditTextBox() As Integer
		Declare Sub EraseTextBox()
		Declare Function HitTest(x As Integer, y As Integer) As Integer
		Declare Sub TextBoxCLS()
end Type

Type TextBoxCollection
	Public: 
		activeTB As TextBox Ptr
		count As Integer
		txb(Any) As TextBox Ptr
		Declare function AddTextBox(tbp As TextBox) As Integer
		Declare Function HitTest(x As Integer, y As Integer) As Integer
		Declare Sub ShowAll()
		Declare Destructor
End Type
Constructor TextBox(x As Integer, y As Integer, w As Integer, h As Integer, _
        		t As String, a As Integer, j As Integer)
	'
	this.x = x
	this.y = y
	this.w = w
	this.h = h
	this.t = t
	this.a = a
	this.j = j
End Constructor
        		
Function TextBox.EditTextBox() As Integer
	' This function is mostly left the way BasicCoder2 posted it. 
	' The only real changes made were to bind it to an instance
	' Instead of using global variable cursor, I use it procedure (method) level 
	' and pass it to the DrawTextBox() method
        dim as string key
        dim as Integer ascKey, cursor

        cursor =(mx - this.x)\8+this.posX
        if cursor > len(this.t) then
            cursor = len(this.t)
        end if
        while mb=1
            GetMouse mx,my,,mb ' [edit] the mx my and mb variables should be changed to local vars
        wend

        this.a = 1 'activate
        while inkey<>"":wend  'clear buffer
        this.drawTextBox(cursor) 'show cursor (code edited for class instance)
        do
            getmouse mx,my,,mb
            if mb=1 then
                if  mx>this.x and mx<this.x+this.w and my>this.y and my<this.y+this.h then
                    cursor =(mx - this.x)\8+this.posX
                    if cursor > len(this.t) then
                        cursor = len(this.t)
                    end if
                    while mb=1
                        getmouse mx,my,,mb
                    wend
                end if
                this.drawTextBox(cursor)  ' to show cursor at new position (code edited for class instance)
            end if
           
            key = inkey
            if key<>"" then
                if len(key)>1 then
                    ascKey = asc(right(key,1))
                    if ascKey = 75 then  'CURSOR LEFT
                        if cursor > 0 then
                            cursor = cursor -1
                        end if
                    end if
                    if ascKey = 77 then  'CURSOR RIGHT
                        if cursor < len(this.t) then
                            cursor = cursor + 1
                        end if
                    end if
                    if ascKey = 83 then 'DELETE
                        this.t = left(this.t,cursor) + right(this.t,len(this.t)-cursor-1)
                    end if
                else
                    ascKey = asc(key)
                    if ascKey =8 then
                        if cursor > 0 then  'BACKSPACE
                            this.t = left(this.t,cursor-1) + mid(this.t,cursor+1,len(this.t)-cursor)
                            cursor = cursor - 1
                        end if
                    else
                        if ascKey<>9 and ascKey<>27 and ascKey<>13 then  'TAB, ESC, ENTER
                            this.t = left(this.t,cursor) + key + right(this.t,len(this.t)-cursor)
                            cursor = cursor + 1
                        end if
                    end if
                end if
                this.drawTextBox(cursor)	' code edited for class instance
            end if
            sleep 2
       
        loop until asc(key)=13 or ascKey=9 or ascKey=27 or mb=1 'ENTER, TAB, ESC

        this.a = 0 'deactivate
        this.drawTextBox()  'this draws contents without cursor (code edited for class instance) 
       
        return ascKey
       
End Function
Sub TextBox.DrawTextBox(cursor As Integer = 0)
	' BasicCoder2 original procedure with only variables changed to reflect class instance
	' There is a parameter added as well 
        dim As integer shiftX
        ScreenLock()
       
        '*******************  THIS DRAWS THE BOX ***************************
        'clear rectangle
        line (this.x,this.y)-(this.x+this.w+8,this.y+this.h),rgb(255,255,255),bf
        'draw border
        line (this.x,this.y)-(this.x+this.w+8,this.y+this.h),rgb(127,127,127),b
        line (this.x+1,this.y+1)-(this.x+this.w+7,this.y+this.h-1),rgb(127,127,127),b
        '*******************************************************************
        if this.a = 1 then
            'keep cursor within the box
            if cursor > this.posX+this.w/8 then
                this.posX = this.posX + 1
            end if
       
            if cursor < this.posX+1 then
                if this.posX>0 then
                    this.posX = this.posX - 1
                end if
            end if
        end if

        if len(this.t)*8 > this.w or this.j = 0 then
            draw string (this.x+4,this.y+4),mid(this.t,this.posX+1,this.w\8)    
            if this.a = 1 then
                line (this.x+cursor*8+4-this.posX*8,this.y+2)-(this.x+cursor*8+5-this.posX*8,this.y + 14),rgb(10,10,10),bf  'draw cursor
            end if
        else
            if this.j = 1 then shiftX = this.w\2-len(this.t)*8/2
            if this.j = 2 then shiftX = this.w-len(this.t)*8
            draw string (this.x+4+shiftX,this.y+4),this.t
            if this.a = 1 then
                line (this.x+cursor*8+4+shiftX,this.y+2)-(this.x+cursor*8+5+shiftX,this.y + 14),rgb(10,10,10),bf  'draw cursor
            end if
        end if
        ScreenUnLock()
        sleep 2
	
End Sub
Sub TextBox.TextBoxCLS()
	' This sub reduces the view to the dimensions of the text box then cls
	' It then returns the view to the entire screen
	View (this.x, this.y)- (this.x + this.w + 8, this.y + this.h -1)
	Cls
	View 
End Sub
Function TextBox.HitTest(x As Integer, y As Integer) As Integer
	' return 1 if x,y is within the bounds of this textbox 
	If x < this.x OrElse x > this.x + this.w - 1 Then 
		Return 0
	EndIf
	If y < this.y OrElse y > this.y + this.h - 1 Then
		Return 0
	EndIf

	Return 1

End Function
Sub TextBoxCollection.ShowAll()
	' Draw all the textboxes in the collection to the screen
	Dim n As Integer
	For n = 1 To this.count
		this.txb(n)->DrawTextBox()
	Next
End Sub
Function TextBoxCollection.HitTest(x As Integer, y As Integer) As Integer
	' If xy is within a collected textbox, return the index of that textbox
	' Otherwise return 0 
	Dim As Integer n, ret 
	
	ret = 0
	For n = 1 To this.count
		If this.txb(n)->HitTest(x, y) > 0 Then
			ret = n
			Exit For
		EndIf 
	Next
	
	Return ret
End Function
Function TextBoxCollection.AddTextBox(tbp As TextBox) As Integer
	' Add a textbox to the collection
	' return the number of textboxes
	Dim As Integer n
	
	n = this.count + 1

	If n > UBound(this.txb) Then
		ReDim Preserve this.txb(1 To n)
	EndIf 
	
	this.txb(n) = Callocate(SizeOf(TextBox))

	*this.txb(n) = tbp
	this.count += 1  
	
	Return this.count
End Function
Destructor TextBoxCollection
	' FB errors on deallocate if there is no valid pointer. 
	' During testing and debugging I had lots of problems 
	' with this routine so I enclosed it in ab #if block. 
	' Its probably unnecessary.
	Dim As Integer n

	For n = 1 To this.count
		#If TypeOf(this.txb(n)) = TypeOf(TextBox Ptr)
			DeAllocate(this.txb(n))
		#EndIf
	Next
	
	this.count = 0
	
End Destructor

'The rest of code here is the main block. This is modified to reflect that 
' TextBox is now a class 
    '=====================================================================
    '===============    USE OF TEXTBOX EXAMPLE    ========================
    '=====================================================================

 	Dim Shared As TextBoxCollection myTxts 
	Dim n As Integer
	Dim tb As TextBox Ptr
 	
 	' I kept this loop using rows and columns so I don't have to figure out spacing 
 	for j As integer = 0 to 19
  		for i as integer = 0 to 4
  			n += 1
  			tb = New TextBox(i * 100 + 50, j * 18 + 50, 11 * 8, 16, "Text" & Str(n),0,0)
  			myTxts.AddTextBox(*tb)
  			Delete tb 
  		Next
    Next


	sub update()
		' It doesn't seem necessary to redraw the textboxes. In testing
		' I couldn't break the display. So I removed the redrawing 
		' of the textboxes.
		' We could remove Update entirely. I left it here to display edited
		' text from textbox the way BasicCoder2 had it. 
		
		ScreenLock

		'myTxts.activeTB->DrawTextBox()		' I don't think this is needed

		Draw string (50,10),"USE MOUSE KEY TO SELECT A TEXT BOX"
		Draw string (50,20),"HIT ESC KEY TO EXIT PROGRAM"
		'
		'display content of current textbox
		Line (0,460)-(639,479),rgb(100,100,200),bf
		Draw string (20,479-12),myTxts.activeTB->t
		'
		ScreenUnLock
	end sub


    '     --- MAIN ---
	myTxts.ShowAll()
	
	' we need an active textbox or update fails
	myTxts.activeTB = myTxts.txb(1)
	
   update()  

	Dim tbn As Integer

    do
        getmouse mx,my,,mb

        If mb = 1 Then
        	' if the left mouse button is down then we check in the 
        	' textbox collection to see if the mouse is over a 
        	' member
        	tbn = myTxts.HitTest(mx, my) 
        	If tbn > 0 Then
        		myTxts.activeTB = myTxts.txb(tbn) 
        		myTxts.activeTB->EditTextBox()
        		update()		
        	EndIf
        	
        EndIf
        
        sleep 2
       
    loop until multikey(&H01)

' this is only here to show how the TextBoxCLS() method works
myTxts.txb(3)->TextBoxCLS()
Sleep
myTxts.Destructor()
Sleep
[edit]
I just want to sneak a comment in here that the method TextBox.EditTextBos() should use local variables for GetMouse() as opposed to what I have.
Last edited by sancho2 on Jul 29, 2015 1:55, edited 1 time in total.
BasicCoder2
Posts: 3906
Joined: Jan 01, 2009 7:03
Location: Australia

Re: retro text box array

Post by BasicCoder2 »

Interesting to see a class version although I don't really know enough to follow the code.
Unfortunately it looks like a I can't run it until I update my FreeBasic compiler.
All I get is a big list of compiler errors...
fbc: FreeBASIC Compiler - Version 0.24.0 (07-30-2012) for win32

I worry about updating as then maybe my current code will fail to compile.
grindstone
Posts: 862
Joined: May 05, 2015 5:35
Location: Germany

Re: retro text box array

Post by grindstone »

BasicCoder2 wrote:I worry about updating as then maybe my current code will fail to compile.
Why don't you install multiple versions of the compiler. If one version fails, just try another. The only thing you have to do then is changing the compiler path at "Options > Path Options > Compiler Path".

Regards
grindstone
Tourist Trap
Posts: 2958
Joined: Jun 02, 2015 16:24

Re: retro text box array

Post by Tourist Trap »

BasicCoder2 wrote:All I get is a big list of compiler errors...
fbc: FreeBASIC Compiler - Version 0.24.0 (07-30-2012) for win32
I worry about updating as then maybe my current code will fail to compile.
BasicCoder2 please please get the last update, it works fine. You won't be able to start any object programing below 0.90 fbc version.

However, as Grindstone has pointed out you can have many versions living side by side. If you don't want to bother changing compiler path in the menu of your IDE, just do like me, have many copies of your IDE and get each one dedicated to one version. I have even set each time a different background color, say ligth green for fbc 0.24, ligth blue for 1.03 and so on, which proves to be very convenient.
sancho2 wrote:My version is incomplete.
Nice reshape anyway.
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: retro text box array

Post by dodicat »

Yea, OK Basiccoder2, nice job.
---
I keep a special copy of fb24 on my desktop, just so I can check quickly if Basiccoder is able to run it.
(Sometimes I forget, but not often)
I also mainly use screen 19 because I believe that basiccoder2's laptop is set to this (maybe silghtly bigger, but not much)

But this is Win XP, I believe that Win *8 and the likes have some folders private and it is more difficult to save compiler copies or fbide copies on the desktop.
Otherwise why on Earth would Basiccoder2 stay with FB24??
BasicCoder2
Posts: 3906
Joined: Jan 01, 2009 7:03
Location: Australia

Re: retro text box array

Post by BasicCoder2 »

@dodicat,
Wow you go to all that trouble just so I can run one of your program snippets!
Actually I never use Screen or the old QBASIC palette numbers instead I like Screenres and 32 bit color with rgb(r,g,b).

The full screen resolution of the current laptop is 1366 x 768
The biggest window I would use is 1340 x 620 otherwise the bottom part vanishes beneath the task bar.

The reason I stay with FB24 is because my programs run fine and I fear I will mess things up if I update. Although I understand the basics of using a class in c++ it is unlikely I will ever bother learning to do likewise with the FreeBASIC's class implementation which is all an update would offer.

I also have a Window8 laptop with a touch screen and had thought of installing the latest FreeBASIC on that machine to run other people's FreeBASIC code. It is my fear that more than one version of FreeBASIC on the same computer will clash. I start FBIDE from the task bar so which version will fire up? There are many details about file paths and so on I have never really got my head around so I play it safe.

The bottom line is I am an old time QBASIC, Assembler, C self taught hobbyist programmer who essentially writes very simple program snippets not actual working functional full blown useful programs. I run all my FreeBasic program snippets from the FBIDE as I did with the QBASIC IDE, usually as a 640 x 480 window.

With regards to the second laptop I have no internet protection apart from window's defender is that sufficient? I tried to install Kaspersky but they insisted on having my phone number.

.
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: retro text box array

Post by MrSwiss »

BasicCoder2 wrote:The reason I stay with FB24 is because my programs run fine and I fear I will mess things up if I update. Although I understand the basics of using a class in c++ it is unlikely I will ever bother learning to do likewise with the FreeBASIC's class implementation which is all an update would offer.

I also have a Window8 laptop with a touch screen and had thought of installing the latest FreeBASIC on that machine to run other people's FreeBASIC code. It is my fear that more than one version of FreeBASIC on the same computer will clash. I start FBIDE from the task bar so which version will fire up? There are many details about file paths and so on I have never really got my head around so I play it safe.
There is absolutely no problem on having "incountable" numbers of FBC's on the same machine:
I have: 0.24.0, 0.90.1, 1.02.1, 1.03.0 (all 32bit, WIN) and
1.02.1, 1.03.0 (both 64bit, WIN) and
1.04.0 Dev. Ver. (32/64bit WIN), more probably later ...
FBEdit 1x directed at the current production compiler 32bit.
But compiling on the CLI with various other Versions ... no problem at all.
You just have to have a structure which is easy to follow like:
  • (drive):\FreeBASIC\FB0240\
    (drive):\FreeBASIC\FB0901\
    (drive):\FreeBASIC\FB1021\32bit
    (drive):\FreeBASIC\FB1021\64bit
    (drive):\FreeBASIC\FB1030\32bit
    (drive):\FreeBASIC\FB1030\64bit
    (drive):\FreeBASIC\FB-DEV\32bit
    (drive):\FreeBASIC\FB-DEV\64bit
Since the version of the DEV version changes with every release, NO ver. info there.
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: retro text box array

Post by dodicat »

Of course you need to download the .zip file, not a self installing .exe.

I use Winrar to expand.
I put the .zip into the folder I choose, right click the .zip and use the extract here option.
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: retro text box array

Post by MrSwiss »

dodicat wrote:Of course you need to download the .zip file, not a self installing .exe.
A very good addition, forgot to mention that.

I usually download the .7z Version and expand with 7Zip ...
Post Reply