Unify scrollable list code

The goal of this patch set is to eliminate the code duplication
caused by copy/paste of the code in the file selector, listbox,
and partition list GUI elements. Those classes will now utilize a
single GUIScrollList class that will handle rendering and
scrolling.

Change-Id: I0cb98ab36cf47178296034293435225658c779cd
This commit is contained in:
Ethan Yonker
2015-02-05 00:48:28 +01:00
parent 9132d91253
commit 0a3a98f892
6 changed files with 1179 additions and 2440 deletions

View File

@@ -25,7 +25,8 @@ LOCAL_SRC_FILES := \
input.cpp \
blanktimer.cpp \
partitionlist.cpp \
mousecursor.cpp
mousecursor.cpp \
scrolllist.cpp
ifneq ($(TWRP_CUSTOM_KEYBOARD),)
LOCAL_SRC_FILES += $(TWRP_CUSTOM_KEYBOARD)

File diff suppressed because it is too large Load Diff

View File

@@ -16,26 +16,9 @@
along with TWRP. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/input.h>
#include <pthread.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/reboot.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <dirent.h>
#include <ctype.h>
#include <algorithm>
extern "C" {
#include "../twcommon.h"
@@ -45,100 +28,18 @@ extern "C" {
#include "rapidxml.hpp"
#include "objects.hpp"
#include "../data.hpp"
#include "../twrp-functions.hpp"
#define SCROLLING_SPEED_DECREMENT 6
#define SCROLLING_FLOOR 10
#define SCROLLING_MULTIPLIER 6
GUIListBox::GUIListBox(xml_node<>* node) : GUIObject(node)
GUIListBox::GUIListBox(xml_node<>* node) : GUIScrollList(node)
{
xml_attribute<>* attr;
xml_node<>* child;
int header_separator_color_specified = 0, header_separator_height_specified = 0, header_text_color_specified = 0, header_background_color_specified = 0;
mStart = mLineSpacing = startY = mFontHeight = mSeparatorH = scrollingY = scrollingSpeed = 0;
mIconWidth = mIconHeight = mSelectedIconHeight = mSelectedIconWidth = mUnselectedIconHeight = mUnselectedIconWidth = mHeaderIconHeight = mHeaderIconWidth = 0;
mHeaderSeparatorH = mLineHeight = mHeaderIsStatic = mHeaderH = actualLineHeight = 0;
mIconSelected = mIconUnselected = mBackground = mFont = mHeaderIcon = NULL;
mBackgroundX = mBackgroundY = mBackgroundW = mBackgroundH = 0;
mFastScrollW = mFastScrollLineW = mFastScrollRectW = mFastScrollRectH = 0;
mFastScrollRectX = mFastScrollRectY = -1;
mIconSelected = mIconUnselected = NULL;
int mSelectedIconWidth = 0, mSelectedIconHeight = 0, mUnselectedIconWidth = 0, mUnselectedIconHeight = 0, mIconWidth = 0, mIconHeight = 0;
mUpdate = 0;
touchDebounce = 6;
ConvertStrToColor("black", &mBackgroundColor);
ConvertStrToColor("black", &mHeaderBackgroundColor);
ConvertStrToColor("black", &mSeparatorColor);
ConvertStrToColor("black", &mHeaderSeparatorColor);
ConvertStrToColor("white", &mFontColor);
ConvertStrToColor("white", &mHeaderFontColor);
ConvertStrToColor("white", &mFastScrollLineColor);
ConvertStrToColor("white", &mFastScrollRectColor);
hasHighlightColor = false;
hasFontHighlightColor = false;
isHighlighted = false;
startSelection = -1;
// Load header text
child = node->first_node("header");
if (child)
{
attr = child->first_attribute("icon");
if (attr)
mHeaderIcon = PageManager::FindResource(attr->value());
attr = child->first_attribute("background");
if (attr)
{
std::string color = attr->value();
ConvertStrToColor(color, &mHeaderBackgroundColor);
header_background_color_specified = -1;
}
attr = child->first_attribute("textcolor");
if (attr)
{
std::string color = attr->value();
ConvertStrToColor(color, &mHeaderFontColor);
header_text_color_specified = -1;
}
attr = child->first_attribute("separatorcolor");
if (attr)
{
std::string color = attr->value();
ConvertStrToColor(color, &mHeaderSeparatorColor);
header_separator_color_specified = -1;
}
attr = child->first_attribute("separatorheight");
if (attr) {
string parsevalue = gui_parse_text(attr->value());
mHeaderSeparatorH = atoi(parsevalue.c_str());
header_separator_height_specified = -1;
}
}
child = node->first_node("text");
if (child) mHeaderText = child->value();
memset(&mHighlightColor, 0, sizeof(COLOR));
child = node->first_node("highlight");
if (child) {
attr = child->first_attribute("color");
if (attr) {
hasHighlightColor = true;
std::string color = attr->value();
ConvertStrToColor(color, &mHighlightColor);
}
}
// Simple way to check for static state
mLastValue = gui_parse_text(mHeaderText);
if (mLastValue != mHeaderText)
mHeaderIsStatic = 0;
else
mHeaderIsStatic = -1;
// Get the icons, if any
child = node->first_node("icon");
if (child)
{
if (child) {
attr = child->first_attribute("selected");
if (attr)
mIconSelected = PageManager::FindResource(attr->value());
@@ -146,187 +47,41 @@ GUIListBox::GUIListBox(xml_node<>* node) : GUIObject(node)
if (attr)
mIconUnselected = PageManager::FindResource(attr->value());
}
child = node->first_node("background");
if (child)
{
attr = child->first_attribute("resource");
if (attr)
mBackground = PageManager::FindResource(attr->value());
attr = child->first_attribute("color");
if (attr)
{
std::string color = attr->value();
ConvertStrToColor(color, &mBackgroundColor);
if (!header_background_color_specified)
ConvertStrToColor(color, &mHeaderBackgroundColor);
}
if (mIconSelected && mIconSelected->GetResource()) {
mSelectedIconWidth = gr_get_width(mIconSelected->GetResource());
mSelectedIconHeight = gr_get_height(mIconSelected->GetResource());
if (mSelectedIconHeight > mIconHeight)
mIconHeight = mSelectedIconHeight;
mIconWidth = mSelectedIconWidth;
}
// Load the placement
LoadPlacement(node->first_node("placement"), &mRenderX, &mRenderY, &mRenderW, &mRenderH);
SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
// Load the font, and possibly override the color
child = node->first_node("font");
if (child)
{
attr = child->first_attribute("resource");
if (attr)
mFont = PageManager::FindResource(attr->value());
attr = child->first_attribute("color");
if (attr)
{
std::string color = attr->value();
ConvertStrToColor(color, &mFontColor);
if (!header_text_color_specified)
ConvertStrToColor(color, &mHeaderFontColor);
}
attr = child->first_attribute("spacing");
if (attr) {
string parsevalue = gui_parse_text(attr->value());
mLineSpacing = atoi(parsevalue.c_str());
}
attr = child->first_attribute("highlightcolor");
memset(&mFontHighlightColor, 0, sizeof(COLOR));
if (attr)
{
std::string color = attr->value();
ConvertStrToColor(color, &mFontHighlightColor);
hasFontHighlightColor = true;
}
}
// Load the separator if it exists
child = node->first_node("separator");
if (child)
{
attr = child->first_attribute("color");
if (attr)
{
std::string color = attr->value();
ConvertStrToColor(color, &mSeparatorColor);
if (!header_separator_color_specified)
ConvertStrToColor(color, &mHeaderSeparatorColor);
}
attr = child->first_attribute("height");
if (attr) {
string parsevalue = gui_parse_text(attr->value());
mSeparatorH = atoi(parsevalue.c_str());
if (!header_separator_height_specified)
mHeaderSeparatorH = mSeparatorH;
}
if (mIconUnselected && mIconUnselected->GetResource()) {
mUnselectedIconWidth = gr_get_width(mIconUnselected->GetResource());
mUnselectedIconHeight = gr_get_height(mIconUnselected->GetResource());
if (mUnselectedIconHeight > mIconHeight)
mIconHeight = mUnselectedIconHeight;
if (mUnselectedIconWidth > mIconWidth)
mIconWidth = mUnselectedIconWidth;
}
SetMaxIconSize(mIconWidth, mIconHeight);
// Handle the result variable
child = node->first_node("data");
if (child)
{
if (child) {
attr = child->first_attribute("name");
if (attr)
mVariable = attr->value();
attr = child->first_attribute("default");
if (attr)
DataManager::SetValue(mVariable, attr->value());
// Get the currently selected value for the list
DataManager::GetValue(mVariable, currentValue);
}
// Fast scroll colors
child = node->first_node("fastscroll");
if (child)
{
attr = child->first_attribute("linecolor");
if(attr)
ConvertStrToColor(attr->value(), &mFastScrollLineColor);
attr = child->first_attribute("rectcolor");
if(attr)
ConvertStrToColor(attr->value(), &mFastScrollRectColor);
attr = child->first_attribute("w");
if (attr) {
string parsevalue = gui_parse_text(attr->value());
mFastScrollW = atoi(parsevalue.c_str());
}
attr = child->first_attribute("linew");
if (attr) {
string parsevalue = gui_parse_text(attr->value());
mFastScrollLineW = atoi(parsevalue.c_str());
}
attr = child->first_attribute("rectw");
if (attr) {
string parsevalue = gui_parse_text(attr->value());
mFastScrollRectW = atoi(parsevalue.c_str());
}
attr = child->first_attribute("recth");
if (attr) {
string parsevalue = gui_parse_text(attr->value());
mFastScrollRectH = atoi(parsevalue.c_str());
}
}
// Retrieve the line height
mFontHeight = gr_getMaxFontHeight(mFont ? mFont->GetResource() : NULL);
mLineHeight = mFontHeight;
mHeaderH = mFontHeight;
if (mIconSelected && mIconSelected->GetResource())
{
mSelectedIconWidth = gr_get_width(mIconSelected->GetResource());
mSelectedIconHeight = gr_get_height(mIconSelected->GetResource());
if (mSelectedIconHeight > (int)mLineHeight)
mLineHeight = mSelectedIconHeight;
mIconWidth = mSelectedIconWidth;
}
if (mIconUnselected && mIconUnselected->GetResource())
{
mUnselectedIconWidth = gr_get_width(mIconUnselected->GetResource());
mUnselectedIconHeight = gr_get_height(mIconUnselected->GetResource());
if (mUnselectedIconHeight > (int)mLineHeight)
mLineHeight = mUnselectedIconHeight;
if (mUnselectedIconWidth > mIconWidth)
mIconWidth = mUnselectedIconWidth;
}
if (mHeaderIcon && mHeaderIcon->GetResource())
{
mHeaderIconWidth = gr_get_width(mHeaderIcon->GetResource());
mHeaderIconHeight = gr_get_height(mHeaderIcon->GetResource());
if (mHeaderIconHeight > mHeaderH)
mHeaderH = mHeaderIconHeight;
if (mHeaderIconWidth > mIconWidth)
mIconWidth = mHeaderIconWidth;
}
mHeaderH += mLineSpacing + mHeaderSeparatorH;
actualLineHeight = mLineHeight + mLineSpacing + mSeparatorH;
if (mHeaderH < actualLineHeight)
mHeaderH = actualLineHeight;
if (actualLineHeight / 3 > 6)
touchDebounce = actualLineHeight / 3;
if (mBackground && mBackground->GetResource())
{
mBackgroundW = gr_get_width(mBackground->GetResource());
mBackgroundH = gr_get_height(mBackground->GetResource());
}
// Get the currently selected value for the list
DataManager::GetValue(mVariable, currentValue);
// Get the data for the list
child = node->first_node("listitem");
if (!child) return;
while (child)
{
while (child) {
ListData data;
attr = child->first_attribute("name");
@@ -348,179 +103,8 @@ GUIListBox::GUIListBox(xml_node<>* node) : GUIObject(node)
GUIListBox::~GUIListBox()
{
}
int GUIListBox::Render(void)
{
if(!isConditionTrue())
return 0;
// First step, fill background
gr_color(mBackgroundColor.red, mBackgroundColor.green, mBackgroundColor.blue, 255);
gr_fill(mRenderX, mRenderY + mHeaderH, mRenderW, mRenderH - mHeaderH);
// Next, render the background resource (if it exists)
if (mBackground && mBackground->GetResource())
{
mBackgroundX = mRenderX + ((mRenderW - mBackgroundW) / 2);
mBackgroundY = mRenderY + ((mRenderH - mBackgroundH) / 2);
gr_blit(mBackground->GetResource(), 0, 0, mBackgroundW, mBackgroundH, mBackgroundX, mBackgroundY);
}
// This tells us how many lines we can actually render
int lines = (mRenderH - mHeaderH) / (actualLineHeight);
int line;
int listSize = mList.size();
int listW = mRenderW;
if (listSize < lines) {
lines = listSize;
scrollingY = 0;
mFastScrollRectX = mFastScrollRectY = -1;
} else {
listW -= mFastScrollW; // space for fast scroll
lines++;
if (lines < listSize)
lines++;
}
void* fontResource = NULL;
if (mFont) fontResource = mFont->GetResource();
int yPos = mRenderY + mHeaderH + scrollingY;
int fontOffsetY = (int)((actualLineHeight - mFontHeight) / 2);
int currentIconHeight = 0, currentIconWidth = 0;
int currentIconOffsetY = 0, currentIconOffsetX = 0;
int UnselectedIconOffsetY = (int)((actualLineHeight - mUnselectedIconHeight) / 2), SelectedIconOffsetY = (int)((actualLineHeight - mSelectedIconHeight) / 2);
int UnselectedIconOffsetX = (mIconWidth - mUnselectedIconWidth) / 2, SelectedIconOffsetX = (mIconWidth - mSelectedIconWidth) / 2;
int actualSelection = mStart;
if (isHighlighted) {
int selectY = scrollingY;
// Locate the correct line for highlighting
while (selectY + actualLineHeight < startSelection) {
selectY += actualLineHeight;
actualSelection++;
}
if (hasHighlightColor) {
// Highlight the area
gr_color(mHighlightColor.red, mHighlightColor.green, mHighlightColor.blue, 255);
int HighlightHeight = actualLineHeight;
if (mRenderY + mHeaderH + selectY + actualLineHeight > mRenderH + mRenderY) {
HighlightHeight = actualLineHeight - (mRenderY + mHeaderH + selectY + actualLineHeight - mRenderH - mRenderY);
}
gr_fill(mRenderX, mRenderY + mHeaderH + selectY, mRenderW, HighlightHeight);
}
}
for (line = 0; line < lines; line++)
{
Resource* icon;
std::string label;
if (line + mStart >= listSize)
continue;
label = mList.at(line + mStart).displayName;
if (isHighlighted && hasFontHighlightColor && line + mStart == actualSelection) {
// Use the highlight color for the font
gr_color(mFontHighlightColor.red, mFontHighlightColor.green, mFontHighlightColor.blue, 255);
} else {
// Set the color for the font
gr_color(mFontColor.red, mFontColor.green, mFontColor.blue, 255);
}
if (mList.at(line + mStart).selected != 0)
{
icon = mIconSelected;
currentIconHeight = mSelectedIconHeight;
currentIconWidth = mSelectedIconWidth;
currentIconOffsetY = SelectedIconOffsetY;
currentIconOffsetX = SelectedIconOffsetX;
}
else
{
icon = mIconUnselected;
currentIconHeight = mSelectedIconHeight;
currentIconWidth = mSelectedIconWidth;
currentIconOffsetY = SelectedIconOffsetY;
currentIconOffsetX = SelectedIconOffsetX;
}
if (icon && icon->GetResource())
{
int rect_y = 0, image_y = (yPos + currentIconOffsetY);
if (image_y + currentIconHeight > mRenderY + mRenderH)
rect_y = mRenderY + mRenderH - image_y;
else
rect_y = currentIconHeight;
gr_blit(icon->GetResource(), 0, 0, currentIconWidth, rect_y, mRenderX + currentIconOffsetX, image_y);
}
gr_textExWH(mRenderX + mIconWidth + 5, yPos + fontOffsetY, label.c_str(), fontResource, mRenderX + listW, mRenderY + mRenderH);
// Add the separator
if (yPos + actualLineHeight < mRenderH + mRenderY) {
gr_color(mSeparatorColor.red, mSeparatorColor.green, mSeparatorColor.blue, 255);
gr_fill(mRenderX, yPos + actualLineHeight - mSeparatorH, listW, mSeparatorH);
}
// Move the yPos
yPos += actualLineHeight;
}
// Render the Header (last so that it overwrites the top most row for per pixel scrolling)
// First step, fill background
gr_color(mHeaderBackgroundColor.red, mHeaderBackgroundColor.green, mHeaderBackgroundColor.blue, 255);
gr_fill(mRenderX, mRenderY, mRenderW, mHeaderH);
// Now, we need the header (icon + text)
yPos = mRenderY;
{
Resource* headerIcon;
int mIconOffsetX = 0;
// render the icon if it exists
headerIcon = mHeaderIcon;
if (headerIcon && headerIcon->GetResource())
{
gr_blit(headerIcon->GetResource(), 0, 0, mHeaderIconWidth, mHeaderIconHeight, mRenderX + ((mHeaderIconWidth - mIconWidth) / 2), (yPos + (int)((mHeaderH - mHeaderIconHeight) / 2)));
mIconOffsetX = mIconWidth;
}
// render the text
gr_color(mHeaderFontColor.red, mHeaderFontColor.green, mHeaderFontColor.blue, 255);
gr_textExWH(mRenderX + mIconOffsetX + 5, yPos + (int)((mHeaderH - mFontHeight) / 2), mLastValue.c_str(), fontResource, mRenderX + mRenderW, mRenderY + mRenderH);
// Add the separator
gr_color(mHeaderSeparatorColor.red, mHeaderSeparatorColor.green, mHeaderSeparatorColor.blue, 255);
gr_fill(mRenderX, yPos + mHeaderH - mHeaderSeparatorH, mRenderW, mHeaderSeparatorH);
}
// render fast scroll
lines = (mRenderH - mHeaderH) / (actualLineHeight);
if(mFastScrollW > 0 && listSize > lines)
{
int startX = listW + mRenderX;
int fWidth = mRenderW - listW;
int fHeight = mRenderH - mHeaderH;
// line
gr_color(mFastScrollLineColor.red, mFastScrollLineColor.green, mFastScrollLineColor.blue, 255);
gr_fill(startX + fWidth/2, mRenderY + mHeaderH, mFastScrollLineW, mRenderH - mHeaderH);
// rect
int pct = ((mStart*actualLineHeight - scrollingY)*100)/((listSize)*actualLineHeight-lines*actualLineHeight);
mFastScrollRectX = startX + (fWidth - mFastScrollRectW)/2;
mFastScrollRectY = mRenderY+mHeaderH + ((fHeight - mFastScrollRectH)*pct)/100;
gr_color(mFastScrollRectColor.red, mFastScrollRectColor.green, mFastScrollRectColor.blue, 255);
gr_fill(mFastScrollRectX, mFastScrollRectY, mFastScrollRectW, mFastScrollRectH);
}
mUpdate = 0;
return 0;
delete mIconSelected;
delete mIconUnselected;
}
int GUIListBox::Update(void)
@@ -528,306 +112,75 @@ int GUIListBox::Update(void)
if(!isConditionTrue())
return 0;
if (!mHeaderIsStatic) {
std::string newValue = gui_parse_text(mHeaderText);
if (mLastValue != newValue) {
mLastValue = newValue;
mUpdate = 1;
}
}
GUIScrollList::Update();
if (mUpdate)
{
if (mUpdate) {
mUpdate = 0;
if (Render() == 0)
return 2;
}
// Handle kinetic scrolling
if (scrollingSpeed == 0) {
// Do nothing
} else if (scrollingSpeed > 0) {
if (scrollingSpeed < ((int) (actualLineHeight * 2.5))) {
scrollingY += scrollingSpeed;
scrollingSpeed -= SCROLLING_SPEED_DECREMENT;
} else {
scrollingY += ((int) (actualLineHeight * 2.5));
scrollingSpeed -= SCROLLING_SPEED_DECREMENT;
}
while (mStart && scrollingY > 0) {
mStart--;
scrollingY -= actualLineHeight;
}
if (mStart == 0 && scrollingY > 0) {
scrollingY = 0;
scrollingSpeed = 0;
} else if (scrollingSpeed < SCROLLING_FLOOR)
scrollingSpeed = 0;
mUpdate = 1;
} else if (scrollingSpeed < 0) {
int totalSize = mList.size();
int lines = (mRenderH - mHeaderH) / (actualLineHeight);
if (totalSize > lines) {
int bottom_offset = ((int)(mRenderH) - mHeaderH) - (lines * actualLineHeight);
bottom_offset -= actualLineHeight;
if (abs(scrollingSpeed) < ((int) (actualLineHeight * 2.5))) {
scrollingY += scrollingSpeed;
scrollingSpeed += SCROLLING_SPEED_DECREMENT;
} else {
scrollingY -= ((int) (actualLineHeight * 2.5));
scrollingSpeed += SCROLLING_SPEED_DECREMENT;
}
while (mStart + lines + (bottom_offset ? 1 : 0) < totalSize && abs(scrollingY) > actualLineHeight) {
mStart++;
scrollingY += actualLineHeight;
}
if (bottom_offset != 0 && mStart + lines + 1 >= totalSize && scrollingY <= bottom_offset) {
mStart = totalSize - lines - 1;
scrollingY = bottom_offset;
} else if (mStart + lines >= totalSize && scrollingY < 0) {
mStart = totalSize - lines;
scrollingY = 0;
} else if (scrollingSpeed * -1 < SCROLLING_FLOOR)
scrollingSpeed = 0;
mUpdate = 1;
}
}
return 0;
}
int GUIListBox::GetSelection(int x, int y)
{
// We only care about y position
if (y < mRenderY || y - mRenderY <= mHeaderH || y - mRenderY > mRenderH) return -1;
return (y - mRenderY - mHeaderH);
}
int GUIListBox::NotifyTouch(TOUCH_STATE state, int x, int y)
{
if(!isConditionTrue())
return -1;
static int lastY = 0, last2Y = 0, fastScroll = 0;
int selection = 0;
switch (state)
{
case TOUCH_START:
if (scrollingSpeed != 0)
startSelection = -1;
else
startSelection = GetSelection(x,y);
isHighlighted = (startSelection > -1);
if (isHighlighted)
mUpdate = 1;
startY = lastY = last2Y = y;
scrollingSpeed = 0;
if(mFastScrollRectX != -1 && x >= mRenderX + mRenderW - mFastScrollW)
fastScroll = 1;
break;
case TOUCH_DRAG:
// Check if we dragged out of the selection window
if (GetSelection(x, y) == -1) {
last2Y = lastY = 0;
if (isHighlighted) {
isHighlighted = false;
mUpdate = 1;
}
break;
}
// Fast scroll
if(fastScroll)
{
int pct = ((y-mRenderY-mHeaderH)*100)/(mRenderH-mHeaderH);
int totalSize = mList.size();
int lines = (mRenderH - mHeaderH) / (actualLineHeight);
float l = float((totalSize-lines)*pct)/100;
if(l + lines >= totalSize)
{
mStart = totalSize - lines;
scrollingY = 0;
}
else
{
mStart = l;
scrollingY = -(l - int(l))*actualLineHeight;
}
startSelection = -1;
mUpdate = 1;
scrollingSpeed = 0;
isHighlighted = false;
break;
}
// Provide some debounce on initial touches
if (startSelection != -1 && abs(y - startY) < touchDebounce) {
isHighlighted = true;
mUpdate = 1;
break;
}
isHighlighted = false;
last2Y = lastY;
lastY = y;
startSelection = -1;
// Handle scrolling
scrollingY += y - startY;
startY = y;
while(mStart && scrollingY > 0) {
mStart--;
scrollingY -= actualLineHeight;
}
if (mStart == 0 && scrollingY > 0)
scrollingY = 0;
{
int totalSize = mList.size();
int lines = (mRenderH - mHeaderH) / (actualLineHeight);
if (totalSize > lines) {
int bottom_offset = ((int)(mRenderH) - mHeaderH) - (lines * actualLineHeight);
bottom_offset -= actualLineHeight;
while (mStart + lines + (bottom_offset ? 1 : 0) < totalSize && abs(scrollingY) > actualLineHeight) {
mStart++;
scrollingY += actualLineHeight;
}
if (bottom_offset != 0 && mStart + lines + 1 >= totalSize && scrollingY <= bottom_offset) {
mStart = totalSize - lines - 1;
scrollingY = bottom_offset;
} else if (mStart + lines >= totalSize && scrollingY < 0) {
mStart = totalSize - lines;
scrollingY = 0;
}
} else
scrollingY = 0;
}
mUpdate = 1;
break;
case TOUCH_RELEASE:
isHighlighted = false;
fastScroll = 0;
if (startSelection >= 0)
{
// We've selected an item!
std::string str;
int listSize = mList.size();
int selectY = scrollingY, actualSelection = mStart;
// Move the selection to the proper place in the array
while (selectY + actualLineHeight < startSelection) {
selectY += actualLineHeight;
actualSelection++;
}
if (actualSelection < listSize && !mVariable.empty())
{
int i;
for (i=0; i<listSize; i++)
mList.at(i).selected = 0;
str = mList.at(actualSelection).variableValue;
mList.at(actualSelection).selected = 1;
DataManager::SetValue(mVariable, str);
mUpdate = 1;
DataManager::Vibrate("tw_button_vibrate");
}
} else {
// This is for kinetic scrolling
scrollingSpeed = lastY - last2Y;
if (abs(scrollingSpeed) > SCROLLING_FLOOR)
scrollingSpeed *= SCROLLING_MULTIPLIER;
else
scrollingSpeed = 0;
}
case TOUCH_REPEAT:
case TOUCH_HOLD:
break;
}
return 0;
}
int GUIListBox::NotifyVarChange(const std::string& varName, const std::string& value)
{
GUIObject::NotifyVarChange(varName, value);
if(!isConditionTrue())
return 0;
if (!mHeaderIsStatic) {
std::string newValue = gui_parse_text(mHeaderText);
if (mLastValue != newValue) {
mLastValue = newValue;
mStart = 0;
scrollingY = 0;
scrollingSpeed = 0;
mUpdate = 1;
}
}
if (varName == mVariable)
{
int i, listSize = mList.size(), selected_index = 0;
GUIScrollList::NotifyVarChange(varName, value);
// Check to see if the variable that we are using to store the list selected value has been updated
if (varName == mVariable) {
int i, listSize = mList.size();
currentValue = value;
for (i=0; i<listSize; i++) {
for (i = 0; i < listSize; i++) {
if (mList.at(i).variableValue == currentValue) {
mList.at(i).selected = 1;
selected_index = i;
SetVisibleListLocation(i);
} else
mList.at(i).selected = 0;
}
int lines = mRenderH / (mLineHeight + mLineSpacing);
int line;
if (selected_index > mStart + lines - 1)
mStart = selected_index;
if (mStart > listSize - lines) {
mStart = listSize - lines;
} else if (selected_index < mStart) {
mStart = selected_index;
}
mUpdate = 1;
return 0;
}
return 0;
}
int GUIListBox::SetRenderPos(int x, int y, int w /* = 0 */, int h /* = 0 */)
{
mRenderX = x;
mRenderY = y;
if (w || h)
{
mRenderW = w;
mRenderH = h;
}
SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
mUpdate = 1;
return 0;
}
void GUIListBox::SetPageFocus(int inFocus)
{
if (inFocus)
{
GUIScrollList::SetPageFocus(inFocus);
if (inFocus) {
DataManager::GetValue(mVariable, currentValue);
NotifyVarChange(mVariable, currentValue);
mUpdate = 1;
}
}
size_t GUIListBox::GetItemCount()
{
return mList.size();
}
int GUIListBox::GetListItem(size_t item_index, Resource*& icon, std::string &text)
{
text = mList.at(item_index).displayName;
if (mList.at(item_index).selected)
icon = mIconSelected;
else
icon = mIconUnselected;
return 0;
}
void GUIListBox::NotifySelect(size_t item_selected)
{
for (size_t i = 0; i < mList.size(); i++) {
mList.at(i).selected = 0;
}
if (item_selected < mList.size()) {
mList.at(item_selected).selected = 1;
string str = mList.at(item_selected).variableValue;
DataManager::SetValue(mVariable, str);
}
mUpdate = 1;
}

View File

@@ -501,11 +501,17 @@ protected:
std::string mVarName;
};
class GUIFileSelector : public GUIObject, public RenderObject, public ActionObject
struct ScrollListData {
Resource* displayResource;
std::string displayName;
int list_index;
};
class GUIScrollList : public GUIObject, public RenderObject, public ActionObject
{
public:
GUIFileSelector(xml_node<>* node);
virtual ~GUIFileSelector();
GUIScrollList(xml_node<>* node);
virtual ~GUIScrollList();
public:
// Render - Render the full object to the GL surface
@@ -530,6 +536,106 @@ public:
// SetPageFocus - Notify when a page gains or loses focus
virtual void SetPageFocus(int inFocus);
protected:
// derived classes need to implement these
// get number of items
virtual size_t GetItemCount() { return 0; }
// get data for one item
virtual int GetListItem(size_t item_index, Resource*& icon, std::string &text)
{ icon = NULL; text = ""; return -1; }
// an item was selected
virtual void NotifySelect(size_t item_selected) {}
enum { NO_ITEM = (size_t)-1 };
// returns item index at coordinates or NO_ITEM if there is no item there
size_t HitTestItem(int x, int y);
// Called by the derived class to set the max icon size so we can calculate the proper actualItemHeight and center smaller icons if the icon size varies
void SetMaxIconSize(int w, int h);
// This will make sure that the item indicated by list_index is visible on the screen
void SetVisibleListLocation(size_t list_index);
// Handle scrolling changes for drags and kinetic scrolling
void HandleScrolling();
// Returns many rows the list is capable of displaying
int GetDisplayItemCount();
// Returns the size in pixels of a partial item or row size
int GetDisplayRemainder();
protected:
// Background
COLOR mBackgroundColor;
Resource* mBackground; // background image, if any, automatically centered
int mBackgroundW, mBackgroundH; // background width and height if using an image for the background
// Header
COLOR mHeaderBackgroundColor;
COLOR mHeaderFontColor;
std::string mHeaderText; // Original header text without parsing any variables
std::string mLastHeaderValue; // Header text after parsing variables
int mHeaderIsStatic; // indicates if the header is static (no need to check for changes in NotifyVarChange)
int mHeaderH; // actual header height including font, icon, padding, and separator heights
Resource* mHeaderIcon;
int mHeaderIconHeight, mHeaderIconWidth; // width and height of the header icon if present
int mHeaderSeparatorH; // Height of the separator between header and list items
COLOR mHeaderSeparatorColor; // color of the header separator
// Per-item layout
Resource* mFont;
COLOR mFontColor;
bool hasHighlightColor; // indicates if a hightlight color was set
bool hasFontHighlightColor; // indicates if the font hightlight color is set
COLOR mHighlightColor; // background row hightlight color
COLOR mFontHighlightColor;
int mFontHeight;
int actualItemHeight; // Actual height of each item in pixels including max icon size, font size, and padding
int maxIconWidth, maxIconHeight; // max icon width and height for the list, set by derived class in SetMaxIconSize
int mItemSpacing; // stores the spacing or padding on the y axis, part of the actualItemHeight
int mSeparatorH; // Height of the separator between items
COLOR mSeparatorColor; // color of the separator that is between items
// Scrolling and dynamic state
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
COLOR mFastScrollLineColor;
COLOR mFastScrollRectColor;
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
int y_offset; // this is how many pixels offset in the y axis for per pixel scrolling, is always <= 0 and should never be < -actualItemHeight
size_t selectedItem; // selected item index after the initial touch, set to -1 if we are scrolling
int touchDebounce; // debounce for touches, minimum of 6 pixels but may be larger calculated based actualItemHeight / 3
int lastY, last2Y; // last 2 touch locations, used for tracking kinetic scroll speed
int fastScroll; // indicates that the inital touch was inside the fastscroll region - makes for easier fast scrolling as the touches don't have to stay within the fast scroll region and you drag your finger
int mUpdate; // indicates that a change took place and we need to re-render
};
class GUIFileSelector : public GUIScrollList
{
public:
GUIFileSelector(xml_node<>* node);
virtual ~GUIFileSelector();
public:
// Update - Update any UI component animations (called <= 30 FPS)
// Return 0 if nothing to update, 1 on success and contiue, >1 if full render required, and <0 on error
virtual int Update(void);
// NotifyVarChange - Notify of a variable change
virtual int NotifyVarChange(const std::string& varName, const std::string& value);
// SetPageFocus - Notify when a page gains or loses focus
virtual void SetPageFocus(int inFocus);
virtual size_t GetItemCount();
virtual int GetListItem(size_t item_index, Resource*& icon, std::string &text);
virtual void NotifySelect(size_t item_selected);
protected:
struct FileData {
std::string fileName;
@@ -544,96 +650,46 @@ protected:
};
protected:
virtual int GetSelection(int x, int y);
virtual int GetFileList(const std::string folder);
static bool fileSort(FileData d1, FileData d2);
protected:
std::vector<FileData> mFolderList;
std::vector<FileData> mFileList;
std::string mPathVar;
std::string mExtn;
std::string mVariable;
std::string mSortVariable;
std::string mSelection;
std::string mHeaderText;
std::string mLastValue;
int actualLineHeight;
int mStart;
int mLineSpacing;
int mSeparatorH;
int mHeaderSeparatorH;
int mShowFolders, mShowFiles, mShowNavFolders;
int mUpdate;
int mBackgroundX, mBackgroundY, mBackgroundW, mBackgroundH;
int mHeaderH;
int mFastScrollW;
int mFastScrollLineW;
int mFastScrollRectW;
int mFastScrollRectH;
int mFastScrollRectX;
int mFastScrollRectY;
static int mSortOrder;
int startY;
int scrollingSpeed;
int scrollingY;
int mHeaderIsStatic;
int touchDebounce;
unsigned mFontHeight;
unsigned mLineHeight;
int mIconWidth, mIconHeight, mFolderIconHeight, mFileIconHeight, mFolderIconWidth, mFileIconWidth, mHeaderIconHeight, mHeaderIconWidth;
Resource* mHeaderIcon;
std::string mPathVar; // current path displayed, saved in the data manager
std::string mExtn; // used for filtering the file list, for example, *.zip
std::string mVariable; // set when the user selects an item, pull path like /path/to/foo
std::string mSortVariable; // data manager variable used to change the sorting of files
std::string mSelection; // set when the user selects an item without the full path like selecting /path/to/foo would just be set to foo
int mShowFolders, mShowFiles; // indicates if the list should show folders and/or files
int mShowNavFolders; // indicates if the list should include the "up a level" item and allow you to traverse folders (nav folders are disabled for the restore list, for instance)
static int mSortOrder; // must be static because it is used by the static function fileSort
Resource* mFolderIcon;
Resource* mFileIcon;
Resource* mBackground;
Resource* mFont;
COLOR mBackgroundColor;
COLOR mFontColor;
COLOR mHeaderBackgroundColor;
COLOR mHeaderFontColor;
COLOR mSeparatorColor;
COLOR mHeaderSeparatorColor;
COLOR mFastScrollLineColor;
COLOR mFastScrollRectColor;
bool hasHighlightColor;
bool hasFontHighlightColor;
bool isHighlighted;
COLOR mHighlightColor;
COLOR mFontHighlightColor;
int startSelection;
bool updateFileList;
};
class GUIListBox : public GUIObject, public RenderObject, public ActionObject
class GUIListBox : public GUIScrollList
{
public:
GUIListBox(xml_node<>* node);
virtual ~GUIListBox();
public:
// Render - Render the full object to the GL surface
// Return 0 on success, <0 on error
virtual int Render(void);
// Update - Update any UI component animations (called <= 30 FPS)
// Return 0 if nothing to update, 1 on success and contiue, >1 if full render required, and <0 on error
virtual int Update(void);
// NotifyTouch - Notify of a touch event
// Return 0 on success, >0 to ignore remainder of touch, and <0 on error
virtual int NotifyTouch(TOUCH_STATE state, int x, int y);
// NotifyVarChange - Notify of a variable change
virtual int NotifyVarChange(const std::string& varName, const std::string& value);
// SetPos - Update the position of the render object
// Return 0 on success, <0 on error
virtual int SetRenderPos(int x, int y, int w = 0, int h = 0);
// SetPageFocus - Notify when a page gains or loses focus
virtual void SetPageFocus(int inFocus);
virtual size_t GetItemCount();
virtual int GetListItem(size_t item_index, Resource*& icon, std::string &text);
virtual void NotifySelect(size_t item_selected);
protected:
struct ListData {
std::string displayName;
@@ -641,90 +697,38 @@ protected:
unsigned int selected;
};
protected:
virtual int GetSelection(int x, int y);
protected:
std::vector<ListData> mList;
std::string mVariable;
std::string mSelection;
std::string currentValue;
std::string mHeaderText;
std::string mLastValue;
int actualLineHeight;
int mStart;
int startY;
int mSeparatorH, mHeaderSeparatorH;
int mLineSpacing;
int mUpdate;
int mBackgroundX, mBackgroundY, mBackgroundW, mBackgroundH, mHeaderH;
int mFastScrollW;
int mFastScrollLineW;
int mFastScrollRectW;
int mFastScrollRectH;
int mFastScrollRectX;
int mFastScrollRectY;
int mIconWidth, mIconHeight, mSelectedIconWidth, mSelectedIconHeight, mUnselectedIconWidth, mUnselectedIconHeight, mHeaderIconHeight, mHeaderIconWidth;
int scrollingSpeed;
int scrollingY;
static int mSortOrder;
unsigned mFontHeight;
unsigned mLineHeight;
Resource* mHeaderIcon;
Resource* mIconSelected;
Resource* mIconUnselected;
Resource* mBackground;
Resource* mFont;
COLOR mBackgroundColor;
COLOR mFontColor;
COLOR mHeaderBackgroundColor;
COLOR mHeaderFontColor;
COLOR mSeparatorColor;
COLOR mHeaderSeparatorColor;
COLOR mFastScrollLineColor;
COLOR mFastScrollRectColor;
bool hasHighlightColor;
bool hasFontHighlightColor;
bool isHighlighted;
COLOR mHighlightColor;
COLOR mFontHighlightColor;
int mHeaderIsStatic;
int startSelection;
int touchDebounce;
};
class GUIPartitionList : public GUIObject, public RenderObject, public ActionObject
class GUIPartitionList : public GUIScrollList
{
public:
GUIPartitionList(xml_node<>* node);
virtual ~GUIPartitionList();
public:
// Render - Render the full object to the GL surface
// Return 0 on success, <0 on error
virtual int Render(void);
// Update - Update any UI component animations (called <= 30 FPS)
// Return 0 if nothing to update, 1 on success and contiue, >1 if full render required, and <0 on error
virtual int Update(void);
// NotifyTouch - Notify of a touch event
// Return 0 on success, >0 to ignore remainder of touch, and <0 on error
virtual int NotifyTouch(TOUCH_STATE state, int x, int y);
virtual int Update();
// NotifyVarChange - Notify of a variable change
virtual int NotifyVarChange(const std::string& varName, const std::string& value);
// SetPos - Update the position of the render object
// Return 0 on success, <0 on error
virtual int SetRenderPos(int x, int y, int w = 0, int h = 0);
// SetPageFocus - Notify when a page gains or loses focus
virtual void SetPageFocus(int inFocus);
virtual size_t GetItemCount();
virtual int GetListItem(size_t item_index, Resource*& icon, std::string &text);
virtual void NotifySelect(size_t item_selected);
protected:
virtual int GetSelection(int x, int y);
virtual void MatchList(void);
void MatchList();
void SetStoragePosition();
protected:
std::vector<PartitionList> mList;
@@ -732,48 +736,9 @@ protected:
std::string mVariable;
std::string selectedList;
std::string currentValue;
std::string mHeaderText;
std::string mLastValue;
int actualLineHeight;
int mStart;
int startY;
int mSeparatorH, mHeaderSeparatorH;
int mLineSpacing;
int mUpdate;
int mBackgroundX, mBackgroundY, mBackgroundW, mBackgroundH, mHeaderH;
int mFastScrollW;
int mFastScrollLineW;
int mFastScrollRectW;
int mFastScrollRectH;
int mFastScrollRectX;
int mFastScrollRectY;
int mIconWidth, mIconHeight, mSelectedIconWidth, mSelectedIconHeight, mUnselectedIconWidth, mUnselectedIconHeight, mHeaderIconHeight, mHeaderIconWidth;
int scrollingSpeed;
int scrollingY;
static int mSortOrder;
unsigned mFontHeight;
unsigned mLineHeight;
Resource* mHeaderIcon;
Resource* mIconSelected;
Resource* mIconUnselected;
Resource* mBackground;
Resource* mFont;
COLOR mBackgroundColor;
COLOR mFontColor;
COLOR mHeaderBackgroundColor;
COLOR mHeaderFontColor;
COLOR mSeparatorColor;
COLOR mHeaderSeparatorColor;
COLOR mFastScrollLineColor;
COLOR mFastScrollRectColor;
bool hasHighlightColor;
bool hasFontHighlightColor;
bool isHighlighted;
COLOR mHighlightColor;
COLOR mFontHighlightColor;
int mHeaderIsStatic;
int startSelection;
int touchDebounce;
bool updateList;
};

File diff suppressed because it is too large Load Diff

709
gui/scrolllist.cpp Normal file
View File

@@ -0,0 +1,709 @@
/*
Copyright 2013 bigbiff/Dees_Troy TeamWin
This file is part of TWRP/TeamWin Recovery Project.
TWRP is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
TWRP is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with TWRP. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
extern "C" {
#include "../twcommon.h"
#include "../minuitwrp/minui.h"
}
#include "rapidxml.hpp"
#include "objects.hpp"
#include "../data.hpp"
const int SCROLLING_SPEED_DECREMENT = 12; // friction
const int SCROLLING_FLOOR = 10; // minimum pixels for scrolling to start or stop
const int SCROLLING_MULTIPLIER = 2; // initial speed of kinetic scrolling
const float SCROLLING_SPEED_LIMIT = 2.5; // maximum number of items to scroll per update
GUIScrollList::GUIScrollList(xml_node<>* node) : GUIObject(node)
{
xml_attribute<>* attr;
xml_node<>* child;
int header_separator_color_specified = 0, header_separator_height_specified = 0, header_text_color_specified = 0, header_background_color_specified = 0;
firstDisplayedItem = mItemSpacing = mFontHeight = mSeparatorH = y_offset = scrollingSpeed = 0;
maxIconWidth = maxIconHeight = mHeaderIconHeight = mHeaderIconWidth = 0;
mHeaderSeparatorH = mHeaderIsStatic = mHeaderH = actualItemHeight = 0;
mBackground = mFont = mHeaderIcon = NULL;
mBackgroundW = mBackgroundH = 0;
mFastScrollW = mFastScrollLineW = mFastScrollRectW = mFastScrollRectH = 0;
lastY = last2Y = fastScroll = 0;
mUpdate = 0;
touchDebounce = 6;
ConvertStrToColor("black", &mBackgroundColor);
ConvertStrToColor("black", &mHeaderBackgroundColor);
ConvertStrToColor("black", &mSeparatorColor);
ConvertStrToColor("black", &mHeaderSeparatorColor);
ConvertStrToColor("white", &mFontColor);
ConvertStrToColor("white", &mHeaderFontColor);
ConvertStrToColor("white", &mFastScrollLineColor);
ConvertStrToColor("white", &mFastScrollRectColor);
hasHighlightColor = false;
hasFontHighlightColor = false;
selectedItem = NO_ITEM;
// Load header text
child = node->first_node("header");
if (child)
{
attr = child->first_attribute("icon");
if (attr)
mHeaderIcon = PageManager::FindResource(attr->value());
attr = child->first_attribute("background");
if (attr)
{
std::string color = attr->value();
ConvertStrToColor(color, &mHeaderBackgroundColor);
header_background_color_specified = -1;
}
attr = child->first_attribute("textcolor");
if (attr)
{
std::string color = attr->value();
ConvertStrToColor(color, &mHeaderFontColor);
header_text_color_specified = -1;
}
attr = child->first_attribute("separatorcolor");
if (attr)
{
std::string color = attr->value();
ConvertStrToColor(color, &mHeaderSeparatorColor);
header_separator_color_specified = -1;
}
attr = child->first_attribute("separatorheight");
if (attr) {
string parsevalue = gui_parse_text(attr->value());
mHeaderSeparatorH = atoi(parsevalue.c_str());
header_separator_height_specified = -1;
}
}
child = node->first_node("text");
if (child) mHeaderText = child->value();
memset(&mHighlightColor, 0, sizeof(COLOR));
child = node->first_node("highlight");
if (child) {
attr = child->first_attribute("color");
if (attr) {
hasHighlightColor = true;
std::string color = attr->value();
ConvertStrToColor(color, &mHighlightColor);
}
}
// Simple way to check for static state
mLastHeaderValue = gui_parse_text(mHeaderText);
if (mLastHeaderValue != mHeaderText)
mHeaderIsStatic = 0;
else
mHeaderIsStatic = -1;
child = node->first_node("background");
if (child)
{
attr = child->first_attribute("resource");
if (attr)
mBackground = PageManager::FindResource(attr->value());
attr = child->first_attribute("color");
if (attr)
{
std::string color = attr->value();
ConvertStrToColor(color, &mBackgroundColor);
if (!header_background_color_specified)
ConvertStrToColor(color, &mHeaderBackgroundColor);
}
}
// Load the placement
LoadPlacement(node->first_node("placement"), &mRenderX, &mRenderY, &mRenderW, &mRenderH);
SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
// Load the font, and possibly override the color
child = node->first_node("font");
if (child)
{
attr = child->first_attribute("resource");
if (attr)
mFont = PageManager::FindResource(attr->value());
attr = child->first_attribute("color");
if (attr)
{
std::string color = attr->value();
ConvertStrToColor(color, &mFontColor);
if (!header_text_color_specified)
ConvertStrToColor(color, &mHeaderFontColor);
}
attr = child->first_attribute("spacing");
if (attr) {
string parsevalue = gui_parse_text(attr->value());
mItemSpacing = atoi(parsevalue.c_str());
}
attr = child->first_attribute("highlightcolor");
memset(&mFontHighlightColor, 0, sizeof(COLOR));
if (attr)
{
std::string color = attr->value();
ConvertStrToColor(color, &mFontHighlightColor);
hasFontHighlightColor = true;
}
}
// Load the separator if it exists
child = node->first_node("separator");
if (child)
{
attr = child->first_attribute("color");
if (attr)
{
std::string color = attr->value();
ConvertStrToColor(color, &mSeparatorColor);
if (!header_separator_color_specified)
ConvertStrToColor(color, &mHeaderSeparatorColor);
}
attr = child->first_attribute("height");
if (attr) {
string parsevalue = gui_parse_text(attr->value());
mSeparatorH = atoi(parsevalue.c_str());
if (!header_separator_height_specified)
mHeaderSeparatorH = mSeparatorH;
}
}
// Fast scroll colors
child = node->first_node("fastscroll");
if (child)
{
attr = child->first_attribute("linecolor");
if(attr)
ConvertStrToColor(attr->value(), &mFastScrollLineColor);
attr = child->first_attribute("rectcolor");
if(attr)
ConvertStrToColor(attr->value(), &mFastScrollRectColor);
attr = child->first_attribute("w");
if (attr) {
string parsevalue = gui_parse_text(attr->value());
mFastScrollW = atoi(parsevalue.c_str());
}
attr = child->first_attribute("linew");
if (attr) {
string parsevalue = gui_parse_text(attr->value());
mFastScrollLineW = atoi(parsevalue.c_str());
}
attr = child->first_attribute("rectw");
if (attr) {
string parsevalue = gui_parse_text(attr->value());
mFastScrollRectW = atoi(parsevalue.c_str());
}
attr = child->first_attribute("recth");
if (attr) {
string parsevalue = gui_parse_text(attr->value());
mFastScrollRectH = atoi(parsevalue.c_str());
}
}
// Retrieve the line height
mFontHeight = gr_getMaxFontHeight(mFont ? mFont->GetResource() : NULL);
mHeaderH = mFontHeight;
if (mHeaderIcon && mHeaderIcon->GetResource())
{
mHeaderIconWidth = gr_get_width(mHeaderIcon->GetResource());
mHeaderIconHeight = gr_get_height(mHeaderIcon->GetResource());
if (mHeaderIconHeight > mHeaderH)
mHeaderH = mHeaderIconHeight;
if (mHeaderIconWidth > maxIconWidth)
maxIconWidth = mHeaderIconWidth;
}
mHeaderH += mItemSpacing + mHeaderSeparatorH;
actualItemHeight = mFontHeight + mItemSpacing + mSeparatorH;
if (mHeaderH < actualItemHeight)
mHeaderH = actualItemHeight;
if (actualItemHeight / 3 > 6)
touchDebounce = actualItemHeight / 3;
if (mBackground && mBackground->GetResource())
{
mBackgroundW = gr_get_width(mBackground->GetResource());
mBackgroundH = gr_get_height(mBackground->GetResource());
}
}
GUIScrollList::~GUIScrollList()
{
delete mHeaderIcon;
delete mBackground;
delete mFont;
}
void GUIScrollList::SetMaxIconSize(int w, int h)
{
if (w > maxIconWidth)
maxIconWidth = w;
if (h > maxIconHeight)
maxIconHeight = h;
if (maxIconHeight > mFontHeight) {
actualItemHeight = maxIconHeight + mItemSpacing + mSeparatorH;
if (actualItemHeight > mHeaderH)
mHeaderH = actualItemHeight;
}
}
void GUIScrollList::SetVisibleListLocation(size_t list_index)
{
// This will make sure that the item indicated by list_index is visible on the screen
size_t lines = GetDisplayItemCount(), listSize = GetItemCount();
if (list_index <= (unsigned)firstDisplayedItem) {
// list_index is above the currently displayed items, put the selected item at the very top
firstDisplayedItem = list_index;
y_offset = 0;
} else if (list_index >= firstDisplayedItem + lines) {
// list_index is below the currently displayed items, put the selected item at the very bottom
firstDisplayedItem = list_index - lines + 1;
if (GetDisplayRemainder() != 0) {
// There's a partial row displayed, set the scrolling offset so that the selected 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;
}
}
scrollingSpeed = 0; // stop kinetic scrolling on setting visible location
mUpdate = 1;
}
int GUIScrollList::Render(void)
{
if(!isConditionTrue())
return 0;
// First step, fill background
gr_color(mBackgroundColor.red, mBackgroundColor.green, mBackgroundColor.blue, 255);
gr_fill(mRenderX, mRenderY + mHeaderH, mRenderW, mRenderH - mHeaderH);
// Next, render the background resource (if it exists)
if (mBackground && mBackground->GetResource())
{
int mBackgroundX = mRenderX + ((mRenderW - mBackgroundW) / 2);
int mBackgroundY = mRenderY + ((mRenderH - mBackgroundH) / 2);
gr_blit(mBackground->GetResource(), 0, 0, mBackgroundW, mBackgroundH, mBackgroundX, mBackgroundY);
}
// This tells us how many lines we can actually render
size_t lines = GetDisplayItemCount();
size_t listSize = GetItemCount();
int listW = mRenderW;
if (listSize <= lines) {
hasScroll = false;
scrollingSpeed = 0;
lines = listSize;
y_offset = 0;
} else {
hasScroll = true;
listW -= mFastScrollW; // space for fast scroll
lines++;
if (lines < listSize)
lines++;
}
void* fontResource = NULL;
if (mFont) fontResource = mFont->GetResource();
int yPos = mRenderY + mHeaderH + y_offset;
int fontOffsetY = (int)((actualItemHeight - mFontHeight) / 2);
// render all visible items
for (size_t line = 0; line < lines; line++)
{
size_t itemindex = line + firstDisplayedItem;
if (itemindex >= listSize)
break;
// get item data
Resource* icon;
std::string label;
if (GetListItem(itemindex, icon, label))
break;
if (hasHighlightColor && itemindex == selectedItem) {
// Highlight the item background of the selected item
gr_color(mHighlightColor.red, mHighlightColor.green, mHighlightColor.blue, 255);
int HighlightHeight = actualItemHeight;
if (yPos + HighlightHeight > mRenderY + mRenderH) {
HighlightHeight = mRenderY + mRenderH - yPos;
}
gr_fill(mRenderX, yPos, mRenderW, HighlightHeight);
}
if (hasFontHighlightColor && itemindex == selectedItem) {
// Use the highlight color for the font
gr_color(mFontHighlightColor.red, mFontHighlightColor.green, mFontHighlightColor.blue, 255);
} else {
// Set the color for the font
gr_color(mFontColor.red, mFontColor.green, mFontColor.blue, 255);
}
if (icon && icon->GetResource()) {
int currentIconHeight = gr_get_height(icon->GetResource());
int currentIconWidth = gr_get_width(icon->GetResource());
int currentIconOffsetY = (int)((actualItemHeight - currentIconHeight) / 2);
int currentIconOffsetX = (maxIconWidth - currentIconWidth) / 2;
int rect_y = 0, image_y = (yPos + currentIconOffsetY);
if (image_y + currentIconHeight > mRenderY + mRenderH)
rect_y = mRenderY + mRenderH - image_y;
else
rect_y = currentIconHeight;
gr_blit(icon->GetResource(), 0, 0, currentIconWidth, rect_y, mRenderX + currentIconOffsetX, image_y);
}
gr_textExWH(mRenderX + maxIconWidth + 5, yPos + fontOffsetY, label.c_str(), fontResource, mRenderX + listW, mRenderY + mRenderH);
// Add the separator
if (yPos + actualItemHeight < mRenderH + mRenderY) {
gr_color(mSeparatorColor.red, mSeparatorColor.green, mSeparatorColor.blue, 255);
gr_fill(mRenderX, yPos + actualItemHeight - mSeparatorH, listW, mSeparatorH);
}
// Move the yPos
yPos += actualItemHeight;
}
// Render the Header (last so that it overwrites the top most row for per pixel scrolling)
// First step, fill background
gr_color(mHeaderBackgroundColor.red, mHeaderBackgroundColor.green, mHeaderBackgroundColor.blue, 255);
gr_fill(mRenderX, mRenderY, mRenderW, mHeaderH);
// Now, we need the header (icon + text)
yPos = mRenderY;
{
Resource* headerIcon;
int mIconOffsetX = 0;
// render the icon if it exists
headerIcon = mHeaderIcon;
if (headerIcon && headerIcon->GetResource())
{
gr_blit(headerIcon->GetResource(), 0, 0, mHeaderIconWidth, mHeaderIconHeight, mRenderX + ((mHeaderIconWidth - maxIconWidth) / 2), (yPos + (int)((mHeaderH - mHeaderIconHeight) / 2)));
mIconOffsetX = maxIconWidth;
}
// render the text
gr_color(mHeaderFontColor.red, mHeaderFontColor.green, mHeaderFontColor.blue, 255);
gr_textExWH(mRenderX + mIconOffsetX + 5, yPos + (int)((mHeaderH - mFontHeight) / 2), mLastHeaderValue.c_str(), fontResource, mRenderX + mRenderW, mRenderY + mRenderH);
// Add the separator
gr_color(mHeaderSeparatorColor.red, mHeaderSeparatorColor.green, mHeaderSeparatorColor.blue, 255);
gr_fill(mRenderX, yPos + mHeaderH - mHeaderSeparatorH, mRenderW, mHeaderSeparatorH);
}
// render fast scroll
lines = GetDisplayItemCount();
if (hasScroll) {
int startX = listW + mRenderX;
int fWidth = mRenderW - listW;
int fHeight = mRenderH - mHeaderH;
// line
gr_color(mFastScrollLineColor.red, mFastScrollLineColor.green, mFastScrollLineColor.blue, 255);
gr_fill(startX + fWidth/2, mRenderY + mHeaderH, mFastScrollLineW, mRenderH - mHeaderH);
// 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;
gr_color(mFastScrollRectColor.red, mFastScrollRectColor.green, mFastScrollRectColor.blue, 255);
gr_fill(mFastScrollRectX, mFastScrollRectY, mFastScrollRectW, mFastScrollRectH);
}
mUpdate = 0;
return 0;
}
int GUIScrollList::Update(void)
{
if(!isConditionTrue())
return 0;
if (!mHeaderIsStatic) {
std::string newValue = gui_parse_text(mHeaderText);
if (mLastHeaderValue != newValue) {
mLastHeaderValue = newValue;
mUpdate = 1;
}
}
// Handle kinetic scrolling
int maxScrollDistance = actualItemHeight * SCROLLING_SPEED_LIMIT;
if (scrollingSpeed == 0) {
// Do nothing
return 0;
} else if (scrollingSpeed > 0) {
if (scrollingSpeed < maxScrollDistance)
y_offset += scrollingSpeed;
else
y_offset += maxScrollDistance;
scrollingSpeed -= SCROLLING_SPEED_DECREMENT;
} else if (scrollingSpeed < 0) {
if (abs(scrollingSpeed) < maxScrollDistance)
y_offset += scrollingSpeed;
else
y_offset -= maxScrollDistance;
scrollingSpeed += SCROLLING_SPEED_DECREMENT;
}
if (abs(scrollingSpeed) < SCROLLING_FLOOR)
scrollingSpeed = 0;
HandleScrolling();
mUpdate = 1;
return 0;
}
size_t GUIScrollList::HitTestItem(int x, int y)
{
// We only care about y position
if (y < mRenderY || y - mRenderY <= mHeaderH || y - mRenderY > mRenderH)
return NO_ITEM;
int startSelection = (y - mRenderY - mHeaderH);
// Locate the correct item
size_t actualSelection = firstDisplayedItem;
int selectY = y_offset;
while (selectY + actualItemHeight < startSelection) {
selectY += actualItemHeight;
actualSelection++;
}
if (actualSelection < GetItemCount())
return actualSelection;
return NO_ITEM;
}
int GUIScrollList::NotifyTouch(TOUCH_STATE state, int x, int y)
{
if(!isConditionTrue())
return -1;
switch (state)
{
case TOUCH_START:
if (hasScroll && x >= mRenderX + mRenderW - mFastScrollW)
fastScroll = 1; // Initial touch is in the fast scroll region
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
} else if (!fastScroll) {
// find out which item the user touched
selectedItem = HitTestItem(x, y);
}
if (selectedItem != NO_ITEM)
mUpdate = 1;
lastY = last2Y = y;
break;
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;
}
selectedItem = NO_ITEM;
mUpdate = 1;
scrollingSpeed = 0; // prevent kinetic scrolling when using fast scroll
break;
}
// Provide some debounce on initial touches
if (selectedItem != NO_ITEM && abs(y - lastY) < touchDebounce) {
mUpdate = 1;
break;
}
selectedItem = NO_ITEM; // nothing is selected because we dragged too far
// Handle scrolling
if (hasScroll) {
y_offset += y - lastY; // adjust the scrolling offset based on the difference between the starting touch and the current touch
last2Y = lastY; // keep track of previous y locations so that we can tell how fast to scroll for kinetic scrolling
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
HandleScrolling();
} else
y_offset = 0;
mUpdate = 1;
break;
case TOUCH_RELEASE:
fastScroll = 0;
if (selectedItem != NO_ITEM) {
// We've selected an item!
NotifySelect(selectedItem);
mUpdate = 1;
DataManager::Vibrate("tw_button_vibrate");
selectedItem = NO_ITEM;
} else {
// Start kinetic scrolling
scrollingSpeed = lastY - last2Y;
if (abs(scrollingSpeed) > SCROLLING_FLOOR)
scrollingSpeed *= SCROLLING_MULTIPLIER;
else
scrollingSpeed = 0;
}
case TOUCH_REPEAT:
case TOUCH_HOLD:
break;
}
return 0;
}
void GUIScrollList::HandleScrolling()
{
// handle dragging downward, scrolling upward
// the offset should always be <= 0 and > -actualItemHeight, adjust the first display row and offset as needed
while(firstDisplayedItem && y_offset > 0) {
firstDisplayedItem--;
y_offset -= actualItemHeight;
}
if (firstDisplayedItem == 0 && y_offset > 0)
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
// handle dragging upward, scrolling downward
int totalSize = GetItemCount();
int lines = GetDisplayItemCount(); // number of full lines our list can display at once
int bottom_offset = GetDisplayRemainder() - actualItemHeight; // extra display area that can display a partial line for per pixel scrolling
// the offset should always be <= 0 and > -actualItemHeight, adjust the first display row and offset as needed
while (firstDisplayedItem + lines + (bottom_offset ? 1 : 0) < totalSize && abs(y_offset) > actualItemHeight) {
firstDisplayedItem++;
y_offset += actualItemHeight;
}
// Check if we dragged too far, set the list at the bottom and adjust offset as needed
if (bottom_offset != 0 && firstDisplayedItem + lines + 1 >= totalSize && y_offset <= bottom_offset) {
firstDisplayedItem = totalSize - lines - 1;
y_offset = bottom_offset;
} else if (firstDisplayedItem + lines >= totalSize && y_offset < 0) {
firstDisplayedItem = totalSize - lines;
y_offset = 0;
}
}
int GUIScrollList::GetDisplayItemCount()
{
return (mRenderH - mHeaderH) / (actualItemHeight);
}
int GUIScrollList::GetDisplayRemainder()
{
return (mRenderH - mHeaderH) % actualItemHeight;
}
int GUIScrollList::NotifyVarChange(const std::string& varName, const std::string& value)
{
GUIObject::NotifyVarChange(varName, value);
if(!isConditionTrue())
return 0;
if (!mHeaderIsStatic) {
std::string newValue = gui_parse_text(mHeaderText);
if (mLastHeaderValue != newValue) {
mLastHeaderValue = newValue;
firstDisplayedItem = 0;
y_offset = 0;
scrollingSpeed = 0; // stop kinetic scrolling on variable changes
mUpdate = 1;
}
}
return 0;
}
int GUIScrollList::SetRenderPos(int x, int y, int w /* = 0 */, int h /* = 0 */)
{
mRenderX = x;
mRenderY = y;
if (w || h)
{
mRenderW = w;
mRenderH = h;
}
SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
mUpdate = 1;
return 0;
}
void GUIScrollList::SetPageFocus(int inFocus)
{
if (inFocus) {
NotifyVarChange("", ""); // This forces a check for the header text
scrollingSpeed = 0; // stop kinetic scrolling on page changes
mUpdate = 1;
}
}