~gerboland/unity/8-refactor-wm-and-test

« back to all changes in this revision

Viewing changes to Components/PageHeader.qml

  • Committer: Michał Sawicz
  • Date: 2013-06-05 22:03:08 UTC
  • Revision ID: michal.sawicz@canonical.com-20130605220308-yny8fv3futtr04fg
Inital unity8 commit.

Previous history can be found at https://code.launchpad.net/~unity-team/unity/phablet

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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.0
 
18
import Ubuntu.Components 0.1
 
19
import Ubuntu.Components.Popups 0.1
 
20
import Ubuntu.Components.ListItems 0.1 as ListItem
 
21
 
 
22
Item {
 
23
    /*!
 
24
     \preliminary
 
25
     The text that is shown inside the Page Header
 
26
     \qmlproperty string text
 
27
    */
 
28
    property alias text: label.text
 
29
 
 
30
    property bool searchEntryEnabled: false
 
31
    property alias searchQuery: searchField.text
 
32
    property ListModel searchHistory: SearchHistoryModel {}
 
33
 
 
34
    height: units.gu(8.5)
 
35
    implicitHeight: units.gu(8.5)
 
36
 
 
37
    function triggerSearch() {
 
38
        if (searchEntryEnabled) searchField.forceActiveFocus()
 
39
    }
 
40
 
 
41
    function resetSearch() {
 
42
        if (!searchHistory) return;
 
43
 
 
44
        searchHistory.addQuery(searchField.text);
 
45
        searchField.text = "";
 
46
        searchField.focus = false;
 
47
    }
 
48
 
 
49
    Connections {
 
50
        target: greeter
 
51
        onShownChanged: if (shown) resetSearch()
 
52
    }
 
53
 
 
54
    Flickable {
 
55
        id: header
 
56
        anchors {
 
57
            left: parent.left
 
58
            right: parent.right
 
59
            top: parent.top
 
60
        }
 
61
        height: units.gu(6.5)
 
62
 
 
63
        interactive: false
 
64
        contentHeight: headerContainer.height
 
65
        clip: true
 
66
 
 
67
        contentY: searchField.activeFocus || searchField.text != "" ? searchContainer.y : headerContainer.y
 
68
 
 
69
        Behavior on contentY { NumberAnimation { duration: 200; easing.type: Easing.OutQuad } }
 
70
 
 
71
        // FIXME this could potentially be simplified to avoid all the containers
 
72
        Item {
 
73
            id: headerContainer
 
74
 
 
75
            width: parent.width
 
76
            height: childrenRect.height
 
77
 
 
78
            Item {
 
79
                id: textContainer
 
80
 
 
81
                width: header.width
 
82
                height: header.height
 
83
 
 
84
                Label {
 
85
                    id: label
 
86
                    anchors {
 
87
                        left: parent.left
 
88
                        leftMargin: units.gu(2)
 
89
                        right: parent.right
 
90
                        verticalCenter: parent.verticalCenter
 
91
                    }
 
92
 
 
93
                    color: "#f3f3e7"
 
94
                    opacity: 0.8
 
95
                    font.family: "Ubuntu"
 
96
                    font.weight: Font.Light
 
97
                    fontSize: "x-large"
 
98
                    elide: Text.ElideRight
 
99
                    style: Text.Raised
 
100
                    styleColor: "black"
 
101
                }
 
102
            }
 
103
 
 
104
            Item {
 
105
                id: searchContainer
 
106
                objectName: "searchContainer"
 
107
 
 
108
                visible: searchEntryEnabled
 
109
 
 
110
                property bool narrowMode: parent.width < label.contentWidth + units.gu(50)
 
111
 
 
112
                property bool active: searchField.text != "" || searchField.activeFocus
 
113
                property var popover: null
 
114
 
 
115
                anchors.right: textContainer.right
 
116
                height: header.height
 
117
 
 
118
                state:
 
119
                    if (active && narrowMode) "narrowActive"
 
120
                    else if (!active && narrowMode) "narrowInactive"
 
121
                    else if (active && !narrowMode) "active"
 
122
                    else if (!active && !narrowMode) "inactive"
 
123
 
 
124
                function openPopover() {
 
125
                    if (searchHistory.count > 0) {
 
126
                        searchContainer.popover = PopupUtils.open(popoverComponent, searchField,
 
127
                                                                  {
 
128
                                                                      "pointerTarget": pointerPositioner,
 
129
                                                                      "contentWidth": searchField.width,
 
130
                                                                      "edgeMargins": units.gu(1)
 
131
                                                                  }
 
132
                                                                 )
 
133
                    }
 
134
                }
 
135
 
 
136
                function closePopover() {
 
137
                    if (searchContainer.popover) PopupUtils.close(searchContainer.popover)
 
138
                }
 
139
 
 
140
                onActiveFocusChanged: if (!activeFocus) { searchHistory.addQuery(searchField.text) }
 
141
 
 
142
                TextField {
 
143
                    id: searchField
 
144
 
 
145
                    anchors.fill: parent
 
146
                    anchors.margins: units.gu(1)
 
147
 
 
148
                    hasClearButton: false
 
149
 
 
150
                    primaryItem: Button {
 
151
                        enabled: searchField.text != ""
 
152
                        ItemStyle.class: "transparent-button"
 
153
                        onClicked: {
 
154
                            if (searchField.text != "") {
 
155
                                searchHistory.addQuery(searchField.text)
 
156
                                searchField.text = ""
 
157
                            }
 
158
                        }
 
159
                        height: parent.height
 
160
                        width: height
 
161
 
 
162
                        Image {
 
163
                            id: primaryImage
 
164
                            anchors {
 
165
                                verticalCenter: parent.verticalCenter
 
166
                                left: parent.left
 
167
                                leftMargin: units.gu(0.5)
 
168
                            }
 
169
                            width: units.gu(3)
 
170
                            height: units.gu(3)
 
171
                        }
 
172
 
 
173
                        Item {
 
174
                            id: pointerPositioner
 
175
                            anchors.left: parent.right
 
176
                            anchors.leftMargin: units.gu(0.5)
 
177
                            anchors.top: parent.bottom
 
178
                        }
 
179
                    }
 
180
 
 
181
                    onTextChanged: {
 
182
                        if (text != "") searchContainer.closePopover()
 
183
                        else if (text == "" && activeFocus) searchContainer.openPopover()
 
184
                    }
 
185
 
 
186
                    onActiveFocusChanged: {
 
187
                        if (!activeFocus) searchContainer.closePopover()
 
188
                    }
 
189
                }
 
190
 
 
191
                states: [
 
192
                    State {
 
193
                        name: "wide"
 
194
                        AnchorChanges { target: textContainer; anchors.top: headerContainer.top }
 
195
                        AnchorChanges { target: searchContainer; anchors.left: undefined; anchors.top: textContainer.top }
 
196
                    },
 
197
                    State {
 
198
                        name: "narrow"
 
199
                        PropertyChanges { target: searchField; highlighted: true }
 
200
                        AnchorChanges { target: textContainer; anchors.top: searchContainer.bottom }
 
201
                        AnchorChanges { target: searchContainer; anchors.left: headerContainer.left; anchors.top: headerContainer.top }
 
202
                    },
 
203
                    State {
 
204
                        name: "active"
 
205
                        extend: "wide"
 
206
                        PropertyChanges { target: searchContainer; width: units.gu(40) }
 
207
                        PropertyChanges { target: primaryImage; source: searchField.text ? "../Dash/graphics/icon_clear.png" : "../Dash/graphics/icon_search_active.png" }
 
208
                        PropertyChanges { target: searchField; highlighted: true }
 
209
                    },
 
210
                    State {
 
211
                        name: "inactive"
 
212
                        extend: "wide"
 
213
                        PropertyChanges { target: searchContainer; width: units.gu(25) }
 
214
                        PropertyChanges { target: primaryImage; source: "../Dash/graphics/icon_search_inactive.png" }
 
215
                        PropertyChanges { target: searchField; highlighted: false }
 
216
                    },
 
217
                    State {
 
218
                        name: "narrowActive"
 
219
                        extend: "narrow"
 
220
                        PropertyChanges { target: header; contentY: 0 }
 
221
                        PropertyChanges { target: primaryImage; source: searchField.text ? "../Dash/graphics/icon_clear.png" : "../Dash/graphics/icon_search_active.png" }
 
222
                    },
 
223
                    State {
 
224
                        name: "narrowInactive"
 
225
                        extend: "narrow"
 
226
                        PropertyChanges { target: header; contentY: header.height }
 
227
                        PropertyChanges { target: primaryImage; source: searchField.text ? "../Dash/graphics/icon_clear.png" : "../Dash/graphics/icon_search_active.png" }
 
228
                    }
 
229
                ]
 
230
 
 
231
                transitions: [
 
232
                    Transition {
 
233
                        to: "active"
 
234
                        SequentialAnimation {
 
235
                            ParallelAnimation {
 
236
                                NumberAnimation { targets: [searchContainer, searchField]; property: "width"; duration: 200; easing.type: Easing.InOutQuad }
 
237
                                PropertyAction  { target: primaryImage; property: "source" }
 
238
                                AnchorAnimation { targets: [searchContainer, textContainer]; duration: 200; easing.type: Easing.InOutQuad }
 
239
                            }
 
240
                            ScriptAction { script: searchContainer.openPopover() }
 
241
                        }
 
242
                    },
 
243
                    Transition {
 
244
                        to: "inactive"
 
245
                        ScriptAction { script: searchContainer.closePopover() }
 
246
                        NumberAnimation { targets: [searchContainer, searchField] ; property: "width"; duration: 200; easing.type: Easing.InOutQuad }
 
247
                        AnchorAnimation { targets: [searchContainer, textContainer]; duration: 200; easing.type: Easing.InOutQuad }
 
248
                    },
 
249
                    Transition {
 
250
                        to: "narrowActive"
 
251
                        SequentialAnimation {
 
252
                            ParallelAnimation {
 
253
                                NumberAnimation { targets: [searchContainer, searchField] ; property: "width"; duration: 200; easing.type: Easing.OutQuad }
 
254
                                AnchorAnimation { targets: [searchContainer, textContainer]; duration: 200; easing.type: Easing.InOutQuad }
 
255
                            }
 
256
                            ScriptAction { script: searchContainer.openPopover() }
 
257
                        }
 
258
                    },
 
259
                    Transition {
 
260
                        to: "narrowInactive"
 
261
                        ScriptAction { script: searchContainer.closePopover() }
 
262
                        NumberAnimation { targets: [searchContainer, searchField] ; property: "width"; duration: 200; easing.type: Easing.OutQuad }
 
263
                        AnchorAnimation { targets: [searchContainer, textContainer]; duration: 200; easing.type: Easing.InOutQuad }
 
264
                    }
 
265
                ]
 
266
 
 
267
                Component {
 
268
                    id: popoverComponent
 
269
                    Popover {
 
270
                        id: popover
 
271
 
 
272
                        // FIXME: this should go into the first item below, but enable: false
 
273
                        // prevents mouse events propagation
 
274
                        Button {
 
275
                            ItemStyle.class: "transparent-button"
 
276
                            anchors {
 
277
                                top: parent.top
 
278
                                right: parent.right
 
279
                            }
 
280
                            height: units.gu(6)
 
281
                            width: height
 
282
 
 
283
                            onClicked: searchContainer.closePopover()
 
284
 
 
285
                            Image {
 
286
                                anchors.centerIn: parent
 
287
                                width: units.gu(2)
 
288
                                height: units.gu(2)
 
289
                                source: "../Dash/graphics/icon_listview_clear.png"
 
290
                            }
 
291
                        }
 
292
 
 
293
                        Column {
 
294
                            anchors {
 
295
                                top: parent.top
 
296
                                left: parent.left
 
297
                                right: parent.right
 
298
                            }
 
299
 
 
300
                            ListItem.Standard { enabled: false; text: i18n.tr("Recent searches") }
 
301
 
 
302
                            Repeater {
 
303
                                id: recentSearches
 
304
                                model: searchHistory
 
305
 
 
306
                                delegate: ListItem.Standard {
 
307
                                    showDivider: index < recentSearches.count - 1
 
308
                                    text: query
 
309
                                    onClicked: {
 
310
                                        searchHistory.addQuery(text)
 
311
                                        searchField.text = text
 
312
                                    }
 
313
                                }
 
314
                            }
 
315
                        }
 
316
                    }
 
317
                }
 
318
 
 
319
                InverseMouseArea {
 
320
                    enabled: searchField.activeFocus
 
321
 
 
322
                    anchors {
 
323
                        top: parent.top
 
324
                        left: parent.left
 
325
                        right: parent.right
 
326
                    }
 
327
 
 
328
                    height: searchContainer.popover ? parent.height + searchContainer.popover.contentHeight + units.gu(2) : parent.height
 
329
 
 
330
                    onPressed: searchField.focus = false
 
331
                }
 
332
            }
 
333
        }
 
334
    }
 
335
 
 
336
    BorderImage {
 
337
        id: bottomBorder
 
338
        anchors {
 
339
            top: header.bottom
 
340
            left: parent.left
 
341
            right: parent.right
 
342
            bottom: parent.bottom
 
343
        }
 
344
 
 
345
        source: "graphics/PageHeaderBaseDivider.sci"
 
346
    }
 
347
}