ARTICLE 7 - UI Menu Primer II
by HypoThermia
This second part of the menu primer builds on what we've already learnt about
the user interface ("ui") menu system. It provides a reference to all the controls,
how to use them, and the parameters used to set them up.
There are plenty of examples of how these controls are used,
but you'll get the most benefit from this material if you've also read the first part
(UI Menu Primer I) and had a quick look at some of
the menu source code.
In the final part of this primer we'll take a look at some of the more
advanced things you can do with menus, talk about how to go about designing a menu
from scratch, and make concrete use of the reference material here.
1. Reference
Some of the more advanced menu features require a more detailed
understanding of the menu framework. This reference material will provide us
with the information we need to tackle things like owner drawn controls, and
avoids repetition.
Remember that co-ordinates refer to a 640 by 480 screen, and are automatically
scaled to the true screen resolution when drawn.
1.1 The generic data common to all controls
All controls carry the same set of common data, refered to as the "generic" data.
This data structure stores all the core essential information used to setup and maintain
a control.
Here's the "generic" data structure:
typedef struct
{
int type;
const char *name;
int id;
int x, y;
int left;
int top;
int right;
int bottom;
menuframework_s *parent;
int menuPosition;
unsigned flags;
void (*callback)( void *self, int event );
void (*statusbar)( void *self );
void (*ownerdraw)( void *self );
} menucommon_s;
Not all of these values need to be filled in, but some are essential.
Lets take a look at how these values are used.
generic.type
- The type of control, must be filled in.
generic.name
- A text string used by the control, usually as a label.
Usage specific to the given control type.
generic.id
- A value used to identify the control. Should take
a unique value for each generic.callback function.
generic.x, generic.y
- Position of the control on screen, must be filled in.
generic.left, generic.top, generic.right, generic.bottom
- Bounding box for control, defining when mouse is over the control. Initialized for you
when Menu_AddItem() is called, but must be filled in if QMF_NODEFAULTINIT
flag is set.
generic.parent
- A pointer to the menuframework_s structure that identifies the menu being displayed.
Initialized for you when Menu_AddItem() is called.
generic.menuPosition
- A unique value for the control. Do not set or modify.
generic.flags
- Defines the behaviour of the control, as well as its current state. Must be initialized
before Menu_AddItem() is called. Some values can be modified as the control is used.
generic.callback
- A handling function for implementing the behaviour of a control. Must be
filled in if the control can accept input.
generic.statusbar
- Called if the control wants to draw something when the mouse cursor is
hovering over it.
generic.ownerdraw
- A handling function that allows a custom drawing behaviour for a control. Used
to extend or modify an existing control.
1.2 Control behaviour through generic.flags
These flag values can be used to define both the behaviour of the control,
as well as give information about its current state.
The full list of 21 flags all begin with QMF_ and can be
found in ui_local.h.
The "Usage" column tells you how the flag is used. "Init" is for the permanent
behaviour of the control, and should only be set during initialization.
"State" describes the current (temporary) status of the control, it can
also be set during initialization.
Some flags have an equivalent value that can be used when calling
text or graphics drawing functions, indicated in the "text effect" column. This
means that an owner drawn control doesn't need to implement the effect, it's
already there to be used.
|
generic.flag |
Usage |
Text effect |
Comment |
|
QMF_BLINK |
State |
UI_BLINK |
Text flashes on and off, following a fixed period. There is no
transition between the on and off states. |
|
QMF_SMALLFONT |
Init |
UI_SMALLFONT |
The text contents of the control will be drawn using a smaller screen font.
By default text is drawn using a bigger font, in the text style UI_BIGFONT.
|
|
QMF_LEFT_JUSTIFY |
Init |
UI_LEFT |
Positioning of the control relative to the generic.x
and generic.y co-ordinates. Not all controls respond to
this value.
Default value. Incompatible with QMF_CENTER_JUSTIFY and QMF_RIGHT_JUSTIFY. |
|
QMF_CENTER_JUSTIFY |
Init |
UI_CENTER |
Positioning of the control relative to the generic.x
and generic.y co-ordinates. Not all controls respond to
this value.
Incompatible with QMF_LEFT_JUSTIFY and QMF_RIGHT_JUSTIFY. |
|
QMF_RIGHT_JUSTIFY |
Init |
UI_RIGHT |
Positioning of the control relative to the generic.x
and generic.y co-ordinates. Not all controls respond to
this value.
Incompatible with QMF_LEFT_JUSTIFY and QMF_CENTER_JUSTIFY. |
|
QMF_NUMBERSONLY |
Init |
- |
Used for text input controls; it rejects all non-numbers. |
|
QMF_HIGHLIGHT |
State |
- |
Control should be drawn with a brighter colour, giving it more
attention.
Incompatible with QMF_PULSE. |
|
QMF_HIGHLIGHT_IF_FOCUS |
Init |
- |
Control will be drawn in a brighter colour if it is the currently
selected control (has focus).
Incompatible with QMF_PULSEIFFOCUS |
|
QMF_PULSEIFFOCUS |
Init |
UI_PULSE |
The control will cycle between bright and dim colours when it is the
currently selected control (has focus).
An effective way to show which control is currently accepting
keyboard input and/or under the mouse cursor.
Incompatible with QMF_HIGHLIGHT_IF_FOCUS. |
|
QMF_HASMOUSEFOCUS |
State |
- |
An internal value used to indicate that the mouse cursor is over a control.
Treat this flag as "read only", you should never need to change it.
Use Menu_SetcursorToItem() instead to choose the active control once
they've all been registered. |
|
QMF_NOONOFFTEXT |
- |
- |
Not used. |
|
QMF_MOUSEONLY |
Init |
- |
Prevents the control from being accessed though keyboard
navigation. |
|
QMF_HIDDEN |
State |
- |
Prevents the control from being drawn onscreen.
If you hide a control that can accept input then you must
use QMF_INACTIVE too. |
|
QMF_GRAYED |
State |
- |
The control does not respond to input from the keyboard or mouse, and should be
drawn in a grey colour to indicate this.
Implicitly includes QMF_INACTIVE. |
|
QMF_INACTIVE |
State |
- |
The control does not respond to input from the keyboard or mouse. There
is no change expected in the visual appearance. |
|
QMF_NODEFAULTINIT |
Init |
- |
Prevents internal initialization of a control when Menu_AddItem()
is called.
Use with caution. If you don't provide the full init yourself then you'll get
unexepected behaviour. |
|
QMF_OWNERDRAW |
- |
- |
Not used. Just set generic.ownerdraw. |
|
QMF_PULSE |
State |
UI_PULSE |
Control should be drawn so it cycles between bright and dim colours.
An effective way to show which control is currently accepting
keyboard input and/or under the mouse cursor. |
|
QMF_LOWERCASE |
Init |
- |
Forces upper case keyboard input into lower case letters. |
|
QMF_UPPERCASE |
Init |
- |
Forces lower case keyboard input into upper case letters. |
|
QMF_SILENT |
State |
- |
Prevents a sound effect being played when a control is selected. |
1.3 The text effects
There are only a few text effects that you can use, they're described in
the table below.
These flags are of use when you're using a menutext_s control, or
providing an ownerdraw custom text control.
|
Flag |
Usage |
|
UI_LEFT |
Text is drawn starting at the (x,y) co-ordinates. |
|
UI_CENTER |
Text is drawn so it is symmetric about the (x) co-ordinate. |
|
UI_RIGHT |
Text is drawn so it ends at the (x,y) co-ordinates. |
|
UI_FORMATMASK |
An internal value. Not for use in a text drawing function. |
|
UI_SMALLFONT |
The text is drawn as a fixed width font of size SMALLCHAR_WIDTH (8),
SMALLCHAR_HEIGHT (16). |
|
UI_BIGFONT |
The text is drawn as a fixed width font of size BIGCHAR_WIDTH (16),
BIGCHAR_HEIGHT (16). |
|
UI_GIANTFONT |
The text is drawn as a fixed width font of size GIANTCHAR_WIDTH (32),
GIANTCHAR_HEIGHT (48). |
|
UI_DROPSHADOW |
A black shadow is drawn underneath the text giving it a raised effect. |
|
UI_BLINK |
Text flashes on and off, following a fixed period. There is no
transition between the on and off states. |
|
UI_INVERSE |
Only effective when calling UI_DrawProportionalString(), its usage
appears to have been changed from its "obvious" operation to a
reduction in colour intensity only. |
|
UI_PULSE |
The text will cycle between bright and dim colours. |
2. Reference: The 7 types of control
There are seven types of control available that can just be dropped into
a menu. This reference will tell you what parts of each control you need to
initialize. If you need to setup these controls yourself using QMF_NODEFAULTINIT
then you'll need to understand the remaining values as used by the control.
Each control includes an example from the source code so you can go look at
something that already works.
Remember that the menu controls should have been initialized to zero using a
memset(), so you'll only need to touch the values you need to setup.
If the generic structure contains some unusual setup values for that
particular control then they will be explained, otherwise refer to earlier description
of that structure.
All text controls can use a smaller font by specifying QMF_SMALLFONT
for generic.flags.
2.1 menufield_s (for text input)
This control accepts typed input. If the number of characters input exceeds
the screen width of the control then the text will be scrolled automatically
to make space on the right.
typedef struct
{
menucommon_s generic;
mfield_t field;
} menufield_s;
generic.type
- Set to MTYPE_FIELD
generic.x, generic.y
- The top left corner of the text input part of the control
generic.name
- If set then this descriptive text will be drawn to the left
of the control. Adding this name does not move the control, the text input box
remains in the same place.
The mfield_t structure is given by:
typedef struct {
int cursor;
int scroll;
int widthInChars;
char buffer[MAX_EDIT_LINE];
int maxchars;
} mfield_t;
field.widthInChars
- The size of the text input area (in characters) as drawn on the screen.
Any extra characters input will cause the text to scroll.
Value must be set.
field.maxchars
- The maximum number of characters that can be stored in the buffer.
If this value is not set then MAX_EDIT_LINE (256) is assumed.
field.buffer
- The text typed in by the user is stored here. It is always correctly '\0'
terminated.
This control also accepts the following keyboard input:
|
Ctrl-C |
Clear the control of all text. |
|
Ctrl-V |
Paste text from the Operating System clipboard (Windows, Mac, Linux). |
|
Ctrl-H |
Backspace (deletes character to left of cursor) |
|
Ctrl-A |
Move to first character of text. |
|
Ctrl-E |
Move to last character of text. |
Example: taken from ui_specifyserver.c, this is the text
input for the server address.
s_specifyserver.domain.generic.type = MTYPE_FIELD;
s_specifyserver.domain.generic.name = "Address:";
s_specifyserver.domain.generic.flags =
QMF_PULSEIFFOCUS|QMF_SMALLFONT;
s_specifyserver.domain.generic.x = 206;
s_specifyserver.domain.generic.y = 220;
s_specifyserver.domain.field.widthInChars = 38;
s_specifyserver.domain.field.maxchars = 80;
Back to the list of controls
2.2 menuslider_s (a slider with thumb)
The easiest way to think of this control is like a volume slider, allowing you
to select a value between the maximum and minimum allowed.
The current value is indicated by a button-like widget called a "thumb",
and this thumb can be dragged to select the value.
Any text associated with this control is always drawn using the
UI_SMALLFONT style.
typedef struct
{
menucommon_s generic;
float minvalue;
float maxvalue;
float curvalue;
float range;
} menuslider_s;
generic.type
- Set to MTYPE_SLIDER.
generic.x, generic.y
- The co-ordinates of the slider.
generic.name
- If set then this descriptive text will be drawn to the left
of the slider. Adding this name doesn't move the slider.
minvalue
- The minimum value allowed for the slider. Must be initialized, and
less than maxvalue.
maxvalue
- The maximum value allowed by the slider. Must be initialized.
curvalue
- The current position of the thumb, stores the value of the control.
Must be initialized.
By default the control allows any value between the minimum and maximum,
but it can be constrained through the generic.callback function. When a
QM_ACTIVATED event is generated for the control then the curvalue
can be adjusted.
Keyboard input through the cursor keys increases or decreases
curvalue by one.
Example: taken from ui_sound.c, this shows how the sound FX volume
slider is initialized.
soundOptionsInfo.sfxvolume.generic.type = MTYPE_SLIDER;
soundOptionsInfo.sfxvolume.generic.name = "Effects Volume:";
soundOptionsInfo.sfxvolume.generic.flags =
QMF_PULSEIFFOCUS|QMF_SMALLFONT;
soundOptionsInfo.sfxvolume.generic.callback =
UI_SoundOptionsMenu_Event;
soundOptionsInfo.sfxvolume.generic.id = ID_EFFECTSVOLUME;
soundOptionsInfo.sfxvolume.generic.x = 400;
soundOptionsInfo.sfxvolume.generic.y = y;
soundOptionsInfo.sfxvolume.minvalue = 0;
soundOptionsInfo.sfxvolume.maxvalue = 10;
soundOptionsInfo.sfxvolume.curvalue =
trap_Cvar_VariableValue( "s_volume" ) * 10;
Back to the list of controls
2.3 menulist_s (choose from a list)
This control presents a list so that one item can be selected. There are
two types of list control, a "spin" control that takes up very little space on
screen, or a "list" control that displays a part of the list.
The spin control is best used when there isn't much space on screen, and
there are only a few options to choose from. Clicking on the control moves to
the next value.
The list control is more complicated: not only can it display more than one
column of information, but you'll also have to add additional buttons
controls to scroll the list.
All text drawn with this control is in the UI_SMALLFONT style.
typedef struct
{
menucommon_s generic;
int oldvalue;
int curvalue;
int numitems;
int top;
const char **itemnames;
int width;
int height;
int columns;
int seperation;
} menulist_s;
For the "spin" control:
generic.type
- Set to MTYPE_SPINCONTROL.
generic.x, generic.y
- The co-ordinates of the control. The top left corner of the
list value.
generic.name
- If set then this descriptive text will be drawn to the left
of the spin control (x,y) co-ordinates. Adding this name doesn't move the list
text.
itemnames
- This is the list of possible values that the control can take. It must
be an array of const char* with the last member being a NULL pointer
(see example below).
curvalue
- The currently selected value in the spin control. An index into the array
set in itemnames.
numitems
- The number of items in the spin control list. Do not initialize,
modify with caution.
Example: the spin control taken from the server browser in
ui_servers2.c.
static const char *master_items[] = {
"Local",
"Mplayer",
"Internet",
"Favorites",
0
};
g_arenaservers.master.generic.type = MTYPE_SPINCONTROL;
g_arenaservers.master.generic.name = "Servers:";
g_arenaservers.master.generic.flags =
QMF_PULSEIFFOCUS|QMF_SMALLFONT;
g_arenaservers.master.generic.callback =
ArenaServers_Event;
g_arenaservers.master.generic.id = ID_MASTER;
g_arenaservers.master.generic.x = 320;
g_arenaservers.master.generic.y = y;
g_arenaservers.master.itemnames = master_items;
Back to the list of controls
For the "list" control:
generic.type
- Set to MTYPE_SCROLLLIST.
generic.x, generic.y
- The co-ordinates of the control. The top left corner of the
list display area. Use QMF_CENTER_JUSTIFY to align symmetrically
about the y co-ordinate instead.
itemnames
- This is the list of possible values that the control can take. It must
be an array of const char* with the last member being a NULL pointer.
curvalue
- The currently selected value in the list. An index into the array
set in itemnames.
numitems
- The number of items displayed from the list. You must initialize
and update this value yourself.
columns
- The number of columns in the display list. Defaults to 1 if not initialized.
seperation
- The separation (in characters) between adjacent columns.
height
- The height of the column, given by the number of rows. Must be initialized.
Example: the listbox taken from the server browser in ui_servers2.c.
g_arenaservers.list.generic.type = MTYPE_SCROLLLIST;
g_arenaservers.list.generic.flags =
QMF_HIGHLIGHT_IF_FOCUS;
g_arenaservers.list.generic.id = ID_LIST;
g_arenaservers.list.generic.callback =
ArenaServers_Event;
g_arenaservers.list.generic.x = 72;
g_arenaservers.list.generic.y = y;
g_arenaservers.list.width = MAX_LISTBOXWIDTH;
g_arenaservers.list.height = 11;
g_arenaservers.list.itemnames =
(const char **)g_arenaservers.items;
and this is the code that scrolls the list when one of two bitmap buttons
is clicked:
static void ArenaServers_Event( void* ptr, int event ) {
int id;
id = ((menucommon_s*)ptr)->id;
// code snipped...
switch( id ) {
// code snipped...
case ID_SCROLL_UP:
ScrollList_Key( &g_arenaservers.list, K_UPARROW );
break;
case ID_SCROLL_DOWN:
ScrollList_Key( &g_arenaservers.list, K_DOWNARROW );
break;
// code snipped...
}
}
You can also use K_PGUP and K_PGDN to move an entire page up
or down (but not on a multi-column list). Move to the start and end of
the list with K_HOME and K_END. Use K_LEFTARROW and
K_RIGHTARROW to move a multi-column list left or right one column.
Back to the list of controls
2.4 menuradiobutton_s (an on/off state)
A simple control that only has an on/off state. It is really just a special
case of the spin control version of menulist_s, with minor graphical
differences, and hard coded text strings for the on/off values.
The control is always drawn using the UI_SMALLFONT text, and with a
small "disc" between the descriptive name and the on/off text.
typedef struct
{
menucommon_s generic;
int curvalue;
} menuradiobutton_s;
generic.type
- Set to MTYPE_RADIOBUTTON.
generic.x, generic.y
- The co-ordinates of the control.
generic.name
- The descriptive text that will be drawn to the left of the control.
curvalue
- Zero if the control is "off", non-zero if displaying "on".
This example is taken from ui_preferences.c:
s_preferences.identifytarget.generic.type =
MTYPE_RADIOBUTTON;
s_preferences.identifytarget.generic.name = "Identify Target:";
s_preferences.identifytarget.generic.flags =
QMF_PULSEIFFOCUS|QMF_SMALLFONT;
s_preferences.identifytarget.generic.callback =
Preferences_Event;
s_preferences.identifytarget.generic.id = ID_IDENTIFYTARGET;
s_preferences.identifytarget.generic.x = PREFERENCES_X_POS;
s_preferences.identifytarget.generic.y = y;
s_preferences.identifytarget.curvalue =
trap_Cvar_VariableValue( "cg_drawCrosshairNames" ) != 0;
Back to the list of controls
2.5 menubitmap_s (draw a button or picture)
Used to draw a clickable button (or static picture) from a JPEG or TARGA
image file. A clickable button makes use of the generic.calback
function to respond to mouse and keyboard events, while a static picture has
QMF_INACTIVE set.
When used as a button you must also set the graphic for the active state
(is selected and has the focus).
If you want your graphics to have a "cut out", non-rectangular look
then you'll have to use TARGA images with a mask channel set. Refer to your
image editing program for more details (I use Micrografx Picture Publisher 8).
typedef struct
{
menucommon_s generic;
char* focuspic;
char* errorpic;
qhandle_t shader;
qhandle_t focusshader;
int width;
int height;
float* focuscolor;
} menubitmap_s;
generic.type
- Set to MTYPE_BITMAP.
generic.x, generic.y
- The top left corner of the image/button by default.
generic.flags
- Set to QMF_INACTIVE for a static picture that doesn't respond to keyboard
or mouse input. You can still control how a static picture is drawn by setting flags
like QMF_GREYED, QMF_PULSE, or QMF_HIGHLIGHT.
For an active button this is done automatically in response to keyboard and mouse input.
The alignment of the picture can be controlled with QMF_LEFT_JUSTIFY
(default), QMF_CENTER_JUSTIFY, and QMF_RIGHT_JUSTIFY.
generic.name, shader
- Set to the image filename and path relative to the mod directory that
contains the pk3 files. If the image is in a pk3 file then use that relative
path e.g. "menu/art/accept_0" could be in baseq3/menu/art/ or
in the menu/art/ directory of any pk3 file.
By default the image has an extension of .tga (TARGA), but you
can also use .jpg (JPEG) explicitly.
The shader is a unique way of identifying an image
registered through trap_R_RegisterShaderNoMip(). You only need to set
the generic.name, and shader will then be set for you. You can also
change the value of shader as you need, without updating generic.name
to match.
If you need to change the name of the graphic drawn, then you must set
shader to zero as well. This forces a refresh of the shader, and you won't
need to call trap_R_RegisterShaderNoMip() for the image.
errorpic
- If for any reason the generic.name image can't be found or
loaded, then an attempt is made to load the errorpic instead. Following
the same naming convention as generic.name, this should be a reasonable default.
You don't have to provide an error picture, but it's recommended when you might
not have control over the image you're trying to load (e.g. level pictures
from pk3 files, some map designers don't provide them).
focuspic, focusshader, focuscolor
- The image file drawn when the button has focus is stored in
focuspic (following the same format as generic.name). This image
is drawn over the generic.name image, not instead of it.
A unique handle for the image shader is stored in focusshader. Behaviour
is identical to that of shader: set to zero to
force a refresh if focuspic changes.
The focuspic will "pulse" in colour if QMF_PULSE is set, or if
QMF_PULSEIFFOCUS is set and the cursor is over the image. You can
change this colour by setting focuscolor.
If QMF_HIGHLIGHT or QMF_HIGHLIGHT_IF_FOCUS are set then
the focuspic is drawn over the generic.name image. Its can also be
changed with focuscolor.
Colour values are stored in a vec4_t which is an
array float[4]. focuscolor[0], focuscolor[1] and
focuscolor[2] are the red, green and blue values respectively, taking
a value between 0.0 and 1.0. The fourth value focuscolor[3] is the
transparency: 1.0 means fully visible, 0.0 means not drawn.
// a pointer is stored in menubitmap_t, so color shouldn't
// be a temporary variable in a function.
static vec4_t color;
menubitmap_t bitmap;
color[0] = color[1] = color[2] = 1.0; // white
color[3] = 0.5; // transparency, dimmed
bitmap.focuscolor = color;
width, height
- The width and height of the image, it also defines the size of the
active region sensitive to the mouse cursor. The image is scaled to fit into
this size, leading to distortion if the image aspect ratio isn't kept.
This example is from ui_servers2.c, and is typical of a
button.
#define ART_BACK0 "menu/art/back_0"
#define ART_BACK1 "menu/art/back_1"
g_arenaservers.back.generic.type = MTYPE_BITMAP;
g_arenaservers.back.generic.name = ART_BACK0;
g_arenaservers.back.generic.flags =
QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
g_arenaservers.back.generic.callback =
ArenaServers_Event;
g_arenaservers.back.generic.id = ID_BACK;
g_arenaservers.back.generic.x = 0;
g_arenaservers.back.generic.y = 480-64;
g_arenaservers.back.width = 128;
g_arenaservers.back.height = 64;
g_arenaservers.back.focuspic = ART_BACK1;
Back to the list of controls
2.6 menutext_s (draws text)
This flexible control is used to draw text as part of a menu or page of
controls. There are three variants of this text control, so choose the appropriate
one for your needs.
A "simple text" control just draws on the screen with the minimal amount
of additional options or processing. Each character occupies the same amount of
screen area, determined by the font size. It can't be clicked upon, and should be
used to provide information only. Set generic.type to MTYPE_TEXT
to use this control.
To draw the text in a "banner style" then set generic.type to
MTYPE_BTEXT. The text will be drawn in a large style, with each character
occupying a proportional width rather than a fixed screen area. Use this type
of control for the text title of a page of controls. Like the MTYPE_TEXT
this control doesn't respond to keyboard or mouse input.
The final type (set generic.type to MTYPE_PTEXT)
should be used as a "menu control" for the text in a list of menu options. It
responds to keyboard and mouse input and, like the banner text, uses a
proportional font width.
typedef struct
{
menucommon_s generic;
char* string;
int style;
float* color;
} menutext_s;
For the "simple text" control:
generic.type
- Set to MTYPE_TEXT.
generic.x, generic.y
- The position of the text on-screen, can be modified by setting style
to UI_CENTER or UI_RIGHT.
generic.flags
- The value QMF_GRAYED is the only value that has an effect,
use style to control appearance.
generic.name
- The text here is placed in front of the text in string.
string
- The text that will be drawn in the fixed width font. The text in
generic.name will be drawn first, and this text then appended.
style
- The flags used to influence how the text is drawn.
color
- The colour in which the text will be drawn. Can be over-ridden by
QMF_GRAYED.
Example: taken from ui_servers2.c, a text control that gives
the information about the server browsing.
g_arenaservers.status.generic.type = MTYPE_TEXT;
g_arenaservers.status.generic.x = 320;
g_arenaservers.status.generic.y = y;
g_arenaservers.status.string = statusbuffer;
g_arenaservers.status.style = UI_CENTER|UI_SMALLFONT;
g_arenaservers.status.color = menu_text_color;
Back to the list of controls
For the "banner style" text control:
generic.type
- Set to MTYPE_BTEXT.
generic.x, generic.y
- The position of the text on-screen, can be modified by setting style
to UI_CENTER or UI_RIGHT.
generic.flags
- The value QMF_GRAYED is the only value that has an effect,
use style to control appearance.
string
- The text that will be drawn for the banner in a proportional font.
style
- The flags used to influence how the text is drawn. Any font size flags
are ignored.
color
- The colour in which the text will be drawn. Can be over-ridden by
QMF_GRAYED.
Example taken from ui_playersettings.c, the banner title
for the player model selection.
s_playermodel.banner.generic.type = MTYPE_BTEXT;
s_playermodel.banner.generic.x = 320;
s_playermodel.banner.generic.y = 16;
s_playermodel.banner.string = "PLAYER MODEL";
s_playermodel.banner.color = color_white;
s_playermodel.banner.style = UI_CENTER;
Back to the list of controls
For the "menu text" control:
generic.type
- Set to MTYPE_PTEXT.
generic.x, generic.y
- The position of the text on-screen, can be modified by setting
QMF_CENTER_JUSTIFY or QMF_RIGHT_JUSTIFY.
generic.flags
- Only a few flags are effective: QMF_PULSEIFFOCUS, QMF_GRAYED,
QMF_CENTER_JUSTIFY, and QMF_RIGHT_JUSTIFY. Other styles are
controlled through the style parameters.
string
- The text that will be drawn for the banner in a proportional font.
style
- The flags used to influence how the text is drawn. Font size flags work.
If you use any of the QMF_*_JUSTIFY flags, then you must set the
matching UI_* flags, otherwise the text will be drawn in the wrong place.
color
- The colour in which the text will be drawn. Can be over-ridden by
QMF_GRAYED.
This example is take from ui_ingame.c, the menu available
when the ESC key is pressed when playing.
Notice how the style value matches the generic.flags
setting of QMF_CENTER_JUSTIFY.
s_ingame.addbots.generic.type = MTYPE_PTEXT;
s_ingame.addbots.generic.flags =
QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
s_ingame.addbots.generic.x = 320;
s_ingame.addbots.generic.y = y;
s_ingame.addbots.generic.id = ID_ADDBOTS;
s_ingame.addbots.generic.callback = InGame_Event;
s_ingame.addbots.string = "ADD BOTS";
s_ingame.addbots.color = color_red;
s_ingame.addbots.style = UI_CENTER|UI_SMALLFONT;
if( !trap_Cvar_VariableValue( "sv_running" ) ||
!trap_Cvar_VariableValue( "bot_enable" ) ||
(trap_Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER))
{
s_ingame.addbots.generic.flags |= QMF_GRAYED;
}
Back to the list of controls
2.7 menuaction_s (big menu text)
Originally intended to be the control on choice for ingame menus, its
use has largely been replaced by the more flexible menutext_s control.
Text is drawn in a big font style, appropriate for main menus.
It has been used extensively in the key setup menus, but in each case
has been over-ridden with custom ownerdrawn routines. For this reason there's
no example.
Use of menutext_s is recommended instead.
typedef struct
{
menucommon_s generic;
} menuaction_s;
generic.type
- Set to MTYPE_ACTION.
generic.x, generic.y
- The co-ordinates of the control. The top left corner of where the
text will be drawn.
generic.name
- The text that will be drawn using the UI_BIGFONT style.
Back to the list of controls
3. To be continued...
With the reference material now out of the way we can look at some of
the more advanced ways of using the menu system. The
final part of this primer will look at
owner drawn controls, status bars, and give some design tips for menus as well.
|