on xenial help sign in
Don't stretch application surfaces when resizing Fixes: #1497083
853d6deDaniel d'Andrada
qml/Stages/ApplicationWindow.qml+27−1
@@ -34,6 +34,8 @@ FocusScope {
34 property QtObject application
34 property QtObject application
35 property int surfaceOrientationAngle
35 property int surfaceOrientationAngle
36 property alias resizeSurface: sessionContainer.resizeSurface
36 property alias resizeSurface: sessionContainer.resizeSurface
37+ property int requestedWidth: -1
38+ property int requestedHeight: -1
37
39
38 QtObject {
40 QtObject {
39 id: d
41 id: d
@@ -135,7 +137,9 @@ FocusScope {
135 id: sessionContainer
137 id: sessionContainer
136 // A fake application might not even have a session property.
138 // A fake application might not even have a session property.
137 session: application && application.session ? application.session : null
139 session: application && application.session ? application.session : null
138− anchors.fill: parent
140+
141+ requestedWidth: root.requestedWidth
142+ requestedHeight: root.requestedHeight
139
143
140 surfaceOrientationAngle: application && application.rotatesWindowContents ? root.surfaceOrientationAngle : 0
144 surfaceOrientationAngle: application && application.rotatesWindowContents ? root.surfaceOrientationAngle : 0
141
145
@@ -150,6 +154,28 @@ FocusScope {
150 focus: true
154 focus: true
151 }
155 }
152
156
157+ // SessionContainer size drives ApplicationWindow size
158+ Binding {
159+ target: root; property: "width"
160+ value: stateGroup.state === "surface" ? sessionContainer.width : root.requestedWidth
161+ when: root.requestedWidth >= 0
162+ }
163+ Binding {
164+ target: root; property: "height"
165+ value: stateGroup.state === "surface" ? sessionContainer.height : root.requestedHeight
166+ when: root.requestedHeight >= 0
167+ }
168+
169+ // ApplicationWindow size drives SessionContainer size
170+ Binding {
171+ target: sessionContainer; property: "width"; value: root.width
172+ when: root.requestedWidth < 0
173+ }
174+ Binding {
175+ target: sessionContainer; property: "height"; value: root.height
176+ when: root.requestedHeight < 0
177+ }
178+
153 StateGroup {
179 StateGroup {
154 id: stateGroup
180 id: stateGroup
155 objectName: "applicationWindowStateGroup"
181 objectName: "applicationWindowStateGroup"
qml/Stages/DecoratedWindow.qml+7−2
@@ -23,6 +23,9 @@ import Unity.Application 0.1
23FocusScope {
23FocusScope {
24 id: root
24 id: root
25
25
26+ width: applicationWindow.width
27+ height: (root.decorationShown ? decoration.height : 0) + applicationWindow.height
28+
26 property alias window: applicationWindow
29 property alias window: applicationWindow
27 property alias application: applicationWindow.application
30 property alias application: applicationWindow.application
28 property alias active: decoration.active
31 property alias active: decoration.active
@@ -32,6 +35,9 @@ FocusScope {
32 property bool highlightShown: false
35 property bool highlightShown: false
33 property real shadowOpacity: 1
36 property real shadowOpacity: 1
34
37
38+ property alias requestedWidth: applicationWindow.requestedWidth
39+ property real requestedHeight
40+
35 signal close()
41 signal close()
36 signal maximize()
42 signal maximize()
37 signal minimize()
43 signal minimize()
@@ -82,8 +88,7 @@ FocusScope {
82 anchors.top: parent.top
88 anchors.top: parent.top
83 anchors.topMargin: decoration.height
89 anchors.topMargin: decoration.height
84 anchors.left: parent.left
90 anchors.left: parent.left
85− width: root.width
91+ requestedHeight: root.requestedHeight - (root.decorationShown ? decoration.height : 0)
86− height: root.height - decoration.height
87 interactive: true
92 interactive: true
88 focus: true
93 focus: true
89 }
94 }
qml/Stages/DesktopStage.qml+9−9
@@ -219,9 +219,11 @@ AbstractStage {
219 objectName: "appDelegate_" + appId
219 objectName: "appDelegate_" + appId
220 z: ApplicationManager.count - index
220 z: ApplicationManager.count - index
221 y: units.gu(3)
221 y: units.gu(3)
222− width: units.gu(60)
222+ width: decoratedWindow.width
223− height: units.gu(50)
223+ height: decoratedWindow.height
224+ property alias requestedWidth: decoratedWindow.requestedWidth
225+ property alias requestedHeight: decoratedWindow.requestedHeight
224− focus: appId === priv.focusedAppId
226+ focus: model.appId === priv.focusedAppId
225
227
226 QtObject {
228 QtObject {
227 id: appDelegatePrivate
229 id: appDelegatePrivate
@@ -325,7 +327,7 @@ AbstractStage {
325 PropertyChanges {
327 PropertyChanges {
326 target: appDelegate;
328 target: appDelegate;
327 x: 0; y: 0;
329 x: 0; y: 0;
328− width: root.width; height: root.height;
330+ requestedWidth: root.width; requestedHeight: root.height;
329 visuallyMinimized: false;
331 visuallyMinimized: false;
330 visuallyMaximized: true
332 visuallyMaximized: true
331 }
333 }
@@ -355,14 +357,14 @@ AbstractStage {
355 to: "normal"
357 to: "normal"
356 enabled: appDelegate.animationsEnabled
358 enabled: appDelegate.animationsEnabled
357 PropertyAction { target: appDelegate; properties: "visuallyMinimized,visuallyMaximized" }
359 PropertyAction { target: appDelegate; properties: "visuallyMinimized,visuallyMaximized" }
358− UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,width,height,scale"; duration: UbuntuAnimation.FastDuration }
360+ UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,requestedWidth,requestedHeight,scale"; duration: UbuntuAnimation.FastDuration }
359 },
361 },
360 Transition {
362 Transition {
361 to: "maximized"
363 to: "maximized"
362 enabled: appDelegate.animationsEnabled
364 enabled: appDelegate.animationsEnabled
363 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
365 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
364 SequentialAnimation {
366 SequentialAnimation {
365− UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,width,height,scale"; duration: UbuntuAnimation.FastDuration }
367+ UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,requestedWidth,requestedHeight,scale"; duration: UbuntuAnimation.FastDuration }
366 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
368 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
367 }
369 }
368 },
370 },
@@ -371,7 +373,7 @@ AbstractStage {
371 enabled: appDelegate.animationsEnabled
373 enabled: appDelegate.animationsEnabled
372 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
374 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
373 SequentialAnimation {
375 SequentialAnimation {
374− UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,width,height,scale"; duration: UbuntuAnimation.FastDuration }
376+ UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,requestedWidth,requestedHeight,scale"; duration: UbuntuAnimation.FastDuration }
375 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
377 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
376 ScriptAction {
378 ScriptAction {
377 script: {
379 script: {
@@ -410,8 +412,6 @@ AbstractStage {
410 objectName: "decoratedWindow"
412 objectName: "decoratedWindow"
411 anchors.left: appDelegate.left
413 anchors.left: appDelegate.left
412 anchors.top: appDelegate.top
414 anchors.top: appDelegate.top
413− width: appDelegate.width
414− height: appDelegate.height
415 application: ApplicationManager.get(index)
415 application: ApplicationManager.get(index)
416 active: ApplicationManager.focusedApplicationId === model.appId
416 active: ApplicationManager.focusedApplicationId === model.appId
417 focus: true
417 focus: true
qml/Stages/SessionContainer.qml+25−1
@@ -29,13 +29,37 @@ FocusScope {
29 property alias surfaceOrientationAngle: _surfaceContainer.surfaceOrientationAngle
29 property alias surfaceOrientationAngle: _surfaceContainer.surfaceOrientationAngle
30 property alias resizeSurface: _surfaceContainer.resizeSurface
30 property alias resizeSurface: _surfaceContainer.resizeSurface
31
31
32+ property int requestedWidth: -1
33+ property int requestedHeight: -1
34+
32 readonly property alias surfaceContainer: _surfaceContainer
35 readonly property alias surfaceContainer: _surfaceContainer
33 SurfaceContainer {
36 SurfaceContainer {
34 id: _surfaceContainer
37 id: _surfaceContainer
35− anchors.fill: parent
38+ requestedWidth: root.requestedWidth
39+ requestedHeight: root.requestedHeight
36 surface: session ? session.surface : null
40 surface: session ? session.surface : null
37 }
41 }
38
42
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+
39 Repeater {
63 Repeater {
40 id: childSessionsRepeater
64 id: childSessionsRepeater
41 model: root.childSessions
65 model: root.childSessions
qml/Stages/SurfaceContainer.qml+55−3
@@ -31,6 +31,9 @@ FocusScope {
31 property string name: surface ? surface.name : ""
31 property string name: surface ? surface.name : ""
32 property bool resizeSurface: true
32 property bool resizeSurface: true
33
33
34+ property int requestedWidth: -1
35+ property int requestedHeight: -1
36+
34 onSurfaceChanged: {
37 onSurfaceChanged: {
35 if (surface) {
38 if (surface) {
36 surfaceItem.surface = surface;
39 surfaceItem.surface = surface;
@@ -54,16 +57,65 @@ FocusScope {
54
57
55 consumesInput: true
58 consumesInput: true
56
59
60+ surfaceWidth: {
57− surfaceWidth: root.resizeSurface ? width : -1
61+ if (root.resizeSurface) {
62+ if (root.requestedWidth >= 0) {
63+ return root.requestedWidth;
64+ } else {
65+ return width;
66+ }
67+ } else {
68+ return -1;
69+ }
70+ }
71+
72+ surfaceHeight: {
58− surfaceHeight: root.resizeSurface ? height : -1
73+ if (root.resizeSurface) {
74+ if (root.requestedHeight >= 0) {
75+ return root.requestedHeight;
76+ } else {
77+ return height;
78+ }
79+ } else {
80+ return -1;
81+ }
82+ }
59
83
60− anchors.fill: root
61 enabled: root.interactive
84 enabled: root.interactive
62 focus: true
85 focus: true
63 antialiasing: !root.interactive
86 antialiasing: !root.interactive
64 orientationAngle: root.surfaceOrientationAngle
87 orientationAngle: root.surfaceOrientationAngle
65 }
88 }
66
89
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+
67 TouchGate {
119 TouchGate {
68 targetItem: surfaceItem
120 targetItem: surfaceItem
69 anchors.fill: root
121 anchors.fill: root
qml/Stages/WindowResizeArea.qml+64−19
@@ -36,6 +36,8 @@ MouseArea {
36 property int borderThickness: 0
36 property int borderThickness: 0
37 property int minWidth: 0
37 property int minWidth: 0
38 property int minHeight: 0
38 property int minHeight: 0
39+ property int defaultWidth: units.gu(60)
40+ property int defaultHeight: units.gu(50)
39 property int screenWidth: 0
41 property int screenWidth: 0
40 property int screenHeight: 0
42 property int screenHeight: 0
41
43
@@ -67,13 +69,14 @@ MouseArea {
67 }
69 }
68
70
69 Component.onCompleted: {
71 Component.onCompleted: {
70− var windowGeometry = windowStateStorage.getGeometry(root.windowId, Qt.rect(target.x, target.y, target.width, target.height))
72+ var windowGeometry = windowStateStorage.getGeometry(root.windowId,
71− if (windowGeometry !== undefined) {
73+ Qt.rect(target.x, target.y, defaultWidth, defaultHeight));
74+
72− target.width = Math.min(windowGeometry.width, root.screenWidth)
75+ target.requestedWidth = Math.min(Math.max(windowGeometry.width, minWidth), screenWidth);
73− target.height = Math.min(windowGeometry.height, root.screenHeight - PanelState.panelHeight)
76+ target.requestedHeight = Math.min(Math.max(windowGeometry.height, minHeight), root.screenHeight - PanelState.panelHeight);
74− target.x = Math.max(Math.min(windowGeometry.x, root.screenWidth - target.width), 0)
77+ target.x = Math.max(Math.min(windowGeometry.x, root.screenWidth - target.requestedWidth), 0)
75− target.y = Math.max(Math.min(windowGeometry.y, root.screenHeight - target.height), PanelState.panelHeight)
78+ target.y = Math.max(Math.min(windowGeometry.y, root.screenHeight - target.requestedHeight), PanelState.panelHeight)
76− }
79+
77 var windowState = windowStateStorage.getState(root.windowId, WindowStateStorage.WindowStateNormal)
80 var windowState = windowStateStorage.getState(root.windowId, WindowStateStorage.WindowStateNormal)
78 if (windowState === WindowStateStorage.WindowStateMaximized) {
81 if (windowState === WindowStateStorage.WindowStateMaximized) {
79 target.maximize(false)
82 target.maximize(false)
@@ -93,6 +96,18 @@ MouseArea {
93 property bool topBorder: false
96 property bool topBorder: false
94 property bool bottomBorder: false
97 property bool bottomBorder: false
95
98
99+ // true - A change in surface size will cause the left border of the window to move accordingly.
100+ // The window's right border will stay in the same position.
101+ // false - a change in surface size will cause the right border of the window to move accordingly.
102+ // The window's left border will stay in the same position.
103+ property bool moveLeftBorder: false
104+
105+ // true - A change in surface size will cause the top border of the window to move accordingly.
106+ // The window's bottom border will stay in the same position.
107+ // false - a change in surface size will cause the bottom border of the window to move accordingly.
108+ // The window's top border will stay in the same position.
109+ property bool moveTopBorder: false
110+
96 property bool dragging: false
111 property bool dragging: false
97 property real startMousePosX
112 property real startMousePosX
98 property real startMousePosY
113 property real startMousePosY
@@ -100,6 +115,8 @@ MouseArea {
100 property real startY
115 property real startY
101 property real startWidth
116 property real startWidth
102 property real startHeight
117 property real startHeight
118+ property real currentWidth
119+ property real currentHeight
103
120
104 property string cursorName: {
121 property string cursorName: {
105 if (root.containsMouse || root.pressed) {
122 if (root.containsMouse || root.pressed) {
@@ -138,11 +155,24 @@ MouseArea {
138 }
155 }
139 }
156 }
140
157
158+ Timer {
159+ id: resetBordersToMoveTimer
160+ interval: 2000
161+ onTriggered: {
162+ d.moveLeftBorder = false;
163+ d.moveTopBorder = false;
164+ }
165+ }
166+
141 onPressedChanged: {
167 onPressedChanged: {
142 var pos = mapToItem(target.parent, mouseX, mouseY);
168 var pos = mapToItem(target.parent, mouseX, mouseY);
143
169
144 if (pressed) {
170 if (pressed) {
145 d.updateBorders();
171 d.updateBorders();
172+ resetBordersToMoveTimer.stop();
173+ d.moveLeftBorder = d.leftBorder;
174+ d.moveTopBorder = d.topBorder;
175+
146 var pos = mapToItem(root.target.parent, mouseX, mouseY);
176 var pos = mapToItem(root.target.parent, mouseX, mouseY);
147 d.startMousePosX = pos.x;
177 d.startMousePosX = pos.x;
148 d.startMousePosY = pos.y;
178 d.startMousePosY = pos.y;
@@ -150,8 +180,11 @@ MouseArea {
150 d.startY = target.y;
180 d.startY = target.y;
151 d.startWidth = target.width;
181 d.startWidth = target.width;
152 d.startHeight = target.height;
182 d.startHeight = target.height;
183+ d.currentWidth = target.width;
184+ d.currentHeight = target.height;
153 d.dragging = true;
185 d.dragging = true;
154 } else {
186 } else {
187+ resetBordersToMoveTimer.start();
155 d.dragging = false;
188 d.dragging = false;
156 if (containsMouse) {
189 if (containsMouse) {
157 d.updateBorders();
190 d.updateBorders();
@@ -182,37 +215,49 @@ MouseArea {
182 if (d.leftBorder) {
215 if (d.leftBorder) {
183 var newTargetX = d.startX + deltaX;
216 var newTargetX = d.startX + deltaX;
184 if (target.x + target.width > newTargetX + minWidth) {
217 if (target.x + target.width > newTargetX + minWidth) {
185− target.width = target.x + target.width - newTargetX;
218+ target.requestedWidth = target.x + target.width - newTargetX;
186− target.x = newTargetX;
187 } else {
219 } else {
188− target.x = target.x + target.width - minWidth;
220+ target.requestedWidth = minWidth;
189− target.width = minWidth;
190 }
221 }
191
222
192 } else if (d.rightBorder) {
223 } else if (d.rightBorder) {
193 if (d.startWidth + deltaX >= minWidth) {
224 if (d.startWidth + deltaX >= minWidth) {
194− target.width = d.startWidth + deltaX;
225+ target.requestedWidth = d.startWidth + deltaX;
195 } else {
226 } else {
196− target.width = minWidth;
227+ target.requestedWidth = minWidth;
197 }
228 }
198 }
229 }
199
230
200 if (d.topBorder) {
231 if (d.topBorder) {
201 var newTargetY = d.startY + deltaY;
232 var newTargetY = d.startY + deltaY;
202 if (target.y + target.height > newTargetY + minHeight) {
233 if (target.y + target.height > newTargetY + minHeight) {
203− target.height = target.y + target.height - newTargetY;
234+ target.requestedHeight = target.y + target.height - newTargetY;
204− target.y = newTargetY;
205 } else {
235 } else {
206− target.y = target.y + target.height - minHeight;
236+ target.requestedHeight = minHeight;
207− target.height = minHeight;
208 }
237 }
209
238
210 } else if (d.bottomBorder) {
239 } else if (d.bottomBorder) {
211 if (d.startHeight + deltaY >= minHeight) {
240 if (d.startHeight + deltaY >= minHeight) {
212− target.height = d.startHeight + deltaY;
241+ target.requestedHeight = d.startHeight + deltaY;
213 } else {
242 } else {
214− target.height = minHeight;
243+ target.requestedHeight = minHeight;
244+ }
245+ }
246+ }
247+
248+ Connections {
249+ target: root.target
250+ onWidthChanged: {
251+ if (d.moveLeftBorder) {
252+ target.x += d.currentWidth - target.width;
253+ }
254+ d.currentWidth = target.width;
255+ }
256+ onHeightChanged: {
257+ if (d.moveTopBorder) {
258+ target.y += d.currentHeight - target.height;
215 }
259 }
260+ d.currentHeight = target.height;
216 }
261 }
217 }
262 }
218}
263}
tests/mocks/Unity/Application/MirSurface.cpp+52
@@ -35,8 +35,12 @@ MirSurface::MirSurface(const QString& name,
35 , m_activeFocus(false)
35 , m_activeFocus(false)
36 , m_width(-1)
36 , m_width(-1)
37 , m_height(-1)
37 , m_height(-1)
38+ , m_slowToResize(false)
38{
39{
39// qDebug() << "MirSurface::MirSurface() " << name;
40// qDebug() << "MirSurface::MirSurface() " << name;
41+ m_delayedResizeTimer.setInterval(600);
42+ m_delayedResizeTimer.setSingleShot(true);
43+ connect(&m_delayedResizeTimer, &QTimer::timeout, this, &MirSurface::applyDelayedResize);
40}
44}
41
45
42MirSurface::~MirSurface()
46MirSurface::~MirSurface()
@@ -196,8 +200,39 @@ int MirSurface::height() const
196}
200}
197
201
198void MirSurface::resize(int width, int height)
202void MirSurface::resize(int width, int height)
203+{
204+ if (m_slowToResize) {
205+ if (!m_delayedResizeTimer.isActive()) {
206+ m_delayedResize.setWidth(width);
207+ m_delayedResize.setHeight(height);
208+ m_delayedResizeTimer.start();
209+ } else {
210+ m_pendingResize.setWidth(width);
211+ m_pendingResize.setHeight(height);
212+ }
213+ } else {
214+ doResize(width, height);
215+ }
216+}
217+
218+void MirSurface::applyDelayedResize()
219+{
220+ doResize(m_delayedResize.width(), m_delayedResize.height());
221+ m_delayedResize.setWidth(-1);
222+ m_delayedResize.setHeight(-1);
223+
224+ if (m_pendingResize.isValid()) {
225+ QSize size = m_pendingResize;
226+ m_pendingResize.setWidth(-1);
227+ m_pendingResize.setHeight(-1);
228+ resize(size.width(), size.height());
229+ }
230+}
231+
232+void MirSurface::doResize(int width, int height)
199{
233{
200 bool changed = false;
234 bool changed = false;
235+
201 if (width != m_width) {
236 if (width != m_width) {
202 m_width = width;
237 m_width = width;
203 Q_EMIT widthChanged();
238 Q_EMIT widthChanged();
@@ -214,3 +249,20 @@ void MirSurface::resize(int width, int height)
214 Q_EMIT sizeChanged(QSize(width, height));
249 Q_EMIT sizeChanged(QSize(width, height));
215 }
250 }
216}
251}
252+
253+bool MirSurface::isSlowToResize() const
254+{
255+ return m_slowToResize;
256+}
257+
258+void MirSurface::setSlowToResize(bool value)
259+{
260+ if (m_slowToResize != value) {
261+ m_slowToResize = value;
262+ Q_EMIT slowToResizeChanged();
263+ if (!m_slowToResize && m_delayedResizeTimer.isActive()) {
264+ m_delayedResizeTimer.stop();
265+ applyDelayedResize();
266+ }
267+ }
268+}
tests/mocks/Unity/Application/MirSurface.h+16
@@ -18,6 +18,7 @@
18#define MOCK_MIR_SURFACE_H
18#define MOCK_MIR_SURFACE_H
19
19
20#include <QObject>
20#include <QObject>
21+#include <QTimer>
21#include <QUrl>
22#include <QUrl>
22#include <QHash>
23#include <QHash>
23
24
@@ -33,6 +34,7 @@ class MirSurface : public unity::shell::application::MirSurfaceInterface
33 Q_PROPERTY(int width READ width NOTIFY widthChanged)
34 Q_PROPERTY(int width READ width NOTIFY widthChanged)
34 Q_PROPERTY(int height READ height NOTIFY heightChanged)
35 Q_PROPERTY(int height READ height NOTIFY heightChanged)
35 Q_PROPERTY(bool activeFocus READ activeFocus NOTIFY activeFocusChanged)
36 Q_PROPERTY(bool activeFocus READ activeFocus NOTIFY activeFocusChanged)
37+ Q_PROPERTY(bool slowToResize READ isSlowToResize WRITE setSlowToResize NOTIFY slowToResizeChanged)
36
38
37public:
39public:
38 MirSurface(const QString& name,
40 MirSurface(const QString& name,
@@ -77,6 +79,9 @@ public:
77 int width() const;
79 int width() const;
78 int height() const;
80 int height() const;
79
81
82+ bool isSlowToResize() const;
83+ void setSlowToResize(bool value);
84+
80 /////
85 /////
81 // internal mock stuff
86 // internal mock stuff
82
87
@@ -94,13 +99,18 @@ Q_SIGNALS:
94 void orientationAngleChanged(Mir::OrientationAngle angle);
99 void orientationAngleChanged(Mir::OrientationAngle angle);
95 void widthChanged();
100 void widthChanged();
96 void heightChanged();
101 void heightChanged();
102+ void slowToResizeChanged();
97
103
98 ////
104 ////
99 // internal mock stuff
105 // internal mock stuff
100 void screenshotUrlChanged(QUrl);
106 void screenshotUrlChanged(QUrl);
101 void activeFocusChanged(bool);
107 void activeFocusChanged(bool);
102
108
109+private Q_SLOTS:
110+ void applyDelayedResize();
111+
103private:
112private:
113+ void doResize(int width, int height);
104 void updateVisibility();
114 void updateVisibility();
105
115
106 const QString m_name;
116 const QString m_name;
@@ -114,6 +124,12 @@ private:
114 bool m_activeFocus;
124 bool m_activeFocus;
115 int m_width;
125 int m_width;
116 int m_height;
126 int m_height;
127+
128+ bool m_slowToResize;
129+ QTimer m_delayedResizeTimer;
130+ QSize m_delayedResize;
131+ QSize m_pendingResize;
132+
117 struct View {
133 struct View {
118 bool visible;
134 bool visible;
119 };
135 };
tests/qmltests/Stages/tst_DesktopStage.qml+16−1
@@ -17,7 +17,7 @@
17import QtQuick 2.4
17import QtQuick 2.4
18import QtTest 1.0
18import QtTest 1.0
19import Ubuntu.Components 1.3
19import Ubuntu.Components 1.3
20−import Ubuntu.Components.ListItems 1.3 as ListItem
20+import Ubuntu.Components.ListItems 1.3
21import Unity.Application 0.1
21import Unity.Application 0.1
22import Unity.Test 0.1
22import Unity.Test 0.1
23import Utils 0.1
23import Utils 0.1
@@ -82,6 +82,21 @@ Item {
82 Column {
82 Column {
83 anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
83 anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
84 spacing: units.gu(1)
84 spacing: units.gu(1)
85+
86+ Button {
87+ color: "white"
88+ text: "Make surface slow to resize"
89+ activeFocusOnPress: false
90+ onClicked: {
91+ if (ApplicationManager.focusedApplicationId) {
92+ var surface = ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).session.surface;
93+ surface.slowToResize = true;
94+ }
95+ }
96+ }
97+
98+ Divider {}
99+
85 Repeater {
100 Repeater {
86 model: ApplicationManager.availableApplications
101 model: ApplicationManager.availableApplications
87 ApplicationCheckBox {
102 ApplicationCheckBox {
tests/qmltests/Stages/tst_WindowResizeArea.qml+31−12
@@ -30,8 +30,6 @@ Item {
30 height: units.gu(60)
30 height: units.gu(60)
31 width: units.gu(60)
31 width: units.gu(60)
32
32
33− property var fakeWindow: windowLoader.item
34−
35 Binding {
33 Binding {
36 target: PanelState
34 target: PanelState
37 property: "panelHeight"
35 property: "panelHeight"
@@ -47,10 +45,10 @@ Item {
47 property alias minHeight: windowResizeArea.minHeight
45 property alias minHeight: windowResizeArea.minHeight
48 x: units.gu(20)
46 x: units.gu(20)
49 y: units.gu(20)
47 y: units.gu(20)
50− height: units.gu(20)
48+ width: requestedWidth
51− width: units.gu(20)
49+ height: requestedHeight
52− property int windowHeight: height
50+ property real requestedWidth
53− property int windowWidth: width
51+ property real requestedHeight
54 state: "normal"
52 state: "normal"
55
53
56 function maximize() {
54 function maximize() {
@@ -63,6 +61,8 @@ Item {
63 borderThickness: units.gu(2)
61 borderThickness: units.gu(2)
64 minWidth: units.gu(15)
62 minWidth: units.gu(15)
65 minHeight: units.gu(10)
63 minHeight: units.gu(10)
64+ defaultWidth: units.gu(20)
65+ defaultHeight: units.gu(20)
66 windowId: "test-window-id"
66 windowId: "test-window-id"
67 screenWidth: root.width
67 screenWidth: root.width
68 screenHeight: root.height
68 screenHeight: root.height
@@ -87,22 +87,41 @@ Item {
87 Loader {
87 Loader {
88 id: windowLoader
88 id: windowLoader
89 sourceComponent: fakeWindowComponent
89 sourceComponent: fakeWindowComponent
90+ active: windowLoaderCheckbox.checked
90 }
91 }
91
92
93+ Column {
92− MouseTouchEmulationCheckbox {
94+ MouseTouchEmulationCheckbox {
93− checked: false
95+ checked: false
96+ color: "black"
97+ }
98+ RowLayout {
99+ Layout.fillWidth: true
100+ CheckBox {
101+ id: windowLoaderCheckbox
102+ checked: true
103+ activeFocusOnPress: false
104+ }
105+ Label {
106+ id: label
94− color: "black"
107+ color: "black"
108+ text: "Window loader active"
109+ anchors.verticalCenter: parent.verticalCenter
110+ }
111+ }
95 }
112 }
96
113
97 UnityTestCase {
114 UnityTestCase {
98 name: "WindowResizeArea"
115 name: "WindowResizeArea"
99 when: windowShown
116 when: windowShown
100
117
118+ property var fakeWindow: windowLoader.item
119+
101 function init() {
120 function init() {
102 fakeWindow.x = units.gu(20)
121 fakeWindow.x = units.gu(20)
103 fakeWindow.y = units.gu(20)
122 fakeWindow.y = units.gu(20)
104− fakeWindow.width = units.gu(20)
123+ fakeWindow.requestedWidth = units.gu(20)
105− fakeWindow.height = units.gu(20)
124+ fakeWindow.requestedHeight = units.gu(20)
106 }
125 }
107
126
108 function test_resizeWindowRightBottom_data() {
127 function test_resizeWindowRightBottom_data() {
@@ -188,7 +207,7 @@ Item {
188 function test_resizeSmallerAndLarger_data() {
207 function test_resizeSmallerAndLarger_data() {
189 return [
208 return [
190 { tag: "topLeft", startX: -1, startY: -1, dx: units.gu(15), dy: units.gu(15) },
209 { tag: "topLeft", startX: -1, startY: -1, dx: units.gu(15), dy: units.gu(15) },
191− { tag: "bottomRight", startX: fakeWindow.width + 1, startY: fakeWindow.height + 1, dx: -units.gu(15), dy: -units.gu(15) }
210+ { tag: "bottomRight", startX: units.gu(20) + 1, startY: units.gu(20) + 1, dx: -units.gu(15), dy: -units.gu(15) }
192 ]
211 ]
193 }
212 }
194
213