Unity 8
OrientedShell.qml
1 /*
2  * Copyright (C) 2015 Canonical, Ltd.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 3.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  */
16 
17 import QtQuick 2.4
18 import QtQuick.Window 2.2
19 import Unity.Session 0.1
20 import GSettings 1.0
21 import "Components"
22 import "Components/UnityInputInfo"
23 import "Rotation"
24 // Backup Workaround https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1473471
25 // in case we remove the UnityInputInfo import
26 import Ubuntu.Components 1.3
27 
28 Rectangle {
29  id: root
30  color: "black"
31 
32  implicitWidth: units.gu(40)
33  implicitHeight: units.gu(71)
34 
35  DeviceConfiguration {
36  id: deviceConfiguration
37  name: applicationArguments.deviceName
38  }
39 
40  property alias orientations: d.orientations
41 
42  Item {
43  id: d
44 
45  property Orientations orientations: Orientations {
46  id: orientations
47  // NB: native and primary orientations here don't map exactly to their QScreen counterparts
48  native_: root.width > root.height ? Qt.LandscapeOrientation : Qt.PortraitOrientation
49 
50  primary: deviceConfiguration.primaryOrientation == deviceConfiguration.useNativeOrientation
51  ? native_ : deviceConfiguration.primaryOrientation
52 
53  landscape: deviceConfiguration.landscapeOrientation
54  invertedLandscape: deviceConfiguration.invertedLandscapeOrientation
55  portrait: deviceConfiguration.portraitOrientation
56  invertedPortrait: deviceConfiguration.invertedPortraitOrientation
57  }
58  }
59  // to be overwritten by tests
60  property var unity8Settings: Unity8Settings {}
61  property var oskSettings: GSettings { schema.id: "com.canonical.keyboard.maliit" }
62 
63  property int physicalOrientation: Screen.orientation
64  property bool orientationLocked: OrientationLock.enabled
65  property var orientationLock: OrientationLock
66 
67  property int orientation
68  onPhysicalOrientationChanged: {
69  if (!orientationLocked) {
70  orientation = physicalOrientation;
71  }
72  }
73  onOrientationLockedChanged: {
74  if (orientationLocked) {
75  orientationLock.savedOrientation = physicalOrientation;
76  } else {
77  orientation = physicalOrientation;
78  }
79  }
80  Component.onCompleted: {
81  if (orientationLocked) {
82  orientation = orientationLock.savedOrientation;
83  }
84  // We need to manually update this on startup as the binding
85  // below doesn't seem to have any effect at that stage
86  oskSettings.stayHidden = UnityInputInfo.keyboards > 0
87  oskSettings.disableHeight = shell.usageScenario == "desktop"
88  }
89 
90  // we must rotate to a supported orientation regardless of shell's preference
91  property bool orientationChangesEnabled:
92  (shell.orientation & supportedOrientations) === 0 ? true
93  : shell.orientationChangesEnabled
94 
95 
96  Binding {
97  target: oskSettings
98  property: "stayHidden"
99  value: UnityInputInfo.keyboards > 0
100  }
101 
102  Binding {
103  target: oskSettings
104  property: "disableHeight"
105  value: shell.usageScenario == "desktop"
106  }
107 
108  readonly property int supportedOrientations: shell.supportedOrientations
109  & (deviceConfiguration.supportedOrientations == deviceConfiguration.useNativeOrientation
110  ? orientations.native_
111  : deviceConfiguration.supportedOrientations)
112 
113  property int acceptedOrientationAngle: {
114  if (orientation & supportedOrientations) {
115  return Screen.angleBetween(orientations.native_, orientation);
116  } else if (shell.orientation & supportedOrientations) {
117  // stay where we are
118  return shell.orientationAngle;
119  } else if (angleToOrientation(shell.mainAppWindowOrientationAngle) & supportedOrientations) {
120  return shell.mainAppWindowOrientationAngle;
121  } else {
122  // rotate to some supported orientation as we can't stay where we currently are
123  // TODO: Choose the closest to the current one
124  if (supportedOrientations & Qt.PortraitOrientation) {
125  return Screen.angleBetween(orientations.native_, Qt.PortraitOrientation);
126  } else if (supportedOrientations & Qt.LandscapeOrientation) {
127  return Screen.angleBetween(orientations.native_, Qt.LandscapeOrientation);
128  } else if (supportedOrientations & Qt.InvertedPortraitOrientation) {
129  return Screen.angleBetween(orientations.native_, Qt.InvertedPortraitOrientation);
130  } else if (supportedOrientations & Qt.InvertedLandscapeOrientation) {
131  return Screen.angleBetween(orientations.native_, Qt.InvertedLandscapeOrientation);
132  } else {
133  // if all fails, fallback to primary orientation
134  return Screen.angleBetween(orientations.native_, orientations.primary);
135  }
136  }
137  }
138 
139  function angleToOrientation(angle) {
140  switch (angle) {
141  case 0:
142  return orientations.native_;
143  case 90:
144  return orientations.native_ === Qt.PortraitOrientation ? Qt.InvertedLandscapeOrientation
145  : Qt.PortraitOrientation;
146  case 180:
147  return orientations.native_ === Qt.PortraitOrientation ? Qt.InvertedPortraitOrientation
148  : Qt.InvertedLandscapeOrientation;
149  case 270:
150  return orientations.native_ === Qt.PortraitOrientation ? Qt.LandscapeOrientation
151  : Qt.InvertedPortraitOrientation;
152  default:
153  console.warn("angleToOrientation: Invalid orientation angle: " + angle);
154  return orientations.primary;
155  }
156  }
157 
158  RotationStates {
159  id: rotationStates
160  objectName: "rotationStates"
161  orientedShell: root
162  shell: shell
163  shellCover: shellCover
164  windowScreenshot: windowScreenshot
165  }
166 
167  Shell {
168  id: shell
169  objectName: "shell"
170  width: root.width
171  height: root.height
172  orientation: root.angleToOrientation(orientationAngle)
173  orientations: root.orientations
174  nativeWidth: root.width
175  nativeHeight: root.height
176  mode: applicationArguments.mode
177  hasMouse: UnityInputInfo.mice > deviceConfiguration.ignoredMice
178 
179  // TODO: Factor in the connected input devices (eg: physical keyboard, mouse, touchscreen),
180  // what's the output device (eg: big TV, desktop monitor, phone display), etc.
181  usageScenario: {
182  if (root.unity8Settings.usageMode === "Windowed") {
183  return "desktop";
184  } else if (root.unity8Settings.usageMode === "Staged") {
185  if (deviceConfiguration.category === "phone") {
186  return "phone";
187  } else {
188  return "tablet";
189  }
190  } else { // automatic
191  if (UnityInputInfo.mice > deviceConfiguration.ignoredMice) {
192  return "desktop";
193  } else {
194  return deviceConfiguration.category;
195  }
196  }
197  }
198 
199  property real transformRotationAngle
200  property real transformOriginX
201  property real transformOriginY
202 
203  transform: Rotation {
204  origin.x: shell.transformOriginX; origin.y: shell.transformOriginY; axis { x: 0; y: 0; z: 1 }
205  angle: shell.transformRotationAngle
206  }
207  }
208 
209  Rectangle {
210  id: shellCover
211  color: "black"
212  anchors.fill: parent
213  visible: false
214  }
215 
216  WindowScreenshot {
217  id: windowScreenshot
218  visible: false
219  width: root.width
220  height: root.height
221 
222  property real transformRotationAngle
223  property real transformOriginX
224  property real transformOriginY
225 
226  transform: Rotation {
227  origin.x: windowScreenshot.transformOriginX; origin.y: windowScreenshot.transformOriginY;
228  axis { x: 0; y: 0; z: 1 }
229  angle: windowScreenshot.transformRotationAngle
230  }
231  }
232 }