From 44925ad19051d4ffe239b3ba99ea27e7275409dc Mon Sep 17 00:00:00 2001 From: Ethan Yonker Date: Wed, 22 Jul 2015 12:33:59 -0500 Subject: GUI TextBox Allows the GUI to create a scrollable text box for long text that may not all fit on the screen. Also includes code to allow the console to wrap on spaces and other such characters instead of wrapping in the middle of a word. To see an example of how to add a text box to the XML, see: https://gerrit.omnirom.org/#/c/14183/ Change-Id: Ifd139172ede290046b58ea3fe526e2e06da1d4ef --- gui/Android.mk | 3 +- gui/console.cpp | 34 +-------------- gui/objects.hpp | 29 ++++++++++++- gui/pages.cpp | 7 ++++ gui/scrolllist.cpp | 40 ++++++++++++++++++ gui/textbox.cpp | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 199 insertions(+), 34 deletions(-) create mode 100644 gui/textbox.cpp diff --git a/gui/Android.mk b/gui/Android.mk index 1d0d134ec..6633e9abc 100644 --- a/gui/Android.mk +++ b/gui/Android.mk @@ -27,7 +27,8 @@ LOCAL_SRC_FILES := \ partitionlist.cpp \ mousecursor.cpp \ scrolllist.cpp \ - patternpassword.cpp + patternpassword.cpp \ + textbox.cpp ifneq ($(TWRP_CUSTOM_KEYBOARD),) LOCAL_SRC_FILES += $(TWRP_CUSTOM_KEYBOARD) diff --git a/gui/console.cpp b/gui/console.cpp index 47caadb15..6b38d6137 100644 --- a/gui/console.cpp +++ b/gui/console.cpp @@ -158,39 +158,9 @@ int GUIConsole::RenderSlideout(void) return 0; } -bool GUIConsole::AddLines() -{ - if (mLastCount == gConsole.size()) - return false; // nothing to add - - size_t prevCount = mLastCount; - mLastCount = gConsole.size(); - - // 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 - // may different in different console windows - for (size_t i = prevCount; i < mLastCount; i++) { - string curr_line = gConsole[i]; - string curr_color = gConsoleColor[i]; - for(;;) { - 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); - curr_line = curr_line.substr(line_char_width); - } else { - rConsole.push_back(curr_line); - rConsoleColor.push_back(curr_color); - break; - } - } - } - return true; -} - int GUIConsole::RenderConsole(void) { - AddLines(); + AddLines(&gConsole, &gConsoleColor, &mLastCount, &rConsole, &rConsoleColor); GUIScrollList::Render(); // if last line is fully visible, keep tracking the last line when new lines are added @@ -241,7 +211,7 @@ int GUIConsole::Update(void) scrollToEnd = true; } - if (AddLines()) { + if (AddLines(&gConsole, &gConsoleColor, &mLastCount, &rConsole, &rConsoleColor)) { // someone added new text // at least the scrollbar must be updated, even if the new lines are currently not visible mUpdate = 1; diff --git a/gui/objects.hpp b/gui/objects.hpp index 73d8717d5..3d217c45f 100644 --- a/gui/objects.hpp +++ b/gui/objects.hpp @@ -545,6 +545,7 @@ protected: 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 + bool AddLines(std::vector* origText, std::vector* origColor, size_t* lastCount, std::vector* rText, std::vector* rColor); }; class GUIFileSelector : public GUIScrollList @@ -680,6 +681,33 @@ protected: bool updateList; }; +class GUITextBox : public GUIScrollList +{ +public: + GUITextBox(xml_node<>* node); + +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); + + // ScrollList interface + virtual size_t GetItemCount(); + virtual void RenderItem(size_t itemindex, int yPos, bool selected); + virtual void NotifySelect(size_t item_selected); +protected: + + size_t mLastCount; + bool mIsStatic; + std::vector mLastValue; // Parsed text - parsed for variables but not word wrapped + std::vector mText; // Original text - not parsed for variables and not word wrapped + std::vector rText; // Rendered text - what we actually see + +}; + class GUIConsole : public GUIScrollList { public: @@ -725,7 +753,6 @@ protected: std::vector rConsoleColor; protected: - bool AddLines(); int RenderSlideout(void); int RenderConsole(void); }; diff --git a/gui/pages.cpp b/gui/pages.cpp index 9bff289d3..3abd28785 100644 --- a/gui/pages.cpp +++ b/gui/pages.cpp @@ -444,6 +444,13 @@ bool Page::ProcessNode(xml_node<>* page, std::vector*> *templates, in mRenders.push_back(element); mActions.push_back(element); } + else if (type == "textbox") + { + GUITextBox* element = new GUITextBox(child); + mObjects.push_back(element); + mRenders.push_back(element); + mActions.push_back(element); + } else if (type == "template") { if (!templates || !child->first_attribute("name")) diff --git a/gui/scrolllist.cpp b/gui/scrolllist.cpp index a033205bb..d857e06b3 100644 --- a/gui/scrolllist.cpp +++ b/gui/scrolllist.cpp @@ -607,3 +607,43 @@ void GUIScrollList::SetPageFocus(int inFocus) mUpdate = 1; } } + +bool GUIScrollList::AddLines(std::vector* origText, std::vector* origColor, size_t* lastCount, std::vector* rText, std::vector* rColor) +{ + if (*lastCount == origText->size()) + return false; // nothing to add + + size_t prevCount = *lastCount; + *lastCount = origText->size(); + + // 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 + // may different in different console windows + for (size_t i = prevCount; i < *lastCount; i++) { + string curr_line = origText->at(i); + string curr_color; + if (origColor) + curr_color = origColor->at(i); + for(;;) { + size_t line_char_width = gr_ttf_maxExW(curr_line.c_str(), mFont->GetResource(), mRenderW); + if (line_char_width < curr_line.size()) { + //string left = curr_line.substr(0, line_char_width); + size_t wrap_pos = curr_line.find_last_of(" ,./:-_;", line_char_width - 1); + if (wrap_pos == string::npos) + wrap_pos = line_char_width; + else if (wrap_pos < line_char_width - 1) + wrap_pos++; + rText->push_back(curr_line.substr(0, wrap_pos)); + if (origColor) + rColor->push_back(curr_color); + curr_line = curr_line.substr(wrap_pos); + } else { + rText->push_back(curr_line); + if (origColor) + rColor->push_back(curr_color); + break; + } + } + } + return true; +} diff --git a/gui/textbox.cpp b/gui/textbox.cpp new file mode 100644 index 000000000..277297fec --- /dev/null +++ b/gui/textbox.cpp @@ -0,0 +1,120 @@ +/* + Copyright 2015 bigbiff/Dees_Troy/_that 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 . +*/ + +// textbox.cpp - GUITextBox object + +#include + +extern "C" { +#include "../twcommon.h" +#include "../minuitwrp/minui.h" +} + +#include "rapidxml.hpp" +#include "objects.hpp" + +GUITextBox::GUITextBox(xml_node<>* node) : GUIScrollList(node) +{ + xml_node<>* child; + + mLastCount = 0; + mIsStatic = true; + + allowSelection = false; // textbox doesn't support list item selections + + child = FindNode(node, "color"); + if (child) + { + mFontColor = LoadAttrColor(child, "foreground", mFontColor); + mBackgroundColor = LoadAttrColor(child, "background", mBackgroundColor); + //mScrollColor = LoadAttrColor(child, "scroll", mScrollColor); + } + child = FindNode(node, "text"); + while (child) { + string txt = child->value(); + mText.push_back(txt); + string lookup = gui_parse_text(txt); + if (lookup != txt) + mIsStatic = false; + mLastValue.push_back(lookup); + child = child->next_sibling("text"); + } +} + +int GUITextBox::Update(void) +{ + if (AddLines(&mLastValue, NULL, &mLastCount, &rText, NULL)) { + // someone added new text + // at least the scrollbar must be updated, even if the new lines are currently not visible + mUpdate = 1; + } + + GUIScrollList::Update(); + + if (mUpdate) { + mUpdate = 0; + if (Render() == 0) + return 2; + } + return 0; +} + +size_t GUITextBox::GetItemCount() +{ + return rText.size(); +} + +void GUITextBox::RenderItem(size_t itemindex, int yPos, bool selected __unused) +{ + // Set the color for the font + gr_color(mFontColor.red, mFontColor.green, mFontColor.blue, mFontColor.alpha); + + // render text + const char* text = rText[itemindex].c_str(); + gr_textEx(mRenderX, yPos, text, mFont->GetResource()); +} + +void GUITextBox::NotifySelect(size_t item_selected __unused) +{ + // do nothing - textbox ignores selections +} + +int GUITextBox::NotifyVarChange(const std::string& varName, const std::string& value) +{ + GUIScrollList::NotifyVarChange(varName, value); + + if(!isConditionTrue() || mIsStatic) + return 0; + + // Check to see if the variable exists in mText + for (size_t i = 0; i < mText.size(); i++) { + string lookup = gui_parse_text(mText.at(i)); + if (lookup != mText.at(i)) { + mLastValue.at(i) = lookup; + mUpdate = 1; + // There are ways to improve efficiency here, but I am not + // sure if we will even use this feature in the stock theme + // at all except for language translation. If we start using + // variables in textboxes in the stock theme, we can circle + // back and make improvements here. + mLastCount = 0; + rText.clear(); + } + } + return 0; +} -- cgit v1.2.3