Skip to content

Commit 43bed26

Browse files
committed
feat(WASM): Support setting the screen orientation
Supports setting the screen orientation through the DisplayInformation.AutoRotationPreferences on supported browsers. Internally the implementation calls the JavaScript screen.orientation.lock API.
1 parent b872d46 commit 43bed26

File tree

4 files changed

+188
-5
lines changed

4 files changed

+188
-5
lines changed

src/Uno.UI/WasmScripts/Uno.UI.d.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -864,19 +864,30 @@ declare namespace Windows.Gaming.Input {
864864
}
865865
}
866866
declare namespace Windows.Graphics.Display {
867-
class DisplayInformation {
867+
enum DisplayOrientations {
868+
None = 0,
869+
Landscape = 1,
870+
Portrait = 2,
871+
LandscapeFlipped = 4,
872+
PortraitFlipped = 8
873+
}
874+
export class DisplayInformation {
868875
private static readonly DpiCheckInterval;
869876
private static lastDpi;
870877
private static dpiWatcher;
871878
private static dispatchOrientationChanged;
872879
private static dispatchDpiChanged;
880+
private static lockingSupported;
873881
static startOrientationChanged(): void;
874882
static stopOrientationChanged(): void;
875883
static startDpiChanged(): void;
876884
static stopDpiChanged(): void;
885+
static setOrientationAsync(uwpOrientations: DisplayOrientations): Promise<void>;
886+
private static parseUwpOrientation;
877887
private static updateDpi;
878888
private static onOrientationChange;
879889
}
890+
export {};
880891
}
881892
interface Window {
882893
SpeechRecognition: any;

src/Uno.UI/WasmScripts/Uno.UI.js

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2773,6 +2773,14 @@ var Windows;
27732773
(function (Graphics) {
27742774
var Display;
27752775
(function (Display) {
2776+
let DisplayOrientations;
2777+
(function (DisplayOrientations) {
2778+
DisplayOrientations[DisplayOrientations["None"] = 0] = "None";
2779+
DisplayOrientations[DisplayOrientations["Landscape"] = 1] = "Landscape";
2780+
DisplayOrientations[DisplayOrientations["Portrait"] = 2] = "Portrait";
2781+
DisplayOrientations[DisplayOrientations["LandscapeFlipped"] = 4] = "LandscapeFlipped";
2782+
DisplayOrientations[DisplayOrientations["PortraitFlipped"] = 8] = "PortraitFlipped";
2783+
})(DisplayOrientations || (DisplayOrientations = {}));
27762784
class DisplayInformation {
27772785
static startOrientationChanged() {
27782786
window.screen.orientation.addEventListener("change", DisplayInformation.onOrientationChange);
@@ -2790,6 +2798,71 @@ var Windows;
27902798
static stopDpiChanged() {
27912799
window.clearInterval(DisplayInformation.dpiWatcher);
27922800
}
2801+
static async setOrientationAsync(uwpOrientations) {
2802+
let oldOrientation = screen.orientation.type;
2803+
let orientations = DisplayInformation.parseUwpOrientation(uwpOrientations);
2804+
if (orientations.includes(oldOrientation)) {
2805+
return;
2806+
}
2807+
// Setting the orientation requires briefly changing the device to fullscreen.
2808+
// This causes a glitch, which is unnecessary for devices which does not support
2809+
// setting the orientation, such as most desktop browsers.
2810+
// We therefore attempt to check for support, and do nothing if the feature is
2811+
// unavailable.
2812+
if (this.lockingSupported == null) {
2813+
try {
2814+
await screen.orientation.lock(oldOrientation);
2815+
this.lockingSupported = true;
2816+
}
2817+
catch (e) {
2818+
if (e instanceof DOMException && e.name === "NotSupportedError") {
2819+
this.lockingSupported = false;
2820+
console.log("This browser does not support setting the orientation.");
2821+
}
2822+
else {
2823+
// On most mobile devices we should reach this line.
2824+
this.lockingSupported = true;
2825+
}
2826+
}
2827+
}
2828+
if (!this.lockingSupported) {
2829+
return;
2830+
}
2831+
let wasFullscreen = document.fullscreenElement != null;
2832+
if (!wasFullscreen) {
2833+
await document.body.requestFullscreen();
2834+
}
2835+
for (let orientation of orientations) {
2836+
try {
2837+
// On success, screen.orientation should fire the 'change' event.
2838+
await screen.orientation.lock(orientation);
2839+
break;
2840+
}
2841+
catch (e) {
2842+
// Absorb all errors to ensure that the exitFullscreen block below is called.
2843+
console.log(`Failed to set the screen orientation to '${orientation}': ${e}`);
2844+
}
2845+
}
2846+
if (!wasFullscreen) {
2847+
await document.exitFullscreen();
2848+
}
2849+
}
2850+
static parseUwpOrientation(uwpOrientations) {
2851+
let orientations = [];
2852+
if (uwpOrientations & DisplayOrientations.Landscape) {
2853+
orientations.push("landscape-primary");
2854+
}
2855+
if (uwpOrientations & DisplayOrientations.Portrait) {
2856+
orientations.push("portrait-primary");
2857+
}
2858+
if (uwpOrientations & DisplayOrientations.LandscapeFlipped) {
2859+
orientations.push("landscape-secondary");
2860+
}
2861+
if (uwpOrientations & DisplayOrientations.PortraitFlipped) {
2862+
orientations.push("portrait-secondary");
2863+
}
2864+
return orientations;
2865+
}
27932866
static updateDpi() {
27942867
const currentDpi = window.devicePixelRatio;
27952868
if (Math.abs(DisplayInformation.lastDpi - currentDpi) > 0.001) {

src/Uno.UI/ts/Windows/Graphics/Display/DisplayInformation.ts

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11
namespace Windows.Graphics.Display {
2+
enum DisplayOrientations {
3+
None = 0,
4+
Landscape = 1,
5+
Portrait = 2,
6+
LandscapeFlipped = 4,
7+
PortraitFlipped = 8
8+
}
29

310
export class DisplayInformation {
411
private static readonly DpiCheckInterval = 1000;
@@ -9,6 +16,8 @@
916
private static dispatchOrientationChanged: (type: string) => number;
1017
private static dispatchDpiChanged: (dpi: number) => number;
1118

19+
private static lockingSupported: boolean | null;
20+
1221
public static startOrientationChanged() {
1322
window.screen.orientation.addEventListener("change", DisplayInformation.onOrientationChange);
1423
}
@@ -25,17 +34,93 @@
2534

2635
// start polling the devicePixel
2736
DisplayInformation.dpiWatcher = window.setInterval(
28-
DisplayInformation.updateDpi,
37+
DisplayInformation.updateDpi,
2938
DisplayInformation.DpiCheckInterval);
3039
}
3140

3241
public static stopDpiChanged() {
3342
window.clearInterval(DisplayInformation.dpiWatcher);
3443
}
3544

45+
public static async setOrientationAsync(uwpOrientations: DisplayOrientations): Promise<void> {
46+
let oldOrientation = screen.orientation.type;
47+
let orientations = DisplayInformation.parseUwpOrientation(uwpOrientations);
48+
49+
if (orientations.includes(oldOrientation)) {
50+
return;
51+
}
52+
53+
// Setting the orientation requires briefly changing the device to fullscreen.
54+
// This causes a glitch, which is unnecessary for devices which does not support
55+
// setting the orientation, such as most desktop browsers.
56+
// We therefore attempt to check for support, and do nothing if the feature is
57+
// unavailable.
58+
if (this.lockingSupported == null) {
59+
try {
60+
await screen.orientation.lock(oldOrientation);
61+
this.lockingSupported = true;
62+
} catch (e) {
63+
if (e instanceof DOMException && e.name === "NotSupportedError") {
64+
this.lockingSupported = false;
65+
console.log("This browser does not support setting the orientation.")
66+
} else {
67+
// On most mobile devices we should reach this line.
68+
this.lockingSupported = true;
69+
}
70+
}
71+
}
72+
73+
if (!this.lockingSupported) {
74+
return;
75+
}
76+
77+
let wasFullscreen = document.fullscreenElement != null;
78+
79+
if (!wasFullscreen) {
80+
await document.body.requestFullscreen();
81+
}
82+
83+
for (let orientation of orientations) {
84+
try {
85+
// On success, screen.orientation should fire the 'change' event.
86+
await screen.orientation.lock(orientation);
87+
break;
88+
} catch (e) {
89+
// Absorb all errors to ensure that the exitFullscreen block below is called.
90+
console.log(`Failed to set the screen orientation to '${orientation}': ${e}`);
91+
}
92+
}
93+
94+
if (!wasFullscreen) {
95+
await document.exitFullscreen();
96+
}
97+
}
98+
99+
private static parseUwpOrientation(uwpOrientations: DisplayOrientations): OrientationLockType[] {
100+
let orientations: OrientationLockType[] = [];
101+
102+
if (uwpOrientations & DisplayOrientations.Landscape) {
103+
orientations.push("landscape-primary");
104+
}
105+
106+
if (uwpOrientations & DisplayOrientations.Portrait) {
107+
orientations.push("portrait-primary");
108+
}
109+
110+
if (uwpOrientations & DisplayOrientations.LandscapeFlipped) {
111+
orientations.push("landscape-secondary");
112+
}
113+
114+
if (uwpOrientations & DisplayOrientations.PortraitFlipped) {
115+
orientations.push("portrait-secondary");
116+
}
117+
118+
return orientations;
119+
}
120+
36121
private static updateDpi() {
37122
const currentDpi = window.devicePixelRatio;
38-
if (Math.abs(DisplayInformation.lastDpi - currentDpi) > 0.001) {
123+
if (Math.abs(DisplayInformation.lastDpi - currentDpi) > 0.001) {
39124
if (DisplayInformation.dispatchDpiChanged == null) {
40125
DisplayInformation.dispatchDpiChanged =
41126
(<any>Module).mono_bind_static_method(

src/Uno.UWP/Graphics/Display/DisplayInformation.wasm.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
#if __WASM__
2-
using Uno;
3-
using Uno.Foundation;
42
using System;
53
using System.Globalization;
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
using Uno;
7+
using Uno.Foundation;
68

79
namespace Windows.Graphics.Display
810
{
@@ -134,6 +136,13 @@ partial void StopDpiChanged()
134136
WebAssemblyRuntime.InvokeJS(command);
135137
}
136138

139+
static partial void SetOrientationPartial(DisplayOrientations orientations)
140+
{
141+
Uno.UI.Dispatching.CoreDispatcher.Main.RunAsync(
142+
Uno.UI.Dispatching.CoreDispatcherPriority.High,
143+
(ct) => SetOrientationAsync(orientations, ct));
144+
}
145+
137146
private static bool TryReadJsFloat(string property, out float value) =>
138147
float.TryParse(WebAssemblyRuntime.InvokeJS(property), NumberStyles.Any, CultureInfo.InvariantCulture, out value);
139148

@@ -152,6 +161,11 @@ private static DisplayOrientations ParseJsOrientation(string jsOrientation)
152161
_ => DisplayOrientations.None,
153162
};
154163
}
164+
165+
private static Task SetOrientationAsync(DisplayOrientations orientations, CancellationToken ct)
166+
{
167+
return WebAssemblyRuntime.InvokeAsync($"{JsType}.setOrientationAsync({(int)orientations})", ct);
168+
}
155169
}
156170
}
157171
#endif

0 commit comments

Comments
 (0)