Skip to content

Commit 93690e9

Browse files
authored
Merge pull request #1 from RetroPie/master
Merge pull request RetroPie#797 from cmitu/sdl-vendored-input
2 parents 0c4b42d + 08d74d3 commit 93690e9

File tree

16 files changed

+226
-150
lines changed

16 files changed

+226
-150
lines changed

es-app/src/animations/MoveCameraAnimation.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,21 @@
77
class MoveCameraAnimation : public Animation
88
{
99
public:
10-
MoveCameraAnimation(Transform4x4f& camera, const Vector3f& target) : mCameraStart(camera), mTarget(target), cameraOut(camera) {}
10+
MoveCameraAnimation(Transform4x4f& camera, const Vector3f& target) : mCameraStart(camera), mTarget(target), mCameraOut(camera) { }
1111

1212
int getDuration() const override { return 400; }
1313

1414
void apply(float t) override
1515
{
16-
// cubic ease out
1716
t -= 1;
18-
cameraOut.translation() = -Vector3f().lerp(-mCameraStart.translation(), mTarget, t*t*t + 1);
17+
mCameraOut.translation() = -Vector3f().lerp(-mCameraStart.translation(), mTarget, t*t*t + 1 /*cubic ease out*/);
1918
}
2019

2120
private:
2221
Transform4x4f mCameraStart;
2322
Vector3f mTarget;
2423

25-
Transform4x4f& cameraOut;
24+
Transform4x4f& mCameraOut;
2625
};
2726

2827
#endif // ES_APP_ANIMATIONS_MOVE_CAMERA_ANIMATION_H

es-app/src/components/TextListComponent.h

Lines changed: 62 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class TextListComponent : public IList<TextListData, T>
3030
using IList<TextListData, T>::getTransform;
3131
using IList<TextListData, T>::mSize;
3232
using IList<TextListData, T>::mCursor;
33+
using IList<TextListData, T>::mViewportTop;
3334
using IList<TextListData, T>::Entry;
3435

3536
public:
@@ -92,7 +93,7 @@ class TextListComponent : public IList<TextListData, T>
9293
Alignment mAlignment;
9394
float mHorizontalMargin;
9495

95-
int getFirstVisibleEntry();
96+
int viewportTop();
9697
std::function<void(CursorState state)> mCursorChangedCallback;
9798

9899
std::shared_ptr<Font> mFont;
@@ -107,10 +108,8 @@ class TextListComponent : public IList<TextListData, T>
107108
std::string mScrollSound;
108109
static const unsigned int COLOR_ID_COUNT = 2;
109110
unsigned int mColors[COLOR_ID_COUNT];
110-
unsigned int mScreenCount;
111-
int mStartEntry = 0;
112-
unsigned int mCursorPrev = -1;
113-
bool mOneEntryUpDn = true;
111+
int mViewportHeight;
112+
int mCursorPrev = -1;
114113

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

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

154-
// number of entries that can fit on the screen simultaniously
155-
mScreenCount = (int)(mSize.y() / entrySize);
153+
// number of listentries that can fit on the screen
154+
mViewportHeight = (int)(mSize.y() / entrySize);
156155

156+
if(mViewportTop == -1)
157+
{
158+
// returning from screen saver activated game launch
159+
mViewportTop = mCursor - mViewportHeight/2;
160+
}
157161
if(mCursor != mCursorPrev)
158162
{
159-
mStartEntry = (size() > mScreenCount) ? getFirstVisibleEntry() : 0;
163+
mViewportTop = (size() > mViewportHeight) ? viewportTop() : 0;
160164
mCursorPrev = mCursor;
161165
}
162166

163-
unsigned int listCutoff = mStartEntry + mScreenCount;
167+
unsigned int listCutoff = mViewportTop + mViewportHeight;
164168
if(listCutoff > size())
165169
listCutoff = size();
166170

167-
float y = (mSize.y() - (mScreenCount * entrySize)) * 0.5f;
171+
float y = (mSize.y() - (mViewportHeight * entrySize)) * 0.5f;
168172

