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.