openmohaa/code/uilib/uilistctrl.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

741 lines
13 KiB
C++

/*
===========================================================================
Copyright (C) 2015 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 "../qcommon/localization.h"
static UISize2D s_columnpadding(2.0, 2.0);
bool UIListCtrl::s_qsortreverse;
int UIListCtrl::s_qsortcolumn;
class UIListCtrl* UIListCtrl::s_qsortobject;
CLASS_DECLARATION( UIListBase, UIListCtrl, NULL )
{
{ &W_SizeChanged, &UIListCtrl::OnSizeChanged },
{ &W_LeftMouseDown, &UIListCtrl::MousePressed },
{ &W_LeftMouseUp, &UIListCtrl::MouseReleased },
{ &W_LeftMouseDragged, &UIListCtrl::MouseDragged },
{ &W_MouseEntered, &UIListCtrl::MouseEntered },
{ NULL, NULL }
};
UIListCtrl::UIListCtrl()
{
m_clickState.point.x = m_clickState.point.y = 0;
m_sizestate.column = 0;
m_iLastSortColumn = 0;
m_bDrawHeader = qtrue;
m_headerfont = NULL;
Connect(this, W_SizeChanged, W_SizeChanged);
m_background_color = UColor(0.02f, 0.07f, 0.005f, 1.0f);
m_foreground_color = UHudColor;
}
UIListCtrl::~UIListCtrl()
{
if (m_headerfont) {
delete m_headerfont;
m_headerfont = NULL;
}
}
int UIListCtrl::StringCompareFunction
(
const UIListCtrlItem *i1,
const UIListCtrlItem *i2,
int columnname
)
{
if (i1->getListItemType(columnname) != TYPE_STRING || i2->getListItemType(columnname) != TYPE_STRING) {
return 0;
}
const char* s1 = i1->getListItemString(columnname);
const char* s2 = i2->getListItemString(columnname);
if (*s1 == '.')
{
if (*s2 != '.') {
return -1;
}
}
else if (*s2 == '.') {
return 1;
}
if (*s1 == '[')
{
if (*s2 != '[') {
return -1;
}
}
else if (*s2 == '[') {
return 1;
}
return Q_stricmp(s1, s2);
}
int UIListCtrl::StringNumberCompareFunction
(
const UIListCtrlItem *i1,
const UIListCtrlItem *i2,
int columnname
)
{
int val1, val2;
val1 = i1->getListItemValue(columnname);
val2 = i2->getListItemValue(columnname);
if (val1 > val2) {
return 1;
} else if (val1 < val2) {
return -1;
} else {
return 0;
}
}
int UIListCtrl::QsortCompare
(
const void *e1,
const void *e2
)
{
int val = s_qsortobject->m_comparefunction(*(const UIListCtrlItem**)e1, *(const UIListCtrlItem**)e2, s_qsortcolumn);
return s_qsortreverse ? -val : val;
}
void UIListCtrl::Draw
(
void
)
{
if (m_bDrawHeader) {
DrawColumns();
}
DrawContent();
}
int UIListCtrl::getHeaderHeight
(
void
)
{
if (!m_bDrawHeader) {
return 0;
}
if (m_headerfont) {
return (int)(m_headerfont->getHeight(getVirtualScale()) + (s_columnpadding.height + s_columnpadding.height) * getVirtualScale()[1]);
}
if (m_font) {
return (int)(m_font->getHeight(getVirtualScale()) + (s_columnpadding.height + s_columnpadding.height) * getVirtualScale()[1]);
}
return (int)((s_columnpadding.height + s_columnpadding.height) * getVirtualScale()[1]);
}
void UIListCtrl::MousePressed
(
Event *ev
)
{
UIPoint2D p;
p = MouseEventToClientPoint(ev);
if (m_clicksound.length()) {
uii.Snd_PlaySound(m_clicksound.c_str());
}
if (getHeaderHeight() > p.y)
{
int closesep = 0;
int atleft = 0;
int closeatleft = 0;
int insep = 0;
for (int i = 1; i <= m_columnlist.NumObjects(); i++) {
const columndef_t& column = m_columnlist.ObjectAt(i);
atleft += column.width;
if (abs(atleft - p.x) > 2)
{
if (atleft - column.width < p.x && atleft > p.x) {
insep = i;
}
}
else
{
closesep = i;
closeatleft = atleft;
}
}
if (closesep)
{
m_sizestate.column = closesep;
m_sizestate.min = closeatleft - m_columnlist.ObjectAt(closesep).width;
uWinMan.setFirstResponder(this);
} else if (insep) {
SortByColumn(m_columnlist.ObjectAt(insep).name);
}
}
else
{
if (m_vertscroll) {
TrySelectItem((m_vertscroll->getTopItem() + 1) + (p.y - getHeaderHeight()) / m_font->getHeight(getVirtualScale()));
} else {
TrySelectItem(1.0 + (p.y - getHeaderHeight()) / m_font->getHeight(getVirtualScale()));
}
if (m_clickState.time + 500 > uid.time
&& m_currentItem == m_clickState.selected
&& fabs(m_clickState.point.x - p.x) <= 2.0
&& fabs(m_clickState.point.y - p.y) <= 2.0)
{
Event newev(EV_UIListBase_ItemDoubleClicked);
newev.AddInteger(m_currentItem);
SendSignal(newev);
m_clickState.time = 0;
}
else
{
m_clickState.time = uid.time;
m_clickState.selected = m_currentItem;
m_clickState.point.x = p.x;
m_clickState.point.y = p.y;
}
}
}
void UIListCtrl::MouseDragged
(
Event *ev
)
{
UIPoint2D p;
int newwidth;
p = MouseEventToClientPoint(ev);
if (m_sizestate.column)
{
columndef_t& column = m_columnlist.ObjectAt(m_sizestate.column);
newwidth = p.x - m_sizestate.min;
if (newwidth > 600) {
newwidth = 600;
} else if (newwidth < 0) {
newwidth = 0;
}
column.width = newwidth;
}
}
void UIListCtrl::MouseReleased
(
Event *ev
)
{
m_sizestate.column = 0;
if (uWinMan.getFirstResponder() == this) {
uWinMan.setFirstResponder(NULL);
}
}
void UIListCtrl::MouseEntered
(
Event *ev
)
{
uWinMan.ActivateControl(this);
}
void UIListCtrl::OnSizeChanged
(
Event *ev
)
{
if (!m_vertscroll) {
return;
}
m_vertscroll->InitFrame(this, m_frame.size.width - 16.0, 0.0, 16.0, m_frame.size.height, -1);
m_vertscroll->setPageHeight((m_frame.size.height - getHeaderHeight()) / m_font->getHeight(getVirtualScale()));
m_vertscroll->setNumItems(m_itemlist.NumObjects());
}
void UIListCtrl::DrawColumns
(
void
)
{
int atleft;
int i;
int height;
UIFont* pFont;
UColor columnColor, textColor;
atleft = 0;
columnColor = UColor(0.07f, 0.06f, 0.005f, 1.0f);
textColor = UHudColor;
pFont = m_headerfont;
if (!pFont) pFont = m_font;
height = (s_columnpadding.height + s_columnpadding.height) * getVirtualScale()[1] + pFont->getHeight(getVirtualScale());
pFont->setColor(textColor);
for (i = 1; i <= m_columnlist.NumObjects(); i++)
{
const columndef_t& column = m_columnlist.ObjectAt(i);
DrawBoxWithSolidBorder(
UIRect2D(atleft, 0, column.width + getVirtualScale()[1], height),
columnColor,
textColor,
1,
3,
m_local_alpha
);
pFont->Print(
atleft / getVirtualScale()[0] + s_columnpadding.width,
s_columnpadding.height,
Sys_LV_CL_ConvertString(column.title.c_str()),
-1,
getVirtualScale()
);
atleft += column.width;
}
if (m_frame.size.width > atleft)
{
DrawBoxWithSolidBorder(
UIRect2D(atleft, 0, m_frame.size.width - atleft + 1.0, height),
columnColor,
textColor,
1,
3,
m_local_alpha
);
}
}
void UIListCtrl::DrawContent
(
void
)
{
int item;
int height, headerheight, iItemHeight;
UIFont* pFont;
int top;
UColor selColor, selText;
UColor backColor, textColor;
height = m_font->getHeight(getVirtualScale());
selColor = UColor(0.21f, 0.18f, 0.015f, 1.0f);
selText = UColor(0.9f, 0.8f, 0.6f, 1.0f);
backColor = m_background_color;
textColor = m_foreground_color;
if (m_headerfont) {
headerheight = m_headerfont->getHeight(getVirtualScale());
} else {
headerheight = 0;
}
selColor.a = m_local_alpha;
selText.a = m_local_alpha;
backColor.a = m_local_alpha;
textColor.a = m_local_alpha;
top = getHeaderHeight();
if (m_vertscroll) {
item = m_vertscroll->getTopItem() + 1;
} else {
item = 1;
}
for (; item <= m_itemlist.NumObjects(); item++)
{
UIListCtrlItem* theitem;
int col;
int atleft;
UColor* itemBg;
UColor* itemText;
theitem = m_itemlist.ObjectAt(item);
atleft = 0;
if (item == m_currentItem)
{
itemBg = &selColor;
itemText = &selText;
}
else
{
itemBg = &backColor;
itemText = &textColor;
}
if (theitem->IsHeaderEntry() && m_headerfont) {
pFont = m_headerfont;
iItemHeight = headerheight;
} else {
pFont = m_font;
iItemHeight = height;
}
pFont->setColor(*itemText);
for (col = 1; col <= m_columnlist.NumObjects(); col++)
{
columndef_t& column = m_columnlist.ObjectAt(col);
UIRect2D drawRect(atleft, top, column.width, iItemHeight);
griditemtype_t itemtype = theitem->getListItemType(column.name);
switch (itemtype)
{
case TYPE_STRING:
DrawBox(drawRect, *itemBg, m_local_alpha);
pFont->Print(
drawRect.pos.x / getVirtualScale()[0] + 1.0,
drawRect.pos.y / getVirtualScale()[1],
Sys_LV_CL_ConvertString(theitem->getListItemString(column.name)),
-1,
getVirtualScale()
);
break;
case TYPE_OWNERDRAW:
theitem->DrawListItem(column.name, drawRect, item == m_currentItem, pFont);
break;
}
atleft += column.width;
}
if (m_frame.size.width > atleft)
{
DrawBox(
atleft,
top,
m_frame.size.width - atleft,
height,
*itemBg,
m_local_alpha
);
}
top += iItemHeight;
if (iItemHeight + top > m_frame.size.height) {
break;
}
}
}
void UIListCtrl::FrameInitialized
(
void
)
{
UIListBase::FrameInitialized();
OnSizeChanged(NULL);
}
void UIListCtrl::SetDrawHeader
(
qboolean bDrawHeader
)
{
m_bDrawHeader = bDrawHeader;
}
void UIListCtrl::AddItem
(
UIListCtrlItem *item
)
{
m_itemlist.AddObject(item);
if (m_vertscroll) {
m_vertscroll->setNumItems(m_itemlist.NumObjects());
}
}
void UIListCtrl::InsertItem
(
UIListCtrlItem *item, int where
)
{
if (where > 0 && where <= m_itemlist.NumObjects())
{
int i;
m_itemlist.AddObject(NULL);
for (i = m_itemlist.NumObjects(); i > where; i++) {
m_itemlist.SetObjectAt(i, m_itemlist.ObjectAt(i - 1));
}
m_itemlist.SetObjectAt(where, item);
}
else
{
m_itemlist.AddObject(item);
}
if (m_vertscroll) {
m_vertscroll->setNumItems(m_itemlist.NumObjects());
}
}
int UIListCtrl::FindItem
(
UIListCtrlItem *item
)
{
return m_itemlist.IndexOfObject(item);
}
UIListCtrlItem *UIListCtrl::GetItem
(
int item
)
{
return m_itemlist.ObjectAt(item);
}
void UIListCtrl::AddColumn
(
str title,
int name,
int width,
bool numeric,
bool reverse_sort
)
{
columndef_t column;
column.title = title;
column.name = name;
column.width = width;
column.numeric = numeric;
column.reverse_sort = reverse_sort;
m_columnlist.AddObject(column);
}
void UIListCtrl::RemoveAllColumns
(
void
)
{
m_columnlist.ClearObjectList();
}
int UIListCtrl::getNumItems
(
void
)
{
return m_itemlist.NumObjects();
}
void UIListCtrl::DeleteAllItems
(
void
)
{
m_itemlist.ClearObjectList();
m_currentItem = 0;
if (m_vertscroll)
{
m_vertscroll->setNumItems(0);
m_vertscroll->setTopItem(0);
}
}
void UIListCtrl::DeleteItem
(
int which
)
{
m_itemlist.RemoveObjectAt(which);
if (m_vertscroll) {
m_vertscroll->setNumItems(m_itemlist.NumObjects());
}
if (m_currentItem > getNumItems()) {
TrySelectItem(getNumItems());
}
}
void UIListCtrl::SortByColumn
(
int column
)
{
int i;
bool exists;
bool numeric;
bool reverse;
UIListCtrlItem* selected;
bool selvisible;
exists = 0;
numeric = 0;
reverse = 0;
selected = NULL;
selvisible = 0;
for (i = 1; i <= m_columnlist.NumObjects(); i++) {
columndef_t& def = m_columnlist.ObjectAt(i);
if (def.name == column) {
numeric = def.numeric;
reverse = def.reverse_sort;
exists = true;
break;
}
}
if (exists)
{
m_iLastSortColumn = column;
s_qsortcolumn = column;
s_qsortobject = this;
s_qsortreverse = reverse;
if (m_currentItem)
{
selected = m_itemlist.ObjectAt(m_currentItem);
if (m_vertscroll)
{
selvisible = false;
if (m_currentItem >= m_vertscroll->getTopItem() + 1) {
selvisible = m_currentItem <= m_vertscroll->getPageHeight() + m_vertscroll->getTopItem();
}
}
else
{
selvisible = false;
// FIXME: selvisible when there is no vertical scroll?
//
//if (m_currentItem > 0) {
// selvisible = m_currentItem <= m_vertscroll->m_pageheight + m_vertscroll->getTopItem();
//}
}
}
if (numeric) {
m_comparefunction = &UIListCtrl::StringNumberCompareFunction;
} else {
m_comparefunction = &UIListCtrl::StringCompareFunction;
}
if (m_itemlist.NumObjects()) {
m_itemlist.Sort(&UIListCtrl::QsortCompare);
}
if (selected)
{
if (selvisible) {
TrySelectItem(FindItem(selected));
} else {
m_currentItem = FindItem(selected);
}
}
}
else
{
m_iLastSortColumn = 0;
}
}
void UIListCtrl::SortByLastSortColumn
(
void
)
{
SortByColumn(m_iLastSortColumn);
}
void UIListCtrl::setCompareFunction
(
int( *func ) ( const UIListCtrlItem *i1, const UIListCtrlItem *i2, int columnname )
)
{
m_comparefunction = func;
}
void UIListCtrl::setHeaderFont
(
const char *name
)
{
if (m_headerfont) {
delete m_headerfont;
}
if (name) {
m_headerfont = new UIFont(name);
} else {
m_headerfont = NULL;
}
}