blob: eb66de8e5c9e510cb20c13e338b09c9853792bd7 [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
thatae4b12e2015-02-06 00:23:05 +010030const int SCROLLING_SPEED_DECREMENT = 6; // friction
Ethan Yonker0a3a98f2015-02-05 00:48:28 +010031const int SCROLLING_FLOOR = 10; // minimum pixels for scrolling to start or stop
thatae4b12e2015-02-06 00:23:05 +010032const int SCROLLING_MULTIPLIER = 1; // initial speed of kinetic scrolling
Ethan Yonker0a3a98f2015-02-05 00:48:28 +010033const float SCROLLING_SPEED_LIMIT = 2.5; // maximum number of items to scroll per update
34
35GUIScrollList::GUIScrollList(xml_node<>* node) : GUIObject(node)
36{
37 xml_attribute<>* attr;
38 xml_node<>* child;
39 int header_separator_color_specified = 0, header_separator_height_specified = 0, header_text_color_specified = 0, header_background_color_specified = 0;
40
41 firstDisplayedItem = mItemSpacing = mFontHeight = mSeparatorH = y_offset = scrollingSpeed = 0;
42 maxIconWidth = maxIconHeight = mHeaderIconHeight = mHeaderIconWidth = 0;
43 mHeaderSeparatorH = mHeaderIsStatic = mHeaderH = actualItemHeight = 0;
44 mBackground = mFont = mHeaderIcon = NULL;
45 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;
59 hasFontHighlightColor = false;
60 selectedItem = NO_ITEM;
61
62 // Load header text
63 child = node->first_node("header");
64 if (child)
65 {
66 attr = child->first_attribute("icon");
67 if (attr)
68 mHeaderIcon = PageManager::FindResource(attr->value());
69
70 attr = child->first_attribute("background");
71 if (attr)
72 {
73 std::string color = attr->value();
74 ConvertStrToColor(color, &mHeaderBackgroundColor);
75 header_background_color_specified = -1;
76 }
77 attr = child->first_attribute("textcolor");
78 if (attr)
79 {
80 std::string color = attr->value();
81 ConvertStrToColor(color, &mHeaderFontColor);
82 header_text_color_specified = -1;
83 }
84 attr = child->first_attribute("separatorcolor");
85 if (attr)
86 {
87 std::string color = attr->value();
88 ConvertStrToColor(color, &mHeaderSeparatorColor);
89 header_separator_color_specified = -1;
90 }
91 attr = child->first_attribute("separatorheight");
92 if (attr) {
93 string parsevalue = gui_parse_text(attr->value());
Ethan Yonker63e414f2015-02-06 15:44:39 -060094 mHeaderSeparatorH = scale_theme_y(atoi(parsevalue.c_str()));
Ethan Yonker0a3a98f2015-02-05 00:48:28 +010095 header_separator_height_specified = -1;
96 }
97 }
98 child = node->first_node("text");
99 if (child) mHeaderText = child->value();
100
101 memset(&mHighlightColor, 0, sizeof(COLOR));
102 child = node->first_node("highlight");
103 if (child) {
104 attr = child->first_attribute("color");
105 if (attr) {
106 hasHighlightColor = true;
107 std::string color = attr->value();
108 ConvertStrToColor(color, &mHighlightColor);
109 }
110 }
111
112 // Simple way to check for static state
113 mLastHeaderValue = gui_parse_text(mHeaderText);
114 if (mLastHeaderValue != mHeaderText)
115 mHeaderIsStatic = 0;
116 else
117 mHeaderIsStatic = -1;
118
119 child = node->first_node("background");
120 if (child)
121 {
122 attr = child->first_attribute("resource");
123 if (attr)
124 mBackground = PageManager::FindResource(attr->value());
125 attr = child->first_attribute("color");
126 if (attr)
127 {
128 std::string color = attr->value();
129 ConvertStrToColor(color, &mBackgroundColor);
130 if (!header_background_color_specified)
131 ConvertStrToColor(color, &mHeaderBackgroundColor);
132 }
133 }
134
135 // Load the placement
136 LoadPlacement(node->first_node("placement"), &mRenderX, &mRenderY, &mRenderW, &mRenderH);
137 SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
138
139 // Load the font, and possibly override the color
140 child = node->first_node("font");
141 if (child)
142 {
143 attr = child->first_attribute("resource");
144 if (attr)
145 mFont = PageManager::FindResource(attr->value());
146
147 attr = child->first_attribute("color");
148 if (attr)
149 {
150 std::string color = attr->value();
151 ConvertStrToColor(color, &mFontColor);
152 if (!header_text_color_specified)
153 ConvertStrToColor(color, &mHeaderFontColor);
154 }
155
156 attr = child->first_attribute("spacing");
157 if (attr) {
158 string parsevalue = gui_parse_text(attr->value());
Ethan Yonker63e414f2015-02-06 15:44:39 -0600159 mItemSpacing = scale_theme_y(atoi(parsevalue.c_str()));
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100160 }
161
162 attr = child->first_attribute("highlightcolor");
163 memset(&mFontHighlightColor, 0, sizeof(COLOR));
164 if (attr)
165 {
166 std::string color = attr->value();
167 ConvertStrToColor(color, &mFontHighlightColor);
168 hasFontHighlightColor = true;
169 }
170 }
171
172 // Load the separator if it exists
173 child = node->first_node("separator");
174 if (child)
175 {
176 attr = child->first_attribute("color");
177 if (attr)
178 {
179 std::string color = attr->value();
180 ConvertStrToColor(color, &mSeparatorColor);
181 if (!header_separator_color_specified)
182 ConvertStrToColor(color, &mHeaderSeparatorColor);
183 }
184
185 attr = child->first_attribute("height");
186 if (attr) {
187 string parsevalue = gui_parse_text(attr->value());
Ethan Yonker63e414f2015-02-06 15:44:39 -0600188 mSeparatorH = scale_theme_y(atoi(parsevalue.c_str()));
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100189 if (!header_separator_height_specified)
190 mHeaderSeparatorH = mSeparatorH;
191 }
192 }
193
194 // Fast scroll colors
195 child = node->first_node("fastscroll");
196 if (child)
197 {
198 attr = child->first_attribute("linecolor");
199 if(attr)
200 ConvertStrToColor(attr->value(), &mFastScrollLineColor);
201
202 attr = child->first_attribute("rectcolor");
203 if(attr)
204 ConvertStrToColor(attr->value(), &mFastScrollRectColor);
205
206 attr = child->first_attribute("w");
207 if (attr) {
208 string parsevalue = gui_parse_text(attr->value());
Ethan Yonker63e414f2015-02-06 15:44:39 -0600209 mFastScrollW = scale_theme_x(atoi(parsevalue.c_str()));
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100210 }
211
212 attr = child->first_attribute("linew");
213 if (attr) {
214 string parsevalue = gui_parse_text(attr->value());
Ethan Yonker63e414f2015-02-06 15:44:39 -0600215 mFastScrollLineW = scale_theme_x(atoi(parsevalue.c_str()));
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100216 }
217
218 attr = child->first_attribute("rectw");
219 if (attr) {
220 string parsevalue = gui_parse_text(attr->value());
Ethan Yonker63e414f2015-02-06 15:44:39 -0600221 mFastScrollRectW = scale_theme_x(atoi(parsevalue.c_str()));
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100222 }
223
224 attr = child->first_attribute("recth");
225 if (attr) {
226 string parsevalue = gui_parse_text(attr->value());
Ethan Yonker63e414f2015-02-06 15:44:39 -0600227 mFastScrollRectH = scale_theme_y(atoi(parsevalue.c_str()));
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100228 }
229 }
230
231 // Retrieve the line height
232 mFontHeight = gr_getMaxFontHeight(mFont ? mFont->GetResource() : NULL);
233 mHeaderH = mFontHeight;
234
235 if (mHeaderIcon && mHeaderIcon->GetResource())
236 {
237 mHeaderIconWidth = gr_get_width(mHeaderIcon->GetResource());
238 mHeaderIconHeight = gr_get_height(mHeaderIcon->GetResource());
239 if (mHeaderIconHeight > mHeaderH)
240 mHeaderH = mHeaderIconHeight;
241 if (mHeaderIconWidth > maxIconWidth)
242 maxIconWidth = mHeaderIconWidth;
243 }
244
245 mHeaderH += mItemSpacing + mHeaderSeparatorH;
246 actualItemHeight = mFontHeight + mItemSpacing + mSeparatorH;
247 if (mHeaderH < actualItemHeight)
248 mHeaderH = actualItemHeight;
249
250 if (actualItemHeight / 3 > 6)
251 touchDebounce = actualItemHeight / 3;
252
253 if (mBackground && mBackground->GetResource())
254 {
255 mBackgroundW = gr_get_width(mBackground->GetResource());
256 mBackgroundH = gr_get_height(mBackground->GetResource());
257 }
258}
259
260GUIScrollList::~GUIScrollList()
261{
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100262}
263
264void GUIScrollList::SetMaxIconSize(int w, int h)
265{
266 if (w > maxIconWidth)
267 maxIconWidth = w;
268 if (h > maxIconHeight)
269 maxIconHeight = h;
270 if (maxIconHeight > mFontHeight) {
271 actualItemHeight = maxIconHeight + mItemSpacing + mSeparatorH;
272 if (actualItemHeight > mHeaderH)
273 mHeaderH = actualItemHeight;
274 }
275}
276
277void GUIScrollList::SetVisibleListLocation(size_t list_index)
278{
279 // This will make sure that the item indicated by list_index is visible on the screen
280 size_t lines = GetDisplayItemCount(), listSize = GetItemCount();
281
282 if (list_index <= (unsigned)firstDisplayedItem) {
283 // list_index is above the currently displayed items, put the selected item at the very top
284 firstDisplayedItem = list_index;
285 y_offset = 0;
286 } else if (list_index >= firstDisplayedItem + lines) {
287 // list_index is below the currently displayed items, put the selected item at the very bottom
288 firstDisplayedItem = list_index - lines + 1;
289 if (GetDisplayRemainder() != 0) {
290 // There's a partial row displayed, set the scrolling offset so that the selected item really is at the very bottom
291 firstDisplayedItem--;
292 y_offset = GetDisplayRemainder() - actualItemHeight;
293 } else {
294 // There's no partial row so zero out the offset
295 y_offset = 0;
296 }
297 }
298 scrollingSpeed = 0; // stop kinetic scrolling on setting visible location
299 mUpdate = 1;
300}
301
302int GUIScrollList::Render(void)
303{
304 if(!isConditionTrue())
305 return 0;
306
307 // First step, fill background
308 gr_color(mBackgroundColor.red, mBackgroundColor.green, mBackgroundColor.blue, 255);
309 gr_fill(mRenderX, mRenderY + mHeaderH, mRenderW, mRenderH - mHeaderH);
310
311 // Next, render the background resource (if it exists)
312 if (mBackground && mBackground->GetResource())
313 {
314 int mBackgroundX = mRenderX + ((mRenderW - mBackgroundW) / 2);
315 int mBackgroundY = mRenderY + ((mRenderH - mBackgroundH) / 2);
316 gr_blit(mBackground->GetResource(), 0, 0, mBackgroundW, mBackgroundH, mBackgroundX, mBackgroundY);
317 }
318
319 // This tells us how many lines we can actually render
320 size_t lines = GetDisplayItemCount();
321
322 size_t listSize = GetItemCount();
323 int listW = mRenderW;
324
325 if (listSize <= lines) {
326 hasScroll = false;
327 scrollingSpeed = 0;
328 lines = listSize;
329 y_offset = 0;
330 } else {
331 hasScroll = true;
332 listW -= mFastScrollW; // space for fast scroll
333 lines++;
334 if (lines < listSize)
335 lines++;
336 }
337
338 void* fontResource = NULL;
339 if (mFont) fontResource = mFont->GetResource();
340
341 int yPos = mRenderY + mHeaderH + y_offset;
342 int fontOffsetY = (int)((actualItemHeight - mFontHeight) / 2);
343
344 // render all visible items
345 for (size_t line = 0; line < lines; line++)
346 {
347 size_t itemindex = line + firstDisplayedItem;
348 if (itemindex >= listSize)
349 break;
350
351 // get item data
352 Resource* icon;
353 std::string label;
354 if (GetListItem(itemindex, icon, label))
355 break;
356
357 if (hasHighlightColor && itemindex == selectedItem) {
358 // Highlight the item background of the selected item
359 gr_color(mHighlightColor.red, mHighlightColor.green, mHighlightColor.blue, 255);
360 int HighlightHeight = actualItemHeight;
361 if (yPos + HighlightHeight > mRenderY + mRenderH) {
362 HighlightHeight = mRenderY + mRenderH - yPos;
363 }
364 gr_fill(mRenderX, yPos, mRenderW, HighlightHeight);
365 }
366
367 if (hasFontHighlightColor && itemindex == selectedItem) {
368 // Use the highlight color for the font
369 gr_color(mFontHighlightColor.red, mFontHighlightColor.green, mFontHighlightColor.blue, 255);
370 } else {
371 // Set the color for the font
372 gr_color(mFontColor.red, mFontColor.green, mFontColor.blue, 255);
373 }
374
375 if (icon && icon->GetResource()) {
376 int currentIconHeight = gr_get_height(icon->GetResource());
377 int currentIconWidth = gr_get_width(icon->GetResource());
378 int currentIconOffsetY = (int)((actualItemHeight - currentIconHeight) / 2);
379 int currentIconOffsetX = (maxIconWidth - currentIconWidth) / 2;
380 int rect_y = 0, image_y = (yPos + currentIconOffsetY);
381 if (image_y + currentIconHeight > mRenderY + mRenderH)
382 rect_y = mRenderY + mRenderH - image_y;
383 else
384 rect_y = currentIconHeight;
385 gr_blit(icon->GetResource(), 0, 0, currentIconWidth, rect_y, mRenderX + currentIconOffsetX, image_y);
386 }
387
388 gr_textExWH(mRenderX + maxIconWidth + 5, yPos + fontOffsetY, label.c_str(), fontResource, mRenderX + listW, mRenderY + mRenderH);
389
390 // Add the separator
391 if (yPos + actualItemHeight < mRenderH + mRenderY) {
392 gr_color(mSeparatorColor.red, mSeparatorColor.green, mSeparatorColor.blue, 255);
393 gr_fill(mRenderX, yPos + actualItemHeight - mSeparatorH, listW, mSeparatorH);
394 }
395
396 // Move the yPos
397 yPos += actualItemHeight;
398 }
399
400 // Render the Header (last so that it overwrites the top most row for per pixel scrolling)
401 // First step, fill background
402 gr_color(mHeaderBackgroundColor.red, mHeaderBackgroundColor.green, mHeaderBackgroundColor.blue, 255);
403 gr_fill(mRenderX, mRenderY, mRenderW, mHeaderH);
404
405 // Now, we need the header (icon + text)
406 yPos = mRenderY;
407 {
408 Resource* headerIcon;
409 int mIconOffsetX = 0;
410
411 // render the icon if it exists
412 headerIcon = mHeaderIcon;
413 if (headerIcon && headerIcon->GetResource())
414 {
415 gr_blit(headerIcon->GetResource(), 0, 0, mHeaderIconWidth, mHeaderIconHeight, mRenderX + ((mHeaderIconWidth - maxIconWidth) / 2), (yPos + (int)((mHeaderH - mHeaderIconHeight) / 2)));
416 mIconOffsetX = maxIconWidth;
417 }
418
419 // render the text
420 gr_color(mHeaderFontColor.red, mHeaderFontColor.green, mHeaderFontColor.blue, 255);
421 gr_textExWH(mRenderX + mIconOffsetX + 5, yPos + (int)((mHeaderH - mFontHeight) / 2), mLastHeaderValue.c_str(), fontResource, mRenderX + mRenderW, mRenderY + mRenderH);
422
423 // Add the separator
424 gr_color(mHeaderSeparatorColor.red, mHeaderSeparatorColor.green, mHeaderSeparatorColor.blue, 255);
425 gr_fill(mRenderX, yPos + mHeaderH - mHeaderSeparatorH, mRenderW, mHeaderSeparatorH);
426 }
427
428 // render fast scroll
429 lines = GetDisplayItemCount();
430 if (hasScroll) {
431 int startX = listW + mRenderX;
432 int fWidth = mRenderW - listW;
433 int fHeight = mRenderH - mHeaderH;
434
435 // line
436 gr_color(mFastScrollLineColor.red, mFastScrollLineColor.green, mFastScrollLineColor.blue, 255);
437 gr_fill(startX + fWidth/2, mRenderY + mHeaderH, mFastScrollLineW, mRenderH - mHeaderH);
438
439 // rect
440 int pct = 0;
441 if (GetDisplayRemainder() != 0) {
442 // Properly handle the percentage if a partial line is present
443 int partial_line_size = actualItemHeight - GetDisplayRemainder();
444 pct = ((firstDisplayedItem*actualItemHeight - y_offset)*100)/(listSize*actualItemHeight-((lines + 1)*actualItemHeight) + partial_line_size);
445 } else {
446 pct = ((firstDisplayedItem*actualItemHeight - y_offset)*100)/(listSize*actualItemHeight-lines*actualItemHeight);
447 }
448 int mFastScrollRectX = startX + (fWidth - mFastScrollRectW)/2;
449 int mFastScrollRectY = mRenderY+mHeaderH + ((fHeight - mFastScrollRectH)*pct)/100;
450
451 gr_color(mFastScrollRectColor.red, mFastScrollRectColor.green, mFastScrollRectColor.blue, 255);
452 gr_fill(mFastScrollRectX, mFastScrollRectY, mFastScrollRectW, mFastScrollRectH);
453 }
454 mUpdate = 0;
455 return 0;
456}
457
458int GUIScrollList::Update(void)
459{
460 if(!isConditionTrue())
461 return 0;
462
463 if (!mHeaderIsStatic) {
464 std::string newValue = gui_parse_text(mHeaderText);
465 if (mLastHeaderValue != newValue) {
466 mLastHeaderValue = newValue;
467 mUpdate = 1;
468 }
469 }
470
471 // Handle kinetic scrolling
472 int maxScrollDistance = actualItemHeight * SCROLLING_SPEED_LIMIT;
473 if (scrollingSpeed == 0) {
474 // Do nothing
475 return 0;
476 } else if (scrollingSpeed > 0) {
477 if (scrollingSpeed < maxScrollDistance)
478 y_offset += scrollingSpeed;
479 else
480 y_offset += maxScrollDistance;
481 scrollingSpeed -= SCROLLING_SPEED_DECREMENT;
482 } else if (scrollingSpeed < 0) {
483 if (abs(scrollingSpeed) < maxScrollDistance)
484 y_offset += scrollingSpeed;
485 else
486 y_offset -= maxScrollDistance;
487 scrollingSpeed += SCROLLING_SPEED_DECREMENT;
488 }
489 if (abs(scrollingSpeed) < SCROLLING_FLOOR)
490 scrollingSpeed = 0;
491 HandleScrolling();
492 mUpdate = 1;
493
494 return 0;
495}
496
497size_t GUIScrollList::HitTestItem(int x, int y)
498{
499 // We only care about y position
500 if (y < mRenderY || y - mRenderY <= mHeaderH || y - mRenderY > mRenderH)
501 return NO_ITEM;
502
503 int startSelection = (y - mRenderY - mHeaderH);
504
505 // Locate the correct item
506 size_t actualSelection = firstDisplayedItem;
507 int selectY = y_offset;
508 while (selectY + actualItemHeight < startSelection) {
509 selectY += actualItemHeight;
510 actualSelection++;
511 }
512
513 if (actualSelection < GetItemCount())
514 return actualSelection;
515
516 return NO_ITEM;
517}
518
519int GUIScrollList::NotifyTouch(TOUCH_STATE state, int x, int y)
520{
521 if(!isConditionTrue())
522 return -1;
523
524 switch (state)
525 {
526 case TOUCH_START:
527 if (hasScroll && x >= mRenderX + mRenderW - mFastScrollW)
528 fastScroll = 1; // Initial touch is in the fast scroll region
529 if (scrollingSpeed != 0) {
530 selectedItem = NO_ITEM; // this allows the user to tap the list to stop the scrolling without selecting the item they tap
531 scrollingSpeed = 0; // stop scrolling on a new touch
532 } else if (!fastScroll) {
533 // find out which item the user touched
534 selectedItem = HitTestItem(x, y);
535 }
536 if (selectedItem != NO_ITEM)
537 mUpdate = 1;
538 lastY = last2Y = y;
539 break;
540
541 case TOUCH_DRAG:
542 if (fastScroll)
543 {
544 int pct = ((y-mRenderY-mHeaderH)*100)/(mRenderH-mHeaderH);
545 int totalSize = GetItemCount();
546 int lines = GetDisplayItemCount();
547
548 float l = float((totalSize-lines)*pct)/100;
549 if(l + lines >= totalSize)
550 {
551 firstDisplayedItem = totalSize - lines;
552 if (GetDisplayRemainder() != 0) {
553 // There's a partial row displayed, set the scrolling offset so that the last item really is at the very bottom
554 firstDisplayedItem--;
555 y_offset = GetDisplayRemainder() - actualItemHeight;
556 } else {
557 // There's no partial row so zero out the offset
558 y_offset = 0;
559 }
560 }
561 else
562 {
563 if (l < 0)
564 l = 0;
565 firstDisplayedItem = l;
566 y_offset = -(l - int(l))*actualItemHeight;
567 if (GetDisplayRemainder() != 0) {
568 // There's a partial row displayed, make sure y_offset doesn't go past the max
569 if (firstDisplayedItem == totalSize - lines - 1 && y_offset < GetDisplayRemainder() - actualItemHeight)
570 y_offset = GetDisplayRemainder() - actualItemHeight;
571 } else if (firstDisplayedItem == totalSize - lines)
572 y_offset = 0;
573 }
574
575 selectedItem = NO_ITEM;
576 mUpdate = 1;
577 scrollingSpeed = 0; // prevent kinetic scrolling when using fast scroll
578 break;
579 }
580
581 // Provide some debounce on initial touches
582 if (selectedItem != NO_ITEM && abs(y - lastY) < touchDebounce) {
583 mUpdate = 1;
584 break;
585 }
586
587 selectedItem = NO_ITEM; // nothing is selected because we dragged too far
588 // Handle scrolling
589 if (hasScroll) {
590 y_offset += y - lastY; // adjust the scrolling offset based on the difference between the starting touch and the current touch
591 last2Y = lastY; // keep track of previous y locations so that we can tell how fast to scroll for kinetic scrolling
592 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
593
594 HandleScrolling();
595 } else
596 y_offset = 0;
597 mUpdate = 1;
598 break;
599
600 case TOUCH_RELEASE:
601 fastScroll = 0;
602 if (selectedItem != NO_ITEM) {
603 // We've selected an item!
604 NotifySelect(selectedItem);
605 mUpdate = 1;
606
607 DataManager::Vibrate("tw_button_vibrate");
608 selectedItem = NO_ITEM;
609 } else {
610 // Start kinetic scrolling
611 scrollingSpeed = lastY - last2Y;
612 if (abs(scrollingSpeed) > SCROLLING_FLOOR)
613 scrollingSpeed *= SCROLLING_MULTIPLIER;
614 else
615 scrollingSpeed = 0;
616 }
617 case TOUCH_REPEAT:
618 case TOUCH_HOLD:
619 break;
620 }
621 return 0;
622}
623
624void GUIScrollList::HandleScrolling()
625{
626 // handle dragging downward, scrolling upward
627 // the offset should always be <= 0 and > -actualItemHeight, adjust the first display row and offset as needed
628 while(firstDisplayedItem && y_offset > 0) {
629 firstDisplayedItem--;
630 y_offset -= actualItemHeight;
631 }
thatde72b6d2015-02-08 08:55:00 +0100632 if (firstDisplayedItem == 0 && y_offset > 0) {
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100633 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 +0100634 scrollingSpeed = 0; // stop kinetic scrolling
635 }
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100636
637 // handle dragging upward, scrolling downward
638 int totalSize = GetItemCount();
639 int lines = GetDisplayItemCount(); // number of full lines our list can display at once
640 int bottom_offset = GetDisplayRemainder() - actualItemHeight; // extra display area that can display a partial line for per pixel scrolling
641
642 // the offset should always be <= 0 and > -actualItemHeight, adjust the first display row and offset as needed
643 while (firstDisplayedItem + lines + (bottom_offset ? 1 : 0) < totalSize && abs(y_offset) > actualItemHeight) {
644 firstDisplayedItem++;
645 y_offset += actualItemHeight;
646 }
647 // Check if we dragged too far, set the list at the bottom and adjust offset as needed
648 if (bottom_offset != 0 && firstDisplayedItem + lines + 1 >= totalSize && y_offset <= bottom_offset) {
649 firstDisplayedItem = totalSize - lines - 1;
650 y_offset = bottom_offset;
thatde72b6d2015-02-08 08:55:00 +0100651 scrollingSpeed = 0; // stop kinetic scrolling
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100652 } else if (firstDisplayedItem + lines >= totalSize && y_offset < 0) {
653 firstDisplayedItem = totalSize - lines;
654 y_offset = 0;
thatde72b6d2015-02-08 08:55:00 +0100655 scrollingSpeed = 0; // stop kinetic scrolling
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100656 }
657}
658
659int GUIScrollList::GetDisplayItemCount()
660{
661 return (mRenderH - mHeaderH) / (actualItemHeight);
662}
663
664int GUIScrollList::GetDisplayRemainder()
665{
666 return (mRenderH - mHeaderH) % actualItemHeight;
667}
668
669int GUIScrollList::NotifyVarChange(const std::string& varName, const std::string& value)
670{
671 GUIObject::NotifyVarChange(varName, value);
672
673 if(!isConditionTrue())
674 return 0;
675
676 if (!mHeaderIsStatic) {
677 std::string newValue = gui_parse_text(mHeaderText);
678 if (mLastHeaderValue != newValue) {
679 mLastHeaderValue = newValue;
680 firstDisplayedItem = 0;
681 y_offset = 0;
682 scrollingSpeed = 0; // stop kinetic scrolling on variable changes
683 mUpdate = 1;
684 }
685 }
686 return 0;
687}
688
689int GUIScrollList::SetRenderPos(int x, int y, int w /* = 0 */, int h /* = 0 */)
690{
691 mRenderX = x;
692 mRenderY = y;
693 if (w || h)
694 {
695 mRenderW = w;
696 mRenderH = h;
697 }
698 SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
699 mUpdate = 1;
700 return 0;
701}
702
703void GUIScrollList::SetPageFocus(int inFocus)
704{
705 if (inFocus) {
706 NotifyVarChange("", ""); // This forces a check for the header text
707 scrollingSpeed = 0; // stop kinetic scrolling on page changes
708 mUpdate = 1;
709 }
710}