blob: 4b772d45d8800cc790cfec863ecb9020c778c669 [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
Ethan Yonker21ff02a2015-02-18 14:35:00 -060068 mHighlightColor = LoadAttrColor(FindNode(node, "highlight"), "color", &hasHighlightColor);
Ethan Yonker0a3a98f2015-02-05 00:48:28 +010069
Ethan Yonker21ff02a2015-02-18 14:35:00 -060070 child = FindNode(node, "background");
Ethan Yonker0a3a98f2015-02-05 00:48:28 +010071 if (child)
72 {
thatf6ed8fc2015-02-14 20:23:16 +010073 mBackground = LoadAttrImage(child, "resource");
that9876ac32015-02-15 21:40:59 +010074 mBackgroundColor = LoadAttrColor(child, "color");
Ethan Yonker0a3a98f2015-02-05 00:48:28 +010075 }
76
77 // Load the placement
Ethan Yonker21ff02a2015-02-18 14:35:00 -060078 LoadPlacement(FindNode(node, "placement"), &mRenderX, &mRenderY, &mRenderW, &mRenderH);
Ethan Yonker0a3a98f2015-02-05 00:48:28 +010079 SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
80
81 // Load the font, and possibly override the color
Ethan Yonker21ff02a2015-02-18 14:35:00 -060082 child = FindNode(node, "font");
Ethan Yonker0a3a98f2015-02-05 00:48:28 +010083 if (child)
84 {
thatf6ed8fc2015-02-14 20:23:16 +010085 mFont = LoadAttrFont(child, "resource");
that9876ac32015-02-15 21:40:59 +010086 mFontColor = LoadAttrColor(child, "color");
87 mFontHighlightColor = LoadAttrColor(child, "highlightcolor", mFontColor);
88 mItemSpacing = LoadAttrIntScaleY(child, "spacing");
Ethan Yonker0a3a98f2015-02-05 00:48:28 +010089 }
90
91 // Load the separator if it exists
Ethan Yonker21ff02a2015-02-18 14:35:00 -060092 child = FindNode(node, "separator");
Ethan Yonker0a3a98f2015-02-05 00:48:28 +010093 if (child)
94 {
that9876ac32015-02-15 21:40:59 +010095 mSeparatorColor = LoadAttrColor(child, "color");
96 mSeparatorH = LoadAttrIntScaleY(child, "height");
Ethan Yonker0a3a98f2015-02-05 00:48:28 +010097 }
98
that9876ac32015-02-15 21:40:59 +010099 // Fast scroll
Ethan Yonker21ff02a2015-02-18 14:35:00 -0600100 child = FindNode(node, "fastscroll");
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100101 if (child)
102 {
that9876ac32015-02-15 21:40:59 +0100103 mFastScrollLineColor = LoadAttrColor(child, "linecolor");
104 mFastScrollRectColor = LoadAttrColor(child, "rectcolor");
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100105
that9876ac32015-02-15 21:40:59 +0100106 mFastScrollW = LoadAttrIntScaleX(child, "w");
107 mFastScrollLineW = LoadAttrIntScaleX(child, "linew");
108 mFastScrollRectW = LoadAttrIntScaleX(child, "rectw");
109 mFastScrollRectH = LoadAttrIntScaleY(child, "recth");
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100110 }
111
112 // Retrieve the line height
thatf6ed8fc2015-02-14 20:23:16 +0100113 mFontHeight = mFont->GetHeight();
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100114 actualItemHeight = mFontHeight + mItemSpacing + mSeparatorH;
that9876ac32015-02-15 21:40:59 +0100115
116 // Load the header if it exists
Ethan Yonker21ff02a2015-02-18 14:35:00 -0600117 child = FindNode(node, "header");
that9876ac32015-02-15 21:40:59 +0100118 if (child)
119 {
120 mHeaderH = mFontHeight;
121 mHeaderIcon = LoadAttrImage(child, "icon");
122 mHeaderBackgroundColor = LoadAttrColor(child, "background", mBackgroundColor);
123 mHeaderFontColor = LoadAttrColor(child, "textcolor", mFontColor);
124 mHeaderSeparatorColor = LoadAttrColor(child, "separatorcolor", mSeparatorColor);
125 mHeaderSeparatorH = LoadAttrIntScaleY(child, "separatorheight", mSeparatorH);
126
127 if (mHeaderIcon && mHeaderIcon->GetResource())
128 {
129 mHeaderIconWidth = mHeaderIcon->GetWidth();
130 mHeaderIconHeight = mHeaderIcon->GetHeight();
131 if (mHeaderIconHeight > mHeaderH)
132 mHeaderH = mHeaderIconHeight;
133 if (mHeaderIconWidth > maxIconWidth)
134 maxIconWidth = mHeaderIconWidth;
135 }
136
137 mHeaderH += mItemSpacing + mHeaderSeparatorH;
138 if (mHeaderH < actualItemHeight)
139 mHeaderH = actualItemHeight;
140 }
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100141
142 if (actualItemHeight / 3 > 6)
143 touchDebounce = actualItemHeight / 3;
144
145 if (mBackground && mBackground->GetResource())
146 {
thatf6ed8fc2015-02-14 20:23:16 +0100147 mBackgroundW = mBackground->GetWidth();
148 mBackgroundH = mBackground->GetHeight();
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100149 }
150}
151
152GUIScrollList::~GUIScrollList()
153{
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100154}
155
156void GUIScrollList::SetMaxIconSize(int w, int h)
157{
158 if (w > maxIconWidth)
159 maxIconWidth = w;
160 if (h > maxIconHeight)
161 maxIconHeight = h;
162 if (maxIconHeight > mFontHeight) {
163 actualItemHeight = maxIconHeight + mItemSpacing + mSeparatorH;
that9876ac32015-02-15 21:40:59 +0100164 if (mHeaderH > 0 && actualItemHeight > mHeaderH)
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100165 mHeaderH = actualItemHeight;
166 }
167}
168
169void GUIScrollList::SetVisibleListLocation(size_t list_index)
170{
171 // This will make sure that the item indicated by list_index is visible on the screen
172 size_t lines = GetDisplayItemCount(), listSize = GetItemCount();
173
174 if (list_index <= (unsigned)firstDisplayedItem) {
175 // list_index is above the currently displayed items, put the selected item at the very top
176 firstDisplayedItem = list_index;
177 y_offset = 0;
178 } else if (list_index >= firstDisplayedItem + lines) {
179 // list_index is below the currently displayed items, put the selected item at the very bottom
180 firstDisplayedItem = list_index - lines + 1;
181 if (GetDisplayRemainder() != 0) {
182 // There's a partial row displayed, set the scrolling offset so that the selected item really is at the very bottom
183 firstDisplayedItem--;
184 y_offset = GetDisplayRemainder() - actualItemHeight;
185 } else {
186 // There's no partial row so zero out the offset
187 y_offset = 0;
188 }
189 }
190 scrollingSpeed = 0; // stop kinetic scrolling on setting visible location
191 mUpdate = 1;
192}
193
194int GUIScrollList::Render(void)
195{
196 if(!isConditionTrue())
197 return 0;
198
199 // First step, fill background
that9876ac32015-02-15 21:40:59 +0100200 gr_color(mBackgroundColor.red, mBackgroundColor.green, mBackgroundColor.blue, mBackgroundColor.alpha);
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100201 gr_fill(mRenderX, mRenderY + mHeaderH, mRenderW, mRenderH - mHeaderH);
202
that9876ac32015-02-15 21:40:59 +0100203 // don't paint outside of the box
204 gr_clip(mRenderX, mRenderY, mRenderW, mRenderH);
205
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100206 // Next, render the background resource (if it exists)
207 if (mBackground && mBackground->GetResource())
208 {
209 int mBackgroundX = mRenderX + ((mRenderW - mBackgroundW) / 2);
210 int mBackgroundY = mRenderY + ((mRenderH - mBackgroundH) / 2);
211 gr_blit(mBackground->GetResource(), 0, 0, mBackgroundW, mBackgroundH, mBackgroundX, mBackgroundY);
212 }
213
that9876ac32015-02-15 21:40:59 +0100214 // This tells us how many full lines we can actually render
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100215 size_t lines = GetDisplayItemCount();
216
217 size_t listSize = GetItemCount();
218 int listW = mRenderW;
219
220 if (listSize <= lines) {
221 hasScroll = false;
222 scrollingSpeed = 0;
223 lines = listSize;
224 y_offset = 0;
225 } else {
226 hasScroll = true;
227 listW -= mFastScrollW; // space for fast scroll
228 lines++;
229 if (lines < listSize)
230 lines++;
231 }
232
233 void* fontResource = NULL;
234 if (mFont) fontResource = mFont->GetResource();
235
236 int yPos = mRenderY + mHeaderH + y_offset;
237 int fontOffsetY = (int)((actualItemHeight - mFontHeight) / 2);
238
239 // render all visible items
240 for (size_t line = 0; line < lines; line++)
241 {
242 size_t itemindex = line + firstDisplayedItem;
243 if (itemindex >= listSize)
244 break;
245
246 // get item data
thatf6ed8fc2015-02-14 20:23:16 +0100247 ImageResource* icon;
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100248 std::string label;
249 if (GetListItem(itemindex, icon, label))
250 break;
251
252 if (hasHighlightColor && itemindex == selectedItem) {
253 // Highlight the item background of the selected item
that9876ac32015-02-15 21:40:59 +0100254 gr_color(mHighlightColor.red, mHighlightColor.green, mHighlightColor.blue, mHighlightColor.alpha);
255 gr_fill(mRenderX, yPos, mRenderW, actualItemHeight);
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100256 }
257
that9876ac32015-02-15 21:40:59 +0100258 if (itemindex == selectedItem) {
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100259 // Use the highlight color for the font
that9876ac32015-02-15 21:40:59 +0100260 gr_color(mFontHighlightColor.red, mFontHighlightColor.green, mFontHighlightColor.blue, mFontHighlightColor.alpha);
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100261 } else {
262 // Set the color for the font
that9876ac32015-02-15 21:40:59 +0100263 gr_color(mFontColor.red, mFontColor.green, mFontColor.blue, mFontColor.alpha);
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100264 }
265
that9876ac32015-02-15 21:40:59 +0100266 // render icon
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100267 if (icon && icon->GetResource()) {
thatf6ed8fc2015-02-14 20:23:16 +0100268 int currentIconHeight = icon->GetHeight();
269 int currentIconWidth = icon->GetWidth();
that9876ac32015-02-15 21:40:59 +0100270 int currentIconOffsetY = (actualItemHeight - currentIconHeight) / 2;
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100271 int currentIconOffsetX = (maxIconWidth - currentIconWidth) / 2;
that9876ac32015-02-15 21:40:59 +0100272 int image_y = (yPos + currentIconOffsetY);
273 gr_blit(icon->GetResource(), 0, 0, currentIconWidth, currentIconHeight, mRenderX + currentIconOffsetX, image_y);
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100274 }
275
that9876ac32015-02-15 21:40:59 +0100276 // render label text
277 gr_textEx(mRenderX + maxIconWidth + 5, yPos + fontOffsetY, label.c_str(), fontResource);
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100278
279 // Add the separator
that9876ac32015-02-15 21:40:59 +0100280 gr_color(mSeparatorColor.red, mSeparatorColor.green, mSeparatorColor.blue, mSeparatorColor.alpha);
281 gr_fill(mRenderX, yPos + actualItemHeight - mSeparatorH, listW, mSeparatorH);
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100282
283 // Move the yPos
284 yPos += actualItemHeight;
285 }
286
287 // Render the Header (last so that it overwrites the top most row for per pixel scrolling)
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100288 yPos = mRenderY;
that9876ac32015-02-15 21:40:59 +0100289 if (mHeaderH > 0) {
290 // First step, fill background
291 gr_color(mHeaderBackgroundColor.red, mHeaderBackgroundColor.green, mHeaderBackgroundColor.blue, mHeaderBackgroundColor.alpha);
292 gr_fill(mRenderX, mRenderY, mRenderW, mHeaderH);
293
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100294 int mIconOffsetX = 0;
295
296 // render the icon if it exists
thatf6ed8fc2015-02-14 20:23:16 +0100297 ImageResource* headerIcon = mHeaderIcon;
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100298 if (headerIcon && headerIcon->GetResource())
299 {
300 gr_blit(headerIcon->GetResource(), 0, 0, mHeaderIconWidth, mHeaderIconHeight, mRenderX + ((mHeaderIconWidth - maxIconWidth) / 2), (yPos + (int)((mHeaderH - mHeaderIconHeight) / 2)));
301 mIconOffsetX = maxIconWidth;
302 }
303
304 // render the text
that9876ac32015-02-15 21:40:59 +0100305 gr_color(mHeaderFontColor.red, mHeaderFontColor.green, mHeaderFontColor.blue, mHeaderFontColor.alpha);
306 gr_textEx(mRenderX + mIconOffsetX + 5, yPos + (int)((mHeaderH - mFontHeight) / 2), mLastHeaderValue.c_str(), fontResource);
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100307
308 // Add the separator
that9876ac32015-02-15 21:40:59 +0100309 gr_color(mHeaderSeparatorColor.red, mHeaderSeparatorColor.green, mHeaderSeparatorColor.blue, mHeaderSeparatorColor.alpha);
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100310 gr_fill(mRenderX, yPos + mHeaderH - mHeaderSeparatorH, mRenderW, mHeaderSeparatorH);
311 }
312
313 // render fast scroll
314 lines = GetDisplayItemCount();
315 if (hasScroll) {
316 int startX = listW + mRenderX;
317 int fWidth = mRenderW - listW;
318 int fHeight = mRenderH - mHeaderH;
319
320 // line
that9876ac32015-02-15 21:40:59 +0100321 gr_color(mFastScrollLineColor.red, mFastScrollLineColor.green, mFastScrollLineColor.blue, mFastScrollLineColor.alpha);
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100322 gr_fill(startX + fWidth/2, mRenderY + mHeaderH, mFastScrollLineW, mRenderH - mHeaderH);
323
324 // rect
325 int pct = 0;
326 if (GetDisplayRemainder() != 0) {
327 // Properly handle the percentage if a partial line is present
328 int partial_line_size = actualItemHeight - GetDisplayRemainder();
329 pct = ((firstDisplayedItem*actualItemHeight - y_offset)*100)/(listSize*actualItemHeight-((lines + 1)*actualItemHeight) + partial_line_size);
330 } else {
331 pct = ((firstDisplayedItem*actualItemHeight - y_offset)*100)/(listSize*actualItemHeight-lines*actualItemHeight);
332 }
333 int mFastScrollRectX = startX + (fWidth - mFastScrollRectW)/2;
334 int mFastScrollRectY = mRenderY+mHeaderH + ((fHeight - mFastScrollRectH)*pct)/100;
335
that9876ac32015-02-15 21:40:59 +0100336 gr_color(mFastScrollRectColor.red, mFastScrollRectColor.green, mFastScrollRectColor.blue, mFastScrollRectColor.alpha);
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100337 gr_fill(mFastScrollRectX, mFastScrollRectY, mFastScrollRectW, mFastScrollRectH);
338 }
339 mUpdate = 0;
that9876ac32015-02-15 21:40:59 +0100340 // reset clipping
341 gr_noclip();
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100342 return 0;
343}
344
345int GUIScrollList::Update(void)
346{
347 if(!isConditionTrue())
348 return 0;
349
350 if (!mHeaderIsStatic) {
351 std::string newValue = gui_parse_text(mHeaderText);
352 if (mLastHeaderValue != newValue) {
353 mLastHeaderValue = newValue;
354 mUpdate = 1;
355 }
356 }
357
358 // Handle kinetic scrolling
359 int maxScrollDistance = actualItemHeight * SCROLLING_SPEED_LIMIT;
that10ec0172015-02-15 23:52:28 +0100360 int oldScrollingSpeed = scrollingSpeed;
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100361 if (scrollingSpeed == 0) {
362 // Do nothing
363 return 0;
364 } else if (scrollingSpeed > 0) {
365 if (scrollingSpeed < maxScrollDistance)
366 y_offset += scrollingSpeed;
367 else
368 y_offset += maxScrollDistance;
that10ec0172015-02-15 23:52:28 +0100369 scrollingSpeed *= SCROLLING_SPEED_DECREMENT;
370 if (scrollingSpeed == oldScrollingSpeed)
371 --scrollingSpeed;
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100372 } else if (scrollingSpeed < 0) {
373 if (abs(scrollingSpeed) < maxScrollDistance)
374 y_offset += scrollingSpeed;
375 else
376 y_offset -= maxScrollDistance;
that10ec0172015-02-15 23:52:28 +0100377 scrollingSpeed *= SCROLLING_SPEED_DECREMENT;
378 if (scrollingSpeed == oldScrollingSpeed)
379 ++scrollingSpeed;
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100380 }
381 if (abs(scrollingSpeed) < SCROLLING_FLOOR)
382 scrollingSpeed = 0;
383 HandleScrolling();
384 mUpdate = 1;
385
386 return 0;
387}
388
389size_t GUIScrollList::HitTestItem(int x, int y)
390{
391 // We only care about y position
392 if (y < mRenderY || y - mRenderY <= mHeaderH || y - mRenderY > mRenderH)
393 return NO_ITEM;
394
395 int startSelection = (y - mRenderY - mHeaderH);
396
397 // Locate the correct item
398 size_t actualSelection = firstDisplayedItem;
399 int selectY = y_offset;
400 while (selectY + actualItemHeight < startSelection) {
401 selectY += actualItemHeight;
402 actualSelection++;
403 }
404
405 if (actualSelection < GetItemCount())
406 return actualSelection;
407
408 return NO_ITEM;
409}
410
411int GUIScrollList::NotifyTouch(TOUCH_STATE state, int x, int y)
412{
413 if(!isConditionTrue())
414 return -1;
415
416 switch (state)
417 {
418 case TOUCH_START:
419 if (hasScroll && x >= mRenderX + mRenderW - mFastScrollW)
420 fastScroll = 1; // Initial touch is in the fast scroll region
421 if (scrollingSpeed != 0) {
422 selectedItem = NO_ITEM; // this allows the user to tap the list to stop the scrolling without selecting the item they tap
423 scrollingSpeed = 0; // stop scrolling on a new touch
424 } else if (!fastScroll) {
425 // find out which item the user touched
426 selectedItem = HitTestItem(x, y);
427 }
428 if (selectedItem != NO_ITEM)
429 mUpdate = 1;
430 lastY = last2Y = y;
431 break;
432
433 case TOUCH_DRAG:
434 if (fastScroll)
435 {
436 int pct = ((y-mRenderY-mHeaderH)*100)/(mRenderH-mHeaderH);
437 int totalSize = GetItemCount();
438 int lines = GetDisplayItemCount();
439
440 float l = float((totalSize-lines)*pct)/100;
441 if(l + lines >= totalSize)
442 {
443 firstDisplayedItem = totalSize - lines;
444 if (GetDisplayRemainder() != 0) {
445 // There's a partial row displayed, set the scrolling offset so that the last item really is at the very bottom
446 firstDisplayedItem--;
447 y_offset = GetDisplayRemainder() - actualItemHeight;
448 } else {
449 // There's no partial row so zero out the offset
450 y_offset = 0;
451 }
452 }
453 else
454 {
455 if (l < 0)
456 l = 0;
457 firstDisplayedItem = l;
458 y_offset = -(l - int(l))*actualItemHeight;
459 if (GetDisplayRemainder() != 0) {
460 // There's a partial row displayed, make sure y_offset doesn't go past the max
461 if (firstDisplayedItem == totalSize - lines - 1 && y_offset < GetDisplayRemainder() - actualItemHeight)
462 y_offset = GetDisplayRemainder() - actualItemHeight;
463 } else if (firstDisplayedItem == totalSize - lines)
464 y_offset = 0;
465 }
466
467 selectedItem = NO_ITEM;
468 mUpdate = 1;
469 scrollingSpeed = 0; // prevent kinetic scrolling when using fast scroll
470 break;
471 }
472
473 // Provide some debounce on initial touches
474 if (selectedItem != NO_ITEM && abs(y - lastY) < touchDebounce) {
475 mUpdate = 1;
476 break;
477 }
478
479 selectedItem = NO_ITEM; // nothing is selected because we dragged too far
480 // Handle scrolling
481 if (hasScroll) {
482 y_offset += y - lastY; // adjust the scrolling offset based on the difference between the starting touch and the current touch
483 last2Y = lastY; // keep track of previous y locations so that we can tell how fast to scroll for kinetic scrolling
484 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
485
486 HandleScrolling();
487 } else
488 y_offset = 0;
489 mUpdate = 1;
490 break;
491
492 case TOUCH_RELEASE:
493 fastScroll = 0;
494 if (selectedItem != NO_ITEM) {
495 // We've selected an item!
496 NotifySelect(selectedItem);
497 mUpdate = 1;
498
499 DataManager::Vibrate("tw_button_vibrate");
500 selectedItem = NO_ITEM;
501 } else {
502 // Start kinetic scrolling
503 scrollingSpeed = lastY - last2Y;
that10ec0172015-02-15 23:52:28 +0100504 if (abs(scrollingSpeed) < touchDebounce)
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100505 scrollingSpeed = 0;
506 }
507 case TOUCH_REPEAT:
508 case TOUCH_HOLD:
509 break;
510 }
511 return 0;
512}
513
514void GUIScrollList::HandleScrolling()
515{
516 // handle dragging downward, scrolling upward
517 // the offset should always be <= 0 and > -actualItemHeight, adjust the first display row and offset as needed
518 while(firstDisplayedItem && y_offset > 0) {
519 firstDisplayedItem--;
520 y_offset -= actualItemHeight;
521 }
thatde72b6d2015-02-08 08:55:00 +0100522 if (firstDisplayedItem == 0 && y_offset > 0) {
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100523 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 +0100524 scrollingSpeed = 0; // stop kinetic scrolling
525 }
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100526
527 // handle dragging upward, scrolling downward
528 int totalSize = GetItemCount();
529 int lines = GetDisplayItemCount(); // number of full lines our list can display at once
530 int bottom_offset = GetDisplayRemainder() - actualItemHeight; // extra display area that can display a partial line for per pixel scrolling
531
532 // the offset should always be <= 0 and > -actualItemHeight, adjust the first display row and offset as needed
533 while (firstDisplayedItem + lines + (bottom_offset ? 1 : 0) < totalSize && abs(y_offset) > actualItemHeight) {
534 firstDisplayedItem++;
535 y_offset += actualItemHeight;
536 }
537 // Check if we dragged too far, set the list at the bottom and adjust offset as needed
538 if (bottom_offset != 0 && firstDisplayedItem + lines + 1 >= totalSize && y_offset <= bottom_offset) {
539 firstDisplayedItem = totalSize - lines - 1;
540 y_offset = bottom_offset;
thatde72b6d2015-02-08 08:55:00 +0100541 scrollingSpeed = 0; // stop kinetic scrolling
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100542 } else if (firstDisplayedItem + lines >= totalSize && y_offset < 0) {
543 firstDisplayedItem = totalSize - lines;
544 y_offset = 0;
thatde72b6d2015-02-08 08:55:00 +0100545 scrollingSpeed = 0; // stop kinetic scrolling
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100546 }
547}
548
549int GUIScrollList::GetDisplayItemCount()
550{
551 return (mRenderH - mHeaderH) / (actualItemHeight);
552}
553
554int GUIScrollList::GetDisplayRemainder()
555{
556 return (mRenderH - mHeaderH) % actualItemHeight;
557}
558
559int GUIScrollList::NotifyVarChange(const std::string& varName, const std::string& value)
560{
561 GUIObject::NotifyVarChange(varName, value);
562
563 if(!isConditionTrue())
564 return 0;
565
566 if (!mHeaderIsStatic) {
567 std::string newValue = gui_parse_text(mHeaderText);
568 if (mLastHeaderValue != newValue) {
569 mLastHeaderValue = newValue;
570 firstDisplayedItem = 0;
571 y_offset = 0;
572 scrollingSpeed = 0; // stop kinetic scrolling on variable changes
573 mUpdate = 1;
574 }
575 }
576 return 0;
577}
578
579int GUIScrollList::SetRenderPos(int x, int y, int w /* = 0 */, int h /* = 0 */)
580{
581 mRenderX = x;
582 mRenderY = y;
583 if (w || h)
584 {
585 mRenderW = w;
586 mRenderH = h;
587 }
588 SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
589 mUpdate = 1;
590 return 0;
591}
592
593void GUIScrollList::SetPageFocus(int inFocus)
594{
595 if (inFocus) {
596 NotifyVarChange("", ""); // This forces a check for the header text
597 scrollingSpeed = 0; // stop kinetic scrolling on page changes
598 mUpdate = 1;
599 }
600}