First implementation of the new ComponentList stuff.

This commit is contained in:
Aloshi 2014-03-01 15:02:44 -06:00
parent 139fc720ac
commit c525d994d3
16 changed files with 1861 additions and 101 deletions

View file

@ -162,6 +162,7 @@ set(ES_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/src/components/AsyncReqComponent.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/ButtonComponent.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/ComponentGrid.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/ComponentList.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/DateTimeComponent.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/HelpComponent.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/IList.h
@ -244,6 +245,7 @@ set(ES_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/src/components/AsyncReqComponent.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/components/ButtonComponent.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/components/ComponentGrid.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/components/ComponentList.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/components/DateTimeComponent.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/components/HelpComponent.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/components/ImageComponent.cpp

File diff suppressed because it is too large Load diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View file

@ -37,7 +37,7 @@ namespace Renderer
void setMatrix(float* mat);
void setMatrix(const Eigen::Affine3f& transform);
void drawRect(int x, int y, int w, int h, unsigned int color);
void drawRect(int x, int y, int w, int h, unsigned int color, GLenum blend_sfactor = GL_SRC_ALPHA, GLenum blend_dfactor = GL_ONE_MINUS_SRC_ALPHA);
}
#endif

View file

@ -70,7 +70,7 @@ namespace Renderer {
}
}
void drawRect(int x, int y, int w, int h, unsigned int color)
void drawRect(int x, int y, int w, int h, unsigned int color, GLenum blend_sfactor, GLenum blend_dfactor)
{
#ifdef USE_OPENGL_ES
GLshort points[12];
@ -90,7 +90,7 @@ namespace Renderer {
buildGLColorArray(colors, color, 6);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBlendFunc(blend_sfactor, blend_dfactor);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);

View file

@ -0,0 +1,164 @@
#include "ComponentList.h"
#define TOTAL_HORIZONTAL_PADDING_PX 12
ComponentList::ComponentList(Window* window) : IList<ComponentListRow, void*>(window, LIST_SCROLL_STYLE_SLOW, LIST_NEVER_LOOP)
{
mSelectorBarOffset = 0;
}
void ComponentList::addRow(const ComponentListRow& row)
{
IList<ComponentListRow, void*>::Entry e;
e.name = "";
e.object = NULL;
e.data = row;
this->add(e);
for(auto it = mEntries.back().data.elements.begin(); it != mEntries.back().data.elements.end(); it++)
addChild(it->component.get());
updateElementSize(mEntries.back().data);
updateElementPosition(mEntries.back().data);
}
void ComponentList::onSizeChanged()
{
for(auto it = mEntries.begin(); it != mEntries.end(); it++)
{
updateElementSize(it->data);
updateElementPosition(it->data);
}
}
bool ComponentList::input(InputConfig* config, Input input)
{
if(size() == 0)
return false;
// give it to the current row's input handler
if(mEntries.at(mCursor).data.input_handler && mEntries.at(mCursor).data.input_handler(config, input))
return true;
// input handler didn't consume the input - try to scroll
if(config->isMappedTo("up", input))
{
return listInput(input.value != 0 ? -1 : 0);
}else if(config->isMappedTo("down", input))
{
return listInput(input.value != 0 ? 1 : 0);
}
return false;
}
void ComponentList::update(int deltaTime)
{
listUpdate(deltaTime);
}
void ComponentList::onCursorChanged(const CursorState& state)
{
// update the selector bar position
// in the future this might be animated
mSelectorBarOffset = 0;
for(int i = 0; i < mCursor; i++)
{
mSelectorBarOffset += getRowHeight(mEntries.at(i).data);
}
}
void ComponentList::render(const Eigen::Affine3f& parentTrans)
{
Eigen::Affine3f trans = parentTrans * getTransform();
// clip our entries inside our bounds
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()));
// draw our entries
renderChildren(trans);
Renderer::popClipRect();
// draw selector bar
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
Renderer::drawRect(0, (int)mSelectorBarOffset, (int)mSize.x(), (int)getRowHeight(mEntries.at(mCursor).data), 0xFFFFFFFF,
GL_ONE_MINUS_DST_COLOR, GL_ZERO);
Renderer::drawRect(0, (int)mSelectorBarOffset, (int)mSize.x(), (int)getRowHeight(mEntries.at(mCursor).data), 0x777777FF,
GL_ONE, GL_ONE);
// draw separators
float y = 0;
for(unsigned int i = 0; i < mEntries.size(); i++)
{
Renderer::drawRect(0, (int)y, (int)mSize.x(), 1, 0xC6C7C688);
y += getRowHeight(mEntries.at(i).data);
}
Renderer::drawRect(0, (int)y, (int)mSize.x(), 1, 0xC6C7C688);
}
float ComponentList::getRowHeight(const ComponentListRow& row)
{
// returns the highest component height found in the row
float height = 0;
for(unsigned int i = 0; i < row.elements.size(); i++)
{
if(row.elements.at(i).component->getSize().y() > height)
height = row.elements.at(i).component->getSize().y();
}
return height;
}
void ComponentList::updateElementPosition(const ComponentListRow& row)
{
float yOffset = 0;
for(auto it = mEntries.begin(); it != mEntries.end() && &it->data != &row; it++)
{
yOffset += getRowHeight(it->data);
}
// assumes updateElementSize has already been called
float rowHeight = getRowHeight(row);
float x = TOTAL_HORIZONTAL_PADDING_PX / 2;
for(unsigned int i = 0; i < row.elements.size(); i++)
{
const auto comp = row.elements.at(i).component;
// center vertically
comp->setPosition(x, (rowHeight - comp->getSize().y()) / 2 + yOffset);
x += comp->getSize().x();
}
}
void ComponentList::updateElementSize(const ComponentListRow& row)
{
float width = mSize.x() - TOTAL_HORIZONTAL_PADDING_PX;
std::vector< std::shared_ptr<GuiComponent> > resizeVec;
for(auto it = row.elements.begin(); it != row.elements.end(); it++)
{
if(it->resize_width)
resizeVec.push_back(it->component);
else
width -= it->component->getSize().x();
}
// redistribute the "unused" width equally among the components with resize_width set to true
width = width / resizeVec.size();
for(auto it = resizeVec.begin(); it != resizeVec.end(); it++)
{
(*it)->setSize(width, (*it)->getSize().y());
}
}

