mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-01 12:07:31 +01:00
On the iPhone Duo's cover screen, the camera sits in a box in the top right corner, which gives a side inset down the whole right edge in portrait. The controls are far below it, so they lose width for nothing. iOS 27.1 reports the box as an occlusion region, and from that we report how far down the side insets are needed (SYSPROP_DISPLAY_SIDE_INSETS_BOTTOM). When that's within the top third, the game screen and the touch layout editor skip the side insets. The rest of the UI, including the FPS counter, still keeps them. The regions can show up after the size and insets have settled, so the insets are now recomputed on every layout, and a screen recreates its views when its layout mode changes, since the touch controls are placed for the bounds they were created in. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
171 lines
4.9 KiB
C++
171 lines
4.9 KiB
C++
// Super basic screen manager. Let's you, well, switch between screens. Can also be used
|
|
// to pop one screen in front for a bit while keeping another one running, it's basically
|
|
// a native "activity stack". Well actually that part is still a TODO.
|
|
//
|
|
// Semantics
|
|
//
|
|
// switchScreen: When you call this, on a newed screen, the ScreenManager takes ownership.
|
|
// On the next update, it switches to the new screen and deletes the previous screen.
|
|
//
|
|
// TODO: A way to do smooth transitions between screens. Will probably involve screenshotting
|
|
// the previous screen and then animating it on top of the current screen with transparency
|
|
// and/or other similar effects.
|
|
|
|
#pragma once
|
|
|
|
#include <cstdint>
|
|
#include <string>
|
|
|
|
#include "Common/Common.h"
|
|
#include "Common/Input/InputState.h"
|
|
#include "Common/System/NativeApp.h"
|
|
#include "Common/System/System.h"
|
|
|
|
namespace UI {
|
|
class View;
|
|
}
|
|
|
|
namespace Draw {
|
|
class DrawContext;
|
|
class Swapchain;
|
|
}
|
|
|
|
// A display other than the primary one, like a second window or a TV. See docs/multi-swapchain.md.
|
|
struct OutputDisplay {
|
|
int id;
|
|
OutputRole role;
|
|
Draw::Swapchain *swapchain; // nullptr while there's no draw context.
|
|
};
|
|
|
|
enum DialogResult : u8 {
|
|
DR_NONE,
|
|
DR_OK,
|
|
DR_CANCEL,
|
|
DR_YES,
|
|
DR_NO,
|
|
DR_BACK,
|
|
};
|
|
const char *DialogResultToString(DialogResult result);
|
|
|
|
class ScreenManager;
|
|
class UIContext;
|
|
|
|
enum class ScreenFocusChange {
|
|
FOCUS_LOST_TOP, // Another screen was pushed on top
|
|
FOCUS_BECAME_TOP, // Became the top screen again
|
|
};
|
|
|
|
enum class ScreenRenderMode {
|
|
DEFAULT = 0,
|
|
FIRST = 1,
|
|
BEHIND = 4,
|
|
TOP = 8,
|
|
};
|
|
ENUM_CLASS_BITOPS(ScreenRenderMode);
|
|
|
|
enum class ScreenRenderRole {
|
|
NONE = 0,
|
|
CAN_BE_BACKGROUND = 1,
|
|
MUST_BE_FIRST = 2,
|
|
};
|
|
ENUM_CLASS_BITOPS(ScreenRenderRole);
|
|
|
|
enum class ViewLayoutMode {
|
|
ApplyInsets = 0, // For gameplay, option #1
|
|
IgnoreInsets, // For gameplay, option #2
|
|
IgnoreBottomInset,
|
|
IgnoreSideInsets,
|
|
};
|
|
|
|
enum class QueuedEventType : u8 {
|
|
KEY,
|
|
AXIS,
|
|
TOUCH,
|
|
};
|
|
|
|
struct QueuedEvent {
|
|
QueuedEventType type;
|
|
union {
|
|
TouchInput touch;
|
|
KeyInput key;
|
|
AxisInput axis;
|
|
};
|
|
};
|
|
|
|
enum class ScreenRenderFlags {
|
|
NONE = 0,
|
|
HANDLED_THROTTLING = 1,
|
|
};
|
|
ENUM_CLASS_BITOPS(ScreenRenderFlags);
|
|
|
|
enum class InputMode {
|
|
None = 0,
|
|
Keyboard = 1,
|
|
Mouse = 2,
|
|
Other = 4,
|
|
ImDebuggerToggle = 8, // ugly hack. debugger and pause goes through.
|
|
AllKeys = 16, // The screen gets keys that are normally left to the OS, like volume. For key mapping.
|
|
};
|
|
ENUM_CLASS_BITOPS(InputMode);
|
|
|
|
class Screen {
|
|
public:
|
|
Screen() = default;
|
|
virtual ~Screen();
|
|
|
|
virtual void onFinish(DialogResult reason) {}
|
|
virtual void update() {}
|
|
virtual ScreenRenderFlags PreRender(ScreenRenderMode mode) { return ScreenRenderFlags::NONE; }
|
|
virtual ScreenRenderFlags render(ScreenRenderMode mode) = 0;
|
|
// Draws this screen's content for a secondary output, whose swapchain is bound. Returns false if the
|
|
// screen has nothing for it. The topmost screen that returns true is the one shown.
|
|
virtual bool renderOutput(const OutputDisplay &output) { return false; }
|
|
virtual void resized() {}
|
|
virtual void dialogFinished(const Screen *dialog, DialogResult result) {}
|
|
virtual void sendMessage(UIMessage message, const char *value) {}
|
|
virtual void deviceLost() {}
|
|
virtual void deviceRestored(Draw::DrawContext *draw) {}
|
|
virtual ScreenRenderRole renderRole(bool isTop) const { return ScreenRenderRole::NONE; }
|
|
virtual bool wantBrightBackground() const { return false; } // special hack for DisplayLayoutScreen.
|
|
|
|
virtual void focusChanged(ScreenFocusChange focusChange);
|
|
|
|
virtual bool touch(const TouchInput &touch) = 0;
|
|
virtual bool key(const KeyInput &key) = 0;
|
|
virtual void axis(const AxisInput &axis) = 0;
|
|
|
|
virtual void RecreateViews() {}
|
|
|
|
// NOTE: This is polled every frame, not called on every input event.
|
|
virtual InputMode PassInputToMapper() const { return InputMode::None; }
|
|
|
|
ScreenManager *screenManager() { return screenManager_; }
|
|
const ScreenManager *screenManager() const { return screenManager_; }
|
|
void setScreenManager(ScreenManager *sm) { screenManager_ = sm; }
|
|
|
|
virtual const char *tag() const = 0;
|
|
|
|
virtual bool isTransparent() const { return false; }
|
|
virtual bool isTopLevel() const { return false; }
|
|
|
|
virtual TouchInput transformTouch(const TouchInput &touch) { return touch; }
|
|
virtual bool WantsTextInput() const { return false; }
|
|
|
|
protected:
|
|
// Requests made while creating views (by the views themselves, or with callbacks that capture them) get a separate
|
|
// token, so they can be forgotten when the views are deleted, while the screen's own requests stay alive.
|
|
int GetRequesterToken();
|
|
void SetCreatingViews(bool creating) { creatingViews_ = creating; }
|
|
bool IsCreatingViews() const { return creatingViews_; }
|
|
// Drops the callbacks of pending requests made by the views, which are about to be deleted.
|
|
void ForgetViewRequests();
|
|
|
|
private:
|
|
ScreenManager *screenManager_ = nullptr;
|
|
int token_ = -1;
|
|
int viewsToken_ = -1;
|
|
bool creatingViews_ = false;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(Screen);
|
|
};
|