How to make my first GUI Application?

As you learned the basics bringing output to the console window and retrieving input from the user it would be nicer if the user has the abilities of buttons and scrollbars to control and you probably don't really understand how to get those in the console window.

This is the case where you take the GUI designer and just forget about the purpose of the console window.

What is the GUI designer?
The GUI designer is NOT the same as the Graphic designer!
The XBasic documentation treats both cases seperately so it's a good suggestion to read through this documentation when you're done with this chapter.

The Graphics Designer represent the core XBasic graphic support functions that allow you to load and manipulate images but also to draw lines and circles.
These functions are merely similar to the basic drawing-sets you can find in older Basic languages like Q(uick)Basic.

The GUI Designer allows you to quickly design your windows and paste buttons and labels into it to you needs, you can change the layouts and the styles of the components like buttons or checkboxes you paste on the window.
The GUI Designer is similar to the Form-editor that you can find in Visual Basic.


How do I start?

First create a new source environment by pressing this icon:
Then select GUI Program from the Dialogue that follows:

This time you start in the PROLOG window and when you click on the function browser you notice that the program already contains a lot of new functions and while examining the existing functions you also notice they already contain primary code.

This is all to create the primary environment necessary for your GUI program to work in.
Please don't change anything in this configuration unless you absolutely need to.

Let's start to design our first window:

To open the Gui-designer toolbox click on the hammer icon in the buttonmenu of the PDE window:

This will display the following toolbox:


The toolbox is described in the HTML docs of XBasic but you can also reference this page

Now select menu "Window" option "New":





An empty window will be displayed on the screen:


 

Here you can add checkboxes, option buttons, textarea and many more features like you easily could do in Visual Basic.

Now click on this toolbox icon:
Notice a small frame displaying at the corner of the window:
This frame you can move around in the window to the location you want it to have inside the "grid" and you can resize the frame to any proportion to feel like.

Let's see if you can get this frame into this shape and position of the grid:

Okay, now select the grid of the window outside the frame area.
It will now show you a gray empty label and a report-window pops up.

You can ignore the report-window for now, I will get back to this later.
Double click the label and when you did that the following window pops up:

You notice a lot of bells and whistles, a short overview of what each part does:

Further more next below the colour palettes, you notice numbers and frame styles, these imply buttons, labels etc. just experiment with them to see what happens.

Good, let's put some text in the label by enter the following behind the Textstring:

 

Notice what happens to the label as soon as you hit the "Enter" key in the textfield:

 

This looks great! now let's take care that we show this stuff alive...
Go to the toolbox and give the window a name:

 

Now select menu "Window" and option "ToFunction":


Now notice where you end up in as soon as you choose that option:

You are now in the functional part of your GUI window.
The GUI designer created two functions with the items you used from the toolkit:
Helloworld() and HelloworldCode()
This is a structural basic of the toolkit and always uses the name you assign to it plus the name and a suffix called Code.
Also as long as you need to add or change things in your window design best thing is to keep your hands of the function() that carries the name you supplied and just add the functionality to the HelloworldCode instead where you are situated now.

For each button you press, messages are sent to the HelloworldCode (there are no buttons yet but in case) so best place to deal with such matters is in the HelloworldCode() function. The Helloworld() function creates your window from zero, it is being done in the CreateWindows() function where the initial calls are placed thanks to the GUIdesigner "ToFunction" option.

Now close the Guidesigner by pressing the close button in the window or just pressing the hammer icon again:

As soon as you do that you notice that your design grid closes as well. The only window that  might remain is probably the report-window. You can also close this now.
 
Good, now press the F1 key or select .

You see your fresh designed window popping up (the grid points are now disappeared) as also again a new report message window:

 


Close your application by hitting the "F4" key or selecting this toolmenu-icon:
The "close" button on the right corner of the window does not work since you have to assign a quit function to the message that sends a "CloseWindow" code back to your functionCode().

 The report message window above at the right is an implemented background tool that is there to support you in providing valuable information in the messages that are being sent to your HelloworldCode() function as well as the arguments that are being sent.
You can read the status of toggle-buttons, checkboxes, option buttons, key-codes of pressed key(-combination)s etc.

If you compile your application to an end-user product, this report window does not disappear if you don't remark the line that enables this.
This is an important thing to know:


The black highlighted line takes care that this report message window pops up. To keep it hidden just put a comment quote in front of it and the window won't appear anymore when running the application. You do have to close it if it popped up earlier ofcourse.
 
