on xenial help sign in
Merge lp:~lukas-kde/unity8/newWindowDecosAndPanel
340b08cDaniel d'Andrada
qml/Components/PanelState/PanelState.qml+3−2
@@ -22,10 +22,11 @@ QtObject {
22
22
23 property string title: ""
23 property string title: ""
24 property bool buttonsVisible: false
24 property bool buttonsVisible: false
25−
25+ property bool dropShadow: false
26− property int panelHeight: 0
26+ property int panelHeight: units.gu(3)
27
27
28 signal close()
28 signal close()
29 signal minimize()
29 signal minimize()
30 signal maximize()
30 signal maximize()
31+ signal focusMaximizedApp()
31}
32}
qml/Components/WindowControlButtons.qml+73−28
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -12,8 +12,6 @@
12 *
12 *
13 * You should have received a copy of the GNU General Public License
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/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Authors: Michael Zanetti <michael.zanetti@canonical.com>
17 */
15 */
18
16
19import QtQuick 2.4
17import QtQuick 2.4
@@ -21,43 +19,90 @@ import Ubuntu.Components 1.3
21
19
22Row {
20Row {
23 id: root
21 id: root
24− spacing: units.gu(0.5)
22+ spacing: units.gu(1)
23+
24+ // to be set from outside
25+ property bool active: false
25
26
26 signal close()
27 signal close()
27 signal minimize()
28 signal minimize()
28 signal maximize()
29 signal maximize()
29
30
30− Rectangle {
31+ MouseArea {
32+ id: closeWindowButton
31 objectName: "closeWindowButton"
33 objectName: "closeWindowButton"
34+ hoverEnabled: true
32− height: parent.height; width: height; radius: height / 2
35+ height: parent.height
33− gradient: Gradient {
36+ width: height
34− GradientStop { color: "#F49073"; position: 0 }
37+ onClicked: root.close()
35− GradientStop { color: "#DF4F1C"; position: 1 }
38+
39+ Rectangle {
40+ anchors.centerIn: parent
41+ width: units.gu(2)
42+ height: units.gu(2)
43+ radius: height / 2
44+ color: "#ed3146"
45+ visible: parent.containsMouse
46+ }
47+ Icon {
48+ width: height
49+ height: parent.height *.5
50+ anchors.centerIn: parent
51+ source: "graphics/window-close.svg"
52+ color: root.active ? "white" : "#5d5d5d"
53+ keyColor: "black"
36 }
54 }
37− border.width: units.dp(.5)
38− border.color: "black"
39− MouseArea { anchors.fill: parent; onClicked: root.close() }
40 }
55 }
41− Rectangle {
56+
57+ MouseArea {
58+ id: minimizeWindowButton
42 objectName: "minimizeWindowButton"
59 objectName: "minimizeWindowButton"
60+ hoverEnabled: true
43− height: parent.height; width: height; radius: height / 2
61+ height: parent.height
44− gradient: Gradient {
62+ width: height
45− GradientStop { color: "#92918C"; position: 0 }
63+ onClicked: root.minimize()
46− GradientStop { color: "#5E5D58"; position: 1 }
64+
65+ Rectangle {
66+ anchors.centerIn: parent
67+ width: units.gu(2)
68+ height: units.gu(2)
69+ radius: height / 2
70+ color: "#888888"
71+ visible: parent.containsMouse
72+ }
73+ Icon {
74+ width: height
75+ height: parent.height *.5
76+ anchors.centerIn: parent
77+ source: "graphics/window-minimize.svg"
78+ color: root.active ? "white" : "#5d5d5d"
79+ keyColor: "black"
47 }
80 }
48− border.width: units.dp(.5)
49− border.color: "black"
50− MouseArea { anchors.fill: parent; onClicked: root.minimize() }
51 }
81 }
52− Rectangle {
82+
83+ MouseArea {
84+ id: maximizeWindowButton
53 objectName: "maximizeWindowButton"
85 objectName: "maximizeWindowButton"
86+ hoverEnabled: true
54− height: parent.height; width: height; radius: height / 2
87+ height: parent.height
55− gradient: Gradient {
88+ width: height
56− GradientStop { color: "#92918C"; position: 0 }
89+ onClicked: root.maximize()
57− GradientStop { color: "#5E5D58"; position: 1 }
90+
91+ Rectangle {
92+ anchors.centerIn: parent
93+ width: units.gu(2)
94+ height: units.gu(2)
95+ radius: height / 2
96+ color: "#888888"
97+ visible: parent.containsMouse
98+ }
99+ Icon {
100+ width: height
101+ height: parent.height *.5
102+ anchors.centerIn: parent
103+ source: "graphics/window-maximize.svg"
104+ color: root.active ? "white" : "#5d5d5d"
105+ keyColor: "black"
58 }
106 }
59− border.width: units.dp(.5)
60− border.color: "black"
61− MouseArea { anchors.fill: parent; onClicked: root.maximize() }
62 }
107 }
63}
108}
qml/Components/graphics/window-close.svgnew file+14
@@ -0,0 +1,14 @@
1+<?xml version="1.0" encoding="iso-8859-1"?>
2+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
4+<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
5+ width="94.926px" height="94.926px" viewBox="0 0 94.926 94.926" style="enable-background:new 0 0 94.926 94.926;"
6+ xml:space="preserve">
7+<g>
8+ <path d="M55.931,47.463L94.306,9.09c0.826-0.827,0.826-2.167,0-2.994L88.833,0.62C88.436,0.224,87.896,0,87.335,0
9+ c-0.562,0-1.101,0.224-1.498,0.62L47.463,38.994L9.089,0.62c-0.795-0.795-2.202-0.794-2.995,0L0.622,6.096
10+ c-0.827,0.827-0.827,2.167,0,2.994l38.374,38.373L0.622,85.836c-0.827,0.827-0.827,2.167,0,2.994l5.473,5.476
11+ c0.397,0.396,0.936,0.62,1.498,0.62s1.1-0.224,1.497-0.62l38.374-38.374l38.374,38.374c0.397,0.396,0.937,0.62,1.498,0.62
12+ s1.101-0.224,1.498-0.62l5.473-5.476c0.826-0.827,0.826-2.167,0-2.994L55.931,47.463z"/>
13+</g>
14+</svg>
qml/Components/graphics/window-maximize.svgnew file+12
@@ -0,0 +1,12 @@
1+<?xml version="1.0" encoding="iso-8859-1"?>
2+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
4+<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
5+ width="459px" height="459px" viewBox="0 0 459 459" style="enable-background:new 0 0 459 459;" xml:space="preserve">
6+<g>
7+ <g id="check-box-outline-blank">
8+ <path d="M408,51v357H51V51H408 M408,0H51C22.95,0,0,22.95,0,51v357c0,28.05,22.95,51,51,51h357c28.05,0,51-22.95,51-51V51
9+ C459,22.95,436.05,0,408,0L408,0z"/>
10+ </g>
11+</g>
12+</svg>
qml/Components/graphics/window-minimize.svgnew file+13
@@ -0,0 +1,13 @@
1+<?xml version="1.0" encoding="iso-8859-1"?>
2+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
4+<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
5+ width="121.805px" height="121.804px" viewBox="0 0 121.805 121.804" style="enable-background:new 0 0 121.805 121.804;"
6+ xml:space="preserve">
7+<g>
8+ <g>
9+ <path d="M7.308,68.211h107.188c4.037,0,7.309-3.272,7.309-7.31c0-4.037-3.271-7.309-7.309-7.309H7.308
10+ C3.272,53.593,0,56.865,0,60.902C0,64.939,3.272,68.211,7.308,68.211z"/>
11+ </g>
12+</g>
13+</svg>
qml/Panel/Handle.qml+2−2
@@ -19,7 +19,7 @@ import Ubuntu.Components 1.3
19
19
20Rectangle {
20Rectangle {
21 id: handle
21 id: handle
22− color: "#333333"
22+ color: "#5d5d5d"
23 height: units.gu(2)
23 height: units.gu(2)
24 property bool active: false
24 property bool active: false
25
25
@@ -35,7 +35,7 @@ Rectangle {
35 id: dot
35 id: dot
36 width: units.dp(3)
36 width: units.dp(3)
37 height: width
37 height: width
38− color: handle.active ? "#de4814" : "#717171"
38+ color: handle.active ? UbuntuColors.orange : "#717171"
39 radius: units.dp(1)
39 radius: units.dp(1)
40 }
40 }
41 }
41 }
qml/Panel/Indicators/client/IndicatorsClient.qml+1−1
@@ -22,7 +22,7 @@ import Ubuntu.Components 1.3
22import Ubuntu.Components.ListItems 1.3 as ListItem
22import Ubuntu.Components.ListItems 1.3 as ListItem
23
23
24Rectangle {
24Rectangle {
25− color: "black"
25+ color: "#333333"
26 id: root
26 id: root
27
27
28 Component.onCompleted: {
28 Component.onCompleted: {
qml/Panel/IndicatorsMenu.qml+11−2
@@ -38,7 +38,7 @@ Showable {
38 property bool enableHint: true
38 property bool enableHint: true
39 property bool contentEnabled: true
39 property bool contentEnabled: true
40 property bool showOnClick: true
40 property bool showOnClick: true
41− property color panelColor: "black"
41+ property color panelColor: "#333333"
42
42
43 signal showTapped(point position)
43 signal showTapped(point position)
44
44
@@ -110,12 +110,21 @@ Showable {
110 height: units.gu(0.5)
110 height: units.gu(0.5)
111 gradient: Gradient {
111 gradient: Gradient {
112 GradientStop { position: 0.0; color: "transparent" }
112 GradientStop { position: 0.0; color: "transparent" }
113− GradientStop { position: 1.0; color: "black" }
113+ GradientStop { position: 1.0; color: "#333333" }
114 }
114 }
115 opacity: 0.3
115 opacity: 0.3
116 }
116 }
117 }
117 }
118
118
119+ PanelSeparatorLine {
120+ id: indicatorOrangeLine
121+ anchors {
122+ bottom: handle.top
123+ left: handle.left
124+ right: handle.right
125+ }
126+ }
127+
119 Rectangle {
128 Rectangle {
120 anchors.fill: bar
129 anchors.fill: bar
121 color: panelColor
130 color: panelColor
qml/Panel/MenuContent.qml+1−1
@@ -27,7 +27,7 @@ Rectangle {
27
27
28 property QtObject indicatorsModel: null
28 property QtObject indicatorsModel: null
29 property int currentMenuIndex: -1
29 property int currentMenuIndex: -1
30− color: "#221e1c" // FIXME not in palette yet
30+ color: "#333333" // FIXME not in palette yet
31
31
32 width: units.gu(40)
32 width: units.gu(40)
33 height: units.gu(42)
33 height: units.gu(42)
qml/Panel/Panel.qml+68−80
@@ -30,23 +30,12 @@ Item {
30 property real indicatorAreaShowProgress: 1.0
30 property real indicatorAreaShowProgress: 1.0
31 property bool locked: false
31 property bool locked: false
32
32
33− opacity: fullscreenMode && indicators.fullyClosed ? 0.0 : 1.0
34−
35− Binding {
36− target: PanelState
37− property: "panelHeight"
38− value: root.panelHeight
39− }
40−
41 Rectangle {
33 Rectangle {
42 id: darkenedArea
34 id: darkenedArea
43 property real darkenedOpacity: 0.6
35 property real darkenedOpacity: 0.6
44 anchors {
36 anchors {
45− top: parent.top
37+ fill: parent
46 topMargin: panelHeight
38 topMargin: panelHeight
47− left: parent.left
48− right: parent.right
49− bottom: parent.bottom
50 }
39 }
51 color: "black"
40 color: "black"
52 opacity: indicators.unitProgress * darkenedOpacity
41 opacity: indicators.unitProgress * darkenedOpacity
@@ -59,16 +48,18 @@ Item {
59 }
48 }
60 }
49 }
61
50
51+ Binding {
52+ target: PanelState
53+ property: "panelHeight"
54+ value: indicators.minimizedPanelHeight
55+ }
56+
62 Item {
57 Item {
63 id: indicatorArea
58 id: indicatorArea
64 objectName: "indicatorArea"
59 objectName: "indicatorArea"
65
60
66 anchors.fill: parent
61 anchors.fill: parent
67
62
68− Behavior on anchors.topMargin {
69− NumberAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
70− }
71−
72 transform: Translate {
63 transform: Translate {
73 y: indicators.state === "initial"
64 y: indicators.state === "initial"
74 ? (1.0 - indicatorAreaShowProgress) * -d.indicatorHeight
65 ? (1.0 - indicatorAreaShowProgress) * -d.indicatorHeight
@@ -76,7 +67,7 @@ Item {
76 }
67 }
77
68
78 BorderImage {
69 BorderImage {
79− id: dropShadow
70+ id: indicatorsDropShadow
80 anchors {
71 anchors {
81 fill: indicators
72 fill: indicators
82 leftMargin: -units.gu(1)
73 leftMargin: -units.gu(1)
@@ -86,9 +77,19 @@ Item {
86 source: "graphics/rectangular_dropshadow.sci"
77 source: "graphics/rectangular_dropshadow.sci"
87 }
78 }
88
79
80+ BorderImage {
81+ id: panelDropShadow
82+ anchors {
83+ fill: indicatorAreaBackground
84+ bottomMargin: -units.gu(1)
85+ }
86+ visible: PanelState.dropShadow && !callHint.visible
87+ source: "graphics/rectangular_dropshadow.sci"
88+ }
89+
89 Rectangle {
90 Rectangle {
90 id: indicatorAreaBackground
91 id: indicatorAreaBackground
91− color: callHint.visible ? "green" : "black"
92+ color: callHint.visible ? "green" : "#333333"
92 anchors {
93 anchors {
93 top: parent.top
94 top: parent.top
94 left: parent.left
95 left: parent.left
@@ -99,30 +100,6 @@ Item {
99 Behavior on color { ColorAnimation { duration: UbuntuAnimation.FastDuration } }
100 Behavior on color { ColorAnimation { duration: UbuntuAnimation.FastDuration } }
100 }
101 }
101
102
102− PanelSeparatorLine {
103− id: orangeLine
104− anchors {
105− top: indicatorAreaBackground.bottom
106− left: parent.left
107− right: indicators.left
108− }
109− saturation: 1 - indicators.unitProgress
110−
111− // Don't let input event pass trough
112− MouseArea { anchors.fill: parent }
113− }
114−
115− Image {
116− anchors {
117− top: indicators.top
118− bottom: indicators.bottom
119− right: indicators.left
120− topMargin: indicatorArea.anchors.topMargin + indicators.minimizedPanelHeight
121− }
122− width: units.dp(2)
123− source: "graphics/VerticalDivider.png"
124− }
125−
126 MouseArea {
103 MouseArea {
127 anchors {
104 anchors {
128 top: parent.top
105 top: parent.top
@@ -130,7 +107,29 @@ Item {
130 right: indicators.left
107 right: indicators.left
131 }
108 }
132 height: indicators.minimizedPanelHeight
109 height: indicators.minimizedPanelHeight
110+ hoverEnabled: true
133− onClicked: { if (callHint.visible) { callHint.showLiveCall(); } }
111+ onClicked: callHint.visible ? callHint.showLiveCall() : PanelState.focusMaximizedApp()
112+ onDoubleClicked: PanelState.maximize()
113+
114+ // WindowControlButtons inside the mouse area, otherwise QML doesn't grok nested hover events :/
115+ // cf. https://bugreports.qt.io/browse/QTBUG-32909
116+ WindowControlButtons {
117+ id: windowControlButtons
118+ objectName: "panelWindowControlButtons"
119+ anchors {
120+ left: parent.left
121+ top: parent.top
122+ leftMargin: units.gu(1)
123+ topMargin: units.gu(0.5)
124+ bottomMargin: units.gu(0.5)
125+ }
126+ height: indicators.minimizedPanelHeight - anchors.topMargin - anchors.bottomMargin
127+ visible: PanelState.buttonsVisible && parent.containsMouse && !root.locked && !callHint.visible
128+ active: PanelState.buttonsVisible
129+ onClose: PanelState.close()
130+ onMinimize: PanelState.minimize()
131+ onMaximize: PanelState.maximize()
132+ }
134 }
133 }
135
134
136 IndicatorsMenu {
135 IndicatorsMenu {
@@ -146,7 +145,7 @@ Item {
146 width: root.width - (windowControlButtons.visible ? windowControlButtons.width + titleLabel.width : 0)
145 width: root.width - (windowControlButtons.visible ? windowControlButtons.width + titleLabel.width : 0)
147 minimizedPanelHeight: units.gu(3)
146 minimizedPanelHeight: units.gu(3)
148 expandedPanelHeight: units.gu(7)
147 expandedPanelHeight: units.gu(7)
149− openedHeight: root.height - indicatorOrangeLine.height
148+ openedHeight: root.height
150
149
151 overFlowWidth: {
150 overFlowWidth: {
152 if (callHint.visible) {
151 if (callHint.visible) {
@@ -163,58 +162,34 @@ Item {
163 callHint.showLiveCall();
162 callHint.showLiveCall();
164 }
163 }
165 }
164 }
166−
167− hideDragHandle {
168− anchors.bottomMargin: -indicatorOrangeLine.height
169− }
170− }
171−
172− WindowControlButtons {
173− id: windowControlButtons
174− objectName: "panelWindowControlButtons"
175− anchors {
176− left: parent.left
177− top: parent.top
178− margins: units.gu(0.7)
179− }
180− height: indicators.minimizedPanelHeight - anchors.margins * 2
181− visible: PanelState.buttonsVisible && !root.locked
182− onClose: PanelState.close()
183− onMinimize: PanelState.minimize()
184− onMaximize: PanelState.maximize()
185 }
165 }
186
166
187 Label {
167 Label {
188 id: titleLabel
168 id: titleLabel
189 objectName: "windowDecorationTitle"
169 objectName: "windowDecorationTitle"
190 anchors {
170 anchors {
191− left: windowControlButtons.right
171+ left: parent.left
192 top: parent.top
172 top: parent.top
173+ leftMargin: units.gu(1)
174+ topMargin: units.gu(0.5)
193− margins: units.gu(0.7)
175+ bottomMargin: units.gu(0.5)
194 }
176 }
195− color: "#DFDBD2"
177+ color: PanelState.buttonsVisible ? "#ffffff" : "#5d5d5d"
196− height: windowControlButtons.height
178+ height: indicators.minimizedPanelHeight - anchors.topMargin - anchors.bottomMargin
197− visible: windowControlButtons.visible
179+ visible: !windowControlButtons.visible && !root.locked && !callHint.visible
198 verticalAlignment: Text.AlignVCenter
180 verticalAlignment: Text.AlignVCenter
199− fontSize: "small"
181+ fontSize: "medium"
200− font.bold: true
182+ font.weight: Font.Normal
201 text: PanelState.title
183 text: PanelState.title
202 }
184 }
203
185
204− PanelSeparatorLine {
186+ // TODO here would the Locally integrated menus come
205− id: indicatorOrangeLine
206− anchors {
207− top: indicators.bottom
208− left: indicators.left
209− right: indicators.right
210− }
211− }
212
187
213 ActiveCallHint {
188 ActiveCallHint {
214 id: __callHint
189 id: __callHint
215 anchors {
190 anchors {
216 top: parent.top
191 top: parent.top
217− left: windowControlButtons.visible ? windowControlButtons.right : parent.left
192+ left: parent.left
218 }
193 }
219 height: indicators.minimizedPanelHeight
194 height: indicators.minimizedPanelHeight
220 visible: active && indicators.state == "initial"
195 visible: active && indicators.state == "initial"
@@ -224,7 +199,7 @@ Item {
224 QtObject {
199 QtObject {
225 id: d
200 id: d
226 objectName: "panelPriv"
201 objectName: "panelPriv"
227− readonly property real indicatorHeight: indicators.minimizedPanelHeight + indicatorOrangeLine.height
202+ readonly property real indicatorHeight: indicators.minimizedPanelHeight
228 }
203 }
229
204
230 states: [
205 states: [
@@ -234,6 +209,7 @@ Item {
234 PropertyChanges {
209 PropertyChanges {
235 target: indicatorArea;
210 target: indicatorArea;
236 anchors.topMargin: 0
211 anchors.topMargin: 0
212+ opacity: 1;
237 }
213 }
238 },
214 },
239 State {
215 State {
@@ -242,6 +218,7 @@ Item {
242 PropertyChanges {
218 PropertyChanges {
243 target: indicatorArea;
219 target: indicatorArea;
244 anchors.topMargin: indicators.state === "initial" ? -d.indicatorHeight : 0
220 anchors.topMargin: indicators.state === "initial" ? -d.indicatorHeight : 0
221+ opacity: indicators.fullyClosed ? 0.0 : 1.0
245 }
222 }
246 PropertyChanges {
223 PropertyChanges {
247 target: indicators.showDragHandle;
224 target: indicators.showDragHandle;
@@ -249,4 +226,15 @@ Item {
249 }
226 }
250 }
227 }
251 ]
228 ]
229+
230+ transitions: [
231+ Transition {
232+ to: "onscreen"
233+ UbuntuNumberAnimation { target: indicatorArea; properties: "anchors.topMargin,opacity" }
234+ },
235+ Transition {
236+ to: "offscreen"
237+ UbuntuNumberAnimation { target: indicatorArea; properties: "anchors.topMargin,opacity" }
238+ }
239+ ]
252}
240}
qml/Panel/PanelBackground.qmldeleted−21
@@ -1,21 +0,0 @@
1−/*
2− * Copyright (C) 2013 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−
19−Rectangle {
20− color: "black"
21−}
qml/Stages/ApplicationWindow.qml+2−2
@@ -56,7 +56,7 @@ FocusScope {
56 // Whether the Application had a surface before but lost it.
56 // Whether the Application had a surface before but lost it.
57 property bool hadSurface: sessionContainer.surfaceContainer.hadSurface
57 property bool hadSurface: sessionContainer.surfaceContainer.hadSurface
58
58
59− property bool needToTakeScreenshot:
59+ readonly property bool needToTakeScreenshot:
60 ((sessionContainer.surface && d.surfaceInitialized) || d.hadSurface)
60 ((sessionContainer.surface && d.surfaceInitialized) || d.hadSurface)
61 && screenshotImage.status === Image.Null
61 && screenshotImage.status === Image.Null
62 && d.applicationState === ApplicationInfoInterface.Stopped
62 && d.applicationState === ApplicationInfoInterface.Stopped
@@ -71,7 +71,7 @@ FocusScope {
71 // Remove this when possible
71 // Remove this when possible
72 property bool surfaceInitialized: false
72 property bool surfaceInitialized: false
73
73
74− property bool supportsSurfaceResize:
74+ readonly property bool supportsSurfaceResize:
75 application &&
75 application &&
76 ((application.supportedOrientations & Qt.PortraitOrientation)
76 ((application.supportedOrientations & Qt.PortraitOrientation)
77 || (application.supportedOrientations & Qt.InvertedPortraitOrientation))
77 || (application.supportedOrientations & Qt.InvertedPortraitOrientation))
qml/Stages/DecoratedWindow.qml+18−15
@@ -24,14 +24,15 @@ FocusScope {
24 id: root
24 id: root
25
25
26 width: applicationWindow.width
26 width: applicationWindow.width
27− height: (root.decorationShown ? decoration.height : 0) + applicationWindow.height
27+ height: (decorationShown ? decoration.height : 0) + applicationWindow.height
28
28
29 property alias window: applicationWindow
29 property alias window: applicationWindow
30 property alias application: applicationWindow.application
30 property alias application: applicationWindow.application
31 property alias active: decoration.active
31 property alias active: decoration.active
32 property alias title: decoration.title
32 property alias title: decoration.title
33+ property alias fullscreen: applicationWindow.fullscreen
33
34
34− property bool decorationShown: true
35+ readonly property bool decorationShown: !fullscreen
35 property bool highlightShown: false
36 property bool highlightShown: false
36 property real shadowOpacity: 1
37 property real shadowOpacity: 1
37
38
@@ -43,15 +44,6 @@ FocusScope {
43 signal minimize()
44 signal minimize()
44 signal decorationPressed()
45 signal decorationPressed()
45
46
46− BorderImage {
47− anchors {
48− fill: root
49− margins: -units.gu(2)
50− }
51− source: "graphics/dropshadow2gu.sci"
52− opacity: root.shadowOpacity * .3
53− }
54−
55 Rectangle {
47 Rectangle {
56 id: selectionHighlight
48 id: selectionHighlight
57 anchors.fill: parent
49 anchors.fill: parent
@@ -64,7 +56,17 @@ FocusScope {
64 anchors { left: selectionHighlight.left; right: selectionHighlight.right; bottom: selectionHighlight.bottom; }
56 anchors { left: selectionHighlight.left; right: selectionHighlight.right; bottom: selectionHighlight.bottom; }
65 height: units.dp(2)
57 height: units.dp(2)
66 color: UbuntuColors.orange
58 color: UbuntuColors.orange
67− visible: root.highlightShown
59+ visible: highlightShown
60+ }
61+
62+ BorderImage {
63+ anchors {
64+ fill: root
65+ margins: active ? -units.gu(2) : -units.gu(1.5)
66+ }
67+ source: "graphics/dropshadow2gu.sci"
68+ opacity: root.shadowOpacity * .3
69+ enabled: !fullscreen
68 }
70 }
69
71
70 WindowDecoration {
72 WindowDecoration {
@@ -74,12 +76,13 @@ FocusScope {
74 anchors { left: parent.left; top: parent.top; right: parent.right }
76 anchors { left: parent.left; top: parent.top; right: parent.right }
75 height: units.gu(3)
77 height: units.gu(3)
76 width: root.width
78 width: root.width
77− title: window.title !== "" ? window.title : ""
79+ title: window.title
80+ visible: root.decorationShown
81+
78 onClose: root.close();
82 onClose: root.close();
79− onMaximize: root.maximize();
83+ onMaximize: { root.decorationPressed(); root.maximize(); }
80 onMinimize: root.minimize();
84 onMinimize: root.minimize();
81 onPressed: root.decorationPressed();
85 onPressed: root.decorationPressed();
82− visible: decorationShown
83 }
86 }
84
87
85 ApplicationWindow {
88 ApplicationWindow {
qml/Stages/DesktopStage.qml+66−42
@@ -12,12 +12,9 @@
12 *
12 *
13 * You should have received a copy of the GNU General Public License
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/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Authors: Michael Zanetti <michael.zanetti@canonical.com>
17 */
15 */
18
16
19import QtQuick 2.4
17import QtQuick 2.4
20−import QtQuick.Layouts 1.1
21import Ubuntu.Components 1.3
18import Ubuntu.Components 1.3
22import Unity.Application 0.1
19import Unity.Application 0.1
23import "../Components/PanelState"
20import "../Components/PanelState"
@@ -55,7 +52,7 @@ AbstractStage {
55 onFocusRequested: {
52 onFocusRequested: {
56 var appIndex = priv.indexOf(appId);
53 var appIndex = priv.indexOf(appId);
57 var appDelegate = appRepeater.itemAt(appIndex);
54 var appDelegate = appRepeater.itemAt(appIndex);
58− appDelegate.restoreFromMinimized();
55+ appDelegate.restore();
59
56
60 if (spread.state == "altTab") {
57 if (spread.state == "altTab") {
61 spread.cancel();
58 spread.cancel();
@@ -107,7 +104,7 @@ AbstractStage {
107 id: minimizeRestoreShortcut
104 id: minimizeRestoreShortcut
108 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Down
105 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Down
109 onTriggered: priv.focusedAppDelegate.maximized || priv.focusedAppDelegate.maximizedLeft || priv.focusedAppDelegate.maximizedRight
106 onTriggered: priv.focusedAppDelegate.maximized || priv.focusedAppDelegate.maximizedLeft || priv.focusedAppDelegate.maximizedRight
110− ? priv.focusedAppDelegate.restore() : priv.focusedAppDelegate.minimize(true)
107+ ? priv.focusedAppDelegate.restoreFromMaximized() : priv.focusedAppDelegate.minimize()
111 active: priv.focusedAppDelegate !== null
108 active: priv.focusedAppDelegate !== null
112 }
109 }
113
110
@@ -119,18 +116,16 @@ AbstractStage {
119 var index = indexOf(focusedAppId);
116 var index = indexOf(focusedAppId);
120 return index >= 0 && index < appRepeater.count ? appRepeater.itemAt(index) : null
117 return index >= 0 && index < appRepeater.count ? appRepeater.itemAt(index) : null
121 }
118 }
119+ onFocusedAppDelegateChanged: updateForegroundMaximizedApp();
120+
122 property int foregroundMaximizedAppIdIndex: -1
121 property int foregroundMaximizedAppIdIndex: -1
123
122
124 function updateForegroundMaximizedApp() {
123 function updateForegroundMaximizedApp() {
125 for (var i = 0; i < appRepeater.count; i++) {
124 for (var i = 0; i < appRepeater.count; i++) {
126 var item = appRepeater.itemAt(i);
125 var item = appRepeater.itemAt(i);
127−
128 if (item && item.visuallyMaximized) {
126 if (item && item.visuallyMaximized) {
129− var app = ApplicationManager.get(i);
130− if (app) {
131− foregroundMaximizedAppIdIndex = i;
127+ foregroundMaximizedAppIdIndex = i;
132− return;
128+ return;
133− }
134 }
129 }
135 }
130 }
136 foregroundMaximizedAppIdIndex = -1;
131 foregroundMaximizedAppIdIndex = -1;
@@ -149,7 +144,7 @@ AbstractStage {
149 for (var i = 0; i < appRepeater.count; i++) {
144 for (var i = 0; i < appRepeater.count; i++) {
150 var appDelegate = appRepeater.itemAt(i);
145 var appDelegate = appRepeater.itemAt(i);
151 if (appDelegate && !appDelegate.minimized) {
146 if (appDelegate && !appDelegate.minimized) {
152− appDelegate.minimize(false); // minimize but don't switch focus
147+ appDelegate.minimize();
153 }
148 }
154 }
149 }
155
150
@@ -173,24 +168,47 @@ AbstractStage {
173 onClose: {
168 onClose: {
174 ApplicationManager.stopApplication(ApplicationManager.focusedApplicationId)
169 ApplicationManager.stopApplication(ApplicationManager.focusedApplicationId)
175 }
170 }
176− onMinimize: appRepeater.itemAt(0).minimize(true);
171+ onMinimize: priv.focusedAppDelegate && priv.focusedAppDelegate.minimize();
177− onMaximize: appRepeater.itemAt(0).restore();
172+ onMaximize: priv.focusedAppDelegate // don't restore minimized apps when double clicking the panel
173+ && priv.focusedAppDelegate.restoreFromMaximized();
174+ onFocusMaximizedApp: if (priv.foregroundMaximizedAppIdIndex != -1) {
175+ ApplicationManager.focusApplication(appRepeater.itemAt(priv.foregroundMaximizedAppIdIndex).appId);
176+ }
178 }
177 }
179
178
180 Binding {
179 Binding {
181 target: PanelState
180 target: PanelState
182 property: "buttonsVisible"
181 property: "buttonsVisible"
183− value: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.maximized
182+ value: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.maximized // FIXME for Locally integrated menus
183+ && spread.state == ""
184 }
184 }
185
185
186 Binding {
186 Binding {
187 target: PanelState
187 target: PanelState
188 property: "title"
188 property: "title"
189+ value: {
190+ if (priv.focusedAppDelegate !== null && spread.state == "") {
191+ if (priv.focusedAppDelegate.maximized)
189− value: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.title
192+ return priv.focusedAppDelegate.title
193+ else
194+ return priv.focusedAppDelegate.appName
195+ }
196+ return ""
197+ }
190− when: priv.focusedAppDelegate && priv.focusedAppDelegate.maximized
198+ when: priv.focusedAppDelegate
191 }
199 }
192
200
201+ Binding {
202+ target: PanelState
203+ property: "dropShadow"
204+ value: priv.focusedAppDelegate && !priv.focusedAppDelegate.maximized && priv.foregroundMaximizedAppIdIndex !== -1
205+ }
206+
207+ Component.onDestruction: {
208+ PanelState.title = "";
193− Component.onDestruction: PanelState.buttonsVisible = false;
209+ PanelState.buttonsVisible = false;
210+ PanelState.dropShadow = false;
211+ }
194
212
195 FocusScope {
213 FocusScope {
196 id: appContainer
214 id: appContainer
@@ -211,19 +229,16 @@ AbstractStage {
211 model: ApplicationManager
229 model: ApplicationManager
212 objectName: "appRepeater"
230 objectName: "appRepeater"
213
231
214− onItemAdded: priv.updateForegroundMaximizedApp()
215− onItemRemoved: priv.updateForegroundMaximizedApp()
216−
217 delegate: FocusScope {
232 delegate: FocusScope {
218 id: appDelegate
233 id: appDelegate
219 objectName: "appDelegate_" + appId
234 objectName: "appDelegate_" + appId
220 z: ApplicationManager.count - index
235 z: ApplicationManager.count - index
221− y: units.gu(3)
236+ y: PanelState.panelHeight
237+ focus: appId === priv.focusedAppId
222 width: decoratedWindow.width
238 width: decoratedWindow.width
223 height: decoratedWindow.height
239 height: decoratedWindow.height
224 property alias requestedWidth: decoratedWindow.requestedWidth
240 property alias requestedWidth: decoratedWindow.requestedWidth
225 property alias requestedHeight: decoratedWindow.requestedHeight
241 property alias requestedHeight: decoratedWindow.requestedHeight
226− focus: model.appId === priv.focusedAppId
227
242
228 QtObject {
243 QtObject {
229 id: appDelegatePrivate
244 id: appDelegatePrivate
@@ -240,6 +255,7 @@ AbstractStage {
240 readonly property string appId: model.appId
255 readonly property string appId: model.appId
241 property bool animationsEnabled: true
256 property bool animationsEnabled: true
242 property alias title: decoratedWindow.title
257 property alias title: decoratedWindow.title
258+ readonly property string appName: model.name
243 property bool visuallyMaximized: false
259 property bool visuallyMaximized: false
244 property bool visuallyMinimized: false
260 property bool visuallyMinimized: false
245
261
@@ -249,7 +265,6 @@ AbstractStage {
249 }
265 }
250 }
266 }
251
267
252− onZChanged: priv.updateForegroundMaximizedApp()
253 onVisuallyMaximizedChanged: priv.updateForegroundMaximizedApp()
268 onVisuallyMaximizedChanged: priv.updateForegroundMaximizedApp()
254
269
255 visible: !visuallyMinimized &&
270 visible: !visuallyMinimized &&
@@ -289,17 +304,16 @@ AbstractStage {
289 }
304 }
290 function minimize(animated) {
305 function minimize(animated) {
291 animationsEnabled = (animated === undefined) || animated;
306 animationsEnabled = (animated === undefined) || animated;
292− appDelegatePrivate.maximized = false;
293 appDelegatePrivate.minimized = true;
307 appDelegatePrivate.minimized = true;
294 }
308 }
295− function restore(animated) {
309+ function restoreFromMaximized(animated) {
296 animationsEnabled = (animated === undefined) || animated;
310 animationsEnabled = (animated === undefined) || animated;
297 appDelegatePrivate.minimized = false;
311 appDelegatePrivate.minimized = false;
298 appDelegatePrivate.maximized = false;
312 appDelegatePrivate.maximized = false;
299 appDelegatePrivate.maximizedLeft = false;
313 appDelegatePrivate.maximizedLeft = false;
300 appDelegatePrivate.maximizedRight = false;
314 appDelegatePrivate.maximizedRight = false;
301 }
315 }
302− function restoreFromMinimized(animated) {
316+ function restore(animated) {
303 animationsEnabled = (animated === undefined) || animated;
317 animationsEnabled = (animated === undefined) || animated;
304 appDelegatePrivate.minimized = false;
318 appDelegatePrivate.minimized = false;
305 if (maximized)
319 if (maximized)
@@ -312,6 +326,14 @@ AbstractStage {
312 }
326 }
313
327
314 states: [
328 states: [
329+ State {
330+ name: "fullscreen"; when: decoratedWindow.fullscreen
331+ extend: "maximized"
332+ PropertyChanges {
333+ target: appDelegate;
334+ y: -PanelState.panelHeight
335+ }
336+ },
315 State {
337 State {
316 name: "normal";
338 name: "normal";
317 when: !appDelegate.maximized && !appDelegate.minimized
339 when: !appDelegate.maximized && !appDelegate.minimized
@@ -323,7 +345,7 @@ AbstractStage {
323 }
345 }
324 },
346 },
325 State {
347 State {
326− name: "maximized"; when: appDelegate.maximized
348+ name: "maximized"; when: appDelegate.maximized && !appDelegate.minimized
327 PropertyChanges {
349 PropertyChanges {
328 target: appDelegate;
350 target: appDelegate;
329 x: 0; y: 0;
351 x: 0; y: 0;
@@ -334,11 +356,13 @@ AbstractStage {
334 },
356 },
335 State {
357 State {
336 name: "maximizedLeft"; when: appDelegate.maximizedLeft && !appDelegate.minimized
358 name: "maximizedLeft"; when: appDelegate.maximizedLeft && !appDelegate.minimized
337− PropertyChanges { target: appDelegate; x: 0; y: units.gu(3); width: root.width/2; height: root.height - units.gu(3) }
359+ PropertyChanges { target: appDelegate; x: 0; y: PanelState.panelHeight;
360+ requestedWidth: root.width/2; requestedHeight: root.height - PanelState.panelHeight }
338 },
361 },
339 State {
362 State {
340 name: "maximizedRight"; when: appDelegate.maximizedRight && !appDelegate.minimized
363 name: "maximizedRight"; when: appDelegate.maximizedRight && !appDelegate.minimized
341− PropertyChanges { target: appDelegate; x: root.width/2; y: units.gu(3); width: root.width/2; height: root.height - units.gu(3) }
364+ PropertyChanges { target: appDelegate; x: root.width/2; y: PanelState.panelHeight;
365+ requestedWidth: root.width/2; requestedHeight: root.height - PanelState.panelHeight }
342 },
366 },
343 State {
367 State {
344 name: "minimized"; when: appDelegate.minimized
368 name: "minimized"; when: appDelegate.minimized
@@ -359,15 +383,6 @@ AbstractStage {
359 PropertyAction { target: appDelegate; properties: "visuallyMinimized,visuallyMaximized" }
383 PropertyAction { target: appDelegate; properties: "visuallyMinimized,visuallyMaximized" }
360 UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,requestedWidth,requestedHeight,scale"; duration: UbuntuAnimation.FastDuration }
384 UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,requestedWidth,requestedHeight,scale"; duration: UbuntuAnimation.FastDuration }
361 },
385 },
362− Transition {
363− to: "maximized"
364− enabled: appDelegate.animationsEnabled
365− PropertyAction { target: appDelegate; property: "visuallyMinimized" }
366− SequentialAnimation {
367− UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,requestedWidth,requestedHeight,scale"; duration: UbuntuAnimation.FastDuration }
368− PropertyAction { target: appDelegate; property: "visuallyMaximized" }
369− }
370− },
371 Transition {
386 Transition {
372 to: "minimized"
387 to: "minimized"
373 enabled: appDelegate.animationsEnabled
388 enabled: appDelegate.animationsEnabled
@@ -377,12 +392,21 @@ AbstractStage {
377 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
392 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
378 ScriptAction {
393 ScriptAction {
379 script: {
394 script: {
380− if (appDelegate.animationsEnabled && state === "minimized" ) {
395+ if (appDelegate.minimized) {
381 priv.focusNext();
396 priv.focusNext();
382 }
397 }
383 }
398 }
384 }
399 }
385 }
400 }
401+ },
402+ Transition {
403+ to: "*" //maximized and fullscreen
404+ enabled: appDelegate.animationsEnabled
405+ PropertyAction { target: appDelegate; property: "visuallyMinimized" }
406+ SequentialAnimation {
407+ UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,requestedWidth,requestedHeight,scale"; duration: UbuntuAnimation.FastDuration }
408+ PropertyAction { target: appDelegate; property: "visuallyMaximized" }
409+ }
386 }
410 }
387 ]
411 ]
388
412
@@ -418,8 +442,8 @@ AbstractStage {
418
442
419 onClose: ApplicationManager.stopApplication(model.appId)
443 onClose: ApplicationManager.stopApplication(model.appId)
420 onMaximize: appDelegate.maximized || appDelegate.maximizedLeft || appDelegate.maximizedRight
444 onMaximize: appDelegate.maximized || appDelegate.maximizedLeft || appDelegate.maximizedRight
421− ? appDelegate.restore() : appDelegate.maximize()
445+ ? appDelegate.restoreFromMaximized() : appDelegate.maximize()
422− onMinimize: appDelegate.minimize(true)
446+ onMinimize: appDelegate.minimize()
423 onDecorationPressed: { ApplicationManager.focusApplication(model.appId) }
447 onDecorationPressed: { ApplicationManager.focusApplication(model.appId) }
424 }
448 }
425 }
449 }
qml/Stages/WindowDecoration.qml+15−10
@@ -33,6 +33,8 @@ MouseArea {
33 signal minimize()
33 signal minimize()
34 signal maximize()
34 signal maximize()
35
35
36+ onDoubleClicked: root.maximize()
37+
36 QtObject {
38 QtObject {
37 id: priv
39 id: priv
38 property real distanceX
40 property real distanceX
@@ -65,20 +67,23 @@ MouseArea {
65 anchors.fill: parent
67 anchors.fill: parent
66 anchors.bottomMargin: -radius
68 anchors.bottomMargin: -radius
67 radius: units.gu(.5)
69 radius: units.gu(.5)
68− gradient: Gradient {
70+ color: "#333333"
69− GradientStop { color: "#626055"; position: 0 }
70− GradientStop { color: "#3C3B37"; position: 1 }
71− }
72 }
71 }
73
72
74 Row {
73 Row {
74+ anchors {
75− anchors { fill: parent; margins: units.gu(0.7) }
75+ fill: parent
76+ leftMargin: units.gu(1)
77+ rightMargin: units.gu(1)
78+ topMargin: units.gu(0.5)
79+ bottomMargin: units.gu(0.5)
80+ }
76− spacing: units.gu(1)
81+ spacing: units.gu(3)
77− opacity: root.active ? 1 : 0.5
78
82
79 WindowControlButtons {
83 WindowControlButtons {
80 id: buttons
84 id: buttons
81 height: parent.height
85 height: parent.height
86+ active: root.active
82 onClose: root.close();
87 onClose: root.close();
83 onMinimize: root.minimize();
88 onMinimize: root.minimize();
84 onMaximize: root.maximize();
89 onMaximize: root.maximize();
@@ -87,12 +92,12 @@ MouseArea {
87 Label {
92 Label {
88 id: titleLabel
93 id: titleLabel
89 objectName: "windowDecorationTitle"
94 objectName: "windowDecorationTitle"
90− color: "#DFDBD2"
95+ color: root.active ? "white" : "#5d5d5d"
91 height: parent.height
96 height: parent.height
92 width: parent.width - buttons.width - parent.anchors.rightMargin - parent.anchors.leftMargin
97 width: parent.width - buttons.width - parent.anchors.rightMargin - parent.anchors.leftMargin
93 verticalAlignment: Text.AlignVCenter
98 verticalAlignment: Text.AlignVCenter
94− fontSize: "small"
99+ fontSize: "medium"
95− font.bold: true
100+ font.weight: root.active ? Font.Light : Font.Normal
96 elide: Text.ElideRight
101 elide: Text.ElideRight
97 }
102 }
98 }
103 }
qml/Stages/WindowResizeArea.qml+1−1
@@ -25,7 +25,7 @@ MouseArea {
25 anchors.fill: target
25 anchors.fill: target
26 anchors.margins: -borderThickness
26 anchors.margins: -borderThickness
27
27
28− hoverEnabled: true
28+ hoverEnabled: target && !target.maximized // don't grab the resize under the panel
29
29
30 property var windowStateStorage: WindowStateStorage
30 property var windowStateStorage: WindowStateStorage
31
31
tests/qmltests/Panel/tst_Panel.qml+44−10
@@ -23,6 +23,7 @@ import Unity.Application 0.1
23import Unity.Indicators 0.1 as Indicators
23import Unity.Indicators 0.1 as Indicators
24import Ubuntu.Telephony 0.1 as Telephony
24import Ubuntu.Telephony 0.1 as Telephony
25import "../../../qml/Panel"
25import "../../../qml/Panel"
26+import "../../../qml/Components/PanelState"
26
27
27IndicatorTest {
28IndicatorTest {
28 id: root
29 id: root
@@ -30,6 +31,12 @@ IndicatorTest {
30 height: units.gu(71)
31 height: units.gu(71)
31 color: "white"
32 color: "white"
32
33
34+ Binding {
35+ target: mouseEmulation
36+ property: "checked"
37+ value: !windowControlsCB.checked
38+ }
39+
33 RowLayout {
40 RowLayout {
34 anchors.fill: parent
41 anchors.fill: parent
35 anchors.margins: units.gu(1)
42 anchors.margins: units.gu(1)
@@ -45,17 +52,18 @@ IndicatorTest {
45 MouseArea {
52 MouseArea {
46 id: backgroundMouseArea
53 id: backgroundMouseArea
47 anchors.fill: parent
54 anchors.fill: parent
55+ hoverEnabled: true
56+
57+ Panel {
58+ id: panel
59+ anchors.fill: parent
60+ indicators {
61+ width: parent.width > units.gu(60) ? units.gu(40) : parent.width
62+ indicatorsModel: root.indicatorsModel
48− }
63+ }
49
64
50− Panel {
65+ property real panelAndSeparatorHeight: panel.indicators.minimizedPanelHeight
51− id: panel
52− anchors.fill: parent
53− indicators {
54− width: parent.width > units.gu(60) ? units.gu(40) : parent.width
55− indicatorsModel: root.indicatorsModel
56 }
66 }
57−
58− property real panelAndSeparatorHeight: panel.indicators.minimizedPanelHeight + units.dp(2)
59 }
67 }
60 }
68 }
61
69
@@ -93,6 +101,32 @@ IndicatorTest {
93 }
101 }
94 }
102 }
95
103
104+ RowLayout {
105+ Layout.fillWidth: true
106+ CheckBox {
107+ id: windowControlsCB
108+ onClicked: PanelState.buttonsVisible = checked
109+ }
110+ Label {
111+ text: "Show window controls"
112+ }
113+ }
114+
115+ RowLayout {
116+ Layout.fillWidth: true
117+ CheckBox {
118+ onClicked: {
119+ if (checked)
120+ PanelState.title = "Fake window title"
121+ else
122+ PanelState.title = ""
123+ }
124+ }
125+ Label {
126+ text: "Show fake window title"
127+ }
128+ }
129+
96 Rectangle {
130 Rectangle {
97 Layout.preferredHeight: units.dp(1);
131 Layout.preferredHeight: units.dp(1);
98 Layout.fillWidth: true;
132 Layout.fillWidth: true;
@@ -127,7 +161,7 @@ IndicatorTest {
127 color: "black"
161 color: "black"
128 }
162 }
129
163
130− MouseTouchEmulationCheckbox {}
164+ MouseTouchEmulationCheckbox { id: mouseEmulation }
131 }
165 }
132 }
166 }
133
167
tests/qmltests/Stages/tst_DesktopStage.qml+65−27
@@ -46,8 +46,8 @@ Item {
46 WindowStateStorage.geometry = {
46 WindowStateStorage.geometry = {
47 'unity8-dash': Qt.rect(0, units.gu(3), units.gu(50), units.gu(40)),
47 'unity8-dash': Qt.rect(0, units.gu(3), units.gu(50), units.gu(40)),
48 'dialer-app': Qt.rect(units.gu(51), units.gu(3), units.gu(50), units.gu(40)),
48 'dialer-app': Qt.rect(units.gu(51), units.gu(3), units.gu(50), units.gu(40)),
49− 'camera-app': Qt.rect(0, units.gu(44), units.gu(50), units.gu(40)),
49+ 'gmail-webapp': Qt.rect(0, units.gu(44), units.gu(50), units.gu(40)),
50− 'gallery-app': Qt.rect(units.gu(51), units.gu(44), units.gu(50), units.gu(40))
50+ 'twitter-webapp': Qt.rect(units.gu(51), units.gu(44), units.gu(50), units.gu(40))
51 }
51 }
52 }
52 }
53
53
@@ -60,10 +60,14 @@ Item {
60
60
61 focus: true
61 focus: true
62
62
63+ property bool itemDestroyed: false
63 sourceComponent: Component {
64 sourceComponent: Component {
64 DesktopStage {
65 DesktopStage {
65 color: "darkblue"
66 color: "darkblue"
66 anchors.fill: parent
67 anchors.fill: parent
68+ Component.onDestruction: {
69+ desktopStageLoader.itemDestroyed = true;
70+ }
67 orientations: Orientations {}
71 orientations: Orientations {}
68 }
72 }
69 }
73 }
@@ -114,10 +118,16 @@ Item {
114 property Item desktopStage: desktopStageLoader.status === Loader.Ready ? desktopStageLoader.item : null
118 property Item desktopStage: desktopStageLoader.status === Loader.Ready ? desktopStageLoader.item : null
115
119
116 function cleanup() {
120 function cleanup() {
121+ desktopStageLoader.itemDestroyed = false;
117 desktopStageLoader.active = false;
122 desktopStageLoader.active = false;
118
123
119 tryCompare(desktopStageLoader, "status", Loader.Null);
124 tryCompare(desktopStageLoader, "status", Loader.Null);
120 tryCompare(desktopStageLoader, "item", null);
125 tryCompare(desktopStageLoader, "item", null);
126+ // Loader.status might be Loader.Null and Loader.item might be null but the Loader
127+ // actually took place. Likely because Loader waits until the next event loop
128+ // iteration to do its work. So to ensure the reload, we will wait until the
129+ // Shell instance gets destroyed.
130+ tryCompare(desktopStageLoader, "itemDestroyed", true);
121
131
122 killAllRunningApps();
132 killAllRunningApps();
123
133
@@ -171,8 +181,8 @@ Item {
171
181
172 function test_tappingOnWindowChangesFocusedApp_data() {
182 function test_tappingOnWindowChangesFocusedApp_data() {
173 return [
183 return [
174− {tag: "dash to dialer", apps: [ "unity8-dash", "dialer-app", "camera-app" ], focusfrom: 0, focusTo: 1 },
184+ {tag: "dash to dialer", apps: [ "unity8-dash", "dialer-app", "gmail-webapp"], focusfrom: 0, focusTo: 1 },
175− {tag: "dialer to dash", apps: [ "unity8-dash", "dialer-app", "camera-app" ], focusfrom: 1, focusTo: 0 },
185+ {tag: "dialer to dash", apps: [ "unity8-dash", "dialer-app", "gmail-webapp"], focusfrom: 1, focusTo: 0 }
176 ]
186 ]
177 }
187 }
178
188
@@ -218,8 +228,8 @@ Item {
218
228
219 function test_tappingOnDecorationFocusesApplication_data() {
229 function test_tappingOnDecorationFocusesApplication_data() {
220 return [
230 return [
221− {tag: "dash to dialer", apps: [ "unity8-dash", "dialer-app", "camera-app" ], focusfrom: 0, focusTo: 1 },
231+ {tag: "dash to dialer", apps: [ "unity8-dash", "dialer-app", "gmail-webapp"], focusfrom: 0, focusTo: 1 },
222− {tag: "dialer to dash", apps: [ "unity8-dash", "dialer-app", "camera-app" ], focusfrom: 1, focusTo: 0 },
232+ {tag: "dialer to dash", apps: [ "unity8-dash", "dialer-app", "gmail-webapp"], focusfrom: 1, focusTo: 0 }
223 ]
233 ]
224 }
234 }
225
235
@@ -229,12 +239,16 @@ Item {
229 var fromAppDecoration = findChild(desktopStage, "appWindowDecoration_" + data.apps[data.focusfrom]);
239 var fromAppDecoration = findChild(desktopStage, "appWindowDecoration_" + data.apps[data.focusfrom]);
230 verify(fromAppDecoration);
240 verify(fromAppDecoration);
231 tap(fromAppDecoration);
241 tap(fromAppDecoration);
232− tryCompare(ApplicationManager.findApplication(data.apps[data.focusfrom]).session.surface, "activeFocus", true);
242+ var fromApp = ApplicationManager.findApplication(data.apps[data.focusfrom]);
243+ verify(fromApp);
244+ tryCompare(fromApp.session.surface, "activeFocus", true);
233
245
234 var toAppDecoration = findChild(desktopStage, "appWindowDecoration_" + data.apps[data.focusTo]);
246 var toAppDecoration = findChild(desktopStage, "appWindowDecoration_" + data.apps[data.focusTo]);
235 verify(toAppDecoration);
247 verify(toAppDecoration);
236 tap(toAppDecoration);
248 tap(toAppDecoration);
237− tryCompare(ApplicationManager.findApplication(data.apps[data.focusTo]).session.surface, "activeFocus", true);
249+ var toApp = ApplicationManager.findApplication(data.apps[data.focusTo]);
250+ verify(toApp);
251+ tryCompare(toApp.session.surface, "activeFocus", true);
238 }
252 }
239
253
240 function test_clickingOnDecorationFocusesApplication_data() {
254 function test_clickingOnDecorationFocusesApplication_data() {
@@ -354,26 +368,26 @@ Item {
354 function test_maximizeApplicationHidesSurfacesBehindIt() {
368 function test_maximizeApplicationHidesSurfacesBehindIt() {
355 var dashApp = startApplication("unity8-dash");
369 var dashApp = startApplication("unity8-dash");
356 var dialerApp = startApplication("dialer-app");
370 var dialerApp = startApplication("dialer-app");
357− var cameraApp = startApplication("camera-app");
371+ var gmailApp = startApplication("gmail-webapp");
358
372
359 var dashDelegate = findChild(desktopStage, "appDelegate_unity8-dash");
373 var dashDelegate = findChild(desktopStage, "appDelegate_unity8-dash");
360 verify(dashDelegate);
374 verify(dashDelegate);
361 var dialerDelegate = findChild(desktopStage, "appDelegate_dialer-app");
375 var dialerDelegate = findChild(desktopStage, "appDelegate_dialer-app");
362 verify(dialerDelegate);
376 verify(dialerDelegate);
363− var cameraDelegate = findChild(desktopStage, "appDelegate_camera-app");
377+ var gmailDelegate = findChild(desktopStage, "appDelegate_gmail-webapp");
364− verify(cameraDelegate);
378+ verify(gmailDelegate);
365
379
366 // maximize
380 // maximize
367− findChild(dialerDelegate, "decoratedWindow").maximize();
381+ dialerDelegate.maximize();
368 tryCompare(dialerDelegate, "visuallyMaximized", true);
382 tryCompare(dialerDelegate, "visuallyMaximized", true);
369
383
370 tryCompare(dashApp.session.surface, "visible", false);
384 tryCompare(dashApp.session.surface, "visible", false);
371− compare(cameraApp.session.surface.visible, true);
385+ compare(gmailApp.session.surface.visible, true);
372
386
373 // restore
387 // restore
374− findChild(dialerDelegate, "decoratedWindow").maximize();
388+ dialerDelegate.restoreFromMaximized();
375 compare(dashApp.session.surface.visible, true);
389 compare(dashApp.session.surface.visible, true);
376− compare(cameraApp.session.surface.visible, true);
390+ compare(gmailApp.session.surface.visible, true);
377 }
391 }
378
392
379 function test_applicationsBecomeVisibleWhenOccludingAppRemoved() {
393 function test_applicationsBecomeVisibleWhenOccludingAppRemoved() {
@@ -385,28 +399,52 @@ Item {
385 var dialerDelegate = findChild(desktopStage, "appDelegate_dialer-app");
399 var dialerDelegate = findChild(desktopStage, "appDelegate_dialer-app");
386 verify(dialerDelegate);
400 verify(dialerDelegate);
387
401
402+ var dialerMaximizeButton = findChild(dialerDelegate, "maximizeWindowButton");
403+ verify(dialerMaximizeButton);
404+ mouseClick(dialerMaximizeButton);
405+
388− var cameraApp = startApplication("camera-app");
406+ var mapApp = startApplication("map");
389− var cameraDelegate = findChild(desktopStage, "appDelegate_camera-app");
407+ var mapDelegate = findChild(desktopStage, "appDelegate_map");
390− verify(cameraDelegate);
408+ verify(mapDelegate);
391− findChild(dialerDelegate, "decoratedWindow").maximize();
392
409
393− var galleryApp = startApplication("gallery-app");
410+ var gmailApp = startApplication("gmail-webapp");
394− var galleryDelegate = findChild(desktopStage, "appDelegate_gallery-app");
411+ var gmailDelegate = findChild(desktopStage, "appDelegate_gmail-webapp");
395− verify(galleryDelegate);
412+ verify(gmailDelegate);
396− findChild(galleryDelegate, "decoratedWindow").maximize();
413+
414+ var gmailMaximizeButton = findChild(gmailDelegate, "maximizeWindowButton");
415+ verify(gmailMaximizeButton);
416+ mouseClick(gmailMaximizeButton);
397
417
398 tryCompare(dialerDelegate, "visuallyMaximized", true);
418 tryCompare(dialerDelegate, "visuallyMaximized", true);
399− tryCompare(galleryDelegate, "visuallyMaximized", true);
419+ tryCompare(gmailDelegate, "visuallyMaximized", true);
400
420
401 tryCompare(dashApp.session.surface, "visible", false);
421 tryCompare(dashApp.session.surface, "visible", false);
402 tryCompare(dialerApp.session.surface, "visible", false);
422 tryCompare(dialerApp.session.surface, "visible", false);
403− tryCompare(cameraApp.session.surface, "visible", false);
423+ tryCompare(mapApp.session.surface, "visible", false);
404
424
405− ApplicationManager.stopApplication("gallery-app");
425+ ApplicationManager.stopApplication("gmail-webapp");
406
426
407− compare(cameraApp.session.surface.visible, true);
427+ compare(mapApp.session.surface.visible, true);
408 tryCompare(dialerApp.session.surface, "visible", true);
428 tryCompare(dialerApp.session.surface, "visible", true);
409 tryCompare(dashApp.session.surface, "visible", false); // still occluded by maximised dialer
429 tryCompare(dashApp.session.surface, "visible", false); // still occluded by maximised dialer
410 }
430 }
431+
432+ function test_maximisedAppStaysVisibleWhenAppStarts() {
433+ var dashApp = startApplication("unity8-dash");
434+ var dashDelegate = findChild(desktopStage, "appDelegate_unity8-dash");
435+ verify(dashDelegate);
436+ // maximize
437+ var dashMaximizeButton = findChild(dashDelegate, "maximizeWindowButton");
438+ verify(dashMaximizeButton);
439+ mouseClick(dashMaximizeButton);
440+ tryCompare(dashDelegate, "visuallyMaximized", true);
441+
442+ var dialerApp = startApplication("dialer-app");
443+ var dialerDelegate = findChild(desktopStage, "appDelegate_dialer-app");
444+ verify(dialerDelegate);
445+
446+ compare(dialerDelegate.visible, true, "Dialer should be visible");
447+ compare(dashDelegate.visible, true, "Dash should still be visible");
448+ }
411 }
449 }
412}
450}
tests/qmltests/tst_Shell.qml−10
@@ -1777,15 +1777,10 @@ Rectangle {
1777 tryCompare(panelButtons, "visible", false);
1777 tryCompare(panelButtons, "visible", false);
1778
1778
1779 appDelegate.maximize(false);
1779 appDelegate.maximize(false);
1780− tryCompare(panelButtons, "visible", true);
1781
1780
1782 shell.usageScenario = "phone";
1781 shell.usageScenario = "phone";
1783 waitForRendering(shell);
1782 waitForRendering(shell);
1784 tryCompare(panelButtons, "visible", false);
1783 tryCompare(panelButtons, "visible", false);
1785−
1786− shell.usageScenario = "desktop";
1787− waitForRendering(shell);
1788− tryCompare(panelButtons, "visible", true);
1789 }
1784 }
1790
1785
1791 function test_lockingGreeterHidesPanelButtons() {
1786 function test_lockingGreeterHidesPanelButtons() {
@@ -1799,15 +1794,10 @@ Rectangle {
1799 tryCompare(panelButtons, "visible", false);
1794 tryCompare(panelButtons, "visible", false);
1800
1795
1801 appDelegate.maximize(false);
1796 appDelegate.maximize(false);
1802− tryCompare(panelButtons, "visible", true);
1803
1797
1804 LightDM.Greeter.showGreeter();
1798 LightDM.Greeter.showGreeter();
1805 waitForRendering(shell);
1799 waitForRendering(shell);
1806 tryCompare(panelButtons, "visible", false);
1800 tryCompare(panelButtons, "visible", false);
1807−
1808− LightDM.Greeter.hideGreeter();
1809− waitForRendering(shell);
1810− tryCompare(panelButtons, "visible", true);
1811 }
1801 }
1812
1802
1813 function test_cantMoveWindowUnderPanel() {
1803 function test_cantMoveWindowUnderPanel() {