diff options
Diffstat (limited to 'Samples/OculusWorldDemo/OptionMenu.cpp')
-rw-r--r-- | Samples/OculusWorldDemo/OptionMenu.cpp | 896 |
1 files changed, 896 insertions, 0 deletions
diff --git a/Samples/OculusWorldDemo/OptionMenu.cpp b/Samples/OculusWorldDemo/OptionMenu.cpp new file mode 100644 index 0000000..283136d --- /dev/null +++ b/Samples/OculusWorldDemo/OptionMenu.cpp @@ -0,0 +1,896 @@ +/************************************************************************************ + +Filename : OptionMenu.h +Content : Option selection and editing for OculusWorldDemo +Created : March 7, 2014 +Authors : Michael Antonov, Caleb Leak + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*************************************************************************************/ + +#include "OptionMenu.h" + +// Embed the font. +#include "../CommonSrc/Render/Render_FontEmbed_DejaVu48.h" + + +//------------------------------------------------------------------------------------- +bool OptionShortcut::MatchKey(KeyCode key, bool shift) const +{ + for (UInt32 i = 0; i < Keys.GetSize(); i++) + { + if (Keys[i].Key != key) + continue; + + if (!shift && Keys[i].ShiftUsage == ShortcutKey::Shift_RequireOn) + continue; + + if (shift && Keys[i].ShiftUsage == ShortcutKey::Shift_RequireOff) + continue; + + if(Keys[i].ShiftUsage == ShortcutKey::Shift_Modify) + { + pNotify->CallNotify(&shift); + } + else + { + pNotify->CallNotify(); + } + return true; + } + return false; +} + +bool OptionShortcut::MatchGamepadButton(UInt32 gamepadButtonMask) const +{ + for (UInt32 i = 0; i < GamepadButtons.GetSize(); i++) + { + if (GamepadButtons[i] & gamepadButtonMask) + { + if (pNotify != NULL) + { + pNotify->CallNotify(); + } + return true; + } + } + return false; +} + + +//------------------------------------------------------------------------------------- +String OptionMenuItem::PopNamespaceFrom(OptionMenuItem* menuItem) +{ + String label = menuItem->Label; + for (UInt32 i = 0; i < label.GetLength(); i++) + { + if (label.GetCharAt(i) == '.') + { + String ns = label.Substring(0, i); + menuItem->Label = label.Substring(i + 1, label.GetLength()); + return ns; + } + } + return ""; +} + +//------------------------------------------------------------------------------------- + +String OptionVar::FormatEnum(OptionVar* var) +{ + UInt32 index = var->GetEnumIndex(); + if (index < var->EnumValues.GetSize()) + return var->EnumValues[index].Name; + return String("<Bad enum index>"); +} + +String OptionVar::FormatInt(OptionVar* var) +{ + char buff[64]; + OVR_sprintf(buff, sizeof(buff), var->FormatString, *var->AsInt()); + return String(buff); +} + +String OptionVar::FormatFloat(OptionVar* var) +{ + char buff[64]; + OVR_sprintf(buff, sizeof(buff), var->FormatString, *var->AsFloat() * var->FormatScale); + return String(buff); +} + +String OptionVar::FormatBool(OptionVar* var) +{ + return *var->AsBool() ? "On" : "Off"; +} + + +OptionVar::OptionVar(const char* name, void* pvar, VarType type, + FormatFunction formatFunction, + UpdateFunction updateFunction) +{ + Label = name; + Type = type; + this->pVar = pvar; + fFormat = formatFunction; + fUpdate = updateFunction; + pNotify = 0; + FormatString= 0; + + MaxFloat = Math<float>::MaxValue; + MinFloat = -Math<float>::MaxValue; + StepFloat = 1.0f; + FormatScale = 1.0f; + + MaxInt = 0x7FFFFFFF; + MinInt = -(MaxInt) - 1; + StepInt = 1; + + ShortcutUp.pNotify = new FunctionNotifyContext<OptionVar, bool>(this, &OptionVar::NextValue); + ShortcutDown.pNotify = new FunctionNotifyContext<OptionVar, bool>(this, &OptionVar::PrevValue); +} + +OptionVar::OptionVar(const char* name, SInt32* pvar, + SInt32 min, SInt32 max, SInt32 stepSize, + const char* formatString, + FormatFunction formatFunction, + UpdateFunction updateFunction) +{ + Label = name; + Type = Type_Int; + this->pVar = pvar; + fFormat = formatFunction ? formatFunction : FormatInt; + fUpdate = updateFunction; + pNotify = 0; + FormatString= formatString; + + MinInt = min; + MaxInt = max; + StepInt = stepSize; + + ShortcutUp.pNotify = new FunctionNotifyContext<OptionVar, bool>(this, &OptionVar::NextValue); + ShortcutDown.pNotify = new FunctionNotifyContext<OptionVar, bool>(this, &OptionVar::PrevValue); +} + +// Float with range and step size. +OptionVar::OptionVar(const char* name, float* pvar, + float minf, float maxf, float stepSize, + const char* formatString, float formatScale, + FormatFunction formatFunction, + UpdateFunction updateFunction) +{ + Label = name; + Type = Type_Float; + this->pVar = pvar; + fFormat = formatFunction ? formatFunction : FormatFloat; + fUpdate = updateFunction; + pNotify = 0; + FormatString= formatString ? formatString : "%.3f"; + + MinFloat = minf; + MaxFloat = maxf; + StepFloat = stepSize; + FormatScale = formatScale; + + ShortcutUp.pNotify = new FunctionNotifyContext<OptionVar, bool>(this, &OptionVar::NextValue); + ShortcutDown.pNotify = new FunctionNotifyContext<OptionVar, bool>(this, &OptionVar::PrevValue); +} + +OptionVar::~OptionVar() +{ + if (pNotify) + delete pNotify; +} + +void OptionVar::NextValue(bool* pFastStep) +{ + bool fastStep = (pFastStep != NULL && *pFastStep); + switch (Type) + { + case Type_Enum: + *AsInt() = ((GetEnumIndex() + 1) % EnumValues.GetSize()); + break; + + case Type_Int: + *AsInt() = Alg::Min<SInt32>(*AsInt() + StepInt * (fastStep ? 5 : 1), MaxInt); + break; + + case Type_Float: + // TODO: Will behave strange with NaN values. + *AsFloat() = Alg::Min<float>(*AsFloat() + StepFloat * (fastStep ? 5.0f : 1.0f), MaxFloat); + break; + + case Type_Bool: + *AsBool() = !*AsBool(); + break; + } + + SignalUpdate(); +} + +void OptionVar::PrevValue(bool* pFastStep) +{ + bool fastStep = (pFastStep != NULL && *pFastStep); + switch (Type) + { + case Type_Enum: + *AsInt() = ((GetEnumIndex() + (UInt32)EnumValues.GetSize() - 1) % EnumValues.GetSize()); + break; + + case Type_Int: + *AsInt() = Alg::Max<SInt32>(*AsInt() - StepInt * (fastStep ? 5 : 1), MinInt); + break; + + case Type_Float: + // TODO: Will behave strange with NaN values. + *AsFloat() = Alg::Max<float>(*AsFloat() - StepFloat * (fastStep ? 5.0f : 1.0f), MinFloat); + break; + + case Type_Bool: + *AsBool() = !*AsBool(); + break; + } + + SignalUpdate(); +} + +String OptionVar::HandleShortcutUpdate() +{ + SignalUpdate(); + return Label + " - " + GetValue(); +} + +String OptionVar::ProcessShortcutKey(KeyCode key, bool shift) +{ + if (ShortcutUp.MatchKey(key, shift) || ShortcutDown.MatchKey(key, shift)) + { + return HandleShortcutUpdate(); + } + + return String(); +} + +String OptionVar::ProcessShortcutButton(UInt32 buttonMask) +{ + if (ShortcutUp.MatchGamepadButton(buttonMask) || ShortcutDown.MatchGamepadButton(buttonMask)) + { + return HandleShortcutUpdate(); + } + return String(); +} + + +OptionVar& OptionVar::AddEnumValue(const char* displayName, SInt32 value) +{ + EnumEntry entry; + entry.Name = displayName; + entry.Value = value; + EnumValues.PushBack(entry); + return *this; +} + +String OptionVar::GetValue() +{ + return fFormat(this); +} + +UInt32 OptionVar::GetEnumIndex() +{ + OVR_ASSERT(Type == Type_Enum); + OVR_ASSERT(EnumValues.GetSize() > 0); + + // TODO: Change this from a linear search to binary or a hash. + for (UInt32 i = 0; i < EnumValues.GetSize(); i++) + { + if (EnumValues[i].Value == *AsInt()) + return i; + } + + // Enum values should always be found. + OVR_ASSERT(false); + return 0; +} + + +//------------------------------------------------------------------------------------- + +OptionSelectionMenu::OptionSelectionMenu(OptionSelectionMenu* parentMenu) +{ + DisplayState = Display_None; + SelectedIndex = 0; + SelectionActive = false; + ParentMenu = parentMenu; + + PopupMessageTimeout = 0.0; + PopupMessageBorder = false; + + // Setup handlers for menu navigation actions. + NavShortcuts[Nav_Up].pNotify = new FunctionNotifyContext<OptionSelectionMenu, bool>(this, &OptionSelectionMenu::HandleUp); + NavShortcuts[Nav_Down].pNotify = new FunctionNotifyContext<OptionSelectionMenu, bool>(this, &OptionSelectionMenu::HandleDown); + NavShortcuts[Nav_Left].pNotify = new FunctionNotifySimple<OptionSelectionMenu>(this, &OptionSelectionMenu::HandleLeft); + NavShortcuts[Nav_Right].pNotify = new FunctionNotifySimple<OptionSelectionMenu>(this, &OptionSelectionMenu::HandleRight); + NavShortcuts[Nav_Select].pNotify = new FunctionNotifySimple<OptionSelectionMenu>(this, &OptionSelectionMenu::HandleSelect); + NavShortcuts[Nav_Back].pNotify = new FunctionNotifySimple<OptionSelectionMenu>(this, &OptionSelectionMenu::HandleBack); + ToggleShortcut.pNotify = new FunctionNotifySimple<OptionSelectionMenu>(this, &OptionSelectionMenu::HandleMenuToggle); + ToggleSingleItemShortcut.pNotify = new FunctionNotifySimple<OptionSelectionMenu>(this, &OptionSelectionMenu::HandleSingleItemToggle); + + // Bind keys and buttons to menu navigation actions. + NavShortcuts[Nav_Up].AddShortcut(ShortcutKey(Key_Up, ShortcutKey::Shift_Modify)); + NavShortcuts[Nav_Up].AddShortcut(Gamepad_Up); + + NavShortcuts[Nav_Down].AddShortcut(ShortcutKey(Key_Down, ShortcutKey::Shift_Modify)); + NavShortcuts[Nav_Down].AddShortcut(Gamepad_Down); + + NavShortcuts[Nav_Left].AddShortcut(ShortcutKey(Key_Left)); + NavShortcuts[Nav_Left].AddShortcut(Gamepad_Left); + + NavShortcuts[Nav_Right].AddShortcut(ShortcutKey(Key_Right)); + NavShortcuts[Nav_Right].AddShortcut(Gamepad_Right); + + NavShortcuts[Nav_Select].AddShortcut(ShortcutKey(Key_Return)); + NavShortcuts[Nav_Select].AddShortcut(Gamepad_A); + + NavShortcuts[Nav_Back].AddShortcut(ShortcutKey(Key_Escape)); + NavShortcuts[Nav_Back].AddShortcut(Gamepad_B); + + ToggleShortcut.AddShortcut(ShortcutKey(Key_Tab, ShortcutKey::Shift_Ignore)); + ToggleShortcut.AddShortcut(Gamepad_Start); + + ToggleSingleItemShortcut.AddShortcut(ShortcutKey(Key_Backspace, ShortcutKey::Shift_Ignore)); +} + +OptionSelectionMenu::~OptionSelectionMenu() +{ + for (UInt32 i = 0; i < Items.GetSize(); i++) + delete Items[i]; +} + +bool OptionSelectionMenu::OnKey(OVR::KeyCode key, int chr, bool down, int modifiers) +{ + bool shift = ((modifiers & Mod_Shift) != 0); + + if (down) + { + String s = ProcessShortcutKey(key, shift); + if (!s.IsEmpty()) + { + PopupMessage = s; + PopupMessageTimeout = ovr_GetTimeInSeconds() + 4.0f; + PopupMessageBorder = false; + return true; + } + } + + if (GetSubmenu() != NULL) + { + return GetSubmenu()->OnKey(key, chr, down, modifiers); + } + + if (down) + { + if (ToggleShortcut.MatchKey(key, shift)) + return true; + + if (ToggleSingleItemShortcut.MatchKey(key, shift)) + return true; + + if (DisplayState == Display_None) + return false; + + for (int i = 0; i < Nav_LAST; i++) + { + if (NavShortcuts[i].MatchKey(key, shift)) + return true; + } + } + + // Let the caller process keystroke + return false; +} + +bool OptionSelectionMenu::OnGamepad(UInt32 buttonMask) +{ + // Check global shortcuts first. + String s = ProcessShortcutButton(buttonMask); + if (!s.IsEmpty()) + { + PopupMessage = s; + PopupMessageTimeout = ovr_GetTimeInSeconds() + 4.0f; + return true; + } + + if (GetSubmenu() != NULL) + { + return GetSubmenu()->OnGamepad(buttonMask); + } + + if (ToggleShortcut.MatchGamepadButton(buttonMask)) + return true; + + if (DisplayState == Display_None) + return false; + + for (int i = 0; i < Nav_LAST; i++) + { + if (NavShortcuts[i].MatchGamepadButton(buttonMask)) + return true; + } + + // Let the caller process keystroke + return false; +} + +String OptionSelectionMenu::ProcessShortcutKey(KeyCode key, bool shift) +{ + String s; + + for (UPInt i = 0; (i < Items.GetSize()) && s.IsEmpty(); i++) + { + s = Items[i]->ProcessShortcutKey(key, shift); + } + + return s; +} + +String OptionSelectionMenu::ProcessShortcutButton(UInt32 buttonMask) +{ + String s; + + for (UPInt i = 0; (i < Items.GetSize()) && s.IsEmpty(); i++) + { + s = Items[i]->ProcessShortcutButton(buttonMask); + } + + return s; +} + +// Fills in inclusive character range; returns false if line not found. +bool FindLineCharRange(const char* text, int searchLine, UPInt charRange[2]) +{ + UPInt i = 0; + + for (int line = 0; line <= searchLine; line ++) + { + if (line == searchLine) + { + charRange[0] = i; + } + + // Find end of line. + while (text[i] != '\n' && text[i] != 0) + { + i++; + } + + if (line == searchLine) + { + charRange[1] = (charRange[0] == i) ? charRange[0] : i-1; + return true; + } + + if (text[i] == 0) + break; + // Skip newline + i++; + } + + return false; +} + + +void OptionSelectionMenu::Render(RenderDevice* prender, String title) +{ + // If we are invisible, render shortcut notifications. + // Both child and parent have visible == true even if only child is shown. + if (DisplayState == Display_None) + { + renderShortcutChangeMessage(prender); + return; + } + + title += Label; + + // Delegate to sub-menu if active. + if (GetSubmenu() != NULL) + { + if (title.GetSize() > 0) + title += " > "; + + GetSubmenu()->Render(prender, title); + return; + } + + Color focusColor(180, 80, 20, 210); + Color pickedColor(120, 55, 10, 140); + Color titleColor(0x18, 0x1A, 0x4D, 210); + Color titleOutlineColor(0x18, 0x18, 0x18, 240); + + float labelsSize[2] = {0.0f, 0.0f}; + float bufferSize[2] = {0.0f, 0.0f}; + float valuesSize[2] = {0.0f, 0.0f}; + float maxValueWidth = 0.0f; + + UPInt selection[2] = { 0, 0 }; + Vector2f labelSelectionRect[2]; + Vector2f valueSelectionRect[2]; + bool havelLabelSelection = false; + bool haveValueSelection = false; + + float textSize = 22.0f; + prender->MeasureText(&DejaVu, " ", textSize, bufferSize); + + String values; + String menuItems; + + int highlightIndex = 0; + if (DisplayState == Display_Menu) + { + highlightIndex = SelectedIndex; + for (UInt32 i = 0; i < Items.GetSize(); i++) + { + if (i > 0) + values += "\n"; + values += Items[i]->GetValue(); + } + + for (UInt32 i = 0; i < Items.GetSize(); i++) + { + if (i > 0) + menuItems += "\n"; + menuItems += Items[i]->GetLabel(); + } + } + else + { + values = Items[SelectedIndex]->GetValue(); + menuItems = Items[SelectedIndex]->GetLabel(); + } + + // Measure labels + const char* menuItemsCStr = menuItems.ToCStr(); + havelLabelSelection = FindLineCharRange(menuItemsCStr, highlightIndex, selection); + prender->MeasureText(&DejaVu, menuItemsCStr, textSize, labelsSize, + selection, labelSelectionRect); + + // Measure label-to-value gap + const char* valuesCStr = values.ToCStr(); + haveValueSelection = FindLineCharRange(valuesCStr, highlightIndex, selection); + prender->MeasureText(&DejaVu, valuesCStr, textSize, valuesSize, selection, valueSelectionRect); + + // Measure max value size (absolute size varies, so just use a reasonable max) + maxValueWidth = prender->MeasureText(&DejaVu, "Max value width", textSize); + maxValueWidth = Alg::Max(maxValueWidth, valuesSize[0]); + + Vector2f borderSize(4.0f, 4.0f); + Vector2f totalDimensions = borderSize * 2 + Vector2f(bufferSize[0], 0) + Vector2f(maxValueWidth, 0) + + Vector2f(labelsSize[0], labelsSize[1]); + + Vector2f fudgeOffset= Vector2f(10.0f, 25.0f); // This offset looks better + Vector2f topLeft = (-totalDimensions / 2.0f) + fudgeOffset; + Vector2f bottomRight = topLeft + totalDimensions; + + // If displaying a single item, shift it down. + if (DisplayState == Display_SingleItem) + { + topLeft.y += textSize * 7; + bottomRight.y += textSize * 7; + } + + prender->FillRect(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y, Color(40,40,100,210)); + + Vector2f labelsPos = topLeft + borderSize; + Vector2f valuesPos = labelsPos + Vector2f(labelsSize[0], 0) + Vector2f(bufferSize[0], 0); + + // Highlight selected label + Vector2f selectionInset = Vector2f(0.3f, 2.0f); + if (DisplayState == Display_Menu) + { + Vector2f labelSelectionTopLeft = labelsPos + labelSelectionRect[0] - selectionInset; + Vector2f labelSelectionBottomRight = labelsPos + labelSelectionRect[1] + selectionInset; + + prender->FillRect(labelSelectionTopLeft.x, labelSelectionTopLeft.y, + labelSelectionBottomRight.x, labelSelectionBottomRight.y, + SelectionActive ? pickedColor : focusColor); + } + + // Highlight selected value if active + if (SelectionActive) + { + Vector2f valueSelectionTopLeft = valuesPos + valueSelectionRect[0] - selectionInset; + Vector2f valueSelectionBottomRight = valuesPos + valueSelectionRect[1] + selectionInset; + prender->FillRect(valueSelectionTopLeft.x, valueSelectionTopLeft.y, + valueSelectionBottomRight.x, valueSelectionBottomRight.y, + focusColor); + } + + // Measure and draw title + if (DisplayState == Display_Menu && title.GetLength() > 0) + { + Vector2f titleDimensions; + prender->MeasureText(&DejaVu, title.ToCStr(), textSize, &titleDimensions.x); + Vector2f titleTopLeft = topLeft - Vector2f(0, borderSize.y) * 2 - Vector2f(0, titleDimensions.y); + titleDimensions.x = totalDimensions.x; + + prender->FillRect(titleTopLeft.x, titleTopLeft.y, + titleTopLeft.x + totalDimensions.x, + titleTopLeft.y + titleDimensions.y + borderSize.y * 2, + titleOutlineColor); + + prender->FillRect(titleTopLeft.x + borderSize.x / 2, titleTopLeft.y + borderSize.y / 2, + titleTopLeft.x + totalDimensions.x - borderSize.x / 2, + titleTopLeft.y + borderSize.y / 2 + titleDimensions.y, + titleColor); + + prender->RenderText(&DejaVu, title.ToCStr(), titleTopLeft.x + borderSize.x, + titleTopLeft.y + borderSize.y, textSize, Color(255,255,0,210)); + } + + prender->RenderText(&DejaVu, menuItemsCStr, labelsPos.x, labelsPos.y, textSize, Color(255,255,0,210)); + + prender->RenderText(&DejaVu, valuesCStr, valuesPos.x, valuesPos.y, textSize, Color(255,255,0,210)); +} + + +void OptionSelectionMenu::renderShortcutChangeMessage(RenderDevice* prender) +{ + if (ovr_GetTimeInSeconds() < PopupMessageTimeout) + { + DrawTextBox(prender, 0, 120, 22.0f, PopupMessage.ToCStr(), + DrawText_Center | (PopupMessageBorder ? DrawText_Border : 0)); + } +} + + +void OptionSelectionMenu::SetPopupMessage(const char* format, ...) +{ + //Lock::Locker lock(pManager->GetHandlerLock()); + char textBuff[2048]; + va_list argList; + va_start(argList, format); + OVR_vsprintf(textBuff, sizeof(textBuff), format, argList); + va_end(argList); + + // Message will time out in 4 seconds. + PopupMessage = textBuff; + PopupMessageTimeout = ovr_GetTimeInSeconds() + 4.0f; + PopupMessageBorder = false; +} + +void OptionSelectionMenu::SetPopupTimeout(double timeoutSeconds, bool border) +{ + PopupMessageTimeout = ovr_GetTimeInSeconds() + timeoutSeconds; + PopupMessageBorder = border; +} + + + +void OptionSelectionMenu::AddItem(OptionMenuItem* menuItem) +{ + String ns = PopNamespaceFrom(menuItem); + + if (ns.GetLength() == 0) + { + Items.PushBack(menuItem); + } + else + { + // Item is part of a submenu, add it to that instead. + GetOrCreateSubmenu(ns)->AddItem(menuItem); + } +} + +//virtual +void OptionSelectionMenu::Select() +{ + SelectedIndex = 0; + SelectionActive = false; + DisplayState = Display_Menu; +} + + +OptionSelectionMenu* OptionSelectionMenu::GetSubmenu() +{ + if (!SelectionActive) + return NULL; + + OptionSelectionMenu* submenu = dynamic_cast<OptionSelectionMenu*>(Items[SelectedIndex]); + return submenu; +} + + +OptionSelectionMenu* OptionSelectionMenu::GetOrCreateSubmenu(String submenuName) +{ + for (UInt32 i = 0; i < Items.GetSize(); i++) + { + OptionSelectionMenu* submenu = dynamic_cast<OptionSelectionMenu*>(Items[i]); + + if (submenu != NULL && submenu->Label == submenuName) + { + return submenu; + } + } + + // Submenu doesn't exist, create it. + OptionSelectionMenu* newSubmenu = new OptionSelectionMenu(this); + newSubmenu->Label = submenuName; + Items.PushBack(newSubmenu); + return newSubmenu; +} + +void OptionSelectionMenu::HandleUp(bool* pFast) +{ + int numItems = (int)Items.GetSize(); + if (SelectionActive) + Items[SelectedIndex]->NextValue(pFast); + else + SelectedIndex = ((SelectedIndex - 1 + numItems) % numItems); +} + +void OptionSelectionMenu::HandleDown(bool* pFast) +{ + if (SelectionActive) + Items[SelectedIndex]->PrevValue(pFast); + else + SelectedIndex = ((SelectedIndex + 1) % Items.GetSize()); +} + +void OptionSelectionMenu::HandleLeft() +{ + if (DisplayState != Display_Menu) + return; + + if (SelectionActive) + SelectionActive = false; + else if (ParentMenu) + { + // Escape to parent menu + ParentMenu->SelectionActive = false; + DisplayState = Display_Menu; + } +} + +void OptionSelectionMenu::HandleRight() +{ + if (DisplayState != Display_Menu) + return; + + if (!SelectionActive) + { + SelectionActive = true; + Items[SelectedIndex]->Select(); + } +} + +void OptionSelectionMenu::HandleSelect() +{ + if (!SelectionActive) + { + SelectionActive = true; + Items[SelectedIndex]->Select(); + } + else + { + Items[SelectedIndex]->NextValue(); + } +} + +void OptionSelectionMenu::HandleBack() +{ + if (DisplayState != Display_Menu) + return; + + if (!SelectionActive) + DisplayState = Display_None; + else + SelectionActive = false; +} + +void OptionSelectionMenu::HandleMenuToggle() +{ + // Mark this & parent With correct visibility. + OptionSelectionMenu* menu = this; + + if (DisplayState == Display_Menu) + DisplayState = Display_None; + else + DisplayState = Display_Menu; + + while (menu) + { + menu->DisplayState = DisplayState; + menu = menu->ParentMenu; + } + // Hide message + PopupMessageTimeout = 0; +} + +void OptionSelectionMenu::HandleSingleItemToggle() +{ + // Mark this & parent With correct visibility. + OptionSelectionMenu* menu = this; + + if (DisplayState == Display_SingleItem) + DisplayState = Display_None; + else + { + DisplayState = Display_SingleItem; + SelectionActive = true; + } + + while (menu) + { + menu->DisplayState = DisplayState; + menu = menu->ParentMenu; + } + // Hide message + PopupMessageTimeout = 0; +} + + +//------------------------------------------------------------------------------------- +// **** Text Rendering / Management + +void DrawTextBox(RenderDevice* prender, float x, float y, + float textSize, const char* text, unsigned centerType) +{ + float ssize[2] = {0.0f, 0.0f}; + + prender->MeasureText(&DejaVu, text, textSize, ssize); + + // Treat 0 a VCenter. + if (centerType & DrawText_HCenter) + { + x -= ssize[0]/2; + } + if (centerType & DrawText_VCenter) + { + y -= ssize[1]/2; + } + + const float borderSize = 4.0f; + float linesHeight = 0.0f; + + if (centerType & DrawText_Border) + linesHeight = 10.0f; + + prender->FillRect(x-borderSize, y-borderSize - linesHeight, + x+ssize[0]+borderSize, y+ssize[1]+borderSize + linesHeight, + Color(40,40,100,210)); + + if (centerType & DrawText_Border) + { + // Add top & bottom lines + float topLineY = y-borderSize - linesHeight * 0.5f, + bottomLineY = y+ssize[1]+borderSize + linesHeight * 0.5f; + + prender->FillRect(x-borderSize * 0.5f, topLineY, + x+ssize[0]+borderSize * 0.5f, topLineY + 2.0f, + Color(255,255,0,210)); + prender->FillRect(x-borderSize * 0.5f, bottomLineY, + x+ssize[0]+borderSize * 0.5f, bottomLineY + 2.0f, + Color(255,255,0,210)); + } + + prender->RenderText(&DejaVu, text, x, y, textSize, Color(255,255,0,210)); +} + +void CleanupDrawTextFont() +{ + if (DejaVu.fill) + { + DejaVu.fill->Release(); + DejaVu.fill = 0; + } +} |