169-
// draw selector bar
170-
if(mStartEntry < listCutoff)
171-
{
172-
if (mSelectorImage.hasImage()) {
173-
mSelectorImage.setPosition(0.f, y + (mCursor - mStartEntry)*entrySize + mSelectorOffsetY, 0.f);
174-
mSelectorImage.render(trans);
175-
} else {
176-
Renderer::setMatrix(trans);
177-
Renderer::drawRect(0.0f, y + (mCursor - mStartEntry)*entrySize + mSelectorOffsetY, mSize.x(),
178-
mSelectorHeight, mSelectorColor, mSelectorColorEnd, mSelectorColorGradientHorizontal);
179-
}
173+
if (mSelectorImage.hasImage()) {
174+
mSelectorImage.setPosition(0.f, y + (mCursor - mViewportTop)*entrySize + mSelectorOffsetY, 0.f);
175+
mSelectorImage.render(trans);
176+
} else {
177+
Renderer::setMatrix(trans);
178+
Renderer::drawRect(0.0f, y + (mCursor - mViewportTop)*entrySize + mSelectorOffsetY, mSize.x(),
179+
mSelectorHeight, mSelectorColor, mSelectorColorEnd, mSelectorColorGradientHorizontal);
180180
}
181181

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

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

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

255255

256256
template <typename T>
257-
int TextListComponent<T>::getFirstVisibleEntry()
257+
int TextListComponent<T>::viewportTop()
258258
{
259+
int viewportTopMax = size() - mViewportHeight;
260+
int topNew = mViewportTop;
261+
259262
if (mCursorPrev == -1)
260-
{
261-
// init or returned from emulator
262263
mCursorPrev = mCursor;
263-
int quot = div(mCursor, mScreenCount).quot;
264-
mStartEntry = quot * mScreenCount;
265-
}
266-
int screenRelCursor = mCursorPrev - mStartEntry;
267-
bool cursorCentered = screenRelCursor == mScreenCount/2;
268-
int visibleEntryMax = size() - mScreenCount;
269-
int firstVisibleEntry = 0;
270264

271-
if(Settings::getInstance()->getBool("UseFullscreenPaging") && !cursorCentered)
265+
int delta = mCursor - mCursorPrev;
266+
267+
if(Settings::getInstance()->getBool("UseFullscreenPaging"))
272268
{
273-
// keep visible cursor constant but move visible list (default)
274-
firstVisibleEntry = mCursor - screenRelCursor;
275-
if(mOneEntryUpDn)
269+
// delta may be greater/less than +/-mViewportHeight on re-sorting of list
270+
if (delta <= -mViewportHeight || delta >= mViewportHeight
271+
// keep cursor sticky at position unless the user navigates
272+
// to the middle of the viewport
273+
|| delta < 0 && mCursor - mViewportTop < mViewportHeight/2
274+
|| delta > 0 && mCursor - mViewportTop > mViewportHeight/2)
276275
{
277-
int delta = mCursor - mCursorPrev;
278-
// detect rollover (== delta is more than one item)
279-
if(delta < -3)
280-
firstVisibleEntry = 0;
281-
else if(delta > 3)
282-
firstVisibleEntry = visibleEntryMax;
283-
else if(screenRelCursor < mScreenCount/2 && delta > 0 /*down pressed*/
284-
|| screenRelCursor > mScreenCount/2 && delta < 0 /*up pressed*/)
285-
// cases for list begin / list end
286-
// move visible cursor and keep visible list section constant
287-
firstVisibleEntry = firstVisibleEntry - delta;
276+
topNew += delta;
288277
}
289-
} else {
290-
// cursor always in middle of visible list
291-
firstVisibleEntry = mCursor - mScreenCount/2;
278+
// no match above will place the cursor more towards the middle
292279
}
293-
// bounds check
294-
if(firstVisibleEntry < 0)
295-
firstVisibleEntry = 0;
296-
else if(firstVisibleEntry > visibleEntryMax)
297-
firstVisibleEntry = visibleEntryMax;
298-
return firstVisibleEntry;
280+
else
281+
// put cursor in middle of visible list
282+
topNew = mCursor - mViewportHeight/2;
283+
284+
if (mCursor <= mViewportHeight/2)
285+
topNew = 0;
286+
else if (mCursor >= viewportTopMax + mViewportHeight/2)
287+
topNew = viewportTopMax;
288+
289+
return topNew;
299290
}
300291

301292
template <typename T>
302293
bool TextListComponent<T>::input(InputConfig* config, Input input)
303294
{
304-
if(size() > 0)
295+
bool isSingleStep = config->isMappedLike("down", input) || config->isMappedLike("up", input);
296+
bool isPageStep = config->isMappedLike("rightshoulder", input) || config->isMappedLike("leftshoulder", input);
297+
298+
if(size() > 0 && (isSingleStep || isPageStep))
305299
{
306300
if(input.value != 0)
307301
{
308-
if(config->isMappedLike("down", input))
309-
{
310-
listInput(1);
311-
mOneEntryUpDn = true;
312-
return true;
313-
}
314-
315-
if(config->isMappedLike("up", input))
316-
{
317-
listInput(-1);
318-
mOneEntryUpDn = true;
319-
return true;
320-
}
321-
if(config->isMappedLike("rightshoulder", input))
322-
{
323-
int delta = Settings::getInstance()->getBool("UseFullscreenPaging") ? mScreenCount : 10;
324-
listInput(delta);
325-
mOneEntryUpDn = false;
326-
return true;
327-
}
328-
329-
if(config->isMappedLike("leftshoulder", input))
302+
int delta;
303+
mCursorPrev = mCursor;
304+
if(isSingleStep)
305+
delta = config->isMappedLike("down", input) ? 1 : -1;
306+
else
330307
{
331-
int delta = Settings::getInstance()->getBool("UseFullscreenPaging") ? mScreenCount : 10;
332-
listInput(-delta);
333-
mOneEntryUpDn = false;
334-
return true;
308+
delta = Settings::getInstance()->getBool("UseFullscreenPaging") ? mViewportHeight : 10;
309+
if (config->isMappedLike("leftshoulder", input))
310+
delta = -delta;
335311
}
312+
listInput(delta);
313+
return true;
336314
}else{
337-
if(config->isMappedLike("down", input) || config->isMappedLike("up", input) ||
338-
config->isMappedLike("rightshoulder", input) || config->isMappedLike("leftshoulder", input))
339-
{
340-
stopScrolling();
341-
}
315+
stopScrolling();
342316
}
343317
}
344318

es-app/src/views/ViewController.cpp

Lines changed: 62 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ void ViewController::goToStart()
5454
if ((*it)->getName() == requestedSystem)
5555
{
5656
goToGameList(*it);
57-
Scripting::fireEvent("system-select", requestedSystem, "requestedsystem");
57+
Scripting::fireEvent("system-select", requestedSystem, "requestedsystem");
5858
FileData* cursor = getGameListView(*it)->getCursor();
5959
if (cursor != NULL)
6060
{
@@ -193,11 +193,50 @@ void ViewController::playViewTransition()
193193
}else{
194194
advanceAnimation(0, (int)(mFadeOpacity * FADE_DURATION));
195195
}
196-
} else if (transition_style == "slide"){
196+
}
197+
else if (transition_style == "slide")
198+
{
197199
// slide or simple slide
198-
setAnimation(new MoveCameraAnimation(mCamera, target));
200+
bool inGamelistNav = -mCamera.translation().y() == target.y() // not in/out gamelist nav
201+
&& -mCamera.translation().x() - target.x(); // left/right movement
202+
cancelAnimation(0);
203+
Vector3f tgt = Vector3f(target);
204+
Vector3f positionOrig;
205+
if (inGamelistNav) {
206+
const float screenWidth = (float)Renderer::getScreenWidth();
207+
if (-mCamera.translation().x() - tgt.x() >= 2 * screenWidth)
208+
{
209+
// right rollover
210+
mLockInput = true;
211+
tgt.x() = screenWidth * mGameListViews.size();
212+
}
213+
else if (-mCamera.translation().x() - tgt.x() <= 2 * -screenWidth)
214+
{
215+
// left rollover
216+
mLockInput = true;
217+
tgt.x() = -screenWidth;
218+
}
219+
// deny any further input on rollover as mCurrentView would be
220+
// different on subsequent animations, resulting in restoring
221+
// a unrelated mCurrentView/mCamera with the original position
222+
if (mLockInput)
223+
{
224+
positionOrig = Vector3f(mCurrentView->getPosition());
225+
mCurrentView->setPosition(tgt.x(), tgt.y());
226+
}
227+
}
228+
229+
setAnimation(new MoveCameraAnimation(mCamera, tgt), 0, [this, positionOrig] {
230+
if (mLockInput) {
231+
mCurrentView->setPosition(positionOrig);
232+
mCamera.translation() = -positionOrig;
233+
}
234+
mLockInput = false;
235+
});
199236
updateHelpPrompts(); // update help prompts immediately
200-
} else {
237+
}
238+
else
239+
{
201240
// instant
202241
setAnimation(new LambdaAnimation(
203242
[this, target](float /*t*/)
@@ -247,8 +286,10 @@ void ViewController::launch(FileData* game, Vector3f center)
247286
game->launchGame(mWindow);
248287
setAnimation(new LambdaAnimation(fadeFunc, 800), 0, [this] { mLockInput = false; }, true);
249288
this->onFileChanged(game, FILE_METADATA_CHANGED);
250-
if (mCurrentView)
289+
if (mCurrentView) {
290+
this->getGameListView(game->getSystem())->setViewportTop(-1);
251291
mCurrentView->onShow();
292+
}
252293
});
253294
} else if (transition_style == "slide"){
254295
// move camera to zoom in on center + fade out, launch game, come back in
@@ -258,8 +299,10 @@ void ViewController::launch(FileData* game, Vector3f center)
258299
mCamera = origCamera;
259300
setAnimation(new LaunchAnimation(mCamera, mFadeOpacity, center, 600), 0, [this] { mLockInput = false; }, true);
260301
this->onFileChanged(game, FILE_METADATA_CHANGED);
261-
if (mCurrentView)
302+
if (mCurrentView) {
303+
this->getGameListView(game->getSystem())->setViewportTop(-1);
262304
mCurrentView->onShow();
305+
}
263306
});
264307
} else { // instant
265308
setAnimation(new LaunchAnimation(mCamera, mFadeOpacity, center, 10), 0, [this, origCamera, center, game]
@@ -268,8 +311,10 @@ void ViewController::launch(FileData* game, Vector3f center)
268311
mCamera = origCamera;
269312
setAnimation(new LaunchAnimation(mCamera, mFadeOpacity, center, 10), 0, [this] { mLockInput = false; }, true);
270313
this->onFileChanged(game, FILE_METADATA_CHANGED);
271-
if (mCurrentView)
314+
if (mCurrentView) {
315+
this->getGameListView(game->getSystem())->setViewportTop(-1);
272316
mCurrentView->onShow();
317+
}
273318
});
274319
}
275320
}
@@ -477,6 +522,7 @@ void ViewController::reloadGameListView(IGameListView* view, bool reloadTheme)
477522
bool isCurrent = (mCurrentView == it->second);
478523
SystemData* system = it->first;
479524
FileData* cursor = view->getCursor();
525+
int viewportTop = view->getViewportTop();
480526
mGameListViews.erase(it);
481527

