Page History: A Simple GUI pack
Compare Page Revisions
Page Revision: 2021/03/21 16:46
Article incomplete
A Universal, Simple GUI pack
The larger MicroMites and ARMMites have a built –in GUI that allows your code to have an interactive LCD panel with all the usual gadgets (buttons, check boxes etc.) that we have become used to in the last 20 years. The MicroMite Mk2 is constrained in that there simply isn’t room in flash to add such luxuries to the firmware for these small but powerful microcontrollers. The following is a pure MMBasic solution so it can be easily deployed on any ‘Mite platform and means code using the GUI becomes platform independent.
Conventions
Throughout this article, things that get put on the screen using this GUI pack are called “Gadgets” (I was an Amiga boy – sue me). In the GUI software pack, anywhere you see “Obj” (short for object) it is the same as a gadget in this article – the two terms are interchangeable here.
Variable and Constant name considerations¶
- Starting with a capital are global (defined in the main body of the program).
- Constants start with upper case.
- Local variable names start with lower case (defined inside a Sub or Function).
- All arrays must start at 0.
Background
Many of my own projects use the small Mk2 and have been, in the past, console only due to the pain of making a good interactive screen (on an LCD panel). In later projects I have been building my own GUIs based very strongly around a WindowsCE/95 look-a-like. I just happen to think the nice clean lines of the GUI with its default grey background and relief style buttons etc is so much nicer that the modern “Metro” or flat look of Windows 10.
Not having a GUI subsystem, any such project that has buttons etc. Had to be built from the ground up each time and were specifically managed with fixed co-ordinates and code to handle the presses of each button. It was a very inflexible solution. Modifications were a nightmare because the co-ordinates where specifically tested for presses etc.
It had been on the back-burner of my mind to build (in MMBasic) a complete subsystem that got rid of that and just let me design the GUI then concentrate on the application code and not have to worry about managing the screen a-fresh each time. Other people had shown an interest in the displays on some projects and it was a bit embarrassing to reveal it was bespoke each time. The following is the fruits of my labours. It is currently about 12K in size – which might come across as a lot of space at first glance, but when you consider I would often have 5 or 6K of very inflexible code to handle a few buttons, it isn’t that much of a sacrifice. You could trim it down to get rid of the bits you don’t want if size becomes a problem - as you will see it provides quite an array of gadgets. Even drop-down lists (eventually)!
A problem with driving LCD panels from MMBasic on smaller beasts is the one-directional nature of the communication with the LCD panel... you can throw stuff on them, but it is hard work (and slow in Basic) to pull back what is on the screen and buffer it so you can repair “damage”. In my approach here (and confession: I have stolen a few ideas from my years as a VB programmer) is structured items in known places that can be quickly re-drawn when scribbled over. An example here is the text box. At first thought you might wonder why you would use it instead of simply throwing text on the panel. If you now think of a drop down menu drawing all over the screen contents, unless you know what it has damaged, how do you repair your lovely GUI? With simple Text, that isn’t possible. With a structured text box, the list or pop-up can tell which items it has drawn over and so when it goes away, it can easily initiate re-drawing just those items.
Approach
Two shared arrays hold the attributes of each gadget; its type, its place on the screen, the colours, its properties etc. Each item has a numerical “name” that can be whatever you like – you can apply structure to your gadgets... you could even have several “layers” of them and simply switch them on and off as you see fit making context sensitive “screens” a breeze. The limit to the number of gadgets is available RAM. The values stored here are then used to draw the gadgets using the in-built primitives of MMBasic (Box, Line, Text etc...)
Stuff you need to know (in no particular order)
- Not all of this is working yet. Gadgets that have yet to be finished are clearly shown.
- There are nine different gadget types – we’ll discuss them
all in detail further on:- Button, Radio, CheckBox, Slider, TextBox, List Static, List DropDown, Progress Bar, Frame
- Every gadget has a set of attributes which are defined in the preamble of your code – actually that is just a convenient place to put them; you can actually define and redefine all aspects of a gadget at anytime. A function, GUIObjDef will format them and correctly fill the O() array to keep track without any further intervention by your own program.
- Every gadget has properties that you can read and write really easily to influence its operation and find out what happened with it.
- After gadgets are defined and properties set, nothing happens on screen until you issue a Draw command for a gadget (an exception to this is animations when you touch certain types of gadget).
- All gadgets are touchable and you can find out which really easily. Even gadgets that don’t generally do anything when touched (like Progress Bars) will report back their number if you touch them. It’s entirely down to you how you “run” your GUI.
- Multiple fonts are not directly supported; if you set the font when you define affected gadgets and when you draw them, it may work. It will come but I just need to get it all working first.
Actually doing it – Getting usable Gadgets on the screen
There is some unavoidable setup required in the preamble of your program besides simply placing the GUI pack in your program. The following fragment of code should be placed near the start and executed before your program gets underway with its main tasks.
Preamble
'{mandatory config
Option Base 0
Const CGy=&hdddddd'Pale grey screen background
Dim Integer Objs=20,P=0,Xt,Yt
Dim Integer O(Objs,10)'GUI object settings
Dim String Ot(Objs) Length 64'GUI object text
Colour 0,CGy:Cls
'}
- A constant is defined for the background colour.
- Some global variables are defined:
- Objs is the total number of gadgets usable, increase or decrease as your application requires.
- P is the pointer to the next available gadget number – this is an internal number and points to a position in the array – it is not your object ID.
- Xt & Yt are the co-ordinates of touches detected on the LCD panel
- O() is a two-dimensional array that holds the specific properties of each gadget e.g. its type, its place on the screen and is state etc.
- Ot() is a single dimensioned string array which holds any text associated with a gadget. Not all gadgets have a text property.
- Finally we clear the panel to the default state of black text of the grey background
All objects are described to the GUI system using the
GUIObjectDef() function. This handles all types of gadget.
Key Functions and Subs¶
Functions return interesting values and are signified by a preceding =
GUIObjDef(a,b,c,d,e,f,g,h,i,j,k)Has 11 arguments, some of which are optional and others which are redundant (meaningless in the context).
Returns -1 if there is no space to store the gadget attributes (increase
Objs in the preamble). Other values can be ignored.
Argument | Attribute | Definition |
---|
a | ID | The numeric name your program wants to use for this gadget. You define this, if you provide the same ID to multiple gadgets, the definition will be over-written. The ID allows you to group your gadgets sensibly. This is not the internal reference for the gadget. |
b | Type | The type number of the gadget - see Gadgets and their Attributes below. |
c | X | The X & Y co-ordinates of the top left corner of the gadget on screen. These are given in “native” LCD panel co-ordinates i.e. 0,0 is the top left corner of the screen and 219,239 (for example) is the bottom right corner. |
d | Y | |
f | W | The width and height of the gadget in pixels which go on to define the bottom right corner of the gadget. Remember these are relative values to the X & Y – they are not physical co-ordinates themselves. |
g | H | |
h | Text | Any text which should be displayed with the gadget. This might be the word inside a button, the text in a textbox etc... Not all types of gadgets will use this attribute. |
i | Type Specific | Optional. This value is interpreted depending on the type of gadget. Consult the relevant section for the gadget type below |
j | Type Specific | Optional. This value is interpreted depending on the type of gadget. Consult the relevant section for the gadget type below |
k | Type Specific | Optional. This value is interpreted depending on the type of gadget. Consult the relevant section for the gadget type below |
Each of the arguments is mapped generally onto the O(n,1 to 10) elements, with some processing where required.
Examples:
A simple Save button
=GUIObjDef(21,1,10,10,80,30,"Save",,,1)Red RadioButton in group 4800
=GUIObjDef(40,2,170,100,0,0,"Red",4800,&hff0000)Blue progress bar ranged 0-5000
=GUIObjDef(61,8,10,210,300,20,"",0,5000,&h0000ff02)GUIObjPSet(a,b)Sets the Properties of the gadget (these are values that are useful to the functions of your program).
Has two arguments:
Argument | Attribute | Definition |
---|
a | ID | The numeric name your program wants to use for this gadget. You define this, if you provide the same ID to multiple gadgets, the definition will be over-written. The ID allows you to group your gadgets sensibly. This is not the internal reference for the gadget. |
b | status byte | Value to set in the status. This is a bit-significant value. The meaning of each bit is as follows |
The Properties of the gadget (these are values that are useful to the running of your code) are stored on O(n,0). This status byte is a bit-field.
Important: Some the properties require
GUIObjDraw() to see.
Status bit meanings (when set)
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|
| Reserved | CleanUp | Set | Touched | Visible | Enabled | Reserved | Reserved |
Note: All reserved bits should be set to zero
- 2: Enable. Says whether the gadget is active or “ghosted”. Disabled (bit 2=0) gadgets are visible on the screen (if bit 3=1) but do not respond to touches. When touched the touch processor will continue to return -1 as if the gadget were not there.
- 3: Visible. The gadget is displayed. If bit 3=0 the, when gadgets will have a grey box drawn over it to erase it from the screen
- 4: The object has been touched. If the object is not enable or not visible (bits 2 or 3=0), this bit is not set by the touch processor. This bit must be cleared by your code. Use of this bit can be avoided if you are polling for touched by regular calls to the touch process Sub, i.e. in the main loop of your program. If however you are using interrupts (maybe your code spends long periods away from the main loop), this bit records touches for later use(Warning: the order in which touches occurred cannot be determined).
- 5: Records the value of a gadget if applicable. Mainly used for Checkboxes and RadioButtons e.g. the state of a checkbox is: checked=1, not checked=0.
- 6: Indicates the gadget may have been damaged by other gadgets and should be re-drawn as soon as possible. For drop-down lists, any gadgets displayed below the top line of the list will be over-written when the menu drops over them. Such gadgets will have bit 6 set in their status. The menu close sub will largely take care of this automatically.
Examples:
Our save Button from above is visible and enabled
=GUIObjPSet(21,&b00001100)Our red RadioButton is disabled
=GUIObjPSet(40,&b00001000)For flipping individual bits, here are two examples using a combination of
GUIObjPGet and
GUIObjPSet...
... to enable a gadget
Result=GUIObjPSet(myObj,GUIObjPGet(myObj) OR &b00000100)... to disable a gadget
Result=GUIObjPSet(myObj,GUIObjPGet(myObj) AND&b11111011)=GUIObjPGet(ID)Returns the Properties of the gadget. See above for the meaning of each bit. The single argument is the numeric name ID assigned to the gadget by your program. The meaning of the bits in the returned value are as shown in
GUIObjPSet() above.
Example:
Has our save button been touched?
=GUIObjPGet(21) AND &b00010000A non-zero result says yes, otherwise no. As an aside, there are two approaches for detecting touches which are discussed in the section “Interrupt or not?” below
The Gadgets and their Attributes¶
We have discussed the functions to get gadgets on the screen, what their properties mean and how to manipulate them. There now follows a detailed discussion of each gadget type.
Buttons
Buttons are the “traditional” command button. Rectangular, bordered areas (it is there even if you don’t show it) of the screen. The entire area is active, i.e. it causes an action if you click anywhere inside it. Usually there is some text which is displayed centrally both horizontally and vertically. It is usual for the border to have a raised appearance and to be animated to a depressed appearance when clicked, returning to its former when released.
GUIObjDef Attributes |
|
ID |
The numeric name your program will use for this Button |
Type |
1 |
X |
Location co-ordinates on screen X,Y |
Y |
W |
Dimensions Width and Height |
H |
Text |
Button text |
<null> |
Argument is ignored |
<null> |
Argument is ignored |
Border Type |
Usually 1, consult the section on border types for options. |
Buttons do not have numeric values that can be altered.
GUIObjValue |
|
ID |
The numeric name your program uses for this Button |
b |
Argument is ignored |
b |
Argument is ignored |
c |
Argument is ignored |
To change the text of a Frame, set the Ot(n) value directly
Example:
Ot(GUIFindObj(id))=”New Text”GUIObjDraw idRadioButtons
RadioButtons are an option button. A circular area bounded by a circular border in relief. Once activated, they can only be de-activated by touching another RadioButton in the same group. Its state can be set or unset by program control manipulating the SET bit in the gadget’s properties. Usually there is some text which is displayed to the right of the RadioButton. The entire area is active – including the text, i.e. it causes an action if you click anywhere on the button or its label. This makes it easier to activate on smaller displays as precise touch is not so critical. An activated RadioButton has a depressed circular appearance. A de-activated RadioButton has a raised circular appearance. Any change is animated when touched or under program control.
The Height of a RadioButton is fixed at 11 pixels, Width is determined by the length of the text+15 pixels.
GUIObjDef Attributes |
|
ID |
The numeric name your program will use for this button |
Type |
2 |
X |
Location on screen X,Y |
Y |
<null> |
Argument is ignored |
<null> |
Argument is ignored |
Text |
RadioButton text |
Group |
The group to which this button belongs. Buttons in the same group are affected by the operation of their members e.g. activating a button will deactivate all others in the group. |
Colour |
The colour for the central “dot” of the button |
<null> |
Argument is ignored |
RadioButtons do not have numeric values that can be altered.
GUIObjValue |
|
ID |
The numeric name your program uses for this RadioButton |
b |
Argument is ignored |
b |
Argument is ignored |
c |
Argument is ignored |
To change the text of a Frame, set the Ot(n) value directly
Example:
Ot(GUIFindObj(id))=”New Text” GUIObjDraw(id)CheckBoxes
Checkboxes are a toggling option button. A small area bounded by a square border in relief. Once activated, they can be de-activated by touching again and vice versa. Its state can be set or unset by program control manipulating the SET bit in the gadget’s properties. Usually there is some text which is displayed to the right of the Checkbox. The entire area is active – including the text, i.e. it causes an action if you click anywhere on the Checkboxes or its label. This makes it easier to activate on smaller displays as precise touch is not so critical. An activated Checkbox has a depressed circular appearance. A de-activated Checkbox has a raised circular appearance. Any change is animated when touched or under program control.
The Height of a Checkbox is fixed at 11 pixels, Width is determined by the length of the text+15 pixels.
GUIObjDef Attributes |
|
ID |
The numeric name your program will use for this Checkbox |
Type |
3 |
X |
Location on screen X,Y |
Y |
<null> |
Argument is ignored |
<null> |
Argument is ignored |
Text |
Checkbox text |
<null> |
Argument is ignored |
<null> |
Argument is ignored |
<null> |
Argument is ignored |
CheckBoxes do not have numeric values that can be altered.
GUIObjValue |
|
ID |
The numeric name your program uses for this Checkbox |
b |
Argument is ignored |
b |
Argument is ignored |
c |
Argument is ignored |
To change the text of a Frame, set the Ot(n) value directly
Example:
Ot(GUIFindObj(id))=”New Text” GUIObjDraw idFrames
A simple rectangular box with a text label. Used for grouping related gadgets more for aesthetics than functionality. Beware defining Frames before the gadgets they contain. They can prevent gadgets from being identified when touched in favour of the enclosing frame. Best to places gadgets on the screen then place Frames last of all.
Unlike desktop GUIs, disabling a frame will not automatically disable all items within it.
GUIObjDef Attributes |
|
ID |
The numeric name your program will use for this Frame |
Type |
9 |
X |
Location co-ordinates on screen X,Y |
Y |
W |
Dimensions Width and Height |
H |
Text |
Frame text |
<null> |
Argument is ignored |
<null> |
Argument is ignored |
Border Type |
Usually 5, consult the section on border types for options. |
Frames do not have numeric values that can be altered.
GUIObjValue |
|
ID |
The numeric name your program uses for this Frame |
b |
Argument is ignored |
b |
Argument is ignored |
c |
Argument is ignored |
To change the text of a Frame, set the Ot(n) value directly
Example:
Ot(GUIFindObj(id))=”New Text” GUIObjDraw idTextBoxes
A rectangular area in which text is written. May have any style of border
GUIObjDef Attributes |
|
ID |
The numeric name your program will use for this TextBox |
Type |
5 |
X |
Location co-ordinates on screen X,Y |
Y |
W |
Dimensions Width and Height |
H |
Text |
Frame text |
Background colour (paper) |
Background colour as &hRRGGBB |
Foreground colour (pen) |
Foreground colour as &hRRGGBB |
Border Type and Alignment |
Border type and Alignment are stuffed in a single byte. Consult the section on border types for options. |
TextBoxes do not have numeric values that can be altered.
GUIObjValue |
|
ID |
The numeric name your program uses for this TextBox |
b |
Argument is ignored |
b |
Argument is ignored |
c |
Argument is ignored |
To change the text of a TextBoxes, set the Ot(n) value directly
Example:
Ot(GUIFindObj(id))=”New Text” GUIObjDraw idProgress bars
A progress bar is a rectangular area with increasing/decreasing coverage depending on the current set value. The minimum and maximum extremes of the range are settable to any value and the physical size of the bar on the screen will be scaled accordingly. The border of the bar may be set to any frame style although the depressed button appearance is most usual.
Progress bars may be horizontal or vertical. They always progress low to high as left to right or bottom to top respectively
GUIObjDef Attributes |
|
ID |
The numeric name your program will use for this button |
Type |
8 |
X |
Location co-ordinates on screen X,Y |
Y |
W |
Dimensions Width and Height |
H |
<null> |
Progress bars do not have a text attribute, Set to "" |
Bottom of range |
>Any numeric value < top of range |
Top of range |
Any numeric value > bottom of range |
Colour, Border Type and Alignment |
Border type and Alignment are stuffed in a single byte. Consult the section on border types for options. |
ProgressBar numeric values that can be altered.
GUIObjValue |
|
ID |
The numeric name your program uses for this ProgressBar |
b |
Min value |
b |
Max Value |
c |
Currrent value |
Note: the current value is constrained when set
Sliders
A moving button that can be slid up and down a scale to indicate a value. style='color:red'> The minimum and maximum extremes of the range are settable to any value and the physical size of the slider on the screen scales accordingly.
GUIObjDef Attributes |
|
ID |
The numeric name your program will use for this button |
Type |
4 |
X |
Location co-ordinates on screen X,Y |
Y |
<null> |
Argument is ignored |
<null> |
Argument is ignored |
<null> |
Sliders do not have a text attribute, Set to "" |
Bottom of range |
Any numeric value < top of range |
Top of range |
Any numeric value > bottom of range |
Colour, Border Type and Alignment |
Border type and Alignment are stuffed in a single byte. Consult the section on border types for options. |
Slider numeric values that can be altered.
GUIObjValue |
|
ID |
The numeric name your program uses for this Slider |
b |
Min value |
b |
Max Value |
c |
Current value |
Note: the current value is constrained when set
List Static
Similar in appearance to the text box, the Static list has direction arrows at each end and the list can be navigated through with the current selected item displaying in the text area.
GUIObjDef Attributes |
|
ID |
The numeric name your program will use for this button |
Type |
6 |
X |
Location co-ordinates on screen X,Y |
Y |
W |
Dimensions Width and Height |
H |
Text |
Frame text |
Background colour (paper) |
Background colour as &hRRGGBB |
Foreground colour (pen) |
Foreground colour as &hRRGGBB |
Border Type and Alignment |
Border type and Alignment are stuffed in a single byte. Consult the section on border types for options. |
List Drop-Down
Similar in appearance to the text box, the Drop-down list displays a rectangular area containing lines of text when touched. An item may be selected by touching it. If more items exist than the box can contain, the up and down arrows on the right of the drop-down can be used to navigate through the list. When an item is selected, the drop-down box is erased, the text is set to the selected item and any damaged items (where the drop-down obliterated them) are set for cleanup.
GUIObjDef Attributes |
|
ID |
The numeric name your program will use for this button |
Type |
7 |
X |
Location co-ordinates on screen X,Y |
Y |
W |
Dimensions Width and Height |
H |
Text |
Frame text |
Background colour (paper) |
Background colour as &hRRGGBB |
Foreground colour (pen) |
Foreground colour as &hRRGGBB |
Border Type and Alignment |
Border type and Alignment are stuffed in a single byte. Consult the section on border types for options. |