From bbb8aeeac30629a9b088116d0cc7bba868acfd7b Mon Sep 17 00:00:00 2001 From: Aloshi Date: Tue, 11 Mar 2014 22:00:08 -0500 Subject: [PATCH] Completely rewrote ComponentGrid to use shared pointers, have support for borders, work better with nested components, and generally suck less. Split the scraper screen into a "ScraperSearchComponent" so it can be reused in other menus (what could this possibly mean for the future?!). Re-designed the ScraperSearchComponent to fit UI concepts. Added the ability to put a row of buttons at the bottom of a MenuComponent. Redid GuiMetaDataEd to use a MenuComponent instead of ComponentGrid. Redid GuiGameScraper to use a ComponentGrid containing a ScraperSearchComponent. Fixed Renderer::pushClipRect not clipping new rects to be within the bounds of the existing clipRect stack. A ton of little fixes that I forgot to mention. It's a good thing I'm the only developer currently, or I would have to actually break this into multiple commits. --- CMakeLists.txt | 2 + src/MetaData.cpp | 30 +- src/MetaData.h | 3 +- src/Renderer_draw_gl.cpp | 21 +- src/components/ButtonComponent.cpp | 33 +- src/components/ButtonComponent.h | 6 +- src/components/ComponentGrid.cpp | 592 ++++++++++------------ src/components/ComponentGrid.h | 124 +++-- src/components/ComponentList.cpp | 41 +- src/components/ComponentList.h | 6 + src/components/MenuComponent.cpp | 68 ++- src/components/MenuComponent.h | 20 +- src/components/ScraperSearchComponent.cpp | 238 +++++++++ src/components/ScraperSearchComponent.h | 64 +++ src/components/TextComponent.cpp | 11 +- src/components/TextComponent.h | 3 +- src/guis/GuiGameScraper.cpp | 237 +++------ src/guis/GuiGameScraper.h | 36 +- src/guis/GuiMetaDataEd.cpp | 118 +---- src/guis/GuiMetaDataEd.h | 23 +- src/guis/GuiScraperLog.cpp | 1 - src/scrapers/GamesDBScraper.cpp | 3 +- 22 files changed, 903 insertions(+), 777 deletions(-) create mode 100644 src/components/ScraperSearchComponent.cpp create mode 100644 src/components/ScraperSearchComponent.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 7dc3fb13f..241b4cbb9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -172,6 +172,7 @@ set(ES_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/components/NinePatchComponent.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/OptionListComponent.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/RatingComponent.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/ScraperSearchComponent.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/ScrollableContainer.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/SliderComponent.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/SwitchComponent.h @@ -252,6 +253,7 @@ set(ES_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/components/MenuComponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/NinePatchComponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/RatingComponent.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/ScraperSearchComponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/ScrollableContainer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/SliderComponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/SwitchComponent.cpp diff --git a/src/MetaData.cpp b/src/MetaData.cpp index 575ea65b2..917fb07f4 100644 --- a/src/MetaData.cpp +++ b/src/MetaData.cpp @@ -133,51 +133,33 @@ boost::posix_time::ptime MetaDataList::getTime(const std::string& key) const return string_to_ptime(get(key), "%Y%m%dT%H%M%S%F%q"); } -GuiComponent* MetaDataList::makeDisplay(Window* window, MetaDataType as) +std::shared_ptr MetaDataList::makeEditor(Window* window, MetaDataType as) { switch(as) { case MD_RATING: { - RatingComponent* comp = new RatingComponent(window); - return comp; - } - default: - TextComponent* comp = new TextComponent(window); - return comp; - } -} - -GuiComponent* MetaDataList::makeEditor(Window* window, MetaDataType as) -{ - switch(as) - { - case MD_RATING: - { - RatingComponent* comp = new RatingComponent(window); - return comp; + return std::make_shared(window); } case MD_MULTILINE_STRING: { - TextEditComponent* comp = new TextEditComponent(window); + auto comp = std::make_shared(window); comp->setSize(comp->getSize().x(), comp->getSize().y() * 3); return comp; } case MD_DATE: { - DateTimeComponent* comp = new DateTimeComponent(window); - return comp; + return std::make_shared(window); } case MD_TIME: { - DateTimeComponent* comp = new DateTimeComponent(window); + auto comp = std::make_shared(window); comp->setDisplayMode(DateTimeComponent::DISP_RELATIVE_TO_NOW); return comp; } default: { - TextEditComponent* comp = new TextEditComponent(window); - return comp; + return std::make_shared(window); } } } diff --git a/src/MetaData.h b/src/MetaData.h index 48c6c3f5c..f7a2b5c0a 100644 --- a/src/MetaData.h +++ b/src/MetaData.h @@ -55,8 +55,7 @@ public: float getFloat(const std::string& key) const; boost::posix_time::ptime getTime(const std::string& key) const; - static GuiComponent* makeDisplay(Window* window, MetaDataType as); - static GuiComponent* makeEditor(Window* window, MetaDataType as); + static std::shared_ptr makeEditor(Window* window, MetaDataType as); inline MetaDataListType getType() const { return mType; } inline const std::vector& getMDD() const { return getMDDByType(getType()); } diff --git a/src/Renderer_draw_gl.cpp b/src/Renderer_draw_gl.cpp index 7e101808c..a5bb90642 100644 --- a/src/Renderer_draw_gl.cpp +++ b/src/Renderer_draw_gl.cpp @@ -39,14 +39,31 @@ namespace Renderer { if(box[3] == 0) box[3] = Renderer::getScreenHeight() - box.y(); - //TODO - make sure the box fits within clipStack.top(), and clip further accordingly! - //glScissor starts at the bottom left of the window //so (0, 0, 1, 1) is the bottom left pixel //everything else uses y+ = down, so flip it to be consistent //rect.pos.y = Renderer::getScreenHeight() - rect.pos.y - rect.size.y; box[1] = Renderer::getScreenHeight() - box.y() - box[3]; + //make sure the box fits within clipStack.top(), and clip further accordingly + if(clipStack.size()) + { + Eigen::Vector4i& top = clipStack.top(); + if(top[0] > box[0]) + box[0] = top[0]; + if(top[1] > box[1]) + box[1] = top[1]; + if(top[0] + top[2] < box[0] + box[2]) + box[2] = (top[0] + top[2]) - box[0]; + if(top[1] + top[3] < box[1] + box[3]) + box[3] = (top[1] + top[3]) - box[1]; + } + + if(box[2] < 0) + box[2] = 0; + if(box[3] < 0) + box[3] = 0; + clipStack.push(box); glScissor(box[0], box[1], box[2], box[3]); glEnable(GL_SCISSOR_TEST); diff --git a/src/components/ButtonComponent.cpp b/src/components/ButtonComponent.cpp index e343ce815..a0d18b463 100644 --- a/src/components/ButtonComponent.cpp +++ b/src/components/ButtonComponent.cpp @@ -2,12 +2,14 @@ #include "../Renderer.h" #include "../Window.h" -ButtonComponent::ButtonComponent(Window* window) : GuiComponent(window), +ButtonComponent::ButtonComponent(Window* window, const std::string& text, const std::string& helpText, const std::function& func) : GuiComponent(window), mBox(window, ":/button.png"), mFocused(false), + mEnabled(true), mTextColorFocused(0xFFFFFFFF), mTextColorUnfocused(0x777777FF) { - setSize(64, 48); + setPressedFunc(func); + setText(text, helpText); } void ButtonComponent::onSizeChanged() @@ -24,7 +26,7 @@ bool ButtonComponent::input(InputConfig* config, Input input) { if(config->isMappedTo("a", input) && input.value != 0) { - if(mPressedFunc) + if(mPressedFunc && mEnabled) mPressedFunc(); return true; } @@ -48,13 +50,34 @@ void ButtonComponent::setText(const std::string& text, const std::string& helpTe void ButtonComponent::onFocusGained() { mFocused = true; - mBox.setImagePath(":/button_filled.png"); + updateImage(); } void ButtonComponent::onFocusLost() { mFocused = false; - mBox.setImagePath(":/button.png"); + updateImage(); +} + +void ButtonComponent::setEnabled(bool enabled) +{ + mEnabled = enabled; + updateImage(); +} + +void ButtonComponent::updateImage() +{ + if(!mEnabled || !mPressedFunc) + { + mBox.setImagePath(":/button.png"); + mBox.setCenterColor(0x770000FF); + mBox.setEdgeColor(0x770000FF); + return; + } + + mBox.setCenterColor(0xFFFFFFFF); + mBox.setEdgeColor(0xFFFFFFFF); + mBox.setImagePath(mFocused ? ":/button_filled.png" : ":/button.png"); } void ButtonComponent::render(const Eigen::Affine3f& parentTrans) diff --git a/src/components/ButtonComponent.h b/src/components/ButtonComponent.h index f048d800c..b82211438 100644 --- a/src/components/ButtonComponent.h +++ b/src/components/ButtonComponent.h @@ -8,10 +8,12 @@ class ButtonComponent : public GuiComponent { public: - ButtonComponent(Window* window); + ButtonComponent(Window* window, const std::string& text = "", const std::string& helpText = "", const std::function& func = nullptr); void setPressedFunc(std::function f); + void setEnabled(bool enable); + bool input(InputConfig* config, Input input) override; void render(const Eigen::Affine3f& parentTrans) override; @@ -28,10 +30,12 @@ private: std::function mPressedFunc; bool mFocused; + bool mEnabled; unsigned int mTextColorFocused; unsigned int mTextColorUnfocused; unsigned int getCurTextColor() const; + void updateImage(); std::string mText; std::string mHelpText; diff --git a/src/components/ComponentGrid.cpp b/src/components/ComponentGrid.cpp index ccf6d32ef..37c379142 100644 --- a/src/components/ComponentGrid.cpp +++ b/src/components/ComponentGrid.cpp @@ -2,281 +2,219 @@ #include "../Log.h" #include "../Renderer.h" -#define INITIAL_CELL_SIZE 12 +using namespace GridFlags; -ComponentGrid::ComponentGrid(Window* window, Eigen::Vector2i gridDimensions) : GuiComponent(window), - mGrid(NULL), mColumnWidths(NULL), mRowHeights(NULL), - mColumnWidthForced(NULL), mRowHeightForced(NULL), - mCursor(-1, -1) +ComponentGrid::ComponentGrid(Window* window, const Eigen::Vector2i& gridDimensions) : GuiComponent(window), + mGridSize(gridDimensions), mCursor(0, 0) { - mEntries.reserve(gridDimensions.x() * gridDimensions.y()); - makeCells(gridDimensions); + assert(gridDimensions.x() > 0 && gridDimensions.y() > 0); + + mCells.reserve(gridDimensions.x() * gridDimensions.y()); + + mColWidths = new float[gridDimensions.x()]; + mRowHeights = new float[gridDimensions.y()]; + for(int x = 0; x < gridDimensions.x(); x++) + mColWidths[x] = 0; + for(int y = 0; y < gridDimensions.y(); y++) + mRowHeights[y] = 0; } ComponentGrid::~ComponentGrid() { - for(auto iter = mEntries.begin(); iter != mEntries.end(); iter++) - { - delete *iter; - } + delete[] mRowHeights; + delete[] mColWidths; } -void ComponentGrid::makeCells(Eigen::Vector2i size) +float ComponentGrid::getColWidth(int col) { - if(mGrid) - delete[] mGrid; - if(mColumnWidths) - delete[] mColumnWidths; - if(mRowHeights) - delete[] mRowHeights; + if(mColWidths[col] != 0) + return mColWidths[col] * mSize.x(); + + // calculate automatic width + float freeWidthPerc = 1; + int between = 0; + for(int x = 0; x < mGridSize.x(); x++) + { + freeWidthPerc -= mColWidths[x]; // if it's 0 it won't do anything + if(mColWidths[x] == 0) + between++; + } - mGridSize = size; - mGrid = new ComponentEntry*[size.x() * size.y()]; - std::fill(mGrid, mGrid + (size.x() * size.y()), (ComponentEntry*)NULL); - - mColumnWidths = new unsigned int[size.x()]; - std::fill(mColumnWidths, mColumnWidths + size.x(), INITIAL_CELL_SIZE); - - mRowHeights = new unsigned int[size.y()]; - std::fill(mRowHeights, mRowHeights + size.y(), INITIAL_CELL_SIZE); - - mColumnWidthForced = new bool[size.x()]; - std::fill(mColumnWidthForced, mColumnWidthForced + size.x(), false); - - mRowHeightForced = new bool[size.y()]; - std::fill(mRowHeightForced, mRowHeightForced + size.y(), false); - - updateSize(); - resetCursor(); + return (freeWidthPerc * mSize.x()) / between; } -void ComponentGrid::setEntry(Eigen::Vector2i pos, Eigen::Vector2i size, GuiComponent* component, bool canFocus, AlignmentType align, - Eigen::Matrix autoFit, UpdateBehavior updateType) +float ComponentGrid::getRowHeight(int row) { - if(pos.x() > mGridSize.x() || pos.y() > mGridSize.y() || pos.x() < 0 || pos.y() < 0) - { - LOG(LogError) << "Tried to set entry beyond grid size!"; - return; - } + if(mRowHeights[row] != 0) + return mRowHeights[row] * mSize.y(); - if(component == NULL) + // calculate automatic height + float freeHeightPerc = 1; + int between = 0; + for(int y = 0; y < mGridSize.y(); y++) { - LOG(LogError) << "Tried to add NULL component to ComponentList!"; - return; + freeHeightPerc -= mRowHeights[y]; // if it's 0 it won't do anything + if(mRowHeights[y] == 0) + between++; } - - ComponentEntry* entry = new ComponentEntry(Eigen::Vector2i(pos.x(), pos.y()), Eigen::Vector2i(size.x(), size.y()), component, updateType, canFocus, align); - mEntries.push_back(entry); + return (freeHeightPerc * mSize.y()) / between; +} - for(int y = pos.y(); y < pos.y() + size.y(); y++) - { - for(int x = pos.x(); x < pos.x() + size.x(); x++) - { - setCell(x, y, mEntries.back()); - } - } +void ComponentGrid::setColWidthPerc(int col, float width) +{ + assert(col >= 0 && col < mGridSize.x()); + mColWidths[col] = width; + onSizeChanged(); +} - if(component->getParent() != NULL) - LOG(LogError) << "ComponentGrid ruining an existing parent-child relationship! Call a social worker!"; - component->setParent(this); +void ComponentGrid::setRowHeightPerc(int row, float height) +{ + assert(row >= 0 && row < mGridSize.y()); + mRowHeights[row] = height; + onSizeChanged(); +} + +void ComponentGrid::setEntry(const std::shared_ptr& comp, const Eigen::Vector2i& pos, bool canFocus, bool resize, const Eigen::Vector2i& size, + unsigned int border, GridFlags::UpdateType updateType) +{ + assert(pos.x() >= 0 && pos.x() < mGridSize.x() && pos.y() >= 0 && pos.y() < mGridSize.y()); + assert(comp != nullptr); + assert(comp->getParent() == NULL); + + GridEntry entry(pos, size, comp, canFocus, resize, updateType, border); + mCells.push_back(entry); + + addChild(comp.get()); if(!cursorValid() && canFocus) mCursor = pos; - //update the column width and row height - //if(autoFit.x() && (int)getColumnWidth(pos.x()) < component->getSize().x()) - // setColumnWidth(pos.x(), (unsigned int)component->getSize().x()); - //if(autoFit.y() && (int)getRowHeight(pos.y()) < component->getSize().y()) - // setRowHeight(pos.y(), (unsigned int)component->getSize().y()); - updateCellSize(mEntries.back(), autoFit.x(), autoFit.y()); - - component->setPosition(getCellOffset(pos)); - - updateSize(); + updateCellComponent(mCells.back()); + updateSeparators(); } -void ComponentGrid::removeEntriesIn(Eigen::Vector2i pos, Eigen::Vector2i size) +bool ComponentGrid::removeEntry(const std::shared_ptr& comp) { - auto iter = mEntries.begin(); - while(iter != mEntries.end()) + for(auto it = mCells.begin(); it != mCells.end(); it++) { - if((*iter)->pos.x() >= pos.x() && (*iter)->pos.x() < pos.x() + size.x() - && (*iter)->pos.y() >= pos.y() && (*iter)->pos.y() < pos.y() + size.y()) + if(it->component == comp) { - if((*iter)->component->getParent() == this) - (*iter)->component->setParent(NULL); - - delete *iter; - iter = mEntries.erase(iter); - }else{ - iter++; + removeChild(comp.get()); + mCells.erase(it); + return true; } } - for(int y = pos.y(); y < pos.y() + size.y(); y++) - { - for(int x = pos.x(); x < pos.x() + size.x(); x++) - { - setCell(x, y, NULL); - } - } - - if(!cursorValid()) - resetCursor(); + return false; } -void ComponentGrid::forceRowHeight(int row, unsigned int size) +void ComponentGrid::updateCellComponent(const GridEntry& cell) { - mRowHeights[row] = size; - mRowHeightForced[row] = true; - updateSize(); - updateComponentOffsets(); -} + // size + Eigen::Vector2f size(0, 0); + for(int x = cell.pos.x(); x < cell.pos.x() + cell.dim.x(); x++) + size[0] += getColWidth(x); + for(int y = cell.pos.y(); y < cell.pos.y() + cell.dim.y(); y++) + size[1] += getRowHeight(y); -void ComponentGrid::forceColumnWidth(int col, unsigned int size) -{ - mColumnWidths[col] = size; - mColumnWidthForced[col] = true; - updateSize(); - updateComponentOffsets(); -} + if(cell.resize) + cell.component->setSize(size); -unsigned int ComponentGrid::getRowHeight(int row) { return mRowHeights[row]; } -unsigned int ComponentGrid::getColumnWidth(int col) { return mColumnWidths[col]; } + // position + // find top left corner + Eigen::Vector3f pos(0, 0, 0); + for(int x = 0; x < cell.pos.x(); x++) + pos[0] += getColWidth(x); + for(int y = 0; y < cell.pos.y(); y++) + pos[1] += getRowHeight(y); -Eigen::Vector3f ComponentGrid::getCellOffset(Eigen::Vector2i pos) -{ - Eigen::Vector3f offset(0, 0, 0); - - for(int y = 0; y < pos.y(); y++) - offset[1] += getRowHeight(y); + // center component + pos[0] = pos.x() + (size.x() - cell.component->getSize().x()) / 2; + pos[1] = pos.y() + (size.y() - cell.component->getSize().y()) / 2; - for(int x = 0; x < pos.x(); x++) - offset[0] += getColumnWidth(x); - - ComponentEntry* entry = getCell(pos.x(), pos.y()); - - Eigen::Vector2i gridSize(0, 0); - for(int x = pos.x(); x < pos.x() + entry->dim[0]; x++) - gridSize[0] += getColumnWidth(x); - for(int y = pos.y(); y < pos.y() + entry->dim[1]; y++) - gridSize[1] += getRowHeight(y); - - //if AlignCenter, add half of cell width - half of control width - if(entry->alignment == AlignCenter) - offset[0] += gridSize.x() / 2 - entry->component->getSize().x() / 2; - - //if AlignRight, add cell width - control width - if(entry->alignment == AlignRight) - offset[0] += gridSize.x() - entry->component->getSize().x(); - - //always center on the Y axis - offset[1] += gridSize.y() / 2.0f - entry->component->getSize().y() / 2.0f; - - return offset; + cell.component->setPosition(pos); } -void ComponentGrid::setCell(unsigned int x, unsigned int y, ComponentEntry* entry) +void ComponentGrid::updateSeparators() { - if(x >= (unsigned int)mGridSize.x() || y >= (unsigned int)mGridSize.y()) + mLines.clear(); + + Eigen::Vector2f pos; + Eigen::Vector2f size; + for(auto it = mCells.begin(); it != mCells.end(); it++) { - LOG(LogError) << "Invalid setCell - position " << x << ", " << y << " out of bounds!"; - return; - } + if(!it->border) + continue; - mGrid[y * mGridSize.x() + x] = entry; -} + // find component position + size + pos << 0, 0; + size << 0, 0; + for(int x = 0; x < it->pos.x(); x++) + pos[0] += getColWidth(x); + for(int y = 0; y < it->pos.y(); y++) + pos[1] += getRowHeight(y); + for(int x = it->pos.x(); x < it->pos.x() + it->dim.x(); x++) + size[0] += getColWidth(x); + for(int y = it->pos.y(); y < it->pos.y() + it->dim.y(); y++) + size[1] += getRowHeight(y); -ComponentGrid::ComponentEntry* ComponentGrid::getCell(unsigned int x, unsigned int y) -{ - if(x >= (unsigned int)mGridSize.x() || y >= (unsigned int)mGridSize.y()) - { - LOG(LogError) << "Invalid getCell - position " << x << ", " << y << " out of bounds!"; - return NULL; - } - - return mGrid[y * mGridSize.x() + x]; -} - -void ComponentGrid::updateSize() -{ - mSize = Eigen::Vector2f(0, 0); - for(int x = 0; x < mGridSize.x(); x++) - mSize.x() += getColumnWidth(x); - for(int y = 0; y < mGridSize.y(); y++) - mSize.y() += getRowHeight(y); -} - -void ComponentGrid::updateComponentOffsets() -{ - for(auto iter = mEntries.begin(); iter != mEntries.end(); iter++) - { - (*iter)->component->setPosition(getCellOffset((*iter)->pos)); - } -} - -void ComponentGrid::updateCellSize(ComponentEntry* e, bool updWidth, bool updHeight) -{ - if(!e) - { - LOG(LogError) << "Tried to updateCellSize NULL ComponentEntry!"; - return; - } - - unsigned int x = e->pos.x(); - unsigned int y = e->pos.y(); - - if(!mColumnWidthForced[x] && updWidth) - { - //recalc width to widest in column - float widest = 0; - for(int row = 0; row < mGridSize.y(); row++) + if(it->border & BORDER_TOP) { - ComponentEntry* check = getCell(x, row); - if(check) - { - if(check->component->getSize().x() > widest) - widest = check->component->getSize().x(); - } + mLines.push_back(Vert(pos.x(), pos.y())); + mLines.push_back(Vert(pos.x() + size.x(), pos.y())); } - - mColumnWidths[x] = (unsigned int)widest; - } - if(!mRowHeightForced[y] && updHeight) - { - float tallest = 0; - for(int col = 0; col < mGridSize.x(); col++) + if(it->border & BORDER_BOTTOM) { - ComponentEntry* check = getCell(col, y); - if(check) - { - if(check->component->getSize().y() > tallest) - tallest = check->component->getSize().y(); - } + mLines.push_back(Vert(pos.x(), pos.y() + size.y())); + mLines.push_back(Vert(pos.x() + size.x(), mLines.back().y)); } - - mRowHeights[y] = (unsigned int)tallest; - } - - updateComponentOffsets(); - updateSize(); -} - -void ComponentGrid::updateComponent(GuiComponent* cmp) -{ - for(auto iter = mEntries.begin(); iter != mEntries.end(); iter++) - { - if((*iter)->component == cmp) + if(it->border & BORDER_LEFT) { - updateCellSize(*iter); + mLines.push_back(Vert(pos.x(), pos.y())); + mLines.push_back(Vert(pos.x(), pos.y() + size.y())); + } + if(it->border & BORDER_RIGHT) + { + mLines.push_back(Vert(pos.x() + size.x(), pos.y())); + mLines.push_back(Vert(mLines.back().x, pos.y() + size.y())); } } + + mLineColors.reserve(mLines.size()); + Renderer::buildGLColorArray((GLubyte*)mLineColors.data(), 0xC6C7C6FF, mLines.size()); +} + +void ComponentGrid::onSizeChanged() +{ + for(auto it = mCells.begin(); it != mCells.end(); it++) + updateCellComponent(*it); + + updateSeparators(); +} + +ComponentGrid::GridEntry* ComponentGrid::getCellAt(int x, int y) +{ + assert(x >= 0 && x < mGridSize.x() && y >= 0 && y < mGridSize.y()); + + for(auto it = mCells.begin(); it != mCells.end(); it++) + { + int xmin = it->pos.x(); + int xmax = xmin + it->dim.x(); + int ymin = it->pos.y(); + int ymax = ymin + it->dim.y(); + + if(x >= xmin && y >= ymin && x < xmax && y < ymax) + return &(*it); + } + + return NULL; } bool ComponentGrid::input(InputConfig* config, Input input) { - if(cursorValid() && getCell(mCursor.x(), mCursor.y())->component->input(config, input)) + GridEntry* cursorEntry = getCellAt(mCursor); + if(cursorEntry && cursorEntry->component->input(config, input)) return true; if(!input.value) @@ -284,23 +222,19 @@ bool ComponentGrid::input(InputConfig* config, Input input) if(config->isMappedTo("down", input)) { - moveCursor(Eigen::Vector2i(0, 1)); - return true; + return moveCursor(Eigen::Vector2i(0, 1)); } if(config->isMappedTo("up", input)) { - moveCursor(Eigen::Vector2i(0, -1)); - return true; + return moveCursor(Eigen::Vector2i(0, -1)); } if(config->isMappedTo("left", input)) { - moveCursor(Eigen::Vector2i(-1, 0)); - return true; + return moveCursor(Eigen::Vector2i(-1, 0)); } if(config->isMappedTo("right", input)) { - moveCursor(Eigen::Vector2i(1, 0)); - return true; + return moveCursor(Eigen::Vector2i(1, 0)); } return false; @@ -308,47 +242,28 @@ bool ComponentGrid::input(InputConfig* config, Input input) void ComponentGrid::resetCursor() { - auto iter = mEntries.begin(); - while(iter != mEntries.end()) - { - if((*iter)->canFocus) - break; - iter++; - } - - if(iter == mEntries.end()) - { - mCursor = Eigen::Vector2i(-1, -1); + if(!mCells.size()) return; - } - const Eigen::Vector2i origCursor = mCursor; - mCursor << (*iter)->pos[0], (*iter)->pos[1]; - onCursorMoved(origCursor, mCursor); -} - -void ComponentGrid::moveCursor(Eigen::Vector2i dir) -{ - if(dir.x() != 0 && dir.y() != 0) + for(auto it = mCells.begin(); it != mCells.end(); it++) { - LOG(LogError) << "Invalid cursor move dir!"; - return; - } - - Eigen::Vector2i origCursor = mCursor; - - if(!cursorValid()) - { - resetCursor(); - - if(!cursorValid()) + if(it->canFocus) { - if(mCursor != origCursor) - onCursorMoved(origCursor, mCursor); - - return; + Eigen::Vector2i origCursor = mCursor; + mCursor = it->pos; + onCursorMoved(origCursor, mCursor); + break; } } +} + +bool ComponentGrid::moveCursor(Eigen::Vector2i dir) +{ + assert(dir.x() || dir.y()); + + const Eigen::Vector2i origCursor = mCursor; + + GridEntry* currentCursorEntry = getCellAt(mCursor); Eigen::Vector2i searchAxis(dir.x() == 0, dir.y() == 0); @@ -358,13 +273,16 @@ void ComponentGrid::moveCursor(Eigen::Vector2i dir) Eigen::Vector2i curDirPos = mCursor; + GridEntry* cursorEntry; //spread out on search axis+ - while(mCursor.x() < mGridSize.x() && mCursor.y() < mGridSize.y()) + while(mCursor.x() < mGridSize.x() && mCursor.y() < mGridSize.y() + && mCursor.x() >= 0 && mCursor.y() >= 0) { - if(cursorValid() && getCell(mCursor.x(), mCursor.y())->canFocus) + cursorEntry = getCellAt(mCursor); + if(cursorEntry && cursorEntry->canFocus && cursorEntry != currentCursorEntry) { onCursorMoved(origCursor, mCursor); - return; + return true; } mCursor += searchAxis; @@ -372,12 +290,14 @@ void ComponentGrid::moveCursor(Eigen::Vector2i dir) //now again on search axis- mCursor = curDirPos; - while(mCursor.x() >= 0 && mCursor.y() >= 0) + while(mCursor.x() >= 0 && mCursor.y() >= 0 + && mCursor.x() < mGridSize.x() && mCursor.y() < mGridSize.y()) { - if(cursorValid() && getCell(mCursor.x(), mCursor.y())->canFocus) + cursorEntry = getCellAt(mCursor); + if(cursorEntry && cursorEntry->canFocus && cursorEntry != currentCursorEntry) { onCursorMoved(origCursor, mCursor); - return; + return true; } mCursor -= searchAxis; @@ -388,31 +308,36 @@ void ComponentGrid::moveCursor(Eigen::Vector2i dir) //failed to find another focusable element in this direction mCursor = origCursor; + return false; +} + +void ComponentGrid::onFocusLost() +{ + GridEntry* cursorEntry = getCellAt(mCursor); + if(cursorEntry) + cursorEntry->component->onFocusLost(); +} + +void ComponentGrid::onFocusGained() +{ + GridEntry* cursorEntry = getCellAt(mCursor); + if(cursorEntry) + cursorEntry->component->onFocusGained(); } bool ComponentGrid::cursorValid() { - if(mCursor.x() < 0 || mCursor.y() < 0 || mCursor.x() >= mGridSize.x() || mCursor.y() >= mGridSize.y()) - return false; - - return getCell(mCursor.x(), mCursor.y()) != NULL; + return getCellAt(mCursor) != NULL; } void ComponentGrid::update(int deltaTime) { - for(auto iter = mEntries.begin(); iter != mEntries.end(); iter++) + // update ALL THE THINGS + GridEntry* cursorEntry = getCellAt(mCursor); + for(auto it = mCells.begin(); it != mCells.end(); it++) { - switch((*iter)->updateType) - { - case UpdateAlways: - (*iter)->component->update(deltaTime); - break; - - case UpdateFocused: - if(cursorValid() && getCell(mCursor.x(), mCursor.y())->component == (*iter)->component) - (*iter)->component->update(deltaTime); - break; - } + if(it->updateType == UPDATE_ALWAYS || (it->updateType == UPDATE_WHEN_SELECTED && cursorEntry == &(*it))) + it->component->update(deltaTime); } } @@ -420,79 +345,72 @@ void ComponentGrid::render(const Eigen::Affine3f& parentTrans) { Eigen::Affine3f trans = parentTrans * getTransform(); - //draw cursor - if(cursorValid()) - { - ComponentEntry* entry = getCell(mCursor.x(), mCursor.y()); - Eigen::Affine3f entryTrans = trans * entry->component->getTransform(); - Renderer::setMatrix(entryTrans); - - Renderer::drawRect(0, 0, 4, 4, 0xFF0000FF); - Renderer::drawRect(0, 0, (int)entry->component->getSize().x(), (int)entry->component->getSize().y(), 0x0000AA22); - } - - for(auto iter = mEntries.begin(); iter != mEntries.end(); iter++) - { - (*iter)->component->render(trans); - } - + renderChildren(trans); - //draw cell outlines - /*Renderer::setMatrix(trans); - Eigen::Vector2i pos(0, 0); - for(int x = 0; x < mGridSize.x(); x++) + // draw cell separators + if(mLines.size()) { - for(int y = 0; y < mGridSize.y(); y++) - { - Renderer::drawRect(pos.x(), pos.y(), getColumnWidth(x), 2, 0x000000AA); - Renderer::drawRect(pos.x(), pos.y(), 2, getRowHeight(y), 0x000000AA); - Renderer::drawRect(pos.x() + getColumnWidth(x), pos.y(), 2, getRowHeight(y), 0x000000AA); - Renderer::drawRect(pos.x(), pos.y() + getRowHeight(y) - 2, getColumnWidth(x), 2, 0x000000AA); + Renderer::setMatrix(trans); - pos[1] += getRowHeight(y); - } + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); - pos[1] = 0; - pos[0] += getColumnWidth(x); - }*/ + glVertexPointer(2, GL_FLOAT, 0, &mLines[0].x); + glColorPointer(4, GL_UNSIGNED_BYTE, 0, mLineColors.data()); - + glDrawArrays(GL_LINES, 0, mLines.size()); + + glDisable(GL_BLEND); + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + } } void ComponentGrid::textInput(const char* text) { - if(getSelectedComponent() != NULL) - getSelectedComponent()->textInput(text); -} - -void ComponentGrid::onPositionChanged() -{ - updateComponentOffsets(); -} - -GuiComponent* ComponentGrid::getSelectedComponent() -{ - if(!cursorValid()) - return NULL; - return getCell(mCursor.x(), mCursor.y())->component; + GridEntry* selectedEntry = getCellAt(mCursor); + if(selectedEntry != NULL) + selectedEntry->component->textInput(text); } void ComponentGrid::onCursorMoved(Eigen::Vector2i from, Eigen::Vector2i to) { - if(from != Eigen::Vector2i(-1, -1)) - getCell(from.x(), from.y())->component->onFocusLost(); + GridEntry* cell = getCellAt(from); + if(cell) + cell->component->onFocusLost(); - if(to != Eigen::Vector2i(-1, -1)) - getCell(to.x(), to.y())->component->onFocusGained(); + cell = getCellAt(to); + if(cell) + cell->component->onFocusGained(); updateHelpPrompts(); } +void ComponentGrid::setCursorTo(const std::shared_ptr& comp) +{ + for(auto it = mCells.begin(); it != mCells.end(); it++) + { + if(it->component == comp) + { + Eigen::Vector2i oldCursor = mCursor; + mCursor = it->pos; + onCursorMoved(oldCursor, mCursor); + return; + } + } + + // component not found!! + assert(false); +} + std::vector ComponentGrid::getHelpPrompts() { std::vector prompts; - if(cursorValid()) - prompts = getSelectedComponent()->getHelpPrompts(); + GridEntry* e = getCellAt(mCursor); + if(e) + prompts = e->component->getHelpPrompts(); bool canScrollVert = true; bool canScrollHoriz = true; diff --git a/src/components/ComponentGrid.h b/src/components/ComponentGrid.h index 635f94fd0..0cd756cce 100644 --- a/src/components/ComponentGrid.h +++ b/src/components/ComponentGrid.h @@ -2,63 +2,87 @@ #include "../GuiComponent.h" +namespace GridFlags +{ + enum UpdateType + { + UPDATE_ALWAYS, + UPDATE_WHEN_SELECTED, + UPDATE_NEVER + }; + + enum Border : unsigned int + { + BORDER_NONE = 0, + + BORDER_TOP = 1, + BORDER_BOTTOM = 2, + BORDER_LEFT = 4, + BORDER_RIGHT = 8 + }; +}; + // Used to arrange a bunch of components in a spreadsheet-esque grid. class ComponentGrid : public GuiComponent { public: - ComponentGrid(Window* window, Eigen::Vector2i gridDimensions); + ComponentGrid(Window* window, const Eigen::Vector2i& gridDimensions); virtual ~ComponentGrid(); - enum UpdateBehavior - { - UpdateAlways, UpdateFocused - }; + bool removeEntry(const std::shared_ptr& comp); - enum AlignmentType - { - AlignLeft, AlignRight, AlignCenter - }; - - //DO NOT USE NEGATIVE NUMBERS FOR POSITION OR SIZE. - void setEntry(Eigen::Vector2i pos, Eigen::Vector2i size, GuiComponent* component, bool canFocus, AlignmentType align, - Eigen::Matrix autoFit = Eigen::Matrix(true, true), UpdateBehavior updateType = UpdateAlways); - - void removeEntriesIn(Eigen::Vector2i pos, Eigen::Vector2i size); - - void onPositionChanged() override; + void setEntry(const std::shared_ptr& comp, const Eigen::Vector2i& pos, bool canFocus, bool resize = true, + const Eigen::Vector2i& size = Eigen::Vector2i(1, 1), unsigned int border = GridFlags::BORDER_NONE, GridFlags::UpdateType updateType = GridFlags::UPDATE_ALWAYS); void textInput(const char* text) override; bool input(InputConfig* config, Input input) override; void update(int deltaTime) override; void render(const Eigen::Affine3f& parentTrans) override; - - void forceColumnWidth(int col, unsigned int size); - void forceRowHeight(int row, unsigned int size); - - void updateComponent(GuiComponent* cmp); + void onSizeChanged() override; void resetCursor(); bool cursorValid(); - GuiComponent* getSelectedComponent(); + float getColWidth(int col); + float getRowHeight(int row); - void moveCursor(Eigen::Vector2i dir); + void setColWidthPerc(int col, float width); + void setRowHeightPerc(int row, float height); + + bool moveCursor(Eigen::Vector2i dir); + void setCursorTo(const std::shared_ptr& comp); + + inline std::shared_ptr getSelectedComponent() + { + GridEntry* e = getCellAt(mCursor); + if(e) + return e->component; + else + return nullptr; + } + + void onFocusLost() override; + void onFocusGained() override; virtual std::vector getHelpPrompts() override; private: - class ComponentEntry + class GridEntry { public: Eigen::Vector2i pos; Eigen::Vector2i dim; - GuiComponent* component; - UpdateBehavior updateType; - AlignmentType alignment; + std::shared_ptr component; bool canFocus; + bool resize; + GridFlags::UpdateType updateType; + unsigned int border; - ComponentEntry() : component(NULL), updateType(UpdateAlways), canFocus(true), alignment(AlignCenter) {}; - ComponentEntry(Eigen::Vector2i p, Eigen::Vector2i d, GuiComponent* comp, UpdateBehavior update, bool focus, AlignmentType align) : pos(p), dim(d), component(comp), updateType(update), canFocus(focus), alignment(align) {}; + GridEntry(const Eigen::Vector2i& p = Eigen::Vector2i::Zero(), const Eigen::Vector2i& d = Eigen::Vector2i::Zero(), + const std::shared_ptr& cmp = nullptr, bool f = false, bool r = true, + GridFlags::UpdateType u = GridFlags::UPDATE_ALWAYS, unsigned int b = GridFlags::BORDER_NONE) : + pos(p), dim(d), component(cmp), canFocus(f), resize(r), updateType(u), border(b) + {}; operator bool() const { @@ -66,30 +90,30 @@ private: } }; - //Offset we render components by (for scrolling). [unimplemented] - Eigen::Vector2f mComponentOffset; + float* mRowHeights; + float* mColWidths; + + struct Vert + { + Vert(float xi = 0, float yi = 0) : x(xi), y(yi) {}; + float x; + float y; + }; + std::vector mLines; + std::vector mLineColors; + + // Update position & size + void updateCellComponent(const GridEntry& cell); + void updateSeparators(); + + GridEntry* getCellAt(int x, int y); + inline GridEntry* getCellAt(const Eigen::Vector2i& pos) { return getCellAt(pos.x(), pos.y()); } + Eigen::Vector2i mGridSize; - ComponentEntry** mGrid; - std::vector mEntries; - void makeCells(Eigen::Vector2i size); - void setCell(unsigned int x, unsigned int y, ComponentEntry* entry); - ComponentEntry* getCell(unsigned int x, unsigned int y); - unsigned int getColumnWidth(int col); - unsigned int getRowHeight(int row); - - unsigned int* mColumnWidths; - unsigned int* mRowHeights; - bool* mColumnWidthForced; - bool* mRowHeightForced; - - Eigen::Vector3f getCellOffset(Eigen::Vector2i gridPos); - void updateSize(); + std::vector mCells; void onCursorMoved(Eigen::Vector2i from, Eigen::Vector2i to); Eigen::Vector2i mCursor; - - void updateComponentOffsets(); - void updateCellSize(ComponentEntry* e, bool updWidth = true, bool updHeight = true); }; diff --git a/src/components/ComponentList.cpp b/src/components/ComponentList.cpp index c4a5de9c0..d21ede3ff 100644 --- a/src/components/ComponentList.cpp +++ b/src/components/ComponentList.cpp @@ -6,6 +6,7 @@ ComponentList::ComponentList(Window* window) : IList(wi { mSelectorBarOffset = 0; mCameraOffset = 0; + mFocused = false; } void ComponentList::addRow(const ComponentListRow& row, bool setCursorHere) @@ -41,6 +42,16 @@ void ComponentList::onSizeChanged() onCursorChanged(mScrollVelocity != 0 ? CURSOR_SCROLLING : CURSOR_STOPPED); } +void ComponentList::onFocusLost() +{ + mFocused = false; +} + +void ComponentList::onFocusGained() +{ + mFocused = true; +} + bool ComponentList::input(InputConfig* config, Input input) { if(size() == 0) @@ -127,23 +138,27 @@ void ComponentList::render(const Eigen::Affine3f& parentTrans) // draw our entries renderChildren(trans); - // draw selector bar + // custom rendering Renderer::setMatrix(trans); - // inversion: src * (1 - dst) + dst * 0 = where src = 1 - // need a function that goes roughly 0x777777 -> 0xFFFFFF - // and 0xFFFFFF -> 0x777777 - // (1 - dst) + 0x77 + // draw selector bar + if(mFocused) + { + // inversion: src * (1 - dst) + dst * 0 = where src = 1 + // need a function that goes roughly 0x777777 -> 0xFFFFFF + // and 0xFFFFFF -> 0x777777 + // (1 - dst) + 0x77 - const float selectedRowHeight = getRowHeight(mEntries.at(mCursor).data); - Renderer::drawRect(0, (int)mSelectorBarOffset, (int)mSize.x(), (int)selectedRowHeight, 0xFFFFFFFF, - GL_ONE_MINUS_DST_COLOR, GL_ZERO); - Renderer::drawRect(0, (int)mSelectorBarOffset, (int)mSize.x(), (int)selectedRowHeight, 0x777777FF, - GL_ONE, GL_ONE); + const float selectedRowHeight = getRowHeight(mEntries.at(mCursor).data); + Renderer::drawRect(0, (int)mSelectorBarOffset, (int)mSize.x(), (int)selectedRowHeight, 0xFFFFFFFF, + GL_ONE_MINUS_DST_COLOR, GL_ZERO); + Renderer::drawRect(0, (int)mSelectorBarOffset, (int)mSize.x(), (int)selectedRowHeight, 0x777777FF, + GL_ONE, GL_ONE); - // hack to draw 2px dark on left/right of the bar - Renderer::drawRect(0, (int)mSelectorBarOffset, 2, (int)selectedRowHeight, 0x878787FF); - Renderer::drawRect((int)mSize.x() - 2, (int)mSelectorBarOffset, 2, (int)selectedRowHeight, 0x878787FF); + // hack to draw 2px dark on left/right of the bar + Renderer::drawRect(0, (int)mSelectorBarOffset, 2, (int)selectedRowHeight, 0x878787FF); + Renderer::drawRect((int)mSize.x() - 2, (int)mSelectorBarOffset, 2, (int)selectedRowHeight, 0x878787FF); + } // draw separators float y = 0; diff --git a/src/components/ComponentList.h b/src/components/ComponentList.h index e5315391c..d4d58c651 100644 --- a/src/components/ComponentList.h +++ b/src/components/ComponentList.h @@ -52,11 +52,17 @@ public: void render(const Eigen::Affine3f& parentTrans) override; void onSizeChanged() override; + void onFocusGained() override; + void onFocusLost() override; + + inline int getCursorId() const { return mCursor; } protected: void onCursorChanged(const CursorState& state) override; private: + bool mFocused; + void updateElementPosition(const ComponentListRow& row); void updateElementSize(const ComponentListRow& row); diff --git a/src/components/MenuComponent.cpp b/src/components/MenuComponent.cpp index ba9a73212..98aef7954 100644 --- a/src/components/MenuComponent.cpp +++ b/src/components/MenuComponent.cpp @@ -1,31 +1,67 @@ #include "MenuComponent.h" +#include "ButtonComponent.h" + +using namespace Eigen; MenuComponent::MenuComponent(Window* window, const char* title) : GuiComponent(window), - mBackground(window), mTitle(window), mList(window) -{ + mBackground(window), mGrid(window, Vector2i(1, 3)) +{ + addChild(&mBackground); + addChild(&mGrid); + mBackground.setImagePath(":/frame.png"); - mTitle.setFont(Font::get(FONT_SIZE_LARGE)); - mTitle.setText(title); - mTitle.setColor(0x555555FF); - mTitle.setCentered(true); + // set up title which will never change + mTitle = std::make_shared(mWindow, title, Font::get(FONT_SIZE_LARGE), 0x555555FF, true); + mGrid.setEntry(mTitle, Vector2i(0, 0), false); - addChild(&mBackground); - addChild(&mTitle); - addChild(&mList); + // set up list which will never change (externally, anyway) + mList = std::make_shared(mWindow); + mGrid.setEntry(mList, Vector2i(0, 1), true); setSize(Renderer::getScreenWidth() * 0.5f, Renderer::getScreenHeight() * 0.75f); + updateGrid(); + mGrid.resetCursor(); } void MenuComponent::onSizeChanged() { mBackground.fitTo(mSize, Eigen::Vector3f::Zero(), Eigen::Vector2f(-32, -32)); - const float titlePadding = mTitle.getFont()->getHeight() * 0.2f; - - mTitle.setSize(mSize.x(), (float)mTitle.getFont()->getHeight()); - mTitle.setPosition(0, titlePadding / 2); - - mList.setPosition(0, mTitle.getSize().y() + titlePadding); - mList.setSize(mSize.x(), mSize.y() - mTitle.getSize().y() - titlePadding); + // update grid row/col sizes + mGrid.setRowHeightPerc(0, mTitle->getSize().y() / mSize.y()); + mGrid.setRowHeightPerc(2, mButtonGrid ? (mButtonGrid->getSize().y() + 32) / mSize.y() : 0.07f); + + mGrid.setSize(mSize); +} + +void MenuComponent::addButton(const std::string& name, const std::string& helpText, const std::function& callback) +{ + mButtons.push_back(std::make_shared(mWindow, name, helpText, callback)); + updateGrid(); + onSizeChanged(); +} + +void MenuComponent::updateGrid() +{ + if(mButtonGrid) + mGrid.removeEntry(mButtonGrid); + + if(mButtons.size()) + { + mButtonGrid = std::make_shared(mWindow, Vector2i(mButtons.size(), 1)); + + float buttonGridWidth = 16.0f * mButtons.size(); // initialize to padding + for(int i = 0; i < (int)mButtons.size(); i++) + { + mButtonGrid->setEntry(mButtons.at(i), Vector2i(i, 0), true, false); + buttonGridWidth += mButtons.at(i)->getSize().x(); + } + + mButtonGrid->setSize(buttonGridWidth, mButtons.at(0)->getSize().y()); + + mGrid.setEntry(mButtonGrid, Vector2i(0, 2), true, false); + }else{ + mButtonGrid.reset(); + } } diff --git a/src/components/MenuComponent.h b/src/components/MenuComponent.h index 1dd577bd1..98a0a54fb 100644 --- a/src/components/MenuComponent.h +++ b/src/components/MenuComponent.h @@ -3,6 +3,9 @@ #include "NinePatchComponent.h" #include "ComponentList.h" #include "TextComponent.h" +#include "ComponentGrid.h" + +class ButtonComponent; class MenuComponent : public GuiComponent { @@ -11,7 +14,7 @@ public: void onSizeChanged() override; - inline void addRow(const ComponentListRow& row, bool setCursorHere = false) { mList.addRow(row, setCursorHere); } + inline void addRow(const ComponentListRow& row, bool setCursorHere = false) { mList->addRow(row, setCursorHere); } inline void addWithLabel(const std::string& label, const std::shared_ptr& comp, bool setCursorHere = false) { @@ -21,8 +24,19 @@ public: addRow(row, setCursorHere); } + void addButton(const std::string& label, const std::string& helpText, const std::function& callback); + + inline void setCursorToList() { mGrid.setCursorTo(mList); } + inline void setCursorToButtons() { assert(mButtonGrid); mGrid.setCursorTo(mButtonGrid); } + private: + void updateGrid(); + NinePatchComponent mBackground; - TextComponent mTitle; - ComponentList mList; + ComponentGrid mGrid; + + std::shared_ptr mTitle; + std::shared_ptr mList; + std::shared_ptr mButtonGrid; + std::vector< std::shared_ptr > mButtons; }; diff --git a/src/components/ScraperSearchComponent.cpp b/src/components/ScraperSearchComponent.cpp new file mode 100644 index 000000000..8974df44f --- /dev/null +++ b/src/components/ScraperSearchComponent.cpp @@ -0,0 +1,238 @@ +#include "ScraperSearchComponent.h" + +#include "../components/TextComponent.h" +#include "../components/ScrollableContainer.h" +#include "../components/ImageComponent.h" +#include "../components/ComponentList.h" +#include "../HttpReq.h" +#include "../Settings.h" +#include "../Log.h" + +ScraperSearchComponent::ScraperSearchComponent(Window* window, SearchType type) : GuiComponent(window), + mGrid(window, Eigen::Vector2i(4, 3)), + mSearchType(type) +{ + mSearchParams.system = NULL; + mSearchParams.game = NULL; + + addChild(&mGrid); + + using namespace Eigen; + + // left spacer (empty component, needed for borders) + mGrid.setEntry(std::make_shared(mWindow), Vector2i(0, 0), false, false, Vector2i(1, 3), GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM); + + // selected result name + mResultName = std::make_shared(mWindow, "Result name", Font::get(FONT_SIZE_MEDIUM), 0x777777FF); + mGrid.setEntry(mResultName, Vector2i(1, 0), false, true, Vector2i(2, 1), GridFlags::BORDER_TOP); + + // selected result thumbnail + mResultThumbnail = std::make_shared(mWindow); + mGrid.setEntry(mResultThumbnail, Vector2i(1, 1), false, false, Vector2i(1, 1)); + + // selected result desc + container + mDescContainer = std::make_shared(mWindow); + mResultDesc = std::make_shared(mWindow, "Result desc", Font::get(FONT_SIZE_SMALL), 0x777777FF); + mDescContainer->addChild(mResultDesc.get()); + mDescContainer->setAutoScroll(2200, 0.015f); + + // result list + mResultList = std::make_shared(mWindow); + + updateViewStyle(); +} + +void ScraperSearchComponent::onSizeChanged() +{ + mGrid.setSize(mSize); + + // column widths + mGrid.setColWidthPerc(0, 0.01f); + mGrid.setColWidthPerc(1, 0.25f); + mGrid.setColWidthPerc(2, 0.25f); + mGrid.setColWidthPerc(3, 0.49f); + + // row heights + const float fontHeightPerc = (mResultName->getFont()->getHeight()) / mGrid.getSize().y(); + mGrid.setRowHeightPerc(0, fontHeightPerc); // result name + mGrid.setRowHeightPerc(2, 0.375f); // description + + mResultThumbnail->setMaxSize(mGrid.getColWidth(1), mGrid.getRowHeight(1)); + mResultDesc->setSize(mDescContainer->getSize().x(), 0); // make desc text wrap at edge of container +} + +void ScraperSearchComponent::updateViewStyle() +{ + using namespace Eigen; + + // unlink description and result list + mGrid.removeEntry(mResultDesc); + mGrid.removeEntry(mResultList); + + // add them back depending on search type + if(mSearchType == ALWAYS_ACCEPT_FIRST_RESULT) + { + // show description on the right + mGrid.setEntry(mDescContainer, Vector2i(3, 0), false, true, Vector2i(1, 3), GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM); + mResultDesc->setSize(mDescContainer->getSize().x(), 0); // make desc text wrap at edge of container + }else{ + // show result list on the right + mGrid.setEntry(mResultList, Vector2i(3, 0), true, true, Vector2i(1, 3), GridFlags::BORDER_LEFT | GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM); + + // show description under image/info + mGrid.setEntry(mDescContainer, Vector2i(1, 2), false, true, Vector2i(2, 1), GridFlags::BORDER_BOTTOM); + mResultDesc->setSize(mDescContainer->getSize().x(), 0); // make desc text wrap at edge of container + } +} + +void ScraperSearchComponent::setSearchParams(const ScraperSearchParams& params) +{ + mSearchParams = params; + search(); +} + +void ScraperSearchComponent::search() +{ + Settings::getInstance()->getScraper()->getResultsAsync(mSearchParams, mWindow, std::bind(&ScraperSearchComponent::onSearchReceived, this, std::placeholders::_1)); +} + +void ScraperSearchComponent::onSearchReceived(std::vector results) +{ + mResultList->clear(); + + mScraperResults = results; + + const int end = results.size() > 5 ? 5 : results.size(); // at max display 5 + + auto font = Font::get(FONT_SIZE_MEDIUM); + unsigned int color = 0x777777FF; + if(end == 0) + { + ComponentListRow row; + row.addElement(std::make_shared(mWindow, "No games found!", font, color), true); + mResultList->addRow(row); + mGrid.resetCursor(); + }else{ + ComponentListRow row; + for(int i = 0; i < end; i++) + { + row.elements.clear(); + row.addElement(std::make_shared(mWindow, results.at(i).get("name"), font, color), true); + mResultList->addRow(row); + } + mGrid.resetCursor(); + } + + updateInfoPane(); + + if(mSearchType == ALWAYS_ACCEPT_FIRST_RESULT) + { + if(mScraperResults.size() == 0) + returnResult(NULL); + else + returnResult(&mScraperResults.front()); + }else if(mSearchType == ALWAYS_ACCEPT_MATCHING_CRC) + { + // TODO + assert(false); + } +} + +int ScraperSearchComponent::getSelectedIndex() +{ + if(mScraperResults.size() && mGrid.getSelectedComponent() != mResultList) + return -1; + + return mResultList->getCursorId(); +} + +void ScraperSearchComponent::updateInfoPane() +{ + int i = getSelectedIndex(); + if(i != -1 && (int)mScraperResults.size() > i) + { + mResultName->setText(mScraperResults.at(i).get("name")); + mResultDesc->setText(mScraperResults.at(i).get("desc")); + mDescContainer->setScrollPos(Eigen::Vector2d(0, 0)); + mDescContainer->resetAutoScrollTimer(); + + std::string thumb = mScraperResults.at(i).get("thumbnail"); + mResultThumbnail->setImage(""); + if(!thumb.empty()) + mThumbnailReq = std::unique_ptr(new HttpReq(thumb)); + else + mThumbnailReq.reset(); + } +} + +bool ScraperSearchComponent::input(InputConfig* config, Input input) +{ + if(config->isMappedTo("a", input) && input.value != 0) + { + //if you're on a result + if(getSelectedIndex() != -1) + { + returnResult(&mScraperResults.at(getSelectedIndex())); + return true; + } + } + + bool ret = GuiComponent::input(config, input); + + if(config->isMappedTo("up", input) || config->isMappedTo("down", input) && input.value != 0) + { + updateInfoPane(); + } + + return ret; +} + +void ScraperSearchComponent::returnResult(MetaDataList* result) +{ + assert(mAcceptCallback); + mAcceptCallback(result); +} + +void ScraperSearchComponent::update(int deltaTime) +{ + if(mThumbnailReq && mThumbnailReq->status() != HttpReq::REQ_IN_PROGRESS) + { + updateThumbnail(); + } + + GuiComponent::update(deltaTime); +} + +void ScraperSearchComponent::updateThumbnail() +{ + if(mThumbnailReq && mThumbnailReq->status() == HttpReq::REQ_SUCCESS) + { + std::string content = mThumbnailReq->getContent(); + mResultThumbnail->setImage(content.data(), content.length()); + }else{ + LOG(LogWarning) << "thumbnail req failed: " << mThumbnailReq->getErrorMsg(); + mResultThumbnail->setImage(""); + } + + mThumbnailReq.reset(); + mGrid.onSizeChanged(); // a hack to fix the thumbnail position since its size changed +} + +std::vector ScraperSearchComponent::getHelpPrompts() +{ + std::vector prompts = mGrid.getHelpPrompts(); + if(getSelectedIndex() != -1) + prompts.push_back(HelpPrompt("a", "accept result")); + + return prompts; +} + +void ScraperSearchComponent::onFocusGained() +{ + mGrid.onFocusGained(); +} + +void ScraperSearchComponent::onFocusLost() +{ + mGrid.onFocusLost(); +} \ No newline at end of file diff --git a/src/components/ScraperSearchComponent.h b/src/components/ScraperSearchComponent.h new file mode 100644 index 000000000..09a7bf221 --- /dev/null +++ b/src/components/ScraperSearchComponent.h @@ -0,0 +1,64 @@ +#pragma once + +#include "../GuiComponent.h" +#include "../scrapers/Scraper.h" +#include "../components/ComponentGrid.h" +#include + +#define MAX_SCRAPER_RESULTS 5 + +class ComponentList; +class TextEditComponent; +class ImageComponent; +class ScrollableContainer; +class HttpReq; + +class ScraperSearchComponent : public GuiComponent +{ +public: + enum SearchType + { + ALWAYS_ACCEPT_FIRST_RESULT, + ALWAYS_ACCEPT_MATCHING_CRC, + NEVER_AUTO_ACCEPT + }; + + ScraperSearchComponent(Window* window, SearchType searchType = NEVER_AUTO_ACCEPT); + + void setSearchParams(const ScraperSearchParams& params); + inline void setAcceptCallback(const std::function& acceptCallback) { mAcceptCallback = acceptCallback; } + + bool input(InputConfig* config, Input input) override; + void update(int deltaTime) override; + std::vector getHelpPrompts() override; + void onSizeChanged() override; + void onFocusGained() override; + void onFocusLost() override; + +private: + void updateViewStyle(); + void updateThumbnail(); + void updateInfoPane(); + + void search(); + void onSearchReceived(std::vector results); + + int getSelectedIndex(); + + void returnResult(MetaDataList* result); + + ComponentGrid mGrid; + + std::shared_ptr mResultName; + std::shared_ptr mDescContainer; + std::shared_ptr mResultDesc; + std::shared_ptr mResultThumbnail; + std::shared_ptr mResultList; + + SearchType mSearchType; + ScraperSearchParams mSearchParams; + std::function mAcceptCallback; + + std::vector mScraperResults; + std::unique_ptr mThumbnailReq; +}; diff --git a/src/components/TextComponent.cpp b/src/components/TextComponent.cpp index df80b8392..e1c2abbf2 100644 --- a/src/components/TextComponent.cpp +++ b/src/components/TextComponent.cpp @@ -9,8 +9,9 @@ TextComponent::TextComponent(Window* window) : GuiComponent(window), { } -TextComponent::TextComponent(Window* window, const std::string& text, const std::shared_ptr& font, unsigned int color, Eigen::Vector3f pos, Eigen::Vector2f size) : GuiComponent(window), - mFont(NULL), mColor(0x000000FF), mAutoCalcExtent(true, true), mCentered(false) +TextComponent::TextComponent(Window* window, const std::string& text, const std::shared_ptr& font, unsigned int color, bool center, + Eigen::Vector3f pos, Eigen::Vector2f size) : GuiComponent(window), + mFont(NULL), mColor(0x000000FF), mAutoCalcExtent(true, true), mCentered(center) { setFont(font); setColor(color); @@ -69,6 +70,10 @@ void TextComponent::render(const Eigen::Affine3f& parentTrans) { Eigen::Affine3f trans = parentTrans * getTransform(); + Eigen::Vector3f dim(mSize.x(), mSize.y(), 0); + dim = trans * dim - trans.translation(); + Renderer::pushClipRect(Eigen::Vector2i((int)trans.translation().x(), (int)trans.translation().y()), Eigen::Vector2i((int)dim.x(), (int)dim.y())); + if(mTextCache) { if(mCentered) @@ -86,6 +91,8 @@ void TextComponent::render(const Eigen::Affine3f& parentTrans) mFont->renderTextCache(mTextCache.get()); } + Renderer::popClipRect(); + GuiComponent::renderChildren(trans); } diff --git a/src/components/TextComponent.h b/src/components/TextComponent.h index a6355617a..e3039a2a1 100644 --- a/src/components/TextComponent.h +++ b/src/components/TextComponent.h @@ -15,7 +15,8 @@ class TextComponent : public GuiComponent { public: TextComponent(Window* window); - TextComponent(Window* window, const std::string& text, const std::shared_ptr& font, unsigned int color = 0x000000FF, Eigen::Vector3f pos = Eigen::Vector3f::Zero(), Eigen::Vector2f size = Eigen::Vector2f::Zero()); + TextComponent(Window* window, const std::string& text, const std::shared_ptr& font, unsigned int color = 0x000000FF, bool center = false, + Eigen::Vector3f pos = Eigen::Vector3f::Zero(), Eigen::Vector2f size = Eigen::Vector2f::Zero()); void setFont(const std::shared_ptr& font); void onSizeChanged() override; diff --git a/src/guis/GuiGameScraper.cpp b/src/guis/GuiGameScraper.cpp index bfee6c299..e9ec0734a 100644 --- a/src/guis/GuiGameScraper.cpp +++ b/src/guis/GuiGameScraper.cpp @@ -4,152 +4,88 @@ #include "../scrapers/Scraper.h" #include "../Settings.h" +#include "../components/TextComponent.h" +#include "../components/ButtonComponent.h" + GuiGameScraper::GuiGameScraper(Window* window, ScraperSearchParams params, std::function doneFunc, std::function skipFunc) : GuiComponent(window), - mList(window, Eigen::Vector2i(2, 7 + MAX_SCRAPER_RESULTS)), + mGrid(window, Eigen::Vector2i(1, 3)), mBox(window, ":/frame.png"), - mHeader(window, getCleanFileName(params.game->getPath()), Font::get(FONT_SIZE_MEDIUM)), - mResultName(window, "", Font::get(FONT_SIZE_MEDIUM)), - mResultInfo(window), - mResultDesc(window, "", Font::get(FONT_SIZE_SMALL)), - mResultThumbnail(window), - - mSearchLabel(window, "Search for: ", Font::get(FONT_SIZE_SMALL)), - mSearchText(window), - mSearchParams(params), mDoneFunc(doneFunc), - mSkipFunc(skipFunc) + mSkipFunc(skipFunc), + mSearchCountdown(2) { + // new screen: + // FILE NAME //-------------------------------------- - //Name................. | - //Desc................. | PREVIEW - //..................... | IMAGE? - //....(autoscroll)..... | + // Result Title | Result #1 + // |-------| ..... | Result #2 + // | IMG | info | Result #3 + // |-------| ..... | ......... + // | ......... + // DESCRIPTION........| ......... + // ...................| ......... + // ...................| ......... //-------------------------------------- - //Search for: [_______________________] - //-------------------------------------- - //Result #1 Name - //Result #2 Name - //Result #3 Name - //Result #4 Name - //Result #5 Name + // [SEARCH NAME] [CANCEL] + addChild(&mBox); - addChild(&mList); + addChild(&mGrid); - float sw = (float)Renderer::getScreenWidth(); - float sh = (float)Renderer::getScreenHeight(); + setSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); - float listWidth = sw * 0.7f; - float col1Width = listWidth * 0.7f; - float col2Width = listWidth * 0.3f; - mList.forceColumnWidth(0, (unsigned int)col1Width); - mList.forceColumnWidth(1, (unsigned int)col2Width); - - using namespace Eigen; + mGrid.setSize(mSize.x() * 0.7f, mSize.y() * 0.65f); - mList.setEntry(Vector2i(0, 0), Vector2i(2, 1), &mHeader, false, ComponentGrid::AlignCenter); + auto headerFont = Font::get(FONT_SIZE_LARGE); - //y = 1 is a spacer row + mGrid.setRowHeightPerc(0, headerFont->getHeight() / mGrid.getSize().y()); // header + mGrid.setRowHeightPerc(2, 0.19f); // buttons - mResultName.setText(params.game->getName()); - mResultName.setColor(0x3B56CCFF); - mList.setEntry(Vector2i(0, 1), Vector2i(1, 1), &mResultName, false, ComponentGrid::AlignLeft); + // header + mGrid.setEntry(std::make_shared(mWindow, getCleanFileName(mSearchParams.game->getName()), + headerFont, 0x777777FF, true), Eigen::Vector2i(0, 0), false, true); - mResultDesc.setText(params.game->metadata.get("desc")); - mResultDesc.setSize(col1Width, 0); - mResultInfo.addChild(&mResultDesc); - mResultInfo.setSize(mResultDesc.getSize().x(), mResultDesc.getFont()->getHeight() * 3.0f); - mList.setEntry(Vector2i(0, 2), Vector2i(1, 1), &mResultInfo, false, ComponentGrid::AlignLeft); + // ScraperSearchComponent + mSearch = std::make_shared(window, ScraperSearchComponent::NEVER_AUTO_ACCEPT); + mGrid.setEntry(mSearch, Eigen::Vector2i(0, 1), true); + + // buttons + auto buttonGrid = std::make_shared(mWindow, Eigen::Vector2i(3, 1)); - mResultThumbnail.setMaxSize(col2Width, mResultInfo.getSize().y()); - mList.setEntry(Vector2i(1, 2), Vector2i(1, 1), &mResultThumbnail, false, ComponentGrid::AlignCenter); + auto manualSearchBtn = std::make_shared(mWindow, "MANUAL SEARCH", "enter search terms"); + auto cancelBtn = std::make_shared(mWindow, "CANCEL", "cancel"); - //y = 3 is a spacer row + buttonGrid->setSize(manualSearchBtn->getSize().x() + cancelBtn->getSize().x() + 18, manualSearchBtn->getSize().y()); + buttonGrid->setColWidthPerc(0, 0.5f); + buttonGrid->setColWidthPerc(1, 0.5f); - mList.setEntry(Vector2i(0, 4), Vector2i(1, 1), &mSearchLabel, false, ComponentGrid::AlignLeft); - mSearchText.setValue(!params.nameOverride.empty() ? params.nameOverride : getCleanFileName(params.game->getPath())); - mSearchText.setSize(listWidth - mSearchLabel.getSize().x() - 20, mSearchText.getSize().y()); - mList.setEntry(Vector2i(1, 4), Vector2i(1, 1), &mSearchText, true, ComponentGrid::AlignRight); + buttonGrid->setEntry(manualSearchBtn, Eigen::Vector2i(0, 0), true, false); + buttonGrid->setEntry(cancelBtn, Eigen::Vector2i(1, 0), true, false); - //y = 5 is a spacer row + mGrid.setEntry(buttonGrid, Eigen::Vector2i(0, 2), true, false); - std::shared_ptr font = Font::get(FONT_SIZE_SMALL); - mResultNames.reserve(MAX_SCRAPER_RESULTS); - for(int i = 0; i < MAX_SCRAPER_RESULTS; i ++) - { - mResultNames.push_back(TextComponent(mWindow, "RESULT...", font)); - mResultNames.at(i).setColor(0x111111FF); - mList.forceRowHeight(6 + i, (unsigned int)mResultNames.at(i).getSize().y()); - } + // center everything + mGrid.setPosition((mSize.x() - mGrid.getSize().x()) / 2, (mSize.y() - mGrid.getSize().y()) / 2); + mBox.fitTo(mGrid.getSize(), mGrid.getPosition(), Eigen::Vector2f(-32, -32)); - mList.setPosition((sw - mList.getSize().x()) / 2, (sh - mList.getSize().y()) / 2); + mSearch->setAcceptCallback( [this](MetaDataList* result) { + if(result != NULL) + this->mDoneFunc(*result); + else if(this->mSkipFunc) + this->mSkipFunc(); - mBox.fitTo(mList.getSize(), mList.getPosition()); + delete this; + }); - mResultInfo.setAutoScroll(2200, 0.015f); - - mList.resetCursor(); -} - -void GuiGameScraper::search() -{ - //update mSearchParams - mSearchParams.nameOverride = mSearchText.getValue(); - - Settings::getInstance()->getScraper()->getResultsAsync(mSearchParams, mWindow, std::bind(&GuiGameScraper::onSearchDone, this, std::placeholders::_1)); -} - -void GuiGameScraper::onSearchDone(std::vector results) -{ - mList.removeEntriesIn(Eigen::Vector2i(0, 6), Eigen::Vector2i(1, 5)); - - mScraperResults = results; - - const int end = results.size() > 5 ? 5 : results.size(); //at max display 5 - - if(end == 0) - { - mResultNames.at(0).setText("No games found!"); - mList.setEntry(Eigen::Vector2i(0, 6), Eigen::Vector2i(1, 1), &mResultNames.at(0), false, ComponentGrid::AlignLeft); - }else{ - for(int i = 0; i < end; i++) - { - mResultNames.at(i).setText(results.at(i).get("name")); - mList.setEntry(Eigen::Vector2i(0, 6 + i), Eigen::Vector2i(1, 1), &mResultNames.at(i), true, ComponentGrid::AlignLeft); - } - } - - mList.resetCursor(); - mList.moveCursor(Eigen::Vector2i(0, 1)); //move cursor to first game if there is one - updateInfoPane(); -} - -int GuiGameScraper::getSelectedIndex() -{ - int index = 0; - for(auto iter = mResultNames.begin(); iter != mResultNames.end(); iter++, index++) - { - if(&(*iter) == mList.getSelectedComponent()) - return index; - } - - return -1; + mGrid.resetCursor(); + //mSearch->setSearchParams(params); // also starts the search } bool GuiGameScraper::input(InputConfig* config, Input input) { - if(config->isMappedTo("a", input) && input.value != 0) - { - //if you're on a result - if(getSelectedIndex() != -1) - { - mDoneFunc(mScraperResults.at(getSelectedIndex())); - delete this; - return true; - } - }else if(config->isMappedTo("b", input) && input.value != 0) + if(config->isMappedTo("b", input) && input.value) { if(mSkipFunc) mSkipFunc(); @@ -157,74 +93,23 @@ bool GuiGameScraper::input(InputConfig* config, Input input) return true; } - bool wasEditing = mSearchText.isEditing(); - bool ret = GuiComponent::input(config, input); - - if(config->isMappedTo("up", input) || config->isMappedTo("down", input) && input.value != 0) - { - updateInfoPane(); - } - - //stopped editing - if(wasEditing && !mSearchText.isEditing()) - { - //update results - search(); - } - - return ret; -} - -void GuiGameScraper::updateInfoPane() -{ - int i = getSelectedIndex(); - if(i != -1) - { - mResultName.setText(mScraperResults.at(i).get("name")); - mResultDesc.setText(mScraperResults.at(i).get("desc")); - mResultInfo.setScrollPos(Eigen::Vector2d(0, 0)); - mResultInfo.resetAutoScrollTimer(); - - std::string thumb = mScraperResults.at(i).get("thumbnail"); - mResultThumbnail.setImage(""); - if(!thumb.empty()) - mThumbnailReq = std::unique_ptr(new HttpReq(thumb)); - else - mThumbnailReq.reset(); - } + return GuiComponent::input(config, input); } void GuiGameScraper::update(int deltaTime) { - if(mThumbnailReq && mThumbnailReq->status() != HttpReq::REQ_IN_PROGRESS) + // HAAACK because AsyncReq wont get pushed in the right order if search happens on creation + if(mSearchCountdown > 0) { - updateThumbnail(); + mSearchCountdown--; + if(mSearchCountdown == 0) + mSearch->setSearchParams(mSearchParams); } GuiComponent::update(deltaTime); } -void GuiGameScraper::updateThumbnail() -{ - if(mThumbnailReq && mThumbnailReq->status() == HttpReq::REQ_SUCCESS) - { - std::string content = mThumbnailReq->getContent(); - mResultThumbnail.setImage(content.data(), content.length()); - }else{ - LOG(LogWarning) << "thumbnail req failed: " << mThumbnailReq->getErrorMsg(); - mResultThumbnail.setImage(""); - } - - mThumbnailReq.reset(); - mList.onPositionChanged(); // a hack to fix the thumbnail position since its size changed -} - std::vector GuiGameScraper::getHelpPrompts() { - std::vector prompts = mList.getHelpPrompts(); - if(getSelectedIndex() != -1) - prompts.push_back(HelpPrompt("a", "accept result")); - - prompts.push_back(HelpPrompt("b", "cancel/skip")); - return prompts; + return mGrid.getHelpPrompts(); } diff --git a/src/guis/GuiGameScraper.h b/src/guis/GuiGameScraper.h index 26af7a416..74e1e01e1 100644 --- a/src/guis/GuiGameScraper.h +++ b/src/guis/GuiGameScraper.h @@ -1,17 +1,8 @@ #pragma once #include "../GuiComponent.h" -#include "../scrapers/Scraper.h" -#include "../components/ComponentGrid.h" -#include "../components/TextComponent.h" -#include "../components/ScrollableContainer.h" -#include "../components/TextEditComponent.h" +#include "../components/ScraperSearchComponent.h" #include "../components/NinePatchComponent.h" -#include "../components/ImageComponent.h" -#include "../Settings.h" -#include "../HttpReq.h" - -#define MAX_SCRAPER_RESULTS 5 class GuiGameScraper : public GuiComponent { @@ -21,37 +12,18 @@ public: bool input(InputConfig* config, Input input) override; void update(int deltaTime) override; - void search(); - virtual std::vector getHelpPrompts() override; private: - int getSelectedIndex(); - void onSearchDone(std::vector results); - void updateInfoPane(); - void updateThumbnail(); + int mSearchCountdown; // haaack - ComponentGrid mList; + ComponentGrid mGrid; NinePatchComponent mBox; - TextComponent mHeader; - - TextComponent mResultName; - ScrollableContainer mResultInfo; - TextComponent mResultDesc; - ImageComponent mResultThumbnail; - - TextComponent mSearchLabel; - TextEditComponent mSearchText; - - std::vector mResultNames; + std::shared_ptr mSearch; ScraperSearchParams mSearchParams; - std::vector mScraperResults; - std::function mDoneFunc; std::function mSkipFunc; - - std::unique_ptr mThumbnailReq; }; diff --git a/src/guis/GuiMetaDataEd.cpp b/src/guis/GuiMetaDataEd.cpp index 87763b513..a99758b79 100644 --- a/src/guis/GuiMetaDataEd.cpp +++ b/src/guis/GuiMetaDataEd.cpp @@ -7,115 +7,50 @@ #include "GuiMsgBoxYesNo.h" #include -#define MDED_RESERVED_ROWS 3 - GuiMetaDataEd::GuiMetaDataEd(Window* window, MetaDataList* md, const std::vector& mdd, ScraperSearchParams scraperParams, const std::string& header, std::function saveCallback, std::function deleteFunc) : GuiComponent(window), mScraperParams(scraperParams), - mBox(mWindow, ":/frame.png", 0xAAAAAAFF, 0xCCCCCCFF), - mList(window, Eigen::Vector2i(2, mdd.size() + MDED_RESERVED_ROWS)), - mHeader(window), + mMenu(window, header.c_str()), mMetaDataDecl(mdd), mMetaData(md), - mSavedCallback(saveCallback), mDeleteFunc(deleteFunc), - mDeleteButton(window), mFetchButton(window), mSaveButton(window) + mSavedCallback(saveCallback), mDeleteFunc(deleteFunc) { - unsigned int sw = Renderer::getScreenWidth(); - unsigned int sh = Renderer::getScreenHeight(); + setSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); - addChild(&mBox); - - addChild(&mHeader); - mHeader.setText(header); - - //initialize buttons - mDeleteButton.setText("DELETE", "delete file"); - if(mDeleteFunc) - { - std::function deleteFileAndSelf = [&] { mDeleteFunc(); delete this; }; - std::function pressedFunc = [this, deleteFileAndSelf] { mWindow->pushGui(new GuiMsgBoxYesNo(mWindow, "This will delete a file!\nAre you sure?", deleteFileAndSelf)); }; - mDeleteButton.setPressedFunc(pressedFunc); - } - - mFetchButton.setText("FETCH", "download metadata"); - mFetchButton.setPressedFunc(std::bind(&GuiMetaDataEd::fetch, this)); - - mSaveButton.setText("SAVE", "save"); - mSaveButton.setPressedFunc([&] { save(); delete this; }); - - //initialize metadata list - addChild(&mList); - populateList(mdd); - mList.setPosition((sw - mList.getSize().x()) / 2.0f, (sh - mList.getSize().y()) / 2.0f); //center it - mList.resetCursor(); - - mBox.fitTo(mList.getSize(), mList.getPosition(), Eigen::Vector2f(12, 12)); - - mHeader.setPosition(mList.getPosition()); - mHeader.setSize(mList.getSize().x(), 0); - mHeader.setCentered(true); -} - -GuiMetaDataEd::~GuiMetaDataEd() -{ - for(auto iter = mLabels.begin(); iter != mLabels.end(); iter++) - { - delete *iter; - } - for(auto iter = mEditors.begin(); iter != mEditors.end(); iter++) - { - delete *iter; - } -} - -void GuiMetaDataEd::populateList(const std::vector& mdd) -{ - // PATH //(centered, not part of componentlist) - - //---GAME NAME--- //(at 1,1; size 3,1) (TextEditComponent) - //DEL SCRP --- //(buttons) - //Fav: Y/N --- //metadata start - //Desc: ... --- //multiline texteditcomponent - //Img: ... --- - //--- SAVE --- - - using namespace Eigen; - - int y = 0; - - //fetch button - mList.setEntry(Vector2i(0, y), Vector2i(1, 1), &mFetchButton, true, ComponentGrid::AlignLeft); - - //delete button - mList.setEntry(Vector2i(1, y), Vector2i(1, 1), &mDeleteButton, true, ComponentGrid::AlignRight); - - y++; + addChild(&mMenu); + // populate list for(auto iter = mdd.begin(); iter != mdd.end(); iter++) { - TextComponent* label = new TextComponent(mWindow); - label->setText(iter->key); - mList.setEntry(Vector2i(0, y), Vector2i(1, 1), label, false, ComponentGrid::AlignLeft); - mLabels.push_back(label); - - GuiComponent* ed = MetaDataList::makeEditor(mWindow, iter->type); - ed->setSize(Renderer::getScreenWidth() * 0.4f, ed->getSize().y()); + auto ed = MetaDataList::makeEditor(mWindow, iter->type); ed->setValue(mMetaData->get(iter->key)); - mList.setEntry(Vector2i(1, y), Vector2i(1, 1), ed, true, ComponentGrid::AlignRight); mEditors.push_back(ed); - - y++; + mMenu.addWithLabel(iter->key, ed); } - //save button - mList.setEntry(Vector2i(0, y), Vector2i(2, 1), &mSaveButton, true, ComponentGrid::AlignCenter); + //add buttons + mMenu.addButton("SCRAPE", "download metadata from the Internet", std::bind(&GuiMetaDataEd::fetch, this)); + mMenu.addButton("SAVE", "save changes", [&] { save(); delete this; }); + + if(mDeleteFunc) + { + auto deleteFileAndSelf = [&] { mDeleteFunc(); delete this; }; + auto deleteBtnFunc = [this, deleteFileAndSelf] { mWindow->pushGui(new GuiMsgBoxYesNo(mWindow, "This will delete a file!\nAre you sure?", deleteFileAndSelf)); }; + mMenu.addButton("DELETE", "delete this game on disk", deleteBtnFunc); + } + + // initially put cursor on "SCRAPE" + mMenu.setCursorToButtons(); + + // position menu + mMenu.setPosition((mSize.x() - mMenu.getSize().x()) / 2, (mSize.y() - mMenu.getSize().y()) / 2); //center it } void GuiMetaDataEd::save() { - for(unsigned int i = 0; i < mLabels.size(); i++) + for(unsigned int i = 0; i < mEditors.size(); i++) { - mMetaData->set(mLabels.at(i)->getValue(), mEditors.at(i)->getValue()); + mMetaData->set(mMetaDataDecl.at(i).key, mEditors.at(i)->getValue()); } if(mSavedCallback) @@ -126,7 +61,6 @@ void GuiMetaDataEd::fetch() { GuiGameScraper* scr = new GuiGameScraper(mWindow, mScraperParams, std::bind(&GuiMetaDataEd::fetchDone, this, std::placeholders::_1)); mWindow->pushGui(scr); - scr->search(); } void GuiMetaDataEd::fetchDone(MetaDataList result) @@ -187,7 +121,7 @@ bool GuiMetaDataEd::input(InputConfig* config, Input input) std::vector GuiMetaDataEd::getHelpPrompts() { - std::vector prompts = mList.getHelpPrompts(); + std::vector prompts = mMenu.getHelpPrompts(); prompts.push_back(HelpPrompt("b", "discard changes")); return prompts; } diff --git a/src/guis/GuiMetaDataEd.h b/src/guis/GuiMetaDataEd.h index 12c191637..69751da9a 100644 --- a/src/guis/GuiMetaDataEd.h +++ b/src/guis/GuiMetaDataEd.h @@ -1,11 +1,8 @@ #pragma once #include "../GuiComponent.h" -#include "../components/ComponentGrid.h" +#include "../components/MenuComponent.h" #include "../MetaData.h" -#include "../components/TextComponent.h" -#include "../components/NinePatchComponent.h" -#include "../components/ButtonComponent.h" #include "../scrapers/Scraper.h" #include @@ -15,8 +12,7 @@ class GuiMetaDataEd : public GuiComponent public: GuiMetaDataEd(Window* window, MetaDataList* md, const std::vector& mdd, ScraperSearchParams params, const std::string& header, std::function savedCallback, std::function deleteFunc); - virtual ~GuiMetaDataEd(); - + bool input(InputConfig* config, Input input) override; virtual std::vector getHelpPrompts() override; @@ -26,25 +22,14 @@ private: void fetch(); void fetchDone(MetaDataList result); - void populateList(const std::vector& mdd); + MenuComponent mMenu; ScraperSearchParams mScraperParams; - NinePatchComponent mBox; - - ComponentGrid mList; - - TextComponent mHeader; - - std::vector mLabels; - std::vector mEditors; + std::vector< std::shared_ptr > mEditors; std::vector mMetaDataDecl; MetaDataList* mMetaData; std::function mSavedCallback; std::function mDeleteFunc; - - ButtonComponent mDeleteButton; - ButtonComponent mFetchButton; - ButtonComponent mSaveButton; }; diff --git a/src/guis/GuiScraperLog.cpp b/src/guis/GuiScraperLog.cpp index f7e8eccbd..b24a8bd2d 100644 --- a/src/guis/GuiScraperLog.cpp +++ b/src/guis/GuiScraperLog.cpp @@ -61,7 +61,6 @@ void GuiScraperLog::next() [this, search] { resultEmpty(search); }); mWindow->pushGui(ggs); - ggs->search(); }else{ std::shared_ptr scraper = Settings::getInstance()->getScraper(); scraper->getResultsAsync(search, mWindow, [this, search] (std::vector mdls) { diff --git a/src/scrapers/GamesDBScraper.cpp b/src/scrapers/GamesDBScraper.cpp index abc8c576b..a4f828959 100644 --- a/src/scrapers/GamesDBScraper.cpp +++ b/src/scrapers/GamesDBScraper.cpp @@ -1,9 +1,10 @@ #include "GamesDBScraper.h" -#include "../guis/GuiGameScraper.h" +#include "../components/ScraperSearchComponent.h" #include "../components/AsyncReqComponent.h" #include "../Log.h" #include "../pugiXML/pugixml.hpp" #include "../MetaData.h" +#include "../Settings.h" #include const char* GamesDBScraper::getName() { return "TheGamesDB"; }