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.
This commit is contained in:
Aloshi 2014-03-11 22:00:08 -05:00
parent 0626f61905
commit bbb8aeeac3
22 changed files with 903 additions and 777 deletions

View file

@ -172,6 +172,7 @@ set(ES_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/src/components/NinePatchComponent.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/NinePatchComponent.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/OptionListComponent.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/OptionListComponent.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/RatingComponent.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/ScrollableContainer.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/SliderComponent.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/SliderComponent.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/SwitchComponent.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/MenuComponent.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/components/NinePatchComponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/NinePatchComponent.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/components/RatingComponent.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/ScrollableContainer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/components/SliderComponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/SliderComponent.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/components/SwitchComponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/SwitchComponent.cpp

View file

@ -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"); return string_to_ptime(get(key), "%Y%m%dT%H%M%S%F%q");
} }
GuiComponent* MetaDataList::makeDisplay(Window* window, MetaDataType as) std::shared_ptr<GuiComponent> MetaDataList::makeEditor(Window* window, MetaDataType as)
{ {
switch(as) switch(as)
{ {
case MD_RATING: case MD_RATING:
{ {
RatingComponent* comp = new RatingComponent(window); return std::make_shared<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;
} }
case MD_MULTILINE_STRING: case MD_MULTILINE_STRING:
{ {
TextEditComponent* comp = new TextEditComponent(window); auto comp = std::make_shared<TextEditComponent>(window);
comp->setSize(comp->getSize().x(), comp->getSize().y() * 3); comp->setSize(comp->getSize().x(), comp->getSize().y() * 3);
return comp; return comp;
} }
case MD_DATE: case MD_DATE:
{ {
DateTimeComponent* comp = new DateTimeComponent(window); return std::make_shared<DateTimeComponent>(window);
return comp;
} }
case MD_TIME: case MD_TIME:
{ {
DateTimeComponent* comp = new DateTimeComponent(window); auto comp = std::make_shared<DateTimeComponent>(window);
comp->setDisplayMode(DateTimeComponent::DISP_RELATIVE_TO_NOW); comp->setDisplayMode(DateTimeComponent::DISP_RELATIVE_TO_NOW);
return comp; return comp;
} }
default: default:
{ {
TextEditComponent* comp = new TextEditComponent(window); return std::make_shared<TextEditComponent>(window);
return comp;
} }
} }
} }

View file

@ -55,8 +55,7 @@ public:
float getFloat(const std::string& key) const; float getFloat(const std::string& key) const;
boost::posix_time::ptime getTime(const std::string& key) const; boost::posix_time::ptime getTime(const std::string& key) const;
static GuiComponent* makeDisplay(Window* window, MetaDataType as); static std::shared_ptr<GuiComponent> makeEditor(Window* window, MetaDataType as);
static GuiComponent* makeEditor(Window* window, MetaDataType as);
inline MetaDataListType getType() const { return mType; } inline MetaDataListType getType() const { return mType; }
inline const std::vector<MetaDataDecl>& getMDD() const { return getMDDByType(getType()); } inline const std::vector<MetaDataDecl>& getMDD() const { return getMDDByType(getType()); }

View file

@ -39,14 +39,31 @@ namespace Renderer {
if(box[3] == 0) if(box[3] == 0)
box[3] = Renderer::getScreenHeight() - box.y(); 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 //glScissor starts at the bottom left of the window
//so (0, 0, 1, 1) is the bottom left pixel //so (0, 0, 1, 1) is the bottom left pixel
//everything else uses y+ = down, so flip it to be consistent //everything else uses y+ = down, so flip it to be consistent
//rect.pos.y = Renderer::getScreenHeight() - rect.pos.y - rect.size.y; //rect.pos.y = Renderer::getScreenHeight() - rect.pos.y - rect.size.y;
box[1] = Renderer::getScreenHeight() - box.y() - box[3]; 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); clipStack.push(box);
glScissor(box[0], box[1], box[2], box[3]); glScissor(box[0], box[1], box[2], box[3]);
glEnable(GL_SCISSOR_TEST); glEnable(GL_SCISSOR_TEST);

View file

