Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix misplaced cursor on LB/LR paging on specific conditions #811

Merged
merged 1 commit into from
Mar 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 62 additions & 88 deletions es-app/src/components/TextListComponent.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class TextListComponent : public IList<TextListData, T>
using IList<TextListData, T>::getTransform;
using IList<TextListData, T>::mSize;
using IList<TextListData, T>::mCursor;
using IList<TextListData, T>::mViewportTop;
using IList<TextListData, T>::Entry;

public:
Expand Down Expand Up @@ -92,7 +93,7 @@ class TextListComponent : public IList<TextListData, T>
Alignment mAlignment;
float mHorizontalMargin;

int getFirstVisibleEntry();
int viewportTop();
std::function<void(CursorState state)> mCursorChangedCallback;

std::shared_ptr<Font> mFont;
Expand All @@ -107,10 +108,8 @@ class TextListComponent : public IList<TextListData, T>
std::string mScrollSound;
static const unsigned int COLOR_ID_COUNT = 2;
unsigned int mColors[COLOR_ID_COUNT];
unsigned int mScreenCount;
int mStartEntry = 0;
unsigned int mCursorPrev = -1;
bool mOneEntryUpDn = true;
int mViewportHeight;
int mCursorPrev = -1;

ImageComponent mSelectorImage;
};
Expand Down Expand Up @@ -151,32 +150,33 @@ void TextListComponent<T>::render(const Transform4x4f& parentTrans)

const float entrySize = Math::max(font->getHeight(1.0), (float)font->getSize()) * mLineSpacing;

// number of entries that can fit on the screen simultaniously
mScreenCount = (int)(mSize.y() / entrySize);
// number of listentries that can fit on the screen
mViewportHeight = (int)(mSize.y() / entrySize);

if(mViewportTop == -1)
{
// returning from screen saver activated game launch
mViewportTop = mCursor - mViewportHeight/2;
}
if(mCursor != mCursorPrev)
{
mStartEntry = (size() > mScreenCount) ? getFirstVisibleEntry() : 0;
mViewportTop = (size() > mViewportHeight) ? viewportTop() : 0;
mCursorPrev = mCursor;
}

unsigned int listCutoff = mStartEntry + mScreenCount;
unsigned int listCutoff = mViewportTop + mViewportHeight;
if(listCutoff > size())
listCutoff = size();

float y = (mSize.y() - (mScreenCount * entrySize)) * 0.5f;
float y = (mSize.y() - (mViewportHeight * entrySize)) * 0.5f;