View file

@ -0,0 +1,48 @@
#pragma once
#include "IList.h"
#include <functional>
struct ComponentListElement
{
ComponentListElement(const std::shared_ptr<GuiComponent>& cmp = nullptr, bool resize_w = true) : component(cmp), resize_width(resize_w) { };
std::shared_ptr<GuiComponent> component;
bool resize_width;
};
struct ComponentListRow
{
std::vector<ComponentListElement> elements;
std::function<bool(InputConfig*, Input)> input_handler;
inline void addElement(const std::shared_ptr<GuiComponent>& component, bool resize_width)
{
elements.push_back(ComponentListElement(component, resize_width));
}
};
class ComponentList : public IList<ComponentListRow, void*>
{
public:
ComponentList(Window* window);
void addRow(const ComponentListRow& row);
bool input(InputConfig* config, Input input) override;
void update(int deltaTime) override;
void render(const Eigen::Affine3f& parentTrans) override;
void onSizeChanged() override;
protected:
void onCursorChanged(const CursorState& state) override;
private:
void updateElementPosition(const ComponentListRow& row);
void updateElementSize(const ComponentListRow& row);
float getRowHeight(const ComponentListRow& row);
float mSelectorBarOffset;
};

View file

@ -14,18 +14,38 @@ enum CursorState
CURSOR_SCROLLING
};
enum ListLoopType
{
LIST_ALWAYS_LOOP,
LIST_PAUSE_AT_END,
LIST_NEVER_LOOP
};
struct ScrollTier
{
int length; // how long we stay on this level before going to the next
int scrollDelay; // how long between scrolls
};
const int SCROLL_SPEED_COUNT = 3;
const ScrollTier SCROLL_SPEED[SCROLL_SPEED_COUNT] = {
struct ScrollTierList
{
const int count;
const ScrollTier* tiers;
};
// default scroll tiers
const ScrollTier QUICK_SCROLL_TIERS[] = {
{500, 500},
{5000, 114},
{0, 8}
};
const ScrollTierList LIST_SCROLL_STYLE_QUICK = { 3, QUICK_SCROLL_TIERS };
const ScrollTier SLOW_SCROLL_TIERS[] = {
{500, 500},
{0, 150}
};
const ScrollTierList LIST_SCROLL_STYLE_SLOW = { 2, SLOW_SCROLL_TIERS };
template <typename EntryData, typename UserData>
class IList : public GuiComponent
@ -52,10 +72,14 @@ protected:
ImageComponent mGradient;
std::shared_ptr<Font> mTitleOverlayFont;
const ScrollTierList& mTierList;
const ListLoopType mLoopType;
std::vector<Entry> mEntries;
public:
IList(Window* window) : GuiComponent(window), mGradient(window)
IList(Window* window, const ScrollTierList& tierList = LIST_SCROLL_STYLE_QUICK, const ListLoopType& loopType = LIST_PAUSE_AT_END) : GuiComponent(window),
mGradient(window), mTierList(tierList), mLoopType(loopType)
{
mCursor = 0;
mScrollTier = 0;
@ -125,7 +149,7 @@ public:
}
// entry management
void add(Entry e)
void add(const Entry& e)
{
mEntries.push_back(e);
}
@ -159,19 +183,22 @@ protected:
}
void listInput(int velocity) // a velocity of 0 = stop scrolling
bool listInput(int velocity) // a velocity of 0 = stop scrolling
{
mScrollVelocity = velocity;
mScrollTier = 0;
mScrollTierAccumulator = 0;
mScrollCursorAccumulator = 0;
int prevCursor = mCursor;
scroll(mScrollVelocity);
return (prevCursor != mCursor);
}
void listUpdate(int deltaTime)
{
// update the title overlay opacity
const int dir = (mScrollTier >= SCROLL_SPEED_COUNT - 1) ? 1 : -1; // fade in if scroll tier is >= 1, otherwise fade out
const int dir = (mScrollTier >= mTierList.count - 1) ? 1 : -1; // fade in if scroll tier is >= 1, otherwise fade out
int op = mTitleOverlayOpacity + deltaTime*dir; // we just do a 1-to-1 time -> opacity, no scaling
if(op >= 255)
mTitleOverlayOpacity = 255;
@ -189,16 +216,16 @@ protected:
// we delay scrolling until after scroll tier has updated so isScrolling() returns accurately during onCursorChanged callbacks
// we don't just do scroll tier first because it would not catch the scrollDelay == tier length case
int scrollCount = 0;
while(mScrollCursorAccumulator >= SCROLL_SPEED[mScrollTier].scrollDelay)
while(mScrollCursorAccumulator >= mTierList.tiers[mScrollTier].scrollDelay)
{
mScrollCursorAccumulator -= SCROLL_SPEED[mScrollTier].scrollDelay;
mScrollCursorAccumulator -= mTierList.tiers[mScrollTier].scrollDelay;
scrollCount++;
}
// are we ready to go even FASTER?
while(mScrollTier < SCROLL_SPEED_COUNT - 1 && mScrollTierAccumulator >= SCROLL_SPEED[mScrollTier].length)
while(mScrollTier < mTierList.count - 1 && mScrollTierAccumulator >= mTierList.tiers[mScrollTier].length)
{
mScrollTierAccumulator -= SCROLL_SPEED[mScrollTier].length;
mScrollTierAccumulator -= mTierList.tiers[mScrollTier].length;
mScrollTier++;
}
@ -237,16 +264,17 @@ protected:
// stop at the end if we've been holding down the button for a long time or
// we're scrolling faster than one item at a time (e.g. page up/down)
// otherwise, loop around
if(mScrollTier > 0 || absAmt > 1)
if((mLoopType == LIST_PAUSE_AT_END && (mScrollTier > 0 || absAmt > 1)) ||
mLoopType == LIST_NEVER_LOOP)
{
if(cursor < 0)
cursor = 0;
else if(cursor >= size())
cursor = size() - 1;
}else{
if(cursor < 0)
while(cursor < 0)
cursor += size();
else if(cursor >= size())
while(cursor >= size())
cursor -= size();
}

View file

@ -0,0 +1,26 @@
#include "MenuComponent.h"
MenuComponent::MenuComponent(Window* window, const char* title) : GuiComponent(window),
mBackground(window), mTitle(window), mList(window)
{
mBackground.setImagePath(":/frame.png");
mTitle.setText(title);
mTitle.setCentered(true);
addChild(&mBackground);
addChild(&mTitle);
addChild(&mList);
setSize(Renderer::getScreenWidth() * 0.6f, Renderer::getScreenHeight() * 0.8f);
}
void MenuComponent::onSizeChanged()
{
mBackground.fitTo(mSize, Eigen::Vector3f::Zero(), Eigen::Vector2f(-2, -2));
mTitle.setSize(mSize.x(), (float)mTitle.getFont()->getHeight());
mList.setPosition(0, mTitle.getSize().y());
mList.setSize(mSize.x(), mSize.y() - mTitle.getSize().y());
}

View file

@ -0,0 +1,20 @@
#pragma once
#include "NinePatchComponent.h"
#include "ComponentList.h"
#include "TextComponent.h"
class MenuComponent : public GuiComponent
{
public:
MenuComponent(Window* window, const char* title);
void onSizeChanged() override;
inline void addRow(const ComponentListRow& row) { mList.addRow(row); }
private:
NinePatchComponent mBackground;
TextComponent mTitle;
ComponentList mList;
};

View file

@ -9,9 +9,10 @@ TextComponent::TextComponent(Window* window) : GuiComponent(window),
{
}
TextComponent::TextComponent(Window* window, const std::string& text, std::shared_ptr<Font> font, Eigen::Vector3f pos, Eigen::Vector2f size) : GuiComponent(window),
TextComponent::TextComponent(Window* window, const std::string& text, std::shared_ptr<Font> font, unsigned int color, Eigen::Vector3f pos, Eigen::Vector2f size) : GuiComponent(window),
mFont(NULL), mColor(0x000000FF), mAutoCalcExtent(true, true), mCentered(false)
{
setColor(color);
setText(text);
setFont(font);
setPosition(pos);

View file

@ -15,7 +15,7 @@ class TextComponent : public GuiComponent
{
public:
TextComponent(Window* window);
TextComponent(Window* window, const std::string& text, std::shared_ptr<Font> font, Eigen::Vector3f pos = Eigen::Vector3f::Zero(), Eigen::Vector2f size = Eigen::Vector2f::Zero());
TextComponent(Window* window, const std::string& text, std::shared_ptr<Font> font, unsigned int color = 0x000000FF, Eigen::Vector3f pos = Eigen::Vector3f::Zero(), Eigen::Vector2f size = Eigen::Vector2f::Zero());
void setFont(std::shared_ptr<Font> font);
void onSizeChanged() override;

View file

@ -2,72 +2,93 @@
#include "GuiSettingsMenu.h"
#include "GuiScraperStart.h"
#include "../Window.h"
#include "../Sound.h"
#include "../Log.h"
#include "GuiMsgBoxYesNo.h"
GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mBackground(window, ":/button.png"), mList(window)
GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "Main Menu")
{
mList.add("Settings", [&] {
mWindow->pushGui(new GuiSettingsMenu(mWindow));
}, 0);
mList.add("Scrape Systems", [&] {
mWindow->pushGui(new GuiScraperStart(mWindow));
}, 0);
mList.add("Restart", [] {
if(system("sudo shutdown -r now") != 0)
LOG(LogWarning) << "Restart terminated with non-zero result!";
}, 0);
mList.add("Shutdown", [] {
if(system("sudo shutdown -h now") != 0)
LOG(LogWarning) << "Shutdown terminated with non-zero result!";
}, 1);
mList.add("Exit", [] {
SDL_Event* ev = new SDL_Event();
ev->type = SDL_QUIT;
SDL_PushEvent(ev);
}, 0);
struct MenuEntry
{
const char* name;
unsigned int color;
bool add_arrow;
std::function<void()> func;
};
MenuEntry entries[] = {
{ "GENERAL SETTINGS", 0x777777FF, true,
[this] { mWindow->pushGui(new GuiSettingsMenu(mWindow)); }
},
{ "SCRAPE NOW", 0x777777FF, true,
[this] { mWindow->pushGui(new GuiScraperStart(mWindow)); }
},
{ "RESTART SYSTEM", 0x990000FF, false,
[this] {
mWindow->pushGui(new GuiMsgBoxYesNo(mWindow, "Do you really want to restart the system?",
[] {
if(system("sudo shutdown -r now") != 0)
LOG(LogWarning) << "Restart terminated with non-zero result!";
}));
}
},
{ "SHUTDOWN SYSTEM", 0x990000FF, false,
[this] {
mWindow->pushGui(new GuiMsgBoxYesNo(mWindow, "Do you really want to shutdown the system?",
[] {
if(system("sudo shutdown -h now") != 0)
LOG(LogWarning) << "Shutdown terminated with non-zero result!";
}));
}
},
{ "EXIT EMULATIONSTATION", 0x990000FF, false,
[] {
SDL_Event ev;
ev.type = SDL_QUIT;
SDL_PushEvent(&ev);
}
}
};
setSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight());
mMenu.setPosition((mSize.x() - mMenu.getSize().x()) / 2, (mSize.y() - mMenu.getSize().y()) / 2);
mList.setPosition(mSize.x() * 0.175f, mSize.y() * 0.05f);
mList.setSize(mSize.x() * 0.65f, mSize.y() * 0.9f);
std::shared_ptr<Font> font = Font::get(FONT_SIZE_LARGE);
mTheme = ThemeData::getDefault();
// populate the list
ComponentListRow row;
using namespace ThemeFlags;
mBackground.applyTheme(mTheme, "menu", "windowBackground", PATH);
mBackground.fitTo(Eigen::Vector2f(mList.getSize().x(), mSize.y()), Eigen::Vector3f(mList.getPosition().x(), 0, 0));
addChild(&mBackground);
for(int i = 0; i < (sizeof(entries) / sizeof(entries[0])); i++)
{
row.elements.clear();
row.addElement(std::make_shared<TextComponent>(mWindow, entries[i].name, font, entries[i].color), true);
mList.setFont(Font::get((int)(0.09f * Renderer::getScreenHeight())));
mList.setSelectorColor(0xBBBBBBFF);
mList.setColor(0, 0x0000FFFF);
mList.setColor(1, 0xFF0000FF);
mList.applyTheme(mTheme, "menu", "menulist", FONT_PATH | COLOR | SOUND);
if(entries[i].add_arrow)
row.addElement(std::make_shared<TextComponent>(mWindow, ">", font, entries[i].color), false);
Sound::getFromTheme(mTheme, "menu", "menuOpen")->play();
std::function<void()>& execFunc = entries[i].func;
row.input_handler = [execFunc](InputConfig* config, Input input) -> bool
{
if(config->isMappedTo("a", input) && input.value != 0)
{
execFunc();
return true;
}
return false;
};
addChild(&mList);
mMenu.addRow(row);
}
addChild(&mMenu);
}
bool GuiMenu::input(InputConfig* config, Input input)
{
if(input.value != 0)
if((config->isMappedTo("b", input) || config->isMappedTo("menu", input)) && input.value != 0)
{
if(config->isMappedTo("b", input) || config->isMappedTo("menu", input))
{
Sound::getFromTheme(mTheme, "menu", "menuClose")->play();
delete this;
return true;
}else if(config->isMappedTo("a", input) && mList.size() > 0)
{
mList.getSelected()();
delete this;
return true;
}
delete this;
return true;
}
return GuiComponent::input(config, input);

View file

@ -1,8 +1,7 @@
#pragma once
#include "../GuiComponent.h"
#include "../components/TextListComponent.h"
#include "../components/NinePatchComponent.h"
#include "../components/MenuComponent.h"
#include <functional>
class GuiMenu : public GuiComponent
@ -13,7 +12,5 @@ public:
bool input(InputConfig* config, Input input) override;
private:
std::shared_ptr<ThemeData> mTheme;
NinePatchComponent mBackground;
TextListComponent< std::function<void()> > mList;
MenuComponent mMenu;
};

View file

@ -10,7 +10,7 @@
#define SELECTED_SCALE 1.5f
#define LOGO_PADDING ((logoSize().x() * (SELECTED_SCALE - 1)/2) + (mSize.x() * 0.06f))
SystemView::SystemView(Window* window) : IList<SystemViewData, SystemData*>(window)
SystemView::SystemView(Window* window) : IList<SystemViewData, SystemData*>(window, LIST_SCROLL_STYLE_SLOW, LIST_ALWAYS_LOOP)
{
mCamOffset = 0;

View file

@ -5,6 +5,8 @@
#include "../../guis/GuiFastSelect.h"
#include "../ViewController.h"
#include "../../Settings.h"
#include "../../Log.h"
#include "../../Sound.h"
bool IGameListView::input(InputConfig* config, Input input)
{
@ -37,6 +39,7 @@ bool IGameListView::input(InputConfig* config, Input input)
}else if(config->isMappedTo("select", input) && input.value != 0)
{
// open fast select
Sound::getFromTheme(mTheme, getName(), "menuOpen")->play();
mWindow->pushGui(new GuiFastSelect(mWindow, this));
return true;
}