@ -2,12 +2,14 @@
#include "../Renderer.h" #include "../Renderer.h"
#include "../Window.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<void()>& func) : GuiComponent(window),
mBox(window, ":/button.png"), mBox(window, ":/button.png"),
mFocused(false), mFocused(false),
mEnabled(true),
mTextColorFocused(0xFFFFFFFF), mTextColorUnfocused(0x777777FF) mTextColorFocused(0xFFFFFFFF), mTextColorUnfocused(0x777777FF)
{ {
setSize(64, 48); setPressedFunc(func);
setText(text, helpText);
} }
void ButtonComponent::onSizeChanged() void ButtonComponent::onSizeChanged()
@ -24,7 +26,7 @@ bool ButtonComponent::input(InputConfig* config, Input input)
{ {
if(config->isMappedTo("a", input) && input.value != 0) if(config->isMappedTo("a", input) && input.value != 0)
{ {
if(mPressedFunc) if(mPressedFunc && mEnabled)
mPressedFunc(); mPressedFunc();
return true; return true;
} }
@ -48,13 +50,34 @@ void ButtonComponent::setText(const std::string& text, const std::string& helpTe
void ButtonComponent::onFocusGained() void ButtonComponent::onFocusGained()
{ {
mFocused = true; mFocused = true;
mBox.setImagePath(":/button_filled.png"); updateImage();
} }
void ButtonComponent::onFocusLost() void ButtonComponent::onFocusLost()
{ {
mFocused = false; mFocused = false;
updateImage();
}
void ButtonComponent::setEnabled(bool enabled)
{
mEnabled = enabled;
updateImage();
}
void ButtonComponent::updateImage()
{
if(!mEnabled || !mPressedFunc)
{
mBox.setImagePath(":/button.png"); 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) void ButtonComponent::render(const Eigen::Affine3f& parentTrans)

View file

@ -8,10 +8,12 @@
class ButtonComponent : public GuiComponent class ButtonComponent : public GuiComponent
{ {
public: public:
ButtonComponent(Window* window); ButtonComponent(Window* window, const std::string& text = "", const std::string& helpText = "", const std::function<void()>& func = nullptr);
void setPressedFunc(std::function<void()> f); void setPressedFunc(std::function<void()> f);
void setEnabled(bool enable);
bool input(InputConfig* config, Input input) override; bool input(InputConfig* config, Input input) override;
void render(const Eigen::Affine3f& parentTrans) override; void render(const Eigen::Affine3f& parentTrans) override;
@ -28,10 +30,12 @@ private:
std::function<void()> mPressedFunc; std::function<void()> mPressedFunc;
bool mFocused; bool mFocused;
bool mEnabled;
unsigned int mTextColorFocused; unsigned int mTextColorFocused;
unsigned int mTextColorUnfocused; unsigned int mTextColorUnfocused;
unsigned int getCurTextColor() const; unsigned int getCurTextColor() const;
void updateImage();
std::string mText; std::string mText;
std::string mHelpText; std::string mHelpText;

View file

@ -2,281 +2,219 @@
#include "../Log.h" #include "../Log.h"
#include "../Renderer.h" #include "../Renderer.h"
#define INITIAL_CELL_SIZE 12 using namespace GridFlags;
ComponentGrid::ComponentGrid(Window* window, Eigen::Vector2i gridDimensions) : GuiComponent(window), ComponentGrid::ComponentGrid(Window* window, const Eigen::Vector2i& gridDimensions) : GuiComponent(window),
mGrid(NULL), mColumnWidths(NULL), mRowHeights(NULL), mGridSize(gridDimensions), mCursor(0, 0)
mColumnWidthForced(NULL), mRowHeightForced(NULL),
mCursor(-1, -1)
{ {
mEntries.reserve(gridDimensions.x() * gridDimensions.y()); assert(gridDimensions.x() > 0 && gridDimensions.y() > 0);
makeCells(gridDimensions);
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() ComponentGrid::~ComponentGrid()
{ {
for(auto iter = mEntries.begin(); iter != mEntries.end(); iter++)
{
delete *iter;
}
}
void ComponentGrid::makeCells(Eigen::Vector2i size)
{
if(mGrid)
delete[] mGrid;
if(mColumnWidths)
delete[] mColumnWidths;
if(mRowHeights)
delete[] mRowHeights; delete[] mRowHeights;
delete[] mColWidths;
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();
} }
void ComponentGrid::setEntry(Eigen::Vector2i pos, Eigen::Vector2i size, GuiComponent* component, bool canFocus, AlignmentType align, float ComponentGrid::getColWidth(int col)
Eigen::Matrix<bool, 1, 2> autoFit, UpdateBehavior updateType)
{ {
if(pos.x() > mGridSize.x() || pos.y() > mGridSize.y() || pos.x() < 0 || pos.y() < 0) 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++)
{ {
LOG(LogError) << "Tried to set entry beyond grid size!"; freeWidthPerc -= mColWidths[x]; // if it's 0 it won't do anything
return; if(mColWidths[x] == 0)
between++;
} }
if(component == NULL) return (freeWidthPerc * mSize.x()) / between;
}
float ComponentGrid::getRowHeight(int row)
{
if(mRowHeights[row] != 0)
return mRowHeights[row] * mSize.y();
// 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!"; freeHeightPerc -= mRowHeights[y]; // if it's 0 it won't do anything
return; 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); return (freeHeightPerc * mSize.y()) / between;
}
mEntries.push_back(entry); void ComponentGrid::setColWidthPerc(int col, float width)
{
assert(col >= 0 && col < mGridSize.x());
mColWidths[col] = width;
onSizeChanged();
}
for(int y = pos.y(); y < pos.y() + size.y(); y++) void ComponentGrid::setRowHeightPerc(int row, float height)
{ {
for(int x = pos.x(); x < pos.x() + size.x(); x++) assert(row >= 0 && row < mGridSize.y());
{ mRowHeights[row] = height;
setCell(x, y, mEntries.back()); onSizeChanged();
} }
}
if(component->getParent() != NULL) void ComponentGrid::setEntry(const std::shared_ptr<GuiComponent>& comp, const Eigen::Vector2i& pos, bool canFocus, bool resize, const Eigen::Vector2i& size,
LOG(LogError) << "ComponentGrid ruining an existing parent-child relationship! Call a social worker!"; unsigned int border, GridFlags::UpdateType updateType)
component->setParent(this); {
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) if(!cursorValid() && canFocus)
mCursor = pos; mCursor = pos;
//update the column width and row height updateCellComponent(mCells.back());
//if(autoFit.x() && (int)getColumnWidth(pos.x()) < component->getSize().x()) updateSeparators();
// 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();
} }
void ComponentGrid::removeEntriesIn(Eigen::Vector2i pos, Eigen::Vector2i size) bool ComponentGrid::removeEntry(const std::shared_ptr<GuiComponent>& comp)
{ {
auto iter = mEntries.begin(); for(auto it = mCells.begin(); it != mCells.end(); it++)
while(iter != mEntries.end())
{ {
if((*iter)->pos.x() >= pos.x() && (*iter)->pos.x() < pos.x() + size.x() if(it->component == comp)
&& (*iter)->pos.y() >= pos.y() && (*iter)->pos.y() < pos.y() + size.y())
{ {
if((*iter)->component->getParent() == this) removeChild(comp.get());
(*iter)->component->setParent(NULL); mCells.erase(it);
return true;
delete *iter;
iter = mEntries.erase(iter);
}else{
iter++;
} }
} }
for(int y = pos.y(); y < pos.y() + size.y(); y++) return false;
}
void ComponentGrid::updateCellComponent(const GridEntry& cell)
{
// 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);
if(cell.resize)
cell.component->setSize(size);
// 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);
// center component
pos[0] = pos.x() + (size.x() - cell.component->getSize().x()) / 2;
pos[1] = pos.y() + (size.y() - cell.component->getSize().y()) / 2;
cell.component->setPosition(pos);
}
void ComponentGrid::updateSeparators()
{
mLines.clear();
Eigen::Vector2f pos;
Eigen::Vector2f size;
for(auto it = mCells.begin(); it != mCells.end(); it++)
{ {
for(int x = pos.x(); x < pos.x() + size.x(); x++) if(!it->border)
continue;
// 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);
if(it->border & BORDER_TOP)
{ {
setCell(x, y, NULL); mLines.push_back(Vert(pos.x(), pos.y()));
mLines.push_back(Vert(pos.x() + size.x(), pos.y()));
}
if(it->border & BORDER_BOTTOM)
{
mLines.push_back(Vert(pos.x(), pos.y() + size.y()));
mLines.push_back(Vert(pos.x() + size.x(), mLines.back().y));
}
if(it->border & BORDER_LEFT)
{
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()));
} }
} }
if(!cursorValid()) mLineColors.reserve(mLines.size());
resetCursor(); Renderer::buildGLColorArray((GLubyte*)mLineColors.data(), 0xC6C7C6FF, mLines.size());
} }
void ComponentGrid::forceRowHeight(int row, unsigned int size) void ComponentGrid::onSizeChanged()
{ {
mRowHeights[row] = size; for(auto it = mCells.begin(); it != mCells.end(); it++)
mRowHeightForced[row] = true; updateCellComponent(*it);
updateSize();
updateComponentOffsets(); updateSeparators();
} }
void ComponentGrid::forceColumnWidth(int col, unsigned int size) ComponentGrid::GridEntry* ComponentGrid::getCellAt(int x, int y)
{ {
mColumnWidths[col] = size; assert(x >= 0 && x < mGridSize.x() && y >= 0 && y < mGridSize.y());
mColumnWidthForced[col] = true;
updateSize();
updateComponentOffsets();
}
unsigned int ComponentGrid::getRowHeight(int row) { return mRowHeights[row]; } for(auto it = mCells.begin(); it != mCells.end(); it++)
unsigned int ComponentGrid::getColumnWidth(int col) { return mColumnWidths[col]; }
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);
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;
}
void ComponentGrid::setCell(unsigned int x, unsigned int y, ComponentEntry* entry)
{
if(x >= (unsigned int)mGridSize.x() || y >= (unsigned int)mGridSize.y())
{ {
LOG(LogError) << "Invalid setCell - position " << x << ", " << y << " out of bounds!"; int xmin = it->pos.x();
return; 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);
} }
mGrid[y * mGridSize.x() + x] = entry;
}
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 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++)
{
ComponentEntry* check = getCell(x, row);
if(check)
{
if(check->component->getSize().x() > widest)
widest = check->component->getSize().x();
}
}
mColumnWidths[x] = (unsigned int)widest;
}
if(!mRowHeightForced[y] && updHeight)
{
float tallest = 0;
for(int col = 0; col < mGridSize.x(); col++)
{
ComponentEntry* check = getCell(col, y);
if(check)
{
if(check->component->getSize().y() > tallest)
tallest = check->component->getSize().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)
{
updateCellSize(*iter);
}
}
} }
bool ComponentGrid::input(InputConfig* config, Input input) 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; return true;
if(!input.value) if(!input.value)
@ -284,23 +222,19 @@ bool ComponentGrid::input(InputConfig* config, Input input)
if(config->isMappedTo("down", input)) if(config->isMappedTo("down", input))
{ {
moveCursor(Eigen::Vector2i(0, 1)); return moveCursor(Eigen::Vector2i(0, 1));
return true;
} }
if(config->isMappedTo("up", input)) if(config->isMappedTo("up", input))
{ {
moveCursor(Eigen::Vector2i(0, -1)); return moveCursor(Eigen::Vector2i(0, -1));
return true;
} }
if(config->isMappedTo("left", input)) if(config->isMappedTo("left", input))
{ {
moveCursor(Eigen::Vector2i(-1, 0)); return moveCursor(Eigen::Vector2i(-1, 0));
return true;
} }
if(config->isMappedTo("right", input)) if(config->isMappedTo("right", input))
{ {
moveCursor(Eigen::Vector2i(1, 0)); return moveCursor(Eigen::Vector2i(1, 0));
return true;
} }
return false; return false;
@ -308,47 +242,28 @@ bool ComponentGrid::input(InputConfig* config, Input input)
void ComponentGrid::resetCursor() void ComponentGrid::resetCursor()
{ {
auto iter = mEntries.begin(); if(!mCells.size())
while(iter != mEntries.end())
{
if((*iter)->canFocus)
break;
iter++;
}
if(iter == mEntries.end())
{
mCursor = Eigen::Vector2i(-1, -1);
return; return;
}
const Eigen::Vector2i origCursor = mCursor; for(auto it = mCells.begin(); it != mCells.end(); it++)
mCursor << (*iter)->pos[0], (*iter)->pos[1]; {
if(it->canFocus)
{
Eigen::Vector2i origCursor = mCursor;
mCursor = it->pos;
onCursorMoved(origCursor, mCursor); onCursorMoved(origCursor, mCursor);
break;
}
}
} }
void ComponentGrid::moveCursor(Eigen::Vector2i dir) bool ComponentGrid::moveCursor(Eigen::Vector2i dir)
{ {
if(dir.x() != 0 && dir.y() != 0) assert(dir.x() || dir.y());
{
LOG(LogError) << "Invalid cursor move dir!";
return;
}
Eigen::Vector2i origCursor = mCursor; const Eigen::Vector2i origCursor = mCursor;
if(!cursorValid()) GridEntry* currentCursorEntry = getCellAt(mCursor);
{
resetCursor();
if(!cursorValid())
{
if(mCursor != origCursor)
onCursorMoved(origCursor, mCursor);
return;
}
}
Eigen::Vector2i searchAxis(dir.x() == 0, dir.y() == 0); Eigen::Vector2i searchAxis(dir.x() == 0, dir.y() == 0);
@ -358,13 +273,16 @@ void ComponentGrid::moveCursor(Eigen::Vector2i dir)
Eigen::Vector2i curDirPos = mCursor; Eigen::Vector2i curDirPos = mCursor;
GridEntry* cursorEntry;
//spread out on search axis+ //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); onCursorMoved(origCursor, mCursor);
return; return true;
} }
mCursor += searchAxis; mCursor += searchAxis;
@ -372,12 +290,14 @@ void ComponentGrid::moveCursor(Eigen::Vector2i dir)
//now again on search axis- //now again on search axis-
mCursor = curDirPos; 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); onCursorMoved(origCursor, mCursor);
return; return true;
} }
mCursor -= searchAxis; mCursor -= searchAxis;
@ -388,31 +308,36 @@ void ComponentGrid::moveCursor(Eigen::Vector2i dir)
//failed to find another focusable element in this direction //failed to find another focusable element in this direction
mCursor = origCursor; 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() bool ComponentGrid::cursorValid()
{ {
if(mCursor.x() < 0 || mCursor.y() < 0 || mCursor.x() >= mGridSize.x() || mCursor.y() >= mGridSize.y()) return getCellAt(mCursor) != NULL;
return false;
return getCell(mCursor.x(), mCursor.y()) != NULL;
} }
void ComponentGrid::update(int deltaTime) 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) if(it->updateType == UPDATE_ALWAYS || (it->updateType == UPDATE_WHEN_SELECTED && cursorEntry == &(*it)))
{ it->component->update(deltaTime);
case UpdateAlways:
(*iter)->component->update(deltaTime);
break;
case UpdateFocused:
if(cursorValid() && getCell(mCursor.x(), mCursor.y())->component == (*iter)->component)
(*iter)->component->update(deltaTime);
break;
}
} }
} }
@ -420,79 +345,72 @@ void ComponentGrid::render(const Eigen::Affine3f& parentTrans)
{ {
Eigen::Affine3f trans = parentTrans * getTransform(); Eigen::Affine3f trans = parentTrans * getTransform();
//draw cursor renderChildren(trans);
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); // draw cell separators
Renderer::drawRect(0, 0, (int)entry->component->getSize().x(), (int)entry->component->getSize().y(), 0x0000AA22); if(mLines.size())
{
Renderer::setMatrix(trans);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
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);
} }
for(auto iter = mEntries.begin(); iter != mEntries.end(); iter++)
{
(*iter)->component->render(trans);
}
//draw cell outlines
/*Renderer::setMatrix(trans);
Eigen::Vector2i pos(0, 0);
for(int x = 0; x < mGridSize.x(); x++)
{
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);
pos[1] += getRowHeight(y);
}
pos[1] = 0;
pos[0] += getColumnWidth(x);
}*/
} }
void ComponentGrid::textInput(const char* text) void ComponentGrid::textInput(const char* text)
{ {
if(getSelectedComponent() != NULL) GridEntry* selectedEntry = getCellAt(mCursor);
getSelectedComponent()->textInput(text); if(selectedEntry != NULL)
} selectedEntry->component->textInput(text);
void ComponentGrid::onPositionChanged()
{
updateComponentOffsets();
}
GuiComponent* ComponentGrid::getSelectedComponent()
{
if(!cursorValid())
return NULL;
return getCell(mCursor.x(), mCursor.y())->component;
} }
void ComponentGrid::onCursorMoved(Eigen::Vector2i from, Eigen::Vector2i to) void ComponentGrid::onCursorMoved(Eigen::Vector2i from, Eigen::Vector2i to)
{ {
if(from != Eigen::Vector2i(-1, -1)) GridEntry* cell = getCellAt(from);
getCell(from.x(), from.y())->component->onFocusLost(); if(cell)
cell->component->onFocusLost();
if(to != Eigen::Vector2i(-1, -1)) cell = getCellAt(to);
getCell(to.x(), to.y())->component->onFocusGained(); if(cell)
cell->component->onFocusGained();
updateHelpPrompts(); updateHelpPrompts();
} }
void ComponentGrid::setCursorTo(const std::shared_ptr<GuiComponent>& 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<HelpPrompt> ComponentGrid::getHelpPrompts() std::vector<HelpPrompt> ComponentGrid::getHelpPrompts()
{ {
std::vector<HelpPrompt> prompts; std::vector<HelpPrompt> prompts;
if(cursorValid()) GridEntry* e = getCellAt(mCursor);
prompts = getSelectedComponent()->getHelpPrompts(); if(e)
prompts = e->component->getHelpPrompts();
bool canScrollVert = true; bool canScrollVert = true;
bool canScrollHoriz = true; bool canScrollHoriz = true;

View file

@ -2,63 +2,87 @@
#include "../GuiComponent.h" #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. // Used to arrange a bunch of components in a spreadsheet-esque grid.
class ComponentGrid : public GuiComponent class ComponentGrid : public GuiComponent
{ {
public: public:
ComponentGrid(Window* window, Eigen::Vector2i gridDimensions); ComponentGrid(Window* window, const Eigen::Vector2i& gridDimensions);
virtual ~ComponentGrid(); virtual ~ComponentGrid();
enum UpdateBehavior bool removeEntry(const std::shared_ptr<GuiComponent>& comp);
{
UpdateAlways, UpdateFocused
};
enum AlignmentType void setEntry(const std::shared_ptr<GuiComponent>& 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);
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<bool, 1, 2> autoFit = Eigen::Matrix<bool, 1, 2>(true, true), UpdateBehavior updateType = UpdateAlways);
void removeEntriesIn(Eigen::Vector2i pos, Eigen::Vector2i size);
void onPositionChanged() override;
void textInput(const char* text) override; void textInput(const char* text) override;
bool input(InputConfig* config, Input input) override; bool input(InputConfig* config, Input input) override;
void update(int deltaTime) override; void update(int deltaTime) override;
void render(const Eigen::Affine3f& parentTrans) override; void render(const Eigen::Affine3f& parentTrans) override;
void onSizeChanged() override;
void forceColumnWidth(int col, unsigned int size);
void forceRowHeight(int row, unsigned int size);
void updateComponent(GuiComponent* cmp);
void resetCursor(); void resetCursor();
bool cursorValid(); 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<GuiComponent>& comp);
inline std::shared_ptr<GuiComponent> getSelectedComponent()
{
GridEntry* e = getCellAt(mCursor);
if(e)
return e->component;
else
return nullptr;
}
void onFocusLost() override;
void onFocusGained() override;
virtual std::vector<HelpPrompt> getHelpPrompts() override; virtual std::vector<HelpPrompt> getHelpPrompts() override;
private: private:
class ComponentEntry class GridEntry
{ {
public: public:
Eigen::Vector2i pos; Eigen::Vector2i pos;
Eigen::Vector2i dim; Eigen::Vector2i dim;
GuiComponent* component; std::shared_ptr<GuiComponent> component;
UpdateBehavior updateType;
AlignmentType alignment;
bool canFocus; bool canFocus;
bool resize;
GridFlags::UpdateType updateType;
unsigned int border;
ComponentEntry() : component(NULL), updateType(UpdateAlways), canFocus(true), alignment(AlignCenter) {}; GridEntry(const Eigen::Vector2i& p = Eigen::Vector2i::Zero(), const Eigen::Vector2i& d = Eigen::Vector2i::Zero(),
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) {}; const std::shared_ptr<GuiComponent>& 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 operator bool() const
{ {
@ -66,30 +90,30 @@ private:
} }
}; };
//Offset we render components by (for scrolling). [unimplemented] float* mRowHeights;
Eigen::Vector2f mComponentOffset; float* mColWidths;
struct Vert
{
Vert(float xi = 0, float yi = 0) : x(xi), y(yi) {};
float x;
float y;
};
std::vector<Vert> mLines;
std::vector<unsigned int> 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; Eigen::Vector2i mGridSize;
ComponentEntry** mGrid;
std::vector<ComponentEntry*> 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); std::vector<GridEntry> mCells;
unsigned int getRowHeight(int row);
unsigned int* mColumnWidths;
unsigned int* mRowHeights;
bool* mColumnWidthForced;
bool* mRowHeightForced;
Eigen::Vector3f getCellOffset(Eigen::Vector2i gridPos);
void updateSize();
void onCursorMoved(Eigen::Vector2i from, Eigen::Vector2i to); void onCursorMoved(Eigen::Vector2i from, Eigen::Vector2i to);
Eigen::Vector2i mCursor; Eigen::Vector2i mCursor;
void updateComponentOffsets();
void updateCellSize(ComponentEntry* e, bool updWidth = true, bool updHeight = true);
}; };

