Code3Arena

PlanetQuake | Code3Arena | Articles | << Prev | Article 7 | Next >>

menu

  • Home/News
  • ModSource
  • Compiling
  • Help!!!
  • Submission
  • Contributors
  • Staff
  • Downloads

    Tutorials
    < Index >
    1. Mod making 101
    2. Up 'n running
    3. Hello, QWorld!
    4. Infinite Haste
    5. Armor Piercing Rails
    6. Bouncing Rockets
    7. Cloaking
    8. Ladders
    9. Favourite Server
    10. Flame Thrower
    11. Vortex Grenades
    12. Grapple
    13. Lightning Discharge
    14. Locational Damage
    15. Leg Shots
    16. Weapon Switching
    17. Scoreboard frag-rate
    18. Vortex Grenades II
    19. Vulnerable Missiles
    20. Creating Classes
    21. Scrolling Credits
    22. Weapon Dropping
    23. Anti-Gravity Boots
    24. HUD scoreboard
    25. Flashlight and laser
    26. Weapon Positioning
    27. Weapon Reloading
    28. Progressive Zooming
    29. Rotating Doors
    30. Beheading (headshot!)
    31. Alt Weapon Fire
    32. Popup Menus I
    33. Popup Menus II
    34. Cluster Grenades
    35. Homing Rockets
    36. Spreadfire Powerup
    37. Instagib gameplay
    38. Accelerating rockets
    39. Server only Instagib
    40. Advanced Grapple Hook
    41. Unlagging your mod


    Articles
    < Index >
    1. Entities
    2. Vectors
    3. Good Coding
    4. Compilers I
    5. Compilers II
    6. UI Menu Primer I
    7. UI Menu Primer II
    8. UI Menu Primer III
    9. QVM Communication, Cvars, commands
    10. Metrowerks CodeWarrior
    11. 1.27g code, bugs, batch


    Links

  • Quake3 Files
  • Quake3 Forums
  • Q3A Editing Message Board
  • Quake3 Editing


    Feedback

  • SumFuka
  • Calrathan
  • HypoThermia
  • WarZone





    Site Design by:
    ICEmosis Design


  •  
    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.

    PlanetQuake | Code3Arena | Articles | << Prev | Article 7 | Next >>