/* =========================================================================== Copyright (C) 2015-2024 the OpenMoHAA team This file is part of OpenMoHAA source code. OpenMoHAA source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. OpenMoHAA source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenMoHAA source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "ui_local.h" #include "localization.h" CLASS_DECLARATION( UIWidget, UIPopupMenu, NULL ) { { &W_LeftMouseUp, &UIPopupMenu::MouseReleased }, { &W_MouseMoved, &UIPopupMenu::MouseMoved }, { &W_MouseExited, &UIPopupMenu::MouseExited }, { &W_Deactivated, &UIPopupMenu::DismissEvent }, { NULL, NULL } }; uipopup_type UI_PopupTypeStringToInt(str type) { if (type == "event_string") { return UIP_EVENT_STRING; } else if (type == "command") { return UIP_CMD; } else if (type == "cvar") { return UIP_CVAR; } else if (type == "separator") { return UIP_SEPARATOR; } return UIP_NONE; } UIPopupMenu::UIPopupMenu() { m_selected = -1; m_listener = NULL; m_parentMenu = NULL; m_submenu = -1; m_marlett = UIFont("marlett"); m_autodismiss = qtrue; m_describe = NULL; m_submenuptr = NULL; m_highlightFGColor = UColor(0.9f, 0.8f, 0.6f, 1.0f); m_highlightBGColor = UColor(0.21f, 0.18f, 0.015f, 1.0f); m_background_color = UColor(0.02f, 0.07f, 0.005f, 1.0f); m_foreground_color = UHudColor; m_iFontAlignmentHorizontal = FONT_JUSTHORZ_RIGHT; } UIPopupMenu::~UIPopupMenu() { if (m_parentMenu) { m_submenu = -1; } if (m_submenu != -1 && m_submenuptr) { m_submenuptr->m_parentMenu = NULL; delete m_submenuptr; } } uipopup_describe *UIPopupMenu::getDescribeFromPoint ( const UIPoint2D& p ) { // FIXME: macro or inline for this somewhere? if (p.y < 4.0f || p.x < 2.0f || m_frame.size.width - 2.0f <= p.x) { return NULL; } float top = getVirtualScale()[1] * 4.0f; for (int i = 1; i <= m_describe->NumObjects(); i++) { uipopup_describe* desc = m_describe->ObjectAt(i); float height = getDescribeHeight(desc); if (p.y >= top && p.y < top + height) { return desc; } top += height; } return NULL; } void UIPopupMenu::MakeSubMenu ( void ) { UIRect2D place; for (int i = 1; i <= m_submenu; i++) { place.pos.y += getDescribeHeight(m_describe->ObjectAt(i)); } place.size.height = getDescribeHeight(m_describe->ObjectAt(m_submenu)) + 8.0f; place.size.width = m_frame.size.width - 40.0f; place.pos.x = 20.0f; if (place.size.width < 0.0f) { place.pos.x -= place.size.width * 0.5f; place.size.width = 0.0f; } place.pos.x += m_screenframe.pos.x; place.pos.y += m_screenframe.pos.y; UIPopupMenu *me = new UIPopupMenu(); me->Create( (Container *)m_describe->ObjectAt(m_submenu)->data, m_listener, place, UIP_WHERE_RIGHT, qfalse, -1.0f ); me->m_parentMenu = this; m_submenuptr = me; } float UIPopupMenu::getDescribeHeight ( uipopup_describe *d ) { if (d->type == UIP_SEPARATOR) { return 8.0f * getVirtualScale()[0]; } return m_font->getHeight(getVirtualScale()); } float UIPopupMenu::getDescribeWidth ( uipopup_describe *d ) { return m_font->getWidth(d->title, -1) * getVirtualScale()[0]; } bool UIPopupMenu::MouseInSubmenus ( void ) { if (m_submenu == -1) { return false; } if (m_submenuptr->m_screenframe.contains(uid.mouseX, uid.mouseY)) { return true; } return m_submenuptr->MouseInSubmenus(); } void UIPopupMenu::Create ( Container *describe, Listener *listener, const UIPoint2D& where ) { Create(describe, listener, UIRect2D(where.x, where.y, 0.0f, 0.0f), UIP_WHERE_RIGHT, qfalse, -1.0f); } void UIPopupMenu::Create ( Container *describe, Listener *listener, const UIRect2D& createRect, uipopup_where where, qboolean bVirtualSize, float width ) { m_describe = describe; m_listener = listener; setFont("verdana-12"); if (bVirtualSize) { m_bVirtual = qtrue; m_vVirtualScale[0] = (float)uid.vidWidth / SCREEN_WIDTH; m_vVirtualScale[1] = (float)uid.vidHeight / SCREEN_HEIGHT; } if (!(m_describe && m_listener)) { return; } UISize2D totalSize; for (int i = 1; i <= m_describe->NumObjects(); i++) { uipopup_describe* desc = m_describe->ObjectAt(i); totalSize.height += getDescribeHeight(desc); if (desc->type != UIP_SEPARATOR) { float newWidth = getDescribeWidth(desc); if (newWidth > totalSize.width) { totalSize.width = newWidth; } } } totalSize.height /= getVirtualScale()[1]; if (width != -1.0f) { totalSize.width = width; } if (totalSize.width == 0.0f || totalSize.height == 0.0f) { return; } totalSize.width += 24.0f; totalSize.height += 8.0f; //if (m_bVirtual) //{ totalSize.width *= getVirtualScale()[0]; totalSize.height *= getVirtualScale()[1]; //} UIPoint2D place; place.x = (where) ? createRect.pos.x : createRect.size.width + createRect.pos.x; place.y = (where) ? createRect.size.height + createRect.pos.y : createRect.pos.y; // FIXME: avoid code repetition, extract these checks below to an inline func // Adjust position of menu rect if it goes beyond the screen width if (place.x + totalSize.width > uid.vidWidth) { place.x = (where) ? createRect.size.width + createRect.pos.x : createRect.pos.x; if (place.x < totalSize.width) { place.x = std::fmax(0.0f, uid.vidWidth - totalSize.width); } else { place.x -= totalSize.width; } } // Adjust position of menu rect if it goes beyond the screen height if (place.y + totalSize.height > uid.vidHeight) { place.y = (where) ? createRect.pos.y : createRect.size.height + createRect.pos.y; if (place.y < totalSize.height) { place.y = std::fmax(0.0f, uid.vidHeight - totalSize.height); } else { place.y -= totalSize.height; } } InitFrame(NULL, place.x, place.y, totalSize.width, totalSize.height, 1); } void UIPopupMenu::YouAreASubmenu ( UIPopupMenu *me ) { m_parentMenu = me; } void UIPopupMenu::Draw ( void ) { const float originX = getVirtualScale()[0] * 4.0f; const float originY = getVirtualScale()[1] * 4.0f; float fFontHeight = m_font->getHeight(getVirtualScale()); float top = originY; for (int i = 1; i <= m_describe->NumObjects(); i++) { uipopup_describe* desc = m_describe->ObjectAt(i); if (desc->type == UIP_SEPARATOR) { DrawBox( originX * 2.0f, originY + top, m_frame.size.width - (originX * 4.0f), 1.0f, m_foreground_color, 1.0f ); } else { UColor textColor; if (m_selected == i || m_submenu == i) { DrawBox( originX / 2.0f, top, m_frame.size.width - originX, fFontHeight, m_highlightBGColor, m_local_alpha ); textColor = m_highlightFGColor; } else { textColor = m_foreground_color; } m_font->setColor(textColor); m_marlett.setColor(textColor); const char *text = Sys_LV_CL_ConvertString(desc->title); m_font->Print(4.0f, top / getVirtualScale()[1], text, -1, getVirtualScale()); if (desc->type == UIP_SUBMENU) { m_marlett.PrintJustified( UIRect2D(0.0f, top, m_frame.size.width - originX, getDescribeHeight(desc)), m_iFontAlignmentHorizontal, m_iFontAlignmentVertical, "4", getVirtualScale() ); } } top += getDescribeHeight(desc); } } void UIPopupMenu::MouseReleased ( Event *ev ) { UIPoint2D p = MouseEventToClientPoint(ev); // unused if (m_selected == -1 || m_selected > m_describe->NumObjects()) { return; } uipopup_describe *desc = m_describe->ObjectAt(m_selected); if (!desc || desc->type == UIP_SEPARATOR || desc->type == UIP_SUBMENU) { return; } // NOTE: Dismiss() below can delete this entire instance! // Save the m_listener pointer for later, as `this->m_listener` // cannot be used after the instance was freed. Listener* listener = m_listener; if (m_autodismiss) { Dismiss(); } switch (desc->type) { case UIP_EVENT: // make sure it's passed in as an Event&, as ProcessEvent(Event*) would try to delete the static event! listener->ProcessEvent(*(Event*)desc->data); break; case UIP_EVENT_STRING: listener->ProcessEvent(new Event((const char*)desc->data)); break; case UIP_CMD: case UIP_CVAR: Cbuf_AddText((const char*)desc->data); break; default: return; } } void UIPopupMenu::MouseMoved ( Event *ev ) { UIPoint2D point = MouseEventToClientPoint(ev); uipopup_describe* describe = getDescribeFromPoint(point); if (describe) { int index = m_describe->IndexOfObject(describe); setSelection(index); } else { setSelection(-1); } } void UIPopupMenu::MouseExited ( Event *ev ) { MouseMoved(ev); } void UIPopupMenu::Dismiss ( void ) { // navigate to the topmost menu if (m_parentMenu) { m_parentMenu->Dismiss(); } else { delete this; } } void UIPopupMenu::DismissEvent ( Event *ev ) { Dismiss(); } void UIPopupMenu::setAutoDismiss ( bool b ) { m_autodismiss = b; } void UIPopupMenu::setNextSelection ( void ) { if (!m_describe->NumObjects()) { return; } if (m_selected == -1) { setSelection(0); return; } int sel = m_selected + 1; if (sel > m_describe->NumObjects()) { // wrap around sel = 0; } setSelection(sel); } void UIPopupMenu::setPrevSelection ( void ) { if (!m_describe->NumObjects()) { return; } if (m_selected == -1) { setSelection(0); return; } int sel; if (m_selected != 0) { sel = m_selected - 1; } else { // wrap around sel = m_describe->NumObjects(); } setSelection(sel); } uipopup_describe *UIPopupMenu::getSelectedDescribe ( void ) { if (m_selected == -1) { return NULL; } return m_describe->ObjectAt(m_selected); } void UIPopupMenu::setSelection ( int sel ) { int maxSel = m_describe->NumObjects(); if (sel > maxSel) { m_selected = maxSel; return; } int newSubMenu = -1; if (sel != -1) { uipopup_describe* desc = m_describe->ObjectAt(sel); if (desc && desc->type == UIP_SUBMENU) { newSubMenu = sel; } } m_selected = sel; if (newSubMenu == m_submenu) { return; } if (newSubMenu == -1) { if (m_submenuptr) { delete m_submenuptr; } m_submenu = -1; } else { if (m_submenu != -1 && m_submenuptr) { delete m_submenuptr; } m_submenu = newSubMenu; MakeSubMenu(); } } void UIPopupMenu::setHighlightFGColor ( UColor c ) { m_highlightFGColor = c; } void UIPopupMenu::setHighlightBGColor ( UColor c ) { m_highlightBGColor = c; } void UIPopupMenu::setPopupHighlightBGColor ( str menu, UColor c ) { // not present in disassembly } void UIPopupMenu::setPopupHighlightFGColor ( str menu, UColor c ) { // not present in disassembly } void UIPopupMenu::getPulldown ( str title ) { // not present in disassembly }