View file

@ -6,6 +6,7 @@ ComponentList::ComponentList(Window* window) : IList<ComponentListRow, void*>(wi
{ {
mSelectorBarOffset = 0; mSelectorBarOffset = 0;
mCameraOffset = 0; mCameraOffset = 0;
mFocused = false;
} }
void ComponentList::addRow(const ComponentListRow& row, bool setCursorHere) void ComponentList::addRow(const ComponentListRow& row, bool setCursorHere)
@ -41,6 +42,16 @@ void ComponentList::onSizeChanged()
onCursorChanged(mScrollVelocity != 0 ? CURSOR_SCROLLING : CURSOR_STOPPED); onCursorChanged(mScrollVelocity != 0 ? CURSOR_SCROLLING : CURSOR_STOPPED);
} }
void ComponentList::onFocusLost()
{
mFocused = false;
}
void ComponentList::onFocusGained()
{
mFocused = true;
}
bool ComponentList::input(InputConfig* config, Input input) bool ComponentList::input(InputConfig* config, Input input)
{ {
if(size() == 0) if(size() == 0)
@ -127,9 +138,12 @@ void ComponentList::render(const Eigen::Affine3f& parentTrans)
// draw our entries // draw our entries
renderChildren(trans); renderChildren(trans);
// draw selector bar // custom rendering
Renderer::setMatrix(trans); Renderer::setMatrix(trans);
// draw selector bar
if(mFocused)
{
// inversion: src * (1 - dst) + dst * 0 = where src = 1 // inversion: src * (1 - dst) + dst * 0 = where src = 1
// need a function that goes roughly 0x777777 -> 0xFFFFFF // need a function that goes roughly 0x777777 -> 0xFFFFFF
// and 0xFFFFFF -> 0x777777 // and 0xFFFFFF -> 0x777777
@ -144,6 +158,7 @@ void ComponentList::render(const Eigen::Affine3f& parentTrans)
// hack to draw 2px dark on left/right of the bar // hack to draw 2px dark on left/right of the bar
Renderer::drawRect(0, (int)mSelectorBarOffset, 2, (int)selectedRowHeight, 0x878787FF); Renderer::drawRect(0, (int)mSelectorBarOffset, 2, (int)selectedRowHeight, 0x878787FF);
Renderer::drawRect((int)mSize.x() - 2, (int)mSelectorBarOffset, 2, (int)selectedRowHeight, 0x878787FF); Renderer::drawRect((int)mSize.x() - 2, (int)mSelectorBarOffset, 2, (int)selectedRowHeight, 0x878787FF);
}
// draw separators // draw separators
float y = 0; float y = 0;

View file

@ -52,11 +52,17 @@ public:
void render(const Eigen::Affine3f& parentTrans) override; void render(const Eigen::Affine3f& parentTrans) override;
void onSizeChanged() override; void onSizeChanged() override;
void onFocusGained() override;
void onFocusLost() override;
inline int getCursorId() const { return mCursor; }
protected: protected:
void onCursorChanged(const CursorState& state) override; void onCursorChanged(const CursorState& state) override;
private: private:
bool mFocused;
void updateElementPosition(const ComponentListRow& row); void updateElementPosition(const ComponentListRow& row);
void updateElementSize(const ComponentListRow& row); void updateElementSize(const ComponentListRow& row);

View file

@ -1,31 +1,67 @@
#include "MenuComponent.h" #include "MenuComponent.h"
#include "ButtonComponent.h"
using namespace Eigen;
MenuComponent::MenuComponent(Window* window, const char* title) : GuiComponent(window), 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"); mBackground.setImagePath(":/frame.png");
mTitle.setFont(Font::get(FONT_SIZE_LARGE)); // set up title which will never change
mTitle.setText(title); mTitle = std::make_shared<TextComponent>(mWindow, title, Font::get(FONT_SIZE_LARGE), 0x555555FF, true);
mTitle.setColor(0x555555FF); mGrid.setEntry(mTitle, Vector2i(0, 0), false);
mTitle.setCentered(true);
addChild(&mBackground); // set up list which will never change (externally, anyway)
addChild(&mTitle); mList = std::make_shared<ComponentList>(mWindow);
addChild(&mList); mGrid.setEntry(mList, Vector2i(0, 1), true);
setSize(Renderer::getScreenWidth() * 0.5f, Renderer::getScreenHeight() * 0.75f); setSize(Renderer::getScreenWidth() * 0.5f, Renderer::getScreenHeight() * 0.75f);
updateGrid();
mGrid.resetCursor();
} }
void MenuComponent::onSizeChanged() void MenuComponent::onSizeChanged()
{ {
mBackground.fitTo(mSize, Eigen::Vector3f::Zero(), Eigen::Vector2f(-32, -32)); mBackground.fitTo(mSize, Eigen::Vector3f::Zero(), Eigen::Vector2f(-32, -32));
const float titlePadding = mTitle.getFont()->getHeight() * 0.2f; // 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);
mTitle.setSize(mSize.x(), (float)mTitle.getFont()->getHeight()); mGrid.setSize(mSize);
mTitle.setPosition(0, titlePadding / 2); }
mList.setPosition(0, mTitle.getSize().y() + titlePadding); void MenuComponent::addButton(const std::string& name, const std::string& helpText, const std::function<void()>& callback)
mList.setSize(mSize.x(), mSize.y() - mTitle.getSize().y() - titlePadding); {
mButtons.push_back(std::make_shared<ButtonComponent>(mWindow, name, helpText, callback));
updateGrid();
onSizeChanged();
}
void MenuComponent::updateGrid()
{
if(mButtonGrid)
mGrid.removeEntry(mButtonGrid);
if(mButtons.size())
{
mButtonGrid = std::make_shared<ComponentGrid>(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();
}
} }

View file

@ -3,6 +3,9 @@
#include "NinePatchComponent.h" #include "NinePatchComponent.h"
#include "ComponentList.h" #include "ComponentList.h"
#include "TextComponent.h" #include "TextComponent.h"
#include "ComponentGrid.h"
class ButtonComponent;
class MenuComponent : public GuiComponent class MenuComponent : public GuiComponent
{ {
@ -11,7 +14,7 @@ public:
void onSizeChanged() override; 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<GuiComponent>& comp, bool setCursorHere = false) inline void addWithLabel(const std::string& label, const std::shared_ptr<GuiComponent>& comp, bool setCursorHere = false)
{ {
@ -21,8 +24,19 @@ public:
addRow(row, setCursorHere); addRow(row, setCursorHere);
} }
void addButton(const std::string& label, const std::string& helpText, const std::function<void()>& callback);
inline void setCursorToList() { mGrid.setCursorTo(mList); }
inline void setCursorToButtons() { assert(mButtonGrid); mGrid.setCursorTo(mButtonGrid); }
private: private:
void updateGrid();
NinePatchComponent mBackground; NinePatchComponent mBackground;
TextComponent mTitle; ComponentGrid mGrid;
ComponentList mList;
std::shared_ptr<TextComponent> mTitle;
std::shared_ptr<ComponentList> mList;
std::shared_ptr<ComponentGrid> mButtonGrid;
std::vector< std::shared_ptr<ButtonComponent> > mButtons;
}; };