You also notice that the GUI-designer translated your gridnames to constant keys containing gridnumbers.
$Helloworld is grid 0 (which is the window background grid)
$XuiLabel716 is grid 1 (or kid 1) that displays your label.

NOTE:
The numbers may differ since they are sort of random generated to it so don't compare the local constants too much with what you see here, as long as $XuiLabel, $XuiTextLine and $XuiPushButton will be the prefixes of those numbers you are still completing your mission correctly.



Below this you see a selection situation "SELECT CASE message".
This is where the message is being examined on it's value.
Each value is predefined in the InitGui() function you will find in your program. These values were all initiated during the creation of your GUI-program after you selected the "New" "GUI-Program" options at the beginning.

Now a selection applies to everything that has a response to a mouse-button such as buttons, checkboxes, radio-buttons and so on.

 If you would scroll down in your HelloworldCode function you would notice the SUB selection:

If $XuiLabel716 would have been a button the program would have jumped to this portion of the case simply because it compares the kid where you clicked your mouse on.

A label does not respond on user input so in this case the SELECT CASE is worthless.

Let's just make one small change in the program to make this SELECT CASE a bit more worth-full;


Bringing back your window-code to the designing table.
Either select the Helloworld() OR the HelloworldCode() function, it does not matter which of the two but it counts that you have the focus on either one of both functions that represent a GUI window you designed in the toolkit so that you can transfer the contents back to the GUI designer.

Now open the toolkit again by selecting the hammer-icon:

Select menu "Window" option "FromFunction"

Note:XBasic adds a history of created windows in the Guidesigner below the "CloseToolkit" option. If you accidently close the toolkit while your design is still open, reopen it and double click (one of) the history designed windows (1:MyWindow) it should redisplay your window and your configured grids. I don't know it's maximum history windows but after 43 the list ends up below the bottom of my windows' desktop.
 

If you did not altered any data in the Helloworld() function everything should translate back fine and the window should again display with the grid-dots around the label.

However if you get an error most likely you have inserted blank lines or removed valuable data necessary for the GUI designer to get back your window-code into the designer table from the window function() (in this case Helloworld()).

For this reason it is best not to touch anything in this Helloworld() function unless you know for sure you don't want to add or change things to it visually anymore and necessary changes are required that can not be done by the GUI designer (like changing font sets of drop-down boxes, lists and menus).

Now open back the appearance window by double-clicking the label and remove the "Hello world!" string from the text-field.

 Then go to the toolkit and click the following option:

You will notice a new frame in the corner, now move this one below the label and stretch it, then select the main grid which lies under the textfield and the label and compare your window if it will look like this:


 Go back to the toolkit and select the following icon:

Grab and drag the frame below the textline and let it look like this:

 

Now double click this button so that the appearance window shows up and enter the words "Print my stuff!" in the textstring then hit the "Enter" key:


Good, if you did everything right you will now see your change like it shows at the right picture above.
Select in the toolkit the menu "Window" option "ToFunction".

Now the code already exists and you will notice two dialogues popping up:

 Now when you choose for "Update", new code which requires the new changes will be added to the function leaving the other stuff that was already there for what it was.
When you choose "Replace" you paste new data across all your existing code in the function. Everything you added previously by hand will be lost.

Mostly in case of function() you can select replace, but most of your process-work will be done in the functionCode() and it would be a waste of time if you would replace all your added code by a new empty window-data so best is you select "Update".

In our case here we have not done anything in this functionCode() (which is HelloworldCode() in our case) so we can also choose for "Replace" this time.

If you do an update, the new kid-numbers will be added but not the kid-selection options in the subroutines.


Starting to add functionality to your GUI window
It's time to add some code to your function, if everything went fine you ended up back in the HelloworldCode() function.
Close the toolkit ()

As you examine the function you notice two new kids in the local constant declaration area;
Scroll down to the SUB Selection where you can also see that the new objects have been integrated in the CASE selection area.

Now add the following three lines under the $XuiPushButton:

Remember, the numbers behind the constant-names may differ, don't copy the above constants if they are not the same, use the constant-names XBasic generated in your situation instead.

Above all XuiSendMessage functions are used to process your window actions.
The "#GetTextString" message will take care that XBasic retrieves the text that is entered in the text-field "$XuiTextLine717" and stores it into a string called "MyInput$"
The "MyInput$" has to be passed by reference so that XBasic know to which memory address it has to transport the text where your string is located so that's why it's prefixed with a "@" sign.

