Unity 8
ChildWindow.qml
1 /*
2  * Copyright (C) 2016-2017 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 import Ubuntu.Components 1.3
19 import Unity.Application 0.1
20 
21 FocusScope {
22  id: root
23  objectName: "childWindow"
24 
25  // Set from outside.
26  property var surface
27  property Item boundsItem
28  property Item target
29  property alias requestedWidth: surfaceContainer.requestedWidth
30  property alias requestedHeight: surfaceContainer.requestedHeight
31  property real decorationHeight
32 
33  width: surface ? surface.size.width : 0
34  height: surface ? surface.size.height : 0
35 
36  // Make it get shown and hidden with a fade in/out effect
37  opacity: surface && surface.state !== Mir.MinimizedState && surface.state !== Mir.HiddenState ? 1.0 : 0.0
38  Behavior on opacity { UbuntuNumberAnimation {} }
39  visible: opacity !== 0.0 // make it transparent to input as well
40 
41  readonly property bool dragging: windowResizeArea.dragging || d.touchOverlayDragging || d.moveHandlerDragging
42 
43  QtObject {
44  id: d
45  readonly property bool decorated: surface ? surface.type === Mir.UtilityType
46  || surface.type === Mir.DialogType
47  || surface.type === Mir.NormalType
48  || surface.type === Mir.SatelliteType
49  : false
50 
51  readonly property bool moveable: decorated
52  readonly property bool resizeable: decorated
53 
54  property alias decoration: decorationLoader.item
55  property alias moveHandler: moveHandlerLoader.item
56 
57  readonly property bool touchOverlayDragging: touchOverlayLoader.item ? touchOverlayLoader.item.dragging : false
58  readonly property bool moveHandlerDragging: moveHandlerLoader.item ? moveHandlerLoader.item.dragging : false
59  }
60 
61  WindowResizeArea {
62  id: windowResizeArea
63  anchors {
64  top: decorationLoader.top
65  bottom: parent.bottom
66  left: parent.left; right: parent.right
67  }
68  target: root.target
69  boundsItem: root.boundsItem
70  minWidth: units.gu(10)
71  minHeight: units.gu(10)
72  borderThickness: units.gu(2)
73  enabled: d.resizeable
74  visible: enabled
75  onPressed: root.surface.activate();
76  }
77 
78  BorderImage {
79  property real shadowThickness: root.surface && root.surface.focused ? units.gu(2) : units.gu(1.5)
80  anchors {
81  top: decorationLoader.top
82  bottom: parent.bottom
83  left: parent.left; right: parent.right
84  margins: -shadowThickness
85  }
86  source: "../graphics/dropshadow2gu.sci"
87  opacity: .3
88  }
89 
90  Loader {
91  id: decorationLoader
92  anchors.bottom: root.top
93  anchors.left: root.left
94  anchors.right: root.right
95 
96  visible: active
97  active: d.decorated
98 
99  height: item ? item.height : 0
100 
101  sourceComponent: Component {
102  WindowDecoration {
103  id: windowDecoration
104  height: root.decorationHeight
105  title: root.surface ? root.surface.name : ""
106  active: root.surface ? root.surface.focused : false
107  minimizeButtonVisible: false
108  maximizeButtonShown: false
109  onPressed: root.surface.activate();
110  onPressedChanged: if (d.moveHandler) { d.moveHandler.handlePressedChanged(pressed, pressedButtons, mouseX, mouseY); }
111  onPositionChanged: if (d.moveHandler) {
112  d.moveHandler.handlePositionChanged(mouse);
113  }
114  onReleased: if (d.moveHandler) { d.moveHandler.handleReleased(); }
115  onCloseClicked: root.surface.close();
116  Binding {
117  target: root.surface
118  property: "topMargin"
119  value: windowDecoration.height
120  }
121  }
122  }
123  }
124 
125  Loader {
126  id: moveHandlerLoader
127  active: d.moveable
128  sourceComponent: Component {
129  MoveHandler {
130  target: root.target
131  buttonsWidth: d.decoration ? d.decoration.buttonsWidth : 0
132  boundsItem: root.boundsItem
133  boundsTopMargin: decorationLoader.height
134  }
135  }
136  }
137 
138  SurfaceContainer {
139  id: surfaceContainer
140 
141  // Do not hold on to a dead surface so that it can be destroyed.
142  // FIXME It should not be QML's job to release the MirSurface if its backing surface goes away. Instead backing
143  // MirSurface should go away but the MirSurfaceItem should be able to live on with the last drawn frame
144  // and properties.
145  surface: root.surface && root.surface.live ? root.surface : null
146 
147  requestedWidth: surface ? surface.size.width : 0
148  requestedHeight: surface ? surface.size.height : 0
149 
150  // TODO ChildWindow parent will probably want to control those
151  interactive: true
152  consumesInput: true
153 
154  focus: true
155  }
156 
157  Loader {
158  active: d.moveable
159  anchors.fill: parent
160  sourceComponent: Component {
161  MouseArea {
162  acceptedButtons: Qt.LeftButton
163  property bool dragging: false
164  cursorShape: undefined // don't interfere with the cursor shape set by the underlying MirSurfaceItem
165  onPressed: {
166  if (mouse.button == Qt.LeftButton && mouse.modifiers == Qt.AltModifier) {
167  d.moveHandler.handlePressedChanged(true, Qt.LeftButton, mouse.x, mouse.y);
168  dragging = true;
169  mouse.accepted = true;
170  } else {
171  mouse.accepted = false;
172  }
173  }
174  onPositionChanged: {
175  if (dragging) {
176  d.moveHandler.handlePositionChanged(mouse);
177  }
178  }
179  onReleased: {
180  if (dragging) {
181  d.moveHandler.handlePressedChanged(false, Qt.LeftButton);
182  d.moveHandler.handleReleased();
183  dragging = false;
184  }
185  }
186  }
187  }
188  }
189 
190  Loader {
191  id: touchOverlayLoader
192  active: d.resizeable || d.moveable
193  anchors.top: decorationLoader.top
194  anchors.bottom: parent.bottom
195  anchors.left: parent.left
196  anchors.right: parent.right
197  sourceComponent: Component { WindowControlsOverlay {
198  target: root.target
199  resizeArea: windowResizeArea
200  boundsItem: root.boundsItem
201  } }
202  }
203 }