View file

@ -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<GuiComponent>(mWindow), Vector2i(0, 0), false, false, Vector2i(1, 3), GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM);
// selected result name
mResultName = std::make_shared<TextComponent>(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<ImageComponent>(mWindow);
mGrid.setEntry(mResultThumbnail, Vector2i(1, 1), false, false, Vector2i(1, 1));
// selected result desc + container
mDescContainer = std::make_shared<ScrollableContainer>(mWindow);
mResultDesc = std::make_shared<TextComponent>(mWindow, "Result desc", Font::get(FONT_SIZE_SMALL), 0x777777FF);
mDescContainer->addChild(mResultDesc.get());
mDescContainer->setAutoScroll(2200, 0.015f);
// result list
mResultList = std::make_shared<ComponentList>(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<MetaDataList> 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<TextComponent>(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<TextComponent>(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<HttpReq>(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<HelpPrompt> ScraperSearchComponent::getHelpPrompts()
{
std::vector<HelpPrompt> 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();
}

View file

@ -0,0 +1,64 @@
#pragma once
#include "../GuiComponent.h"
#include "../scrapers/Scraper.h"
#include "../components/ComponentGrid.h"
#include <functional>
#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<void(MetaDataList*)>& acceptCallback) { mAcceptCallback = acceptCallback; }
bool input(InputConfig* config, Input input) override;
void update(int deltaTime) override;
std::vector<HelpPrompt> getHelpPrompts() override;
void onSizeChanged() override;
void onFocusGained() override;
void onFocusLost() override;
private:
void updateViewStyle();
void updateThumbnail();
void updateInfoPane();
void search();
void onSearchReceived(std::vector<MetaDataList> results);
int getSelectedIndex();
void returnResult(MetaDataList* result);
ComponentGrid mGrid;
std::shared_ptr<TextComponent> mResultName;
std::shared_ptr<ScrollableContainer> mDescContainer;
std::shared_ptr<TextComponent> mResultDesc;
std::shared_ptr<ImageComponent> mResultThumbnail;
std::shared_ptr<ComponentList> mResultList;
SearchType mSearchType;
ScraperSearchParams mSearchParams;
std::function<void(MetaDataList*)> mAcceptCallback;
std::vector<MetaDataList> mScraperResults;
std::unique_ptr<HttpReq> mThumbnailReq;
};

View file

@ -9,8 +9,9 @@ TextComponent::TextComponent(Window* window) : GuiComponent(window),
{ {
} }
TextComponent::TextComponent(Window* window, const std::string& text, const std::shared_ptr<Font>& font, unsigned int color, Eigen::Vector3f pos, Eigen::Vector2f size) : GuiComponent(window), TextComponent::TextComponent(Window* window, const std::string& text, const std::shared_ptr<Font>& font, unsigned int color, bool center,
mFont(NULL), mColor(0x000000FF), mAutoCalcExtent(true, true), mCentered(false) Eigen::Vector3f pos, Eigen::Vector2f size) : GuiComponent(window),
mFont(NULL), mColor(0x000000FF), mAutoCalcExtent(true, true), mCentered(center)
{ {
setFont(font); setFont(font);
setColor(color); setColor(color);
@ -69,6 +70,10 @@ void TextComponent::render(const Eigen::Affine3f& parentTrans)
{ {
Eigen::Affine3f trans = parentTrans * getTransform(); 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(mTextCache)
{ {
if(mCentered) if(mCentered)
@ -86,6 +91,8 @@ void TextComponent::render(const Eigen::Affine3f& parentTrans)
mFont->renderTextCache(mTextCache.get()); mFont->renderTextCache(mTextCache.get());
} }
Renderer::popClipRect();
GuiComponent::renderChildren(trans); GuiComponent::renderChildren(trans);
} }

View file

@ -15,7 +15,8 @@ class TextComponent : public GuiComponent
{ {
public: public:
TextComponent(Window* window); TextComponent(Window* window);
TextComponent(Window* window, const std::string& text, const std::shared_ptr<Font>& 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>& 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>& font); void setFont(const std::shared_ptr<Font>& font);
void onSizeChanged() override; void onSizeChanged() override;

View file

@ -4,152 +4,88 @@
#include "../scrapers/Scraper.h" #include "../scrapers/Scraper.h"
#include "../Settings.h" #include "../Settings.h"
#include "../components/TextComponent.h"
#include "../components/ButtonComponent.h"
GuiGameScraper::GuiGameScraper(Window* window, ScraperSearchParams params, std::function<void(MetaDataList)> doneFunc, std::function<void()> skipFunc) : GuiComponent(window), GuiGameScraper::GuiGameScraper(Window* window, ScraperSearchParams params, std::function<void(MetaDataList)> doneFunc, std::function<void()> skipFunc) : GuiComponent(window),
mList(window, Eigen::Vector2i(2, 7 + MAX_SCRAPER_RESULTS)), mGrid(window, Eigen::Vector2i(1, 3)),
mBox(window, ":/frame.png"), 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), mSearchParams(params),
mDoneFunc(doneFunc), mDoneFunc(doneFunc),
mSkipFunc(skipFunc) mSkipFunc(skipFunc),
mSearchCountdown(2)
{ {
// new screen:
// FILE NAME // FILE NAME
//-------------------------------------- //--------------------------------------
//Name................. | // Result Title | Result #1
//Desc................. | PREVIEW // |-------| ..... | Result #2
//..................... | IMAGE? // | IMG | info | Result #3
//....(autoscroll)..... | // |-------| ..... | .........
// | .........
// DESCRIPTION........| .........
// ...................| .........
// ...................| .........
//-------------------------------------- //--------------------------------------
//Search for: [_______________________] // [SEARCH NAME] [CANCEL]
//--------------------------------------
//Result #1 Name
//Result #2 Name
//Result #3 Name
//Result #4 Name
//Result #5 Name
addChild(&mBox); addChild(&mBox);
addChild(&mList); addChild(&mGrid);
float sw = (float)Renderer::getScreenWidth(); setSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight());
float sh = (float)Renderer::getScreenHeight();
float listWidth = sw * 0.7f; mGrid.setSize(mSize.x() * 0.7f, mSize.y() * 0.65f);
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; auto headerFont = Font::get(FONT_SIZE_LARGE);
mList.setEntry(Vector2i(0, 0), Vector2i(2, 1), &mHeader, false, ComponentGrid::AlignCenter); mGrid.setRowHeightPerc(0, headerFont->getHeight() / mGrid.getSize().y()); // header
mGrid.setRowHeightPerc(2, 0.19f); // buttons
//y = 1 is a spacer row // header
mGrid.setEntry(std::make_shared<TextComponent>(mWindow, getCleanFileName(mSearchParams.game->getName()),
headerFont, 0x777777FF, true), Eigen::Vector2i(0, 0), false, true);
mResultName.setText(params.game->getName()); // ScraperSearchComponent
mResultName.setColor(0x3B56CCFF); mSearch = std::make_shared<ScraperSearchComponent>(window, ScraperSearchComponent::NEVER_AUTO_ACCEPT);
mList.setEntry(Vector2i(0, 1), Vector2i(1, 1), &mResultName, false, ComponentGrid::AlignLeft); mGrid.setEntry(mSearch, Eigen::Vector2i(0, 1), true);
mResultDesc.setText(params.game->metadata.get("desc")); // buttons
mResultDesc.setSize(col1Width, 0); auto buttonGrid = std::make_shared<ComponentGrid>(mWindow, Eigen::Vector2i(3, 1));
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);
mResultThumbnail.setMaxSize(col2Width, mResultInfo.getSize().y()); auto manualSearchBtn = std::make_shared<ButtonComponent>(mWindow, "MANUAL SEARCH", "enter search terms");
mList.setEntry(Vector2i(1, 2), Vector2i(1, 1), &mResultThumbnail, false, ComponentGrid::AlignCenter); auto cancelBtn = std::make_shared<ButtonComponent>(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); buttonGrid->setEntry(manualSearchBtn, Eigen::Vector2i(0, 0), true, false);
mSearchText.setValue(!params.nameOverride.empty() ? params.nameOverride : getCleanFileName(params.game->getPath())); buttonGrid->setEntry(cancelBtn, Eigen::Vector2i(1, 0), true, false);
mSearchText.setSize(listWidth - mSearchLabel.getSize().x() - 20, mSearchText.getSize().y());
mList.setEntry(Vector2i(1, 4), Vector2i(1, 1), &mSearchText, true, ComponentGrid::AlignRight);
//y = 5 is a spacer row mGrid.setEntry(buttonGrid, Eigen::Vector2i(0, 2), true, false);
std::shared_ptr<Font> font = Font::get(FONT_SIZE_SMALL); // center everything
mResultNames.reserve(MAX_SCRAPER_RESULTS); mGrid.setPosition((mSize.x() - mGrid.getSize().x()) / 2, (mSize.y() - mGrid.getSize().y()) / 2);
for(int i = 0; i < MAX_SCRAPER_RESULTS; i ++) mBox.fitTo(mGrid.getSize(), mGrid.getPosition(), Eigen::Vector2f(-32, -32));
{
mResultNames.push_back(TextComponent(mWindow, "RESULT...", font));
mResultNames.at(i).setColor(0x111111FF);
mList.forceRowHeight(6 + i, (unsigned int)mResultNames.at(i).getSize().y());
}
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); mGrid.resetCursor();
//mSearch->setSearchParams(params); // also starts the search
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<MetaDataList> 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;
} }
bool GuiGameScraper::input(InputConfig* config, Input input) bool GuiGameScraper::input(InputConfig* config, Input input)
{ {
if(config->isMappedTo("a", input) && input.value != 0) if(config->isMappedTo("b", input) && input.value)
{
//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(mSkipFunc) if(mSkipFunc)
mSkipFunc(); mSkipFunc();
@ -157,74 +93,23 @@ bool GuiGameScraper::input(InputConfig* config, Input input)
return true; return true;
} }
bool wasEditing = mSearchText.isEditing(); return GuiComponent::input(config, input);
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<HttpReq>(new HttpReq(thumb));
else
mThumbnailReq.reset();
}
} }
void GuiGameScraper::update(int deltaTime) 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); 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<HelpPrompt> GuiGameScraper::getHelpPrompts() std::vector<HelpPrompt> GuiGameScraper::getHelpPrompts()
{ {
std::vector<HelpPrompt> prompts = mList.getHelpPrompts(); return mGrid.getHelpPrompts();
if(getSelectedIndex() != -1)
prompts.push_back(HelpPrompt("a", "accept result"));
prompts.push_back(HelpPrompt("b", "cancel/skip"));
return prompts;
} }

View file

@ -1,17 +1,8 @@
#pragma once #pragma once
#include "../GuiComponent.h" #include "../GuiComponent.h"
#include "../scrapers/Scraper.h" #include "../components/ScraperSearchComponent.h"
#include "../components/ComponentGrid.h"
#include "../components/TextComponent.h"
#include "../components/ScrollableContainer.h"
#include "../components/TextEditComponent.h"
#include "../components/NinePatchComponent.h" #include "../components/NinePatchComponent.h"
#include "../components/ImageComponent.h"
#include "../Settings.h"
#include "../HttpReq.h"
#define MAX_SCRAPER_RESULTS 5
class GuiGameScraper : public GuiComponent class GuiGameScraper : public GuiComponent
{ {
@ -21,37 +12,18 @@ public:
bool input(InputConfig* config, Input input) override; bool input(InputConfig* config, Input input) override;
void update(int deltaTime) override; void update(int deltaTime) override;
void search();
virtual std::vector<HelpPrompt> getHelpPrompts() override; virtual std::vector<HelpPrompt> getHelpPrompts() override;
private: private:
int getSelectedIndex(); int mSearchCountdown; // haaack
void onSearchDone(std::vector<MetaDataList> results);
void updateInfoPane();
void updateThumbnail();
ComponentGrid mList; ComponentGrid mGrid;
NinePatchComponent mBox; NinePatchComponent mBox;
TextComponent mHeader; std::shared_ptr<ScraperSearchComponent> mSearch;
TextComponent mResultName;
ScrollableContainer mResultInfo;
TextComponent mResultDesc;
ImageComponent mResultThumbnail;
TextComponent mSearchLabel;
TextEditComponent mSearchText;
std::vector<TextComponent> mResultNames;
ScraperSearchParams mSearchParams; ScraperSearchParams mSearchParams;
std::vector<MetaDataList> mScraperResults;
std::function<void(MetaDataList)> mDoneFunc; std::function<void(MetaDataList)> mDoneFunc;
std::function<void()> mSkipFunc; std::function<void()> mSkipFunc;
std::unique_ptr<HttpReq> mThumbnailReq;
}; };

View file

@ -7,115 +7,50 @@
#include "GuiMsgBoxYesNo.h" #include "GuiMsgBoxYesNo.h"
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#define MDED_RESERVED_ROWS 3
GuiMetaDataEd::GuiMetaDataEd(Window* window, MetaDataList* md, const std::vector<MetaDataDecl>& mdd, ScraperSearchParams scraperParams, GuiMetaDataEd::GuiMetaDataEd(Window* window, MetaDataList* md, const std::vector<MetaDataDecl>& mdd, ScraperSearchParams scraperParams,
const std::string& header, std::function<void()> saveCallback, std::function<void()> deleteFunc) : GuiComponent(window), const std::string& header, std::function<void()> saveCallback, std::function<void()> deleteFunc) : GuiComponent(window),
mScraperParams(scraperParams), mScraperParams(scraperParams),
mBox(mWindow, ":/frame.png", 0xAAAAAAFF, 0xCCCCCCFF), mMenu(window, header.c_str()),
mList(window, Eigen::Vector2i(2, mdd.size() + MDED_RESERVED_ROWS)),
mHeader(window),
mMetaDataDecl(mdd), mMetaDataDecl(mdd),
mMetaData(md), mMetaData(md),
mSavedCallback(saveCallback), mDeleteFunc(deleteFunc), mSavedCallback(saveCallback), mDeleteFunc(deleteFunc)
mDeleteButton(window), mFetchButton(window), mSaveButton(window)
{ {
unsigned int sw = Renderer::getScreenWidth(); setSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight());
unsigned int sh = Renderer::getScreenHeight();
addChild(&mBox); addChild(&mMenu);
addChild(&mHeader);
mHeader.setText(header);
//initialize buttons
mDeleteButton.setText("DELETE", "delete file");
if(mDeleteFunc)
{
std::function<void()> deleteFileAndSelf = [&] { mDeleteFunc(); delete this; };
std::function<void()> 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<MetaDataDecl>& 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++;
// populate list
for(auto iter = mdd.begin(); iter != mdd.end(); iter++) for(auto iter = mdd.begin(); iter != mdd.end(); iter++)
{ {
TextComponent* label = new TextComponent(mWindow); auto ed = MetaDataList::makeEditor(mWindow, iter->type);
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());
ed->setValue(mMetaData->get(iter->key)); ed->setValue(mMetaData->get(iter->key));
mList.setEntry(Vector2i(1, y), Vector2i(1, 1), ed, true, ComponentGrid::AlignRight);
mEditors.push_back(ed); mEditors.push_back(ed);
mMenu.addWithLabel(iter->key, ed);
y++;
} }
//save button //add buttons
mList.setEntry(Vector2i(0, y), Vector2i(2, 1), &mSaveButton, true, ComponentGrid::AlignCenter); 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() 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) if(mSavedCallback)
@ -126,7 +61,6 @@ void GuiMetaDataEd::fetch()
{ {
GuiGameScraper* scr = new GuiGameScraper(mWindow, mScraperParams, std::bind(&GuiMetaDataEd::fetchDone, this, std::placeholders::_1)); GuiGameScraper* scr = new GuiGameScraper(mWindow, mScraperParams, std::bind(&GuiMetaDataEd::fetchDone, this, std::placeholders::_1));
mWindow->pushGui(scr); mWindow->pushGui(scr);
scr->search();
} }
void GuiMetaDataEd::fetchDone(MetaDataList result) void GuiMetaDataEd::fetchDone(MetaDataList result)
@ -187,7 +121,7 @@ bool GuiMetaDataEd::input(InputConfig* config, Input input)
std::vector<HelpPrompt> GuiMetaDataEd::getHelpPrompts() std::vector<HelpPrompt> GuiMetaDataEd::getHelpPrompts()
{ {
std::vector<HelpPrompt> prompts = mList.getHelpPrompts(); std::vector<HelpPrompt> prompts = mMenu.getHelpPrompts();
prompts.push_back(HelpPrompt("b", "discard changes")); prompts.push_back(HelpPrompt("b", "discard changes"));
return prompts; return prompts;
} }

View file

@ -1,11 +1,8 @@
#pragma once #pragma once
#include "../GuiComponent.h" #include "../GuiComponent.h"
#include "../components/ComponentGrid.h" #include "../components/MenuComponent.h"
#include "../MetaData.h" #include "../MetaData.h"
#include "../components/TextComponent.h"
#include "../components/NinePatchComponent.h"
#include "../components/ButtonComponent.h"
#include "../scrapers/Scraper.h" #include "../scrapers/Scraper.h"
#include <functional> #include <functional>
@ -15,7 +12,6 @@ class GuiMetaDataEd : public GuiComponent
public: public:
GuiMetaDataEd(Window* window, MetaDataList* md, const std::vector<MetaDataDecl>& mdd, ScraperSearchParams params, GuiMetaDataEd(Window* window, MetaDataList* md, const std::vector<MetaDataDecl>& mdd, ScraperSearchParams params,
const std::string& header, std::function<void()> savedCallback, std::function<void()> deleteFunc); const std::string& header, std::function<void()> savedCallback, std::function<void()> deleteFunc);
virtual ~GuiMetaDataEd();
bool input(InputConfig* config, Input input) override; bool input(InputConfig* config, Input input) override;
@ -26,25 +22,14 @@ private:
void fetch(); void fetch();
void fetchDone(MetaDataList result); void fetchDone(MetaDataList result);
void populateList(const std::vector<MetaDataDecl>& mdd); MenuComponent mMenu;
ScraperSearchParams mScraperParams; ScraperSearchParams mScraperParams;
NinePatchComponent mBox; std::vector< std::shared_ptr<GuiComponent> > mEditors;
ComponentGrid mList;
TextComponent mHeader;
std::vector<TextComponent*> mLabels;
std::vector<GuiComponent*> mEditors;
std::vector<MetaDataDecl> mMetaDataDecl; std::vector<MetaDataDecl> mMetaDataDecl;
MetaDataList* mMetaData; MetaDataList* mMetaData;
std::function<void()> mSavedCallback; std::function<void()> mSavedCallback;
std::function<void()> mDeleteFunc; std::function<void()> mDeleteFunc;
ButtonComponent mDeleteButton;
ButtonComponent mFetchButton;
ButtonComponent mSaveButton;
}; };

