From dcd38e6a42fd9f7532de6b20086bcb1d3c8075b1 Mon Sep 17 00:00:00 2001 From: Rudolf Polzer Date: Tue, 8 Oct 2013 02:58:50 +0200 Subject: [PATCH 1/1] Refactor to try to support variable sized list items. --- qcsrc/menu/item/listbox.c | 104 ++++++++++++++++++++++++++------------ 1 file changed, 73 insertions(+), 31 deletions(-) diff --git a/qcsrc/menu/item/listbox.c b/qcsrc/menu/item/listbox.c index c32057da8d..dc2663149e 100644 --- a/qcsrc/menu/item/listbox.c +++ b/qcsrc/menu/item/listbox.c @@ -38,6 +38,15 @@ CLASS(ListBox) EXTENDS(Item) METHOD(ListBox, drawListBoxItem, void(entity, float, vector, float)) // item number, width/height, selected METHOD(ListBox, clickListBoxItem, void(entity, float, vector)) // item number, relative clickpos METHOD(ListBox, setSelected, void(entity, float)) + + METHOD(ListBox, getLastFullyVisibleItemAtScrollPos, float(entity, float)) + METHOD(ListBox, getFirstFullyVisibleItemAtScrollPos, float(entity, float)) + + // NOTE: override these four methods if you want variable sized list items + METHOD(ListBox, getTotalHeight, float(entity)) + METHOD(ListBox, getItemAtPos, float(entity, float)) + METHOD(ListBox, getItemStart, float(entity, float)) + METHOD(ListBox, getItemHeight, float(entity, float)) ENDCLASS(ListBox) #endif @@ -56,23 +65,55 @@ void ListBox_configureListBox(entity me, float theScrollbarWidth, float theItemH me.scrollbarWidth = theScrollbarWidth; me.itemHeight = theItemHeight; } + +float ListBox_getTotalHeight(entity me) +{ + return me.nItems * me.itemHeight; +} +float ListBox_getItemAtPos(entity me, float pos) +{ + return floor(pos / me.itemHeight); +} +float ListBox_getItemStart(entity me, float i) +{ + return me.itemHeight * i; +} +float ListBox_getItemHeight(entity me, float i) +{ + return me.itemHeight; +} + +float ListBox_getLastFullyVisibleItemAtScrollPos(entity me, float pos) +{ + return me.getItemAtPos(me, pos + 1.001) - 1; +} +float ListBox_getFirstFullyVisibleItemAtScrollPos(entity me, float pos) +{ + return me.getItemAtPos(me, pos - 0.001) + 1; +} float ListBox_keyDown(entity me, float key, float ascii, float shift) { me.dragScrollTimer = time; if(key == K_MWHEELUP) { me.scrollPos = max(me.scrollPos - 0.5, 0); - me.setSelected(me, min(me.selectedItem, floor((me.scrollPos + 1) / me.itemHeight - 1))); + me.setSelected(me, min(me.selectedItem, me.getLastFullyVisibleItemAtScrollPos(me, me.scrollPos))); } else if(key == K_MWHEELDOWN) { - me.scrollPos = min(me.scrollPos + 0.5, me.nItems * me.itemHeight - 1); - me.setSelected(me, max(me.selectedItem, ceil(me.scrollPos / me.itemHeight))); + me.scrollPos = min(me.scrollPos + 0.5, me.getTotalHeight(me) - 1); + me.setSelected(me, max(me.selectedItem, me.getFirstFullyVisibleItemAtScrollPos(me, me.scrollPos))); } else if(key == K_PGUP || key == K_KP_PGUP) - me.setSelected(me, me.selectedItem - 1 / me.itemHeight); + { + me.scrollPos = max(me.scrollPos - 1, 0); + me.setSelected(me, min(me.selectedItem, me.getLastFullyVisibleItemAtScrollPos(me, me.scrollPos))); + } else if(key == K_PGDN || key == K_KP_PGDN) - me.setSelected(me, me.selectedItem + 1 / me.itemHeight); + { + me.scrollPos = min(me.scrollPos + 1, me.getTotalHeight(me) - 1); + me.setSelected(me, max(me.selectedItem, me.getFirstFullyVisibleItemAtScrollPos(me, me.scrollPos))); + } else if(key == K_UPARROW || key == K_KP_UPARROW) me.setSelected(me, me.selectedItem - 1); else if(key == K_DOWNARROW || key == K_KP_DOWNARROW) @@ -84,7 +125,7 @@ float ListBox_keyDown(entity me, float key, float ascii, float shift) } else if(key == K_END || key == K_KP_END) { - me.scrollPos = max(0, me.nItems * me.itemHeight - 1); + me.scrollPos = max(0, me.getTotalHeight(me) - 1); me.setSelected(me, me.nItems - 1); } else @@ -108,20 +149,20 @@ float ListBox_mouseDrag(entity me, vector pos) { // calculate new pos to v float d; - d = (pos_y - me.pressOffset) / (1 - (me.controlBottom - me.controlTop)) * (me.nItems * me.itemHeight - 1); + d = (pos_y - me.pressOffset) / (1 - (me.controlBottom - me.controlTop)) * (me.getTotalHeight(me) - 1); me.scrollPos = me.previousValue + d; } else me.scrollPos = me.previousValue; - me.scrollPos = min(me.scrollPos, me.nItems * me.itemHeight - 1); + me.scrollPos = min(me.scrollPos, me.getTotalHeight(me) - 1); me.scrollPos = max(me.scrollPos, 0); - i = min(me.selectedItem, floor((me.scrollPos + 1) / me.itemHeight - 1)); - i = max(i, ceil(me.scrollPos / me.itemHeight)); + i = min(me.selectedItem, me.getLastFullyVisibleItemAtScrollPos(me, me.scrollPos)); + i = max(i, ListBox_getFirstFullyVisibleItemAtScrollPos(me, me.scrollPos)); me.setSelected(me, i); } else if(me.pressed == 2) { - me.setSelected(me, floor((me.scrollPos + pos_y) / me.itemHeight)); + me.setSelected(me, me.getItemAtPos(me, me.scrollPos + pos_y)); } return 1; } @@ -141,13 +182,13 @@ float ListBox_mousePress(entity me, vector pos) { // page up me.scrollPos = max(me.scrollPos - 1, 0); - me.setSelected(me, min(me.selectedItem, floor((me.scrollPos + 1) / me.itemHeight - 1))); + me.setSelected(me, min(me.selectedItem, ListBox_getLastFullyVisibleItemAtScrollPos(me, me.scrollPos))); } else if(pos_y > me.controlBottom) { // page down - me.scrollPos = min(me.scrollPos + 1, me.nItems * me.itemHeight - 1); - me.setSelected(me, max(me.selectedItem, ceil(me.scrollPos / me.itemHeight))); + me.scrollPos = min(me.scrollPos + 1, me.getTotalHeight(me) - 1); + me.setSelected(me, max(me.selectedItem, ListBox_getFirstFullyVisibleItemAtScrollPos(me, me.scrollPos))); } else { @@ -161,7 +202,7 @@ float ListBox_mousePress(entity me, vector pos) // continue doing that while dragging (even when dragging outside). When releasing, forward the click to the then selected item. me.pressed = 2; // an item has been clicked. Select it, ... - me.setSelected(me, floor((me.scrollPos + pos_y) / me.itemHeight)); + me.setSelected(me, me.getItemAtPos(me, me.scrollPos + pos_y)); } return 1; } @@ -177,11 +218,11 @@ float ListBox_mouseRelease(entity me, vector pos) me.pressed = 3; // do that here, so setSelected can know the mouse has been released // item dragging mode // select current one one last time... - me.setSelected(me, floor((me.scrollPos + pos_y) / me.itemHeight)); + me.setSelected(me, me.getItemAtPos(me, me.scrollPos + pos_y)); // and give it a nice click event if(me.nItems > 0) { - me.clickListBoxItem(me, me.selectedItem, globalToBox(pos, eY * (me.selectedItem * me.itemHeight - me.scrollPos), eX * (1 - me.controlWidth) + eY * me.itemHeight)); + me.clickListBoxItem(me, me.selectedItem, globalToBox(pos, eY * (me.getItemStart(me, me.selectedItem) - me.scrollPos), eX * (1 - me.controlWidth) + eY * me.getItemHeight(me, me.selectedItem))); } } me.pressed = 0; @@ -198,7 +239,7 @@ void ListBox_updateControlTopBottom(entity me) { float f; // scrollPos is in 0..1 and indicates where the "page" currently shown starts. - if(me.nItems * me.itemHeight <= 1) + if(me.getTotalHeight(me) <= 1) { // we don't need no stinkin' scrollbar, we don't need no view control... me.controlTop = 0; @@ -214,20 +255,20 @@ void ListBox_updateControlTopBottom(entity me) float save; save = me.scrollPos; // if selected item is below listbox, increase scrollpos so it is in - me.scrollPos = max(me.scrollPos, me.selectedItem * me.itemHeight - 1 + me.itemHeight); + me.scrollPos = max(me.scrollPos, me.getItemStart(me, me.selectedItem) + me.getItemHeight(me, me.selectedItem) - 1); // if selected item is above listbox, decrease scrollpos so it is in - me.scrollPos = min(me.scrollPos, me.selectedItem * me.itemHeight); + me.scrollPos = min(me.scrollPos, me.getItemStart(me, me.selectedItem)); if(me.scrollPos != save) me.dragScrollTimer = time + 0.2; } } // if scroll pos is below end of list, fix it - me.scrollPos = min(me.scrollPos, me.nItems * me.itemHeight - 1); + me.scrollPos = min(me.scrollPos, me.getTotalHeight(me) - 1); // if scroll pos is above beginning of list, fix it me.scrollPos = max(me.scrollPos, 0); // now that we know where the list is scrolled to, find out where to draw the control - me.controlTop = max(0, me.scrollPos / (me.nItems * me.itemHeight)); - me.controlBottom = min((me.scrollPos + 1) / (me.nItems * me.itemHeight), 1); + me.controlTop = max(0, me.scrollPos / me.getTotalHeight(me)); + me.controlBottom = min((me.scrollPos + 1) / me.getTotalHeight(me), 1); float minfactor; minfactor = 1 * me.controlWidth / me.size_y * me.size_x; @@ -258,7 +299,7 @@ void ListBox_draw(entity me) if(me.controlWidth) { draw_VertButtonPicture(eX * (1 - me.controlWidth), strcat(me.src, "_s"), eX * me.controlWidth + eY, me.color2, 1); - if(me.nItems * me.itemHeight > 1) + if(me.getTotalHeight(me) > 1) { vector o, s; o = eX * (1 - me.controlWidth) + eY * me.controlTop; @@ -274,16 +315,17 @@ void ListBox_draw(entity me) draw_SetClip(); oldshift = draw_shift; oldscale = draw_scale; - absSize = boxToGlobalSize(me.size, eX * (1 - me.controlWidth) + eY * me.itemHeight); - draw_scale = boxToGlobalSize(eX * (1 - me.controlWidth) + eY * me.itemHeight, oldscale); - for(i = floor(me.scrollPos / me.itemHeight); i < me.nItems; ++i) + float y; + i = me.getItemAtPos(me, me.scrollPos); + y = me.getItemStart(me, i) - me.scrollPos; + for(; i < me.nItems && y < 1; ++i) { - float y; - y = i * me.itemHeight - me.scrollPos; - if(y >= 1) - break; draw_shift = boxToGlobal(eY * y, oldshift, oldscale); + vector relSize = eX * (1 - me.controlWidth) + eY * me.getItemHeight(me, i); + absSize = boxToGlobalSize(relSize, me.size); + draw_scale = boxToGlobalSize(relSize, oldscale); me.drawListBoxItem(me, i, absSize, (me.selectedItem == i)); + y += absSize_y; } draw_ClearClip(); -- 2.39.2