Descent3/ui/UIListBox.cpp
2024-05-24 20:57:17 -04:00

884 lines
24 KiB
C++

/*
* Descent 3
* Copyright (C) 2024 Parallax Software
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
--- HISTORICAL COMMENTS FOLLOW ---
* $Logfile: /DescentIII/Main/ui/UIListBox.cpp $
* $Revision: 42 $
* $Date: 4/30/99 6:49p $
* $Author: Samir $
*
* Listbox code
*
* $Log: /DescentIII/Main/ui/UIListBox.cpp $
*
* 42 4/30/99 6:49p Samir
* fixed bug when scroll down with keyboard in last visible item of
* listbox.
*
* 41 4/14/99 1:53a Jeff
* fixed case mismatched #includes
*
* 40 3/22/99 1:55p Jeff
* fixed more crashes
*
* 39 3/22/99 12:47p Jeff
* oops...fixed some listbox crashes
*
* 38 3/19/99 9:21p Jeff
* fixed some listbox sorting issues
*
* 37 2/24/99 1:14p Jeff
* fixed sorting bug when you have 2 identical items
*
* 36 2/21/99 6:36p Samir
* focusing changes and key input changes to ui.,
*
* 35 2/04/99 12:10p Jeff
* fixed SelectItem
*
* 34 1/27/99 10:26p Jeff
* fixed sorting bug, very odd it hasn't cropped up before, only happened
* when allocating a new buffer of indices
*
* 32 10/19/98 6:30p Jeff
* changes made for detail variables. Put in preset values. Preset
* options. Removed terrain_cast from detail. Put new callbacks in
* UIListBox and UISlider
*
* 31 10/06/98 8:05p Samir
* added page up and page down!
*
* 30 10/06/98 7:30p Samir
* scrolling listbox is cleaner.
*
* 29 9/30/98 4:33p Samir
* listbox broke for keyboard for now.
*
* 28 9/22/98 3:57p Samir
* listbox prob fixed again.
*
* 27 9/10/98 6:32p Samir
* changed some visual characteristics of listboxes.
*
* 26 9/02/98 6:50p Samir
* fixed some double clicking issues for listboxes.
*
* 25 8/31/98 12:37p Samir
* SetSelectedIndex modifies visible index as well, and dblclick works
* again.
*
* 24 8/25/98 5:27p Samir
* new focusing rules for keyboard.
*
* 23 8/24/98 3:12p Samir
* fixed text clipping
*
* 22 8/11/98 11:38a Samir
* fixed listbox probs yet again. arrows on side this time.
*
* 21 7/29/98 3:26p Samir
* fixed some listbox mouse problems.
*
* 20 7/28/98 7:13p Samir
* reset index when removing all items.
*
* 19 7/15/98 2:26p Samir
* may have fixed something with arrows.
*
* 18 6/22/98 2:15p Samir
* implemented border flag.
*
* 17 6/05/98 5:36p Samir
* cleaner keyboard inteface for listboxes.
*
* 16 4/29/98 1:04p Jeff
* fixed up the UIListBox a bit (color control and scroll buttons)
*
* 15 4/29/98 11:55a Samir
* allow user to change color of selected listbox item.
*
* 14 4/13/98 7:02p Samir
* made some things protected.
*
* 13 3/16/98 11:43a Brent
* (Jeff) fixed UIListBox so it doesn't draw arrows if nothing in the box
*
* 12 3/13/98 5:19p Jeff
* added scroll button code
*
* 11 3/09/98 5:08p Samir
* fixed bug.
*
* 10 3/09/98 5:05p Samir
* Added callback to selection change function.
*
* 9 3/09/98 3:43p Samir
* Single clicks should select an item in a listbox.
*
* 8 2/24/98 3:05p Samir
* don't set listindex when selecting.
*
* 7 2/15/98 7:07p Samir
* Listbox now supports up and down arrow keys.
*
* 6 2/13/98 6:36p Samir
* Double click to select.
*
* 5 2/10/98 4:56p Samir
* Listbox with alphas and a selection bar.
*
* 4 2/10/98 3:45p Samir
* Fixed bug in drawing items over the boundaries of the listbox.
*
* 3 1/30/98 7:07p Samir
* Simple box containing items. Must be handled by a parent.
*
* 2 1/13/98 4:28p Samir
* Fixed typo.
*
* 1 1/09/98 12:00p Samir
* Initial revision
*
* $NoKeywords: $
*/
#include "UIlib.h"
#include <stdlib.h>
#include <string.h>
#define SCROLL_BUTTON_GAP 4
// Construction and destruction
UIListBox::UIListBox() {
m_NumItems = 0;
m_SelectedIndex = 0;
m_Index = 0;
m_ItemList = NULL;
m_Virt2Real = NULL;
m_Real2Virt = NULL;
m_MouseState = 0;
selectchange_fn = NULL;
selectchange_id_fn = NULL;
m_callbackptr = NULL;
m_TextOffX = 4;
m_TextOffY = 4;
}
UIListBox::~UIListBox() {}
void UIListBox::Create(UIWindow *parent, int id, int x, int y, int w, int h, int flags) {
m_NumItems = 0;
m_ItemList = NULL;
m_Virt2Real = NULL;
m_Real2Virt = NULL;
m_SelectedIndex = 0;
m_Index = 0;
m_MouseState = 0;
m_ClickTime = 0;
selectchange_fn = NULL;
selectchange_id_fn = NULL;
m_callbackptr = NULL;
m_ShowDown = false;
m_ShowUp = false;
m_SelectColor = GR_WHITE;
m_HiliteColor = GR_DARKGRAY;
m_NumVisibleItems = 0;
m_Alpha = 192;
UIGadget::Create(parent, id, x, y, w, h, flags);
m_CX = 1;
m_CY = 1;
m_CX2 = m_W - 1;
m_CY2 = m_H - 1;
}
// settings
// adds an item to the list, no sorting
void UIListBox::AddItem(const UIItem *item) {
UIItem **temp;
int i;
int *tempvirt2real, *tempreal2virt;
int real_index = -1;
if (m_Flags & UILB_NOSORT) {
// insert without sort
if ((m_NumItems % LISTBOX_BUFFER_SIZE) == 0) {
// dynamically allocated memory for listbox, expand as needed.
temp = new UIItem *[m_NumItems + LISTBOX_BUFFER_SIZE];
tempvirt2real = new int[m_NumItems + LISTBOX_BUFFER_SIZE];
tempreal2virt = new int[m_NumItems + LISTBOX_BUFFER_SIZE];
if (m_ItemList) {
for (i = 0; i < m_NumItems; i++) {
temp[i] = m_ItemList[i];
tempvirt2real[i] = m_Virt2Real[i];
tempreal2virt[i] = m_Real2Virt[i];
}
delete[] m_ItemList;
delete[] m_Virt2Real;
delete[] m_Real2Virt;
}
m_ItemList = temp;
m_Virt2Real = tempvirt2real;
m_Real2Virt = tempreal2virt;
}
real_index = m_NumItems;
} else {
// only text item listboxes!
// insert with sorting
if ((m_NumItems % LISTBOX_BUFFER_SIZE) == 0) {
// dynamically allocated memory for listbox, expand as needed.
temp = new UIItem *[m_NumItems + LISTBOX_BUFFER_SIZE];
tempvirt2real = new int[m_NumItems + LISTBOX_BUFFER_SIZE];
tempreal2virt = new int[m_NumItems + LISTBOX_BUFFER_SIZE];
int oidx = 0, cidx = 0;
int pidx = 0;
real_index = -1;
int result;
if (m_ItemList) {
while (oidx <= m_NumItems) {
if (oidx == m_NumItems) {
if (real_index == -1) {
real_index = oidx;
} else {
temp[cidx] = m_ItemList[pidx];
tempreal2virt[cidx] = m_Real2Virt[pidx];
}
oidx++;
continue;
}
if (m_Flags & UILB_CASESENSITIVE)
result = strcmp(((UITextItem *)m_ItemList[cidx])->GetBuffer(), ((UITextItem *)item)->GetBuffer());
else
result = stricmp(((UITextItem *)m_ItemList[cidx])->GetBuffer(), ((UITextItem *)item)->GetBuffer());
if (result < 0) {
// copy old
temp[cidx] = m_ItemList[pidx];
tempreal2virt[cidx] = m_Real2Virt[pidx];
cidx++;
pidx++;
} else if (result >= 0) {
// see if we need to insert, if not just keep copying
if (real_index == -1) {
real_index = cidx;
cidx++;
} else {
temp[cidx] = m_ItemList[pidx];
tempreal2virt[cidx] = m_Real2Virt[pidx];
cidx++;
pidx++;
}
}
oidx++;
}
ASSERT(real_index != -1);
// now we need to fix m_Virt2Real to represent the new structure,
// all values>=real_index must be incremented
for (oidx = 0; oidx <= m_NumItems; oidx++) {
if (oidx < m_NumItems) {
tempvirt2real[oidx] = m_Virt2Real[oidx];
// if(oidx!=real_index){
if (tempvirt2real[oidx] >= real_index)
tempvirt2real[oidx]++;
}
}
delete[] m_ItemList;
delete[] m_Virt2Real;
delete[] m_Real2Virt;
} else {
real_index = 0;
}
m_ItemList = temp;
m_Virt2Real = tempvirt2real;
m_Real2Virt = tempreal2virt;
} else {
// find where to insert, without allocating memory
int oidx = 0, cidx = 0;
real_index = -1;
int result;
int old_r2v;
UIItem *old_item = NULL;
while (oidx <= m_NumItems) {
if (oidx == m_NumItems) {
// we're past the slot of the old buffer
if (real_index == -1) {
// we haven't set the real index yet, so it has to go here
real_index = m_NumItems;
} else {
// the real index has been set, we're still pulling back
m_Real2Virt[cidx] = old_r2v;
m_ItemList[cidx] = old_item;
}
oidx++;
continue;
} else {
if (m_Flags & UILB_CASESENSITIVE)
result = strcmp(((UITextItem *)m_ItemList[cidx])->GetBuffer(), ((UITextItem *)item)->GetBuffer());
else
result = stricmp(((UITextItem *)m_ItemList[cidx])->GetBuffer(), ((UITextItem *)item)->GetBuffer());
}
if (result < 0) {
// we haven't gotten to the string yet, keep as they are
cidx++;
} else if (result >= 0) {
// see if we need to insert, if not just keep copying
if (real_index == -1) {
// we need to insert here
real_index = cidx;
old_item = m_ItemList[cidx];
old_r2v = m_Real2Virt[cidx];
cidx++;
} else {
// old_item and old_r2v should be set!
ASSERT(old_item);
UIItem *oi;
int or2v;
or2v = m_Real2Virt[cidx];
oi = m_ItemList[cidx];
m_Real2Virt[cidx] = old_r2v;
m_ItemList[cidx] = old_item;
old_r2v = or2v;
old_item = oi;
cidx++;
}
}
// continue on
oidx++;
}
ASSERT(real_index != -1);
// now we need to fix m_Virt2Real to represent the new structure,
// all values>=real_index must be incremented
cidx = 0;
for (oidx = 0; oidx <= m_NumItems; oidx++) {
if (oidx != real_index) {
if (m_Virt2Real[cidx] >= real_index)
m_Virt2Real[cidx]++;
cidx++;
}
}
}
}
// copy item passed to list.
m_ItemList[real_index] = (UIItem *)item;
m_Virt2Real[m_NumItems] = real_index;
m_Real2Virt[real_index] = m_NumItems;
m_NumItems++;
if (real_index <= m_SelectedIndex) {
// adjust selected index
if (m_SelectedIndex < m_NumItems - 1)
m_SelectedIndex++;
}
}
// removes an item from the list.
void UIListBox::RemoveItem(const UIItem *item) {
int i, j, found = -1;
for (i = 0; i < m_NumItems; i++) {
if (item == m_ItemList[i]) {
found = i;
break;
}
}
if (found == -1) {
mprintf(0, "UIListBox:: Didn't find item to remove!\n");
return;
}
// found is the real index to remove
// go through virt2real and subtract one from values of found or higher
// go through real2virt and move everything from index found on down, and subtract
// 1 from those with value of found or higher
int real_index = found; // index to remove
int virt_index = m_Real2Virt[found]; // index to remove
int real_thresh = virt_index;
int virt_thresh = real_index;
for (j = 0; j < (m_NumItems - 1); j++) {
if (j >= real_index) {
m_ItemList[j] = m_ItemList[j + 1];
m_Real2Virt[j] = m_Real2Virt[j + 1];
}
if (j >= virt_index) {
m_Virt2Real[j] = m_Virt2Real[j + 1];
}
if (m_Real2Virt[j] >= real_thresh)
m_Real2Virt[j]--;
if (m_Virt2Real[j] >= virt_thresh)
m_Virt2Real[j]--;
}
m_NumItems--;
if (real_index <= m_SelectedIndex) {
// adjust selected index
if (m_SelectedIndex > 0)
m_SelectedIndex--;
}
}
// empties listbox
void UIListBox::RemoveAll() {
m_Index = m_SelectedIndex = m_NumItems = 0;
if (m_ItemList)
delete[] m_ItemList;
if (m_Virt2Real)
delete[] m_Virt2Real;
if (m_Real2Virt)
delete[] m_Real2Virt;
m_ItemList = NULL;
m_Virt2Real = NULL;
m_Real2Virt = NULL;
}
// selects the given item in the list.
void UIListBox::SelectItem(const UIItem *item) {
int i, found = 0;
for (i = 0; i < m_NumItems; i++) {
if (item == m_ItemList[i]) {
SetSelectedIndex(m_Real2Virt[i]);
found = 1;
break;
}
}
if (!found) {
mprintf(0, "UIListBox::SelectItem item not found!\n");
}
}
// returns the item at listbox index given.
UIItem *UIListBox::GetItem(int index) const {
if (m_NumItems == 0)
return NULL;
if (index >= 0 && index < m_NumItems) {
int real_index = m_Virt2Real[index];
ASSERT(real_index >= 0 && real_index < m_NumItems);
return m_ItemList[real_index];
} else
return NULL;
}
// returns the current listbox index
int UIListBox::GetListIndex() const {
if (m_NumItems == 0)
return 0;
if (m_Index < 0)
return m_Index;
if (!m_Real2Virt)
return 0;
int virt_index = m_Real2Virt[m_Index];
ASSERT(virt_index >= 0 && virt_index < m_NumItems);
return virt_index;
}
// sets the index of a listbox
void UIListBox::SetListIndex(int index) {
if (m_NumItems == 0)
return;
if (index >= 0 && index < m_NumItems) {
int real_index = m_Virt2Real[index];
ASSERT(real_index >= 0 && real_index < m_NumItems);
m_Index = real_index;
}
}
// returns which item index is selected
int UIListBox::GetSelectedIndex() const {
if (m_NumItems == 0)
return 0;
if (m_SelectedIndex < 0)
return m_SelectedIndex;
if (!m_Real2Virt)
return 0;
int virt_index = m_Real2Virt[m_SelectedIndex];
ASSERT(virt_index >= 0 && virt_index < m_NumItems);
return virt_index;
}
// sets the selected index.
void UIListBox::SetSelectedIndex(int index) {
if (m_NumItems == 0)
return;
if (index >= 0 && index < m_NumItems) {
int real_index = m_Virt2Real[index];
ASSERT(real_index >= 0 && real_index < m_NumItems);
int i, y = m_TextOffY;
m_SelectedIndex = real_index;
for (i = 0; i < m_NumItems; i++) {
y += m_ItemList[i]->height() + 2;
if (y > (m_H - m_TextOffY))
break;
}
m_Index = m_SelectedIndex - i;
if (m_Index < 0)
m_Index = 0;
if (selectchange_fn)
(*selectchange_fn)(index);
if (selectchange_id_fn)
(*selectchange_id_fn)(GetID(), m_callbackptr);
}
}
void UIListBox::SetInternalSelectedIndex(int index) {
if (m_NumItems == 0)
return;
if (index >= 0 && index < m_NumItems) {
int real_index = m_Virt2Real[index];
ASSERT(real_index >= 0 && real_index < m_NumItems);
m_SelectedIndex = real_index;
if (selectchange_fn)
(*selectchange_fn)(index);
if (selectchange_id_fn)
(*selectchange_id_fn)(GetID(), m_callbackptr);
}
}
void UIListBox::SetSelectChangeCallback(void (*fn)(int)) { selectchange_fn = fn; }
void UIListBox::SetSelectChangeCallback(void (*fn)(int, void *), void *ptr) {
selectchange_id_fn = fn;
m_callbackptr = ptr;
}
// called from outside gadget hierarchy.
void UIListBox::OnDestroy() {
UIGadget::OnDestroy();
if (m_ItemList)
delete[] m_ItemList;
if (m_Real2Virt)
delete[] m_Real2Virt;
if (m_Virt2Real)
delete[] m_Virt2Real;
m_ItemList = NULL;
m_Real2Virt = NULL;
m_Virt2Real = NULL;
}
// behavior when gadget is being drawn.
void UIListBox::OnDraw() {
bool use_scroll = !(m_Flags & UILB_NOSCROLL);
bool auto_select = ((m_Flags & UILB_AUTOSELECT) > 0);
UITextItem uarrow;
UITextItem darrow;
char arrowbuf[2];
if (m_Flags & UIF_BORDER)
ui_DrawBox(GR_WHITE, 0, 0, m_W, m_H);
ui_SetTextClip(m_CX, m_CY, m_CX2, m_CY2);
// draw all items in box that can be visible.
int x, y, i;
uint8_t alpha;
x = m_TextOffX;
y = m_TextOffY;
if (m_NumItems) {
snprintf(arrowbuf, sizeof(arrowbuf), "%c", UI_UP_ARROW_CHAR);
uarrow = UITextItem(arrowbuf, m_ItemList[m_Index]->get_color(), 255);
snprintf(arrowbuf, sizeof(arrowbuf), "%c", UI_DOWN_ARROW_CHAR);
darrow = UITextItem(arrowbuf, m_ItemList[m_Index]->get_color(), 255);
// reset these
m_ShowUp = m_ShowDown = false;
if (use_scroll) {
m_UpArrowY0 = y;
m_UpArrowY1 = y + uarrow.height();
if (m_Index > 0)
uarrow.draw(m_TextOffX, m_UpArrowY0, IsDisabled() ? uiDrawFaded : uiDrawNormal);
m_ShowUp = true;
m_ArrowWidth = uarrow.width() + 2;
x = x + m_ArrowWidth;
} else
m_ArrowWidth = 0;
}
alpha = m_Alpha;
for (i = m_Index; i < m_NumItems; i++) {
// check to see if scroll arrow down needs to be drawn.
// if (m_SelectedIndex == i) {
// ui_DrawSetAlpha((alpha/4)+1);
// ui_DrawRect(m_HiliteColor, x,y,m_W-m_TextOffX,y+m_ItemList[i]->height()+2);
// }
m_ItemList[i]->set_alpha((uint8_t)alpha);
if (m_SelectedIndex == i) {
ddgr_color old_color = m_ItemList[i]->get_color();
m_ItemList[i]->set_color(m_SelectColor);
m_ItemList[i]->draw(x + 1, y + 1, IsDisabled() ? uiDrawFaded : uiDrawNormal);
if (!IsDisabled())
m_ItemList[i]->draw(x + 1, y + 1, uiDrawAlphaSaturate);
m_ItemList[i]->set_color(old_color);
} else {
m_ItemList[i]->draw(x + 1, y + 1, IsDisabled() ? uiDrawFaded : uiDrawNormal);
}
if (use_scroll && (i < m_NumItems)) {
if ((y + m_ItemList[i]->height()) > (m_H - m_TextOffY - darrow.height() + 1)) {
if (i < (m_NumItems - 1)) {
m_DownArrowY0 = m_H - m_TextOffY - darrow.height() + 1;
m_DownArrowY1 = m_H - m_TextOffY;
darrow.draw(m_TextOffX, m_DownArrowY0, IsDisabled() ? uiDrawFaded : uiDrawNormal);
m_ShowDown = true;
}
break;
}
} else if ((y + m_ItemList[i]->height() + 2) > (m_H - m_TextOffY)) {
break;
}
y += m_ItemList[i]->height() + 2;
}
if (m_NumItems) {
if (use_scroll && auto_select) {
if (m_SelectedIndex < m_Index)
UIListBox::SetSelectedIndex(m_Real2Virt[m_Index]);
if (m_SelectedIndex > i)
UIListBox::SetSelectedIndex(m_Real2Virt[i]);
}
}
m_NumVisibleItems = i - m_Index;
ui_ResetTextClip();
}
// behavior when key is pressed.
void UIListBox::OnKeyDown(int key) {
UIGadget *gadget = this;
if (key == KEY_UP) {
if (m_SelectedIndex > 0) {
if (m_SelectedIndex == m_Index)
m_Index--;
m_SelectedIndex--;
UIListBox::SetInternalSelectedIndex((m_Real2Virt) ? m_Real2Virt[m_SelectedIndex] : 0);
}
} else if (key == KEY_DOWN) {
if (m_SelectedIndex < (m_NumItems - 1)) {
if (m_SelectedIndex >= (m_Index + m_NumVisibleItems))
m_Index++;
m_SelectedIndex++;
UIListBox::SetInternalSelectedIndex((m_Real2Virt) ? m_Real2Virt[m_SelectedIndex] : 0);
}
} else if (key == KEY_PAGEDOWN) {
if (m_Index < (m_NumItems - m_NumVisibleItems))
m_Index = m_Index + m_NumVisibleItems;
if (m_Index > (m_NumItems - m_NumVisibleItems))
m_Index = m_NumItems - m_NumVisibleItems;
m_SelectedIndex = m_Index;
UIListBox::SetInternalSelectedIndex((m_Real2Virt) ? m_Real2Virt[m_SelectedIndex] : 0);
} else if (key == KEY_PAGEUP) {
m_Index = m_Index - m_NumVisibleItems;
if (m_Index < 0)
m_Index = 0;
m_SelectedIndex = m_Index;
UIListBox::SetInternalSelectedIndex((m_Real2Virt) ? m_Real2Virt[m_SelectedIndex] : 0);
}
}
// behavior when key is released.
void UIListBox::OnKeyUp(int key) {}
// behavior when mouse button is pressed.
void UIListBox::OnMouseBtnDown(int btn) {
// One day, this system needs a complete rewrite, for all this stuff is a HACK!!!!
if (btn == UILMSEBTN) {
if (m_MouseState == 1 || m_MouseState == 2) { // single click pass or double clicking
// handle first slow delay scrolling (like keyboard input)
if ((m_ClickTime + KEYDOWN_FIRST_DELAY) > UI_TIME()) {
// mprintf(0, "old=%.3f cur=%.3f\n", m_ClickTime, UI_TIME());
return;
} else
m_MouseState = 3;
} else if (m_MouseState == 3) {
// handle repeat scrolling (note this is if the user has scrolled once already.
if ((m_ClickTime + KEYDOWN_REPEAT_DELAY) > UI_TIME()) {
// mprintf(0, "old=%.3f cur=%.3f\n", m_ClickTime, UI_TIME());
return;
}
} else if (m_MouseState == 0) {
m_MouseState = 1;
m_MouseX = SCREEN_TO_GAD_X(this, UI_input.mx);
m_MouseY = SCREEN_TO_GAD_Y(this, UI_input.my);
MseCheckSelect(m_MouseX, m_MouseY);
if (UI_TIME() < (m_ClickTime + UI_DBLCLICK_DELAY)) {
if (abs(m_LastMseX - m_MouseX) < UI_DBLCLICK_MSEDELTA && abs(m_LastMseY - m_MouseY) < UI_DBLCLICK_MSEDELTA) {
if (MseCheckSelect(m_MouseX, m_MouseY))
OnSelect();
m_MouseState = 2;
}
}
LOCK_FOCUS(this);
}
m_ClickTime = UI_TIME(); // use for scroll timing, this should be reset on button release
// mprintf(0, "new=%.3f\n", m_ClickTime);
// code here should allow for continuous scrolling on mouse down.
// we need to add timing so scrolling isn't do damn quick.
m_LastMseX = m_MouseX;
m_LastMseY = m_MouseY;
if (m_ShowUp && m_MouseY >= m_UpArrowY0 && m_MouseY <= m_UpArrowY1) {
if (m_MouseX < (m_TextOffX + m_ArrowWidth)) {
if (m_Index > 0)
m_Index--;
}
} else if (m_ShowDown && m_MouseY >= m_DownArrowY0 && m_MouseY <= m_DownArrowY1) {
if (m_MouseX < (m_TextOffX + m_ArrowWidth)) {
if (m_Index < (m_NumItems - m_NumVisibleItems))
m_Index++;
}
}
}
}
// behavior when mouse button is released.
void UIListBox::OnMouseBtnUp(int btn) {
if (btn == UILMSEBTN) {
if (m_MouseState == 0)
return;
if (m_MouseState == 2) // double clicked.
m_ClickTime = -2.0f;
else if (m_MouseState == 1 || m_MouseState == 3) // single clicked or prolonged single click.
m_ClickTime = UI_TIME();
m_MouseState = 0;
UNLOCK_FOCUS(this);
}
}
void UIListBox::OnSelect() { UIGadget::OnSelect(); }
void UIListBox::SetSelectedColor(ddgr_color col) { m_SelectColor = col; }
void UIListBox::SetHiliteColor(ddgr_color col) { m_HiliteColor = col; }
// override: behavior when gadget loses input focus.
void UIListBox::OnLostFocus() {
m_Alpha = 192;
UIGadget::OnLostFocus();
}
// override: behavior when gadget gains input focus.
void UIListBox::OnGainFocus() {
m_Alpha = 255;
UIGadget::OnGainFocus();
}
// override: behavior when gadget is processed
void UIListBox::OnUserProcess() {
// if mouse is outside listbox, unlock the focus on this listbox.
// if (this->IsLocked()) {
// if (!m_Wnd->HasKeyFocus() && !PT_IN_GADGET(UIGadget::m_Wnd, this, UI_input.mx, UI_input.my)) {
// UNLOCK_FOCUS(this);
// this->LostFocus();
// }
// }
}
// check if x and y selection hits a selected item.
bool UIListBox::MseCheckSelect(int x, int y) {
int i, ty;
bool use_scroll = !(m_Flags & UILB_NOSCROLL);
if (!m_NumItems)
return false;
if (m_ShowUp)
ty = m_UpArrowY0;
else
ty = m_TextOffY;
for (i = m_Index; i < m_NumItems; i++) {
// check if mouse down event occurred within this item.
if (y >= ty && y < (ty + m_ItemList[i]->height() + 2)) {
if (x > (m_ArrowWidth + m_TextOffX)) {
UIListBox::SetInternalSelectedIndex((m_Real2Virt) ? m_Real2Virt[i] : 0);
return true;
}
}
if (m_ShowDown && (ty + m_ItemList[i]->height()) > m_DownArrowY1)
return false;
else if ((ty + m_ItemList[i]->height()) > (m_H - m_TextOffY))
return false;
ty += m_ItemList[i]->height() + 2;
}
return false;
}