openmohaa/code/uilib/uipopupmenu.cpp

666 lines
11 KiB
C++
Raw Normal View History

2016-03-27 11:49:47 +02:00
/*
===========================================================================
Copyright (C) 2015-2024 the OpenMoHAA team
2016-03-27 11:49:47 +02:00
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"
2016-03-27 11:49:47 +02:00
CLASS_DECLARATION( UIWidget, UIPopupMenu, NULL )
{
{ &W_LeftMouseUp, &UIPopupMenu::MouseReleased },
{ &W_MouseMoved, &UIPopupMenu::MouseMoved },
{ &W_MouseExited, &UIPopupMenu::MouseExited },
{ &W_Deactivated, &UIPopupMenu::DismissEvent },
{ NULL, NULL }
2016-03-27 11:49:47 +02:00
};
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;
}
2016-03-27 11:49:47 +02:00
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;
}
2016-03-27 11:49:47 +02:00
}
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;
}
2016-03-27 11:49:47 +02:00
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;
2016-03-27 11:49:47 +02:00
}
float UIPopupMenu::getDescribeHeight
(
uipopup_describe *d
)
{
if (d->type == UIP_SEPARATOR)
{
return 8.0f * getVirtualScale()[0];
}
return m_font->getHeight(getVirtualScale());
2016-03-27 11:49:47 +02:00
}
float UIPopupMenu::getDescribeWidth
(
uipopup_describe *d
)
{
return m_font->getWidth(d->title, -1) * getVirtualScale()[0];
2016-03-27 11:49:47 +02:00
}
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();
2016-03-27 11:49:47 +02:00
}
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);
2016-03-27 11:49:47 +02:00
}
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);
2016-03-27 11:49:47 +02:00
}
void UIPopupMenu::YouAreASubmenu
(
UIPopupMenu *me
)
{
m_parentMenu = me;
2016-03-27 11:49:47 +02:00
}
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);
}
2016-03-27 11:49:47 +02:00
}
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;
}
2016-03-27 11:49:47 +02:00
}
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);
}
2016-03-27 11:49:47 +02:00
}
void UIPopupMenu::MouseExited
(
Event *ev
)
{
MouseMoved(ev);
2016-03-27 11:49:47 +02:00
}
void UIPopupMenu::Dismiss
(
void
)
{
// navigate to the topmost menu
if (m_parentMenu) {
m_parentMenu->Dismiss();
} else {
delete this;
}
2016-03-27 11:49:47 +02:00
}
void UIPopupMenu::DismissEvent
(
Event *ev
)
{
Dismiss();
2016-03-27 11:49:47 +02:00
}
void UIPopupMenu::setAutoDismiss
(
bool b
)
{
m_autodismiss = b;
2016-03-27 11:49:47 +02:00
}
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);
2016-03-27 11:49:47 +02:00
}
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);
2016-03-27 11:49:47 +02:00
}
uipopup_describe *UIPopupMenu::getSelectedDescribe
(
void
)
{
if (m_selected == -1)
{
return NULL;
}
return m_describe->ObjectAt(m_selected);
2016-03-27 11:49:47 +02:00
}
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();
}
2016-03-27 11:49:47 +02:00
}
void UIPopupMenu::setHighlightFGColor
(
UColor c
)
{
m_highlightFGColor = c;
2016-03-27 11:49:47 +02:00
}
void UIPopupMenu::setHighlightBGColor
(
UColor c
)
{
m_highlightBGColor = c;
2016-03-27 11:49:47 +02:00
}
void UIPopupMenu::setPopupHighlightBGColor
(
str menu,
UColor c
)
{
// not present in disassembly
2016-03-27 11:49:47 +02:00
}
void UIPopupMenu::setPopupHighlightFGColor
(
str menu,
UColor c
)
{
// not present in disassembly
2016-03-27 11:49:47 +02:00
}
void UIPopupMenu::getPulldown
(
str title
)
{
// not present in disassembly
2016-03-27 11:49:47 +02:00
}