The "#SetTextString" on the other hand will place the contents of the string "MyInput$" to the label "$XuiLabel716".If you want the routine to print something on your screen you better take care that the string you sent to the label is filled with data.

The "#Redraw" message is very important in most cases, it takes care that it will make the changes visible that you or the user did in the window. If you forget the "#Redraw" message you won't notice any changes unless you Eg. minimize the window and bring it back to front again or you hide and display it again.

In cases like droplists or text-areas you can even use a "#RedrawText" message which takes care only the area the text is placed in is refreshed without the shocky effects of the scrollbars being redrawn all the time.
For a label this "#RedrawText" won't work because a label can also contain images or serve as a parent kid for things like a new set of radio buttons next to an existing set of radio buttons.
(to be honest, everything on a label is actually drawn and not printed!)


 Good, now run your program, type some text in the textline then push on the button and notice what happens...



Using text-events

It's nice that when you push the button, the text contents appear in the label. But it would probably be nicer if it would appear there if you just hit the enter key?

For this you need to change a couple of things in the code.

Just stop your application from running by "F4" or
Go to the top of the HelloworldCode() function where the "SELECT CASE  message" is located;
Remove the comment quote that remarks the "CASE #TextEvent"

Well, this TextEvent subroutine does not yet exist so we have to add it, go the bottom of your source and add the following under the selection subroutine:

You can copy and paste the complete "SELECT CASE kid " routine and move the processing code from underneath the $XuiPushButton underneath the $XuiTextLine case.

The "IF v0 == 0x0D10000D" condition comes from the information I drew from the Report-message window that runs in the background:

 

The above left example is not really correct since a text-event message involves more arguments but I was not capable of quickly snapshot the window that displays the keycode since after an "Enter" event, a Selection event will be triggered which masks the "Enter" text-event values. If you closely look at the example on the right, you notice that v0 to v3 contain values.
The XB documents explain that when a #TextEvent message is sent, v2 will contain the keycode for the key(s) that is (are) pressed..
In this case it will be "0xD10000D"since that code represents the "Enter" key.

By taking the hexadecimal values you are capable of using them in your code to instantly compare the value as is presented which is far easier then trying to remember the decimal value of the keycode "219152397".
 
This is also nice to know if you want to convert Hexadecimal input to decimal or backwards:
In Visual Basic or Q(uick)Basic you are required to define the data-type of the input field that you want to get from the user.
If you would insert something like "&Hff" (hex type-prefix for VB/QB) in a textstring field, both basics will return an error while XBasic just translates "0xff" to any other value you want it to when entered, this flexability of XBasic makes it a pretty elastic programming environment.
 
Okay, back to the story, when running your program by hitting "F1" or

Enter some stuff in the text-field and instead of clicking the button, hit the "Enter" key.

 

Adding new message interceptors

I can understand that it would be nice if you could do more than only intercept selection messages and text events.

Stop your application and just add the following code to the HelloworldCode() function:

 
Now run the program again and just click on the "Close" button in the upper right corner.
Goody, the program ends by user input.

