mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-24 21:11:39 +00:00
534 lines
13 KiB
Text
534 lines
13 KiB
Text
/*
|
|
** optionmenu.cpp
|
|
** Handler class for the option menus and associated items
|
|
**
|
|
**---------------------------------------------------------------------------
|
|
** Copyright 2010-2017 Christoph Oelckers
|
|
** All rights reserved.
|
|
**
|
|
** Redistribution and use in source and binary forms, with or without
|
|
** modification, are permitted provided that the following conditions
|
|
** are met:
|
|
**
|
|
** 1. Redistributions of source code must retain the above copyright
|
|
** notice, this list of conditions and the following disclaimer.
|
|
** 2. Redistributions in binary form must reproduce the above copyright
|
|
** notice, this list of conditions and the following disclaimer in the
|
|
** documentation and/or other materials provided with the distribution.
|
|
** 3. The name of the author may not be used to endorse or promote products
|
|
** derived from this software without specific prior written permission.
|
|
**
|
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
**---------------------------------------------------------------------------
|
|
**
|
|
*/
|
|
|
|
struct FOptionMenuSettings
|
|
{
|
|
int mTitleColor;
|
|
int mFontColor;
|
|
int mFontColorValue;
|
|
int mFontColorMore;
|
|
int mFontColorHeader;
|
|
int mFontColorHighlight;
|
|
int mFontColorSelection;
|
|
int mLinespacing;
|
|
}
|
|
|
|
class OptionMenuDescriptor : MenuDescriptor native
|
|
{
|
|
native Array<OptionMenuItem> mItems;
|
|
native String mTitle;
|
|
native int mSelectedItem;
|
|
native int mDrawTop;
|
|
native int mScrollTop;
|
|
native int mScrollPos;
|
|
native int mIndent;
|
|
native int mPosition;
|
|
native bool mDontDim;
|
|
|
|
void Reset()
|
|
{
|
|
// Reset the default settings (ignore all other values in the struct)
|
|
mPosition = 0;
|
|
mScrollTop = 0;
|
|
mIndent = 0;
|
|
mDontDim = 0;
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
//
|
|
//
|
|
//=============================================================================
|
|
|
|
void CalcIndent()
|
|
{
|
|
// calculate the menu indent
|
|
int widest = 0, thiswidth;
|
|
|
|
for (int i = 0; i < mItems.Size(); i++)
|
|
{
|
|
thiswidth = mItems[i].GetIndent();
|
|
if (thiswidth > widest) widest = thiswidth;
|
|
}
|
|
mIndent = widest + 4;
|
|
}
|
|
}
|
|
|
|
|
|
class OptionMenu : Menu
|
|
{
|
|
OptionMenuDescriptor mDesc;
|
|
bool CanScrollUp;
|
|
bool CanScrollDown;
|
|
int VisBottom;
|
|
OptionMenuItem mFocusControl;
|
|
|
|
//=============================================================================
|
|
//
|
|
//
|
|
//
|
|
//=============================================================================
|
|
|
|
virtual void Init(Menu parent, OptionMenuDescriptor desc)
|
|
{
|
|
mParentMenu = parent;
|
|
mDesc = desc;
|
|
if (mDesc != NULL && mDesc.mSelectedItem == -1) mDesc.mSelectedItem = FirstSelectable();
|
|
mDesc.CalcIndent();
|
|
}
|
|
|
|
|
|
//=============================================================================
|
|
//
|
|
//
|
|
//
|
|
//=============================================================================
|
|
|
|
OptionMenuItem GetItem(Name name)
|
|
{
|
|
for(int i = 0; i < mDesc.mItems.Size(); i++)
|
|
{
|
|
Name nm = mDesc.mItems[i].GetAction();
|
|
if (nm == name) return mDesc.mItems[i];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//=============================================================================
|
|
//
|
|
//
|
|
//
|
|
//=============================================================================
|
|
|
|
int FirstSelectable()
|
|
{
|
|
if (mDesc != NULL)
|
|
{
|
|
// Go down to the first selectable item
|
|
int i = -1;
|
|
do
|
|
{
|
|
i++;
|
|
}
|
|
while (i < mDesc.mItems.Size() && !mDesc.mItems[i].Selectable());
|
|
if (i>=0 && i < mDesc.mItems.Size()) return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
//
|
|
//
|
|
//=============================================================================
|
|
|
|
override bool Responder (InputEventData ev)
|
|
{
|
|
if (ev.type == InputEventData.GUI_Event)
|
|
{
|
|
if (ev.subtype == InputEventData.GUI_WheelUp)
|
|
{
|
|
int scrollamt = MIN(2, mDesc.mScrollPos);
|
|
mDesc.mScrollPos -= scrollamt;
|
|
return true;
|
|
}
|
|
else if (ev.subtype == InputEventData.GUI_WheelDown)
|
|
{
|
|
if (CanScrollDown)
|
|
{
|
|
if (VisBottom >= 0 && VisBottom < (mDesc.mItems.Size()-2))
|
|
{
|
|
mDesc.mScrollPos += 2;
|
|
VisBottom += 2;
|
|
}
|
|
else
|
|
{
|
|
mDesc.mScrollPos++;
|
|
VisBottom++;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
return Super.Responder(ev);
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
//
|
|
//
|
|
//=============================================================================
|
|
|
|
override bool MenuEvent (int mkey, bool fromcontroller)
|
|
{
|
|
int startedAt = mDesc.mSelectedItem;
|
|
|
|
switch (mkey)
|
|
{
|
|
case MKEY_Up:
|
|
if (mDesc.mSelectedItem == -1)
|
|
{
|
|
mDesc.mSelectedItem = FirstSelectable();
|
|
break;
|
|
}
|
|
do
|
|
{
|
|
--mDesc.mSelectedItem;
|
|
|
|
if (mDesc.mScrollPos > 0 &&
|
|
mDesc.mSelectedItem <= mDesc.mScrollTop + mDesc.mScrollPos)
|
|
{
|
|
mDesc.mScrollPos = MAX(mDesc.mSelectedItem - mDesc.mScrollTop - 1, 0);
|
|
}
|
|
|
|
if (mDesc.mSelectedItem < 0)
|
|
{
|
|
// Figure out how many lines of text fit on the menu
|
|
int y = mDesc.mPosition;
|
|
|
|
if (y <= 0)
|
|
{
|
|
if (BigFont && mDesc.mTitle.Length() > 0)
|
|
{
|
|
y = -y + BigFont.GetHeight();
|
|
}
|
|
else
|
|
{
|
|
y = -y;
|
|
}
|
|
}
|
|
y *= CleanYfac_1;
|
|
int rowheight = OptionMenuSettings.mLinespacing * CleanYfac_1;
|
|
int maxitems = (screen.GetHeight() - rowheight - y) / rowheight + 1;
|
|
|
|
mDesc.mScrollPos = MAX (0, mDesc.mItems.Size() - maxitems + mDesc.mScrollTop);
|
|
mDesc.mSelectedItem = mDesc.mItems.Size()-1;
|
|
}
|
|
}
|
|
while (!mDesc.mItems[mDesc.mSelectedItem].Selectable() && mDesc.mSelectedItem != startedAt);
|
|
break;
|
|
|
|
case MKEY_Down:
|
|
if (mDesc.mSelectedItem == -1)
|
|
{
|
|
mDesc.mSelectedItem = FirstSelectable();
|
|
break;
|
|
}
|
|
do
|
|
{
|
|
++mDesc.mSelectedItem;
|
|
|
|
if (CanScrollDown && mDesc.mSelectedItem == VisBottom)
|
|
{
|
|
mDesc.mScrollPos++;
|
|
VisBottom++;
|
|
}
|
|
if (mDesc.mSelectedItem >= mDesc.mItems.Size())
|
|
{
|
|
if (startedAt == -1)
|
|
{
|
|
mDesc.mSelectedItem = -1;
|
|
mDesc.mScrollPos = -1;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
mDesc.mSelectedItem = 0;
|
|
mDesc.mScrollPos = 0;
|
|
}
|
|
}
|
|
}
|
|
while (!mDesc.mItems[mDesc.mSelectedItem].Selectable() && mDesc.mSelectedItem != startedAt);
|
|
break;
|
|
|
|
case MKEY_PageUp:
|
|
if (mDesc.mScrollPos > 0)
|
|
{
|
|
mDesc.mScrollPos -= VisBottom - mDesc.mScrollPos - mDesc.mScrollTop;
|
|
if (mDesc.mScrollPos < 0)
|
|
{
|
|
mDesc.mScrollPos = 0;
|
|
}
|
|
if (mDesc.mSelectedItem != -1)
|
|
{
|
|
mDesc.mSelectedItem = mDesc.mScrollTop + mDesc.mScrollPos + 1;
|
|
while (!mDesc.mItems[mDesc.mSelectedItem].Selectable())
|
|
{
|
|
if (++mDesc.mSelectedItem >= mDesc.mItems.Size())
|
|
{
|
|
mDesc.mSelectedItem = 0;
|
|
}
|
|
}
|
|
if (mDesc.mScrollPos > mDesc.mSelectedItem)
|
|
{
|
|
mDesc.mScrollPos = mDesc.mSelectedItem;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MKEY_PageDown:
|
|
if (CanScrollDown)
|
|
{
|
|
int pagesize = VisBottom - mDesc.mScrollPos - mDesc.mScrollTop;
|
|
mDesc.mScrollPos += pagesize;
|
|
if (mDesc.mScrollPos + mDesc.mScrollTop + pagesize > mDesc.mItems.Size())
|
|
{
|
|
mDesc.mScrollPos = mDesc.mItems.Size() - mDesc.mScrollTop - pagesize;
|
|
}
|
|
if (mDesc.mSelectedItem != -1)
|
|
{
|
|
mDesc.mSelectedItem = mDesc.mScrollTop + mDesc.mScrollPos;
|
|
while (!mDesc.mItems[mDesc.mSelectedItem].Selectable())
|
|
{
|
|
if (++mDesc.mSelectedItem >= mDesc.mItems.Size())
|
|
{
|
|
mDesc.mSelectedItem = 0;
|
|
}
|
|
}
|
|
if (mDesc.mScrollPos > mDesc.mSelectedItem)
|
|
{
|
|
mDesc.mScrollPos = mDesc.mSelectedItem;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MKEY_Enter:
|
|
if (mDesc.mSelectedItem >= 0 && mDesc.mItems[mDesc.mSelectedItem].Activate())
|
|
{
|
|
return true;
|
|
}
|
|
// fall through to default
|
|
default:
|
|
if (mDesc.mSelectedItem >= 0 &&
|
|
mDesc.mItems[mDesc.mSelectedItem].MenuEvent(mkey, fromcontroller)) return true;
|
|
return Super.MenuEvent(mkey, fromcontroller);
|
|
}
|
|
|
|
if (mDesc.mSelectedItem != startedAt)
|
|
{
|
|
MenuSound ("menu/cursor");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
//=============================================================================
|
|
//
|
|
//
|
|
//
|
|
//=============================================================================
|
|
|
|
override bool MouseEvent(int type, int x, int y)
|
|
{
|
|
y = (y / CleanYfac_1) - mDesc.mDrawTop;
|
|
|
|
if (mFocusControl)
|
|
{
|
|
mFocusControl.MouseEvent(type, x, y);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
int yline = (y / OptionMenuSettings.mLinespacing);
|
|
if (yline >= mDesc.mScrollTop)
|
|
{
|
|
yline += mDesc.mScrollPos;
|
|
}
|
|
if (yline >= 0 && yline < mDesc.mItems.Size() && mDesc.mItems[yline].Selectable())
|
|
{
|
|
if (yline != mDesc.mSelectedItem)
|
|
{
|
|
mDesc.mSelectedItem = yline;
|
|
//S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
|
|
}
|
|
mDesc.mItems[yline].MouseEvent(type, x, y);
|
|
return true;
|
|
}
|
|
}
|
|
mDesc.mSelectedItem = -1;
|
|
return Super.MouseEvent(type, x, y);
|
|
}
|
|
|
|
|
|
//=============================================================================
|
|
//
|
|
//
|
|
//
|
|
//=============================================================================
|
|
|
|
override void Ticker ()
|
|
{
|
|
Super.Ticker();
|
|
for(int i = 0; i < mDesc.mItems.Size(); i++)
|
|
{
|
|
mDesc.mItems[i].Ticker();
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
//
|
|
//
|
|
//=============================================================================
|
|
|
|
override void Drawer ()
|
|
{
|
|
int y = mDesc.mPosition;
|
|
|
|
if (y <= 0)
|
|
{
|
|
if (BigFont && mDesc.mTitle.Length() > 0)
|
|
{
|
|
let tt = Stringtable.Localize(mDesc.mTitle);
|
|
screen.DrawText (BigFont, OptionMenuSettings.mTitleColor,
|
|
(screen.GetWidth() - BigFont.StringWidth(tt) * CleanXfac_1) / 2, 10*CleanYfac_1,
|
|
tt, DTA_CleanNoMove_1, true);
|
|
y = -y + BigFont.GetHeight();
|
|
}
|
|
else
|
|
{
|
|
y = -y;
|
|
}
|
|
}
|
|
mDesc.mDrawTop = y;
|
|
int fontheight = OptionMenuSettings.mLinespacing * CleanYfac_1;
|
|
y *= CleanYfac_1;
|
|
|
|
int indent = mDesc.mIndent;
|
|
if (indent > 280)
|
|
{ // kludge for the compatibility options with their extremely long labels
|
|
if (indent + 40 <= CleanWidth_1)
|
|
{
|
|
indent = (screen.GetWidth() - ((indent + 40) * CleanXfac_1)) / 2 + indent * CleanXfac_1;
|
|
}
|
|
else
|
|
{
|
|
indent = screen.GetWidth() - 40 * CleanXfac_1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
indent = (indent - 160) * CleanXfac_1 + screen.GetWidth() / 2;
|
|
}
|
|
|
|
int ytop = y + mDesc.mScrollTop * 8 * CleanYfac_1;
|
|
int lastrow = screen.GetHeight() - SmallFont.GetHeight() * CleanYfac_1;
|
|
|
|
int i;
|
|
for (i = 0; i < mDesc.mItems.Size() && y <= lastrow; i++)
|
|
{
|
|
// Don't scroll the uppermost items
|
|
if (i == mDesc.mScrollTop)
|
|
{
|
|
i += mDesc.mScrollPos;
|
|
if (i >= mDesc.mItems.Size()) break; // skipped beyond end of menu
|
|
}
|
|
bool isSelected = mDesc.mSelectedItem == i;
|
|
int cur_indent = mDesc.mItems[i].Draw(mDesc, y, indent, isSelected);
|
|
if (cur_indent >= 0 && isSelected && mDesc.mItems[i].Selectable())
|
|
{
|
|
if (((MenuTime() % 8) < 6) || GetCurrentMenu() != self)
|
|
{
|
|
DrawConText(OptionMenuSettings.mFontColorSelection, cur_indent + 3 * CleanXfac_1, y+fontheight-9*CleanYfac_1, "\xd");
|
|
}
|
|
}
|
|
y += fontheight;
|
|
}
|
|
|
|
CanScrollUp = (mDesc.mScrollPos > 0);
|
|
CanScrollDown = (i < mDesc.mItems.Size());
|
|
VisBottom = i - 1;
|
|
|
|
if (CanScrollUp)
|
|
{
|
|
DrawConText(Font.CR_ORANGE, 3 * CleanXfac_1, ytop, "\x1a");
|
|
}
|
|
if (CanScrollDown)
|
|
{
|
|
DrawConText(Font.CR_ORANGE, 3 * CleanXfac_1, y - 8*CleanYfac_1, "\x1b");
|
|
}
|
|
Super.Drawer();
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
//
|
|
//
|
|
//=============================================================================
|
|
|
|
override void SetFocus(MenuItemBase fc)
|
|
{
|
|
mFocusControl = OptionMenuItem(fc);
|
|
}
|
|
|
|
override bool CheckFocus(MenuItemBase fc)
|
|
{
|
|
return mFocusControl == fc;
|
|
}
|
|
|
|
override void ReleaseFocus()
|
|
{
|
|
mFocusControl = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
class GameplayMenu : OptionMenu
|
|
{
|
|
override void Drawer ()
|
|
{
|
|
Super.Drawer();
|
|
|
|
String s = String.Format("dmflags = %d dmflags2 = %d", dmflags, dmflags2);
|
|
screen.DrawText (SmallFont, OptionMenuSettings.mFontColorValue,
|
|
(screen.GetWidth() - SmallFont.StringWidth (s) * CleanXfac_1) / 2, 0, s,
|
|
DTA_CleanNoMove_1, true);
|
|
}
|
|
}
|
|
|
|
class CompatibilityMenu : OptionMenu
|
|
{
|
|
override void Drawer ()
|
|
{
|
|
Super.Drawer();
|
|
|
|
String s = String.Format("compatflags = %d compatflags2 = %d", compatflags, compatflags2);
|
|
screen.DrawText (SmallFont, OptionMenuSettings.mFontColorValue,
|
|
(screen.GetWidth() - SmallFont.StringWidth (s) * CleanXfac_1) / 2, 0, s,
|
|
DTA_CleanNoMove_1, true);
|
|
}
|
|
}
|