View file

@ -61,7 +61,6 @@ void GuiScraperLog::next()
[this, search] { resultEmpty(search); }); [this, search] { resultEmpty(search); });
mWindow->pushGui(ggs); mWindow->pushGui(ggs);
ggs->search();
}else{ }else{
std::shared_ptr<Scraper> scraper = Settings::getInstance()->getScraper(); std::shared_ptr<Scraper> scraper = Settings::getInstance()->getScraper();
scraper->getResultsAsync(search, mWindow, [this, search] (std::vector<MetaDataList> mdls) { scraper->getResultsAsync(search, mWindow, [this, search] (std::vector<MetaDataList> mdls) {

View file

@ -1,9 +1,10 @@
#include "GamesDBScraper.h" #include "GamesDBScraper.h"
#include "../guis/GuiGameScraper.h" #include "../components/ScraperSearchComponent.h"
#include "../components/AsyncReqComponent.h" #include "../components/AsyncReqComponent.h"
#include "../Log.h" #include "../Log.h"
#include "../pugiXML/pugixml.hpp" #include "../pugiXML/pugixml.hpp"
#include "../MetaData.h" #include "../MetaData.h"
#include "../Settings.h"
#include <boost/assign.hpp> #include <boost/assign.hpp>
const char* GamesDBScraper::getName() { return "TheGamesDB"; } const char* GamesDBScraper::getName() { return "TheGamesDB"; }