There might be cases where you don't want to quit the program but just close the window.
Instead of using QUIT(0) you can enter the following simple line:
XuiSendMessage (grid, #HideWindow,0,0,0,0,0,0)

Now your window will be hidden ("closed").

If you use a message like "#DestroyWindow", your window and all of it's contents are really destroyed and you can't retrieve data from it neither can you redisplay it unless you recreate it so it's better to choose for "#HideWindow" and "#DisplayWindow" instead.

 Advanced coding methods
Okay, you got the hang of it now, let's do something more advanced.
Further back above when describing the "Image" button of the "Appearance window" I mentioned that you could instantly load and display a bitmap image on a label or button etc.

I'm going to show you another possible way how you can do this without needing to use the GUI-designer.

add the following few changes to the already existing code in the HelloworldCode() function:
        $Helloworld        =   0  ' kid   0 grid type = Helloworld
        $XuiLabel716       =   1  ' kid   1 grid type = XuiLabel
        $XuiTextLine717    =   2  ' kid   2 grid type = XuiTextLine
        $XuiPushButton719  =   3  ' kid   3 grid type = XuiPushButton
        $UpperKid          =   3  ' kid maximum
        UBYTE MyImage[]  'Define a calculation image array so the bitmap image can be read into the array


-Snip-


'
'
' *****  Selection  *****
'
SUB Selection
        SELECT CASE kid
                CASE $Helloworld        :
                CASE $XuiLabel716       :
                CASE $XuiTextLine717    :
                CASE $XuiPushButton719  :
                        XuiSendMessage(grid, #GetTextString,0,0,0,0,$XuiTextLine717,@MyInput$)
                                IF RIGHT$(LCASE$(MyInput$),4) == ".bmp" THEN
                                        XgrLoadImage(MyInput$,@MyImage[])
                                        XgrGetImageArrayInfo (@MyImage[], @bbp, @width, @height)
                                        XuiSendMessage ( grid, #SetImage, 0, 0, 2, 2, $XuiLabel716, @MyInput$)
                                        XuiSendMessage ( grid, #SetImageCoords, 0, 0, width, height, $XuiLabel716, 0)
                             ELSE
                                        XuiSendMessage(grid, #SetTextString,0,0,0,0,$XuiLabel716,MyInput$)
                             END IF
                        XuiSendMessage(grid, #Redraw,0,0,0,0,$XuiLabel716,0)
        END SELECT
END SUB

SUB TextEvent
        SELECT CASE kid
                CASE $Helloworld        :
                CASE $XuiLabel716       :
                CASE $XuiTextLine717    :
                        IF v2 == 0x0D10000D
                                XuiSendMessage(grid, #GetTextString,0,0,0,0,$XuiTextLine717,@MyInput$)
                                IF RIGHT$(LCASE$(MyInput$),4) == ".bmp" THEN
                                        XgrLoadImage(MyInput$,@MyImage[])
                                        XgrGetImageArrayInfo (@MyImage[], @bbp, @width, @height)
                                        XuiSendMessage ( grid, #SetImage, 0, 0, 2, 2, $XuiLabel716, @MyInput$)
                                        XuiSendMessage ( grid, #SetImageCoords, 0, 0, width, height, $XuiLabel716, 0)
                                ELSE
                                       XuiSendMessage(grid, #SetTextString,0,0,0,0,$XuiLabel716,MyInput$)
                                END IF
                                XuiSendMessage(grid, #Redraw,0,0,0,0,$XuiLabel716,0)
                        END IF
                CASE $XuiPushButton719  :
        END SELECT
END SUB

 

 What are the most important changes:

First get the line entered by the user:
 XuiSendMessage(grid, #GetTextString,0,0,0,0,$XuiTextLine717,@MyInput$)

Try to find out if the user entered a location of a bitmap picture:
IF RIGHT$(LCASE$(MyInput$),4) == ".bmp" THEN

Load the image into an array, in this case it's only to get the bitmap's statistics like height and width:
XgrLoadImage(MyInput$,@MyImage[])
XgrGetImageArrayInfo (@MyImage[], @bbp, @width, @height)

Now just load the image again but then paste it straight into the label offsets 2 pixels from the top and two pixels from the right:
XuiSendMessage ( grid, #SetImage, 0, 0, 2, 2, $XuiLabel716, @MyInput$)
XuiSendMessage ( grid, #SetImageCoords, 0, 0, width, height, $XuiLabel716, 0)

If the user did not entered a bitmap location then
ELSE

Just put the text at that spot
XuiSendMessage(grid, #SetTextString,0,0,0,0,$XuiLabel716,MyInput$)


So how can the output look like?


Okay your first GUI application is actually finished, you can feel free to experiment with the styles and colours to change your design to your needs but remember to save your source-files very often during design and when you change your GUI window design, "Replace" the function() (Hellworld()) and "Update" the functionCode() (HelloworldCode()) else all the things we did previously gets lost



 That's nice but....
It's all cool stuff but how did you found out all these XuiSendMessages parts? and how did you found out to print all these images on a label?

Well, that s the part I have to reference to the documents AND examples.
Some of the contents I have grabed from the example "aviewbmp.x" and to use the messages like "#SetImage", I just put a label in the GUI designer form and just added a picture to it, examined the function() to the part where it created the label and loaded the image and I just adapted it to use it in the functionCode()

Above are just a few initiatives you can take to explore XBasic.

XBasic already comes with plenty of examples and I hope for you that this small "how to..." pages contribute to your effort of being able in getting started.

I know that the documents are not complete and I also know that the docs contain a few mistakes.

Mentioning that the "image-array for the bitmap should be dimensioned XLONG" is one of those mistakes, it should be dimensioned UBYTE instead.
But also this mistake I figured out because I figured that out by examining the source code of XBasic itself.

Well this last option is something that you can do as soon as you get a bit more experienced in using XBasic but you can in some cases just draw some examples from it where necessary.