Browse Source

Add love.window.getSafeArea (resolves issue #1437). Returns a rectangle (x, y, w, h) inside the window which is known to be unobstructed by a system title bar, the iPhone X notch, etc. Useful for making sure UI elements can be seen by the user.

Currently it only has a proper implementation on iOS and will return the window's full size otherwise.
Alex Szpakowski 6 years ago
parent
commit
d41aca836e

+ 10 - 0
src/common/ios.h

@@ -25,8 +25,12 @@
 
 
 #ifdef LOVE_IOS
 #ifdef LOVE_IOS
 
 
+#include "common/math.h"
+
 #include <string>
 #include <string>
 
 
+struct SDL_Window;
+
 namespace love
 namespace love
 {
 {
 namespace ios
 namespace ios
@@ -74,6 +78,12 @@ void setAudioMixWithOthers(bool mixEnabled);
  **/
  **/
 bool hasBackgroundMusic();
 bool hasBackgroundMusic();
 
 
+/**
+ * Gets the area in the window that is safe for UI to render to (not covered by
+ * the status bar, notch, etc.)
+ **/
+Rect getSafeArea(SDL_Window *window);
+
 } // ios
 } // ios
 } // love
 } // love
 
 

+ 41 - 3
src/common/ios.mm

@@ -31,6 +31,8 @@
 #include <vector>
 #include <vector>
 
 
 #include <SDL_events.h>
 #include <SDL_events.h>
+#include <SDL_video.h>
+#include <SDL_syswm.h>
 
 
 static NSArray *getLovesInDocuments();
 static NSArray *getLovesInDocuments();
 static bool deleteFileInDocuments(NSString *filename);
 static bool deleteFileInDocuments(NSString *filename);
@@ -362,9 +364,45 @@ void setAudioMixWithOthers(bool mixEnabled)
 
 
 bool hasBackgroundMusic()
 bool hasBackgroundMusic()
 {
 {
-	if ([[AVAudioSession sharedInstance] respondsToSelector:@selector(secondaryAudioShouldBeSilencedHint)])
-		return [[AVAudioSession sharedInstance] secondaryAudioShouldBeSilencedHint];
-	return false;
+	@autoreleasepool
+	{
+		AVAudioSession *session = [AVAudioSession sharedInstance];
+		if ([session respondsToSelector:@selector(secondaryAudioShouldBeSilencedHint)])
+			return session.secondaryAudioShouldBeSilencedHint;
+		return false;
+	}
+}
+
+Rect getSafeArea(SDL_Window *window)
+{
+	@autoreleasepool
+	{
+		Rect rect = {};
+		SDL_GetWindowSize(window, &rect.w, &rect.h);
+
+		SDL_SysWMinfo info = {};
+		SDL_VERSION(&info.version);
+		if (SDL_GetWindowWMInfo(window, &info))
+		{
+			UIView *view = info.info.uikit.window.rootViewController.view;
+			if (@available(iOS 11.0, tvOS 11.0, *))
+			{
+				UIEdgeInsets insets = view.safeAreaInsets;
+
+				rect.x += insets.left;
+				rect.w -= insets.left;
+
+				rect.w -= insets.right;
+
+				rect.y += insets.top;
+				rect.h -= insets.top;
+
+				rect.h -= insets.bottom;
+			}
+		}
+
+		return rect;
+	}
 }
 }
 
 
 } // ios
 } // ios

+ 3 - 0
src/modules/window/Window.h

@@ -24,6 +24,7 @@
 // LOVE
 // LOVE
 #include "common/Module.h"
 #include "common/Module.h"
 #include "common/StringMap.h"
 #include "common/StringMap.h"
+#include "common/math.h"
 #include "image/ImageData.h"
 #include "image/ImageData.h"
 
 
 // C++
 // C++
@@ -151,6 +152,8 @@ public:
 	virtual void setPosition(int x, int y, int displayindex) = 0;
 	virtual void setPosition(int x, int y, int displayindex) = 0;
 	virtual void getPosition(int &x, int &y, int &displayindex) = 0;
 	virtual void getPosition(int &x, int &y, int &displayindex) = 0;
 
 
+	virtual Rect getSafeArea() const = 0;
+
 	virtual bool isOpen() const = 0;
 	virtual bool isOpen() const = 0;
 
 
 	virtual void setWindowTitle(const std::string &title) = 0;
 	virtual void setWindowTitle(const std::string &title) = 0;

+ 14 - 0
src/modules/window/sdl/Window.cpp

@@ -27,6 +27,10 @@
 #include "common/android.h"
 #include "common/android.h"
 #endif
 #endif
 
 
+#ifdef LOVE_IOS
+#include "common/ios.h"
+#endif
+
 // C++
 // C++
 #include <iostream>
 #include <iostream>
 #include <vector>
 #include <vector>
@@ -839,6 +843,16 @@ void Window::getPosition(int &x, int &y, int &displayindex)
 	}
 	}
 }
 }
 
 
+Rect Window::getSafeArea() const
+{
+#ifdef LOVE_IOS
+	if (window != nullptr)
+		return love::ios::getSafeArea(window);
+#endif
+
+	return {0, 0, getWidth(), getHeight()};
+}
+
 bool Window::isOpen() const
 bool Window::isOpen() const
 {
 {
 	return open;
 	return open;

+ 2 - 0
src/modules/window/sdl/Window.h

@@ -66,6 +66,8 @@ public:
 	void setPosition(int x, int y, int displayindex) override;
 	void setPosition(int x, int y, int displayindex) override;
 	void getPosition(int &x, int &y, int &displayindex) override;
 	void getPosition(int &x, int &y, int &displayindex) override;
 
 
+	Rect getSafeArea() const override;
+
 	bool isOpen() const override;
 	bool isOpen() const override;
 
 
 	void setWindowTitle(const std::string &title) override;
 	void setWindowTitle(const std::string &title) override;

+ 11 - 0
src/modules/window/wrap_Window.cpp

@@ -365,6 +365,16 @@ int w_getPosition(lua_State *L)
 	return 3;
 	return 3;
 }
 }
 
 
+int w_getSafeArea(lua_State *L)
+{
+	Rect area = instance()->getSafeArea();
+	lua_pushnumber(L, area.x);
+	lua_pushnumber(L, area.y);
+	lua_pushnumber(L, area.w);
+	lua_pushnumber(L, area.h);
+	return 4;
+}
+
 int w_setIcon(lua_State *L)
 int w_setIcon(lua_State *L)
 {
 {
 	image::ImageData *i = luax_checktype<image::ImageData>(L, 1);
 	image::ImageData *i = luax_checktype<image::ImageData>(L, 1);
@@ -607,6 +617,7 @@ static const luaL_Reg functions[] =
 	{ "getDesktopDimensions", w_getDesktopDimensions },
 	{ "getDesktopDimensions", w_getDesktopDimensions },
 	{ "setPosition", w_setPosition },
 	{ "setPosition", w_setPosition },
 	{ "getPosition", w_getPosition },
 	{ "getPosition", w_getPosition },
+	{ "getSafeArea", w_getSafeArea },
 	{ "setIcon", w_setIcon },
 	{ "setIcon", w_setIcon },
 	{ "getIcon", w_getIcon },
 	{ "getIcon", w_getIcon },
 	{ "setVSync", w_setVSync },
 	{ "setVSync", w_setVSync },