on xenial help sign in
Don't stretch application surfaces when resizing
commitb71b294a7824af49b7ee14c480de3bf4d67a2161 authorDaniel d'Andrada <daniel.dandrada@canonical.com> dateThu 22 Oct 2015, 15:49 (11 years ago) parent5c86320
12 files changed+304−47
qml/Stages/ApplicationWindow.qml+27−1 qml/Stages/DecoratedWindow.qml+7−2 qml/Stages/DesktopStage.qml+6−6 qml/Stages/SessionContainer.qml+25−1 qml/Stages/SurfaceContainer.qml+56−3 qml/Stages/WindowResizeArea.qml+46−18 tests/mocks/Unity/Application/MirSurface.cpp+52 tests/mocks/Unity/Application/MirSurface.h+16 tests/mocks/Unity/Application/MirSurfaceItem.cpp+14 tests/mocks/Unity/Application/MirSurfaceItem.h+5 tests/qmltests/Stages/tst_DesktopStage.qml+18−2 tests/qmltests/Stages/tst_WindowResizeArea.qml+32−14
qml/Stages/ApplicationWindow.qml+27−1
@@ -32,6 +32,8 @@ FocusScope {
3232 property QtObject application
3333 property int surfaceOrientationAngle
3434 property alias resizeSurface: sessionContainer.resizeSurface
35+ property int requestedWidth: -1
36+ property int requestedHeight: -1
3537
3638 QtObject {
3739 id: d
@@ -133,7 +135,9 @@ FocusScope {
133135 id: sessionContainer
134136 // A fake application might not even have a session property.
135137 session: application && application.session ? application.session : null
136− anchors.fill: parent
138+
139+ requestedWidth: root.requestedWidth
140+ requestedHeight: root.requestedHeight
137141
138142 surfaceOrientationAngle: application && application.rotatesWindowContents ? root.surfaceOrientationAngle : 0
139143
@@ -148,6 +152,28 @@ FocusScope {
148152 focus: true
149153 }
150154
155+ // SessionContainer size drives ApplicationWindow size
156+ Binding {
157+ target: root; property: "width"
158+ value: stateGroup.state === "surface" ? sessionContainer.width : root.requestedWidth
159+ when: root.requestedWidth >= 0
160+ }
161+ Binding {
162+ target: root; property: "height"
163+ value: stateGroup.state === "surface" ? sessionContainer.height : root.requestedHeight
164+ when: root.requestedHeight >= 0
165+ }
166+
167+ // ApplicationWindow size drives SessionContainer size
168+ Binding {
169+ target: sessionContainer; property: "width"; value: root.width
170+ when: root.requestedWidth < 0
171+ }
172+ Binding {
173+ target: sessionContainer; property: "height"; value: root.height
174+ when: root.requestedHeight < 0
175+ }
176+
151177 StateGroup {
152178 id: stateGroup
153179 objectName: "applicationWindowStateGroup"
qml/Stages/DecoratedWindow.qml+7−2
@@ -23,6 +23,9 @@ import Unity.Application 0.1
2323FocusScope {
2424 id: root
2525
26+ width: applicationWindow.width
27+ height: (root.decorationShown ? decoration.height : 0) + applicationWindow.height
28+
2629 property alias window: applicationWindow
2730 property alias application: applicationWindow.application
2831 property alias active: decoration.active
@@ -31,6 +34,9 @@ FocusScope {
3134 property bool highlightShown: false
3235 property real shadowOpacity: 1
3336
37+ property alias requestedWidth: applicationWindow.requestedWidth
38+ property real requestedHeight
39+
3440 signal close()
3541 signal maximize()
3642 signal minimize()
@@ -80,8 +86,7 @@ FocusScope {
8086 anchors.top: parent.top
8187 anchors.topMargin: decoration.height
8288 anchors.left: parent.left
83− width: root.width
84− height: root.height - decoration.height
89+ requestedHeight: root.requestedHeight - (root.decorationShown ? decoration.height : 0)
8590 interactive: true
8691 focus: true
8792 }
qml/Stages/DesktopStage.qml+6−6
@@ -134,8 +134,10 @@ Rectangle {
134134 id: appDelegate
135135 z: ApplicationManager.count - index
136136 y: units.gu(3)
137− width: units.gu(60)
138− height: units.gu(50)
137+ width: decoratedWindow.width
138+ height: decoratedWindow.height
139+ property alias requestedWidth: decoratedWindow.requestedWidth
140+ property alias requestedHeight: decoratedWindow.requestedHeight
139141 focus: model.appId === priv.focusedAppId
140142
141143 property bool maximized: false
@@ -177,7 +179,7 @@ Rectangle {
177179 },
178180 State {
179181 name: "maximized"; when: appDelegate.maximized
180− PropertyChanges { target: appDelegate; x: 0; y: 0; width: root.width; height: root.height }
182+ PropertyChanges { target: appDelegate; x: 0; y: 0; requestedWidth: root.width; requestedHeight: root.height }
181183 },
182184 State {
183185 name: "minimized"; when: appDelegate.minimized
@@ -188,7 +190,7 @@ Rectangle {
188190 Transition {
189191 from: "maximized,minimized,normal,"
190192 to: "maximized,minimized,normal,"
191− PropertyAnimation { target: appDelegate; properties: "x,y,opacity,width,height,scale" }
193+ PropertyAnimation { target: appDelegate; properties: "x,y,opacity,requestedWidth,requestedHeight,scale" }
192194 },
193195 Transition {
194196 from: ""
@@ -227,8 +229,6 @@ Rectangle {
227229 objectName: "decoratedWindow"
228230 anchors.left: appDelegate.left
229231 anchors.top: appDelegate.top
230− width: appDelegate.width
231− height: appDelegate.height
232232 application: ApplicationManager.get(index)
233233 active: ApplicationManager.focusedApplicationId === model.appId
234234 focus: true
qml/Stages/SessionContainer.qml+25−1
@@ -29,13 +29,37 @@ FocusScope {
2929 property alias surfaceOrientationAngle: _surfaceContainer.surfaceOrientationAngle
3030 property alias resizeSurface: _surfaceContainer.resizeSurface
3131
32+ property int requestedWidth: -1
33+ property int requestedHeight: -1
34+
3235 readonly property alias surfaceContainer: _surfaceContainer
3336 SurfaceContainer {
3437 id: _surfaceContainer
35− anchors.fill: parent
38+ requestedWidth: root.requestedWidth
39+ requestedHeight: root.requestedHeight
3640 surface: session ? session.surface : null
3741 }
3842
43+ // SurfaceContainer size drives SessionContainer size
44+ Binding {
45+ target: root; property: "width"; value: _surfaceContainer.width
46+ when: root.requestedWidth >= 0
47+ }
48+ Binding {
49+ target: root; property: "height"; value: _surfaceContainer.height
50+ when: root.requestedHeight >= 0
51+ }
52+
53+ // SessionContainer size drives SurfaceContainer size
54+ Binding {
55+ target: _surfaceContainer; property: "width"; value: root.width
56+ when: root.requestedWidth < 0
57+ }
58+ Binding {
59+ target: _surfaceContainer; property: "height"; value: root.height
60+ when: root.requestedHeight < 0
61+ }
62+
3963 Repeater {
4064 id: childSessionsRepeater
4165 model: root.childSessions
qml/Stages/SurfaceContainer.qml+56−3
@@ -30,6 +30,9 @@ FocusScope {
3030 property int surfaceOrientationAngle: 0
3131 property bool resizeSurface: true
3232
33+ property int requestedWidth: -1
34+ property int requestedHeight: -1
35+
3336 onSurfaceChanged: {
3437 if (surface) {
3538 surfaceItem.surface = surface;
@@ -53,16 +56,66 @@ FocusScope {
5356
5457 consumesInput: true
5558
56− surfaceWidth: root.resizeSurface ? width : -1
57− surfaceHeight: root.resizeSurface ? height : -1
59+ surfaceWidth: {
60+ if (root.resizeSurface) {
61+ if (root.requestedWidth >= 0) {
62+ return root.requestedWidth;
63+ } else {
64+ return width;
65+ }
66+ } else {
67+ return -1;
68+ }
69+ }
5870
59− anchors.fill: root
71+ surfaceHeight: {
72+ if (root.resizeSurface) {
73+ if (root.requestedHeight >= 0) {
74+ return root.requestedHeight;
75+ } else {
76+ return height;
77+ }
78+ } else {
79+ return -1;
80+ }
81+ }
82+
83+ fillMode: root.resizeSurface ? MirSurfaceItem.PadOrCrop : MirSurfaceItem.Stretch
6084 enabled: root.interactive
6185 focus: true
6286 antialiasing: !root.interactive
6387 orientationAngle: root.surfaceOrientationAngle
6488 }
6589
90+ // MirSurface size drives SurfaceContainer size
91+ Binding {
92+ target: surfaceItem; property: "width"; value: root.surface ? root.surface.size.width : 0
93+ when: root.requestedWidth >= 0 && root.surface
94+ }
95+ Binding {
96+ target: surfaceItem; property: "height"; value: root.surface ? root.surface.size.height : 0
97+ when: root.requestedHeight >= 0 && root.surface
98+ }
99+ Binding {
100+ target: root; property: "width"; value: surfaceItem.width
101+ when: root.requestedWidth >= 0
102+ }
103+ Binding {
104+ target: root; property: "height"; value: surfaceItem.height
105+ when: root.requestedHeight >= 0
106+ }
107+
108+ // SurfaceContainer size drives MirSurface size
109+ Binding {
110+ target: surfaceItem; property: "width"; value: root.width
111+ when: root.requestedWidth < 0
112+ }
113+ Binding {
114+ target: surfaceItem; property: "height"; value: root.height
115+ when: root.requestedHeight < 0
116+ }
117+
118+
66119 TouchGate {
67120 targetItem: surfaceItem
68121 anchors.fill: root
qml/Stages/WindowResizeArea.qml+46−18
@@ -35,14 +35,26 @@ MouseArea {
3535 property int borderThickness: 0
3636 property int minWidth: 0
3737 property int minHeight: 0
38+ property int defaultWidth: units.gu(60)
39+ property int defaultHeight: units.gu(50)
3840
3941 Component.onCompleted: {
40− var windowState = windowStateStorage.getGeometry(root.windowId, Qt.rect(target.x, target.y, target.width, target.height))
41− if (windowState !== undefined) {
42− target.x = windowState.x
43− target.y = windowState.y
44− target.width = windowState.width
45− target.height = windowState.height
42+ var windowState = windowStateStorage.getGeometry(root.windowId,
43+ Qt.rect(target.x, target.y, defaultWidth, defaultHeight) /* default geometry */);
44+
45+ target.x = windowState.x;
46+ target.y = windowState.y;
47+
48+ if (windowState.width >= minWidth) {
49+ target.requestedWidth = windowState.width;
50+ } else {
51+ target.requestedWidth = minWidth;
52+ }
53+
54+ if (windowState.height >= minHeight) {
55+ target.requestedHeight = windowState.height;
56+ } else {
57+ target.requestedHeight = minHeight;
4658 }
4759 }
4860
@@ -64,6 +76,8 @@ MouseArea {
6476 property real startY
6577 property real startWidth
6678 property real startHeight
79+ property real currentWidth
80+ property real currentHeight
6781
6882 property string cursorName: {
6983 if (root.containsMouse || root.pressed) {
@@ -114,6 +128,8 @@ MouseArea {
114128 d.startY = target.y;
115129 d.startWidth = target.width;
116130 d.startHeight = target.height;
131+ d.currentWidth = target.width;
132+ d.currentHeight = target.height;
117133 d.dragging = true;
118134 } else {
119135 d.dragging = false;
@@ -146,37 +162,49 @@ MouseArea {
146162 if (d.leftBorder) {
147163 var newTargetX = d.startX + deltaX;
148164 if (target.x + target.width > newTargetX + minWidth) {
149− target.width = target.x + target.width - newTargetX;
150− target.x = newTargetX;
165+ target.requestedWidth = target.x + target.width - newTargetX;
151166 } else {
152− target.x = target.x + target.width - minWidth;
153− target.width = minWidth;
167+ target.requestedWidth = minWidth;
154168 }
155169
156170 } else if (d.rightBorder) {
157171 if (d.startWidth + deltaX >= minWidth) {
158− target.width = d.startWidth + deltaX;
172+ target.requestedWidth = d.startWidth + deltaX;
159173 } else {
160− target.width = minWidth;
174+ target.requestedWidth = minWidth;
161175 }
162176 }
163177
164178 if (d.topBorder) {
165179 var newTargetY = d.startY + deltaY;
166180 if (target.y + target.height > newTargetY + minHeight) {
167− target.height = target.y + target.height - newTargetY;
168− target.y = newTargetY;
181+ target.requestedHeight = target.y + target.height - newTargetY;
169182 } else {
170− target.y = target.y + target.height - minHeight;
171− target.height = minHeight;
183+ target.requestedHeight = minHeight;
172184 }
173185
174186 } else if (d.bottomBorder) {
175187 if (d.startHeight + deltaY >= minHeight) {
176− target.height = d.startHeight + deltaY;
188+ target.requestedHeight = d.startHeight + deltaY;
177189 } else {
178− target.height = minHeight;
190+ target.requestedHeight = minHeight;
191+ }
192+ }
193+ }
194+
195+ Connections {
196+ target: root.target
197+ onWidthChanged: {
198+ if (root.pressed && d.leftBorder) {
199+ target.x += d.currentWidth - target.width;
200+ }
201+ d.currentWidth = target.width;
202+ }
203+ onHeightChanged: {
204+ if (root.pressed && d.topBorder) {
205+ target.y += d.currentHeight - target.height;
179206 }
207+ d.currentHeight = target.height;
180208 }
181209 }
182210}
tests/mocks/Unity/Application/MirSurface.cpp+52
@@ -35,8 +35,12 @@ MirSurface::MirSurface(const QString& name,
3535 , m_activeFocus(false)
3636 , m_width(-1)
3737 , m_height(-1)
38+ , m_slowToResize(false)
3839{
3940// qDebug() << "MirSurface::MirSurface() " << name;
41+ m_delayedResizeTimer.setInterval(600);
42+ m_delayedResizeTimer.setSingleShot(true);
43+ connect(&m_delayedResizeTimer, &QTimer::timeout, this, &MirSurface::applyDelayedResize);
4044}
4145
4246MirSurface::~MirSurface()
@@ -169,8 +173,39 @@ int MirSurface::height() const
169173}
170174
171175void MirSurface::resize(int width, int height)
176+{
177+ if (m_slowToResize) {
178+ if (!m_delayedResizeTimer.isActive()) {
179+ m_delayedResize.setWidth(width);
180+ m_delayedResize.setHeight(height);
181+ m_delayedResizeTimer.start();
182+ } else {
183+ m_pendingResize.setWidth(width);
184+ m_pendingResize.setHeight(height);
185+ }
186+ } else {
187+ doResize(width, height);
188+ }
189+}
190+
191+void MirSurface::applyDelayedResize()
192+{
193+ doResize(m_delayedResize.width(), m_delayedResize.height());
194+ m_delayedResize.setWidth(-1);
195+ m_delayedResize.setHeight(-1);
196+
197+ if (m_pendingResize.isValid()) {
198+ QSize size = m_pendingResize;
199+ m_pendingResize.setWidth(-1);
200+ m_pendingResize.setHeight(-1);
201+ resize(size.width(), size.height());
202+ }
203+}
204+
205+void MirSurface::doResize(int width, int height)
172206{
173207 bool changed = false;
208+
174209 if (width != m_width) {
175210 m_width = width;
176211 Q_EMIT widthChanged();
@@ -187,3 +222,20 @@ void MirSurface::resize(int width, int height)
187222 Q_EMIT sizeChanged(QSize(width, height));
188223 }
189224}
225+
226+bool MirSurface::isSlowToResize() const
227+{
228+ return m_slowToResize;
229+}
230+
231+void MirSurface::setSlowToResize(bool value)
232+{
233+ if (m_slowToResize != value) {
234+ m_slowToResize = value;
235+ Q_EMIT slowToResizeChanged();
236+ if (!m_slowToResize && m_delayedResizeTimer.isActive()) {
237+ m_delayedResizeTimer.stop();
238+ applyDelayedResize();
239+ }
240+ }
241+}
tests/mocks/Unity/Application/MirSurface.h+16
@@ -18,6 +18,7 @@
1818#define MOCK_MIR_SURFACE_H
1919
2020#include <QObject>
21+#include <QTimer>
2122#include <QUrl>
2223
2324// unity-api
@@ -32,6 +33,7 @@ class MirSurface : public unity::shell::application::MirSurfaceInterface
3233 Q_PROPERTY(int width READ width NOTIFY widthChanged)
3334 Q_PROPERTY(int height READ height NOTIFY heightChanged)
3435 Q_PROPERTY(bool activeFocus READ activeFocus NOTIFY activeFocusChanged)
36+ Q_PROPERTY(bool slowToResize READ isSlowToResize WRITE setSlowToResize NOTIFY slowToResizeChanged)
3537
3638public:
3739 MirSurface(const QString& name,
@@ -73,6 +75,9 @@ public:
7375 int width() const;
7476 int height() const;
7577
78+ bool isSlowToResize() const;
79+ void setSlowToResize(bool value);
80+
7681 /////
7782 // internal mock stuff
7883
@@ -90,13 +95,19 @@ Q_SIGNALS:
9095 void orientationAngleChanged(Mir::OrientationAngle angle);
9196 void widthChanged();
9297 void heightChanged();
98+ void slowToResizeChanged();
9399
94100 ////
95101 // internal mock stuff
96102 void screenshotUrlChanged(QUrl);
97103 void activeFocusChanged(bool);
98104
105+private Q_SLOTS:
106+ void applyDelayedResize();
107+
99108private:
109+ void doResize(int width, int height);
110+
100111 const QString m_name;
101112 const Mir::Type m_type;
102113 Mir::State m_state;
@@ -108,6 +119,11 @@ private:
108119 bool m_activeFocus;
109120 int m_width;
110121 int m_height;
122+
123+ bool m_slowToResize;
124+ QTimer m_delayedResizeTimer;
125+ QSize m_delayedResize;
126+ QSize m_pendingResize;
111127};
112128
113129#endif // MOCK_MIR_SURFACE_H
tests/mocks/Unity/Application/MirSurfaceItem.cpp+14
@@ -37,6 +37,7 @@ MirSurfaceItem::MirSurfaceItem(QQuickItem *parent)
3737 , m_surfaceHeight(0)
3838 , m_touchPressCount(0)
3939 , m_touchReleaseCount(0)
40+ , m_fillMode(Stretch)
4041{
4142// qDebug() << "MirSurfaceItem::MirSurfaceItem() " << (void*)(this) << name();
4243 setAcceptedMouseButtons(Qt::LeftButton | Qt::MiddleButton | Qt::RightButton |
@@ -295,3 +296,16 @@ void MirSurfaceItem::updateSurfaceSize()
295296 m_qmlSurface->resize(m_surfaceWidth, m_surfaceHeight);
296297 }
297298}
299+
300+MirSurfaceItem::FillMode MirSurfaceItem::fillMode() const
301+{
302+ return m_fillMode;
303+}
304+
305+void MirSurfaceItem::setFillMode(FillMode value)
306+{
307+ if (m_fillMode != value) {
308+ m_fillMode = value;
309+ Q_EMIT fillModeChanged(m_fillMode);
310+ }
311+}
tests/mocks/Unity/Application/MirSurfaceItem.h+5
@@ -62,6 +62,9 @@ public:
6262 int surfaceHeight() const override;
6363 void setSurfaceHeight(int value) override;
6464
65+ FillMode fillMode() const override;
66+ void setFillMode(FillMode value) override;
67+
6568 /////
6669 // For use in qml tests
6770
@@ -106,6 +109,8 @@ private:
106109 int m_touchPressCount;
107110 int m_touchReleaseCount;
108111
112+ FillMode m_fillMode;
113+
109114 friend class SurfaceManager;
110115};
111116
tests/qmltests/Stages/tst_DesktopStage.qml+18−2
@@ -15,9 +15,10 @@
1515 */
1616
1717import QtQuick 2.0
18+import QtQuick.Layouts 1.1
1819import QtTest 1.0
19−import Ubuntu.Components 1.1
20−import Ubuntu.Components.ListItems 1.0 as ListItem
20+import Ubuntu.Components 1.3
21+import Ubuntu.Components.ListItems 1.3
2122import Unity.Application 0.1
2223import Unity.Test 0.1
2324import Utils 0.1
@@ -78,6 +79,21 @@ Item {
7879 Column {
7980 anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
8081 spacing: units.gu(1)
82+
83+ Button {
84+ color: "white"
85+ text: "Make surface slow to resize"
86+ activeFocusOnPress: false
87+ onClicked: {
88+ if (ApplicationManager.focusedApplicationId) {
89+ var surface = ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).session.surface;
90+ surface.slowToResize = true;
91+ }
92+ }
93+ }
94+
95+ Divider {}
96+
8197 Repeater {
8298 model: ApplicationManager.availableApplications
8399 ApplicationCheckBox {
tests/qmltests/Stages/tst_WindowResizeArea.qml+32−14
@@ -29,8 +29,6 @@ Item {
2929 height: units.gu(60)
3030 width: units.gu(60)
3131
32− property var fakeWindow: windowLoader.item
33−
3432 Component {
3533 id: fakeWindowComponent
3634
@@ -40,12 +38,10 @@ Item {
4038 property alias minHeight: windowResizeArea.minHeight
4139 x: units.gu(20)
4240 y: units.gu(20)
43− height: units.gu(20)
44− width: units.gu(20)
45− property int windowHeight: height
46− property int windowWidth: width
47− onWindowHeightChanged: height = windowHeight
48− onWindowWidthChanged: width = windowWidth
41+ width: requestedWidth
42+ height: requestedHeight
43+ property real requestedWidth
44+ property real requestedHeight
4945
5046 WindowResizeArea {
5147 id: windowResizeArea
@@ -53,6 +49,8 @@ Item {
5349 borderThickness: units.gu(2)
5450 minWidth: units.gu(15)
5551 minHeight: units.gu(10)
52+ defaultWidth: units.gu(20)
53+ defaultHeight: units.gu(20)
5654 windowId: "test-window-id"
5755 }
5856
@@ -75,22 +73,41 @@ Item {
7573 Loader {
7674 id: windowLoader
7775 sourceComponent: fakeWindowComponent
76+ active: windowLoaderCheckbox.checked
7877 }
7978
80− MouseTouchEmulationCheckbox {
81− checked: false
82− color: "black"
79+ Column {
80+ MouseTouchEmulationCheckbox {
81+ checked: false
82+ color: "black"
83+ }
84+ RowLayout {
85+ Layout.fillWidth: true
86+ CheckBox {
87+ id: windowLoaderCheckbox
88+ checked: true
89+ activeFocusOnPress: false
90+ }
91+ Label {
92+ id: label
93+ color: "black"
94+ text: "Window loader active"
95+ anchors.verticalCenter: parent.verticalCenter
96+ }
97+ }
8398 }
8499
85100 UnityTestCase {
86101 name: "WindowResizeArea"
87102 when: windowShown
88103
104+ property var fakeWindow: windowLoader.item
105+
89106 function init() {
90107 fakeWindow.x = units.gu(20)
91108 fakeWindow.y = units.gu(20)
92− fakeWindow.width = units.gu(20)
93− fakeWindow.height = units.gu(20)
109+ fakeWindow.requestedWidth = units.gu(20)
110+ fakeWindow.requestedHeight = units.gu(20)
94111 }
95112
96113 function test_resizeWindowRightBottom_data() {
@@ -176,11 +193,12 @@ Item {
176193 function test_resizeSmallerAndLarger_data() {
177194 return [
178195 { tag: "topLeft", startX: -1, startY: -1, dx: units.gu(15), dy: units.gu(15) },
179− { tag: "bottomRight", startX: fakeWindow.width + 1, startY: fakeWindow.height + 1, dx: -units.gu(15), dy: -units.gu(15) }
196+ { tag: "bottomRight", startX: units.gu(20) + 1, startY: units.gu(20) + 1, dx: -units.gu(15), dy: -units.gu(15) }
180197 ]
181198 }
182199
183200 function test_resizeSmallerAndLarger(data) {
201+ console.log("windowLoader="+windowLoader + ", windowLoader.item="+windowLoader.item + ", windowLoader.active="+windowLoader.active);
184202 var initialWindowX = fakeWindow.x;
185203 var initialWindowY = fakeWindow.y;
186204 var initialWindowWidth = fakeWindow.width