diff options
Diffstat (limited to 'gui')
-rw-r--r-- | gui/console.cpp | 256 | ||||
-rw-r--r-- | gui/devices/landscape/res/landscape.xml | 1 | ||||
-rw-r--r-- | gui/devices/portrait/res/portrait.xml | 1 | ||||
-rw-r--r-- | gui/devices/watch/res/watch.xml | 1 | ||||
-rw-r--r-- | gui/objects.hpp | 112 | ||||
-rw-r--r-- | gui/pages.cpp | 3 | ||||
-rw-r--r-- | gui/scrolllist.cpp | 16 |
7 files changed, 156 insertions, 234 deletions
diff --git a/gui/console.cpp b/gui/console.cpp index 3623f5558..b1b025c48 100644 --- a/gui/console.cpp +++ b/gui/console.cpp @@ -99,49 +99,37 @@ extern "C" void gui_set_FILE(FILE* f) ors_file = f; } -GUIConsole::GUIConsole(xml_node<>* node) : GUIObject(node) +GUIConsole::GUIConsole(xml_node<>* node) : GUIScrollList(node) { xml_node<>* child; - mFont = NULL; - mCurrentLine = -1; - memset(&mForegroundColor, 255, sizeof(COLOR)); - memset(&mBackgroundColor, 0, sizeof(COLOR)); - mBackgroundColor.alpha = 255; - memset(&mScrollColor, 0x08, sizeof(COLOR)); - mScrollColor.alpha = 255; mLastCount = 0; + scrollToEnd = true; + mSlideoutX = mSlideoutY = mSlideoutW = mSlideoutH = 0; mSlideout = 0; - RenderCount = 0; - mSlideoutState = hidden; - mRender = true; + mSlideoutState = visible; - mRenderX = 0; mRenderY = 0; mRenderW = gr_fb_width(); mRenderH = gr_fb_height(); + allowSelection = false; // console doesn't support list item selections if (!node) { - mSlideoutX = 0; mSlideoutY = 0; mSlideoutW = 0; mSlideoutH = 0; - mConsoleX = 0; mConsoleY = 0; mConsoleW = gr_fb_width(); mConsoleH = gr_fb_height(); + mRenderX = 0; mRenderY = 0; mRenderW = gr_fb_width(); mRenderH = gr_fb_height(); } else { - mFont = LoadAttrFont(FindNode(node, "font"), "resource"); - child = FindNode(node, "color"); if (child) { - mForegroundColor = LoadAttrColor(child, "foreground", mForegroundColor); + mFontColor = LoadAttrColor(child, "foreground", mFontColor); mBackgroundColor = LoadAttrColor(child, "background", mBackgroundColor); - mScrollColor = LoadAttrColor(child, "scroll", mScrollColor); + //mScrollColor = LoadAttrColor(child, "scroll", mScrollColor); } - // Load the placement - LoadPlacement(FindNode(node, "placement"), &mConsoleX, &mConsoleY, &mConsoleW, &mConsoleH); - child = FindNode(node, "slideout"); if (child) { mSlideout = 1; + mSlideoutState = hidden; LoadPlacement(child, &mSlideoutX, &mSlideoutY); mSlideoutImage = LoadAttrImage(child, "resource"); @@ -153,10 +141,6 @@ GUIConsole::GUIConsole(xml_node<>* node) : GUIObject(node) } } } - - mFontHeight = mFont->GetHeight(); - SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH); - SetRenderPos(mConsoleX, mConsoleY); } int GUIConsole::RenderSlideout(void) @@ -168,25 +152,13 @@ int GUIConsole::RenderSlideout(void) return 0; } -int GUIConsole::RenderConsole(void) +bool GUIConsole::AddLines() { - void* fontResource = NULL; - if (mFont) - fontResource = mFont->GetResource(); - - // We fill the background - gr_color(mBackgroundColor.red, mBackgroundColor.green, mBackgroundColor.blue, 255); - gr_fill(mConsoleX, mConsoleY, mConsoleW, mConsoleH); - - gr_color(mScrollColor.red, mScrollColor.green, mScrollColor.blue, mScrollColor.alpha); - gr_fill(mConsoleX + (mConsoleW * 9 / 10), mConsoleY, (mConsoleW / 10), mConsoleH); + if (mLastCount == gConsole.size()) + return false; // nothing to add - // Don't try to continue to render without data size_t prevCount = mLastCount; mLastCount = gConsole.size(); - mRender = false; - if (mLastCount == 0) - return (mSlideout ? RenderSlideout() : 0); // Due to word wrap, figure out what / how the newly added text needs to be added to the render vector that is word wrapped // Note, that multiple consoles on different GUI pages may be different widths or use different fonts, so the word wrapping @@ -195,7 +167,7 @@ int GUIConsole::RenderConsole(void) string curr_line = gConsole[i]; string curr_color = gConsoleColor[i]; for(;;) { - size_t line_char_width = gr_maxExW(curr_line.c_str(), fontResource, mConsoleW); + size_t line_char_width = gr_maxExW(curr_line.c_str(), mFont->GetResource(), mRenderW); if (line_char_width < curr_line.size()) { rConsole.push_back(curr_line.substr(0, line_char_width)); rConsoleColor.push_back(curr_color); @@ -207,41 +179,29 @@ int GUIConsole::RenderConsole(void) } } } - RenderCount = rConsole.size(); - - // Find the start point - int start; - int curLine = mCurrentLine; // Thread-safing (Another thread updates this value) - if (curLine == -1) // follow tail - { - start = RenderCount - mMaxRows; - } - else - { - if (curLine > (int) RenderCount) - curLine = (int) RenderCount; - if ((int) mMaxRows > curLine) - curLine = (int) mMaxRows; - start = curLine - mMaxRows; - } + return true; +} - // note: start can be negative here - for (int line = 0; line < mMaxRows; line++) - { - int index = start + line; - if (index >= 0 && index < (int) RenderCount) { - if (rConsoleColor[index] == "normal") { - gr_color(mForegroundColor.red, mForegroundColor.green, mForegroundColor.blue, mForegroundColor.alpha); - } else { - COLOR mFontColor; - std::string color = rConsoleColor[index]; - ConvertStrToColor(color, &mFontColor); - mFontColor.alpha = 255; - gr_color(mFontColor.red, mFontColor.green, mFontColor.blue, mFontColor.alpha); - } - gr_textExW(mConsoleX, mStartY + (line * mFontHeight), rConsole[index].c_str(), fontResource, mConsoleW + mConsoleX); - } +int GUIConsole::RenderConsole(void) +{ + AddLines(); + GUIScrollList::Render(); + + // if last line is fully visible, keep tracking the last line when new lines are added + int bottom_offset = GetDisplayRemainder() - actualItemHeight; + bool isAtBottom = firstDisplayedItem == GetItemCount() - GetDisplayItemCount() - (bottom_offset != 0) && y_offset == bottom_offset; + if (isAtBottom) + scrollToEnd = true; +#if 0 + // debug - show if we are tracking the last line + if (scrollToEnd) { + gr_color(0,255,0,255); + gr_fill(mRenderX+mRenderW-5, mRenderY+mRenderH-5, 5, 5); + } else { + gr_color(255,0,0,255); + gr_fill(mRenderX+mRenderW-5, mRenderY+mRenderH-5, 5, 5); } +#endif return (mSlideout ? RenderSlideout() : 0); } @@ -258,9 +218,6 @@ int GUIConsole::Render(void) int GUIConsole::Update(void) { - if(!isConditionTrue()) - return 0; - if (mSlideout && mSlideoutState != visible) { if (mSlideoutState == hidden) @@ -272,45 +229,30 @@ int GUIConsole::Update(void) if (mSlideoutState == request_show) mSlideoutState = visible; - // Any time we activate the slider, we reset the position - mCurrentLine = -1; - return 2; + // Any time we activate the console, we reset the position + SetVisibleListLocation(rConsole.size() - 1); + mUpdate = 1; + scrollToEnd = true; } - if (mCurrentLine == -1 && mLastCount != gConsole.size()) - { - // We can use Render, and return for just a flip - Render(); - return 2; + if (AddLines()) { + // someone added new text + // at least the scrollbar must be updated, even if the new lines are currently not visible + mUpdate = 1; } - else if (mRender) - { - // They're still touching, so re-render - Render(); - return 2; - } - return 0; -} -int GUIConsole::SetRenderPos(int x, int y, int w, int h) -{ - // Adjust the stub position accordingly - mSlideoutX += (x - mConsoleX); - mSlideoutY += (y - mConsoleY); - - mConsoleX = x; - mConsoleY = y; - if (w || h) - { - mConsoleW = w; - mConsoleH = h; + if (scrollToEnd) { + // keep the last line in view + SetVisibleListLocation(rConsole.size() - 1); } - // Calculate the max rows - mMaxRows = mConsoleH / mFontHeight; + GUIScrollList::Update(); - // Adjust so we always fit to bottom - mStartY = mConsoleY + (mConsoleH % mFontHeight); + if (mUpdate) { + mUpdate = 0; + if (Render() == 0) + return 2; + } return 0; } @@ -318,10 +260,9 @@ int GUIConsole::SetRenderPos(int x, int y, int w, int h) // Return 1 if this object handles the request, 0 if not int GUIConsole::IsInRegion(int x, int y) { - if (mSlideout) - { + if (mSlideout) { // Check if they tapped the slideout button - if (x >= mSlideoutX && x <= mSlideoutX + mSlideoutW && y >= mSlideoutY && y < mSlideoutY + mSlideoutH) + if (x >= mSlideoutX && x < mSlideoutX + mSlideoutW && y >= mSlideoutY && y < mSlideoutY + mSlideoutH) return 1; // If we're only rendering the slideout, bail now @@ -329,7 +270,7 @@ int GUIConsole::IsInRegion(int x, int y) return 0; } - return (x < mConsoleX || x >= mConsoleX + mConsoleW || y < mConsoleY || y >= mConsoleY + mConsoleH) ? 0 : 1; + return GUIScrollList::IsInRegion(x, y); } // NotifyTouch - Notify of a touch event @@ -339,64 +280,43 @@ int GUIConsole::NotifyTouch(TOUCH_STATE state, int x, int y) if(!isConditionTrue()) return -1; - if (mSlideout && mSlideoutState == hidden) - { - if (state == TOUCH_START) - { - mSlideoutState = request_show; - return 1; - } - } - else if (mSlideout && mSlideoutState == visible) - { - // Are we sliding it back in? - if (state == TOUCH_START && x > mSlideoutX && x < (mSlideoutX + mSlideoutW) && y > mSlideoutY && y < (mSlideoutY + mSlideoutH)) - { - mSlideoutState = request_hide; - return 1; + if (mSlideout && x >= mSlideoutX && x < mSlideoutX + mSlideoutW && y >= mSlideoutY && y < mSlideoutY + mSlideoutH) { + if (state == TOUCH_START) { + if (mSlideoutState == hidden) + mSlideoutState = request_show; + else if (mSlideoutState == visible) + mSlideoutState = request_hide; } + return 1; } + scrollToEnd = false; + return GUIScrollList::NotifyTouch(state, x, y); +} - // If we don't have enough lines to scroll, throw this away. - if ((int)RenderCount < mMaxRows) return 1; - - // We are scrolling!!! - switch (state) - { - case TOUCH_START: - mLastTouchX = x; - mLastTouchY = y; - break; - - case TOUCH_DRAG: - if (x < mConsoleX || x > mConsoleX + mConsoleW || y < mConsoleY || y > mConsoleY + mConsoleH) - break; // touch is outside of the console area -- do nothing - if (y > mLastTouchY + mFontHeight) { - while (y > mLastTouchY + mFontHeight) { - if (mCurrentLine == -1) - mCurrentLine = RenderCount - 1; - else if (mCurrentLine > mMaxRows) - mCurrentLine--; - mLastTouchY += mFontHeight; - } - mRender = true; - } else if (y < mLastTouchY - mFontHeight) { - while (y < mLastTouchY - mFontHeight) { - if (mCurrentLine >= 0) - mCurrentLine++; - mLastTouchY -= mFontHeight; - } - if (mCurrentLine >= (int) RenderCount) - mCurrentLine = -1; - mRender = true; - } - break; +size_t GUIConsole::GetItemCount() +{ + return rConsole.size(); +} - case TOUCH_RELEASE: - mLastTouchY = -1; - case TOUCH_REPEAT: - case TOUCH_HOLD: - break; +void GUIConsole::RenderItem(size_t itemindex, int yPos, bool selected) +{ + // Set the color for the font + if (rConsoleColor[itemindex] == "normal") { + gr_color(mFontColor.red, mFontColor.green, mFontColor.blue, mFontColor.alpha); + } else { + COLOR FontColor; + std::string color = rConsoleColor[itemindex]; + ConvertStrToColor(color, &FontColor); + FontColor.alpha = 255; + gr_color(FontColor.red, FontColor.green, FontColor.blue, FontColor.alpha); } - return 0; + + // render text + const char* text = rConsole[itemindex].c_str(); + gr_textEx(mRenderX, yPos, text, mFont->GetResource()); +} + +void GUIConsole::NotifySelect(size_t item_selected) +{ + // do nothing - console ignores selections } diff --git a/gui/devices/landscape/res/landscape.xml b/gui/devices/landscape/res/landscape.xml index aaa509e9c..c05ab50e4 100644 --- a/gui/devices/landscape/res/landscape.xml +++ b/gui/devices/landscape/res/landscape.xml @@ -87,6 +87,7 @@ <style name="console"> <color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" /> <font resource="fixed" /> + <fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" /> </style> <style name="input"> diff --git a/gui/devices/portrait/res/portrait.xml b/gui/devices/portrait/res/portrait.xml index 021e81df0..beb076b27 100644 --- a/gui/devices/portrait/res/portrait.xml +++ b/gui/devices/portrait/res/portrait.xml @@ -82,6 +82,7 @@ <style name="console"> <color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" /> <font resource="fixed" /> + <fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" /> </style> <style name="input"> diff --git a/gui/devices/watch/res/watch.xml b/gui/devices/watch/res/watch.xml index 0169a99b4..00806f0cf 100644 --- a/gui/devices/watch/res/watch.xml +++ b/gui/devices/watch/res/watch.xml @@ -82,6 +82,7 @@ <style name="console"> <color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" /> <font resource="fixed" /> + <fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" /> </style> <style name="input"> diff --git a/gui/objects.hpp b/gui/objects.hpp index d5c3b2738..a86747afb 100644 --- a/gui/objects.hpp +++ b/gui/objects.hpp @@ -366,67 +366,6 @@ protected: int simulate; }; -class GUIConsole : public GUIObject, public RenderObject, public ActionObject -{ -public: - GUIConsole(xml_node<>* node); - -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); - - // SetRenderPos - Update the position of the object - // Return 0 on success, <0 on error - virtual int SetRenderPos(int x, int y, int w = 0, int h = 0); - - // IsInRegion - Checks if the request is handled by this object - // Return 1 if this object handles the request, 0 if not - virtual int IsInRegion(int x, int y); - - // NotifyTouch - Notify of a touch event - // Return 0 on success, >0 to ignore remainder of touch, and <0 on error (Return error to allow other handlers) - virtual int NotifyTouch(TOUCH_STATE state, int x, int y); - -protected: - enum SlideoutState - { - hidden = 0, - visible, - request_hide, - request_show - }; - - FontResource* mFont; - ImageResource* mSlideoutImage; - COLOR mForegroundColor; - COLOR mBackgroundColor; - COLOR mScrollColor; - int mFontHeight; - int mCurrentLine; // index of last line to show; -1 to keep tracking last line - size_t mLastCount; // lines from gConsole that are already split and copied into rConsole - size_t RenderCount; // total number of lines after wrapping - int mMaxRows; // height of console in text rows - int mStartY; - int mSlideoutX, mSlideoutY, mSlideoutW, mSlideoutH; - int mSlideinX, mSlideinY, mSlideinW, mSlideinH; - int mConsoleX, mConsoleY, mConsoleW, mConsoleH; - int mLastTouchX, mLastTouchY; - int mSlideout; - SlideoutState mSlideoutState; - std::vector<std::string> rConsole; - std::vector<std::string> rConsoleColor; - bool mRender; - -protected: - virtual int RenderSlideout(void); - virtual int RenderConsole(void); -}; - class GUIButton : public GUIObject, public RenderObject, public ActionObject { public: @@ -606,6 +545,7 @@ protected: 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 + bool allowSelection; // true if touched item can be selected, false for pure read-only lists and the console 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 @@ -741,6 +681,56 @@ protected: bool updateList; }; +class GUIConsole : public GUIScrollList +{ +public: + GUIConsole(xml_node<>* node); + +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); + + // IsInRegion - Checks if the request is handled by this object + // Return 1 if this object handles the request, 0 if not + virtual int IsInRegion(int x, int y); + + // NotifyTouch - Notify of a touch event + // Return 0 on success, >0 to ignore remainder of touch, and <0 on error (Return error to allow other handlers) + virtual int NotifyTouch(TOUCH_STATE state, int x, int y); + + // ScrollList interface + virtual size_t GetItemCount(); + virtual void RenderItem(size_t itemindex, int yPos, bool selected); + virtual void NotifySelect(size_t item_selected); +protected: + enum SlideoutState + { + hidden = 0, + visible, + request_hide, + request_show + }; + + ImageResource* mSlideoutImage; + size_t mLastCount; // lines from gConsole that are already split and copied into rConsole + bool scrollToEnd; // true if we want to keep tracking the last line + int mSlideoutX, mSlideoutY, mSlideoutW, mSlideoutH; + int mSlideout; + SlideoutState mSlideoutState; + std::vector<std::string> rConsole; + std::vector<std::string> rConsoleColor; + +protected: + bool AddLines(); + int RenderSlideout(void); + int RenderConsole(void); +}; + // GUIAnimation - Used for animations class GUIAnimation : public GUIObject, public RenderObject { diff --git a/gui/pages.cpp b/gui/pages.cpp index 50c60a695..18ddf9c33 100644 --- a/gui/pages.cpp +++ b/gui/pages.cpp @@ -105,6 +105,9 @@ int ConvertStrToColor(std::string str, COLOR* color) // Helper APIs xml_node<>* FindNode(xml_node<>* parent, const char* nodename, int depth /* = 0 */) { + if (!parent) + return NULL; + xml_node<>* child = parent->first_node(nodename); if (child) return child; diff --git a/gui/scrolllist.cpp b/gui/scrolllist.cpp index aa9623c68..ec42fe6c0 100644 --- a/gui/scrolllist.cpp +++ b/gui/scrolllist.cpp @@ -29,7 +29,6 @@ extern "C" { const float SCROLLING_SPEED_DECREMENT = 0.9; // friction const int SCROLLING_FLOOR = 2; // minimum pixels for scrolling to stop -const float SCROLLING_SPEED_LIMIT = 2.5; // maximum number of items to scroll per update GUIScrollList::GUIScrollList(xml_node<>* node) : GUIObject(node) { @@ -56,10 +55,12 @@ GUIScrollList::GUIScrollList(xml_node<>* node) : GUIObject(node) ConvertStrToColor("white", &mFastScrollLineColor); ConvertStrToColor("white", &mFastScrollRectColor); hasHighlightColor = false; + allowSelection = true; selectedItem = NO_ITEM; // Load header text - child = node->first_node("text"); + // note: node can be NULL for the emergency console + child = node ? node->first_node("text") : NULL; if (child) mHeaderText = child->value(); // Simple way to check for static state mLastHeaderValue = gui_parse_text(mHeaderText); @@ -163,7 +164,7 @@ void GUIScrollList::SetMaxIconSize(int w, int h) 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(); + size_t lines = GetDisplayItemCount(); if (list_index <= (unsigned)firstDisplayedItem) { // list_index is above the currently displayed items, put the selected item at the very top @@ -180,6 +181,8 @@ void GUIScrollList::SetVisibleListLocation(size_t list_index) // There's no partial row so zero out the offset y_offset = 0; } + if (firstDisplayedItem < 0) + firstDisplayedItem = 0; } scrollingSpeed = 0; // stop kinetic scrolling on setting visible location mUpdate = 1; @@ -361,7 +364,10 @@ int GUIScrollList::Update(void) } // Handle kinetic scrolling - int maxScrollDistance = actualItemHeight * SCROLLING_SPEED_LIMIT; + // maximum number of items to scroll per update + float maxItemsScrolledPerFrame = std::max(2.5, float(GetDisplayItemCount() / 4) + 0.5); + + int maxScrollDistance = actualItemHeight * maxItemsScrolledPerFrame; int oldScrollingSpeed = scrollingSpeed; if (scrollingSpeed == 0) { // Do nothing @@ -438,7 +444,7 @@ int GUIScrollList::NotifyTouch(TOUCH_STATE state, int x, int y) 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) { + } else if (!fastScroll && allowSelection) { // find out which item the user touched selectedItem = HitTestItem(x, y); } |