482528
if(reloadTheme)
@@ -487,6 +533,7 @@ void ViewController::reloadGameListView(IGameListView* view, bool reloadTheme)
487533
// to counter having come from a placeholder
488534
if (!cursor->isPlaceHolder()) {
489535
newView->setCursor(cursor);
536+
newView->setViewportTop(viewportTop);
490537
}
491538
if(isCurrent)
492539
mCurrentView = newView;
@@ -504,9 +551,11 @@ void ViewController::reloadAll()
504551
{
505552
// clear all gamelistviews
506553
std::map<SystemData*, FileData*> cursorMap;
554+
std::map<SystemData*, int> viewportTopMap;
507555
for(auto it = mGameListViews.cbegin(); it != mGameListViews.cend(); it++)
508556
{
509557
cursorMap[it->first] = it->second->getCursor();
558+
viewportTopMap[it->first] = it->second->getViewportTop();
510559
}
511560
mGameListViews.clear();
512561

@@ -519,6 +568,12 @@ void ViewController::reloadAll()
519568
getGameListView(it->first)->setCursor(it->second);
520569
}
521570

571+
// restore index of first list item on display
572+
for(auto it = viewportTopMap.cbegin(); it != viewportTopMap.cend(); it++)
573+
{
574+
getGameListView(it->first)->setViewportTop(it->second);
575+
}
576+
522577
// Rebuild SystemListView
523578
mSystemListView.reset();
524579
getSystemListView();

0 commit comments

Comments
 (0)