on c1843bf help sign in
unity8/qml/OrientedShell.qml
295 lines 11 kB history raw
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 */
17import QtQuick 2.4
18import QtQuick.Window 2.2
19import Unity.InputInfo 0.1
20import Unity.Session 0.1
21import Unity.Screens 0.1
22import Utils 0.1
23import GSettings 1.0
24import "Components"
25import "Rotation"
26// Workaround https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1473471
27import Ubuntu.Components 1.3
29Rectangle {
30 id: root
31 color: "black"
33 implicitWidth: units.gu(40)
34 implicitHeight: units.gu(71)
36 DeviceConfiguration {
37 id: deviceConfiguration
38 name: applicationArguments.deviceName
39 }
41 property alias orientations: d.orientations
43 Item {
44 id: d
46 property Orientations orientations: Orientations {
47 id: orientations
48 // NB: native and primary orientations here don't map exactly to their QScreen counterparts
49 native_: root.width > root.height ? Qt.LandscapeOrientation : Qt.PortraitOrientation
51 primary: deviceConfiguration.primaryOrientation == deviceConfiguration.useNativeOrientation
52 ? native_ : deviceConfiguration.primaryOrientation
54 landscape: deviceConfiguration.landscapeOrientation
55 invertedLandscape: deviceConfiguration.invertedLandscapeOrientation
56 portrait: deviceConfiguration.portraitOrientation
57 invertedPortrait: deviceConfiguration.invertedPortraitOrientation
58 }
59 }
60 // to be overwritten by tests
61 property var unity8Settings: GSettings { schema.id: "com.canonical.Unity8" }
62 property var oskSettings: GSettings { schema.id: "com.canonical.keyboard.maliit" }
64 property int physicalOrientation: Screen.orientation
65 property bool orientationLocked: OrientationLock.enabled
66 property var orientationLock: OrientationLock
68 InputDeviceModel {
69 id: miceModel
70 deviceFilter: InputInfo.Mouse
71 property int oldCount: 0
72 }
74 InputDeviceModel {
75 id: touchPadModel
76 deviceFilter: InputInfo.TouchPad
77 property int oldCount: 0
78 }
80 InputDeviceModel {
81 id: keyboardsModel
82 deviceFilter: InputInfo.Keyboard
83 onDeviceAdded: forceOSKEnabled = autopilotDevicePresent();
84 onDeviceRemoved: forceOSKEnabled = autopilotDevicePresent();
85 }
87 InputDeviceModel {
88 id: touchScreensModel
89 deviceFilter: InputInfo.TouchScreen
90 }
92 readonly property int pointerInputDevices: miceModel.count + touchPadModel.count
93 onPointerInputDevicesChanged: {
94 console.log("Pointer input devices changed:", pointerInputDevices, "current mode:", root.unity8Settings.usageMode, "old device count", miceModel.oldCount + touchPadModel.oldCount)
95 if (root.unity8Settings.usageMode === "Windowed") {
96 if (pointerInputDevices === 0) {
97 // All pointer devices have been unplugged. Move to staged.
98 root.unity8Settings.usageMode = "Staged";
99 }
100 } else {
101 var longEdgeWidth = Math.max(root.width, root.height)
102 if (longEdgeWidth > units.gu(90)){
103 if (pointerInputDevices > 0 && pointerInputDevices > miceModel.oldCount + touchPadModel.oldCount) {
104 root.unity8Settings.usageMode = "Windowed";
105 }
106 } else {
107 // Make sure we initialize to something sane
108 root.unity8Settings.usageMode = "Staged";
109 }
110 }
111 miceModel.oldCount = miceModel.count;
112 touchPadModel.oldCount = touchPadModel.count;
113 }
115 /* FIXME: This exposes the NameRole as a work arround for lp:1542224.
116 * When QInputInfo exposes NameRole to QML, this should be removed.
117 */
118 property bool forceOSKEnabled: false
119 property var autopilotEmulatedDeviceNames: ["py-evdev-uinput"]
120 UnitySortFilterProxyModel {
121 id: autopilotDevices
122 model: keyboardsModel
123 }
125 function autopilotDevicePresent() {
126 for(var i = 0; i < autopilotDevices.count; i++) {
127 var device = autopilotDevices.get(i);
128 if (autopilotEmulatedDeviceNames.indexOf(device.name) != -1) {
129 console.warn("Forcing the OSK to be enabled as there is an autopilot eumlated device present.")
130 return true;
131 }
132 }
133 return false;
134 }
136 Screens {
137 id: screens
138 }
140 property int orientation
141 onPhysicalOrientationChanged: {
142 if (!orientationLocked) {
143 orientation = physicalOrientation;
144 }
145 }
146 onOrientationLockedChanged: {
147 if (orientationLocked) {
148 orientationLock.savedOrientation = physicalOrientation;
149 } else {
150 orientation = physicalOrientation;
151 }
152 }
153 Component.onCompleted: {
154 if (orientationLocked) {
155 orientation = orientationLock.savedOrientation;
156 }
157 // We need to manually update this on startup as the binding
158 // below doesn't seem to have any effect at that stage
159 oskSettings.disableHeight = !shell.oskEnabled || shell.usageScenario == "desktop"
160 }
162 // we must rotate to a supported orientation regardless of shell's preference
163 property bool orientationChangesEnabled:
164 (shell.orientation & supportedOrientations) === 0 ? true
165 : shell.orientationChangesEnabled
167 Binding {
168 target: oskSettings
169 property: "disableHeight"
170 value: !shell.oskEnabled || shell.usageScenario == "desktop"
171 }
173 readonly property int supportedOrientations: shell.supportedOrientations
174 & (deviceConfiguration.supportedOrientations == deviceConfiguration.useNativeOrientation
175 ? orientations.native_
176 : deviceConfiguration.supportedOrientations)
178 property int acceptedOrientationAngle: {
179 if (orientation & supportedOrientations) {
180 return Screen.angleBetween(orientations.native_, orientation);
181 } else if (shell.orientation & supportedOrientations) {
182 // stay where we are
183 return shell.orientationAngle;
184 } else if (angleToOrientation(shell.mainAppWindowOrientationAngle) & supportedOrientations) {
185 return shell.mainAppWindowOrientationAngle;
186 } else {
187 // rotate to some supported orientation as we can't stay where we currently are
188 // TODO: Choose the closest to the current one
189 if (supportedOrientations & Qt.PortraitOrientation) {
190 return Screen.angleBetween(orientations.native_, Qt.PortraitOrientation);
191 } else if (supportedOrientations & Qt.LandscapeOrientation) {
192 return Screen.angleBetween(orientations.native_, Qt.LandscapeOrientation);
193 } else if (supportedOrientations & Qt.InvertedPortraitOrientation) {
194 return Screen.angleBetween(orientations.native_, Qt.InvertedPortraitOrientation);
195 } else if (supportedOrientations & Qt.InvertedLandscapeOrientation) {
196 return Screen.angleBetween(orientations.native_, Qt.InvertedLandscapeOrientation);
197 } else {
198 // if all fails, fallback to primary orientation
199 return Screen.angleBetween(orientations.native_, orientations.primary);
200 }
201 }
202 }
204 function angleToOrientation(angle) {
205 switch (angle) {
206 case 0:
207 return orientations.native_;
208 case 90:
209 return orientations.native_ === Qt.PortraitOrientation ? Qt.InvertedLandscapeOrientation
210 : Qt.PortraitOrientation;
211 case 180:
212 return orientations.native_ === Qt.PortraitOrientation ? Qt.InvertedPortraitOrientation
213 : Qt.InvertedLandscapeOrientation;
214 case 270:
215 return orientations.native_ === Qt.PortraitOrientation ? Qt.LandscapeOrientation
216 : Qt.InvertedPortraitOrientation;
217 default:
218 console.warn("angleToOrientation: Invalid orientation angle: " + angle);
219 return orientations.primary;
220 }
221 }
223 RotationStates {
224 id: rotationStates
225 objectName: "rotationStates"
226 orientedShell: root
227 shell: shell
228 shellCover: shellCover
229 windowScreenshot: windowScreenshot
230 }
232 Shell {
233 id: shell
234 objectName: "shell"
235 width: root.width
236 height: root.height
237 orientation: root.angleToOrientation(orientationAngle)
238 orientations: root.orientations
239 nativeWidth: root.width
240 nativeHeight: root.height
241 mode: applicationArguments.mode
242 hasMouse: miceModel.count + touchPadModel.count > 0
243 // TODO: Factor in if the current screen is a touch screen and if the user wants to
244 // have multiple keyboards around. For now we only enable one keyboard at a time
245 // thus hiding it here if there is a physical one around or if we have a second
246 // screen (the virtual touchpad & osk on the phone) attached.
247 oskEnabled: (keyboardsModel.count === 0 && screens.count === 1) ||
248 forceOSKEnabled
250 usageScenario: {
251 if (root.unity8Settings.usageMode === "Windowed") {
252 return "desktop";
253 } else {
254 if (deviceConfiguration.category === "phone") {
255 return "phone";
256 } else {
257 return "tablet";
258 }
259 }
260 }
262 property real transformRotationAngle
263 property real transformOriginX
264 property real transformOriginY
266 transform: Rotation {
267 origin.x: shell.transformOriginX; origin.y: shell.transformOriginY; axis { x: 0; y: 0; z: 1 }
268 angle: shell.transformRotationAngle
269 }
270 }
272 Rectangle {
273 id: shellCover
274 color: "black"
275 anchors.fill: parent
276 visible: false
277 }
279 WindowScreenshot {
280 id: windowScreenshot
281 visible: false
282 width: root.width
283 height: root.height
285 property real transformRotationAngle
286 property real transformOriginX
287 property real transformOriginY
289 transform: Rotation {
290 origin.x: windowScreenshot.transformOriginX; origin.y: windowScreenshot.transformOriginY;
291 axis { x: 0; y: 0; z: 1 }
292 angle: windowScreenshot.transformRotationAngle
293 }
294 }