// draw selector bar
if(mStartEntry < listCutoff)
{
if (mSelectorImage.hasImage()) {
mSelectorImage.setPosition(0.f, y + (mCursor - mStartEntry)*entrySize + mSelectorOffsetY, 0.f);
mSelectorImage.render(trans);
} else {
Renderer::setMatrix(trans);
Renderer::drawRect(0.0f, y + (mCursor - mStartEntry)*entrySize + mSelectorOffsetY, mSize.x(),
mSelectorHeight, mSelectorColor, mSelectorColorEnd, mSelectorColorGradientHorizontal);
}
if (mSelectorImage.hasImage()) {
mSelectorImage.setPosition(0.f, y + (mCursor - mViewportTop)*entrySize + mSelectorOffsetY, 0.f);
mSelectorImage.render(trans);
} else {
Renderer::setMatrix(trans);
Renderer::drawRect(0.0f, y + (mCursor - mViewportTop)*entrySize + mSelectorOffsetY, mSize.x(),
mSelectorHeight, mSelectorColor, mSelectorColorEnd, mSelectorColorGradientHorizontal);
}

// clip to inside margins
Expand All @@ -185,7 +185,7 @@ void TextListComponent<T>::render(const Transform4x4f& parentTrans)
Renderer::pushClipRect(Vector2i((int)(trans.translation().x() + mHorizontalMargin), (int)trans.translation().y()),
Vector2i((int)(dim.x() - mHorizontalMargin*2), (int)dim.y()));

for(int i = mStartEntry; i < listCutoff; i++)
for(int i = mViewportTop; i < listCutoff; i++)
{
typename IList<TextListData, T>::Entry& entry = mEntries.at((unsigned int)i);

Expand Down Expand Up @@ -254,91 +254,65 @@ void TextListComponent<T>::render(const Transform4x4f& parentTrans)


template <typename T>
int TextListComponent<T>::getFirstVisibleEntry()
int TextListComponent<T>::viewportTop()
{
int viewportTopMax = size() - mViewportHeight;
int topNew = mViewportTop;

if (mCursorPrev == -1)
{
// init or returned from emulator
mCursorPrev = mCursor;
int quot = div(mCursor, mScreenCount).quot;
mStartEntry = quot * mScreenCount;
}
int screenRelCursor = mCursorPrev - mStartEntry;
bool cursorCentered = screenRelCursor == mScreenCount/2;
int visibleEntryMax = size() - mScreenCount;
int firstVisibleEntry = 0;

if(Settings::getInstance()->getBool("UseFullscreenPaging") && !cursorCentered)
int delta = mCursor - mCursorPrev;

if(Settings::getInstance()->getBool("UseFullscreenPaging"))
{
// keep visible cursor constant but move visible list (default)
firstVisibleEntry = mCursor - screenRelCursor;
if(mOneEntryUpDn)
// delta may be greater/less than +/-mViewportHeight on re-sorting of list
if (delta <= -mViewportHeight || delta >= mViewportHeight
// keep cursor sticky at position unless the user navigates
// to the middle of the viewport
|| delta < 0 && mCursor - mViewportTop < mViewportHeight/2
|| delta > 0 && mCursor - mViewportTop > mViewportHeight/2)
{
int delta = mCursor - mCursorPrev;
// detect rollover (== delta is more than one item)
if(delta < -3)
firstVisibleEntry = 0;
else if(delta > 3)
firstVisibleEntry = visibleEntryMax;
else if(screenRelCursor < mScreenCount/2 && delta > 0 /*down pressed*/
|| screenRelCursor > mScreenCount/2 && delta < 0 /*up pressed*/)
// cases for list begin / list end
// move visible cursor and keep visible list section constant
firstVisibleEntry = firstVisibleEntry - delta;
topNew += delta;
}
} else {
// cursor always in middle of visible list
firstVisibleEntry = mCursor - mScreenCount/2;
// no match above will place the cursor more towards the middle
}
// bounds check
if(firstVisibleEntry < 0)
firstVisibleEntry = 0;
else if(firstVisibleEntry > visibleEntryMax)
firstVisibleEntry = visibleEntryMax;
return firstVisibleEntry;
else
// put cursor in middle of visible list
topNew = mCursor - mViewportHeight/2;

if (mCursor <= mViewportHeight/2)
topNew = 0;
else if (mCursor >= viewportTopMax + mViewportHeight/2)
topNew = viewportTopMax;

return topNew;
}

template <typename T>
bool TextListComponent<T>::input(InputConfig* config, Input input)
{
if(size() > 0)
bool isSingleStep = config->isMappedLike("down", input) || config->isMappedLike("up", input);
bool isPageStep = config->isMappedLike("rightshoulder", input) || config->isMappedLike("leftshoulder", input);

if(size() > 0 && (isSingleStep || isPageStep))
{
if(input.value != 0)
{
if(config->isMappedLike("down", input))
{
listInput(1);
mOneEntryUpDn = true;
return true;
}

if(config->isMappedLike("up", input))
{
listInput(-1);
mOneEntryUpDn = true;
return true;
}
if(config->isMappedLike("rightshoulder", input))
{
int delta = Settings::getInstance()->getBool("UseFullscreenPaging") ? mScreenCount : 10;
listInput(delta);
mOneEntryUpDn = false;
return true;
}

if(config->isMappedLike("leftshoulder", input))
int delta;
mCursorPrev = mCursor;
if(isSingleStep)
delta = config->isMappedLike("down", input) ? 1 : -1;
else
{
int delta = Settings::getInstance()->getBool("UseFullscreenPaging") ? mScreenCount : 10;
listInput(-delta);
mOneEntryUpDn = false;
return true;
delta = Settings::getInstance()->getBool("UseFullscreenPaging") ? mViewportHeight : 10;
if (config->isMappedLike("leftshoulder", input))
delta = -delta;
}
listInput(delta);
return true;
}else{
if(config->isMappedLike("down", input) || config->isMappedLike("up", input) ||
config->isMappedLike("rightshoulder", input) || config->isMappedLike("leftshoulder", input))
{
stopScrolling();
}
stopScrolling();
}
}

Expand Down
22 changes: 19 additions & 3 deletions es-app/src/views/ViewController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,10 @@ void ViewController::launch(FileData* game, Vector3f center)
game->launchGame(mWindow);
setAnimation(new LambdaAnimation(fadeFunc, 800), 0, [this] { mLockInput = false; }, true);
this->onFileChanged(game, FILE_METADATA_CHANGED);
if (mCurrentView)
if (mCurrentView) {
this->getGameListView(game->getSystem())->setViewportTop(-1);
mCurrentView->onShow();
}
});
} else if (transition_style == "slide"){
// move camera to zoom in on center + fade out, launch game, come back in
Expand All @@ -258,8 +260,10 @@ void ViewController::launch(FileData* game, Vector3f center)
mCamera = origCamera;
setAnimation(new LaunchAnimation(mCamera, mFadeOpacity, center, 600), 0, [this] { mLockInput = false; }, true);
this->onFileChanged(game, FILE_METADATA_CHANGED);
if (mCurrentView)
if (mCurrentView) {
this->getGameListView(game->getSystem())->setViewportTop(-1);
mCurrentView->onShow();
}
});
} else { // instant
setAnimation(new LaunchAnimation(mCamera, mFadeOpacity, center, 10), 0, [this, origCamera, center, game]
Expand All @@ -268,8 +272,10 @@ void ViewController::launch(FileData* game, Vector3f center)
mCamera = origCamera;
setAnimation(new LaunchAnimation(mCamera, mFadeOpacity, center, 10), 0, [this] { mLockInput = false; }, true);
this->onFileChanged(game, FILE_METADATA_CHANGED);
if (mCurrentView)
if (mCurrentView) {
this->getGameListView(game->getSystem())->setViewportTop(-1);
mCurrentView->onShow();
}
});
}
}
Expand Down Expand Up @@ -477,6 +483,7 @@ void ViewController::reloadGameListView(IGameListView* view, bool reloadTheme)
bool isCurrent = (mCurrentView == it->second);
SystemData* system = it->first;
FileData* cursor = view->getCursor();
int viewportTop = view->getViewportTop();
mGameListViews.erase(it);

