mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-23 13:30:02 +00:00
Add really primitive save preview on "Next Slot". Improve pause screen layout a little.
This commit is contained in:
parent
6420f5f476
commit
001eda39e0
@ -18,6 +18,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "base/timeutil.h"
|
||||
#include "base/NativeApp.h"
|
||||
#include "i18n/i18n.h"
|
||||
|
||||
#include "Common/StdMutex.h"
|
||||
@ -308,6 +309,11 @@ namespace SaveState
|
||||
}
|
||||
}
|
||||
|
||||
int GetCurrentSlot()
|
||||
{
|
||||
return g_Config.iCurrentStateSlot;
|
||||
}
|
||||
|
||||
void NextSlot()
|
||||
{
|
||||
I18NCategory *sy = GetI18NCategory("System");
|
||||
@ -315,6 +321,7 @@ namespace SaveState
|
||||
char msg[128];
|
||||
snprintf(msg, sizeof(msg), "%s: %d", sy->T("Savestate Slot"), g_Config.iCurrentStateSlot + 1);
|
||||
osm.Show(msg);
|
||||
NativeMessageReceived("slotchanged", "");
|
||||
}
|
||||
|
||||
void LoadSlot(int slot, Callback callback, void *cbUserData)
|
||||
|
@ -38,6 +38,9 @@ namespace SaveState
|
||||
// Checks whether there's an existing save in the specified slot.
|
||||
bool HasSaveInSlot(int slot);
|
||||
bool HasScreenshotInSlot(int slot);
|
||||
|
||||
int GetCurrentSlot();
|
||||
|
||||
// Returns -1 if there's no newest slot.
|
||||
int GetNewestSlot();
|
||||
|
||||
|
@ -16,9 +16,11 @@
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "android/app-android.h"
|
||||
#include "base/display.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/timeutil.h"
|
||||
|
||||
#include "gfx_es2/glsl_program.h"
|
||||
#include "gfx_es2/gl_state.h"
|
||||
@ -63,7 +65,7 @@
|
||||
#include "UI/InstallZipScreen.h"
|
||||
|
||||
EmuScreen::EmuScreen(const std::string &filename)
|
||||
: bootPending_(true), gamePath_(filename), invalid_(true), quit_(false), pauseTrigger_(false) {
|
||||
: bootPending_(true), gamePath_(filename), invalid_(true), quit_(false), pauseTrigger_(false), saveStatePreviewShownTime_(0.0), saveStatePreview_(nullptr) {
|
||||
memset(axisState_, 0, sizeof(axisState_));
|
||||
}
|
||||
|
||||
@ -242,6 +244,22 @@ void EmuScreen::sendMessage(const char *message, const char *value) {
|
||||
} else {
|
||||
gstate_c.skipDrawReason &= ~SKIPDRAW_WINDOW_MINIMIZED;
|
||||
}
|
||||
} else if (!strcmp(message, "slotchanged")) {
|
||||
if (saveStatePreview_) {
|
||||
int curSlot = SaveState::GetCurrentSlot();
|
||||
std::string fn;
|
||||
if (SaveState::HasSaveInSlot(curSlot)) {
|
||||
fn = SaveState::GenerateSaveSlotFilename(curSlot, "jpg");
|
||||
}
|
||||
|
||||
saveStatePreview_->SetFilename(fn);
|
||||
if (!fn.empty()) {
|
||||
saveStatePreview_->SetVisibility(UI::V_VISIBLE);
|
||||
saveStatePreviewShownTime_ = time_now_d();
|
||||
} else {
|
||||
saveStatePreview_->SetVisibility(UI::V_GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -561,13 +579,19 @@ void EmuScreen::processAxis(const AxisInput &axis, int direction) {
|
||||
}
|
||||
|
||||
void EmuScreen::CreateViews() {
|
||||
using namespace UI;
|
||||
const Bounds &bounds = screenManager()->getUIContext()->GetBounds();
|
||||
InitPadLayout(bounds.w, bounds.h);
|
||||
root_ = CreatePadLayout(bounds.w, bounds.h, &pauseTrigger_);
|
||||
if (g_Config.bShowDeveloperMenu) {
|
||||
root_->Add(new UI::Button("DevMenu"))->OnClick.Handle(this, &EmuScreen::OnDevTools);
|
||||
root_->Add(new Button("DevMenu"))->OnClick.Handle(this, &EmuScreen::OnDevTools);
|
||||
}
|
||||
root_->Add(new OnScreenMessagesView(new UI::AnchorLayoutParams((UI::Size)bounds.w, (UI::Size)bounds.h)));
|
||||
saveStatePreview_ = new AsyncImageFileView("", IS_FIXED, nullptr, new AnchorLayoutParams(bounds.centerX(), 100, NONE, NONE, true));
|
||||
saveStatePreview_->SetFixedSize(160, 90);
|
||||
saveStatePreview_->SetColor(0x90FFFFFF);
|
||||
saveStatePreview_->SetVisibility(V_GONE);
|
||||
root_->Add(saveStatePreview_);
|
||||
root_->Add(new OnScreenMessagesView(new AnchorLayoutParams((Size)bounds.w, (Size)bounds.h)));
|
||||
}
|
||||
|
||||
UI::EventReturn EmuScreen::OnDevTools(UI::EventParams ¶ms) {
|
||||
@ -668,6 +692,10 @@ void EmuScreen::update(InputState &input) {
|
||||
pauseTrigger_ = false;
|
||||
screenManager()->push(new GamePauseScreen(gamePath_));
|
||||
}
|
||||
|
||||
if (time_now_d() - saveStatePreviewShownTime_ > 2 && saveStatePreview_->GetVisibility() == UI::V_VISIBLE) {
|
||||
saveStatePreview_->SetVisibility(UI::V_GONE);
|
||||
}
|
||||
}
|
||||
|
||||
void EmuScreen::checkPowerDown() {
|
||||
|
@ -28,6 +28,8 @@
|
||||
|
||||
struct AxisInput;
|
||||
|
||||
class AsyncImageFileView;
|
||||
|
||||
class EmuScreen : public UIScreen {
|
||||
public:
|
||||
EmuScreen(const std::string &filename);
|
||||
@ -82,4 +84,7 @@ private:
|
||||
|
||||
// De-noise mapped axis updates
|
||||
int axisState_[JOYSTICK_AXIS_MAX];
|
||||
|
||||
double saveStatePreviewShownTime_;
|
||||
AsyncImageFileView *saveStatePreview_;
|
||||
};
|
||||
|
@ -343,8 +343,8 @@ void GameSettingsScreen::CreateViews() {
|
||||
|
||||
#ifdef _WIN32
|
||||
if (IsVistaOrHigher()) {
|
||||
static const char *backend[] = { "Auto", "DirectSound (compatible)", "WASAPI (fast)" };
|
||||
PopupMultiChoice *audioBackend = audioSettings->Add(new PopupMultiChoice(&g_Config.iAudioBackend, a->T("Audio backend", "Audio backend (change requires restart)"), backend, 0, ARRAY_SIZE(backend), a, screenManager()));
|
||||
static const char *backend[] = { "Auto", "DSound (compatible)", "WASAPI (fast)" };
|
||||
PopupMultiChoice *audioBackend = audioSettings->Add(new PopupMultiChoice(&g_Config.iAudioBackend, a->T("Audio backend", "Audio backend (restart req.)"), backend, 0, ARRAY_SIZE(backend), a, screenManager()));
|
||||
audioBackend->SetEnabledPtr(&g_Config.bEnableSound);
|
||||
}
|
||||
#endif
|
||||
@ -354,7 +354,7 @@ void GameSettingsScreen::CreateViews() {
|
||||
|
||||
lowAudio->SetEnabledPtr(&g_Config.bEnableSound);
|
||||
if (System_GetPropertyInt(SYSPROP_AUDIO_SAMPLE_RATE) == 44100) {
|
||||
CheckBox *resampling = audioSettings->Add(new CheckBox(&g_Config.bAudioResampler, a->T("Audio sync", "Audio sync (using resampling)")));
|
||||
CheckBox *resampling = audioSettings->Add(new CheckBox(&g_Config.bAudioResampler, a->T("Audio sync", "Audio sync (resampling)")));
|
||||
resampling->SetEnabledPtr(&g_Config.bEnableSound);
|
||||
}
|
||||
|
||||
|
@ -728,15 +728,6 @@ void NativeRender() {
|
||||
TakeScreenshot();
|
||||
}
|
||||
|
||||
if (resized) {
|
||||
resized = false;
|
||||
if (g_Config.iGPUBackend == GPU_BACKEND_DIRECT3D9) {
|
||||
#ifdef _WIN32
|
||||
D3D9_Resize(0);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
thin3d->SetScissorEnabled(false);
|
||||
if (g_Config.iGPUBackend == GPU_BACKEND_OPENGL) {
|
||||
glstate.depthWrite.set(GL_TRUE);
|
||||
@ -747,6 +738,15 @@ void NativeRender() {
|
||||
DX9::dxstate.colorMask.set(true, true, true, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (resized) {
|
||||
resized = false;
|
||||
if (g_Config.iGPUBackend == GPU_BACKEND_DIRECT3D9) {
|
||||
#ifdef _WIN32
|
||||
D3D9_Resize(0);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HandleGlobalMessage(const std::string &msg, const std::string &value) {
|
||||
|
@ -42,39 +42,16 @@
|
||||
#include "gfx_es2/draw_buffer.h"
|
||||
#include "ui/ui_context.h"
|
||||
|
||||
// TextureView takes a texture that is assumed to be alive during the lifetime
|
||||
// of the view. TODO: Actually make async using the task.
|
||||
class AsyncImageFileView : public UI::Clickable {
|
||||
public:
|
||||
AsyncImageFileView(const std::string &filename, UI::ImageSizeMode sizeMode, PrioritizedWorkQueue *wq, UI::LayoutParams *layoutParams = 0)
|
||||
: UI::Clickable(layoutParams), filename_(filename), color_(0xFFFFFFFF), sizeMode_(sizeMode), texture_(NULL), textureFailed_(false) {}
|
||||
~AsyncImageFileView() {
|
||||
delete texture_;
|
||||
}
|
||||
|
||||
void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
|
||||
void Draw(UIContext &dc) override;
|
||||
|
||||
void SetTexture(Thin3DTexture *texture) { texture_ = texture; }
|
||||
void SetColor(uint32_t color) { color_ = color; }
|
||||
|
||||
const std::string &GetFilename() const { return filename_; }
|
||||
|
||||
private:
|
||||
std::string filename_;
|
||||
uint32_t color_;
|
||||
UI::ImageSizeMode sizeMode_;
|
||||
|
||||
Thin3DTexture *texture_;
|
||||
bool textureFailed_;
|
||||
};
|
||||
|
||||
void AsyncImageFileView::GetContentDimensions(const UIContext &dc, float &w, float &h) const {
|
||||
// TODO: involve sizemode
|
||||
if (texture_) {
|
||||
float texw = (float)texture_->Width();
|
||||
float texh = (float)texture_->Height();
|
||||
switch (sizeMode_) {
|
||||
case UI::IS_FIXED:
|
||||
w = fixedSizeW_;
|
||||
h = fixedSizeH_;
|
||||
break;
|
||||
case UI::IS_DEFAULT:
|
||||
default:
|
||||
w = texw;
|
||||
@ -87,8 +64,19 @@ void AsyncImageFileView::GetContentDimensions(const UIContext &dc, float &w, flo
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncImageFileView::SetFilename(std::string filename) {
|
||||
if (filename_ != filename) {
|
||||
textureFailed_ = false;
|
||||
filename_ = filename;
|
||||
if (texture_) {
|
||||
texture_->Release();
|
||||
texture_ = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncImageFileView::Draw(UIContext &dc) {
|
||||
if (!texture_ && !textureFailed_) {
|
||||
if (!texture_ && !textureFailed_ && !filename_.empty()) {
|
||||
texture_ = dc.GetThin3DContext()->CreateTextureFromFile(filename_.c_str(), DETECT);
|
||||
if (!texture_)
|
||||
textureFailed_ = true;
|
||||
@ -99,18 +87,29 @@ void AsyncImageFileView::Draw(UIContext &dc) {
|
||||
dc.Flush();
|
||||
dc.GetThin3DContext()->SetTexture(0, texture_);
|
||||
dc.Draw()->Rect(bounds_.x, bounds_.y, bounds_.w, bounds_.h, color_);
|
||||
if (!text_.empty()) {
|
||||
dc.DrawText(text_.c_str(), bounds_.centerX(), bounds_.centerY(), 0xFFFFFFFF, ALIGN_CENTER | FLAG_DYNAMIC_ASCII);
|
||||
}
|
||||
dc.Flush();
|
||||
dc.RebindTexture();
|
||||
} else {
|
||||
// draw a dark gray rectangle to represent the texture.
|
||||
dc.FillRect(UI::Drawable(0x50202020), GetBounds());
|
||||
if (!filename_.empty()) {
|
||||
// draw a black rectangle to represent the missing screenshot.
|
||||
dc.FillRect(UI::Drawable(0xFF000000), GetBounds());
|
||||
} else {
|
||||
// draw a dark gray rectangle to represent no save state.
|
||||
dc.FillRect(UI::Drawable(0x50202020), GetBounds());
|
||||
}
|
||||
if (!text_.empty()) {
|
||||
dc.DrawText(text_.c_str(), bounds_.centerX(), bounds_.centerY(), 0xFFFFFFFF, ALIGN_CENTER | FLAG_DYNAMIC_ASCII);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ScreenshotViewScreen : public PopupScreen {
|
||||
public:
|
||||
ScreenshotViewScreen(std::string filename, std::string title, int slot, I18NCategory *i18n)
|
||||
: PopupScreen(title, i18n->T("Load State"), i18n->T("Back")), filename_(filename), slot_(slot) {}
|
||||
: PopupScreen(title, i18n->T("Load State"), "Back"), filename_(filename), slot_(slot) {} // PopupScreen will translate Back on its own
|
||||
|
||||
int GetSlot() const {
|
||||
return slot_;
|
||||
@ -137,22 +136,31 @@ private:
|
||||
class SaveSlotView : public UI::LinearLayout {
|
||||
public:
|
||||
SaveSlotView(int slot, UI::LayoutParams *layoutParams = nullptr) : UI::LinearLayout(UI::ORIENT_HORIZONTAL, layoutParams), slot_(slot) {
|
||||
using namespace UI;
|
||||
|
||||
screenshotFilename_ = SaveState::GenerateSaveSlotFilename(slot, "jpg");
|
||||
PrioritizedWorkQueue *wq = g_gameInfoCache.WorkQueue();
|
||||
Add(new UI::Spacer(10));
|
||||
Add(new UI::TextView(StringFromFormat("%i", slot_ + 1), 0, false, new UI::LinearLayoutParams(0.0, UI::G_CENTER)));
|
||||
AsyncImageFileView *fv = Add(new AsyncImageFileView(screenshotFilename_, UI::IS_DEFAULT, wq, new UI::LayoutParams(80 * 2, 45 * 2)));
|
||||
|
||||
AsyncImageFileView *fv = Add(new AsyncImageFileView(screenshotFilename_, IS_DEFAULT, wq, new UI::LayoutParams(82 * 2, 47 * 2)));
|
||||
fv->SetOverlayText(StringFromFormat("%i", slot_ + 1));
|
||||
|
||||
I18NCategory *i = GetI18NCategory("Pause");
|
||||
|
||||
saveStateButton_ = Add(new UI::Button(i->T("Save State"), new UI::LinearLayoutParams(0.0, UI::G_VCENTER)));
|
||||
LinearLayout *buttons = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT));
|
||||
buttons->SetSpacing(2.0);
|
||||
Add(buttons);
|
||||
|
||||
saveStateButton_ = buttons->Add(new Button(i->T("Save State"), new LinearLayoutParams(0.0, G_VCENTER)));
|
||||
saveStateButton_->OnClick.Handle(this, &SaveSlotView::OnSaveState);
|
||||
|
||||
if (SaveState::HasSaveInSlot(slot)) {
|
||||
loadStateButton_ = Add(new UI::Button(i->T("Load State"), new UI::LinearLayoutParams(0.0, UI::G_VCENTER)));
|
||||
loadStateButton_ = buttons->Add(new Button(i->T("Load State"), new LinearLayoutParams(0.0, G_VCENTER)));
|
||||
loadStateButton_->OnClick.Handle(this, &SaveSlotView::OnLoadState);
|
||||
|
||||
fv->OnClick.Handle(this, &SaveSlotView::OnScreenshotClick);
|
||||
} else {
|
||||
fv->SetFilename("");
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,7 +170,7 @@ public:
|
||||
|
||||
void Draw(UIContext &dc) {
|
||||
if (g_Config.iCurrentStateSlot == slot_) {
|
||||
dc.FillRect(UI::Drawable(0x40FFFFFF), GetBounds());
|
||||
dc.FillRect(UI::Drawable(0x70FFFFFF), GetBounds().Expand(3));
|
||||
}
|
||||
UI::LinearLayout::Draw(dc);
|
||||
}
|
||||
@ -245,11 +253,9 @@ void GamePauseScreen::CreateViews() {
|
||||
|
||||
root_ = new LinearLayout(ORIENT_HORIZONTAL);
|
||||
|
||||
ViewGroup *leftColumn = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(520, FILL_PARENT, actionMenuMargins));
|
||||
ViewGroup *leftColumn = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0, actionMenuMargins));
|
||||
root_->Add(leftColumn);
|
||||
|
||||
root_->Add(new Spacer(new LinearLayoutParams(1.0)));
|
||||
|
||||
ViewGroup *leftColumnItems = new LinearLayout(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, WRAP_CONTENT));
|
||||
leftColumn->Add(leftColumnItems);
|
||||
|
||||
|
@ -59,3 +59,38 @@ private:
|
||||
// hack
|
||||
bool finishNextFrame_;
|
||||
};
|
||||
|
||||
class PrioritizedWorkQueue;
|
||||
|
||||
// TextureView takes a texture that is assumed to be alive during the lifetime
|
||||
// of the view. TODO: Actually make async using the task.
|
||||
class AsyncImageFileView : public UI::Clickable {
|
||||
public:
|
||||
AsyncImageFileView(const std::string &filename, UI::ImageSizeMode sizeMode, PrioritizedWorkQueue *wq, UI::LayoutParams *layoutParams = 0)
|
||||
: UI::Clickable(layoutParams), filename_(filename), color_(0xFFFFFFFF), sizeMode_(sizeMode), texture_(NULL), textureFailed_(false), fixedSizeW_(0.0f), fixedSizeH_(0.0f) {}
|
||||
~AsyncImageFileView() {
|
||||
delete texture_;
|
||||
}
|
||||
|
||||
void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
|
||||
void Draw(UIContext &dc) override;
|
||||
|
||||
void SetFilename(std::string filename);
|
||||
void SetTexture(Thin3DTexture *texture) { texture_ = texture; }
|
||||
void SetColor(uint32_t color) { color_ = color; }
|
||||
void SetOverlayText(std::string text) { text_ = text; }
|
||||
void SetFixedSize(float fixW, float fixH) { fixedSizeW_ = fixW; fixedSizeH_ = fixH; }
|
||||
|
||||
const std::string &GetFilename() const { return filename_; }
|
||||
|
||||
private:
|
||||
std::string filename_;
|
||||
std::string text_;
|
||||
uint32_t color_;
|
||||
UI::ImageSizeMode sizeMode_;
|
||||
|
||||
Thin3DTexture *texture_;
|
||||
bool textureFailed_;
|
||||
float fixedSizeW_;
|
||||
float fixedSizeH_;
|
||||
};
|
||||
|
@ -210,6 +210,7 @@ void D3D9_Resize(HWND window) {
|
||||
HRESULT hr = device->Reset(&pp);
|
||||
if (FAILED(hr)) {
|
||||
ERROR_LOG_REPORT(G3D, "Unable to reset device: %s", DXGetErrorStringA(hr));
|
||||
PanicAlert("Unable to reset D3D9 device: %s", DXGetErrorStringA(hr));
|
||||
}
|
||||
DX9::fbo_init(d3d);
|
||||
}
|
||||
|
@ -72,6 +72,7 @@ void GL_SwapBuffers() { }
|
||||
void NativeUpdate(InputState &input_state) { }
|
||||
void NativeRender() { }
|
||||
void NativeResized() { }
|
||||
void NativeMessageReceived(const char *message, const char *value) {}
|
||||
|
||||
std::string System_GetProperty(SystemProperty prop) { return ""; }
|
||||
int System_GetPropertyInt(SystemProperty prop) { return -1; }
|
||||
|
2
native
2
native
@ -1 +1 @@
|
||||
Subproject commit a72858d15e3678051787738076912b11e33982b9
|
||||
Subproject commit 5becb9a4be8f6e18b102de4eafe902284e9faded
|
@ -46,6 +46,7 @@
|
||||
|
||||
std::string System_GetProperty(SystemProperty prop) { return ""; }
|
||||
int System_GetPropertyInt(SystemProperty prop) { return -1; }
|
||||
void NativeMessageReceived(const char *message, const char *value) {}
|
||||
|
||||
#define M_PI_2 1.57079632679489661923
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user