diff --git a/data/com.canonical.Unity8.gschema.xml b/data/com.canonical.Unity8.gschema.xml index fa531b873f..429064c1db 100644 --- a/data/com.canonical.Unity8.gschema.xml +++ b/data/com.canonical.Unity8.gschema.xml @@ -63,6 +63,11 @@ Whether the OSK switch should be visible Toggle the visibility of the OSK switch + + false + Disable top margin for the notch + This will restore the previous behavior on devices with a notch. If this option is enabled some indicators might not be visible. + diff --git a/plugins/Utils/deviceconfigparser.cpp b/plugins/Utils/deviceconfigparser.cpp index 3db61a28d6..d63c110482 100644 --- a/plugins/Utils/deviceconfigparser.cpp +++ b/plugins/Utils/deviceconfigparser.cpp @@ -134,6 +134,24 @@ bool DeviceConfigParser::readBoolFromConfig(const QString &key, bool defaultValu return ret; } +int DeviceConfigParser::topMargin() const +{ + return readIntegerFromConfig("TopMargin", 0); +} + +int DeviceConfigParser::readIntegerFromConfig(const QString &key, int defaultValue) const +{ + m_config->beginGroup(m_name); + + int ret = defaultValue; + if (m_config->contains(key)) { + ret = m_config->value(key).toInt(); + } + + m_config->endGroup(); + return ret; +} + QStringList DeviceConfigParser::readOrientationsFromConfig(const QString &key) const { m_config->beginGroup(m_name); diff --git a/plugins/Utils/deviceconfigparser.h b/plugins/Utils/deviceconfigparser.h index f20108f075..3da4624546 100644 --- a/plugins/Utils/deviceconfigparser.h +++ b/plugins/Utils/deviceconfigparser.h @@ -34,6 +34,7 @@ class DeviceConfigParser: public QObject Q_PROPERTY(Qt::ScreenOrientation invertedPortraitOrientation READ invertedPortraitOrientation NOTIFY changed) Q_PROPERTY(QString category READ category NOTIFY changed) Q_PROPERTY(bool supportsMultiColorLed READ supportsMultiColorLed NOTIFY changed) + Q_PROPERTY(int topMargin READ topMargin NOTIFY changed) public: DeviceConfigParser(QObject *parent = nullptr); @@ -49,6 +50,7 @@ class DeviceConfigParser: public QObject Qt::ScreenOrientation invertedPortraitOrientation() const; QString category() const; bool supportsMultiColorLed() const; + int topMargin() const; Q_SIGNALS: void changed(); @@ -61,6 +63,7 @@ class DeviceConfigParser: public QObject QString readOrientationFromConfig(const QString &key) const; Qt::ScreenOrientation stringToOrientation(const QString &orientationString, Qt::ScreenOrientation defaultValue) const; bool readBoolFromConfig(const QString &key, bool defaultValue) const; + int readIntegerFromConfig(const QString &key, int defaultValue) const; }; #endif diff --git a/qml/DeviceConfiguration.qml b/qml/DeviceConfiguration.qml index c73f43a915..0189616802 100644 --- a/qml/DeviceConfiguration.qml +++ b/qml/DeviceConfiguration.qml @@ -32,6 +32,7 @@ QtObject { readonly property alias invertedLandscapeOrientation: priv.invertedLandscapeOrientation readonly property alias portraitOrientation: priv.portraitOrientation readonly property alias invertedPortraitOrientation: priv.invertedPortraitOrientation + readonly property alias topMargin: priv.topMargin readonly property alias category: priv.category @@ -55,6 +56,7 @@ QtObject { property int invertedPortraitOrientation: deviceConfigParser.invertedPortraitOrientation property string category: deviceConfigParser.category property bool supportsMultiColorLed: deviceConfigParser.supportsMultiColorLed + property int topMargin: deviceConfigParser.topMargin states: [ State { @@ -104,6 +106,22 @@ QtObject { category: "phone" } }, + State { + name: "has-notch" + PropertyChanges { + target: priv + primaryOrientation: root.useNativeOrientation + supportedOrientations: Qt.PortraitOrientation + | Qt.LandscapeOrientation + | Qt.InvertedLandscapeOrientation + landscapeOrientation: Qt.LandscapeOrientation + invertedLandscapeOrientation: Qt.InvertedLandscapeOrientation + portraitOrientation: Qt.PortraitOrientation + invertedPortraitOrientation: Qt.InvertedPortraitOrientation + category: "phone" + topMargin: 20 + } + }, State { name: "manta" PropertyChanges { @@ -147,6 +165,7 @@ QtObject { portraitOrientation: Qt.PortraitOrientation invertedPortraitOrientation: Qt.InvertedPortraitOrientation category: "desktop" + topMargin: 0 } }, State { diff --git a/qml/OrientedShell.qml b/qml/OrientedShell.qml index e03a37ae87..038163bfa4 100644 --- a/qml/OrientedShell.qml +++ b/qml/OrientedShell.qml @@ -263,74 +263,81 @@ Item { shellSnapshot: shellSnapshot } - Shell { - id: shell - objectName: "shell" - width: root.width - height: root.height - orientation: root.angleToOrientation(orientationAngle) - orientations: root.orientations - nativeWidth: root.width - nativeHeight: root.height - mode: applicationArguments.mode - interactiveBlur: applicationArguments.interactiveBlur - hasMouse: pointerInputDevices > 0 - hasKeyboard: keyboardsModel.count > 0 - hasTouchscreen: touchScreensModel.count > 0 - supportsMultiColorLed: deviceConfiguration.supportsMultiColorLed - lightIndicators: root.lightIndicators - - // Since we dont have proper multiscreen support yet - // hardcode screen count to only show osk on this screen - // when it's the only one connected. - // FIXME once multiscreen has landed - oskEnabled: (!hasKeyboard && screens.count === 1) || - unity8Settings.alwaysShowOsk || forceOSKEnabled - - usageScenario: { - if (unity8Settings.usageMode === "Windowed") { - return "desktop"; - } else { - if (deviceConfiguration.category === "phone") { - return "phone"; + Item { + id: shellContainer + objectName: "shellContainer" + anchors.fill: parent + anchors.topMargin: !unity8Settings.disableTopMargin ? deviceConfiguration.topMargin : 0 + + Shell { + id: shell + objectName: "shell" + width: parent.width + height: parent.height + orientation: root.angleToOrientation(orientationAngle) + orientations: root.orientations + nativeWidth: parent.width + nativeHeight: parent.height + mode: applicationArguments.mode + interactiveBlur: applicationArguments.interactiveBlur + hasMouse: pointerInputDevices > 0 + hasKeyboard: keyboardsModel.count > 0 + hasTouchscreen: touchScreensModel.count > 0 + supportsMultiColorLed: deviceConfiguration.supportsMultiColorLed + lightIndicators: root.lightIndicators + + // Since we dont have proper multiscreen support yet + // hardcode screen count to only show osk on this screen + // when it's the only one connected. + // FIXME once multiscreen has landed + oskEnabled: (!hasKeyboard && screens.count === 1) || + unity8Settings.alwaysShowOsk || forceOSKEnabled + + usageScenario: { + if (unity8Settings.usageMode === "Windowed") { + return "desktop"; } else { - return "tablet"; + if (deviceConfiguration.category === "phone") { + return "phone"; + } else { + return "tablet"; + } } } - } - property real transformRotationAngle - property real transformOriginX - property real transformOriginY + property real transformRotationAngle + property real transformOriginX + property real transformOriginY - transform: Rotation { - origin.x: shell.transformOriginX; origin.y: shell.transformOriginY; axis { x: 0; y: 0; z: 1 } - angle: shell.transformRotationAngle + transform: Rotation { + origin.x: shell.transformOriginX; origin.y: shell.transformOriginY; axis { x: 0; y: 0; z: 1 } + angle: shell.transformRotationAngle + } } - } - Rectangle { - id: shellCover - color: "black" - anchors.fill: parent - visible: false - } + Rectangle { + id: shellCover + color: "black" + anchors.fill: parent + visible: false + } - ItemSnapshot { - id: shellSnapshot - target: shell - visible: false - width: root.width - height: root.height - - property real transformRotationAngle - property real transformOriginX - property real transformOriginY - - transform: Rotation { - origin.x: shellSnapshot.transformOriginX; origin.y: shellSnapshot.transformOriginY; - axis { x: 0; y: 0; z: 1 } - angle: shellSnapshot.transformRotationAngle + ItemSnapshot { + id: shellSnapshot + target: shell + visible: false + width: parent.width + height: parent.height + + property real transformRotationAngle + property real transformOriginX + property real transformOriginY + + transform: Rotation { + origin.x: shellSnapshot.transformOriginX; origin.y: shellSnapshot.transformOriginY; + axis { x: 0; y: 0; z: 1 } + angle: shellSnapshot.transformRotationAngle + } } } } diff --git a/qml/Rotation/HalfLoopRotationAnimation.qml b/qml/Rotation/HalfLoopRotationAnimation.qml index 864e865766..bfab4e3f48 100644 --- a/qml/Rotation/HalfLoopRotationAnimation.qml +++ b/qml/Rotation/HalfLoopRotationAnimation.qml @@ -30,8 +30,8 @@ SequentialAnimation { ScriptAction { script: { info.transitioning = true; shell.orientationAngle = root.toAngle; - shell.x = (orientedShell.width - shell.width) / 2 - shell.y = (orientedShell.height - shell.height) / 2; + shell.x = (shellContainer.width - shell.width) / 2 + shell.y = (shellContainer.height - shell.height) / 2; shell.transformOriginX = shell.width / 2; shell.transformOriginY = shell.height / 2; shell.updateFocusedAppOrientation(); diff --git a/qml/Rotation/NinetyRotationAnimation.qml b/qml/Rotation/NinetyRotationAnimation.qml index f170e0048f..8ab612d6d0 100644 --- a/qml/Rotation/NinetyRotationAnimation.qml +++ b/qml/Rotation/NinetyRotationAnimation.qml @@ -24,8 +24,8 @@ SequentialAnimation { property var info property var shell - readonly property real fromY: fromAngle === 0 || fromAngle === 90 ? 0 : orientedShell.height - orientedShell.width; - readonly property real toY: toAngle === 0 || toAngle === 90 ? 0 : orientedShell.height - orientedShell.width; + readonly property real fromY: fromAngle === 0 || fromAngle === 90 ? 0 : shellContainer.height - shellContainer.width; + readonly property real toY: toAngle === 0 || toAngle === 90 ? 0 : shellContainer.height - shellContainer.width; readonly property bool flipShellDimensions: toAngle == 90 || toAngle == 270 ScriptAction { script: { @@ -33,10 +33,10 @@ SequentialAnimation { shell.orientationAngle = root.toAngle; shell.x = 0; - shell.width = flipShellDimensions ? orientedShell.height : orientedShell.width; - shell.height = flipShellDimensions ? orientedShell.width : orientedShell.height; - shell.transformOriginX = orientedShell.width / 2; - shell.transformOriginY = orientedShell.width / 2; + shell.width = flipShellDimensions ? shellContainer.height : shellContainer.width; + shell.height = flipShellDimensions ? shellContainer.width : shellContainer.height; + shell.transformOriginX = shellContainer.width / 2; + shell.transformOriginY = shellContainer.width / 2; shell.updateFocusedAppOrientation(); shellCover.visible = true; diff --git a/qml/Rotation/UpdateShellTransformations.qml b/qml/Rotation/UpdateShellTransformations.qml index 55812fb677..f9ba2085b9 100644 --- a/qml/Rotation/UpdateShellTransformations.qml +++ b/qml/Rotation/UpdateShellTransformations.qml @@ -25,17 +25,18 @@ ScriptAction { shell.transformRotationAngle = rotationAngle; // They must all be bindings as orientedShell's size can change + // ( shellContainer takes into account the margin in case a notch is present ) if (rotationAngle === 90 || rotationAngle === 270) { - shell.width = Qt.binding(function() { return orientedShell.height; }); - shell.height = Qt.binding(function() { return orientedShell.width; }); + shell.width = Qt.binding(function() { return shellContainer.height; }); + shell.height = Qt.binding(function() { return shellContainer.width; }); } else { - shell.width = Qt.binding(function() { return orientedShell.width; }); - shell.height = Qt.binding(function() { return orientedShell.height; }); + shell.width = Qt.binding(function() { return shellContainer.width; }); + shell.height = Qt.binding(function() { return shellContainer.height; }); } - shell.x = Qt.binding(function() { return (orientedShell.width - shell.width) / 2; }); - shell.y = Qt.binding(function() { return (orientedShell.height - shell.height) / 2; }); + shell.x = Qt.binding(function() { return (shellContainer.width - shell.width) / 2; }); + shell.y = Qt.binding(function() { return (shellContainer.height - shell.height) / 2; }); shell.transformOriginX = Qt.binding(function() { return shell.width / 2; }); shell.transformOriginY = Qt.binding(function() { return shell.height / 2; }); } diff --git a/tests/mocks/GSettings.1.0/fake_gsettings.cpp b/tests/mocks/GSettings.1.0/fake_gsettings.cpp index c72dec76a1..58f2a0645c 100644 --- a/tests/mocks/GSettings.1.0/fake_gsettings.cpp +++ b/tests/mocks/GSettings.1.0/fake_gsettings.cpp @@ -29,6 +29,7 @@ GSettingsControllerQml::GSettingsControllerQml() , m_edgeDragWidth(2) , m_enableIndicatorMenu(true) , m_oskSwitchVisible(false) + , m_disableTopMargin(false) { } @@ -173,6 +174,19 @@ void GSettingsControllerQml::setOskSwitchVisible(bool oskSwitchVisible) } } +bool GSettingsControllerQml::disableTopMargin() const +{ + return m_disableTopMargin; +} + +void GSettingsControllerQml::setDisableTopMargin(bool disableTopMargin) +{ + if (m_disableTopMargin != disableTopMargin) { + m_disableTopMargin = disableTopMargin; + Q_EMIT disableTopMarginChanged(disableTopMargin); + } +} + GSettingsSchemaQml::GSettingsSchemaQml(QObject *parent): QObject(parent) { } @@ -241,6 +255,8 @@ void GSettingsQml::componentComplete() this, &GSettingsQml::enableIndicatorMenuChanged); connect(GSettingsControllerQml::instance(), &GSettingsControllerQml::oskSwitchVisibleChanged, this, &GSettingsQml::oskSwitchVisibleChanged); + connect(GSettingsControllerQml::instance(), &GSettingsControllerQml::disableTopMarginChanged, + this, &GSettingsQml::disableTopMarginChanged); Q_EMIT disableHeightChanged(); Q_EMIT pictureUriChanged(); @@ -374,6 +390,14 @@ QVariant GSettingsQml::oskSwitchVisible() const return QVariant(); } +QVariant GSettingsQml::disableTopMargin() const +{ + if (m_valid && m_schema->id() == "com.canonical.Unity8") { + return GSettingsControllerQml::instance()->disableTopMargin(); + } + return QVariant(); +} + void GSettingsQml::setLifecycleExemptAppids(const QVariant &appIds) { if (m_valid && m_schema->id() == "com.canonical.qtmir") { @@ -415,3 +439,10 @@ void GSettingsQml::setOskSwitchVisible(const QVariant &oskSwitchVisible) GSettingsControllerQml::instance()->setOskSwitchVisible(oskSwitchVisible.toBool()); } } + +void GSettingsQml::setDisableTopMargin(const QVariant &disableTopMargin) +{ + if (m_valid && m_schema->id() == "com.canonical.Unity8") { + GSettingsControllerQml::instance()->setDisableTopMargin(disableTopMargin.toBool()); + } +} diff --git a/tests/mocks/GSettings.1.0/fake_gsettings.h b/tests/mocks/GSettings.1.0/fake_gsettings.h index aef88a8fed..4ac8ab326e 100644 --- a/tests/mocks/GSettings.1.0/fake_gsettings.h +++ b/tests/mocks/GSettings.1.0/fake_gsettings.h @@ -59,6 +59,7 @@ class GSettingsQml: public QObject, public QQmlParserStatus Q_PROPERTY(QVariant edgeDragWidth READ edgeDragWidth WRITE setEdgeDragWidth NOTIFY edgeDragWidthChanged) Q_PROPERTY(QVariant enableIndicatorMenu READ enableIndicatorMenu WRITE setEnableIndicatorMenu NOTIFY enableIndicatorMenuChanged) Q_PROPERTY(QVariant oskSwitchVisible READ oskSwitchVisible WRITE setOskSwitchVisible NOTIFY oskSwitchVisibleChanged) + Q_PROPERTY(QVariant disableTopMargin READ disableTopMargin WRITE setDisableTopMargin NOTIFY disableTopMarginChanged) public: GSettingsQml(QObject *parent = nullptr); @@ -77,6 +78,7 @@ class GSettingsQml: public QObject, public QQmlParserStatus QVariant edgeDragWidth() const; QVariant enableIndicatorMenu() const; QVariant oskSwitchVisible() const; + QVariant disableTopMargin() const; void setDisableHeight(const QVariant &val); void setPictureUri(const QVariant &str); @@ -88,6 +90,7 @@ class GSettingsQml: public QObject, public QQmlParserStatus void setEdgeDragWidth(const QVariant &edgeDragWidth); void setEnableIndicatorMenu(const QVariant &enableIndicatorMenu); void setOskSwitchVisible(const QVariant &oskSwitchVisible); + void setDisableTopMargin(const QVariant &disableTopMargin); Q_SIGNALS: void disableHeightChanged(); @@ -101,6 +104,7 @@ class GSettingsQml: public QObject, public QQmlParserStatus void edgeDragWidthChanged(); void enableIndicatorMenuChanged(); void oskSwitchVisibleChanged(); + void disableTopMarginChanged(); private: GSettingsSchemaQml* m_schema; @@ -147,6 +151,9 @@ class GSettingsControllerQml: public QObject bool oskSwitchVisible() const; Q_INVOKABLE void setOskSwitchVisible(bool oskSwitchVisible); + bool disableTopMargin() const; + Q_INVOKABLE void setDisableTopMargin(bool disableTopMargin); + Q_SIGNALS: void disableHeightChanged(); void pictureUriChanged(const QString&); @@ -158,6 +165,7 @@ class GSettingsControllerQml: public QObject void edgeDragWidthChanged(uint edgeDragWidth); void enableIndicatorMenuChanged(bool enableIndicatorMenu); void oskSwitchVisibleChanged(bool oskSwitchVisible); + void disableTopMarginChanged(bool disableTopMargin); private: GSettingsControllerQml(); @@ -172,6 +180,7 @@ class GSettingsControllerQml: public QObject uint m_edgeDragWidth; bool m_enableIndicatorMenu; bool m_oskSwitchVisible; + bool m_disableTopMargin; static GSettingsControllerQml* s_controllerInstance; QList m_registeredGSettings; diff --git a/tests/plugins/Utils/DeviceConfigParserTest.cpp b/tests/plugins/Utils/DeviceConfigParserTest.cpp index bc8b0cdbaf..cfbe979652 100644 --- a/tests/plugins/Utils/DeviceConfigParserTest.cpp +++ b/tests/plugins/Utils/DeviceConfigParserTest.cpp @@ -44,6 +44,7 @@ private Q_SLOTS: QCOMPARE(p.landscapeOrientation(), Qt::LandscapeOrientation); QCOMPARE(p.invertedLandscapeOrientation(), Qt::InvertedLandscapeOrientation); QCOMPARE(p.supportsMultiColorLed(), true); + QCOMPARE(p.topMargin(), 0); } void testCustomFile() { @@ -54,6 +55,7 @@ private Q_SLOTS: s.setValue("SupportedOrientations", QStringList() << "Portrait" << "Landscape" << "InvertedLandscape"); s.setValue("PrimaryOrientation", "InvertedLandscape"); s.setValue("SupportsMultiColorLed", false); + s.setValue("TopMargin", 20); s.sync(); qputenv("XDG_CONFIG_HOME", dir.path().toUtf8()); @@ -67,6 +69,7 @@ private Q_SLOTS: QCOMPARE(p.landscapeOrientation(), Qt::LandscapeOrientation); QCOMPARE(p.invertedLandscapeOrientation(), Qt::InvertedLandscapeOrientation); QCOMPARE(p.supportsMultiColorLed(), false); + QCOMPARE(p.topMargin(), 20); } }; diff --git a/tests/qmltests/tst_OrientedShell.qml b/tests/qmltests/tst_OrientedShell.qml index 50eebfdefa..626571a4bc 100644 --- a/tests/qmltests/tst_OrientedShell.qml +++ b/tests/qmltests/tst_OrientedShell.qml @@ -109,6 +109,22 @@ Rectangle { primaryOrientationAngle: 0 } }, + State { + name: "has-notch" + PropertyChanges { + target: shellRect + width: units.gu(40) + height: units.gu(71) + } + PropertyChanges { + target: root + physicalOrientation0: Qt.PortraitOrientation + physicalOrientation90: Qt.InvertedLandscapeOrientation + physicalOrientation180: Qt.InvertedPortraitOrientation + physicalOrientation270: Qt.LandscapeOrientation + primaryOrientationAngle: 0 + } + }, State { name: "manta" PropertyChanges { @@ -320,7 +336,7 @@ Rectangle { anchors { left: parent.left; right: parent.right } activeFocusOnPress: false text: "Device Name" - model: ["mako", "manta", "flo", "desktop"] + model: ["mako", "has-notch", "manta", "flo", "desktop"] onSelectedIndexChanged: { destroyShell(); applicationArguments.deviceName = model[selectedIndex]; @@ -1700,6 +1716,23 @@ Rectangle { MockInputDeviceBackend.addMockDevice("/kbd0", InputInfo.Keyboard); tryCompare(promptKeyboard, "visible", true); + function test_disableTopMarginSetting_data() { + return [ + {tag: "top margin enabled", disabled: false, expectedMargin: 20}, + {tag: "top margin disabled", disabled: true, expectedMargin: 0} + ] + } + + function test_disableTopMarginSetting(data) { + /* Test the GSettings option to disable the topMargin + * that reverts to the previous behavior when a + * topMargin is specified for the notch. + */ + var orientedShell = loadShell("has-notch"); + var shellContainer = findChild(orientedShell, "shellContainer"); + GSettingsController.setDisableTopMargin(data.disabled); + + tryCompare(shellContainer.anchors, "topMargin", data.expectedMargin); } } }