ATTRIB(ListBox, focusable, float, 1)
ATTRIB(ListBox, focusedItem, int, -1)
ATTRIB(ListBox, focusedItemAlpha, float, 0.3)
- ATTRIB(ListBox, focusedItemPos, vector, '0 0 0')
+ ATTRIB(ListBox, mouseMoveOffset, float, -1) // let know where the cursor is when the list scrolls without moving the cursor
ATTRIB(ListBox, allowFocusSound, float, 1)
ATTRIB(ListBox, selectedItem, int, 0)
ATTRIB(ListBox, size, vector, '0 0 0')
ATTRIB(ListBox, origin, vector, '0 0 0')
ATTRIB(ListBox, scrollPos, float, 0) // measured in window heights, fixed when needed
ATTRIB(ListBox, scrollPosTarget, float, 0)
+ ATTRIB(ListBox, needScrollToItem, float, -1)
+ METHOD(ListBox, scrollToItem, void(entity, int))
ATTRIB(ListBox, previousValue, float, 0)
ATTRIB(ListBox, pressed, float, 0) // 0 = normal, 1 = scrollbar dragging, 2 = item dragging, 3 = released
ATTRIB(ListBox, pressOffset, float, 0)
ATTRIB(ListBox, controlBottom, float, 0)
ATTRIB(ListBox, controlWidth, float, 0)
ATTRIB(ListBox, dragScrollPos, vector, '0 0 0')
+ ATTRIB(ListBox, selectionDoesntMatter, bool, false) // improves scrolling by keys for lists that don't need to show an active selection
ATTRIB(ListBox, src, string, string_null) // scrollbar
ATTRIB(ListBox, color, vector, '1 1 1')
#endif
#ifdef IMPLEMENTATION
-void ListBox_setSelected(entity me, float i)
+void ListBox_scrollToItem(entity me, int i)
{
+ // scroll doesn't work properly until itemHeight is set to the correct value
+ // at the first resizeNotify call
+ if(me.itemHeight == 1) // initial temporary value of itemHeight is 1
+ {
+ me.needScrollToItem = i;
+ return;
+ }
+
i = bound(0, i, me.nItems - 1);
// scroll the list to make sure the selected item is visible
if(i == me.nItems - 1)
me.scrollPosTarget = me.getTotalHeight(me) - 1;
else
- {
me.scrollPosTarget = me.getItemStart(me, i + 1) - 1;
- }
}
+}
+
+void ListBox_setSelected(entity me, float i)
+{
+ i = bound(0, i, me.nItems - 1);
+ me.scrollToItem(me, i);
me.selectedItem = i;
}
void ListBox_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
}
else if(key == K_PGUP || key == K_KP_PGUP)
{
+ if(me.selectionDoesntMatter)
+ {
+ me.scrollPosTarget = max(me.scrollPosTarget - 0.5, 0);
+ return 1;
+ }
+
float i = me.selectedItem;
float a = me.getItemHeight(me, i);
for (;;)
}
else if(key == K_PGDN || key == K_KP_PGDN)
{
+ if(me.selectionDoesntMatter)
+ {
+ me.scrollPosTarget = min(me.scrollPosTarget + 0.5, me.nItems * me.itemHeight - 1);
+ return 1;
+ }
+
float i = me.selectedItem;
float a = me.getItemHeight(me, i);
for (;;)
me.setSelected(me, i - 1);
}
else if(key == K_UPARROW || key == K_KP_UPARROW)
+ {
+ if(me.selectionDoesntMatter)
+ {
+ me.scrollPosTarget = max(me.scrollPosTarget - me.itemHeight, 0);
+ return 1;
+ }
+
me.setSelected(me, me.selectedItem - 1);
+ }
else if(key == K_DOWNARROW || key == K_KP_DOWNARROW)
+ {
+ if(me.selectionDoesntMatter)
+ {
+ me.scrollPosTarget = min(me.scrollPosTarget + me.itemHeight, me.nItems * me.itemHeight - 1);
+ return 1;
+ }
+
me.setSelected(me, me.selectedItem + 1);
+ }
else if(key == K_HOME || key == K_KP_HOME)
me.setSelected(me, 0);
else if(key == K_END || key == K_KP_END)
}
float ListBox_mouseMove(entity me, vector pos)
{
- float focusedItem_save = me.focusedItem;
- me.focusedItem = -1;
+ me.mouseMoveOffset = -1;
if(pos_x < 0) return 0;
if(pos_y < 0) return 0;
if(pos_x >= 1) return 0;
if(pos_y >= 1) return 0;
if(pos_x < 1 - me.controlWidth)
+ me.mouseMoveOffset = pos.y;
+ else
{
- me.focusedItem = me.getItemAtPos(me, me.scrollPos + pos.y);
- me.focusedItemPos = eY * pos.y;
- if(focusedItem_save != me.focusedItem)
- me.focusedItemAlpha = SKINALPHA_LISTBOX_FOCUSED;
+ me.focusedItem = -1;
+ me.mouseMoveOffset = -1;
}
return 1;
}
}
else if(me.pressed == 2)
{
- me.focusedItem = -1;
me.setSelected(me, me.getItemAtPos(me, me.scrollPos + pos.y));
+ me.focusedItem = me.selectedItem;
+ me.mouseMoveOffset = -1;
}
return 1;
}
me.pressed = 2;
// an item has been clicked. Select it, ...
me.setSelected(me, me.getItemAtPos(me, me.scrollPos + pos.y));
+ me.focusedItem = me.selectedItem;
}
return 1;
}
// item dragging mode
// select current one one last time...
me.setSelected(me, me.getItemAtPos(me, me.scrollPos + pos.y));
+ me.focusedItem = me.selectedItem;
// and give it a nice click event
if(me.nItems > 0)
{
// for example showing a dialog on right click
me.pressed = 0;
me.focusedItem = -1;
+ me.mouseMoveOffset = -1;
}
void ListBox_updateControlTopBottom(entity me)
{
vector absSize, fillSize = '0 0 0';
vector oldshift, oldscale;
+ // we can't do this in mouseMove as the list can scroll without moving the cursor
+ float focusedItem_save = me.focusedItem;
+ if(me.mouseMoveOffset != -1)
+ me.focusedItem = me.getItemAtPos(me, me.scrollPos + me.mouseMoveOffset);
+ if(me.focusedItem >= 0)
+ if(focusedItem_save != me.focusedItem)
+ me.focusedItemAlpha = SKINALPHA_LISTBOX_FOCUSED;
+
+ if(me.needScrollToItem >= 0)
+ {
+ me.scrollToItem(me, me.needScrollToItem);
+ me.needScrollToItem = -1;
+ }
if(me.scrollPos != me.scrollPosTarget)
{
float PI = 3.1415926535897932384626433832795028841971693993751058209749445923;
- // this formula is guaranted to work with whatever framerate
- float f = sin(PI / 2 * pow(frametime, 0.65));
+ float exp_factor = 0.65;
+ if(me.pressed == 1)
+ exp_factor = 0.45; // scroll faster while dragging the scrollbar
+ // this formula works with whatever framerate
+ float f = sin(PI / 2 * pow(frametime, exp_factor));
me.scrollPos = me.scrollPos * (1 - f) + me.scrollPosTarget * f;
if(fabs(me.scrollPos - me.scrollPosTarget) < 0.001)
me.scrollPos = me.scrollPosTarget;
-
- // update focusedItem while scrolling
- if(me.focusedItem >= 0)
- me.mouseMove(me, me.focusedItemPos);
}
if(me.pressed == 2)