diff --git a/CMakeLists.txt b/CMakeLists.txt index 797d34039c..7e99e5664b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -861,6 +861,8 @@ set(NativeAppSource UI/NativeApp.cpp UI/BackgroundAudio.cpp UI/DevScreens.cpp + UI/DisplayLayoutEditor.cpp + UI/DisplayLayoutScreen.cpp UI/EmuScreen.cpp UI/GameInfoCache.cpp UI/MainScreen.cpp diff --git a/Core/Config.cpp b/Core/Config.cpp index 60e4ebb1da..ccc791ba73 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -436,14 +436,17 @@ static ConfigSetting graphicsSettings[] = { ReportedConfigSetting("TextureSecondaryCache", &g_Config.bTextureSecondaryCache, false, true, true), ReportedConfigSetting("VertexDecJit", &g_Config.bVertexDecoderJit, &DefaultJit, false), -#ifdef _WIN32 +#ifndef MOBILE_DEVICE ConfigSetting("FullScreen", &g_Config.bFullScreen, false), #endif // TODO: Replace these settings with a list of options ConfigSetting("PartialStretch", &g_Config.bPartialStretch, &DefaultPartialStretch, true, true), ConfigSetting("StretchToDisplay", &g_Config.bStretchToDisplay, false, true, true), - ConfigSetting("SmallDisplay", &g_Config.bSmallDisplay, false, true, true), + ConfigSetting("SmallDisplayZoom", &g_Config.iSmallDisplayZoom, 0, true, true), + ConfigSetting("SmallDisplayOffsetX", &g_Config.fSmallDisplayOffsetX, 0.5f, true, true), + ConfigSetting("SmallDisplayOffsetY", &g_Config.fSmallDisplayOffsetY, 0.5f, true, true), + ConfigSetting("SmallDisplayCustomZoom", &g_Config.fSmallDisplayCustomZoom, 8.0f, true, true), ConfigSetting("ImmersiveMode", &g_Config.bImmersiveMode, false, true, true), ReportedConfigSetting("TrueColor", &g_Config.bTrueColor, true, true, true), diff --git a/Core/Config.h b/Core/Config.h index 677a80fef1..5e81e41217 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -144,7 +144,10 @@ public: int iBufFilter; // 1 = linear, 2 = nearest bool bPartialStretch; bool bStretchToDisplay; - bool bSmallDisplay; // Useful on large tablets with touch controls to not overlap the image. Temporary setting - will be replaced by more comprehensive display size settings. + int iSmallDisplayZoom; // Used to fit display into screen 0 = auto, anything higher is used to set's integer zoom of psp resolution and allows manual editing + float fSmallDisplayOffsetX; // Along with Y it goes from 0.0 to 1.0, XY (0.5, 0.5) = center of the screen + float fSmallDisplayOffsetY; + float fSmallDisplayCustomZoom; //This is actually used for zoom, both in and out. bool bImmersiveMode; // Mode on Android Kitkat 4.4 that hides the back button etc. bool bVSync; int iFrameSkip; diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 60704f07ac..f7cb00e122 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -30,7 +30,7 @@ #include "GPU/GPUState.h" #include "UI/OnScreenDisplay.h" // Gross dependency! -void CenterRect(float *x, float *y, float *w, float *h, float origW, float origH, float frameW, float frameH, int rotation) { +void CenterDisplayOutputRect(float *x, float *y, float *w, float *h, float origW, float origH, float frameW, float frameH, int rotation, bool invertY) { float outW; float outH; @@ -40,16 +40,50 @@ void CenterRect(float *x, float *y, float *w, float *h, float origW, float origH outW = frameW; outH = frameH; } else { - // Add special case for 1080p displays, cutting off the bottom and top 1-pixel rows from the original 480x272. - // This will be what 99.9% of users want. - if (origW == 480 && origH == 272 && frameW == 1920 && frameH == 1080 && !rotated) { - *x = 0; - *y = -4; - *w = 1920; - *h = 1088; - return; + bool fullScreenZoom = true; +#ifndef MOBILE_DEVICE + fullScreenZoom = g_Config.bFullScreen; +#endif + if (fullScreenZoom) { + if (g_Config.iSmallDisplayZoom != 0) { + float offsetX = (g_Config.fSmallDisplayOffsetX - 0.5f) * 2.0f * frameW; + float offsetY = (g_Config.fSmallDisplayOffsetY - 0.5f) * 2.0f * frameH; + // Have to invert Y coordinates for software rendering as well as non buffered mode for GL +#if defined(USING_WIN_UI) + if (g_Config.bSoftwareRendering || (g_Config.iRenderingMode == FB_NON_BUFFERED_MODE && g_Config.iGPUBackend == GPU_BACKEND_OPENGL && !invertY)) { offsetY = offsetY * -1.0f; } +#else + if (g_Config.bSoftwareRendering || (g_Config.iRenderingMode == FB_NON_BUFFERED_MODE && !invertY)) { offsetY = offsetY * -1.0f; } +#endif + float customZoom = g_Config.fSmallDisplayCustomZoom / 8.0f; + float smallDisplayW = origW * customZoom; + float smallDisplayH = origH * customZoom; + if (!rotated) { + *x = ((frameW - smallDisplayW) / 2.0f) + offsetX; + *y = ((frameH - smallDisplayH) / 2.0f) + offsetY; + *w = smallDisplayW; + *h = smallDisplayH; + return; + } else { + *x = ((frameW - smallDisplayH) / 2.0f) + offsetX; + *y = ((frameH - smallDisplayW) / 2.0f) + offsetY; + *w = smallDisplayH; + *h = smallDisplayW; + return; + } + } else { + float pixelCrop = frameH / 270.0f; + float resCommonWidescreen = pixelCrop - floor(pixelCrop); + if (!rotated && resCommonWidescreen == 0.0f) { + *x = 0; + *y = -pixelCrop; + *w = frameW; + *h = pixelCrop * 272.0f; + return; + } + } } + float origRatio = !rotated ? origW / origH : origH / origW; float frameRatio = frameW / frameH; @@ -67,11 +101,6 @@ void CenterRect(float *x, float *y, float *w, float *h, float origW, float origH } } - if (g_Config.bSmallDisplay) { - outW /= 2.0f; - outH /= 2.0f; - } - *x = (frameW - outW) / 2.0f; *y = (frameH - outH) / 2.0f; *w = outW; diff --git a/GPU/Common/FramebufferCommon.h b/GPU/Common/FramebufferCommon.h index 51f386f292..4017ecb3fd 100644 --- a/GPU/Common/FramebufferCommon.h +++ b/GPU/Common/FramebufferCommon.h @@ -284,4 +284,4 @@ protected: }; }; -void CenterRect(float *x, float *y, float *w, float *h, float origW, float origH, float frameW, float frameH, int rotation); +void CenterDisplayOutputRect(float *x, float *y, float *w, float *h, float origW, float origH, float frameW, float frameH, int rotation, bool invertY); diff --git a/GPU/Directx9/FramebufferDX9.cpp b/GPU/Directx9/FramebufferDX9.cpp index cc5ac2b558..e0d4d51f2c 100644 --- a/GPU/Directx9/FramebufferDX9.cpp +++ b/GPU/Directx9/FramebufferDX9.cpp @@ -203,7 +203,7 @@ namespace DX9 { // (it always runs at output resolution so FXAA may look odd). float x, y, w, h; int uvRotation = (g_Config.iRenderingMode != FB_NON_BUFFERED_MODE) ? g_Config.iInternalScreenRotation : ROTATION_LOCKED_HORIZONTAL; - CenterRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight, uvRotation); + CenterDisplayOutputRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight, uvRotation, false); DrawActiveTexture(drawPixelsTex_, x, y, w, h, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight, false, 0.0f, 0.0f, 480.0f / 512.0f, uvRotation); } @@ -764,7 +764,7 @@ namespace DX9 { // Output coordinates float x, y, w, h; int uvRotation = (g_Config.iRenderingMode != FB_NON_BUFFERED_MODE) ? g_Config.iInternalScreenRotation : ROTATION_LOCKED_HORIZONTAL; - CenterRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight, uvRotation); + CenterDisplayOutputRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight, uvRotation, false); const float u0 = offsetX / (float)vfb->bufferWidth; const float v0 = offsetY / (float)vfb->bufferHeight; diff --git a/GPU/Directx9/StateMappingDX9.cpp b/GPU/Directx9/StateMappingDX9.cpp index 9ab320b77a..5562961adc 100644 --- a/GPU/Directx9/StateMappingDX9.cpp +++ b/GPU/Directx9/StateMappingDX9.cpp @@ -649,7 +649,7 @@ void TransformDrawEngineDX9::ApplyDrawState(int prim) { } else { float pixelW = PSP_CoreParameter().pixelWidth; float pixelH = PSP_CoreParameter().pixelHeight; - CenterRect(&renderX, &renderY, &renderWidth, &renderHeight, 480, 272, pixelW, pixelH, ROTATION_LOCKED_HORIZONTAL); + CenterDisplayOutputRect(&renderX, &renderY, &renderWidth, &renderHeight, 480, 272, pixelW, pixelH, ROTATION_LOCKED_HORIZONTAL, false); renderWidthFactor = renderWidth / 480.0f; renderHeightFactor = renderHeight / 272.0f; } diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index c4e86d89b5..e4b21dfa4c 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -230,7 +230,7 @@ void FramebufferManager::UpdatePostShaderUniforms(int renderWidth, int renderHei float v_pixel_delta = v_delta; if (postShaderAtOutputResolution_) { float x, y, w, h; - CenterRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)pixelWidth_, (float)pixelHeight_, ROTATION_LOCKED_HORIZONTAL); + CenterDisplayOutputRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)pixelWidth_, (float)pixelHeight_, ROTATION_LOCKED_HORIZONTAL, false); u_pixel_delta = 1.0f / w; v_pixel_delta = 1.0f / h; } @@ -395,7 +395,7 @@ void FramebufferManager::DrawPixels(VirtualFramebuffer *vfb, int dstX, int dstY, glViewport(0, 0, vfb->renderWidth, vfb->renderHeight); } else { float x, y, w, h; - CenterRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)pixelWidth_, (float)pixelHeight_, false); + CenterDisplayOutputRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)pixelWidth_, (float)pixelHeight_, false, false); glViewport(x, y, w, h); } MakePixelTexture(srcPixels, srcPixelFormat, srcStride, width, height); @@ -418,7 +418,7 @@ void FramebufferManager::DrawFramebuffer(const u8 *srcPixels, GEBufferFormat src // (it always runs at output resolution so FXAA may look odd). float x, y, w, h; int uvRotation = (g_Config.iRenderingMode != FB_NON_BUFFERED_MODE) ? g_Config.iInternalScreenRotation : ROTATION_LOCKED_HORIZONTAL; - CenterRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)pixelWidth_, (float)pixelHeight_, uvRotation); + CenterDisplayOutputRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)pixelWidth_, (float)pixelHeight_, uvRotation, true); if (applyPostShader) { glsl_bind(postShaderProgram_); UpdatePostShaderUniforms(renderWidth_, renderHeight_); @@ -1049,7 +1049,7 @@ void FramebufferManager::CopyDisplayToOutput() { // Output coordinates float x, y, w, h; - CenterRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)pixelWidth_, (float)pixelHeight_, uvRotation); + CenterDisplayOutputRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)pixelWidth_, (float)pixelHeight_, uvRotation, false); // TODO ES3: Use glInvalidateFramebuffer to discard depth/stencil data at the end of frame. diff --git a/GPU/GLES/StateMapping.cpp b/GPU/GLES/StateMapping.cpp index 47da0bc632..399663346c 100644 --- a/GPU/GLES/StateMapping.cpp +++ b/GPU/GLES/StateMapping.cpp @@ -740,7 +740,7 @@ void TransformDrawEngine::ApplyDrawState(int prim) { } else { float pixelW = PSP_CoreParameter().pixelWidth; float pixelH = PSP_CoreParameter().pixelHeight; - CenterRect(&displayOffsetX, &displayOffsetY, &renderWidth, &renderHeight, 480, 272, pixelW, pixelH, ROTATION_LOCKED_HORIZONTAL); + CenterDisplayOutputRect(&displayOffsetX, &displayOffsetY, &renderWidth, &renderHeight, 480, 272, pixelW, pixelH, ROTATION_LOCKED_HORIZONTAL, false); renderWidthFactor = renderWidth / 480.0f; renderHeightFactor = renderHeight / 272.0f; } diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index c60bd16018..34bb0a50cf 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -34,6 +34,7 @@ #include "GPU/Software/SoftGpu.h" #include "GPU/Software/TransformUnit.h" #include "GPU/Software/Rasterizer.h" +#include "GPU/Common/FramebufferCommon.h" static GLuint temp_texture = 0; @@ -48,9 +49,6 @@ FormatBuffer fb; FormatBuffer depthbuf; u32 clut[4096]; -// TODO: This one lives in GPU/GLES/Framebuffer.cpp, move it to somewhere common. -void CenterRect(float *x, float *y, float *w, float *h, - float origW, float origH, float frameW, float frameH, int rotation); GLuint OpenGL_CompileProgram(const char* vertexShader, const char* fragmentShader) { @@ -247,7 +245,7 @@ void SoftGPU::CopyToCurrentFboFromDisplayRam(int srcwidth, int srcheight) glUseProgram(program); float x, y, w, h; - CenterRect(&x, &y, &w, &h, 480.0f, 272.0f, dstwidth, dstheight, ROTATION_LOCKED_HORIZONTAL); + CenterDisplayOutputRect(&x, &y, &w, &h, 480.0f, 272.0f, dstwidth, dstheight, ROTATION_LOCKED_HORIZONTAL, false); x /= 0.5f * dstwidth; y /= 0.5f * dstheight; diff --git a/UI/DisplayLayoutEditor.cpp b/UI/DisplayLayoutEditor.cpp new file mode 100644 index 0000000000..fba521d051 --- /dev/null +++ b/UI/DisplayLayoutEditor.cpp @@ -0,0 +1,54 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "DisplayLayoutEditor.h" +#include "ui/ui_context.h" +#include "ui_atlas.h" + +#include + +void MultiTouchDisplay::GetContentDimensions(const UIContext &dc, float &w, float &h) const { + const AtlasImage &image = dc.Draw()->GetAtlas()->images[img_]; + w = image.w * scale_; + h = image.h * scale_; +} + +void MultiTouchDisplay::Touch(const TouchInput &input) { + if ((input.flags & TOUCH_DOWN) && bounds_.Contains(input.x, input.y)) { + pointerDownMask_ |= 1 << input.id; + } + if (input.flags & TOUCH_MOVE) { + if (bounds_.Contains(input.x, input.y)) + pointerDownMask_ |= 1 << input.id; + else + pointerDownMask_ &= ~(1 << input.id); + } + if (input.flags & TOUCH_UP) { + pointerDownMask_ &= ~(1 << input.id); + } + if (input.flags & TOUCH_RELEASE_ALL) { + pointerDownMask_ = 0; + } +} + + +void MultiTouchDisplay::Draw(UIContext &dc) { + float opacity = 0.5f; + float scale = scale_; + uint32_t color = colorAlpha(0xFFFFFF, opacity); + dc.Draw()->DrawImageRotated(img_, bounds_.centerX(), bounds_.centerY(), scale, angle_ * (M_PI * 2 / 360.0f), color, flipImageH_); +} diff --git a/UI/DisplayLayoutEditor.h b/UI/DisplayLayoutEditor.h new file mode 100644 index 0000000000..a6cfefb1cf --- /dev/null +++ b/UI/DisplayLayoutEditor.h @@ -0,0 +1,51 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once +#include "gfx_es2/draw_buffer.h" +#include "ui/view.h" +#include "ui/viewgroup.h" + +class MultiTouchDisplay : public UI::View { +public: + MultiTouchDisplay(int img, float scale, UI::LayoutParams *layoutParams) + : UI::View(layoutParams), pointerDownMask_(0), scale_(scale), img_(img), angle_(0.0f), flipImageH_(false) { + } + virtual void Touch(const TouchInput &input) override; + virtual void Draw(UIContext &dc) override; + virtual void GetContentDimensions(const UIContext &dc, float &w, float &h) const override; + // chainable + MultiTouchDisplay *FlipImageH(bool flip) { flipImageH_ = flip; return this; } + MultiTouchDisplay *SetAngle(float angle) { angle_ = angle; return this; } + +protected: + uint32_t pointerDownMask_; + float scale_; + +private: + int img_; + float angle_; + bool flipImageH_; +}; + +class PSPDisplay : public MultiTouchDisplay { +public: + PSPDisplay(int img, float scale, UI::LayoutParams *layoutParams) + : MultiTouchDisplay(img, scale, layoutParams) { + } +}; + diff --git a/UI/DisplayLayoutScreen.cpp b/UI/DisplayLayoutScreen.cpp new file mode 100644 index 0000000000..ee2ac03a5e --- /dev/null +++ b/UI/DisplayLayoutScreen.cpp @@ -0,0 +1,227 @@ +// Copyright (c) 2013- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include + +#include "base/colorutil.h" +#include "gfx_es2/draw_buffer.h" +#include "i18n/i18n.h" +#include "ui/ui_context.h" +#include "ui_atlas.h" + +#include "DisplayLayoutScreen.h" +#include "Core/Config.h" +#include "Core/System.h" +#include "DisplayLayoutEditor.h" + +static const int leftColumnWidth = 200; + +// Ugly hackery, need to rework some stuff to get around this +static float local_dp_xres; +static float local_dp_yres; + +class DragDropDisplay : public MultiTouchDisplay { +public: + DragDropDisplay(float &x, float &y, int img, float &scale) + : MultiTouchDisplay(img, scale, new UI::AnchorLayoutParams(x*local_dp_xres, y*local_dp_yres, UI::NONE, UI::NONE, true)), + x_(x), y_(y), theScale_(scale) { + scale_ = theScale_; + } + + virtual void SaveDisplayPosition() { + x_ = bounds_.centerX() / local_dp_xres; + y_ = bounds_.centerY() / local_dp_yres; + scale_ = theScale_; + } + + virtual float GetScale() const { return theScale_; } + virtual void SetScale(float s) { theScale_ = s; scale_ = s; } + + private: + + float &x_, &y_; + float &theScale_; +}; + +DisplayLayoutScreen::DisplayLayoutScreen() { + picked_ = 0; +}; + + +bool DisplayLayoutScreen::touch(const TouchInput &touch) { + UIScreen::touch(touch); + + using namespace UI; + + int mode = mode_->GetSelection(); + if (g_Config.iSmallDisplayZoom == 0) { mode = -1; } + + const Bounds &screen_bounds = screenManager()->getUIContext()->GetBounds(); + + if ((touch.flags & TOUCH_MOVE) && picked_ != 0) { + if (mode == 0) { + const Bounds &bounds = picked_->GetBounds(); + + int mintouchX = screen_bounds.w / 4; + int maxTouchX = screen_bounds.w - screen_bounds.w / 4; + + int minTouchY = screen_bounds.h / 4; + int maxTouchY = screen_bounds.h - screen_bounds.h / 4; + + int newX = bounds.centerX(), newY = bounds.centerY(); + // we have to handle x and y separately since even if x is blocked, y may not be. + if (touch.x > mintouchX && touch.x < maxTouchX) { + // if the leftmost point of the control is ahead of the margin, + // move it. Otherwise, don't. + newX = touch.x; + } + if (touch.y > minTouchY && touch.y < maxTouchY) { + newY = touch.y; + } + picked_->ReplaceLayoutParams(new UI::AnchorLayoutParams(newX, newY, NONE, NONE, true)); + } + else if (mode == 1) { + // Resize. Vertical = scaling, horizontal = spacing; + // Up should be bigger so let's negate in that direction + float diffX = (touch.x - startX_); + float diffY = -(touch.y - startY_); + + float movementScale = 0.5f; + float newScale = startScale_ + diffY * movementScale; + if (newScale > 100.0f) newScale = 100.0f; + if (newScale < 1.0f) newScale = 1.0f; + picked_->SetScale(newScale); + } + } + if ((touch.flags & TOUCH_DOWN) && picked_ == 0) { + picked_ = displayRepresentation_; + if (picked_) { + startX_ = touch.x; + startY_ = touch.y; + startScale_ = picked_->GetScale(); + } + } + if ((touch.flags & TOUCH_UP) && picked_ != 0) { + const Bounds &bounds = picked_->GetBounds(); + float saveX_ = touch.x; + float saveY_ = touch.y; + startScale_ = picked_->GetScale(); + picked_->SaveDisplayPosition(); + picked_ = 0; + } + return true; +}; + +void DisplayLayoutScreen::onFinish(DialogResult reason) { + g_Config.Save(); +} + +UI::EventReturn DisplayLayoutScreen::OnCenter(UI::EventParams &e) { + g_Config.fSmallDisplayOffsetX = 0.5f; + g_Config.fSmallDisplayOffsetY = 0.5f; + RecreateViews(); + return UI::EVENT_DONE; +}; + +UI::EventReturn DisplayLayoutScreen::OnZoomChange(UI::EventParams &e) { + if (g_Config.iSmallDisplayZoom > 0) { + g_Config.fSmallDisplayCustomZoom = (float)(g_Config.iSmallDisplayZoom * 8); + } else { + const Bounds &bounds = screenManager()->getUIContext()->GetBounds(); + float autoBound = bounds.w / 480.0f * 8.0f; + g_Config.fSmallDisplayCustomZoom = autoBound; + g_Config.fSmallDisplayOffsetX = 0.5f; + g_Config.fSmallDisplayOffsetY = 0.5f; + } + RecreateViews(); + return UI::EVENT_DONE; +}; + + +void DisplayLayoutScreen::dialogFinished(const Screen *dialog, DialogResult result) { + RecreateViews(); +} + +void DisplayLayoutScreen::CreateViews() { + if (g_Config.bStretchToDisplay) { + // Shouldn't even be able to get here as the way into this dialog should be closed. + return; + } + const Bounds &bounds = screenManager()->getUIContext()->GetBounds(); + + local_dp_xres = bounds.w; + local_dp_yres = bounds.h; + + using namespace UI; + + I18NCategory *gr = GetI18NCategory("Graphics"); + + root_ = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT)); + + // Just visual boundaries of the screen, should be easier to use than imagination + float verticalBoundaryPositionL = local_dp_xres / 4.0f; + float verticalBoundaryPositionR = local_dp_xres - verticalBoundaryPositionL; + float horizontalBoundaryPositionL = local_dp_yres / 4.0f; + float horizontalBoundaryPositionR = local_dp_yres - horizontalBoundaryPositionL; + TabHolder *verticalBoundaryL = new TabHolder(ORIENT_VERTICAL, verticalBoundaryPositionL, new AnchorLayoutParams(0, 0, 0, 0, false)); + TabHolder *verticalBoundaryR = new TabHolder(ORIENT_VERTICAL, verticalBoundaryPositionR, new AnchorLayoutParams(0, 0, 0, 0, false)); + TabHolder *horizontalBoundaryL = new TabHolder(ORIENT_VERTICAL, verticalBoundaryPositionL * 2.0f, new AnchorLayoutParams(verticalBoundaryPositionL * 2.0f, horizontalBoundaryPositionL - 31.0f, 0, 0, true)); + TabHolder *horizontalBoundaryR = new TabHolder(ORIENT_VERTICAL, verticalBoundaryPositionL * 2.0f, new AnchorLayoutParams(verticalBoundaryPositionL * 2.0f, horizontalBoundaryPositionR + 31.0f, 0, 0, true)); + AnchorLayout *topBoundary = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT)); + AnchorLayout *bottomBoundary = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT)); + root_->Add(verticalBoundaryL); + root_->Add(verticalBoundaryR); + root_->Add(horizontalBoundaryL); + root_->Add(horizontalBoundaryR); + horizontalBoundaryL->AddTab("", topBoundary); + horizontalBoundaryR->AddTab("", bottomBoundary); + + Choice *back = new Choice(gr->T("Back"), "", false, new AnchorLayoutParams(leftColumnWidth, WRAP_CONTENT, 10, NONE, NONE, 10)); + static const char *zoomLevels[] = { "Auto", "1x", "2x", "3x", "4x", "5x", "6x", "7x", "8x", "9x", "10x" }; + zoom_ = new PopupMultiChoice(&g_Config.iSmallDisplayZoom, gr->T("Zoom settings"), zoomLevels, 0, ARRAY_SIZE(zoomLevels), gr->GetName(), screenManager(), new AnchorLayoutParams(300, WRAP_CONTENT, verticalBoundaryPositionL * 2 - 150, NONE, NONE, 10)); + zoom_->OnChoice.Handle(this, &DisplayLayoutScreen::OnZoomChange); + + + + mode_ = new ChoiceStrip(ORIENT_VERTICAL, new AnchorLayoutParams(leftColumnWidth, WRAP_CONTENT, 10, NONE, NONE, 158 + 64 + 10)); + if (g_Config.iSmallDisplayZoom == 0) { + mode_->AddChoice(gr->T("Activated(Auto)")); + float autoBound = bounds.w / 480.0f * 8.0f; + g_Config.fSmallDisplayCustomZoom = autoBound; + g_Config.fSmallDisplayOffsetX = 0.5f; + g_Config.fSmallDisplayOffsetY = 0.5f; + } else { + Choice *center = new Choice(gr->T("Center"), "", false, new AnchorLayoutParams(leftColumnWidth, WRAP_CONTENT, 10, NONE, NONE, 84)); + center->OnClick.Handle(this, &DisplayLayoutScreen::OnCenter); + root_->Add(center); + mode_->AddChoice(gr->T("Move")); + mode_->AddChoice(gr->T("Resize")); + mode_->SetSelection(0); + } + + + back->OnClick.Handle(this, &UIScreen::OnBack); + root_->Add(mode_); + root_->Add(zoom_); + root_->Add(back); + + displayRepresentation_ = new DragDropDisplay(g_Config.fSmallDisplayOffsetX, g_Config.fSmallDisplayOffsetY, I_PSP_DISPLAY, g_Config.fSmallDisplayCustomZoom); + if (g_Config.iInternalScreenRotation == 2 || g_Config.iInternalScreenRotation == 4) { + displayRepresentation_->SetAngle(90.0f); + } + root_->Add(displayRepresentation_); +} diff --git a/UI/DisplayLayoutScreen.h b/UI/DisplayLayoutScreen.h new file mode 100644 index 0000000000..6aa8761f69 --- /dev/null +++ b/UI/DisplayLayoutScreen.h @@ -0,0 +1,50 @@ +// Copyright (c) 2013- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include "ui/view.h" +#include "ui/viewgroup.h" +#include "MiscScreens.h" +#include + +class DragDropDisplay; + +class DisplayLayoutScreen : public UIDialogScreenWithBackground { +public: + DisplayLayoutScreen(); + + virtual void CreateViews() override; + virtual bool touch(const TouchInput &touch) override; + virtual void dialogFinished(const Screen *dialog, DialogResult result) override; + virtual void onFinish(DialogResult reason) override; + +protected: + virtual UI::EventReturn OnCenter(UI::EventParams &e); + virtual UI::EventReturn OnZoomChange(UI::EventParams &e); + +private: + DragDropDisplay *picked_; + DragDropDisplay *displayRepresentation_; + UI::ChoiceStrip *mode_; + UI::PopupMultiChoice *zoom_; + // Touch down state for drag to resize etc + float startX_; + float startY_; + float startScale_; + +}; diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 75e17a9f5b..ddcf864cb8 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -33,6 +33,7 @@ #include "UI/MiscScreens.h" #include "UI/ControlMappingScreen.h" #include "UI/DevScreens.h" +#include "UI/DisplayLayoutScreen.h" #include "UI/SavedataScreen.h" #include "UI/TouchControlLayoutScreen.h" #include "UI/TouchControlVisibilityScreen.h" @@ -163,8 +164,12 @@ void GameSettingsScreen::CreateViews() { graphicsSettings->Add(new CheckBox(&g_Config.bFullScreen, gr->T("FullScreen")))->OnClick.Handle(this, &GameSettingsScreen::OnFullscreenChange); #endif graphicsSettings->Add(new CheckBox(&g_Config.bStretchToDisplay, gr->T("Stretch to Display"))); - // Small Display: To avoid overlapping touch controls on large tablets. Better control over this will be coming later. - graphicsSettings->Add(new CheckBox(&g_Config.bSmallDisplay, gr->T("Small Display"))); + + // Display Layout Editor: To avoid overlapping touch controls on large tablets, meet geeky demands for integer zoom/unstretched image etc. + displayEditor_ = graphicsSettings->Add(new Choice(gr->T("Display layout editor"))); + displayEditor_->OnClick.Handle(this, &GameSettingsScreen::OnDisplayLayoutEditor); + displayEditor_->SetDisabledPtr(&g_Config.bStretchToDisplay); + if (pixel_xres < pixel_yres * 1.3) // Smaller than 4:3 graphicsSettings->Add(new CheckBox(&g_Config.bPartialStretch, gr->T("Partial Vertical Stretch"))); @@ -774,6 +779,11 @@ UI::EventReturn GameSettingsScreen::OnFullscreenChange(UI::EventParams &e) { return UI::EVENT_DONE; } +UI::EventReturn GameSettingsScreen::OnDisplayLayoutEditor(UI::EventParams &e) { + screenManager()->push(new DisplayLayoutScreen()); //TouchControlLayoutScreen());// + return UI::EVENT_DONE; +}; + UI::EventReturn GameSettingsScreen::OnResolutionChange(UI::EventParams &e) { if (gpu) { gpu->Resized(); diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index 14afd16e0e..49d1c624f5 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -49,6 +49,7 @@ private: UI::CheckBox *enableReportsCheckbox_; UI::Choice *layoutEditorChoice_; UI::Choice *postProcChoice_; + UI::Choice *displayEditor_; UI::PopupMultiChoice *resolutionChoice_; UI::CheckBox *frameSkipAuto_; #ifdef _WIN32 @@ -80,6 +81,7 @@ private: UI::EventReturn OnChangeMacAddress(UI::EventParams &e); UI::EventReturn OnClearRecents(UI::EventParams &e); UI::EventReturn OnFullscreenChange(UI::EventParams &e); + UI::EventReturn OnDisplayLayoutEditor(UI::EventParams &e); UI::EventReturn OnResolutionChange(UI::EventParams &e); UI::EventReturn OnHwScaleChange(UI::EventParams &e); UI::EventReturn OnShaderChange(UI::EventParams &e); diff --git a/UI/UI.vcxproj b/UI/UI.vcxproj index 3e3ba97c97..27c6e27f72 100644 --- a/UI/UI.vcxproj +++ b/UI/UI.vcxproj @@ -24,6 +24,8 @@ + + @@ -50,6 +52,8 @@ + + diff --git a/UI/UI.vcxproj.filters b/UI/UI.vcxproj.filters index fd94f1ff83..72f02e5b73 100644 --- a/UI/UI.vcxproj.filters +++ b/UI/UI.vcxproj.filters @@ -62,6 +62,10 @@ Screens + + Screens + + @@ -124,10 +128,14 @@ Screens + + Screens + + {faee5dce-633b-4ba6-b19d-ea70ee3c1c38} - + diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 64776ca274..6ff5076007 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -334,6 +334,8 @@ LOCAL_SRC_FILES := \ $(SRC)/android/jni/native-audio-so.cpp \ $(SRC)/UI/BackgroundAudio.cpp \ $(SRC)/UI/DevScreens.cpp \ + $(SRC)/UI/DisplayLayoutEditor.cpp \ + $(SRC)/UI/DisplayLayoutScreen.cpp \ $(SRC)/UI/EmuScreen.cpp \ $(SRC)/UI/MainScreen.cpp \ $(SRC)/UI/MiscScreens.cpp \