openmohaa/code/uilib/uipopupmenu.cpp
smallmodel 6fe1e86b31
Automatically scale UI elements for high resolutions
This fixes UI elements being tiny on high resolutions like 4K. Now most UI elements will scale automatically with resolutions above 1920x1080.
2024-11-30 22:40:00 +01:00

665 lines
11 KiB
C++

/*
===========================================================================
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<uipopup_describe*> *)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<uipopup_describe*> *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<uipopup_describe*> *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
}