if(reloadTheme)
Expand All @@ -487,6 +494,7 @@ void ViewController::reloadGameListView(IGameListView* view, bool reloadTheme)
// to counter having come from a placeholder
if (!cursor->isPlaceHolder()) {
newView->setCursor(cursor);
newView->setViewportTop(viewportTop);
}
if(isCurrent)
mCurrentView = newView;
Expand All @@ -504,9 +512,11 @@ void ViewController::reloadAll()
{
// clear all gamelistviews
std::map<SystemData*, FileData*> cursorMap;
std::map<SystemData*, int> viewportTopMap;
for(auto it = mGameListViews.cbegin(); it != mGameListViews.cend(); it++)
{
cursorMap[it->first] = it->second->getCursor();
viewportTopMap[it->first] = it->second->getViewportTop();
}
mGameListViews.clear();

Expand All @@ -519,6 +529,12 @@ void ViewController::reloadAll()
getGameListView(it->first)->setCursor(it->second);
}

// restore index of first list item on display
for(auto it = viewportTopMap.cbegin(); it != viewportTopMap.cend(); it++)
{
getGameListView(it->first)->setViewportTop(it->second);
}

// Rebuild SystemListView
mSystemListView.reset();
getSystemListView();
Expand Down
11 changes: 11 additions & 0 deletions es-app/src/views/gamelist/BasicGameListView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,17 @@ void BasicGameListView::setCursor(FileData* cursor)
}
}

void BasicGameListView::setViewportTop(int index)
{
mList.setViewportTop(index);
}


int BasicGameListView::getViewportTop()
{
return mList.getViewportTop();
}

void BasicGameListView::addPlaceholder()
{
// empty list - add a placeholder
Expand Down
2 changes: 2 additions & 0 deletions es-app/src/views/gamelist/BasicGameListView.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ class BasicGameListView : public ISimpleGameListView

virtual FileData* getCursor() override;
virtual void setCursor(FileData* file) override;
virtual int getViewportTop() override;
virtual void setViewportTop(int index) override;

virtual const char* getName() const override { return "basic"; }

Expand Down
2 changes: 2 additions & 0 deletions es-app/src/views/gamelist/GridGameListView.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ class GridGameListView : public ISimpleGameListView

virtual FileData* getCursor() override;
virtual void setCursor(FileData*) override;
virtual void setViewportTop(int index) { ; }
virtual int getViewportTop() { return -1; }

virtual bool input(InputConfig* config, Input input) override;

Expand Down
2 changes: 2 additions & 0 deletions es-app/src/views/gamelist/IGameListView.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ class IGameListView : public GuiComponent

virtual FileData* getCursor() = 0;
virtual void setCursor(FileData*) = 0;
virtual int getViewportTop() = 0;
virtual void setViewportTop(int index) = 0;

virtual bool input(InputConfig* config, Input input) override;
virtual void remove(FileData* game, bool deleteFile, bool refreshView=true) = 0;
Expand Down
2 changes: 2 additions & 0 deletions es-app/src/views/gamelist/ISimpleGameListView.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ class ISimpleGameListView : public IGameListView

virtual FileData* getCursor() = 0;
virtual void setCursor(FileData*) = 0;
virtual int getViewportTop() = 0;
virtual void setViewportTop(int index) = 0;

virtual bool input(InputConfig* config, Input input) override;
virtual void launch(FileData* game) = 0;
Expand Down
12 changes: 12 additions & 0 deletions es-core/src/components/IList.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class IList : public GuiComponent

protected:
int mCursor;
int mViewportTop;

int mScrollTier;
int mScrollVelocity;
Expand All @@ -81,6 +82,7 @@ class IList : public GuiComponent
mGradient(window), mTierList(tierList), mLoopType(loopType)
{
mCursor = 0;
mViewportTop = 0;
mScrollTier = 0;
mScrollVelocity = 0;
mScrollTierAccumulator = 0;
Expand Down Expand Up @@ -158,6 +160,16 @@ class IList : public GuiComponent
return false;
}

void setViewportTop(int index)
{
mViewportTop = index;
}

int getViewportTop()
{
return mViewportTop;
}

// entry management
void add(const Entry& e)
{
Expand Down