Skip to content

Commit

Permalink
Game Menu|Added: Added a fixed layout method
Browse files Browse the repository at this point in the history
Menu pages can now use either a fixed layout (all objects positioned
absolutely relative to the page origin) or make use of the dynamic
layout algorithm (which trys to automatically position elements to
make best use of the available screen real estate).

Made use of the fixed layout to address the positioning issue with
Hexen's player class selection page.

Todo: Use the fixed layout for all original game menu pages other
than the various options pages (necessary for mod compatibility).
  • Loading branch information
danij-deng committed Feb 26, 2012
1 parent c132e40 commit 74ecbb7
Show file tree
Hide file tree
Showing 5 changed files with 435 additions and 371 deletions.
22 changes: 18 additions & 4 deletions doomsday/plugins/common/include/hu_lib.h
Expand Up @@ -153,9 +153,13 @@ typedef struct mn_object_s {
/// Object group identifier.
int _group;

/// @see menuObjectFlags.
/// @ref menuObjectFlags.
int _flags;

/// Used with the fixed layout method for positioning this object in
/// the owning page's coordinate space.
Point2Raw _origin;

/// DDKEY shortcut used to switch focus to this object directly.
/// @c 0= no shortcut defined.
int _shortcut;
Expand Down Expand Up @@ -303,6 +307,13 @@ typedef enum {

#define VALID_MNPAGE_FONTID(v) ((v) >= MENU_FONT1 && (v) < MENU_FONT_COUNT)

/**
* @defgroup menuPageFlags Menu Page Flags
*/
///@{
#define MPF_LAYOUT_FIXED 0x1 ///< Page uses a fixed layout.
///@}

typedef struct mn_page_s {
/// Collection of objects on this page.
mn_object_t* objects;
Expand All @@ -321,6 +332,9 @@ typedef struct mn_page_s {
/// Index of the currently focused object else @c -1
int focus;

/// @ref menuPageFlags
int flags;

/// Predefined fonts objects on this page.
fontid_t fonts[MENU_FONT_COUNT];

Expand All @@ -333,7 +347,7 @@ typedef struct mn_page_s {
/// Page drawing routine.
void (*drawer) (struct mn_page_s* page, const Point2Raw* offset);

/// Menu command responder routine.
/// Menu-command responder routine.
int (*cmdResponder) (struct mn_page_s* page, menucommand_e cmd);

/// User data.
Expand Down Expand Up @@ -773,8 +787,8 @@ void MNSlider_SetValue(mn_object_t* ob, int flags, float value);
/**
* Mobj preview visual.
*/
#define MNDATA_MOBJPREVIEW_WIDTH 38
#define MNDATA_MOBJPREVIEW_HEIGHT 52
#define MNDATA_MOBJPREVIEW_WIDTH 44
#define MNDATA_MOBJPREVIEW_HEIGHT 66

typedef struct mndata_mobjpreview_s {
int mobjType;
Expand Down
17 changes: 13 additions & 4 deletions doomsday/plugins/common/include/hu_menu.h
Expand Up @@ -111,10 +111,19 @@ boolean Hu_MenuIsVisible(void);

mn_page_t* Hu_MenuFindPageByName(const char* name);

mn_page_t* Hu_MenuNewPage(const char* name, const Point2Raw* origin,
void (*ticker) (struct mn_page_s* page),
void (*drawer) (struct mn_page_s* page, const Point2Raw* origin),
int (*cmdResponder) (struct mn_page_s* page, menucommand_e cmd),
/**
* @param name Symbolic name.
* @param origin Topleft corner.
* @param flags @see menuPageFlags.
* @param ticker Ticker callback.
* @param drawer Page drawing routine.
* @param cmdResponder Menu-command responder routine.
* @param userData User data pointer to be associated with the page.
*/
mn_page_t* Hu_MenuNewPage(const char* name, const Point2Raw* origin, int flags,
void (*ticker) (mn_page_t* page),
void (*drawer) (mn_page_t* page, const Point2Raw* origin),
int (*cmdResponder) (mn_page_t* page, menucommand_e cmd),
void* userData);

/**
Expand Down
92 changes: 61 additions & 31 deletions doomsday/plugins/common/src/hu_lib.c
Expand Up @@ -827,16 +827,35 @@ static void applyPageLayout(mn_page_t* page)

if(!page) return;

Rect_SetXY(page->geometry, 0, 0);
Rect_SetWidthHeight(page->geometry, 0, 0);

// Apply layout logic to this page.

if(page->flags & MPF_LAYOUT_FIXED)
{
// This page uses a fixed layout.
for(i = 0; i < page->objectsCount; ++i)
{
mn_object_t* ob = &page->objects[i];

if(!MNObject_IsDrawable(ob)) continue;

Rect_SetXY(ob->_geometry, ob->_origin.x, ob->_origin.y);
Rect_Unite(page->geometry, ob->_geometry);
}
return;
}

// This page uses a dynamic layout.

// Calculate leading/line offset.
FR_SetFont(MNPage_PredefinedFont(page, MENU_FONT1));
/// \kludge We cannot yet query line height from the font.
/// @kludge We cannot yet query line height from the font.
lineHeight = FR_TextHeight("{case}WyQ");
lineOffset = MAX_OF(1, .5f + lineHeight * .34f);
// kludge end.

Rect_SetXY(page->geometry, 0, 0);
Rect_SetWidthHeight(page->geometry, 0, 0);

// Apply layout logic to this page.
for(i = 0; i < page->objectsCount;)
{
mn_object_t* ob = &page->objects[i];
Expand All @@ -854,7 +873,7 @@ static void applyPageLayout(mn_page_t* page)
// Orient label plus button/inline-list/textual-slider pairs about a
// vertical dividing line, with the label on the left, other object
// on the right.
// \todo Do not assume pairing, an object should designate it's pair.
// @todo Do not assume pairing, an object should designate it's pair.
if(MNObject_Type(ob) == MN_TEXT && nextOb)
{
if(MNObject_IsDrawable(nextOb) &&
Expand Down Expand Up @@ -943,8 +962,9 @@ static void drawPageHeading(mn_page_t* page, const Point2Raw* offset)

if(!page) return;

/// \kludge no title = no heading.
/// @kludge no title = no heading.
if(Str_IsEmpty(&page->title)) return;
// kludge end.

origin.x = SCREENWIDTH/2;
origin.y = (SCREENHEIGHT/2) - ((SCREENHEIGHT/2-5)/cfg.menuScale);
Expand All @@ -955,8 +975,8 @@ static void drawPageHeading(mn_page_t* page, const Point2Raw* offset)
}

FR_PushAttrib();
Hu_MenuDrawPageTitle(Str_Text(&page->title), origin.x, origin.y); origin.y += 16;
drawPageNavigation(page, origin.x, origin.y);
Hu_MenuDrawPageTitle(Str_Text(&page->title), origin.x, origin.y); origin.y += 16;
drawPageNavigation(page, origin.x, origin.y);
FR_PopAttrib();
}

Expand Down Expand Up @@ -997,11 +1017,11 @@ void MN_DrawPage(mn_page_t* page, float alpha, boolean showFocusCursor)
focusObjHeight = Size2_Height(MNObject_Size(focusObj));

// Determine the origin and dimensions of the cursor.
// \todo Each object should define a focus origin...
// @todo Each object should define a focus origin...
cursorOrigin.x = 0;
cursorOrigin.y = Point2_Y(MNObject_Origin(focusObj));

/// \kludge
/// @kludge
/// We cannot yet query the subobjects of the list for these values
/// so we must calculate them ourselves, here.
if(MN_LIST == MNObject_Type(focusObj) && (MNObject_Flags(focusObj) & MNF_ACTIVE) &&
Expand Down Expand Up @@ -1064,7 +1084,7 @@ void MN_DrawPage(mn_page_t* page, float alpha, boolean showFocusCursor)
}

// How about a focus cursor?
/// \todo cursor should be drawn on top of the page drawer.
/// @todo cursor should be drawn on top of the page drawer.
if(showFocusCursor && focusObj)
{
Hu_MenuDrawFocusCursor(cursorOrigin.x, cursorOrigin.y, focusObjHeight, alpha);
Expand Down Expand Up @@ -3347,30 +3367,32 @@ void MNMobjPreview_SetTranslationMap(mn_object_t* obj, int tMap)
}

/// @todo We can do better - the engine should be able to render this visual for us.
void MNMobjPreview_Drawer(mn_object_t* ob, const Point2Raw* origin)
void MNMobjPreview_Drawer(mn_object_t* ob, const Point2Raw* offset)
{
mndata_mobjpreview_t* mop = (mndata_mobjpreview_t*)ob->_typedata;
float x, y, w, h, s, t, scale;
int tClass, tMap, spriteFrame;
spritetype_e sprite;
spriteinfo_t info;
float s, t, scale;
Point2Raw origin;
Size2Raw size;
assert(ob->_type == MN_MOBJPREVIEW);

if(MT_NONE == mop->mobjType) return;

findSpriteForMobjType(mop->mobjType, &sprite, &spriteFrame);
if(!R_GetSpriteInfo(sprite, spriteFrame, &info)) return;

x = origin->x;
y = origin->y;
w = info.geometry.size.width;
h = info.geometry.size.height;
scale = (h > w? MNDATA_MOBJPREVIEW_HEIGHT / h : MNDATA_MOBJPREVIEW_WIDTH / w);
w *= scale;
h *= scale;
origin.x = info.geometry.origin.x;
origin.y = info.geometry.origin.y;
size.width = info.geometry.size.width;
size.height = info.geometry.size.height;

x += MNDATA_MOBJPREVIEW_WIDTH/2 - info.geometry.size.width/2 * scale;
y += MNDATA_MOBJPREVIEW_HEIGHT - info.geometry.size.height * scale;
scale = (size.height > size.width? (float)MNDATA_MOBJPREVIEW_HEIGHT / size.height :
(float)MNDATA_MOBJPREVIEW_WIDTH / size.width);

s = info.texCoord[0];
t = info.texCoord[1];

tClass = mop->tClass;
tMap = mop->tMap;
Expand All @@ -3382,27 +3404,35 @@ void MNMobjPreview_Drawer(mn_object_t* ob, const Point2Raw* origin)
R_GetTranslation(mop->plrClass, tMap, &tClass, &tMap);
#endif

DGL_Enable(DGL_TEXTURE_2D);
DGL_SetPSprite2(info.material, tClass, tMap);
DGL_MatrixMode(DGL_MODELVIEW);
DGL_PushMatrix();

s = info.texCoord[0];
t = info.texCoord[1];
DGL_Translatef(offset->x, offset->y, 0);
DGL_Scalef(scale, scale, 1);
// Translate origin to the top left.
DGL_Translatef(-origin.x, -origin.y, 0);

DGL_Enable(DGL_TEXTURE_2D);
DGL_SetPSprite2(info.material, tClass, tMap);
DGL_Color4f(1, 1, 1, rs.pageAlpha);

DGL_Begin(DGL_QUADS);
DGL_TexCoord2f(0, 0 * s, 0);
DGL_Vertex2f(x, y);
DGL_Vertex2f(0, 0);

DGL_TexCoord2f(0, 1 * s, 0);
DGL_Vertex2f(x + w, y);
DGL_Vertex2f(size.width, 0);

DGL_TexCoord2f(0, 1 * s, t);
DGL_Vertex2f(x + w, y + h);
DGL_Vertex2f(size.width, size.height);

DGL_TexCoord2f(0, 0 * s, t);
DGL_Vertex2f(x, y + h);
DGL_Vertex2f(0, size.height);
DGL_End();

DGL_MatrixMode(DGL_MODELVIEW);
DGL_PopMatrix();

DGL_Disable(DGL_TEXTURE_2D);
}

Expand Down

0 comments on commit 74ecbb7

Please sign in to comment.