diff options
-rw-r--r-- | gui/Android.mk | 3 | ||||
-rw-r--r-- | gui/fileselector.cpp | 900 | ||||
-rw-r--r-- | gui/listbox.cpp | 771 | ||||
-rw-r--r-- | gui/objects.hpp | 295 | ||||
-rw-r--r-- | gui/partitionlist.cpp | 929 | ||||
-rw-r--r-- | gui/scrolllist.cpp | 709 |
6 files changed, 1173 insertions, 2434 deletions
diff --git a/gui/Android.mk b/gui/Android.mk index fca5e0787..93913f55a 100644 --- a/gui/Android.mk +++ b/gui/Android.mk @@ -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) diff --git a/gui/fileselector.cpp b/gui/fileselector.cpp index cf7a9a908..c7805c42a 100644 --- a/gui/fileselector.cpp +++ b/gui/fileselector.cpp @@ -16,25 +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" { @@ -49,188 +33,23 @@ extern "C" { #define TW_FILESELECTOR_UP_A_LEVEL "(Up A Level)" -#define SCROLLING_SPEED_DECREMENT 6 -#define SCROLLING_FLOOR 10 -#define SCROLLING_MULTIPLIER 6 - int GUIFileSelector::mSortOrder = 0; -GUIFileSelector::GUIFileSelector(xml_node<>* node) : GUIObject(node) +GUIFileSelector::GUIFileSelector(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 = mFolderIconHeight = mFileIconHeight = mFolderIconWidth = mFileIconWidth = mHeaderIconHeight = mHeaderIconWidth = 0; - mHeaderSeparatorH = mLineHeight = mHeaderIsStatic = mHeaderH = actualLineHeight = 0; - mFolderIcon = mFileIcon = mBackground = mFont = mHeaderIcon = NULL; - mBackgroundX = mBackgroundY = mBackgroundW = mBackgroundH = 0; + int mIconWidth = 0, mIconHeight = 0, mFolderIconHeight = 0, mFileIconHeight = 0, mFolderIconWidth = 0, mFileIconWidth = 0; + mFolderIcon = mFileIcon = NULL; mShowFolders = mShowFiles = mShowNavFolders = 1; - mFastScrollW = mFastScrollLineW = mFastScrollRectW = mFastScrollRectH = 0; - mFastScrollRectX = mFastScrollRectY = -1; mUpdate = 0; - touchDebounce = 6; mPathVar = "cwd"; - 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; updateFileList = 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; - - child = node->first_node("icon"); - if (child) - { - attr = child->first_attribute("folder"); - if (attr) - mFolderIcon = PageManager::FindResource(attr->value()); - attr = child->first_attribute("file"); - if (attr) - mFileIcon = 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); - } - } - - // 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; - } - } + // Load filter for filtering files (e.g. *.zip for only zips) child = node->first_node("filter"); - if (child) - { + if (child) { attr = child->first_attribute("extn"); if (attr) mExtn = attr->value(); @@ -247,8 +66,7 @@ GUIFileSelector::GUIFileSelector(xml_node<>* node) : GUIObject(node) // Handle the path variable child = node->first_node("path"); - if (child) - { + if (child) { attr = child->first_attribute("name"); if (attr) mPathVar = attr->value(); @@ -259,8 +77,7 @@ GUIFileSelector::GUIFileSelector(xml_node<>* node) : GUIObject(node) // Handle the result variable child = node->first_node("data"); - if (child) - { + if (child) { attr = child->first_attribute("name"); if (attr) mVariable = attr->value(); @@ -271,8 +88,7 @@ GUIFileSelector::GUIFileSelector(xml_node<>* node) : GUIObject(node) // Handle the sort variable child = node->first_node("sort"); - if (child) - { + if (child) { attr = child->first_attribute("name"); if (attr) mSortVariable = attr->value(); @@ -285,100 +101,37 @@ GUIFileSelector::GUIFileSelector(xml_node<>* node) : GUIObject(node) // Handle the selection variable child = node->first_node("selection"); - if (child) - { - attr = child->first_attribute("name"); - if (attr) - mSelection = attr->value(); - else - mSelection = "0"; - } else + if (child && (attr = child->first_attribute("name"))) + mSelection = attr->value(); + else mSelection = "0"; - // 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()); - } + // Get folder and file icons if present + child = node->first_node("icon"); + if (child) { + attr = child->first_attribute("folder"); + if (attr) + mFolderIcon = PageManager::FindResource(attr->value()); + attr = child->first_attribute("file"); + if (attr) + mFileIcon = PageManager::FindResource(attr->value()); } - - // Retrieve the line height - mFontHeight = gr_getMaxFontHeight(mFont ? mFont->GetResource() : NULL); - mLineHeight = mFontHeight; - mHeaderH = mFontHeight; - - if (mFolderIcon && mFolderIcon->GetResource()) - { + if (mFolderIcon && mFolderIcon->GetResource()) { mFolderIconWidth = gr_get_width(mFolderIcon->GetResource()); mFolderIconHeight = gr_get_height(mFolderIcon->GetResource()); - if (mFolderIconHeight > (int)mLineHeight) - mLineHeight = mFolderIconHeight; + if (mFolderIconHeight > mIconHeight) + mIconHeight = mFolderIconHeight; mIconWidth = mFolderIconWidth; } - - if (mFileIcon && mFileIcon->GetResource()) - { + if (mFileIcon && mFileIcon->GetResource()) { mFileIconWidth = gr_get_width(mFileIcon->GetResource()); mFileIconHeight = gr_get_height(mFileIcon->GetResource()); - if (mFileIconHeight > (int)mLineHeight) - mLineHeight = mFileIconHeight; + if (mFileIconHeight > mIconHeight) + mIconHeight = mFileIconHeight; if (mFileIconWidth > mIconWidth) mIconWidth = mFileIconWidth; } - - 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()); - } + SetMaxIconSize(mIconWidth, mIconHeight); // Fetch the file/folder list std::string value; @@ -388,533 +141,61 @@ GUIFileSelector::GUIFileSelector(xml_node<>* node) : GUIObject(node) GUIFileSelector::~GUIFileSelector() { + delete mFileIcon; + delete mFolderIcon; } -int GUIFileSelector::Render(void) +int GUIFileSelector::Update(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); - } + GUIScrollList::Update(); // Update the file list if needed if (updateFileList) { string value; DataManager::GetValue(mPathVar, value); - if (GetFileList(value) == 0) + if (GetFileList(value) == 0) { updateFileList = false; - else - return 0; - } - - // This tells us how many lines we can actually render - int lines = (mRenderH - mHeaderH) / (actualLineHeight); - int line; - - int folderSize = mShowFolders ? mFolderList.size() : 0; - int fileSize = mShowFiles ? mFileList.size() : 0; - - int listW = mRenderW; - - if (folderSize + fileSize < lines) { - lines = folderSize + fileSize; - scrollingY = 0; - mFastScrollRectX = mFastScrollRectY = -1; - } else { - listW -= mFastScrollW; // space for fast scroll - lines++; - if (lines < folderSize + fileSize) - 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 folderIconOffsetY = (int)((actualLineHeight - mFolderIconHeight) / 2), fileIconOffsetY = (int)((actualLineHeight - mFileIconHeight) / 2); - int folderIconOffsetX = (mIconWidth - mFolderIconWidth) / 2, fileIconOffsetX = (mIconWidth - mFileIconWidth) / 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 (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 (line + mStart < folderSize) - { - icon = mFolderIcon; - label = mFolderList.at(line + mStart).fileName; - currentIconHeight = mFolderIconHeight; - currentIconWidth = mFolderIconWidth; - currentIconOffsetY = folderIconOffsetY; - currentIconOffsetX = folderIconOffsetX; - } - else if (line + mStart < folderSize + fileSize) - { - icon = mFileIcon; - label = mFileList.at((line + mStart) - folderSize).fileName; - currentIconHeight = mFileIconHeight; - currentIconWidth = mFileIconWidth; - currentIconOffsetY = fileIconOffsetY; - currentIconOffsetX = fileIconOffsetX; - } else { - continue; - } - - 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 && folderSize + fileSize > 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)/((folderSize + fileSize)*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); - } - - // If a change came in during the render then we need to do another redraw so leave mUpdate alone if updateFileList is true. - if (!updateFileList) { - mUpdate = 0; - } - return 0; -} - -int GUIFileSelector::Update(void) -{ - if(!isConditionTrue()) - return 0; - - if (!mHeaderIsStatic) { - std::string newValue = gui_parse_text(mHeaderText); - if (mLastValue != newValue) { - mLastValue = newValue; mUpdate = 1; - } + } else + return 0; } - 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 = (mShowFolders ? mFolderList.size() : 0) + (mShowFiles ? mFileList.size() : 0); - 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 GUIFileSelector::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 GUIFileSelector::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 = (mShowFolders ? mFolderList.size() : 0) + (mShowFiles ? mFileList.size() : 0); - 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 = (mShowFolders ? mFolderList.size() : 0) + (mShowFiles ? mFileList.size() : 0); - 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 folderSize = mShowFolders ? mFolderList.size() : 0; - int fileSize = mShowFiles ? mFileList.size() : 0; - int selectY = scrollingY, actualSelection = mStart; - - // Move the selection to the proper place in the array - while (selectY + actualLineHeight < startSelection) { - selectY += actualLineHeight; - actualSelection++; - } - startSelection = actualSelection; - - if (startSelection < folderSize + fileSize) - { - DataManager::Vibrate("tw_button_vibrate"); - - if (startSelection < folderSize) - { - std::string oldcwd; - std::string cwd; - - str = mFolderList.at(startSelection).fileName; - if (mSelection != "0") - DataManager::SetValue(mSelection, str); - DataManager::GetValue(mPathVar, cwd); - - oldcwd = cwd; - // Ignore requests to do nothing - if (str == ".") return 0; - if (str == TW_FILESELECTOR_UP_A_LEVEL) - { - if (cwd != "/") - { - size_t found; - found = cwd.find_last_of('/'); - cwd = cwd.substr(0,found); - - if (cwd.length() < 2) cwd = "/"; - } - } - else - { - // Add a slash if we're not the root folder - if (cwd != "/") cwd += "/"; - cwd += str; - } - - if (mShowNavFolders == 0 && mShowFiles == 0) - { - // This is a "folder" selection - DataManager::SetValue(mVariable, cwd); - } - else - { - DataManager::SetValue(mPathVar, cwd); - mStart = 0; - scrollingY = 0; - mUpdate = 1; - } - } - else if (!mVariable.empty()) - { - str = mFileList.at(startSelection - folderSize).fileName; - if (mSelection != "0") - DataManager::SetValue(mSelection, str); - - std::string cwd; - DataManager::GetValue(mPathVar, cwd); - if (cwd != "/") cwd += "/"; - DataManager::SetValue(mVariable, cwd + str); - } - } - } 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 GUIFileSelector::NotifyVarChange(const std::string& varName, const std::string& value) { - GUIObject::NotifyVarChange(varName, value); - if(!isConditionTrue()) return 0; + GUIScrollList::NotifyVarChange(varName, value); + if (varName.empty()) { // Always clear the data variable so we know to use it DataManager::SetValue(mVariable, ""); } - if (!mHeaderIsStatic) { - std::string newValue = gui_parse_text(mHeaderText); - if (mLastValue != newValue) { - mLastValue = newValue; - mStart = 0; - scrollingY = 0; - scrollingSpeed = 0; - mUpdate = 1; - } - } - if (varName == mPathVar || varName == mSortVariable) - { + if (varName == mPathVar || varName == mSortVariable) { if (varName == mSortVariable) { DataManager::GetValue(mSortVariable, mSortOrder); + } else { + // Reset the list to the top + SetVisibleListLocation(0); } updateFileList = true; - mStart = 0; - scrollingY = 0; - scrollingSpeed = 0; mUpdate = 1; return 0; } return 0; } -int GUIFileSelector::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; -} - bool GUIFileSelector::fileSort(FileData d1, FileData d2) { if (d1.fileName == ".") @@ -948,6 +229,7 @@ bool GUIFileSelector::fileSort(FileData d1, FileData d2) default: // should be a 1 - sort by name ascending return (strcasecmp(d1.fileName.c_str(), d2.fileName.c_str()) < 0); } + return 0; } int GUIFileSelector::GetFileList(const std::string folder) @@ -961,8 +243,7 @@ int GUIFileSelector::GetFileList(const std::string folder) mFileList.clear(); d = opendir(folder.c_str()); - if (d == NULL) - { + if (d == NULL) { LOGINFO("Unable to open '%s'\n", folder.c_str()); if (folder != "/" && (mShowNavFolders != 0 || mShowFiles != 0)) { size_t found; @@ -978,8 +259,7 @@ int GUIFileSelector::GetFileList(const std::string folder) return -1; } - while ((de = readdir(d)) != NULL) - { + while ((de = readdir(d)) != NULL) { FileData data; data.fileName = de->d_name; @@ -1007,15 +287,11 @@ int GUIFileSelector::GetFileList(const std::string folder) if (data.fileType == DT_UNKNOWN) { data.fileType = TWFunc::Get_D_Type_From_Stat(path); } - if (data.fileType == DT_DIR) - { + if (data.fileType == DT_DIR) { if (mShowNavFolders || (data.fileName != "." && data.fileName != TW_FILESELECTOR_UP_A_LEVEL)) mFolderList.push_back(data); - } - else if (data.fileType == DT_REG || data.fileType == DT_LNK || data.fileType == DT_BLK) - { - if (mExtn.empty() || (data.fileName.length() > mExtn.length() && data.fileName.substr(data.fileName.length() - mExtn.length()) == mExtn)) - { + } else if (data.fileType == DT_REG || data.fileType == DT_LNK || data.fileType == DT_BLK) { + if (mExtn.empty() || (data.fileName.length() > mExtn.length() && data.fileName.substr(data.fileName.length() - mExtn.length()) == mExtn)) { mFileList.push_back(data); } } @@ -1030,11 +306,85 @@ int GUIFileSelector::GetFileList(const std::string folder) void GUIFileSelector::SetPageFocus(int inFocus) { - if (inFocus) - { + GUIScrollList::SetPageFocus(inFocus); + if (inFocus) { updateFileList = true; - scrollingY = 0; - scrollingSpeed = 0; mUpdate = 1; } } + +size_t GUIFileSelector::GetItemCount() +{ + size_t folderSize = mShowFolders ? mFolderList.size() : 0; + size_t fileSize = mShowFiles ? mFileList.size() : 0; + return folderSize + fileSize; +} + +int GUIFileSelector::GetListItem(size_t item_index, Resource*& icon, std::string &text) +{ + size_t folderSize = mShowFolders ? mFolderList.size() : 0; + size_t fileSize = mShowFiles ? mFileList.size() : 0; + + if (item_index < folderSize) { + text = mFolderList.at(item_index).fileName; + icon = mFolderIcon; + } else { + text = mFileList.at(item_index - folderSize).fileName; + icon = mFileIcon; + } + return 0; +} + +void GUIFileSelector::NotifySelect(size_t item_selected) +{ + size_t folderSize = mShowFolders ? mFolderList.size() : 0; + size_t fileSize = mShowFiles ? mFileList.size() : 0; + + if (item_selected < folderSize + fileSize) { + // We've selected an item! + std::string str; + if (item_selected < folderSize) { + std::string cwd; + + str = mFolderList.at(item_selected).fileName; + if (mSelection != "0") + DataManager::SetValue(mSelection, str); + DataManager::GetValue(mPathVar, cwd); + + // Ignore requests to do nothing + if (str == ".") return; + if (str == TW_FILESELECTOR_UP_A_LEVEL) { + if (cwd != "/") { + size_t found; + found = cwd.find_last_of('/'); + cwd = cwd.substr(0,found); + + if (cwd.length() < 2) cwd = "/"; + } + } else { + // Add a slash if we're not the root folder + if (cwd != "/") cwd += "/"; + cwd += str; + } + + if (mShowNavFolders == 0 && mShowFiles == 0) { + // nav folders and files are disabled, this is probably the restore list and we need to save chosen location to mVariable instead of mPathVar + DataManager::SetValue(mVariable, cwd); + } else { + // We are changing paths, so we need to set mPathVar + DataManager::SetValue(mPathVar, cwd); + } + } else if (!mVariable.empty()) { + str = mFileList.at(item_selected - folderSize).fileName; + if (mSelection != "0") + DataManager::SetValue(mSelection, str); + + std::string cwd; + DataManager::GetValue(mPathVar, cwd); + if (cwd != "/") + cwd += "/"; + DataManager::SetValue(mVariable, cwd + str); + } + } + mUpdate = 1; +} diff --git a/gui/listbox.cpp b/gui/listbox.cpp index 851b37348..545b7a294 100644 --- a/gui/listbox.cpp +++ b/gui/listbox.cpp @@ -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); - } - } - - // 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; - } + 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 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 */) +void GUIListBox::SetPageFocus(int inFocus) { - mRenderX = x; - mRenderY = y; - if (w || h) - { - mRenderW = w; - mRenderH = h; + GUIScrollList::SetPageFocus(inFocus); + if (inFocus) { + DataManager::GetValue(mVariable, currentValue); + NotifyVarChange(mVariable, currentValue); } - SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH); - 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::SetPageFocus(int inFocus) +void GUIListBox::NotifySelect(size_t item_selected) { - if (inFocus) - { - DataManager::GetValue(mVariable, currentValue); - NotifyVarChange(mVariable, currentValue); - mUpdate = 1; + 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; } diff --git a/gui/objects.hpp b/gui/objects.hpp index 3e335615e..0928f3050 100644 --- a/gui/objects.hpp +++ b/gui/objects.hpp @@ -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 @@ -531,6 +537,106 @@ public: 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; unsigned char fileType; // Uses d_type format from struct dirent @@ -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; @@ -642,89 +698,37 @@ protected: }; 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; }; diff --git a/gui/partitionlist.cpp b/gui/partitionlist.cpp index 3ec15488a..48bd295d6 100644 --- a/gui/partitionlist.cpp +++ b/gui/partitionlist.cpp @@ -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,98 +28,17 @@ extern "C" { #include "rapidxml.hpp" #include "objects.hpp" #include "../data.hpp" -#include "../twrp-functions.hpp" #include "../partitions.hpp" -#define SCROLLING_SPEED_DECREMENT 6 -#define SCROLLING_FLOOR 10 -#define SCROLLING_MULTIPLIER 6 - -GUIPartitionList::GUIPartitionList(xml_node<>* node) : GUIObject(node) +GUIPartitionList::GUIPartitionList(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; + + int mIconWidth = 0, mIconHeight = 0, mSelectedIconHeight = 0, mSelectedIconWidth = 0, mUnselectedIconHeight = 0, mUnselectedIconWidth = 0; + mIconSelected = mIconUnselected = NULL; 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; updateList = 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; child = node->first_node("icon"); if (child) @@ -148,80 +50,6 @@ GUIPartitionList::GUIPartitionList(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); - } - } - - // 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; - } - } // Handle the result variable child = node->first_node("data"); @@ -235,54 +63,12 @@ GUIPartitionList::GUIPartitionList(xml_node<>* node) : GUIObject(node) selectedList = attr->value(); } - // 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; + if (mSelectedIconHeight > mIconHeight) + mIconHeight = mSelectedIconHeight; mIconWidth = mSelectedIconWidth; } @@ -290,47 +76,17 @@ GUIPartitionList::GUIPartitionList(xml_node<>* node) : GUIObject(node) { mUnselectedIconWidth = gr_get_width(mIconUnselected->GetResource()); mUnselectedIconHeight = gr_get_height(mIconUnselected->GetResource()); - if (mUnselectedIconHeight > (int)mLineHeight) - mLineHeight = mUnselectedIconHeight; + if (mUnselectedIconHeight > mIconHeight) + mIconHeight = 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()); - } + SetMaxIconSize(mIconWidth, mIconHeight); child = node->first_node("listtype"); - if (child) { - attr = child->first_attribute("name"); - if (attr) { - ListType = attr->value(); - PartitionManager.Get_Partition_List(ListType, &mList); - } else { - mList.clear(); - LOGERR("No partition listtype name specified for partitionlist GUI element\n"); - return; - } + if (child && (attr = child->first_attribute("name"))) { + ListType = attr->value(); + updateList = true; } else { mList.clear(); LOGERR("No partition listtype specified for partitionlist GUI element\n"); @@ -340,190 +96,8 @@ GUIPartitionList::GUIPartitionList(xml_node<>* node) : GUIObject(node) GUIPartitionList::~GUIPartitionList() { -} - -int GUIPartitionList::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; - - if (updateList) { - mList.clear(); - PartitionManager.Get_Partition_List(ListType, &mList); - updateList = false; - if (ListType == "backup") - MatchList(); - } - - int listSize = mList.size(); - int listW = mRenderW; - - if (listSize < lines) { - lines = listSize; - scrollingY = 0; - mFastScrollRectX = mFastScrollRectY = -1; - } else { - lines++; - if (lines < listSize) - lines++; - if (listSize >= lines) - listW -= mFastScrollW; // space for fast scrollbar - else - mFastScrollRectX = mFastScrollRectY = -1; // no fast scrollbar - } - - 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).Display_Name; - 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 GUIPartitionList::Update(void) @@ -531,14 +105,6 @@ int GUIPartitionList::Update(void) if(!isConditionTrue()) return 0; - if (!mHeaderIsStatic) { - std::string newValue = gui_parse_text(mHeaderText); - if (mLastValue != newValue) { - mLastValue = newValue; - mUpdate = 1; - } - } - // Check for changes in mount points if the list type is mount and update the list and render if needed if (ListType == "mount") { int listSize = mList.size(); @@ -553,329 +119,49 @@ int GUIPartitionList::Update(void) } } - 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 GUIPartitionList::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 GUIPartitionList::NotifyTouch(TOUCH_STATE state, int x, int y) -{ - if(!isConditionTrue()) - return -1; - - static int lastY = 0, last2Y = 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; - 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(mFastScrollRectX != -1 && x >= mRenderX + mRenderW - mFastScrollW) - { - 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; - } + GUIScrollList::Update(); - 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; + if (updateList) { + int listSize = 0; - 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; - } + // Completely update the list if needed -- Used primarily for + // restore as the list for restore will change depending on what + // partitions were backed up + mList.clear(); + PartitionManager.Get_Partition_List(ListType, &mList); + SetVisibleListLocation(0); + updateList = false; mUpdate = 1; - break; - - case TOUCH_RELEASE: - isHighlighted = false; - if (startSelection >= 0) - { - // We've selected an item! - 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 && ListType == "mount") { - DataManager::Vibrate("tw_button_vibrate"); - - if (!mList.at(actualSelection).selected) { - if (PartitionManager.Mount_By_Path(mList.at(actualSelection).Mount_Point, true)) { - mList.at(actualSelection).selected = 1; - PartitionManager.Add_MTP_Storage(mList.at(actualSelection).Mount_Point); - mUpdate = 1; - } - } else { - if (PartitionManager.UnMount_By_Path(mList.at(actualSelection).Mount_Point, true)) { - mList.at(actualSelection).selected = 0; - mUpdate = 1; - } - } - } else if (actualSelection < listSize && !mVariable.empty()) { - DataManager::Vibrate("tw_button_vibrate"); - - if (ListType == "storage") { - int i; - std::string str = mList.at(actualSelection).Mount_Point; - bool update_size = false; - TWPartition* Part = PartitionManager.Find_Partition_By_Path(str); - if (Part == NULL) { - LOGERR("Unable to locate partition for '%s'\n", str.c_str()); - return 0; - } - if (!Part->Is_Mounted() && Part->Removable) - update_size = true; - if (!Part->Mount(true)) { - // Do Nothing - } else if (update_size && !Part->Update_Size(true)) { - // Do Nothing - } else { - for (i=0; i<listSize; i++) - mList.at(i).selected = 0; - - if (update_size) { - char free_space[255]; - sprintf(free_space, "%llu", Part->Free / 1024 / 1024); - mList.at(actualSelection).Display_Name = Part->Storage_Name + " ("; - mList.at(actualSelection).Display_Name += free_space; - mList.at(actualSelection).Display_Name += "MB)"; - } - mList.at(actualSelection).selected = 1; - mUpdate = 1; - - DataManager::SetValue(mVariable, str); - } - } else { - if (ListType == "flashimg") { // only one item can be selected for flashing images - for (int i=0; i<listSize; i++) - mList.at(i).selected = 0; - } - if (mList.at(actualSelection).selected) - mList.at(actualSelection).selected = 0; - else - mList.at(actualSelection).selected = 1; - - int i; - string variablelist; - for (i=0; i<listSize; i++) { - if (mList.at(i).selected) { - variablelist += mList.at(i).Mount_Point + ";"; - } - } + if (ListType == "backup") + MatchList(); + } - mUpdate = 1; - if (selectedList.empty()) - DataManager::SetValue(mVariable, variablelist); - else - DataManager::SetValue(selectedList, variablelist); - } - } - } 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; + if (mUpdate) { + mUpdate = 0; + if (Render() == 0) + return 2; } + return 0; } int GUIPartitionList::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; - } - } + GUIScrollList::NotifyVarChange(varName, value); + if (varName == mVariable && !mUpdate) { if (ListType == "storage") { - int i, listSize = mList.size(), selected_index = 0; - currentValue = value; - - for (i=0; i<listSize; i++) { - if (mList.at(i).Mount_Point == currentValue) { - mList.at(i).selected = 1; - selected_index = i; - } else - mList.at(i).selected = 0; - } - - int lines = mRenderH / (mLineHeight + mLineSpacing); - int line; - - if (selected_index > mStart + lines - 1) { - mStart = selected_index; - } else if (selected_index < mStart) { - mStart = selected_index; - } + SetStoragePosition(); } else if (ListType == "backup") { MatchList(); } else if (ListType == "restore") { updateList = true; + SetVisibleListLocation(0); } mUpdate = 1; @@ -884,44 +170,13 @@ int GUIPartitionList::NotifyVarChange(const std::string& varName, const std::str return 0; } -int GUIPartitionList::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 GUIPartitionList::SetPageFocus(int inFocus) { + GUIScrollList::SetPageFocus(inFocus); if (inFocus) { if (ListType == "storage") { - int i, listSize = mList.size(), selected_index = 0; - DataManager::GetValue(mVariable, currentValue); - - for (i=0; i<listSize; i++) { - if (mList.at(i).Mount_Point == currentValue) { - mList.at(i).selected = 1; - selected_index = i; - } else - mList.at(i).selected = 0; - } - - int lines = mRenderH / (mLineHeight + mLineSpacing); - int line; - - if (selected_index > mStart + lines - 1) { - mStart = selected_index; - } else if (selected_index < mStart) { - mStart = selected_index; - } + SetStoragePosition(); } updateList = true; mUpdate = 1; @@ -935,7 +190,7 @@ void GUIPartitionList::MatchList(void) { DataManager::GetValue(mVariable, variablelist); - for (i=0; i<listSize; i++) { + for (i = 0; i < listSize; i++) { searchvalue = mList.at(i).Mount_Point + ";"; pos = variablelist.find(searchvalue); if (pos != string::npos) { @@ -945,3 +200,109 @@ void GUIPartitionList::MatchList(void) { } } } + +void GUIPartitionList::SetStoragePosition() { + int listSize = mList.size(); + + for (int i = 0; i < listSize; i++) { + if (mList.at(i).Mount_Point == currentValue) { + mList.at(i).selected = 1; + SetVisibleListLocation(i); + } else { + mList.at(i).selected = 0; + SetVisibleListLocation(0); + } + } +} + +size_t GUIPartitionList::GetItemCount() +{ + return mList.size(); +} + +int GUIPartitionList::GetListItem(size_t item_index, Resource*& icon, std::string &text) +{ + text = mList.at(item_index).Display_Name; + if (mList.at(item_index).selected) + icon = mIconSelected; + else + icon = mIconUnselected; + return 0; +} + +void GUIPartitionList::NotifySelect(size_t item_selected) +{ + if (item_selected < mList.size()) { + int listSize = mList.size(); + if (ListType == "mount") { + if (!mList.at(item_selected).selected) { + if (PartitionManager.Mount_By_Path(mList.at(item_selected).Mount_Point, true)) { + mList.at(item_selected).selected = 1; + PartitionManager.Add_MTP_Storage(mList.at(item_selected).Mount_Point); + mUpdate = 1; + } + } else { + if (PartitionManager.UnMount_By_Path(mList.at(item_selected).Mount_Point, true)) { + mList.at(item_selected).selected = 0; + mUpdate = 1; + } + } + } else if (!mVariable.empty()) { + if (ListType == "storage") { + int i; + std::string str = mList.at(item_selected).Mount_Point; + bool update_size = false; + TWPartition* Part = PartitionManager.Find_Partition_By_Path(str); + if (Part == NULL) { + LOGERR("Unable to locate partition for '%s'\n", str.c_str()); + return; + } + if (!Part->Is_Mounted() && Part->Removable) + update_size = true; + if (!Part->Mount(true)) { + // Do Nothing + } else if (update_size && !Part->Update_Size(true)) { + // Do Nothing + } else { + for (i=0; i<listSize; i++) + mList.at(i).selected = 0; + + if (update_size) { + char free_space[255]; + sprintf(free_space, "%llu", Part->Free / 1024 / 1024); + mList.at(item_selected).Display_Name = Part->Storage_Name + " ("; + mList.at(item_selected).Display_Name += free_space; + mList.at(item_selected).Display_Name += "MB)"; + } + mList.at(item_selected).selected = 1; + mUpdate = 1; + + DataManager::SetValue(mVariable, str); + } + } else { + if (ListType == "flashimg") { // only one item can be selected for flashing images + for (int i=0; i<listSize; i++) + mList.at(i).selected = 0; + } + if (mList.at(item_selected).selected) + mList.at(item_selected).selected = 0; + else + mList.at(item_selected).selected = 1; + + int i; + string variablelist; + for (i=0; i<listSize; i++) { + if (mList.at(i).selected) { + variablelist += mList.at(i).Mount_Point + ";"; + } + } + + mUpdate = 1; + if (selectedList.empty()) + DataManager::SetValue(mVariable, variablelist); + else + DataManager::SetValue(selectedList, variablelist); + } + } + } +} diff --git a/gui/scrolllist.cpp b/gui/scrolllist.cpp new file mode 100644 index 000000000..9e8db4c93 --- /dev/null +++ b/gui/scrolllist.cpp @@ -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; + } +} |