1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
|
/*
* This file is part of unity-2d
*
* Copyright 2010-2011 Canonical Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 1.1
import "utils.js" as Utils
/*
Each SpreadWindow represents a real window on the desktop (we are
passed a WindowInfo object with all the information about it).
Its state ("" or "spread") decides which mode the window should
follow to position itself on screen ("screen" or "spread" mode
respectively).
In screen mode we use the real window's position and size to exactly mimic it.
In spread mode, we are assigned a cell in the spread, and we resize
and reposition ourselves to be fully constrained and centered inside
that specific cell.
The shot should occupy as much space as possible inside the cell,
but never be bigger than its original window's size, and always
maintain the same aspect ratio as the original window.
*/
Item {
id: window
property variant windowInfo
property bool animating
/* Maintain the selection status of this item to adjust visual appearence,
but never change it from inside the component. Since all selection logic
need to be managed outside of the component due to interaction with keyboard,
we just forward mouse signals. */
property bool isSelected
signal clicked
signal entered
signal exited
property bool enableBehaviors: false
/* Screenshot of the window, minus the decorations. The actual image is
obtained via the WindowImageProvider which serves the "image://window/*" source URIs.
Please note that the screenshot is taken at the moment the source property is
actually assigned, during component initialization.
If taking the screenshot fails (for example for minimized windows), then this
is hidden and the icon box (see "icon_box" below) is shown. */
Image {
id: shot
anchors.fill: parent
fillMode: Image.Stretch
source: "image://window/" + windowInfo.decoratedXid + "|"
+ windowInfo.contentXid
cache: false
/* The window is scaled to a rectangle as large as possible inside
sourceSize while preserving its aspect ratio.
It saves video memory when using the OpenGL backend.
It makes scaling cheaper in the spread when using the raster backend.
*/
sourceSize.width: 512
sourceSize.height: 512
/* Disabled during animations for performance reasons */
smooth: !animating
visible: (status != Image.Error)
}
/* This replaces the shot whenever retrieving its image fails.
It is essentially a white rectangle of the same size as the shot,
with a border and the window icon floating in the center.
*/
Rectangle {
id: iconBox
anchors.fill: parent
border.width: 1
border.color: "black"
visible: (shot.status == Image.Error)
Image {
source: "image://icons/" + windowInfo.icon
asynchronous: true
anchors.centerIn: parent
fillMode: Image.PreserveAspectFit
/* Please note that sourceSize is necessary, otherwise the
IconImageProvider will crash when loading the icon */
height: 48
width: 48
sourceSize { width: width; height: height }
}
}
Item {
id: overlay
anchors.fill: parent
/* A label with the window title centered over the shot.
It will appear only for the currently selected window. See overlay.states */
Rectangle {
id: labelBox
/* The width of the box around the text should be the same as
the text itself, with 3 pixels of margin on all sides, but it should also
never overflow the shot's borders.
Normally one would just set anchors.margins, but this can't work
here because first we need to let the Text calculate it's "natural" width
("paintedWidth" in QT terms) -- that is, the size it would have
ìf free to expand horizontally unconstrained, to know if it's smaller than
the labelBox or not.
However if we bind the Text's width to the width of the labelBox, and the
width of the labelBox to the Text's size, we get a binding loop error.
The trick is to bind the Text's width to the labelBox's parent, and then
the labelBox to the Text's size. Since the relation between labelBox and
parent is taken care of by the positioner indirectly, there's no loop.
Yeah, messy. Blame QML ;)
*/
property int labelMargins: 6
width: Math.min(parent.width, label.paintedWidth + labelMargins)
height: label.height + labelMargins
anchors.centerIn: parent
/* This equals backgroundColor: "black" and opacity: 0.6
but we don't want to set it that way since it would be
inherited by the Text child, and we want it to be fully
opaque instead */
color: "#99000000"
radius: 3
visible: window.isSelected
Text {
id: label
anchors.centerIn: parent
width: overlay.width - parent.labelMargins
text: windowInfo.title
elide: Text.ElideRight
horizontalAlignment: Text.AlignHCenter
property real originalFontSize
Component.onCompleted: {
originalFontSize = font.pointSize
}
color: "white"
}
}
}
MouseArea {
id: mouseArea
width: shot.paintedWidth
height: shot.paintedHeight
anchors.centerIn: parent
hoverEnabled: true
onClicked: window.clicked()
onEntered: window.entered()
onExited: window.exited()
}
}
|