summaryrefslogtreecommitdiffstats
path: root/gui
diff options
context:
space:
mode:
Diffstat (limited to 'gui')
-rw-r--r--gui/objects.hpp9
-rw-r--r--gui/scrolllist.cpp107
2 files changed, 63 insertions, 53 deletions
diff --git a/gui/objects.hpp b/gui/objects.hpp
index ceb2c6c0c..00770b378 100644
--- a/gui/objects.hpp
+++ b/gui/objects.hpp
@@ -589,13 +589,18 @@ protected:
int mSeparatorH; // Height of the separator between items
COLOR mSeparatorColor; // color of the separator that is between items
- // Scrolling and dynamic state
+ // Scrollbar
int mFastScrollW; // width of the fastscroll area
int mFastScrollLineW; // width of the line for fastscroll rendering
int mFastScrollRectW; // width of the rectangle for fastscroll
- int mFastScrollRectH; // height of the rectangle for fastscroll
+ int mFastScrollRectH; // minimum height of the rectangle for fastscroll
COLOR mFastScrollLineColor;
COLOR mFastScrollRectColor;
+
+ // Scrolling and dynamic state
+ int mFastScrollRectCurrentY; // current top of fastscroll rect relative to list top
+ int mFastScrollRectCurrentH; // current height of fastscroll rect
+ int mFastScrollRectTouchY; // offset from top of fastscroll rect where the user initially touched
bool hasScroll; // indicates that we have enough items in the list to scroll
int firstDisplayedItem; // this item goes at the top of the display list - may only be partially visible
int scrollingSpeed; // on a touch release, this is set based on the difference in the y-axis between the last 2 touches and indicates how fast the kinetic scrolling will go
diff --git a/gui/scrolllist.cpp b/gui/scrolllist.cpp
index 4b772d45d..70a54e5ac 100644
--- a/gui/scrolllist.cpp
+++ b/gui/scrolllist.cpp
@@ -44,6 +44,7 @@ GUIScrollList::GUIScrollList(xml_node<>* node) : GUIObject(node)
mFont = NULL;
mBackgroundW = mBackgroundH = 0;
mFastScrollW = mFastScrollLineW = mFastScrollRectW = mFastScrollRectH = 0;
+ mFastScrollRectCurrentY = mFastScrollRectCurrentH = mFastScrollRectTouchY = 0;
lastY = last2Y = fastScroll = 0;
mUpdate = 0;
touchDebounce = 6;
@@ -284,7 +285,7 @@ int GUIScrollList::Render(void)
yPos += actualItemHeight;
}
- // Render the Header (last so that it overwrites the top most row for per pixel scrolling)
+ // Render the Header
yPos = mRenderY;
if (mHeaderH > 0) {
// First step, fill background
@@ -310,35 +311,41 @@ int GUIScrollList::Render(void)
gr_fill(mRenderX, yPos + mHeaderH - mHeaderSeparatorH, mRenderW, mHeaderSeparatorH);
}
+ // reset clipping
+ gr_noclip();
+
// render fast scroll
- lines = GetDisplayItemCount();
if (hasScroll) {
- int startX = listW + mRenderX;
int fWidth = mRenderW - listW;
int fHeight = mRenderH - mHeaderH;
+ int centerX = listW + mRenderX + fWidth / 2;
- // line
- gr_color(mFastScrollLineColor.red, mFastScrollLineColor.green, mFastScrollLineColor.blue, mFastScrollLineColor.alpha);
- gr_fill(startX + fWidth/2, mRenderY + mHeaderH, mFastScrollLineW, mRenderH - mHeaderH);
+ // first determine the total list height and where we are in the list
+ int totalHeight = GetItemCount() * actualItemHeight; // total height of the full list in pixels
+ int topPos = firstDisplayedItem * actualItemHeight - y_offset;
- // rect
- int pct = 0;
- if (GetDisplayRemainder() != 0) {
- // Properly handle the percentage if a partial line is present
- int partial_line_size = actualItemHeight - GetDisplayRemainder();
- pct = ((firstDisplayedItem*actualItemHeight - y_offset)*100)/(listSize*actualItemHeight-((lines + 1)*actualItemHeight) + partial_line_size);
- } else {
- pct = ((firstDisplayedItem*actualItemHeight - y_offset)*100)/(listSize*actualItemHeight-lines*actualItemHeight);
- }
- int mFastScrollRectX = startX + (fWidth - mFastScrollRectW)/2;
- int mFastScrollRectY = mRenderY+mHeaderH + ((fHeight - mFastScrollRectH)*pct)/100;
+ // now scale it proportionally to the scrollbar height
+ int boxH = fHeight * fHeight / totalHeight; // proportional height of the displayed portion
+ boxH = std::max(boxH, mFastScrollRectH); // but keep a minimum height
+ int boxY = (fHeight - boxH) * topPos / (totalHeight - fHeight); // pixels relative to top of list
+ int boxW = mFastScrollRectW;
+
+ int x = centerX - boxW / 2;
+ int y = mRenderY + mHeaderH + boxY;
+
+ // line above and below box (needs to be split because box can be transparent)
+ gr_color(mFastScrollLineColor.red, mFastScrollLineColor.green, mFastScrollLineColor.blue, mFastScrollLineColor.alpha);
+ gr_fill(centerX - mFastScrollLineW / 2, mRenderY + mHeaderH, mFastScrollLineW, boxY);
+ gr_fill(centerX - mFastScrollLineW / 2, y + boxH, mFastScrollLineW, fHeight - boxY - boxH);
+ // box
gr_color(mFastScrollRectColor.red, mFastScrollRectColor.green, mFastScrollRectColor.blue, mFastScrollRectColor.alpha);
- gr_fill(mFastScrollRectX, mFastScrollRectY, mFastScrollRectW, mFastScrollRectH);
+ gr_fill(x, y, boxW, boxH);
+
+ mFastScrollRectCurrentY = boxY;
+ mFastScrollRectCurrentH = boxH;
}
mUpdate = 0;
- // reset clipping
- gr_noclip();
return 0;
}
@@ -416,8 +423,20 @@ int GUIScrollList::NotifyTouch(TOUCH_STATE state, int x, int y)
switch (state)
{
case TOUCH_START:
- if (hasScroll && x >= mRenderX + mRenderW - mFastScrollW)
+ if (hasScroll && x >= mRenderX + mRenderW - mFastScrollW) {
fastScroll = 1; // Initial touch is in the fast scroll region
+ int fastScrollBoxTop = mFastScrollRectCurrentY + mRenderY + mHeaderH;
+ int fastScrollBoxBottom = fastScrollBoxTop + mFastScrollRectCurrentH;
+ if (y >= fastScrollBoxTop && y < fastScrollBoxBottom)
+ // user grabbed the fastscroll bar
+ // try to keep the initially touched part of the scrollbar under the finger
+ mFastScrollRectTouchY = y - fastScrollBoxTop;
+ else
+ // user tapped outside the fastscroll bar
+ // center fastscroll rect on the initial touch position
+ mFastScrollRectTouchY = mFastScrollRectCurrentH / 2;
+ }
+
if (scrollingSpeed != 0) {
selectedItem = NO_ITEM; // this allows the user to tap the list to stop the scrolling without selecting the item they tap
scrollingSpeed = 0; // stop scrolling on a new touch
@@ -433,36 +452,20 @@ int GUIScrollList::NotifyTouch(TOUCH_STATE state, int x, int y)
case TOUCH_DRAG:
if (fastScroll)
{
- int pct = ((y-mRenderY-mHeaderH)*100)/(mRenderH-mHeaderH);
- int totalSize = GetItemCount();
- int lines = GetDisplayItemCount();
-
- float l = float((totalSize-lines)*pct)/100;
- if(l + lines >= totalSize)
- {
- firstDisplayedItem = totalSize - lines;
- if (GetDisplayRemainder() != 0) {
- // There's a partial row displayed, set the scrolling offset so that the last item really is at the very bottom
- firstDisplayedItem--;
- y_offset = GetDisplayRemainder() - actualItemHeight;
- } else {
- // There's no partial row so zero out the offset
- y_offset = 0;
- }
- }
- else
- {
- if (l < 0)
- l = 0;
- firstDisplayedItem = l;
- y_offset = -(l - int(l))*actualItemHeight;
- if (GetDisplayRemainder() != 0) {
- // There's a partial row displayed, make sure y_offset doesn't go past the max
- if (firstDisplayedItem == totalSize - lines - 1 && y_offset < GetDisplayRemainder() - actualItemHeight)
- y_offset = GetDisplayRemainder() - actualItemHeight;
- } else if (firstDisplayedItem == totalSize - lines)
- y_offset = 0;
- }
+ int relY = y - mRenderY - mHeaderH; // touch position relative to window
+ int windowH = mRenderH - mHeaderH;
+ int totalHeight = GetItemCount() * actualItemHeight; // total height of the full list in pixels
+
+ // calculate new top position of the fastscroll bar relative to window
+ int newY = relY - mFastScrollRectTouchY;
+ // keep it fully inside the list
+ newY = std::min(std::max(newY, 0), windowH - mFastScrollRectCurrentH);
+
+ // now compute the new scroll position for the list
+ int newTopPos = newY * (totalHeight - windowH) / (windowH - mFastScrollRectCurrentH); // new top pixel of list
+ newTopPos = std::min(newTopPos, totalHeight - windowH); // account for rounding errors
+ firstDisplayedItem = newTopPos / actualItemHeight;
+ y_offset = - newTopPos % actualItemHeight;
selectedItem = NO_ITEM;
mUpdate = 1;
@@ -490,6 +493,8 @@ int GUIScrollList::NotifyTouch(TOUCH_STATE state, int x, int y)
break;
case TOUCH_RELEASE:
+ if (fastScroll)
+ mUpdate = 1; // get rid of touch effects on the fastscroll bar
fastScroll = 0;
if (selectedItem != NO_ITEM) {
// We've selected an item!