diff options
author | Tim Angus <tim@ngus.net> | 2009-11-15 01:10:27 +0000 |
---|---|---|
committer | Tim Angus <tim@ngus.net> | 2013-01-03 00:17:19 +0000 |
commit | 8f759de26ac1a17676f3937019804b3a9a972e35 (patch) | |
tree | 4182465207276ea0b51051b6e9d6ad992b07f74d /src/ui/ui_shared.c | |
parent | 00043d33bad26cedbfa105269ae2b1b66684b721 (diff) |
* s/ITEM_TYPE_COMBO/ITEM_TYPE_CYCLE/
* Add ITEM_TYPE_COMBOBOX -- a proper combobox widget
* Use ITEM_TYPE_COMBOBOX for video mode selection
* General refactoring of the listbox code
* Various other assorted tidy-ups
Diffstat (limited to 'src/ui/ui_shared.c')
-rw-r--r-- | src/ui/ui_shared.c | 1405 |
1 files changed, 636 insertions, 769 deletions
diff --git a/src/ui/ui_shared.c b/src/ui/ui_shared.c index 2e54df2b..2199a449 100644 --- a/src/ui/ui_shared.c +++ b/src/ui/ui_shared.c @@ -66,6 +66,7 @@ static qboolean g_editingField = qfalse; static itemDef_t *g_bindItem = NULL; static itemDef_t *g_editItem = NULL; +static itemDef_t *g_comboBoxItem = NULL; menuDef_t Menus[MAX_MENUS]; // defined menus int menuCount = 0; // how many @@ -79,6 +80,8 @@ static int lastListBoxClickTime = 0; void Item_RunScript( itemDef_t *item, const char *s ); void Item_SetupKeywordHash( void ); static ID_INLINE qboolean Item_IsEditField( itemDef_t *item ); +static ID_INLINE qboolean Item_IsListBox( itemDef_t *item ); +static void Item_ListBox_SetStartPos( itemDef_t *item, int startPos ); void Menu_SetupKeywordHash( void ); int BindingIDFromName( const char *name ); qboolean Item_Bind_HandleKey( itemDef_t *item, int key, qboolean down ); @@ -430,7 +433,7 @@ exprList_t; /* ================= OpPrec - + Return a value reflecting operator precedence ================= */ @@ -964,9 +967,8 @@ void GradientBar_Paint( rectDef_t *rect, vec4_t color ) /* ================== Window_Init - + Initializes a window structure ( windowDef_t ) with defaults - ================== */ void Window_Init( Window *w ) @@ -1236,6 +1238,23 @@ void Menu_AspectCompensate( menuDef_t *menu ) } } +static int Menu_CompareItemTypes( const void *a, const void *b ) +{ + itemDef_t *itemA = *(itemDef_t **)a; + itemDef_t *itemB = *(itemDef_t **)b; + qboolean itemAIsList = Item_IsListBox( itemA ); + qboolean itemBIsList = Item_IsListBox( itemB ); + + if( itemAIsList && itemBIsList ) + return 0; + else if( itemAIsList ) + return 1; + else if( itemBIsList ) + return -1; + else + return 0; +} + void Menu_PostParse( menuDef_t *menu ) { if( menu == NULL ) @@ -1251,6 +1270,11 @@ void Menu_PostParse( menuDef_t *menu ) Menu_AspectCompensate( menu ); Menu_UpdatePosition( menu ); + + // Sort lists to the end of the array as they can potentially be drawn on top + // of other elements + if( menu->itemCount > 1 ) + qsort( menu->items, menu->itemCount, sizeof( itemDef_t* ), Menu_CompareItemTypes ); } itemDef_t *Menu_ClearFocus( menuDef_t *menu ) @@ -1576,6 +1600,7 @@ void Menus_CloseAll( void ) g_editingField = qfalse; g_waitingForKey = qfalse; + g_comboBoxItem = NULL; } @@ -1775,11 +1800,11 @@ void Script_Reset( itemDef_t *item, char **args ) if( resetItem ) { - if( resetItem->type == ITEM_TYPE_LISTBOX ) + if( Item_IsListBox( resetItem ) ) { - resetItem->cursorPos = 0; - resetItem->typeData.list->startPos = 0; - DC->feederSelection( resetItem->feederID, 0 ); + resetItem->cursorPos = DC->feederInitialise( resetItem->feederID ); + Item_ListBox_SetStartPos( resetItem, 0 ); + DC->feederSelection( resetItem->feederID, resetItem->cursorPos ); } } } @@ -2067,7 +2092,7 @@ float UI_Text_EmHeight( float scale ) /* ================ UI_AdjustFrom640 - + Adjusted for resolution and screen aspect ratio ================ */ @@ -2079,6 +2104,35 @@ void UI_AdjustFrom640( float *x, float *y, float *w, float *h ) *h *= DC->yscale; } +/* +================ +UI_SetClipRegion +================= +*/ +void UI_SetClipRegion( float x, float y, float w, float h ) +{ + vec4_t clip; + + UI_AdjustFrom640( &x, &y, &w, &h ); + + clip[ 0 ] = x; + clip[ 1 ] = y; + clip[ 2 ] = x + w; + clip[ 3 ] = y + h; + + trap_R_SetClipRegion( clip ); +} + +/* +================ +UI_ClearClipRegion +================= +*/ +void UI_ClearClipRegion( void ) +{ + trap_R_SetClipRegion( NULL ); +} + static void UI_Text_PaintChar( float x, float y, float scale, glyphInfo_t *glyph, float size ) { @@ -2290,19 +2344,19 @@ commandDef_t commandList[] = { {"close", &Script_Close}, // menu {"conditionalopen", &Script_ConditionalOpen}, // menu - {"exec", &Script_Exec}, // group/name + {"exec", &Script_Exec}, // group/name {"fadein", &Script_FadeIn}, // group/name {"fadeout", &Script_FadeOut}, // group/name {"hide", &Script_Hide}, // group/name {"open", &Script_Open}, // menu {"orbit", &Script_Orbit}, // group/name - {"play", &Script_Play}, // group/name + {"play", &Script_Play}, // group/name {"playlooped", &Script_playLooped}, // group/name {"reset", &Script_Reset}, // resets the state of the item argument {"setasset", &Script_SetAsset}, // works on this {"setbackground", &Script_SetBackground}, // works on this {"setcolor", &Script_SetColor}, // works on this - {"setcvar", &Script_SetCvar}, // group/name + {"setcvar", &Script_SetCvar}, // group/name {"setfocus", &Script_SetFocus}, // sets this background color to team color {"setitemcolor", &Script_SetItemColor}, // group/name {"setplayerhead", &Script_SetPlayerHead}, // sets this background color to team color @@ -2482,16 +2536,27 @@ qboolean Item_SetFocus( itemDef_t *item, float x, float y ) return qtrue; } -int Item_ListBox_MaxScroll( itemDef_t *item ) +static float Item_ListBox_HeightForNumItems( itemDef_t *item, int numItems ) +{ + listBoxDef_t *listPtr = item->typeData.list; + + return ( listPtr->elementHeight * numItems ) + 2.0f; +} + +static int Item_ListBox_NumItemsForItemHeight( itemDef_t *item ) { listBoxDef_t *listPtr = item->typeData.list; - int count = DC->feederCount( item->feederID ); - int max; - if( item->window.flags & WINDOW_HORIZONTAL ) - max = count - ( item->window.rect.w / listPtr->elementWidth ) + 1; + if( item->type == ITEM_TYPE_COMBOBOX ) + return listPtr->dropItems; else - max = count - ( item->window.rect.h / listPtr->elementHeight ) + 1; + return ( ( item->window.rect.h - 2.0f ) / listPtr->elementHeight ); +} + +int Item_ListBox_MaxScroll( itemDef_t *item ) +{ + int total = DC->feederCount( item->feederID ); + int max = total - Item_ListBox_NumItemsForItemHeight( item ); if( max < 0 ) return 0; @@ -2499,68 +2564,86 @@ int Item_ListBox_MaxScroll( itemDef_t *item ) return max; } -int Item_ListBox_ThumbPosition( itemDef_t *item ) -{ - float max, pos, size; - int startPos = item->typeData.list->startPos; +static float oldComboBoxY; +static float oldComboBoxH; - max = Item_ListBox_MaxScroll( item ); +static qboolean Item_ComboBox_MaybeCastToListBox( itemDef_t *item ) +{ + listBoxDef_t *listPtr = item->typeData.list; + qboolean cast = g_comboBoxItem != NULL && + ( item->type == ITEM_TYPE_COMBOBOX ); - if( item->window.flags & WINDOW_HORIZONTAL ) + if( cast ) { - size = item->window.rect.w - ( SCROLLBAR_WIDTH * 2 ) - 2; - - if( max > 0 ) - pos = ( size - SCROLLBAR_WIDTH ) / ( float ) max; - else - pos = 0; + oldComboBoxY = item->window.rect.y; + oldComboBoxH = item->window.rect.h; - pos *= startPos; - return item->window.rect.x + 1 + SCROLLBAR_WIDTH + pos; + item->window.rect.y += item->window.rect.h; + item->window.rect.h = Item_ListBox_HeightForNumItems( item, listPtr->dropItems ); + item->type = ITEM_TYPE_LISTBOX; } - else - { - size = item->window.rect.h - ( SCROLLBAR_HEIGHT * 2 ) - 2; - if( max > 0 ) - pos = ( size - SCROLLBAR_HEIGHT ) / ( float ) max; - else - pos = 0; + return cast; +} - pos *= startPos; - return item->window.rect.y + 1 + SCROLLBAR_HEIGHT + pos; +static void Item_ComboBox_MaybeUnCastFromListBox( itemDef_t *item, qboolean unCast ) +{ + if( unCast ) + { + item->window.rect.y = oldComboBoxY; + item->window.rect.h = oldComboBoxH; + item->type = ITEM_TYPE_COMBOBOX; } } -int Item_ListBox_ThumbDrawPosition( itemDef_t *item ) +static void Item_ListBox_SetStartPos( itemDef_t *item, int startPos ) { - int min, max; + listBoxDef_t *listPtr = item->typeData.list; + int total = DC->feederCount( item->feederID ); + int max = Item_ListBox_MaxScroll( item ); + + if( startPos < 0 ) + listPtr->startPos = 0; + else if( startPos > max ) + listPtr->startPos = max; + else + listPtr->startPos = startPos; + + listPtr->endPos = listPtr->startPos + MIN( ( total - listPtr->startPos ), + Item_ListBox_NumItemsForItemHeight( item ) ); +} +float Item_ListBox_ThumbPosition( itemDef_t *item ) +{ + float max, pos, size; + float startPos = (float)item->typeData.list->startPos; + + max = Item_ListBox_MaxScroll( item ); + size = SCROLLBAR_SLIDER_HEIGHT( item ); + + if( max > 0.0f ) + pos = ( size - SCROLLBAR_ARROW_HEIGHT ) / max; + else + pos = 0.0f; + + pos *= startPos; + + return SCROLLBAR_SLIDER_Y( item ) + pos; +} + +float Item_ListBox_ThumbDrawPosition( itemDef_t *item ) +{ if( itemCapture == item ) { - if( item->window.flags & WINDOW_HORIZONTAL ) - { - min = item->window.rect.x + SCROLLBAR_WIDTH + 1; - max = item->window.rect.x + item->window.rect.w - 2 * SCROLLBAR_WIDTH - 1; - - if( DC->cursorx >= min + SCROLLBAR_WIDTH / 2 && DC->cursorx <= max + SCROLLBAR_WIDTH / 2 ) - return DC->cursorx - SCROLLBAR_WIDTH / 2; - else - return Item_ListBox_ThumbPosition( item ); - } - else - { - min = item->window.rect.y + SCROLLBAR_HEIGHT + 1; - max = item->window.rect.y + item->window.rect.h - 2 * SCROLLBAR_HEIGHT - 1; + float min = SCROLLBAR_SLIDER_Y( item ); + float max = min + SCROLLBAR_SLIDER_HEIGHT( item ) - SCROLLBAR_ARROW_HEIGHT; + float halfThumbSize = SCROLLBAR_ARROW_HEIGHT / 2.0f; - if( DC->cursory >= min + SCROLLBAR_HEIGHT / 2 && DC->cursory <= max + SCROLLBAR_HEIGHT / 2 ) - return DC->cursory - SCROLLBAR_HEIGHT / 2; - else - return Item_ListBox_ThumbPosition( item ); - } + if( DC->cursory >= min + halfThumbSize && DC->cursory <= max + halfThumbSize ) + return DC->cursory - halfThumbSize; } - else - return Item_ListBox_ThumbPosition( item ); + + return Item_ListBox_ThumbPosition( item ); } float Item_Slider_ThumbPosition( itemDef_t *item ) @@ -2625,76 +2708,36 @@ int Item_ListBox_OverLB( itemDef_t *item, float x, float y ) count = DC->feederCount( item->feederID ); - if( item->window.flags & WINDOW_HORIZONTAL ) - { - // check if on left arrow - r.x = item->window.rect.x; - r.y = item->window.rect.y + item->window.rect.h - SCROLLBAR_HEIGHT; - r.w = SCROLLBAR_WIDTH; - r.h = SCROLLBAR_HEIGHT; - - if( Rect_ContainsPoint( &r, x, y ) ) - return WINDOW_LB_LEFTARROW; - - // check if on right arrow - r.x = item->window.rect.x + item->window.rect.w - SCROLLBAR_WIDTH; + r.x = SCROLLBAR_SLIDER_X( item ); + r.y = SCROLLBAR_Y( item ); + r.w = SCROLLBAR_ARROW_WIDTH; + r.h = SCROLLBAR_ARROW_HEIGHT; - if( Rect_ContainsPoint( &r, x, y ) ) - return WINDOW_LB_RIGHTARROW; - - // check if on thumb - thumbstart = Item_ListBox_ThumbPosition( item ); - - r.x = thumbstart; - - if( Rect_ContainsPoint( &r, x, y ) ) - return WINDOW_LB_THUMB; - - r.x = item->window.rect.x + SCROLLBAR_WIDTH; - r.w = thumbstart - r.x; - - if( Rect_ContainsPoint( &r, x, y ) ) - return WINDOW_LB_PGUP; - - r.x = thumbstart + SCROLLBAR_WIDTH; - r.w = item->window.rect.x + item->window.rect.w - SCROLLBAR_WIDTH; - - if( Rect_ContainsPoint( &r, x, y ) ) - return WINDOW_LB_PGDN; - } - else - { - r.x = item->window.rect.x + item->window.rect.w - SCROLLBAR_WIDTH; - r.y = item->window.rect.y; - r.w = SCROLLBAR_WIDTH; - r.h = SCROLLBAR_HEIGHT; - - if( Rect_ContainsPoint( &r, x, y ) ) - return WINDOW_LB_LEFTARROW; + if( Rect_ContainsPoint( &r, x, y ) ) + return WINDOW_LB_UPARROW; - r.y = item->window.rect.y + item->window.rect.h - SCROLLBAR_HEIGHT; + r.y = SCROLLBAR_SLIDER_Y( item ) + SCROLLBAR_SLIDER_HEIGHT( item ); - if( Rect_ContainsPoint( &r, x, y ) ) - return WINDOW_LB_RIGHTARROW; + if( Rect_ContainsPoint( &r, x, y ) ) + return WINDOW_LB_DOWNARROW; - thumbstart = Item_ListBox_ThumbPosition( item ); - r.y = thumbstart; + thumbstart = Item_ListBox_ThumbPosition( item ); + r.y = thumbstart; - if( Rect_ContainsPoint( &r, x, y ) ) - return WINDOW_LB_THUMB; + if( Rect_ContainsPoint( &r, x, y ) ) + return WINDOW_LB_THUMB; - r.y = item->window.rect.y + SCROLLBAR_HEIGHT; - r.h = thumbstart - r.y; + r.y = SCROLLBAR_SLIDER_Y( item ); + r.h = thumbstart - r.y; - if( Rect_ContainsPoint( &r, x, y ) ) - return WINDOW_LB_PGUP; + if( Rect_ContainsPoint( &r, x, y ) ) + return WINDOW_LB_PGUP; - r.y = thumbstart + SCROLLBAR_HEIGHT; - r.h = item->window.rect.y + item->window.rect.h - SCROLLBAR_HEIGHT; + r.y = thumbstart + SCROLLBAR_ARROW_HEIGHT; + r.h = ( SCROLLBAR_SLIDER_Y( item ) + SCROLLBAR_SLIDER_HEIGHT( item ) ) - r.y; - if( Rect_ContainsPoint( &r, x, y ) ) - return WINDOW_LB_PGDN; - } + if( Rect_ContainsPoint( &r, x, y ) ) + return WINDOW_LB_PGDN; return 0; } @@ -2702,52 +2745,32 @@ int Item_ListBox_OverLB( itemDef_t *item, float x, float y ) void Item_ListBox_MouseEnter( itemDef_t *item, float x, float y ) { - rectDef_t r; - listBoxDef_t *listPtr = item->typeData.list; + rectDef_t r; + listBoxDef_t *listPtr = item->typeData.list; + int listBoxFlags = ( WINDOW_LB_UPARROW | WINDOW_LB_DOWNARROW | WINDOW_LB_THUMB | + WINDOW_LB_PGUP | WINDOW_LB_PGDN ); + int total = DC->feederCount( item->feederID ); - item->window.flags &= ~( WINDOW_LB_LEFTARROW | WINDOW_LB_RIGHTARROW | WINDOW_LB_THUMB | - WINDOW_LB_PGUP | WINDOW_LB_PGDN ); + item->window.flags &= ~listBoxFlags; item->window.flags |= Item_ListBox_OverLB( item, x, y ); - if( item->window.flags & WINDOW_HORIZONTAL ) - { - if( !( item->window.flags & ( WINDOW_LB_LEFTARROW | WINDOW_LB_RIGHTARROW | WINDOW_LB_THUMB | - WINDOW_LB_PGUP | WINDOW_LB_PGDN ) ) ) - { - // check for selection hit as we have exausted buttons and thumb - - if( listPtr->elementStyle == LISTBOX_IMAGE ) - { - r.x = item->window.rect.x; - r.y = item->window.rect.y; - r.h = item->window.rect.h - SCROLLBAR_HEIGHT; - r.w = item->window.rect.w - listPtr->drawPadding; - - if( Rect_ContainsPoint( &r, x, y ) ) - { - listPtr->cursorPos = ( int )( ( x - r.x ) / listPtr->elementWidth ) + listPtr->startPos; - - if( listPtr->cursorPos >= listPtr->endPos ) - listPtr->cursorPos = listPtr->endPos; - } - } - } - } - else if( !( item->window.flags & ( WINDOW_LB_LEFTARROW | WINDOW_LB_RIGHTARROW | - WINDOW_LB_THUMB | WINDOW_LB_PGUP | WINDOW_LB_PGDN ) ) ) + if( !( item->window.flags & listBoxFlags ) ) { - r.x = item->window.rect.x; - r.y = item->window.rect.y; - r.w = item->window.rect.w - SCROLLBAR_WIDTH; - r.h = item->window.rect.h - listPtr->drawPadding; + r.x = SCROLLBAR_X( item ); + r.y = SCROLLBAR_Y( item ); + r.w = SCROLLBAR_W( item ); + r.h = listPtr->elementHeight * + MIN( Item_ListBox_NumItemsForItemHeight( item ), total ); if( Rect_ContainsPoint( &r, x, y ) ) { - listPtr->cursorPos = ( int )( ( y - 2 - r.y ) / listPtr->elementHeight ) + listPtr->startPos; + listPtr->cursorPos = (int)( ( y - r.y ) / listPtr->elementHeight ) + listPtr->startPos; - if( listPtr->cursorPos > listPtr->endPos ) - listPtr->cursorPos = listPtr->endPos; + if( listPtr->cursorPos >= listPtr->endPos ) + listPtr->cursorPos = listPtr->endPos - 1; } + else + listPtr->cursorPos = -1; } } @@ -2818,7 +2841,7 @@ void Item_MouseLeave( itemDef_t *item ) } Item_RunScript( item, item->mouseExit ); - item->window.flags &= ~( WINDOW_LB_RIGHTARROW | WINDOW_LB_LEFTARROW ); + item->window.flags &= ~( WINDOW_LB_DOWNARROW | WINDOW_LB_UPARROW ); } } @@ -2859,299 +2882,158 @@ qboolean Item_ListBox_HandleKey( itemDef_t *item, int key, qboolean down, qboole { listBoxDef_t *listPtr = item->typeData.list; int count = DC->feederCount( item->feederID ); - int max, viewmax; + int viewmax; if( force || ( Rect_ContainsPoint( &item->window.rect, DC->cursorx, DC->cursory ) && item->window.flags & WINDOW_HASFOCUS ) ) { - max = Item_ListBox_MaxScroll( item ); + viewmax = Item_ListBox_NumItemsForItemHeight( item ); - if( item->window.flags & WINDOW_HORIZONTAL ) + switch( key ) { - viewmax = ( item->window.rect.w / listPtr->elementWidth ); - - if( key == K_LEFTARROW || key == K_KP_LEFTARROW ) - { - if( !listPtr->notselectable ) + case K_MOUSE1: + case K_MOUSE2: + if( item->window.flags & WINDOW_LB_UPARROW ) + Item_ListBox_SetStartPos( item, listPtr->startPos - 1 ); + else if( item->window.flags & WINDOW_LB_DOWNARROW ) + Item_ListBox_SetStartPos( item, listPtr->startPos + 1 ); + else if( item->window.flags & WINDOW_LB_PGUP ) + Item_ListBox_SetStartPos( item, listPtr->startPos - viewmax ); + else if( item->window.flags & WINDOW_LB_PGDN ) + Item_ListBox_SetStartPos( item, listPtr->startPos + viewmax ); + else if( item->window.flags & WINDOW_LB_THUMB ) + break; // Handled by capture function + else { - listPtr->cursorPos--; + // Select an item + qboolean runDoubleClick = qfalse; + // Mouse isn't over an item if( listPtr->cursorPos < 0 ) - listPtr->cursorPos = 0; - - if( listPtr->cursorPos < listPtr->startPos ) - listPtr->startPos = listPtr->cursorPos; - - if( listPtr->cursorPos >= listPtr->startPos + viewmax ) - listPtr->startPos = listPtr->cursorPos - viewmax + 1; + break; - item->cursorPos = listPtr->cursorPos; - DC->feederSelection( item->feederID, item->cursorPos ); - } - else - { - listPtr->startPos--; + if( item->cursorPos != listPtr->cursorPos ) + { + item->cursorPos = listPtr->cursorPos; + DC->feederSelection( item->feederID, item->cursorPos ); + } - if( listPtr->startPos < 0 ) - listPtr->startPos = 0; - } + runDoubleClick = DC->realTime < lastListBoxClickTime && listPtr->doubleClick; + lastListBoxClickTime = DC->realTime + DOUBLE_CLICK_DELAY; - return qtrue; - } + // Made a selection, so close combobox + if( g_comboBoxItem != NULL ) + { + if( listPtr->doubleClick ) + runDoubleClick = qtrue; - if( key == K_RIGHTARROW || key == K_KP_RIGHTARROW ) - { - if( !listPtr->notselectable ) - { - listPtr->cursorPos++; + g_comboBoxItem = NULL; + } - if( listPtr->cursorPos < listPtr->startPos ) - listPtr->startPos = listPtr->cursorPos; + if( runDoubleClick ) + Item_RunScript( item, listPtr->doubleClick ); + } - if( listPtr->cursorPos >= count ) - listPtr->cursorPos = count - 1; + break; - if( listPtr->cursorPos >= listPtr->startPos + viewmax ) - listPtr->startPos = listPtr->cursorPos - viewmax + 1; + case K_MWHEELUP: + Item_ListBox_SetStartPos( item, listPtr->startPos - 1 ); + break; - item->cursorPos = listPtr->cursorPos; - DC->feederSelection( item->feederID, item->cursorPos ); - } - else - { - listPtr->startPos++; + case K_MWHEELDOWN: + Item_ListBox_SetStartPos( item, listPtr->startPos + 1 ); + break; - if( listPtr->startPos >= count ) - listPtr->startPos = count - 1; - } + case K_ENTER: + // Invoke the doubleClick handler when enter is pressed + if( listPtr->doubleClick ) + Item_RunScript( item, listPtr->doubleClick ); - return qtrue; - } - } - else - { - viewmax = ( item->window.rect.h / listPtr->elementHeight ); + break; - if( key == K_UPARROW || key == K_KP_UPARROW ) - { + case K_PGUP: + case K_KP_PGUP: if( !listPtr->notselectable ) { - listPtr->cursorPos--; + listPtr->cursorPos -= viewmax; if( listPtr->cursorPos < 0 ) listPtr->cursorPos = 0; if( listPtr->cursorPos < listPtr->startPos ) - listPtr->startPos = listPtr->cursorPos; + Item_ListBox_SetStartPos( item, listPtr->cursorPos ); if( listPtr->cursorPos >= listPtr->startPos + viewmax ) - listPtr->startPos = listPtr->cursorPos - viewmax + 1; + Item_ListBox_SetStartPos( item, listPtr->cursorPos - viewmax + 1 ); item->cursorPos = listPtr->cursorPos; DC->feederSelection( item->feederID, item->cursorPos ); } else - { - listPtr->startPos--; - - if( listPtr->startPos < 0 ) - listPtr->startPos = 0; - } + Item_ListBox_SetStartPos( item, listPtr->startPos - viewmax ); - return qtrue; - } + break; - if( key == K_DOWNARROW || key == K_KP_DOWNARROW ) - { + case K_PGDN: + case K_KP_PGDN: if( !listPtr->notselectable ) { - listPtr->cursorPos++; + listPtr->cursorPos += viewmax; if( listPtr->cursorPos < listPtr->startPos ) - listPtr->startPos = listPtr->cursorPos; + Item_ListBox_SetStartPos( item, listPtr->cursorPos ); if( listPtr->cursorPos >= count ) listPtr->cursorPos = count - 1; if( listPtr->cursorPos >= listPtr->startPos + viewmax ) - listPtr->startPos = listPtr->cursorPos - viewmax + 1; + Item_ListBox_SetStartPos( item, listPtr->cursorPos - viewmax + 1 ); item->cursorPos = listPtr->cursorPos; DC->feederSelection( item->feederID, item->cursorPos ); } else - { - listPtr->startPos++; - - if( listPtr->startPos > max ) - listPtr->startPos = max; - } - - return qtrue; - } - } + Item_ListBox_SetStartPos( item, listPtr->startPos + viewmax ); - // mouse hit - if( key == K_MOUSE1 || key == K_MOUSE2 ) - { - if( item->window.flags & WINDOW_LB_LEFTARROW ) - { - listPtr->startPos--; - - if( listPtr->startPos < 0 ) - listPtr->startPos = 0; - } - else if( item->window.flags & WINDOW_LB_RIGHTARROW ) - { - // one down - listPtr->startPos++; - - if( listPtr->startPos > max ) - listPtr->startPos = max; - } - else if( item->window.flags & WINDOW_LB_PGUP ) - { - // page up - listPtr->startPos -= viewmax; - - if( listPtr->startPos < 0 ) - listPtr->startPos = 0; - } - else if( item->window.flags & WINDOW_LB_PGDN ) - { - // page down - listPtr->startPos += viewmax; - - if( listPtr->startPos > max ) - listPtr->startPos = max; - } - else if( item->window.flags & WINDOW_LB_THUMB ) - { - // Display_SetCaptureItem(item); - } - else - { - // select an item - - if( item->cursorPos != listPtr->cursorPos ) - { - item->cursorPos = listPtr->cursorPos; - DC->feederSelection( item->feederID, item->cursorPos ); - } - - if( DC->realTime < lastListBoxClickTime && listPtr->doubleClick ) - Item_RunScript( item, listPtr->doubleClick ); - - lastListBoxClickTime = DC->realTime + DOUBLE_CLICK_DELAY; - } - - return qtrue; - } - - // Scroll wheel - if( key == K_MWHEELUP ) - { - listPtr->startPos--; - - if( listPtr->startPos < 0 ) - listPtr->startPos = 0; - - return qtrue; - } - - if( key == K_MWHEELDOWN ) - { - listPtr->startPos++; - - if( listPtr->startPos > max ) - listPtr->startPos = max; - - return qtrue; - } - - // Invoke the doubleClick handler when enter is pressed - if( key == K_ENTER ) - { - if( listPtr->doubleClick ) - Item_RunScript( item, listPtr->doubleClick ); - - return qtrue; - } - - if( key == K_HOME || key == K_KP_HOME ) - { - // home - listPtr->startPos = 0; - return qtrue; - } + break; - if( key == K_END || key == K_KP_END ) - { - // end - listPtr->startPos = max; - return qtrue; + default: + // Not handled + return qfalse; } - if( key == K_PGUP || key == K_KP_PGUP ) - { - // page up - - if( !listPtr->notselectable ) - { - listPtr->cursorPos -= viewmax; - - if( listPtr->cursorPos < 0 ) - listPtr->cursorPos = 0; - - if( listPtr->cursorPos < listPtr->startPos ) - listPtr->startPos = listPtr->cursorPos; + return qtrue; + } - if( listPtr->cursorPos >= listPtr->startPos + viewmax ) - listPtr->startPos = listPtr->cursorPos - viewmax + 1; + return qfalse; +} - item->cursorPos = listPtr->cursorPos; - DC->feederSelection( item->feederID, item->cursorPos ); - } - else - { - listPtr->startPos -= viewmax; +qboolean Item_ComboBox_HandleKey( itemDef_t *item, int key, qboolean down, qboolean force ) +{ + if( g_comboBoxItem != NULL ) + { + qboolean result; - if( listPtr->startPos < 0 ) - listPtr->startPos = 0; - } + qboolean cast = Item_ComboBox_MaybeCastToListBox( item ); + result = Item_ListBox_HandleKey( item, key, down, force ); + Item_ComboBox_MaybeUnCastFromListBox( item, cast ); - return qtrue; - } + if( !result ) + g_comboBoxItem = NULL; - if( key == K_PGDN || key == K_KP_PGDN ) + return result; + } + else + { + if( force || ( Rect_ContainsPoint( &item->window.rect, DC->cursorx, DC->cursory ) && + item->window.flags & WINDOW_HASFOCUS ) ) { - // page down - - if( !listPtr->notselectable ) - { - listPtr->cursorPos += viewmax; - - if( listPtr->cursorPos < listPtr->startPos ) - listPtr->startPos = listPtr->cursorPos; - - if( listPtr->cursorPos >= count ) - listPtr->cursorPos = count - 1; - - if( listPtr->cursorPos >= listPtr->startPos + viewmax ) - listPtr->startPos = listPtr->cursorPos - viewmax + 1; - - item->cursorPos = listPtr->cursorPos; - DC->feederSelection( item->feederID, item->cursorPos ); - } - else + if( key == K_MOUSE1 || key == K_MOUSE2 ) { - listPtr->startPos += viewmax; + g_comboBoxItem = item; - if( listPtr->startPos > max ) - listPtr->startPos = max; + return qtrue; } - - return qtrue; } } @@ -3246,13 +3128,13 @@ const char *Item_Multi_Setting( itemDef_t *item ) return ""; } -qboolean Item_Combobox_HandleKey( itemDef_t *item, int key ) +qboolean Item_Cycle_HandleKey( itemDef_t *item, int key ) { - comboBoxDef_t *comboPtr = item->typeData.combo; + cycleDef_t *cyclePtr = item->typeData.cycle; qboolean mouseOver = Rect_ContainsPoint( &item->window.rect, DC->cursorx, DC->cursory ); int count = DC->feederCount( item->feederID ); - if( comboPtr ) + if( cyclePtr ) { if( item->window.flags & WINDOW_HASFOCUS ) { @@ -3260,9 +3142,9 @@ qboolean Item_Combobox_HandleKey( itemDef_t *item, int key ) key == K_ENTER || key == K_RIGHTARROW || key == K_DOWNARROW ) { if( count > 0 ) - comboPtr->cursorPos = ( comboPtr->cursorPos + 1 ) % count; + cyclePtr->cursorPos = ( cyclePtr->cursorPos + 1 ) % count; - DC->feederSelection( item->feederID, comboPtr->cursorPos ); + DC->feederSelection( item->feederID, cyclePtr->cursorPos ); return qtrue; } @@ -3270,9 +3152,9 @@ qboolean Item_Combobox_HandleKey( itemDef_t *item, int key ) key == K_LEFTARROW || key == K_UPARROW ) { if( count > 0 ) - comboPtr->cursorPos = ( count + comboPtr->cursorPos - 1 ) % count; + cyclePtr->cursorPos = ( count + cyclePtr->cursorPos - 1 ) % count; - DC->feederSelection( item->feederID, comboPtr->cursorPos ); + DC->feederSelection( item->feederID, cyclePtr->cursorPos ); return qtrue; } @@ -3541,16 +3423,15 @@ exit: return !releaseFocus; } -static void Scroll_ListBox_AutoFunc( void *p ) +static void _Scroll_ListBox_AutoFunc( scrollInfo_t *si ) { - scrollInfo_t *si = ( scrollInfo_t* )p; - if( DC->realTime > si->nextScrollTime ) { // need to scroll which is done by simulating a click to the item // this is done a bit sideways as the autoscroll "knows" that the item is a listbox // so it calls it directly Item_ListBox_HandleKey( si->item, si->scrollKey, qtrue, qfalse ); + si->nextScrollTime = DC->realTime + si->adjustValue; } @@ -3563,49 +3444,36 @@ static void Scroll_ListBox_AutoFunc( void *p ) } } -static void Scroll_ListBox_ThumbFunc( void *p ) +static void Scroll_ListBox_AutoFunc( void *p ) { scrollInfo_t *si = ( scrollInfo_t* )p; - rectDef_t r; - int pos, max; - - if( si->item->window.flags & WINDOW_HORIZONTAL ) - { - if( DC->cursorx == si->xStart ) - return; - r.x = si->item->window.rect.x + SCROLLBAR_WIDTH + 1; - r.y = si->item->window.rect.y + si->item->window.rect.h - SCROLLBAR_HEIGHT - 1; - r.w = si->item->window.rect.w - ( SCROLLBAR_WIDTH * 2 ) - 2; - r.h = SCROLLBAR_HEIGHT; - max = Item_ListBox_MaxScroll( si->item ); - // - pos = ( DC->cursorx - r.x - SCROLLBAR_WIDTH / 2 ) * max / ( r.w - SCROLLBAR_WIDTH ); + qboolean cast = Item_ComboBox_MaybeCastToListBox( si->item ); + _Scroll_ListBox_AutoFunc( si ); + Item_ComboBox_MaybeUnCastFromListBox( si->item, cast ); +} - if( pos < 0 ) - pos = 0; - else if( pos > max ) - pos = max; +static void _Scroll_ListBox_ThumbFunc( scrollInfo_t *si ) +{ + rectDef_t r; + int pos, max; - si->item->typeData.list->startPos = pos; - si->xStart = DC->cursorx; - } - else if( DC->cursory != si->yStart ) + if( DC->cursory != si->yStart ) { - r.x = si->item->window.rect.x + si->item->window.rect.w - SCROLLBAR_WIDTH - 1; - r.y = si->item->window.rect.y + SCROLLBAR_HEIGHT + 1; - r.w = SCROLLBAR_WIDTH; - r.h = si->item->window.rect.h - ( SCROLLBAR_HEIGHT * 2 ) - 2; + r.x = si->item->window.rect.x + si->item->window.rect.w - SCROLLBAR_ARROW_WIDTH - 1; + r.y = si->item->window.rect.y + SCROLLBAR_ARROW_HEIGHT + 1; + r.w = SCROLLBAR_ARROW_WIDTH; + r.h = si->item->window.rect.h - ( SCROLLBAR_ARROW_HEIGHT * 2 ) - 2; max = Item_ListBox_MaxScroll( si->item ); // - pos = ( DC->cursory - r.y - SCROLLBAR_HEIGHT / 2 ) * max / ( r.h - SCROLLBAR_HEIGHT ); + pos = ( DC->cursory - r.y - SCROLLBAR_ARROW_HEIGHT / 2 ) * max / ( r.h - SCROLLBAR_ARROW_HEIGHT ); if( pos < 0 ) pos = 0; else if( pos > max ) pos = max; - si->item->typeData.list->startPos = pos; + Item_ListBox_SetStartPos( si->item, pos ); si->yStart = DC->cursory; } @@ -3615,6 +3483,7 @@ static void Scroll_ListBox_ThumbFunc( void *p ) // this is done a bit sideways as the autoscroll "knows" that the item is a listbox // so it calls it directly Item_ListBox_HandleKey( si->item, si->scrollKey, qtrue, qfalse ); + si->nextScrollTime = DC->realTime + si->adjustValue; } @@ -3627,6 +3496,15 @@ static void Scroll_ListBox_ThumbFunc( void *p ) } } +static void Scroll_ListBox_ThumbFunc( void *p ) +{ + scrollInfo_t *si = ( scrollInfo_t* )p; + + qboolean cast = Item_ComboBox_MaybeCastToListBox( si->item ); + _Scroll_ListBox_ThumbFunc( si ); + Item_ComboBox_MaybeUnCastFromListBox( si->item, cast ); +} + static void Scroll_Slider_ThumbFunc( void *p ) { float x, value, cursorx; @@ -3662,20 +3540,20 @@ void Item_StartCapture( itemDef_t *item, int key ) switch( item->type ) { - case ITEM_TYPE_EDITFIELD: - case ITEM_TYPE_SAYFIELD: - case ITEM_TYPE_NUMERICFIELD: case ITEM_TYPE_LISTBOX: + case ITEM_TYPE_COMBOBOX: { + qboolean cast = Item_ComboBox_MaybeCastToListBox( item ); flags = Item_ListBox_OverLB( item, DC->cursorx, DC->cursory ); + Item_ComboBox_MaybeUnCastFromListBox( item, cast ); - if( flags & ( WINDOW_LB_LEFTARROW | WINDOW_LB_RIGHTARROW ) ) + if( flags & ( WINDOW_LB_UPARROW | WINDOW_LB_DOWNARROW ) ) { scrollInfo.nextScrollTime = DC->realTime + SCROLL_TIME_START; scrollInfo.nextAdjustTime = DC->realTime + SCROLL_TIME_ADJUST; scrollInfo.adjustValue = SCROLL_TIME_START; scrollInfo.scrollKey = key; - scrollInfo.scrollDir = ( flags & WINDOW_LB_LEFTARROW ) ? qtrue : qfalse; + scrollInfo.scrollDir = ( flags & WINDOW_LB_UPARROW ) ? qtrue : qfalse; scrollInfo.item = item; UI_InstallCaptureFunc( Scroll_ListBox_AutoFunc, &scrollInfo, 0 ); itemCapture = item; @@ -3786,12 +3664,15 @@ qboolean Item_HandleKey( itemDef_t *item, int key, qboolean down ) case ITEM_TYPE_CHECKBOX: return qfalse; - case ITEM_TYPE_COMBO: - return Item_Combobox_HandleKey( item, key ); + case ITEM_TYPE_CYCLE: + return Item_Cycle_HandleKey( item, key ); case ITEM_TYPE_LISTBOX: return Item_ListBox_HandleKey( item, key, down, qfalse ); + case ITEM_TYPE_COMBOBOX: + return Item_ComboBox_HandleKey( item, key, down, qfalse ); + case ITEM_TYPE_YESNO: return Item_YesNo_HandleKey( item, key ); @@ -3921,7 +3802,7 @@ static void Display_CloseCinematics( void ) Menu_CloseCinematics( &Menus[i] ); } -void Menus_Activate( menuDef_t *menu ) +void Menus_Activate( menuDef_t *menu ) { int i; qboolean onTopOfMenuStack = qfalse; @@ -3950,15 +3831,15 @@ void Menus_Activate( menuDef_t *menu ) for( i = 0; i < menu->itemCount; i++ ) // reset selection in listboxes when opened { - if( menu->items[ i ]->type == ITEM_TYPE_LISTBOX ) + if( Item_IsListBox( menu->items[ i ] ) ) { - menu->items[ i ]->cursorPos = 0; - menu->items[ i ]->typeData.list->startPos = 0; - DC->feederSelection( menu->items[ i ]->feederID, 0 ); + menu->items[ i ]->cursorPos = DC->feederInitialise( menu->items[ i ]->feederID ); + Item_ListBox_SetStartPos( menu->items[ i ], 0 ); + DC->feederSelection( menu->items[ i ]->feederID, menu->items[ i ]->cursorPos ); } - else if( menu->items[ i ]->type == ITEM_TYPE_COMBO ) + else if( menu->items[ i ]->type == ITEM_TYPE_CYCLE ) { - menu->items[ i ]->typeData.combo->cursorPos = + menu->items[ i ]->typeData.cycle->cursorPos = DC->feederInitialise( menu->items[ i ]->feederID ); } @@ -3977,10 +3858,10 @@ qboolean Menus_ReplaceActive( menuDef_t *menu ) if( openMenuCount < 1 ) return qfalse; - active = menuStack[ openMenuCount - 1]; + active = menuStack[ openMenuCount - 1 ]; - if( !( active->window.flags & WINDOW_HASFOCUS ) || - !( active->window.flags & WINDOW_VISIBLE ) ) + if( !( active->window.flags & WINDOW_HASFOCUS ) || + !( active->window.flags & WINDOW_VISIBLE ) ) { return qfalse; } @@ -3990,8 +3871,8 @@ qboolean Menus_ReplaceActive( menuDef_t *menu ) if( menu->itemCount != active->itemCount ) return qfalse; - - for( i = 0; i < menu->itemCount; i++ ) + + for( i = 0; i < menu->itemCount; i++ ) { if( menu->items[ i ]->type != active->items[ i ]->type ) return qfalse; @@ -4007,14 +3888,7 @@ qboolean Menus_ReplaceActive( menuDef_t *menu ) item.parent = menu; Item_RunScript( &item, menu->onOpen ); } - - for( i = 0; i < menu->itemCount; i++ ) - { - menu->items[ i ]->cursorPos = active->items[ i ]->cursorPos; - menu->items[ i ]->typeData.list->startPos = active->items[ i ]->typeData.list->startPos; - menu->items[ i ]->feederID = active->items[ i ]->feederID; - menu->items[ i ]->typeData.combo->cursorPos = active->items[ i ]->typeData.combo->cursorPos; - } + return qtrue; } @@ -4134,12 +4008,17 @@ void Menu_HandleKey( menuDef_t *menu, int key, qboolean down ) } } - // get the item with focus - for( i = 0; i < menu->itemCount; i++ ) + if( g_comboBoxItem == NULL ) { - if( menu->items[i]->window.flags & WINDOW_HASFOCUS ) - item = menu->items[i]; + // get the item with focus + for( i = 0; i < menu->itemCount; i++ ) + { + if( menu->items[i]->window.flags & WINDOW_HASFOCUS ) + item = menu->items[i]; + } } + else + item = g_comboBoxItem; if( item != NULL ) { @@ -4973,7 +4852,7 @@ void Item_Multi_Paint( itemDef_t *item ) UI_Text_Paint( item->textRect.x, item->textRect.y, item->textscale, newColor, text, 0, 0, item->textStyle ); } -void Item_Combobox_Paint( itemDef_t *item ) +void Item_Cycle_Paint( itemDef_t *item ) { vec4_t newColor; const char *text = ""; @@ -4984,8 +4863,8 @@ void Item_Combobox_Paint( itemDef_t *item ) else memcpy( &newColor, &item->window.foreColor, sizeof( vec4_t ) ); - if( item->typeData.combo ) - text = DC->feederItemText( item->feederID, item->typeData.combo->cursorPos, + if( item->typeData.cycle ) + text = DC->feederItemText( item->feederID, item->typeData.cycle->cursorPos, 0, NULL ); if( item->text ) @@ -5422,16 +5301,6 @@ qboolean Item_Bind_HandleKey( itemDef_t *item, int key, qboolean down ) } - -void AdjustFrom640( float *x, float *y, float *w, float *h ) -{ - //*x = *x * DC->scale + DC->bias; - *x *= DC->xscale; - *y *= DC->yscale; - *w *= DC->xscale; - *h *= DC->yscale; -} - void Item_Model_Paint( itemDef_t *item ) { float x, y, w, h; @@ -5456,7 +5325,7 @@ void Item_Model_Paint( itemDef_t *item ) w = item->window.rect.w - 2; h = item->window.rect.h - 2; - AdjustFrom640( &x, &y, &w, &h ); + UI_AdjustFrom640( &x, &y, &w, &h ); refdef.x = x; refdef.y = y; @@ -5524,294 +5393,222 @@ void Item_Model_Paint( itemDef_t *item ) } -void Item_Image_Paint( itemDef_t *item ) +void Item_ListBoxRow_Paint( itemDef_t *item, int row, int renderPos, qboolean highlight, qboolean scrollbar ) { - if( item == NULL ) - return; - - DC->drawHandlePic( item->window.rect.x + 1, item->window.rect.y + 1, - item->window.rect.w - 2, item->window.rect.h - 2, item->asset ); -} + float x, y, w; + listBoxDef_t *listPtr = item->typeData.list; + menuDef_t *menu = ( menuDef_t * )item->parent; + float one, two; -void Item_ListBox_Paint( itemDef_t *item ) -{ - float x, y, size, thumb; - int i, count; - qhandle_t image; - qhandle_t optionalImage; - listBoxDef_t *listPtr = item->typeData.list; - menuDef_t *menu = ( menuDef_t * )item->parent; - float one, two; + one = 1.0f * DC->aspectScale; + two = 2.0f * DC->aspectScale; - if( menu->window.aspectBias != ASPECT_NONE || item->window.aspectBias != ASPECT_NONE ) - { - one = 1.0f * DC->aspectScale; - two = 2.0f * DC->aspectScale; - } - else - { - one = 1.0f; - two = 2.0f; - } + x = SCROLLBAR_X( item ); + y = SCROLLBAR_Y( item ) + ( listPtr->elementHeight * renderPos ); + w = item->window.rect.w - ( two * item->window.borderSize ); - // the listbox is horizontal or vertical and has a fixed size scroll bar going either direction - // elements are enumerated from the DC and either text or image handles are acquired from the DC as well - // textscale is used to size the text, textalignx and textaligny are used to size image elements - // there is no clipping available so only the last completely visible item is painted - count = DC->feederCount( item->feederID ); + if( scrollbar ) + w -= SCROLLBAR_ARROW_WIDTH; - // default is vertical if horizontal flag is not here - if( item->window.flags & WINDOW_HORIZONTAL ) + if( listPtr->elementStyle == LISTBOX_IMAGE ) { - //FIXME: unmaintained cruft? + qhandle_t image = DC->feederItemImage( item->feederID, row ); - if( !listPtr->noscrollbar ) - { - // draw scrollbar in bottom of the window - // bar - x = item->window.rect.x + 1; - y = item->window.rect.y + item->window.rect.h - SCROLLBAR_HEIGHT - 1; - DC->drawHandlePic( x, y, SCROLLBAR_WIDTH, SCROLLBAR_HEIGHT, DC->Assets.scrollBarArrowLeft ); - x += SCROLLBAR_WIDTH - 1; - size = item->window.rect.w - ( SCROLLBAR_WIDTH * 2 ); - DC->drawHandlePic( x, y, size + 1, SCROLLBAR_HEIGHT, DC->Assets.scrollBar ); - x += size - 1; - DC->drawHandlePic( x, y, SCROLLBAR_WIDTH, SCROLLBAR_HEIGHT, DC->Assets.scrollBarArrowRight ); - // thumb - thumb = Item_ListBox_ThumbDrawPosition( item );//Item_ListBox_ThumbPosition(item); + UI_SetClipRegion( x, y, listPtr->elementWidth, listPtr->elementHeight ); - if( thumb > x - SCROLLBAR_WIDTH - 1 ) - thumb = x - SCROLLBAR_WIDTH - 1; + if( image ) + DC->drawHandlePic( x + one, y + 1.0f, listPtr->elementWidth - two, listPtr->elementHeight - 2.0f, image ); - DC->drawHandlePic( thumb, y, SCROLLBAR_WIDTH, SCROLLBAR_HEIGHT, DC->Assets.scrollBarThumb ); - // - listPtr->endPos = listPtr->startPos; + if( highlight && row == item->cursorPos ) + { + DC->drawRect( x, y, listPtr->elementWidth, listPtr->elementHeight, + item->window.borderSize, item->window.borderColor ); } - size = item->window.rect.w - 2; - // items - // size contains max available space + UI_ClearClipRegion( ); + } + else + { + const float m = UI_Text_EmHeight( item->textscale ); + char text[ MAX_STRING_CHARS ]; + qhandle_t optionalImage; - if( listPtr->elementStyle == LISTBOX_IMAGE ) + if( listPtr->numColumns > 0 ) { - // fit = 0; - x = item->window.rect.x + 1; - y = item->window.rect.y + 1; + int j; - for( i = listPtr->startPos; i < count; i++ ) + for( j = 0; j < listPtr->numColumns; j++ ) { - // always draw at least one - // which may overdraw the box if it is too small for the element - image = DC->feederItemImage( item->feederID, i ); + float columnPos; + float width, height, yOffset; - if( image ) - DC->drawHandlePic( x + 1, y + 1, listPtr->elementWidth - 2, listPtr->elementHeight - 2, image ); - - if( i == item->cursorPos ) + if( menu->window.aspectBias != ASPECT_NONE || item->window.aspectBias != ASPECT_NONE ) { - DC->drawRect( x, y, listPtr->elementWidth - 1, listPtr->elementHeight - 1, - item->window.borderSize, item->window.borderColor ); + columnPos = ( listPtr->columnInfo[ j ].pos + 4.0f ) * DC->aspectScale; + width = listPtr->columnInfo[ j ].width * DC->aspectScale; } - - listPtr->endPos++; - size -= listPtr->elementWidth; - - if( size < listPtr->elementWidth ) + else { - listPtr->drawPadding = size; //listPtr->elementWidth - size; - break; + columnPos = ( listPtr->columnInfo[ j ].pos + 4.0f ); + width = listPtr->columnInfo[ j ].width; } - x += listPtr->elementWidth; - // fit++; - } - } - } - else - { - if( !listPtr->noscrollbar ) - { - // draw scrollbar to right side of the window - x = item->window.rect.x + item->window.rect.w - SCROLLBAR_WIDTH - one; - y = item->window.rect.y + 1; - DC->drawHandlePic( x, y, SCROLLBAR_WIDTH, SCROLLBAR_HEIGHT, DC->Assets.scrollBarArrowUp ); - y += SCROLLBAR_HEIGHT - 1; - - listPtr->endPos = listPtr->startPos; - size = item->window.rect.h - ( SCROLLBAR_HEIGHT * 2 ); - DC->drawHandlePic( x, y, SCROLLBAR_WIDTH, size + 1, DC->Assets.scrollBar ); - y += size - 1; - DC->drawHandlePic( x, y, SCROLLBAR_WIDTH, SCROLLBAR_HEIGHT, DC->Assets.scrollBarArrowDown ); - // thumb - thumb = Item_ListBox_ThumbDrawPosition( item );//Item_ListBox_ThumbPosition(item); + height = listPtr->columnInfo[ j ].width; + yOffset = y + ( ( listPtr->elementHeight - height ) / 2.0f ); - if( thumb > y - SCROLLBAR_HEIGHT - 1 ) - thumb = y - SCROLLBAR_HEIGHT - 1; + Q_strncpyz( text, DC->feederItemText( item->feederID, row, j, &optionalImage ), sizeof( text ) ); - DC->drawHandlePic( x, thumb, SCROLLBAR_WIDTH, SCROLLBAR_HEIGHT, DC->Assets.scrollBarThumb ); - } + UI_SetClipRegion( x + columnPos, yOffset, width, height ); - // adjust size for item painting - size = item->window.rect.h - 2; + if( optionalImage >= 0 ) + DC->drawHandlePic( x + columnPos, yOffset, width, height, optionalImage ); + else if( text[ 0 ] ) + { + float alignOffset = 0.0f, tw; - if( listPtr->elementStyle == LISTBOX_IMAGE ) - { - // fit = 0; - x = item->window.rect.x + one; - y = item->window.rect.y + 1; + tw = UI_Text_Width( text, item->textscale, 0 ); - for( i = listPtr->startPos; i < count; i++ ) - { - // always draw at least one - // which may overdraw the box if it is too small for the element - image = DC->feederItemImage( item->feederID, i ); + switch( listPtr->columnInfo[ j ].align ) + { + case ALIGN_LEFT: + alignOffset = 0.0f; + break; - if( image ) - DC->drawHandlePic( x + one, y + 1, listPtr->elementWidth - two, listPtr->elementHeight - 2, image ); + case ALIGN_RIGHT: + alignOffset = width - tw; + break; - if( i == item->cursorPos ) - { - DC->drawRect( x, y, listPtr->elementWidth - one, listPtr->elementHeight - 1, - item->window.borderSize, item->window.borderColor ); - } + case ALIGN_CENTER: + alignOffset = ( width / 2.0f ) - ( tw / 2.0f ); + break; - listPtr->endPos++; - size -= listPtr->elementWidth; + default: + alignOffset = 0.0f; + } - if( size < listPtr->elementHeight ) - { - listPtr->drawPadding = listPtr->elementHeight - size; - break; + UI_Text_Paint( x + columnPos + alignOffset, + y + m + ( ( listPtr->elementHeight - m ) / 2.0f ), + item->textscale, item->window.foreColor, text, 0, + 0, item->textStyle ); } - y += listPtr->elementHeight; - // fit++; + UI_ClearClipRegion( ); } } else { - float m = UI_Text_EmHeight( item->textscale ); - x = item->window.rect.x + one; - y = item->window.rect.y + 1; + float offset; - for( i = listPtr->startPos; i < count; i++ ) - { - char text[ MAX_STRING_CHARS ]; - // always draw at least one - // which may overdraw the box if it is too small for the element + if( menu->window.aspectBias != ASPECT_NONE || item->window.aspectBias != ASPECT_NONE ) + offset = 4.0f * DC->aspectScale; + else + offset = 4.0f; - if( listPtr->numColumns > 0 ) - { - int j; + Q_strncpyz( text, DC->feederItemText( item->feederID, row, 0, &optionalImage ), sizeof( text ) ); - for( j = 0; j < listPtr->numColumns; j++ ) - { - float columnPos; - float width, height; + UI_SetClipRegion( x, y, w, listPtr->elementHeight ); - if( menu->window.aspectBias != ASPECT_NONE || item->window.aspectBias != ASPECT_NONE ) - { - columnPos = ( listPtr->columnInfo[ j ].pos + 4.0f ) * DC->aspectScale; - width = listPtr->columnInfo[ j ].width * DC->aspectScale; - } - else - { - columnPos = ( listPtr->columnInfo[ j ].pos + 4.0f ); - width = listPtr->columnInfo[ j ].width; - } + if( optionalImage >= 0 ) + DC->drawHandlePic( x + offset, y, listPtr->elementHeight, listPtr->elementHeight, optionalImage ); + else if( text[ 0 ] ) + { + UI_Text_Paint( x + offset, y + m + ( ( listPtr->elementHeight - m ) / 2.0f ), + item->textscale, item->window.foreColor, text, 0, + 0, item->textStyle ); + } - height = listPtr->columnInfo[ j ].width; + UI_ClearClipRegion( ); + } - Q_strncpyz( text, DC->feederItemText( item->feederID, i, j, &optionalImage ), sizeof( text ) ); + if( highlight && row == item->cursorPos ) + DC->fillRect( x, y, w, listPtr->elementHeight, item->window.outlineColor ); + } +} - if( optionalImage >= 0 ) - { - DC->drawHandlePic( x + columnPos, y + ( ( listPtr->elementHeight - height ) / 2.0f ), - width, height, optionalImage ); - } - else if( text[ 0 ] ) - { - int alignOffset = 0.0f, tw; +void Item_ListBox_Paint( itemDef_t *item ) +{ + float size; + int i; + listBoxDef_t *listPtr = item->typeData.list; + int count = DC->feederCount( item->feederID ); + qboolean scrollbar = !listPtr->noscrollbar && + count > Item_ListBox_NumItemsForItemHeight( item ); - tw = UI_Text_Width( text, item->textscale, 0 ); + if( scrollbar ) + { + float x = SCROLLBAR_SLIDER_X( item ); + float y = SCROLLBAR_Y( item ); + float thumbY = Item_ListBox_ThumbDrawPosition( item ); - // Shorten the string if it's too long + // Up arrow + DC->drawHandlePic( x, y, SCROLLBAR_ARROW_WIDTH, SCROLLBAR_ARROW_HEIGHT, DC->Assets.scrollBarArrowUp ); + y = SCROLLBAR_SLIDER_Y( item ); - while( tw > width && strlen( text ) > 0 ) - { - text[ strlen( text ) - 1 ] = '\0'; - tw = UI_Text_Width( text, item->textscale, 0 ); - } + // Scroll bar + size = SCROLLBAR_SLIDER_HEIGHT( item ); + DC->drawHandlePic( x, y, SCROLLBAR_ARROW_WIDTH, size, DC->Assets.scrollBar ); + y = SCROLLBAR_SLIDER_Y( item ) + size; - switch( listPtr->columnInfo[ j ].align ) - { - case ALIGN_LEFT: - alignOffset = 0.0f; - break; + // Down arrow + DC->drawHandlePic( x, y, SCROLLBAR_ARROW_WIDTH, SCROLLBAR_ARROW_HEIGHT, DC->Assets.scrollBarArrowDown ); - case ALIGN_RIGHT: - alignOffset = width - tw; - break; + // Thumb + DC->drawHandlePic( x, thumbY, SCROLLBAR_ARROW_WIDTH, SCROLLBAR_ARROW_HEIGHT, DC->Assets.scrollBarThumb ); + } - case ALIGN_CENTER: - alignOffset = ( width / 2.0f ) - ( tw / 2.0f ); - break; + // Paint rows + for( i = listPtr->startPos; i < listPtr->endPos; i++ ) + Item_ListBoxRow_Paint( item, i, i - listPtr->startPos, qtrue, scrollbar ); +} - default: - alignOffset = 0.0f; - } +void Item_Paint( itemDef_t *item ); - UI_Text_Paint( x + columnPos + alignOffset, - y + m + ( ( listPtr->elementHeight - m ) / 2.0f ), - item->textscale, item->window.foreColor, text, 0, - 0, item->textStyle ); - } - } - } - else - { - float offset; +void Item_ComboBox_Paint( itemDef_t *item ) +{ + float x, y, h; - if( menu->window.aspectBias != ASPECT_NONE || item->window.aspectBias != ASPECT_NONE ) - offset = 4.0f * DC->aspectScale; - else - offset = 4.0f; + x = SCROLLBAR_SLIDER_X( item ); + y = SCROLLBAR_Y( item ); + h = item->window.rect.h - 2.0f; - Q_strncpyz( text, DC->feederItemText( item->feederID, i, 0, &optionalImage ), sizeof( text ) ); + // Down arrow + DC->drawHandlePic( x, y, SCROLLBAR_ARROW_WIDTH, h, DC->Assets.scrollBarArrowDown ); - if( optionalImage >= 0 ) - DC->drawHandlePic( x + offset, y, listPtr->elementHeight, listPtr->elementHeight, optionalImage ); - else if( text[ 0 ] ) - { - UI_Text_Paint( x + offset, y + m + ( ( listPtr->elementHeight - m ) / 2.0f ), - item->textscale, item->window.foreColor, text, 0, - 0, item->textStyle ); - } - } + Item_ListBoxRow_Paint( item, item->cursorPos, 0, qfalse, qtrue ); - if( i == item->cursorPos ) - { - DC->fillRect( x, y, item->window.rect.w - SCROLLBAR_WIDTH - ( two * item->window.borderSize ), - listPtr->elementHeight, item->window.outlineColor ); - } + if( g_comboBoxItem != NULL ) + { + qboolean cast = Item_ComboBox_MaybeCastToListBox( item ); + Item_Paint( item ); + Item_ComboBox_MaybeUnCastFromListBox( item, cast ); + } +} - listPtr->endPos++; - size -= listPtr->elementHeight; +void Item_ListBox_Update( itemDef_t *item ) +{ + listBoxDef_t *listPtr = item->typeData.list; + int feederCount = DC->feederCount( item->feederID ); - if( size < listPtr->elementHeight ) - { - listPtr->drawPadding = listPtr->elementHeight - size; - break; - } + if( listPtr->lastFeederCount != feederCount ) + { + if( listPtr->resetonfeederchange ) + { + item->cursorPos = DC->feederInitialise( item->feederID ); + Item_ListBox_SetStartPos( item, 0 ); + DC->feederSelection( item->feederID, item->cursorPos ); + } + else + { + // Make sure endPos is up-to-date + Item_ListBox_SetStartPos( item, listPtr->startPos ); - y += listPtr->elementHeight; - // fit++; - } + // If the selection is off the end now, select the last element + if( item->cursorPos >= feederCount ) + item->cursorPos = feederCount - 1; } } - // FIXME: hacky fix to off-by-one bug - listPtr->endPos--; + listPtr->lastFeederCount = feederCount; } void Item_OwnerDraw_Paint( itemDef_t *item ) @@ -5893,6 +5690,15 @@ void Item_OwnerDraw_Paint( itemDef_t *item ) } +void Item_Update( itemDef_t *item ) +{ + if( item == NULL ) + return; + + if( Item_IsListBox( item ) ) + Item_ListBox_Update( item ); +} + void Item_Paint( itemDef_t *item ) { vec4_t red; @@ -6067,7 +5873,7 @@ void Item_Paint( itemDef_t *item ) if( !( item->window.flags & WINDOW_VISIBLE ) ) return; - Window_Paint( &item->window, parent->fadeAmount , parent->fadeClamp, parent->fadeCycle ); + Window_Paint( &item->window, parent->fadeAmount, parent->fadeClamp, parent->fadeCycle ); if( DC->getCVarValue( "ui_developer" ) ) { @@ -6095,17 +5901,17 @@ void Item_Paint( itemDef_t *item ) case ITEM_TYPE_CHECKBOX: break; - case ITEM_TYPE_COMBO: - Item_Combobox_Paint( item ); + case ITEM_TYPE_CYCLE: + Item_Cycle_Paint( item ); break; case ITEM_TYPE_LISTBOX: Item_ListBox_Paint( item ); break; - //case ITEM_TYPE_IMAGE: - // Item_Image_Paint(item); - // break; + case ITEM_TYPE_COMBOBOX: + Item_ComboBox_Paint( item ); + break; case ITEM_TYPE_MODEL: Item_Model_Paint( item ); @@ -6185,9 +5991,14 @@ void Menu_ScrollFeeder( menuDef_t *menu, int feeder, qboolean down ) for( i = 0; i < menu->itemCount; i++ ) { - if( menu->items[i]->feederID == feeder ) + itemDef_t *item = menu->items[ i ]; + + if( item->feederID == feeder ) { - Item_ListBox_HandleKey( menu->items[i], ( down ) ? K_DOWNARROW : K_UPARROW, qtrue, qtrue ); + qboolean cast = Item_ComboBox_MaybeCastToListBox( item ); + Item_ListBox_HandleKey( item, down ? K_DOWNARROW : K_UPARROW, qtrue, qtrue ); + Item_ComboBox_MaybeUnCastFromListBox( item, cast ); + return; } } @@ -6214,10 +6025,10 @@ void Menu_SetFeederSelection( menuDef_t *menu, int feeder, int index, const char { if( menu->items[i]->feederID == feeder ) { - if( menu->items[i]->type == ITEM_TYPE_LISTBOX && index == 0 ) + if( Item_IsListBox( menu->items[i] ) && index == 0 ) { menu->items[ i ]->typeData.list->cursorPos = 0; - menu->items[ i ]->typeData.list->startPos = 0; + Item_ListBox_SetStartPos( menu->items[ i ], 0 ); } menu->items[i]->cursorPos = index; @@ -6297,12 +6108,41 @@ void Item_Init( itemDef_t *item ) item->window.aspectBias = ASPECT_NONE; } +static qboolean Item_HandleMouseMove( itemDef_t *item, float x, float y, int pass, qboolean focusSet ) +{ + if( Rect_ContainsPoint( &item->window.rect, x, y ) ) + { + if( pass == 1 ) + { + if( item->type == ITEM_TYPE_TEXT && item->text ) + { + if( !Rect_ContainsPoint( Item_CorrectedTextRect( item ), x, y ) ) + return qtrue; + } + + // if we are over an item + if( IsVisible( item->window.flags ) ) + { + // different one + Item_MouseEnter( item, x, y ); + + if( !focusSet ) + focusSet = Item_SetFocus( item, x, y ); + } + } + + return qtrue; + } + + return qfalse; +} + void Menu_HandleMouseMove( menuDef_t *menu, float x, float y ) { int i, pass; qboolean focusSet = qfalse; - - itemDef_t *overItem; + qboolean result; + qboolean cast; if( menu == NULL ) return; @@ -6319,65 +6159,60 @@ void Menu_HandleMouseMove( menuDef_t *menu, float x, float y ) if( g_waitingForKey || g_editingField ) return; + if( g_comboBoxItem != NULL ) + { + Item_SetFocus( g_comboBoxItem, x, y ); + focusSet = qtrue; + } + // FIXME: this is the whole issue of focus vs. mouse over.. // need a better overall solution as i don't like going through everything twice for( pass = 0; pass < 2; pass++ ) { for( i = 0; i < menu->itemCount; i++ ) { + itemDef_t *item = menu->items[ i ]; + // turn off focus each item // menu->items[i].window.flags &= ~WINDOW_HASFOCUS; - if( !( menu->items[i]->window.flags & ( WINDOW_VISIBLE | WINDOW_FORCED ) ) ) + if( !( item->window.flags & ( WINDOW_VISIBLE | WINDOW_FORCED ) ) ) continue; // items can be enabled and disabled based on cvars - if( menu->items[i]->cvarFlags & ( CVAR_ENABLE | CVAR_DISABLE ) && - !Item_EnableShowViaCvar( menu->items[i], CVAR_ENABLE ) ) + if( item->cvarFlags & ( CVAR_ENABLE | CVAR_DISABLE ) && + !Item_EnableShowViaCvar( item, CVAR_ENABLE ) ) continue; - if( menu->items[i]->cvarFlags & ( CVAR_SHOW | CVAR_HIDE ) && - !Item_EnableShowViaCvar( menu->items[i], CVAR_SHOW ) ) + if( item->cvarFlags & ( CVAR_SHOW | CVAR_HIDE ) && + !Item_EnableShowViaCvar( item, CVAR_SHOW ) ) continue; + cast = Item_ComboBox_MaybeCastToListBox( item ); + result = Item_HandleMouseMove( item, x, y, pass, focusSet ); + Item_ComboBox_MaybeUnCastFromListBox( item, cast ); - - if( Rect_ContainsPoint( &menu->items[i]->window.rect, x, y ) ) - { - if( pass == 1 ) - { - overItem = menu->items[i]; - - if( overItem->type == ITEM_TYPE_TEXT && overItem->text ) - { - if( !Rect_ContainsPoint( Item_CorrectedTextRect( overItem ), x, y ) ) - continue; - } - - // if we are over an item - if( IsVisible( overItem->window.flags ) ) - { - // different one - Item_MouseEnter( overItem, x, y ); - // Item_SetMouseOver(overItem, qtrue); - - // if item is not a decoration see if it can take focus - - if( !focusSet ) - focusSet = Item_SetFocus( overItem, x, y ); - } - } - } - else if( menu->items[i]->window.flags & WINDOW_MOUSEOVER ) + if( !result && item->window.flags & WINDOW_MOUSEOVER ) { - Item_MouseLeave( menu->items[i] ); - Item_SetMouseOver( menu->items[i], qfalse ); + Item_MouseLeave( item ); + Item_SetMouseOver( item, qfalse ); } } } } +void Menu_Update( menuDef_t *menu ) +{ + int i; + + if( menu == NULL ) + return; + + for( i = 0; i < menu->itemCount; i++ ) + Item_Update( menu->items[ i ] ); +} + void Menu_Paint( menuDef_t *menu, qboolean forcePaint ) { int i; @@ -6385,7 +6220,7 @@ void Menu_Paint( menuDef_t *menu, qboolean forcePaint ) if( menu == NULL ) return; - if( !( menu->window.flags & WINDOW_VISIBLE ) && !forcePaint ) + if( !( menu->window.flags & WINDOW_VISIBLE ) && !forcePaint ) return; if( menu->window.ownerDrawFlags && DC->ownerDrawVisible && !DC->ownerDrawVisible( menu->window.ownerDrawFlags ) ) @@ -6501,9 +6336,10 @@ itemDataType_t Item_DataType( itemDef_t *item ) return TYPE_NONE; case ITEM_TYPE_LISTBOX: + case ITEM_TYPE_COMBOBOX: return TYPE_LIST; - case ITEM_TYPE_COMBO: + case ITEM_TYPE_CYCLE: return TYPE_COMBO; case ITEM_TYPE_EDITFIELD: @@ -6544,6 +6380,24 @@ static ID_INLINE qboolean Item_IsEditField( itemDef_t *item ) /* =============== +Item_IsListBox +=============== +*/ +static ID_INLINE qboolean Item_IsListBox( itemDef_t *item ) +{ + switch( item->type ) + { + case ITEM_TYPE_LISTBOX: + case ITEM_TYPE_COMBOBOX: + return qtrue; + + default: + return qfalse; + } +} + +/* +=============== Item Keyword Parse functions =============== */ @@ -6693,21 +6547,21 @@ qboolean ItemParse_noscrollbar( itemDef_t *item, int handle ) return qtrue; } -// auto wrapped -qboolean ItemParse_wrapped( itemDef_t *item, int handle ) +// resetonfeederchange +qboolean ItemParse_resetonfeederchange( itemDef_t *item, int handle ) { - item->window.flags |= WINDOW_WRAPPED; + item->typeData.list->resetonfeederchange = qtrue; return qtrue; } - -// horizontalscroll -qboolean ItemParse_horizontalscroll( itemDef_t *item, int handle ) +// auto wrapped +qboolean ItemParse_wrapped( itemDef_t *item, int handle ) { - item->window.flags |= WINDOW_HORIZONTAL; + item->window.flags |= WINDOW_WRAPPED; return qtrue; } + // type <integer> qboolean ItemParse_type( itemDef_t *item, int handle ) { @@ -6730,13 +6584,14 @@ qboolean ItemParse_type( itemDef_t *item, int handle ) switch( item->type ) { case ITEM_TYPE_LISTBOX: + case ITEM_TYPE_COMBOBOX: item->typeData.list = UI_Alloc( sizeof( listBoxDef_t ) ); memset( item->typeData.list, 0, sizeof( listBoxDef_t ) ); break; - case ITEM_TYPE_COMBO: - item->typeData.combo = UI_Alloc( sizeof( comboBoxDef_t ) ); - memset( item->typeData.combo, 0, sizeof( comboBoxDef_t ) ); + case ITEM_TYPE_CYCLE: + item->typeData.cycle = UI_Alloc( sizeof( cycleDef_t ) ); + memset( item->typeData.cycle, 0, sizeof( cycleDef_t ) ); break; case ITEM_TYPE_EDITFIELD: @@ -6771,19 +6626,23 @@ qboolean ItemParse_type( itemDef_t *item, int handle ) } // elementwidth, used for listbox image elements -// uses textalignx for storage qboolean ItemParse_elementwidth( itemDef_t *item, int handle ) { return PC_Float_Parse( handle, &item->typeData.list->elementWidth ); } // elementheight, used for listbox image elements -// uses textaligny for storage qboolean ItemParse_elementheight( itemDef_t *item, int handle ) { return PC_Float_Parse( handle, &item->typeData.list->elementHeight ); } +// dropitems, number of items to drop from a combobox +qboolean ItemParse_dropitems( itemDef_t *item, int handle ) +{ + return PC_Int_Parse( handle, &item->typeData.list->dropItems ); +} + // feeder <int> qboolean ItemParse_feeder( itemDef_t *item, int handle ) { @@ -7353,10 +7212,11 @@ keywordHash_t itemParseKeywords[] = { {"decoration", ItemParse_decoration, TYPE_ANY}, {"notselectable", ItemParse_notselectable, TYPE_LIST}, {"noscrollbar", ItemParse_noscrollbar, TYPE_LIST}, + {"resetonfeederchange", ItemParse_resetonfeederchange, TYPE_LIST}, {"wrapped", ItemParse_wrapped, TYPE_ANY}, - {"horizontalscroll", ItemParse_horizontalscroll, TYPE_ANY}, {"elementwidth", ItemParse_elementwidth, TYPE_LIST}, {"elementheight", ItemParse_elementheight, TYPE_LIST}, + {"dropitems", ItemParse_dropitems, TYPE_LIST}, {"feeder", ItemParse_feeder, TYPE_ANY}, {"elementtype", ItemParse_elementtype, TYPE_LIST}, {"columns", ItemParse_columns, TYPE_LIST}, @@ -7493,15 +7353,14 @@ void Item_InitControls( itemDef_t *item ) if( item == NULL ) return; - if( item->type == ITEM_TYPE_LISTBOX ) + if( Item_IsListBox( item ) ) { item->cursorPos = 0; if( item->typeData.list ) { item->typeData.list->cursorPos = 0; - item->typeData.list->startPos = 0; - item->typeData.list->endPos = 0; + Item_ListBox_SetStartPos( item, 0 ); item->typeData.list->cursorPos = 0; } } @@ -7994,6 +7853,14 @@ int Menu_Count( void ) return menuCount; } +void Menu_UpdateAll( void ) +{ + int i; + + for( i = 0; i < openMenuCount; i++ ) + Menu_Update( menuStack[ i ] ); +} + void Menu_PaintAll( void ) { int i; @@ -8012,7 +7879,7 @@ void Menu_PaintAll( void ) } for( i = 0; i < openMenuCount; i++ ) - Menu_Paint( menuStack[i], qfalse ); + Menu_Paint( menuStack[ i ], qfalse ); if( DC->getCVarValue( "ui_developer" ) ) { |