diff options
Diffstat (limited to 'gui/scrolllist.cpp')
-rw-r--r-- | gui/scrolllist.cpp | 709 |
1 files changed, 709 insertions, 0 deletions
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; + } +} |