diff --git a/src/menu/joystickmenu.cpp b/src/menu/joystickmenu.cpp index 9e9aacde8..eef4aa3bf 100644 --- a/src/menu/joystickmenu.cpp +++ b/src/menu/joystickmenu.cpp @@ -124,13 +124,6 @@ DEFINE_ACTION_FUNCTION(IJoystickConfig, SetAxisMap) DOptionMenuDescriptor *UpdateJoystickConfigMenu(IJoystickConfig *joy); -class DJoystickConfigMenu : public DOptionMenu -{ - DECLARE_CLASS(DJoystickConfigMenu, DOptionMenu) -}; - -IMPLEMENT_CLASS(DJoystickConfigMenu, false, false) - /*======================================= * * Joystick Menu @@ -295,7 +288,7 @@ void UpdateJoystickMenu(IJoystickConfig *selected) if (i == (int)Joysticks.Size()) { SELECTED_JOYSTICK = NULL; - if (DMenu::CurrentMenu != NULL && DMenu::CurrentMenu->IsKindOf(RUNTIME_CLASS(DJoystickConfigMenu))) + if (DMenu::CurrentMenu != NULL && DMenu::CurrentMenu->IsKindOf("JoystickConfigMenu")) { DMenu::CurrentMenu->Close(); } diff --git a/src/menu/menu.cpp b/src/menu/menu.cpp index a64f7b6df..46a0978af 100644 --- a/src/menu/menu.cpp +++ b/src/menu/menu.cpp @@ -378,6 +378,24 @@ void DMenu::Ticker () { } +DEFINE_ACTION_FUNCTION(DMenu, Ticker) +{ + PARAM_SELF_PROLOGUE(DMenu); + self->Drawer(); + return 0; +} + +void DMenu::CallTicker() +{ + IFVIRTUAL(DMenu, Ticker) + { + VMValue params[] = { (DObject*)this }; + GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + } + else Drawer(); +} + + void DMenu::Drawer () { if (this == DMenu::CurrentMenu && BackbuttonAlpha > 0 && m_show_backbutton >= 0 && m_use_mouse) @@ -608,10 +626,14 @@ void M_SetMenu(FName menu, int param) else if ((*desc)->IsKindOf(RUNTIME_CLASS(DOptionMenuDescriptor))) { DOptionMenuDescriptor *ld = static_cast(*desc); - const PClass *cls = ld->mClass == nullptr? RUNTIME_CLASS(DOptionMenu) : ld->mClass; + const PClass *cls = ld->mClass == nullptr? PClass::FindClass("OptionMenu") : ld->mClass; - DOptionMenu *newmenu = (DOptionMenu *)cls->CreateNew(); - newmenu->Init(DMenu::CurrentMenu, ld); + DMenu *newmenu = (DMenu*)cls->CreateNew(); + IFVIRTUALPTRNAME(newmenu, "OptionMenu", Init) + { + VMValue params[3] = { newmenu, DMenu::CurrentMenu, ld }; + GlobalVMStack.Call(func, params, 3, nullptr, 0); + } M_ActivateMenu(newmenu); } return; @@ -847,7 +869,7 @@ void M_Ticker (void) DMenu::MenuTime++; if (DMenu::CurrentMenu != nullptr && menuactive != MENU_Off) { - DMenu::CurrentMenu->Ticker(); + DMenu::CurrentMenu->CallTicker(); for (int i = 0; i < NUM_MKEYS; ++i) { @@ -856,7 +878,7 @@ void M_Ticker (void) if (MenuButtonTickers[i] > 0 && --MenuButtonTickers[i] <= 0) { MenuButtonTickers[i] = KEY_REPEAT_RATE; - DMenu::CurrentMenu->MenuEvent(i, MenuButtonOrigin[i]); + DMenu::CurrentMenu->CallMenuEvent(i, MenuButtonOrigin[i]); } } } @@ -1170,9 +1192,6 @@ CCMD(undocolorpic) -//native void OptionMenuDescriptor.CalcIndent(); -//native OptionMenuItem OptionMenuDescriptor.GetItem(Name iname); - DEFINE_FIELD(DMenu, mParentMenu) DEFINE_FIELD(DMenuDescriptor, mMenuName) @@ -1211,12 +1230,6 @@ DEFINE_FIELD(DOptionMenuDescriptor, mIndent) DEFINE_FIELD(DOptionMenuDescriptor, mPosition) DEFINE_FIELD(DOptionMenuDescriptor, mDontDim) -DEFINE_FIELD(DOptionMenu, CanScrollUp) -DEFINE_FIELD(DOptionMenu, CanScrollDown) -DEFINE_FIELD(DOptionMenu, VisBottom) -DEFINE_FIELD(DOptionMenu, mFocusControl) -DEFINE_FIELD(DOptionMenu, mDesc) - DEFINE_FIELD(FOptionMenuSettings, mTitleColor) DEFINE_FIELD(FOptionMenuSettings, mFontColor) DEFINE_FIELD(FOptionMenuSettings, mFontColorValue) diff --git a/src/menu/menu.h b/src/menu/menu.h index 9e64fdd8e..d6edb861b 100644 --- a/src/menu/menu.h +++ b/src/menu/menu.h @@ -282,6 +282,7 @@ public: bool CallResponder(event_t *ev); bool CallMenuEvent(int mkey, bool fromcontroller); bool CallMouseEvent(int type, int x, int y); + void CallTicker(); void CallDrawer(); bool MouseEventBack(int type, int x, int y); @@ -394,50 +395,6 @@ typedef TMap< FName, FOptionValues* > FOptionMap; extern FOptionMap OptionValues; -//============================================================================= -// -// Option menu class runs a menu described by a DOptionMenuDescriptor -// -//============================================================================= - -class DOptionMenu : public DMenu -{ - DECLARE_CLASS(DOptionMenu, DMenu) - HAS_OBJECT_POINTERS; - -public: // needs to be public for script access - bool CanScrollUp; - bool CanScrollDown; - int VisBottom; - DMenuItemBase *mFocusControl; - DOptionMenuDescriptor *mDesc; - -//public: - DMenuItemBase *GetItem(FName name); - DOptionMenu(DMenu *parent = NULL, DOptionMenuDescriptor *desc = NULL); - virtual void Init(DMenu *parent = NULL, DOptionMenuDescriptor *desc = NULL); - int FirstSelectable(); - bool Responder (event_t *ev); - bool MenuEvent (int mkey, bool fromcontroller); - bool MouseEvent(int type, int x, int y); - void Ticker (); - void Drawer (); - const DOptionMenuDescriptor *GetDescriptor() const { return mDesc; } - void SetFocus(DMenuItemBase *fc) - { - mFocusControl = fc; - } - bool CheckFocus(DMenuItemBase *fc) - { - return mFocusControl == fc; - } - void ReleaseFocus() - { - mFocusControl = NULL; - } -}; - - //============================================================================= // // Input some text diff --git a/src/menu/menudef.cpp b/src/menu/menudef.cpp index f02203841..9aff4350a 100644 --- a/src/menu/menudef.cpp +++ b/src/menu/menudef.cpp @@ -700,7 +700,7 @@ static void ParseOptionMenuBody(FScanner &sc, DOptionMenuDescriptor *desc) { sc.MustGetString(); const PClass *cls = PClass::FindClass(sc.String); - if (cls == nullptr || !cls->IsDescendantOf(RUNTIME_CLASS(DOptionMenu))) + if (cls == nullptr || !cls->IsDescendantOf("OptionMenu")) { sc.ScriptError("Unknown menu class '%s'", sc.String); } diff --git a/src/menu/optionmenu.cpp b/src/menu/optionmenu.cpp index c6165b6e3..acbb3c58e 100644 --- a/src/menu/optionmenu.cpp +++ b/src/menu/optionmenu.cpp @@ -3,7 +3,7 @@ ** Handler class for the option menus and associated items ** **--------------------------------------------------------------------------- -** Copyright 2010 Christoph Oelckers +** Copyright 2010-2017 Christoph Oelckers ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without @@ -49,431 +49,12 @@ #include "menu/menu.h" -//============================================================================= -// -// Draws a string in the console font, scaled to the 8x8 cells -// used by the default console font. -// -//============================================================================= - -void M_DrawConText (int color, int x, int y, const char *str) -{ - screen->DrawText (ConFont, color, x, y, str, - DTA_CellX, 8 * CleanXfac_1, - DTA_CellY, 8 * CleanYfac_1, - TAG_DONE); -} - - -IMPLEMENT_CLASS(DOptionMenu, false, false) - -IMPLEMENT_POINTERS_START(DOptionMenu) -IMPLEMENT_POINTER(mFocusControl) -IMPLEMENT_POINTERS_END - //============================================================================= // // // //============================================================================= -DOptionMenu::DOptionMenu(DMenu *parent, DOptionMenuDescriptor *desc) -: DMenu(parent) -{ - CanScrollUp = false; - CanScrollDown = false; - VisBottom = 0; - mFocusControl = NULL; - Init(parent, desc); -} - -//============================================================================= -// -// -// -//============================================================================= - -void DOptionMenu::Init(DMenu *parent, DOptionMenuDescriptor *desc) -{ - mParentMenu = parent; - GC::WriteBarrier(this, parent); - mDesc = desc; - if (mDesc != NULL && mDesc->mSelectedItem == -1) mDesc->mSelectedItem = FirstSelectable(); - -} - -//============================================================================= -// -// -// -//============================================================================= - -int DOptionMenu::FirstSelectable() -{ - if (mDesc != NULL) - { - // Go down to the first selectable item - int i = -1; - do - { - i++; - } - while (i < (int)mDesc->mItems.Size() && !mDesc->mItems[i]->Selectable()); - if (i>=0 && i < (int)mDesc->mItems.Size()) return i; - } - return -1; -} - -//============================================================================= -// -// -// -//============================================================================= - -DMenuItemBase *DOptionMenu::GetItem(FName name) -{ - for(unsigned i=0;imItems.Size(); i++) - { - FName nm = mDesc->mItems[i]->GetAction(NULL); - if (nm == name) return mDesc->mItems[i]; - } - return NULL; -} - -//============================================================================= -// -// -// -//============================================================================= - -bool DOptionMenu::Responder (event_t *ev) -{ - if (ev->type == EV_GUI_Event) - { - if (ev->subtype == EV_GUI_WheelUp) - { - int scrollamt = MIN(2, mDesc->mScrollPos); - mDesc->mScrollPos -= scrollamt; - return true; - } - else if (ev->subtype == EV_GUI_WheelDown) - { - if (CanScrollDown) - { - if (VisBottom < (int)(mDesc->mItems.Size()-2)) - { - mDesc->mScrollPos += 2; - VisBottom += 2; - } - else - { - mDesc->mScrollPos++; - VisBottom++; - } - } - return true; - } - } - return Super::Responder(ev); -} - -//============================================================================= -// -// -// -//============================================================================= - -bool DOptionMenu::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.IsNotEmpty()) - { - y = -y + BigFont->GetHeight(); - } - else - { - y = -y; - } - } - y *= CleanYfac_1; - int rowheight = OptionSettings.mLinespacing * CleanYfac_1; - int maxitems = (screen->GetHeight() - rowheight - y) / rowheight + 1; - - mDesc->mScrollPos = MAX (0, (int)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 >= (int)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 >= (int)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 > (int)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 >= (int)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) - { - S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); - } - return true; -} - -//============================================================================= -// -// -// -//============================================================================= - -bool DOptionMenu::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 / OptionSettings.mLinespacing); - if (yline >= mDesc->mScrollTop) - { - yline += mDesc->mScrollPos; - } - if ((unsigned)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); -} - -//============================================================================= -// -// -// -//============================================================================= - -void DOptionMenu::Ticker () -{ - Super::Ticker(); - for(unsigned i=0;imItems.Size(); i++) - { - mDesc->mItems[i]->Ticker(); - } -} - -//============================================================================= -// -// -// -//============================================================================= - -void DOptionMenu::Drawer () -{ - int y = mDesc->mPosition; - - if (y <= 0) - { - if (BigFont && mDesc->mTitle.IsNotEmpty()) - { - const char *tt = mDesc->mTitle; - if (*tt == '$') tt = GStrings(tt+1); - screen->DrawText (BigFont, OptionSettings.mTitleColor, - (screen->GetWidth() - BigFont->StringWidth(tt) * CleanXfac_1) / 2, 10*CleanYfac_1, - tt, DTA_CleanNoMove_1, true, TAG_DONE); - y = -y + BigFont->GetHeight(); - } - else - { - y = -y; - } - } - mDesc->mDrawTop = y; - int fontheight = OptionSettings.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; - - unsigned i; - for (i = 0; i < mDesc->mItems.Size() && y <= lastrow; i++, y += fontheight) - { - // Don't scroll the uppermost items - if ((int)i == mDesc->mScrollTop) - { - i += mDesc->mScrollPos; - if (i >= mDesc->mItems.Size()) break; // skipped beyond end of menu - } - bool isSelected = mDesc->mSelectedItem == (int)i; - int cur_indent = mDesc->mItems[i]->Draw(mDesc, y, indent, isSelected); - if (cur_indent >= 0 && isSelected && mDesc->mItems[i]->Selectable()) - { - if (((DMenu::MenuTime%8) < 6) || DMenu::CurrentMenu != this) - { - M_DrawConText(OptionSettings.mFontColorSelection, cur_indent + 3 * CleanXfac_1, y+fontheight-9*CleanYfac_1, "\xd"); - } - } - } - - CanScrollUp = (mDesc->mScrollPos > 0); - CanScrollDown = (i < mDesc->mItems.Size()); - VisBottom = i - 1; - - if (CanScrollUp) - { - M_DrawConText(CR_ORANGE, 3 * CleanXfac_1, ytop, "\x1a"); - } - if (CanScrollDown) - { - M_DrawConText(CR_ORANGE, 3 * CleanXfac_1, y - 8*CleanYfac_1, "\x1b"); - } - Super::Drawer(); -} - void DOptionMenuDescriptor::CalcIndent() { // calculate the menu indent diff --git a/src/virtual.h b/src/virtual.h index f0479e663..a25e543ac 100644 --- a/src/virtual.h +++ b/src/virtual.h @@ -22,7 +22,7 @@ inline unsigned GetVirtualIndex(PClass *cls, const char *funcname) #define IFVIRTUALPTRNAME(self, cls, funcname) \ static unsigned VIndex = ~0u; \ if (VIndex == ~0u) { \ - VIndex = GetVirtualIndex(PClass::FindActor(cls), #funcname); \ + VIndex = GetVirtualIndex(PClass::FindClass(cls), #funcname); \ assert(VIndex != ~0u); \ } \ auto clss = self->GetClass(); \ diff --git a/wadsrc/static/zscript/menu/menu.txt b/wadsrc/static/zscript/menu/menu.txt index a1af55458..1d878e112 100644 --- a/wadsrc/static/zscript/menu/menu.txt +++ b/wadsrc/static/zscript/menu/menu.txt @@ -104,6 +104,7 @@ class Menu : Object native native virtual bool Responder(InputEvent ev); native virtual bool MenuEvent (int mkey, bool fromcontroller); native virtual bool MouseEvent(int type, int mx, int my); + native virtual void Ticker(); native virtual void Drawer(); native void Close(); native MenuItemBase GetItem(Name n); @@ -200,66 +201,3 @@ class OptionMenuDescriptor : MenuDescriptor native } } -class OptionMenu : Menu native -{ - native OptionMenuDescriptor mDesc; - native bool CanScrollUp; - native bool CanScrollDown; - native int VisBottom; - native OptionMenuItem mFocusControl; - - //============================================================================= - // - // - // - //============================================================================= - - virtual void Init(Menu parent, OptionMenuDescriptor desc) - { - mParentMenu = parent; - mDesc = desc; - if (mDesc != NULL && mDesc.mSelectedItem == -1) mDesc.mSelectedItem = FirstSelectable(); - - } - - //============================================================================= - // - // - // - //============================================================================= - - 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 void SetFocus(MenuItemBase fc) - { - mFocusControl = OptionMenuItem(fc); - } - - override bool CheckFocus(MenuItemBase fc) - { - return mFocusControl == fc; - } - - override void ReleaseFocus() - { - mFocusControl = NULL; - } - - -} - diff --git a/wadsrc/static/zscript/menu/optionmenu.txt b/wadsrc/static/zscript/menu/optionmenu.txt index 78feb2336..7d2f34c77 100644 --- a/wadsrc/static/zscript/menu/optionmenu.txt +++ b/wadsrc/static/zscript/menu/optionmenu.txt @@ -1,7 +1,439 @@ +/* +** 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. +**--------------------------------------------------------------------------- +** +*/ +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(); + } + + + //============================================================================= + // + // + // + //============================================================================= + + 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 (InputEvent ev) + { + if (ev.type == InputEvent.GUI_Event) + { + if (ev.subtype == InputEvent.GUI_WheelUp) + { + int scrollamt = MIN(2, mDesc.mScrollPos); + mDesc.mScrollPos -= scrollamt; + return true; + } + else if (ev.subtype == InputEvent.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 @@ -29,3 +461,9 @@ class CompatibilityMenu : OptionMenu DTA_CleanNoMove_1, true); } } + +class JoystickConfigMenu : OptionMenu +{ + // This is not really needed anymore but needs to be kept for old MENUDEFs that keep the entry +} +