Permalink
Browse files

Merge pull request #10385 from unknownbrackets/ui-tween

UI: Add a soft fade when selecting choices / etc.
  • Loading branch information...
hrydgard committed Dec 10, 2017
2 parents b72c37b + bdac150 commit da3a7fbc5f6778d20f8ae4a3bb1b097b6cac25ea
Showing with 227 additions and 62 deletions.
  1. +0 −2 GPU/D3D11/FramebufferManagerD3D11.cpp
  2. +16 −7 UI/DevScreens.cpp
  3. +2 −0 UI/EmuScreen.cpp
  4. +56 −20 UI/Store.cpp
  5. +6 −2 UI/Store.h
  6. +23 −1 ext/native/ui/ui_tween.cpp
  7. +52 −11 ext/native/ui/ui_tween.h
  8. +56 −8 ext/native/ui/view.cpp
  9. +16 −11 ext/native/ui/view.h
@@ -46,8 +46,6 @@
#include "GPU/D3D11/TextureCacheD3D11.h"
#include "GPU/D3D11/DrawEngineD3D11.h"
#include "UI/OnScreenDisplay.h"
#include "ext/native/thin3d/thin3d.h"
#include <algorithm>
View
@@ -475,6 +475,15 @@ void SystemInfoScreen::CreateViews() {
#endif
#ifdef MOBILE_DEVICE
buildConfig->Add(new InfoItem("MOBILE_DEVICE", ""));
#endif
#if PPSSPP_ARCH(ARMV7S)
buildConfig->Add(new InfoItem("ARMV7S", ""));
#endif
#if PPSSPP_ARCH(ARM_NEON)
buildConfig->Add(new InfoItem("ARM_NEON", ""));
#endif
#ifdef _M_SSE
buildConfig->Add(new InfoItem("_M_SSE", StringFromFormat("0x%x", _M_SSE)));
#endif
if (System_GetPropertyBool(SYSPROP_APP_GOLD)) {
buildConfig->Add(new InfoItem("GOLD", ""));
@@ -492,7 +501,7 @@ void SystemInfoScreen::CreateViews() {
std::vector<std::string> exts;
SplitString(cpu_info.Summarize(), ',', exts);
for (size_t i = 2; i < exts.size(); i++) {
cpuExtensions->Add(new TextView(exts[i]))->SetFocusable(true);
cpuExtensions->Add(new TextView(exts[i], new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);
}
ViewGroup *gpuExtensionsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT));
@@ -514,8 +523,8 @@ void SystemInfoScreen::CreateViews() {
exts.clear();
SplitString(g_all_gl_extensions, ' ', exts);
std::sort(exts.begin(), exts.end());
for (size_t i = 0; i < exts.size(); i++) {
gpuExtensions->Add(new TextView(exts[i]))->SetFocusable(true);
for (auto &extension : exts) {
gpuExtensions->Add(new TextView(extension, new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);
}
exts.clear();
@@ -534,8 +543,8 @@ void SystemInfoScreen::CreateViews() {
eglExtensions->Add(new ItemHeader(si->T("EGL Extensions")));
for (size_t i = 0; i < exts.size(); i++) {
eglExtensions->Add(new TextView(exts[i]))->SetFocusable(true);
for (auto &extension : exts) {
eglExtensions->Add(new TextView(extension, new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);
}
}
} else if (g_Config.iGPUBackend == GPU_BACKEND_VULKAN) {
@@ -544,12 +553,12 @@ void SystemInfoScreen::CreateViews() {
gpuExtensions->Add(new ItemHeader(si->T("Vulkan Features")));
std::vector<std::string> features = draw->GetFeatureList();
for (auto &feature : features) {
gpuExtensions->Add(new TextView(feature))->SetFocusable(true);
gpuExtensions->Add(new TextView(feature, new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);
}
gpuExtensions->Add(new ItemHeader(si->T("Vulkan Extensions")));
std::vector<std::string> extensions = draw->GetExtensionList();
for (auto &extension : extensions) {
gpuExtensions->Add(new TextView(extension))->SetFocusable(true);
gpuExtensions->Add(new TextView(extension, new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);
}
}
}
View
@@ -831,7 +831,9 @@ void EmuScreen::CreateViews() {
// We start invisible here, in case of recreated views.
loadingViewColor_ = loadingView_->AddTween(new TextColorTween(0x00FFFFFF, 0x00FFFFFF, 0.2f, &bezierEaseInOut));
loadingViewColor_->Persist();
loadingViewVisible_ = loadingView_->AddTween(new VisibilityTween(UI::V_INVISIBLE, UI::V_INVISIBLE, 0.2f, &bezierEaseInOut));
loadingViewVisible_->Persist();
}
UI::EventReturn EmuScreen::OnDevTools(UI::EventParams &params) {
View
@@ -152,47 +152,54 @@ void HttpImageFileView::Draw(UIContext &dc) {
// TODO: involve sizemode
if (texture_) {
float tw = texture_->Width();
float th = texture_->Height();
float x = bounds_.x;
float y = bounds_.y;
float w = bounds_.w;
float h = bounds_.h;
if (tw / th < w / h) {
float nw = h * tw / th;
x += (w - nw) / 2.0f;
w = nw;
} else {
float nh = w * th / tw;
y += (h - nh) / 2.0f;
h = nh;
}
dc.Flush();
dc.GetDrawContext()->BindTexture(0, texture_->GetTexture());
dc.Draw()->Rect(bounds_.x, bounds_.y, bounds_.w, bounds_.h, color_);
dc.Draw()->Rect(x, y, w, h, color_);
dc.Flush();
dc.RebindTexture();
} else {
// draw a black rectangle to represent the missing image.
dc.FillRect(UI::Drawable(0xFF000000), GetBounds());
dc.FillRect(UI::Drawable(0x7F000000), GetBounds());
}
}
// This is the entry in a list. Does not have install buttons and so on.
class ProductItemView : public UI::Choice {
class ProductItemView : public UI::StickyChoice {
public:
ProductItemView(const StoreEntry &entry, UI::LayoutParams *layoutParams = 0)
: UI::Choice(entry.name, layoutParams), entry_(entry) {}
: UI::StickyChoice(entry.name, "", layoutParams), entry_(entry) {}
void GetContentDimensions(const UIContext &dc, float &w, float &h) const override {
w = 300;
h = 164;
}
void Update() override;
void Draw(UIContext &dc) override;
StoreEntry GetEntry() const { return entry_; }
private:
const StoreEntry &entry_;
};
void ProductItemView::Draw(UIContext &dc) {
UI::Choice::Draw(dc);
// dc.DrawText(entry_.name.c_str(), bounds_.centerX(), bounds_.centerY(), 0xFFFFFFFF, ALIGN_CENTER);
}
void ProductItemView::Update() {
View::Update();
}
// This is a "details" view of a game. Lets you install it.
class ProductView : public UI::LinearLayout {
public:
@@ -253,7 +260,7 @@ void ProductView::CreateViews() {
cancelButton_->SetVisibility(V_GONE);
// Add star rating, comments etc?
Add(new TextView(entry_.description));
Add(new TextView(entry_.description, ALIGN_LEFT | FLAG_WRAP_TEXT, false));
float size = entry_.size / (1024.f * 1024.f);
char temp[256];
@@ -326,7 +333,7 @@ UI::EventReturn ProductView::OnLaunchClick(UI::EventParams &e) {
return UI::EVENT_DONE;
}
StoreScreen::StoreScreen() : loading_(true), connectionError_(false) {
StoreScreen::StoreScreen() {
StoreFilter noFilter;
SetFilter(noFilter);
lang_ = g_Config.sLanguageIni;
@@ -438,23 +445,37 @@ void StoreScreen::CreateViews() {
content->Add(new TextView(loading_ ? st->T("Loading...") : st->T("Connection Error")));
content->Add(new Button(di->T("Retry")))->OnClick.Handle(this, &StoreScreen::OnRetry);
content->Add(new Button(di->T("Back")))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
scrollItemView_ = nullptr;
productPanel_ = nullptr;
} else {
content = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT, 1.0f));
ScrollView *leftScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(WRAP_CONTENT, FILL_PARENT, 0.4f));
leftScroll->SetTag("StoreMainList");
content->Add(leftScroll);
LinearLayout *scrollItemView = new LinearLayout(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, WRAP_CONTENT));
leftScroll->Add(scrollItemView);
scrollItemView_ = new LinearLayout(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, WRAP_CONTENT));
leftScroll->Add(scrollItemView_);
std::vector<StoreEntry> entries = FilterEntries();
for (size_t i = 0; i < entries.size(); i++) {
scrollItemView->Add(new ProductItemView(entries_[i]))->OnClick.Handle(this, &StoreScreen::OnGameSelected);
scrollItemView_->Add(new ProductItemView(entries_[i]))->OnClick.Handle(this, &StoreScreen::OnGameSelected);
}
// TODO: Similar apps, etc etc
productPanel_ = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(0.5f));
leftScroll->SetTag("StoreMainProduct");
content->Add(productPanel_);
ProductItemView *selectedItem = GetSelectedItem();
if (selectedItem) {
ProductView *productView = new ProductView(selectedItem->GetEntry());
productView->OnClickLaunch.Handle(this, &StoreScreen::OnGameLaunch);
productPanel_->Add(productView);
selectedItem->Press();
} else {
lastSelectedName_ = "";
}
}
root_->Add(content);
}
@@ -469,6 +490,16 @@ std::vector<StoreEntry> StoreScreen::FilterEntries() {
return filtered;
}
ProductItemView *StoreScreen::GetSelectedItem() {
for (int i = 0; i < scrollItemView_->GetNumSubviews(); ++i) {
ProductItemView *item = static_cast<ProductItemView *>(scrollItemView_->GetViewByIndex(i));
if (item->GetEntry().name == lastSelectedName_)
return item;
}
return nullptr;
}
UI::EventReturn StoreScreen::OnGameSelected(UI::EventParams &e) {
ProductItemView *item = static_cast<ProductItemView *>(e.v);
if (!item)
@@ -478,6 +509,11 @@ UI::EventReturn StoreScreen::OnGameSelected(UI::EventParams &e) {
ProductView *productView = new ProductView(item->GetEntry());
productView->OnClickLaunch.Handle(this, &StoreScreen::OnGameLaunch);
productPanel_->Add(productView);
ProductItemView *previousItem = GetSelectedItem();
if (previousItem && previousItem != item)
previousItem->Release();
lastSelectedName_ = item->GetEntry().name;
return UI::EVENT_DONE;
}
View
@@ -30,6 +30,7 @@
// Uses GameInfoCache heavily to implement the functionality.
struct json_value;
class ProductItemView;
enum EntryType {
ENTRY_PBPZIP,
@@ -74,6 +75,7 @@ class StoreScreen : public UIDialogScreenWithBackground {
private:
void SetFilter(const StoreFilter &filter);
void ParseListing(std::string json);
ProductItemView *GetSelectedItem();
std::vector<StoreEntry> FilterEntries();
std::string GetStoreJsonURL(std::string storePath) const;
@@ -86,8 +88,8 @@ class StoreScreen : public UIDialogScreenWithBackground {
// local filesystems at the moment.
std::string storePath_;
bool loading_;
bool connectionError_;
bool loading_ = true;
bool connectionError_ = false;
std::map<std::string, StoreCategory> categories_;
@@ -97,7 +99,9 @@ class StoreScreen : public UIDialogScreenWithBackground {
StoreFilter filter_;
std::string lang_;
std::string lastSelectedName_;
UI::ViewGroup *scrollItemView_;
UI::ViewGroup *productPanel_;
UI::TextView *titleText_;
};
View
@@ -5,20 +5,32 @@
namespace UI {
void Tween::Apply(View *view) {
if (time_now() >= start_ + duration_)
if (!valid_)
return;
if (DurationOffset() >= duration_)
finishApplied_ = true;
float pos = Position();
DoApply(view, pos);
if (finishApplied_) {
UI::EventParams e{};
e.v = view;
e.f = DurationOffset() - duration_;
Finish.Trigger(e);
}
}
template <typename Value>
void TweenBase<Value>::PersistData(PersistStatus status, std::string anonId, PersistMap &storage) {
struct TweenData {
float start;
float duration;
float delay;
Value from;
Value to;
bool valid;
};
PersistBuffer &buffer = storage["TweenBase::" + anonId];
@@ -30,17 +42,21 @@ void TweenBase<Value>::PersistData(PersistStatus status, std::string anonId, Per
TweenData &data = *(TweenData *)&buffer[0];
data.start = start_;
data.duration = duration_;
data.delay = delay_;
data.from = from_;
data.to = to_;
data.valid = valid_;
}
break;
case UI::PERSIST_RESTORE:
if (buffer.size() >= sizeof(TweenData) / sizeof(int)) {
TweenData data = *(TweenData *)&buffer[0];
start_ = data.start;
duration_ = data.duration;
delay_ = data.delay;
from_ = data.from;
to_ = data.to;
valid_ = data.valid;
// We skip finishApplied_ here so that the tween will reapply.
// This does mean it's important to remember to update tweens even after finish.
}
@@ -61,6 +77,12 @@ void TextColorTween::DoApply(View *view, float pos) {
tv->SetTextColor(Current(pos));
}
void CallbackColorTween::DoApply(View *view, float pos) {
if (callback_) {
callback_(view, Current(pos));
}
}
void VisibilityTween::DoApply(View *view, float pos) {
view->SetVisibility(Current(pos));
}
Oops, something went wrong.

0 comments on commit da3a7fb

Please sign in to comment.