blob: 8d9ab42f2624ae041dbee35129c61da0c18428ed [file] [log] [blame]
Ethan Yonker0a3a98f2015-02-05 00:48:28 +01001/*
2 Copyright 2013 bigbiff/Dees_Troy TeamWin
3 This file is part of TWRP/TeamWin Recovery Project.
4
5 TWRP is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 TWRP is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with TWRP. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19#include <string.h>
20
21extern "C" {
22#include "../twcommon.h"
23#include "../minuitwrp/minui.h"
24}
25
26#include "rapidxml.hpp"
27#include "objects.hpp"
28#include "../data.hpp"
29
that10ec0172015-02-15 23:52:28 +010030const float SCROLLING_SPEED_DECREMENT = 0.9; // friction
31const int SCROLLING_FLOOR = 2; // minimum pixels for scrolling to stop
Ethan Yonker0a3a98f2015-02-05 00:48:28 +010032const float SCROLLING_SPEED_LIMIT = 2.5; // maximum number of items to scroll per update
33
34GUIScrollList::GUIScrollList(xml_node<>* node) : GUIObject(node)
35{
36 xml_attribute<>* attr;
37 xml_node<>* child;
Ethan Yonker0a3a98f2015-02-05 00:48:28 +010038
39 firstDisplayedItem = mItemSpacing = mFontHeight = mSeparatorH = y_offset = scrollingSpeed = 0;
40 maxIconWidth = maxIconHeight = mHeaderIconHeight = mHeaderIconWidth = 0;
that9876ac32015-02-15 21:40:59 +010041 mHeaderSeparatorH = mHeaderH = actualItemHeight = 0;
42 mHeaderIsStatic = false;
thatf6ed8fc2015-02-14 20:23:16 +010043 mBackground = mHeaderIcon = NULL;
44 mFont = NULL;
Ethan Yonker0a3a98f2015-02-05 00:48:28 +010045 mBackgroundW = mBackgroundH = 0;
46 mFastScrollW = mFastScrollLineW = mFastScrollRectW = mFastScrollRectH = 0;
47 lastY = last2Y = fastScroll = 0;
48 mUpdate = 0;
49 touchDebounce = 6;
50 ConvertStrToColor("black", &mBackgroundColor);
51 ConvertStrToColor("black", &mHeaderBackgroundColor);
52 ConvertStrToColor("black", &mSeparatorColor);
53 ConvertStrToColor("black", &mHeaderSeparatorColor);
54 ConvertStrToColor("white", &mFontColor);
55 ConvertStrToColor("white", &mHeaderFontColor);
56 ConvertStrToColor("white", &mFastScrollLineColor);
57 ConvertStrToColor("white", &mFastScrollRectColor);
58 hasHighlightColor = false;
Ethan Yonker0a3a98f2015-02-05 00:48:28 +010059 selectedItem = NO_ITEM;
60
61 // Load header text
Ethan Yonker0a3a98f2015-02-05 00:48:28 +010062 child = node->first_node("text");
63 if (child) mHeaderText = child->value();
that9876ac32015-02-15 21:40:59 +010064 // Simple way to check for static state
65 mLastHeaderValue = gui_parse_text(mHeaderText);
66 mHeaderIsStatic = (mLastHeaderValue == mHeaderText);
Ethan Yonker0a3a98f2015-02-05 00:48:28 +010067
68 memset(&mHighlightColor, 0, sizeof(COLOR));
69 child = node->first_node("highlight");
70 if (child) {
71 attr = child->first_attribute("color");
72 if (attr) {
73 hasHighlightColor = true;
74 std::string color = attr->value();
75 ConvertStrToColor(color, &mHighlightColor);
76 }
77 }
78
Ethan Yonker0a3a98f2015-02-05 00:48:28 +010079 child = node->first_node("background");
80 if (child)
81 {
thatf6ed8fc2015-02-14 20:23:16 +010082 mBackground = LoadAttrImage(child, "resource");
that9876ac32015-02-15 21:40:59 +010083 mBackgroundColor = LoadAttrColor(child, "color");
Ethan Yonker0a3a98f2015-02-05 00:48:28 +010084 }
85
86 // Load the placement
87 LoadPlacement(node->first_node("placement"), &mRenderX, &mRenderY, &mRenderW, &mRenderH);
88 SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
89
90 // Load the font, and possibly override the color
91 child = node->first_node("font");
92 if (child)
93 {
thatf6ed8fc2015-02-14 20:23:16 +010094 mFont = LoadAttrFont(child, "resource");
that9876ac32015-02-15 21:40:59 +010095 mFontColor = LoadAttrColor(child, "color");
96 mFontHighlightColor = LoadAttrColor(child, "highlightcolor", mFontColor);
97 mItemSpacing = LoadAttrIntScaleY(child, "spacing");
Ethan Yonker0a3a98f2015-02-05 00:48:28 +010098 }
99
100 // Load the separator if it exists
101 child = node->first_node("separator");
102 if (child)
103 {
that9876ac32015-02-15 21:40:59 +0100104 mSeparatorColor = LoadAttrColor(child, "color");
105 mSeparatorH = LoadAttrIntScaleY(child, "height");
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100106 }
107
that9876ac32015-02-15 21:40:59 +0100108 // Fast scroll
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100109 child = node->first_node("fastscroll");
110 if (child)
111 {
that9876ac32015-02-15 21:40:59 +0100112 mFastScrollLineColor = LoadAttrColor(child, "linecolor");
113 mFastScrollRectColor = LoadAttrColor(child, "rectcolor");
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100114
that9876ac32015-02-15 21:40:59 +0100115 mFastScrollW = LoadAttrIntScaleX(child, "w");
116 mFastScrollLineW = LoadAttrIntScaleX(child, "linew");
117 mFastScrollRectW = LoadAttrIntScaleX(child, "rectw");
118 mFastScrollRectH = LoadAttrIntScaleY(child, "recth");
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100119 }
120
121 // Retrieve the line height
thatf6ed8fc2015-02-14 20:23:16 +0100122 mFontHeight = mFont->GetHeight();
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100123 actualItemHeight = mFontHeight + mItemSpacing + mSeparatorH;
that9876ac32015-02-15 21:40:59 +0100124
125 // Load the header if it exists
126 child = node->first_node("header");
127 if (child)
128 {
129 mHeaderH = mFontHeight;
130 mHeaderIcon = LoadAttrImage(child, "icon");
131 mHeaderBackgroundColor = LoadAttrColor(child, "background", mBackgroundColor);
132 mHeaderFontColor = LoadAttrColor(child, "textcolor", mFontColor);
133 mHeaderSeparatorColor = LoadAttrColor(child, "separatorcolor", mSeparatorColor);
134 mHeaderSeparatorH = LoadAttrIntScaleY(child, "separatorheight", mSeparatorH);
135
136 if (mHeaderIcon && mHeaderIcon->GetResource())
137 {
138 mHeaderIconWidth = mHeaderIcon->GetWidth();
139 mHeaderIconHeight = mHeaderIcon->GetHeight();
140 if (mHeaderIconHeight > mHeaderH)
141 mHeaderH = mHeaderIconHeight;
142 if (mHeaderIconWidth > maxIconWidth)
143 maxIconWidth = mHeaderIconWidth;
144 }
145
146 mHeaderH += mItemSpacing + mHeaderSeparatorH;
147 if (mHeaderH < actualItemHeight)
148 mHeaderH = actualItemHeight;
149 }
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100150
151 if (actualItemHeight / 3 > 6)
152 touchDebounce = actualItemHeight / 3;
153
154 if (mBackground && mBackground->GetResource())
155 {
thatf6ed8fc2015-02-14 20:23:16 +0100156 mBackgroundW = mBackground->GetWidth();
157 mBackgroundH = mBackground->GetHeight();
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100158 }
159}
160
161GUIScrollList::~GUIScrollList()
162{
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100163}
164
165void GUIScrollList::SetMaxIconSize(int w, int h)
166{
167 if (w > maxIconWidth)
168 maxIconWidth = w;
169 if (h > maxIconHeight)
170 maxIconHeight = h;
171 if (maxIconHeight > mFontHeight) {
172 actualItemHeight = maxIconHeight + mItemSpacing + mSeparatorH;
that9876ac32015-02-15 21:40:59 +0100173 if (mHeaderH > 0 && actualItemHeight > mHeaderH)
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100174 mHeaderH = actualItemHeight;
175 }
176}
177
178void GUIScrollList::SetVisibleListLocation(size_t list_index)
179{
180 // This will make sure that the item indicated by list_index is visible on the screen
181 size_t lines = GetDisplayItemCount(), listSize = GetItemCount();
182
183 if (list_index <= (unsigned)firstDisplayedItem) {
184 // list_index is above the currently displayed items, put the selected item at the very top
185 firstDisplayedItem = list_index;
186 y_offset = 0;
187 } else if (list_index >= firstDisplayedItem + lines) {
188 // list_index is below the currently displayed items, put the selected item at the very bottom
189 firstDisplayedItem = list_index - lines + 1;
190 if (GetDisplayRemainder() != 0) {
191 // There's a partial row displayed, set the scrolling offset so that the selected item really is at the very bottom
192 firstDisplayedItem--;
193 y_offset = GetDisplayRemainder() - actualItemHeight;
194 } else {
195 // There's no partial row so zero out the offset
196 y_offset = 0;
197 }
198 }
199 scrollingSpeed = 0; // stop kinetic scrolling on setting visible location
200 mUpdate = 1;
201}
202
203int GUIScrollList::Render(void)
204{
205 if(!isConditionTrue())
206 return 0;
207
208 // First step, fill background
that9876ac32015-02-15 21:40:59 +0100209 gr_color(mBackgroundColor.red, mBackgroundColor.green, mBackgroundColor.blue, mBackgroundColor.alpha);
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100210 gr_fill(mRenderX, mRenderY + mHeaderH, mRenderW, mRenderH - mHeaderH);
211
that9876ac32015-02-15 21:40:59 +0100212 // don't paint outside of the box
213 gr_clip(mRenderX, mRenderY, mRenderW, mRenderH);
214
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100215 // Next, render the background resource (if it exists)
216 if (mBackground && mBackground->GetResource())
217 {
218 int mBackgroundX = mRenderX + ((mRenderW - mBackgroundW) / 2);
219 int mBackgroundY = mRenderY + ((mRenderH - mBackgroundH) / 2);
220 gr_blit(mBackground->GetResource(), 0, 0, mBackgroundW, mBackgroundH, mBackgroundX, mBackgroundY);
221 }
222
that9876ac32015-02-15 21:40:59 +0100223 // This tells us how many full lines we can actually render
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100224 size_t lines = GetDisplayItemCount();
225
226 size_t listSize = GetItemCount();
227 int listW = mRenderW;
228
229 if (listSize <= lines) {
230 hasScroll = false;
231 scrollingSpeed = 0;
232 lines = listSize;
233 y_offset = 0;
234 } else {
235 hasScroll = true;
236 listW -= mFastScrollW; // space for fast scroll
237 lines++;
238 if (lines < listSize)
239 lines++;
240 }
241
242 void* fontResource = NULL;
243 if (mFont) fontResource = mFont->GetResource();
244
245 int yPos = mRenderY + mHeaderH + y_offset;
246 int fontOffsetY = (int)((actualItemHeight - mFontHeight) / 2);
247
248 // render all visible items
249 for (size_t line = 0; line < lines; line++)
250 {
251 size_t itemindex = line + firstDisplayedItem;
252 if (itemindex >= listSize)
253 break;
254
255 // get item data
thatf6ed8fc2015-02-14 20:23:16 +0100256 ImageResource* icon;
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100257 std::string label;
258 if (GetListItem(itemindex, icon, label))
259 break;
260
261 if (hasHighlightColor && itemindex == selectedItem) {
262 // Highlight the item background of the selected item
that9876ac32015-02-15 21:40:59 +0100263 gr_color(mHighlightColor.red, mHighlightColor.green, mHighlightColor.blue, mHighlightColor.alpha);
264 gr_fill(mRenderX, yPos, mRenderW, actualItemHeight);
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100265 }
266
that9876ac32015-02-15 21:40:59 +0100267 if (itemindex == selectedItem) {
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100268 // Use the highlight color for the font
that9876ac32015-02-15 21:40:59 +0100269 gr_color(mFontHighlightColor.red, mFontHighlightColor.green, mFontHighlightColor.blue, mFontHighlightColor.alpha);
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100270 } else {
271 // Set the color for the font
that9876ac32015-02-15 21:40:59 +0100272 gr_color(mFontColor.red, mFontColor.green, mFontColor.blue, mFontColor.alpha);
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100273 }
274
that9876ac32015-02-15 21:40:59 +0100275 // render icon
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100276 if (icon && icon->GetResource()) {
thatf6ed8fc2015-02-14 20:23:16 +0100277 int currentIconHeight = icon->GetHeight();
278 int currentIconWidth = icon->GetWidth();
that9876ac32015-02-15 21:40:59 +0100279 int currentIconOffsetY = (actualItemHeight - currentIconHeight) / 2;
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100280 int currentIconOffsetX = (maxIconWidth - currentIconWidth) / 2;
that9876ac32015-02-15 21:40:59 +0100281 int image_y = (yPos + currentIconOffsetY);
282 gr_blit(icon->GetResource(), 0, 0, currentIconWidth, currentIconHeight, mRenderX + currentIconOffsetX, image_y);
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100283 }
284
that9876ac32015-02-15 21:40:59 +0100285 // render label text
286 gr_textEx(mRenderX + maxIconWidth + 5, yPos + fontOffsetY, label.c_str(), fontResource);
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100287
288 // Add the separator
that9876ac32015-02-15 21:40:59 +0100289 gr_color(mSeparatorColor.red, mSeparatorColor.green, mSeparatorColor.blue, mSeparatorColor.alpha);
290 gr_fill(mRenderX, yPos + actualItemHeight - mSeparatorH, listW, mSeparatorH);
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100291
292 // Move the yPos
293 yPos += actualItemHeight;
294 }
295
296 // Render the Header (last so that it overwrites the top most row for per pixel scrolling)
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100297 yPos = mRenderY;
that9876ac32015-02-15 21:40:59 +0100298 if (mHeaderH > 0) {
299 // First step, fill background
300 gr_color(mHeaderBackgroundColor.red, mHeaderBackgroundColor.green, mHeaderBackgroundColor.blue, mHeaderBackgroundColor.alpha);
301 gr_fill(mRenderX, mRenderY, mRenderW, mHeaderH);
302
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100303 int mIconOffsetX = 0;
304
305 // render the icon if it exists
thatf6ed8fc2015-02-14 20:23:16 +0100306 ImageResource* headerIcon = mHeaderIcon;
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100307 if (headerIcon && headerIcon->GetResource())
308 {
309 gr_blit(headerIcon->GetResource(), 0, 0, mHeaderIconWidth, mHeaderIconHeight, mRenderX + ((mHeaderIconWidth - maxIconWidth) / 2), (yPos + (int)((mHeaderH - mHeaderIconHeight) / 2)));
310 mIconOffsetX = maxIconWidth;
311 }
312
313 // render the text
that9876ac32015-02-15 21:40:59 +0100314 gr_color(mHeaderFontColor.red, mHeaderFontColor.green, mHeaderFontColor.blue, mHeaderFontColor.alpha);
315 gr_textEx(mRenderX + mIconOffsetX + 5, yPos + (int)((mHeaderH - mFontHeight) / 2), mLastHeaderValue.c_str(), fontResource);
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100316
317 // Add the separator
that9876ac32015-02-15 21:40:59 +0100318 gr_color(mHeaderSeparatorColor.red, mHeaderSeparatorColor.green, mHeaderSeparatorColor.blue, mHeaderSeparatorColor.alpha);
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100319 gr_fill(mRenderX, yPos + mHeaderH - mHeaderSeparatorH, mRenderW, mHeaderSeparatorH);
320 }
321
322 // render fast scroll
323 lines = GetDisplayItemCount();
324 if (hasScroll) {
325 int startX = listW + mRenderX;
326 int fWidth = mRenderW - listW;
327 int fHeight = mRenderH - mHeaderH;
328
329 // line
that9876ac32015-02-15 21:40:59 +0100330 gr_color(mFastScrollLineColor.red, mFastScrollLineColor.green, mFastScrollLineColor.blue, mFastScrollLineColor.alpha);
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100331 gr_fill(startX + fWidth/2, mRenderY + mHeaderH, mFastScrollLineW, mRenderH - mHeaderH);
332
333 // rect
334 int pct = 0;
335 if (GetDisplayRemainder() != 0) {
336 // Properly handle the percentage if a partial line is present
337 int partial_line_size = actualItemHeight - GetDisplayRemainder();
338 pct = ((firstDisplayedItem*actualItemHeight - y_offset)*100)/(listSize*actualItemHeight-((lines + 1)*actualItemHeight) + partial_line_size);
339 } else {
340 pct = ((firstDisplayedItem*actualItemHeight - y_offset)*100)/(listSize*actualItemHeight-lines*actualItemHeight);
341 }
342 int mFastScrollRectX = startX + (fWidth - mFastScrollRectW)/2;
343 int mFastScrollRectY = mRenderY+mHeaderH + ((fHeight - mFastScrollRectH)*pct)/100;
344
that9876ac32015-02-15 21:40:59 +0100345 gr_color(mFastScrollRectColor.red, mFastScrollRectColor.green, mFastScrollRectColor.blue, mFastScrollRectColor.alpha);
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100346 gr_fill(mFastScrollRectX, mFastScrollRectY, mFastScrollRectW, mFastScrollRectH);
347 }
348 mUpdate = 0;
that9876ac32015-02-15 21:40:59 +0100349 // reset clipping
350 gr_noclip();
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100351 return 0;
352}
353
354int GUIScrollList::Update(void)
355{
356 if(!isConditionTrue())
357 return 0;
358
359 if (!mHeaderIsStatic) {
360 std::string newValue = gui_parse_text(mHeaderText);
361 if (mLastHeaderValue != newValue) {
362 mLastHeaderValue = newValue;
363 mUpdate = 1;
364 }
365 }
366
367 // Handle kinetic scrolling
368 int maxScrollDistance = actualItemHeight * SCROLLING_SPEED_LIMIT;
that10ec0172015-02-15 23:52:28 +0100369 int oldScrollingSpeed = scrollingSpeed;
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100370 if (scrollingSpeed == 0) {
371 // Do nothing
372 return 0;
373 } else if (scrollingSpeed > 0) {
374 if (scrollingSpeed < maxScrollDistance)
375 y_offset += scrollingSpeed;
376 else
377 y_offset += maxScrollDistance;
that10ec0172015-02-15 23:52:28 +0100378 scrollingSpeed *= SCROLLING_SPEED_DECREMENT;
379 if (scrollingSpeed == oldScrollingSpeed)
380 --scrollingSpeed;
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100381 } else if (scrollingSpeed < 0) {
382 if (abs(scrollingSpeed) < maxScrollDistance)
383 y_offset += scrollingSpeed;
384 else
385 y_offset -= maxScrollDistance;
that10ec0172015-02-15 23:52:28 +0100386 scrollingSpeed *= SCROLLING_SPEED_DECREMENT;
387 if (scrollingSpeed == oldScrollingSpeed)
388 ++scrollingSpeed;
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100389 }
390 if (abs(scrollingSpeed) < SCROLLING_FLOOR)
391 scrollingSpeed = 0;
392 HandleScrolling();
393 mUpdate = 1;
394
395 return 0;
396}
397
398size_t GUIScrollList::HitTestItem(int x, int y)
399{
400 // We only care about y position
401 if (y < mRenderY || y - mRenderY <= mHeaderH || y - mRenderY > mRenderH)
402 return NO_ITEM;
403
404 int startSelection = (y - mRenderY - mHeaderH);
405
406 // Locate the correct item
407 size_t actualSelection = firstDisplayedItem;
408 int selectY = y_offset;
409 while (selectY + actualItemHeight < startSelection) {
410 selectY += actualItemHeight;
411 actualSelection++;
412 }
413
414 if (actualSelection < GetItemCount())
415 return actualSelection;
416
417 return NO_ITEM;
418}
419
420int GUIScrollList::NotifyTouch(TOUCH_STATE state, int x, int y)
421{
422 if(!isConditionTrue())
423 return -1;
424
425 switch (state)
426 {
427 case TOUCH_START:
428 if (hasScroll && x >= mRenderX + mRenderW - mFastScrollW)
429 fastScroll = 1; // Initial touch is in the fast scroll region
430 if (scrollingSpeed != 0) {
431 selectedItem = NO_ITEM; // this allows the user to tap the list to stop the scrolling without selecting the item they tap
432 scrollingSpeed = 0; // stop scrolling on a new touch
433 } else if (!fastScroll) {
434 // find out which item the user touched
435 selectedItem = HitTestItem(x, y);
436 }
437 if (selectedItem != NO_ITEM)
438 mUpdate = 1;
439 lastY = last2Y = y;
440 break;
441
442 case TOUCH_DRAG:
443 if (fastScroll)
444 {
445 int pct = ((y-mRenderY-mHeaderH)*100)/(mRenderH-mHeaderH);
446 int totalSize = GetItemCount();
447 int lines = GetDisplayItemCount();
448
449 float l = float((totalSize-lines)*pct)/100;
450 if(l + lines >= totalSize)
451 {
452 firstDisplayedItem = totalSize - lines;
453 if (GetDisplayRemainder() != 0) {
454 // There's a partial row displayed, set the scrolling offset so that the last item really is at the very bottom
455 firstDisplayedItem--;
456 y_offset = GetDisplayRemainder() - actualItemHeight;
457 } else {
458 // There's no partial row so zero out the offset
459 y_offset = 0;
460 }
461 }
462 else
463 {
464 if (l < 0)
465 l = 0;
466 firstDisplayedItem = l;
467 y_offset = -(l - int(l))*actualItemHeight;
468 if (GetDisplayRemainder() != 0) {
469 // There's a partial row displayed, make sure y_offset doesn't go past the max
470 if (firstDisplayedItem == totalSize - lines - 1 && y_offset < GetDisplayRemainder() - actualItemHeight)
471 y_offset = GetDisplayRemainder() - actualItemHeight;
472 } else if (firstDisplayedItem == totalSize - lines)
473 y_offset = 0;
474 }
475
476 selectedItem = NO_ITEM;
477 mUpdate = 1;
478 scrollingSpeed = 0; // prevent kinetic scrolling when using fast scroll
479 break;
480 }
481
482 // Provide some debounce on initial touches
483 if (selectedItem != NO_ITEM && abs(y - lastY) < touchDebounce) {
484 mUpdate = 1;
485 break;
486 }
487
488 selectedItem = NO_ITEM; // nothing is selected because we dragged too far
489 // Handle scrolling
490 if (hasScroll) {
491 y_offset += y - lastY; // adjust the scrolling offset based on the difference between the starting touch and the current touch
492 last2Y = lastY; // keep track of previous y locations so that we can tell how fast to scroll for kinetic scrolling
493 lastY = y; // update last touch to the current touch so we can tell how far and what direction we scroll for the next touch event
494
495 HandleScrolling();
496 } else
497 y_offset = 0;
498 mUpdate = 1;
499 break;
500
501 case TOUCH_RELEASE:
502 fastScroll = 0;
503 if (selectedItem != NO_ITEM) {
504 // We've selected an item!
505 NotifySelect(selectedItem);
506 mUpdate = 1;
507
508 DataManager::Vibrate("tw_button_vibrate");
509 selectedItem = NO_ITEM;
510 } else {
511 // Start kinetic scrolling
512 scrollingSpeed = lastY - last2Y;
that10ec0172015-02-15 23:52:28 +0100513 if (abs(scrollingSpeed) < touchDebounce)
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100514 scrollingSpeed = 0;
515 }
516 case TOUCH_REPEAT:
517 case TOUCH_HOLD:
518 break;
519 }
520 return 0;
521}
522
523void GUIScrollList::HandleScrolling()
524{
525 // handle dragging downward, scrolling upward
526 // the offset should always be <= 0 and > -actualItemHeight, adjust the first display row and offset as needed
527 while(firstDisplayedItem && y_offset > 0) {
528 firstDisplayedItem--;
529 y_offset -= actualItemHeight;
530 }
thatde72b6d2015-02-08 08:55:00 +0100531 if (firstDisplayedItem == 0 && y_offset > 0) {
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100532 y_offset = 0; // user kept dragging downward past the top of the list, so always reset the offset to 0 since we can't scroll any further in this direction
thatde72b6d2015-02-08 08:55:00 +0100533 scrollingSpeed = 0; // stop kinetic scrolling
534 }
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100535
536 // handle dragging upward, scrolling downward
537 int totalSize = GetItemCount();
538 int lines = GetDisplayItemCount(); // number of full lines our list can display at once
539 int bottom_offset = GetDisplayRemainder() - actualItemHeight; // extra display area that can display a partial line for per pixel scrolling
540
541 // the offset should always be <= 0 and > -actualItemHeight, adjust the first display row and offset as needed
542 while (firstDisplayedItem + lines + (bottom_offset ? 1 : 0) < totalSize && abs(y_offset) > actualItemHeight) {
543 firstDisplayedItem++;
544 y_offset += actualItemHeight;
545 }
546 // Check if we dragged too far, set the list at the bottom and adjust offset as needed
547 if (bottom_offset != 0 && firstDisplayedItem + lines + 1 >= totalSize && y_offset <= bottom_offset) {
548 firstDisplayedItem = totalSize - lines - 1;
549 y_offset = bottom_offset;
thatde72b6d2015-02-08 08:55:00 +0100550 scrollingSpeed = 0; // stop kinetic scrolling
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100551 } else if (firstDisplayedItem + lines >= totalSize && y_offset < 0) {
552 firstDisplayedItem = totalSize - lines;
553 y_offset = 0;
thatde72b6d2015-02-08 08:55:00 +0100554 scrollingSpeed = 0; // stop kinetic scrolling
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100555 }
556}
557
558int GUIScrollList::GetDisplayItemCount()
559{
560 return (mRenderH - mHeaderH) / (actualItemHeight);
561}
562
563int GUIScrollList::GetDisplayRemainder()
564{
565 return (mRenderH - mHeaderH) % actualItemHeight;
566}
567
568int GUIScrollList::NotifyVarChange(const std::string& varName, const std::string& value)
569{
570 GUIObject::NotifyVarChange(varName, value);
571
572 if(!isConditionTrue())
573 return 0;
574
575 if (!mHeaderIsStatic) {
576 std::string newValue = gui_parse_text(mHeaderText);
577 if (mLastHeaderValue != newValue) {
578 mLastHeaderValue = newValue;
579 firstDisplayedItem = 0;
580 y_offset = 0;
581 scrollingSpeed = 0; // stop kinetic scrolling on variable changes
582 mUpdate = 1;
583 }
584 }
585 return 0;
586}
587
588int GUIScrollList::SetRenderPos(int x, int y, int w /* = 0 */, int h /* = 0 */)
589{
590 mRenderX = x;
591 mRenderY = y;
592 if (w || h)
593 {
594 mRenderW = w;
595 mRenderH = h;
596 }
597 SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
598 mUpdate = 1;
599 return 0;
600}
601
602void GUIScrollList::SetPageFocus(int inFocus)
603{
604 if (inFocus) {
605 NotifyVarChange("", ""); // This forces a check for the header text
606 scrollingSpeed = 0; // stop kinetic scrolling on page changes
607 mUpdate = 1;
608 }
609}