on 8a968c8 help sign in
unity8/qml/Dash/GenericScopeView.qml
723 lines 31 kB history raw
1/*
2 * Copyright (C) 2013-2014 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 */
17import QtQuick 2.0
18import Ubuntu.Components 1.1
19import Utils 0.1
20import Unity 0.2
21import Dash 0.1
22import "../Components"
23import "../Components/ListItems" as ListItems
25FocusScope {
26 id: scopeView
28 readonly property bool navigationDisableParentInteractive: pageHeaderLoader.item ? pageHeaderLoader.item.bottomItem[0].disableParentInteractive : false
29 property bool forceNonInteractive: false
30 property var scope: null
31 property UnitySortFilterProxyModel categories: categoryFilter
32 property bool isCurrent: false
33 property alias moving: categoryView.moving
34 property bool hasBackAction: false
35 property bool enableHeightBehaviorOnNextCreation: false
36 property var categoryView: categoryView
37 property bool showPageHeader: true
38 readonly property alias subPageShown: subPageLoader.subPageShown
39 property int paginationCount: 0
40 property int paginationIndex: 0
41 property bool visibleToParent: false
42 property alias pageHeaderTotallyVisible: categoryView.pageHeaderTotallyVisible
43 property var holdingList: null
44 property bool wasCurrentOnMoveStart: false
46 property var scopeStyle: ScopeStyle {
47 style: scope ? scope.customizations : {}
48 }
50 readonly property bool processing: scope ? scope.searchInProgress || subPageLoader.processing : false
52 signal backClicked()
54 onScopeChanged: {
55 floatingSeeLess.companionBase = null;
56 }
58 function positionAtBeginning() {
59 categoryView.positionAtBeginning()
60 }
62 function showHeader() {
63 categoryView.showHeader()
64 }
66 function closePreview() {
67 subPageLoader.closeSubPage()
68 }
70 function itemClicked(index, result, item, itemModel, resultsModel, limitedCategoryItemCount) {
71 if (itemModel.uri.indexOf("scope://") === 0 || scope.id === "clickscope") {
72 // TODO Technically it is possible that calling activate() will make the scope emit
73 // previewRequested so that we show a preview but there's no scope that does that yet
74 // so it's not implemented
75 scope.activate(result)
76 } else {
77 if (scope.preview(result)) {
78 openPreview(index, resultsModel, limitedCategoryItemCount);
79 }
80 }
81 }
83 function itemPressedAndHeld(index, result, itemModel, resultsModel, limitedCategoryItemCount) {
84 if (itemModel.uri.indexOf("scope://") !== 0) {
85 if (scope.preview(result)) {
86 openPreview(index, resultsModel, limitedCategoryItemCount);
87 }
88 }
89 }
91 function openPreview(index, resultsModel, limitedCategoryItemCount) {
92 if (limitedCategoryItemCount > 0) {
93 previewLimitModel.model = resultsModel;
94 previewLimitModel.limit = limitedCategoryItemCount;
95 subPageLoader.model = previewLimitModel;
96 } else {
97 subPageLoader.model = resultsModel;
98 }
99 subPageLoader.initialIndex = -1;
100 subPageLoader.initialIndex = index;
101 subPageLoader.openSubPage("preview");
102 }
104 Binding {
105 target: scope
106 property: "isActive"
107 value: isCurrent && !subPageLoader.open
108 }
110 UnitySortFilterProxyModel {
111 id: categoryFilter
112 model: scope ? scope.categories : null
113 dynamicSortFilter: true
114 filterRole: Categories.RoleCount
115 filterRegExp: /^0$/
116 invertMatch: true
117 }
119 onIsCurrentChanged: {
120 if (!holdingList || !holdingList.moving) {
121 wasCurrentOnMoveStart = scopeView.isCurrent;
122 }
123 if (pageHeaderLoader.item && showPageHeader) {
124 pageHeaderLoader.item.resetSearch();
125 }
126 subPageLoader.closeSubPage();
127 }
129 Binding {
130 target: scopeView.scope
131 property: "searchQuery"
132 value: pageHeaderLoader.item ? pageHeaderLoader.item.searchQuery : ""
133 when: isCurrent && showPageHeader
134 }
136 Binding {
137 target: pageHeaderLoader.item
138 property: "searchQuery"
139 value: scopeView.scope ? scopeView.scope.searchQuery : ""
140 when: isCurrent && showPageHeader
141 }
143 Connections {
144 target: scopeView.scope
145 onShowDash: subPageLoader.closeSubPage()
146 onHideDash: subPageLoader.closeSubPage()
147 }
149 Connections {
150 target: holdingList
151 onMovingChanged: {
152 if (!moving) {
153 wasCurrentOnMoveStart = scopeView.isCurrent;
154 }
155 }
156 }
158 Rectangle {
159 anchors.fill: parent
160 color: scopeView.scopeStyle ? scopeView.scopeStyle.background : "transparent"
161 visible: color != "transparent"
162 }
164 ScopeListView {
165 id: categoryView
166 objectName: "categoryListView"
167 interactive: !forceNonInteractive
169 x: subPageLoader.open ? -width : 0
170 visible: x != -width
171 Behavior on x { UbuntuNumberAnimation { } }
172 width: parent.width
173 height: floatingSeeLess.visible ? parent.height - floatingSeeLess.height + floatingSeeLess.yOffset
174 : parent.height
175 clip: height != parent.height
177 model: scopeView.categories
178 forceNoClip: subPageLoader.open
179 pixelAligned: true
181 property string expandedCategoryId: ""
182 property int runMaximizeAfterSizeChanges: 0
184 readonly property bool pageHeaderTotallyVisible: scopeView.showPageHeader &&
185 ((headerItemShownHeight == 0 && categoryView.contentY <= categoryView.originY) || (headerItemShownHeight == pageHeaderLoader.item.height))
187 onExpandedCategoryIdChanged: {
188 var firstCreated = firstCreatedIndex();
189 var shrinkingAny = false;
190 var shrinkHeightDifference = 0;
191 for (var i = 0; i < createdItemCount(); ++i) {
192 var baseItem = item(firstCreated + i);
193 if (baseItem.expandable) {
194 var shouldExpand = baseItem.category === expandedCategoryId;
195 if (shouldExpand != baseItem.expanded) {
196 var animate = false;
197 if (!subPageLoader.open) {
198 var animateShrinking = !shouldExpand && baseItem.y + baseItem.item.collapsedHeight + baseItem.seeAllButton.height < categoryView.height;
199 var animateGrowing = shouldExpand && baseItem.y + baseItem.height < categoryView.height;
200 animate = shrinkingAny || animateShrinking || animateGrowing;
201 }
203 if (!shouldExpand) {
204 shrinkingAny = true;
205 shrinkHeightDifference = baseItem.item.expandedHeight - baseItem.item.collapsedHeight;
206 }
208 if (shouldExpand && !subPageLoader.open) {
209 if (!shrinkingAny) {
210 categoryView.maximizeVisibleArea(firstCreated + i, baseItem.item.expandedHeight + baseItem.seeAllButton.height);
211 } else {
212 // If the space that shrinking is smaller than the one we need to grow we'll call maximizeVisibleArea
213 // after the shrink/grow animation ends
214 var growHeightDifference = baseItem.item.expandedHeight - baseItem.item.collapsedHeight;
215 if (growHeightDifference > shrinkHeightDifference) {
216 runMaximizeAfterSizeChanges = 2;
217 } else {
218 runMaximizeAfterSizeChanges = 0;
219 }
220 }
221 }
223 baseItem.expand(shouldExpand, animate);
224 }
225 }
226 }
227 }
229 delegate: DashCategoryBase {
230 id: baseItem
231 objectName: "dashCategory" + category
233 property Item seeAllButton: seeAll
235 readonly property bool expandable: {
236 if (categoryView.model.count === 1) return false;
237 if (cardTool.template && cardTool.template["collapsed-rows"] === 0) return false;
238 if (item && item.expandedHeight > item.collapsedHeight) return true;
239 return false;
240 }
241 property bool expanded: false
242 readonly property string category: categoryId
243 readonly property string headerLink: model.headerLink
244 readonly property var item: rendererLoader.item
246 function expand(expand, animate) {
247 heightBehaviour.enabled = animate;
248 expanded = expand;
249 }
251 CardTool {
252 id: cardTool
253 objectName: "cardTool"
254 count: results ? results.count : 0
255 template: model.renderer
256 components: model.components
257 viewWidth: parent.width
258 }
260 onExpandableChanged: {
261 // This can happen with the VJ that doesn't know how height it will be on creation
262 // so doesn't set expandable until a bit too late for onLoaded
263 if (expandable) {
264 var shouldExpand = baseItem.category === categoryView.expandedCategoryId;
265 baseItem.expand(shouldExpand, false /*animate*/);
266 }
267 }
269 onHeightChanged: rendererLoader.updateRanges();
270 onYChanged: rendererLoader.updateRanges();
272 Loader {
273 id: rendererLoader
274 anchors {
275 top: parent.top
276 left: parent.left
277 right: parent.right
278 topMargin: name != "" ? 0 : units.gu(2)
279 }
281 Behavior on height {
282 id: heightBehaviour
283 enabled: false
284 animation: UbuntuNumberAnimation {
285 duration: UbuntuAnimation.FastDuration
286 onRunningChanged: {
287 if (!running) {
288 heightBehaviour.enabled = false
289 if (categoryView.runMaximizeAfterSizeChanges > 0) {
290 categoryView.runMaximizeAfterSizeChanges--;
291 if (categoryView.runMaximizeAfterSizeChanges == 0) {
292 var firstCreated = categoryView.firstCreatedIndex();
293 for (var i = 0; i < categoryView.createdItemCount(); ++i) {
294 var baseItem = categoryView.item(firstCreated + i);
295 if (baseItem.category === categoryView.expandedCategoryId) {
296 categoryView.maximizeVisibleArea(firstCreated + i, baseItem.item.expandedHeight + baseItem.seeAllButton.height);
297 break;
298 }
299 }
300 }
301 }
302 }
303 }
304 }
305 }
307 readonly property bool expanded: baseItem.expanded || !baseItem.expandable
308 height: expanded ? item.expandedHeight : item.collapsedHeight
310 source: {
311 switch (cardTool.categoryLayout) {
312 case "carousel": return "CardCarousel.qml";
313 case "vertical-journal": return "CardVerticalJournal.qml";
314 case "horizontal-list": return "CardHorizontalList.qml";
315 case "grid":
316 default: return "CardGrid.qml";
317 }
318 }
320 onLoaded: {
321 if (item.enableHeightBehavior !== undefined && item.enableHeightBehaviorOnNextCreation !== undefined) {
322 item.enableHeightBehavior = scopeView.enableHeightBehaviorOnNextCreation;
323 scopeView.enableHeightBehaviorOnNextCreation = false;
324 }
325 item.model = Qt.binding(function() { return results })
326 item.objectName = Qt.binding(function() { return categoryId })
327 item.scopeStyle = scopeView.scopeStyle;
328 if (baseItem.expandable) {
329 var shouldExpand = baseItem.category === categoryView.expandedCategoryId;
330 baseItem.expand(shouldExpand, false /*animate*/);
331 }
332 updateRanges();
333 if (scope && scope.id === "clickscope" && (categoryId === "predefined" || categoryId === "local")) {
334 // Yeah, hackish :/
335 cardTool.artShapeSize = Qt.size(units.gu(8), units.gu(7.5));
336 }
337 item.cardTool = cardTool;
338 }
340 Component.onDestruction: {
341 if (item.enableHeightBehavior !== undefined && item.enableHeightBehaviorOnNextCreation !== undefined) {
342 scopeView.enableHeightBehaviorOnNextCreation = item.enableHeightBehaviorOnNextCreation;
343 }
344 }
346 Connections {
347 target: rendererLoader.item
348 onClicked: {
349 scopeView.itemClicked(index, result, item, itemModel, target.model, categoryItemCount());
350 }
352 onPressAndHold: {
353 scopeView.itemPressedAndHeld(index, result, itemModel, target.model, categoryItemCount());
354 }
356 function categoryItemCount() {
357 var categoryItemCount = -1;
358 if (!rendererLoader.expanded && !seeAllLabel.visible && target.collapsedItemCount > 0) {
359 categoryItemCount = target.collapsedItemCount;
360 }
361 return categoryItemCount;
362 }
363 }
364 Connections {
365 target: categoryView
366 onOriginYChanged: rendererLoader.updateRanges();
367 onContentYChanged: rendererLoader.updateRanges();
368 onHeightChanged: rendererLoader.updateRanges();
369 onContentHeightChanged: rendererLoader.updateRanges();
370 }
371 Connections {
372 target: scopeView
373 onIsCurrentChanged: rendererLoader.updateRanges();
374 onVisibleToParentChanged: rendererLoader.updateRanges();
375 }
376 Connections {
377 target: holdingList
378 onMovingChanged: if (!moving) rendererLoader.updateRanges();
379 }
381 function updateRanges() {
382 // Don't want to create stress by requesting more items during scope
383 // changes so unless you're not part of the visible scopes just return.
384 // For the visible scopes we need to do some work, the previously non visible
385 // scope needs to adjust its ranges so that we define the new visible range,
386 // that still means no creation/destruction of delegates, it's just about changing
387 // the culling of the items so they are actually visible
388 if (holdingList && holdingList.moving && !scopeView.visibleToParent) {
389 return;
390 }
392 if (categoryView.moving) {
393 // Do not update the range if we are overshooting up or down, since we'll come back
394 // to the stable position and delete/create items without any reason
395 if (categoryView.contentY < categoryView.originY) {
396 return;
397 } else if (categoryView.contentHeight - categoryView.originY > categoryView.height &&
398 categoryView.contentY + categoryView.height > categoryView.contentHeight) {
399 return;
400 }
401 }
403 if (item && item.hasOwnProperty("displayMarginBeginning")) {
404 // A item view creates its delegates synchronously from
405 // -displayMarginBeginning
406 // to
407 // height + displayMarginEnd
408 // Around that area it adds the cacheBuffer area where delegates are created async
409 //
410 // We adjust displayMarginBeginning and displayMarginEnd so
411 // * In non visible scopes nothing is considered visible and we set cacheBuffer
412 // so that creates the items that would be in the viewport asynchronously
413 // * For the current scope set the visible range to the viewport and then
414 // use cacheBuffer to create extra items for categoryView.height * 1.5
415 // to make scrolling nicer by mantaining a higher number of
416 // cached items
417 // * For non current but visible scopes (i.e. when the user changes from one scope
418 // to the next, we set the visible range to the viewport so
419 // items are not culled (invisible) but still use no cacheBuffer
420 // (it will be set once the scope is the current one)
421 var displayMarginBeginning = baseItem.y;
422 displayMarginBeginning = -Math.max(-displayMarginBeginning, 0);
423 displayMarginBeginning = -Math.min(-displayMarginBeginning, baseItem.height);
424 displayMarginBeginning = Math.round(displayMarginBeginning);
425 var displayMarginEnd = -baseItem.height + seeAll.height + categoryView.height - baseItem.y;
426 displayMarginEnd = -Math.max(-displayMarginEnd, 0);
427 displayMarginEnd = -Math.min(-displayMarginEnd, baseItem.height);
428 displayMarginEnd = Math.round(displayMarginEnd);
429 if (scopeView.isCurrent || scopeView.visibleToParent) {
430 item.displayMarginBeginning = displayMarginBeginning;
431 item.displayMarginEnd = displayMarginEnd;
432 if (holdingList && holdingList.moving) {
433 // If we are moving we need to reset the cache buffer of the
434 // view that was not visible (i.e. !wasCurrentOnMoveStart) to 0 since
435 // otherwise the cache buffer we had set to preload the items of the
436 // visible range will trigger some item creations and we want move to
437 // be as smooth as possible meaning no need creations
438 if (!wasCurrentOnMoveStart) {
439 item.cacheBuffer = 0;
440 }
441 } else {
442 item.cacheBuffer = categoryView.height * 1.5;
443 }
444 } else {
445 var visibleRange = baseItem.height + displayMarginEnd + displayMarginBeginning;
446 if (visibleRange < 0) {
447 item.displayMarginBeginning = displayMarginBeginning;
448 item.displayMarginEnd = displayMarginEnd;
449 item.cacheBuffer = 0;
450 } else {
451 // This should be visibleRange/2 in each of the properties
452 // but some item views still (like GridView) like creating sync delegates even if
453 // the visible range is 0 so let's make sure the visible range is negative
454 item.displayMarginBeginning = displayMarginBeginning - visibleRange;
455 item.displayMarginEnd = displayMarginEnd - visibleRange;
456 item.cacheBuffer = visibleRange;
457 }
458 }
459 }
460 }
461 }
463 AbstractButton {
464 id: seeAll
465 objectName: "seeAll"
466 anchors {
467 top: rendererLoader.bottom
468 left: parent.left
469 right: parent.right
470 }
471 height: baseItem.expandable && !baseItem.headerLink ? seeAllLabel.font.pixelSize + units.gu(4) : 0
472 visible: height != 0
474 onClicked: {
475 if (categoryView.expandedCategoryId !== baseItem.category) {
476 categoryView.expandedCategoryId = baseItem.category;
477 floatingSeeLess.companionBase = baseItem;
478 } else {
479 categoryView.expandedCategoryId = "";
480 }
481 }
483 Label {
484 id: seeAllLabel
485 text: baseItem.expanded ? i18n.tr("See less") : i18n.tr("See all")
486 anchors {
487 centerIn: parent
488 verticalCenterOffset: units.gu(-0.5)
489 }
490 fontSize: "small"
491 font.weight: Font.Bold
492 color: scopeStyle ? scopeStyle.foreground : Theme.palette.normal.baseText
493 }
494 }
496 Image {
497 visible: index != 0
498 anchors {
499 top: parent.top
500 left: parent.left
501 right: parent.right
502 }
503 fillMode: Image.Stretch
504 source: "graphics/dash_divider_top_lightgrad.png"
505 z: -1
506 }
508 Image {
509 // FIXME Should not rely on model.count but view.count, but ListViewWithPageHeader doesn't expose it yet.
510 visible: index != categoryView.model.count - 1
511 anchors {
512 bottom: seeAll.bottom
513 left: parent.left
514 right: parent.right
515 }
516 fillMode: Image.Stretch
517 source: "graphics/dash_divider_top_darkgrad.png"
518 z: -1
519 }
520 }
522 sectionProperty: "name"
523 sectionDelegate: ListItems.Header {
524 objectName: "dashSectionHeader" + (delegate ? delegate.category : "")
525 readonly property var delegate: categoryView.item(delegateIndex)
526 width: categoryView.width
527 height: section != "" ? units.gu(5) : 0
528 text: section
529 color: scopeStyle ? scopeStyle.foreground : Theme.palette.normal.baseText
530 iconName: delegate && delegate.headerLink ? "go-next" : ""
531 onClicked: {
532 if (delegate.headerLink) scopeView.scope.performQuery(delegate.headerLink);
533 }
534 }
536 pageHeader: scopeView.showPageHeader ? pageHeaderLoader : null
537 Loader {
538 id: pageHeaderLoader
539 width: parent.width
540 sourceComponent: scopeView.showPageHeader ? pageHeaderComponent : undefined
541 Component {
542 id: pageHeaderComponent
543 PageHeader {
544 objectName: "scopePageHeader"
545 width: parent.width
546 title: scopeView.scope ? scopeView.scope.name : ""
547 searchHint: scopeView.scope && scopeView.scope.searchHint || i18n.tr("Search")
548 showBackButton: scopeView.hasBackAction
549 searchEntryEnabled: true
550 settingsEnabled: scopeView.scope && scopeView.scope.settings && scopeView.scope.settings.count > 0 || false
551 favoriteEnabled: scopeView.scope && scopeView.scope.id !== "clickscope"
552 favorite: scopeView.scope && scopeView.scope.favorite
553 scopeStyle: scopeView.scopeStyle
554 paginationCount: scopeView.paginationCount
555 paginationIndex: scopeView.paginationIndex
557 bottomItem: DashNavigation {
558 scope: scopeView.scope
559 anchors { left: parent.left; right: parent.right }
560 windowHeight: scopeView.height
561 windowWidth: scopeView.width
562 scopeStyle: scopeView.scopeStyle
563 }
565 onBackClicked: scopeView.backClicked()
566 onSettingsClicked: subPageLoader.openSubPage("settings")
567 onFavoriteClicked: scopeView.scope.favorite = !scopeView.scope.favorite
568 }
569 }
570 }
571 }
573 Item {
574 id: pullToRefreshClippingItem
575 anchors.left: parent.left
576 anchors.right: parent.right
577 anchors.bottom: parent.bottom
578 height: parent.height - pullToRefresh.contentY + (pageHeaderLoader.item ? pageHeaderLoader.item.bottomItem[0].height - pageHeaderLoader.item.height : 0)
579 clip: true
581 PullToRefresh {
582 id: pullToRefresh
583 objectName: "pullToRefresh"
584 target: categoryView
586 readonly property real contentY: categoryView.contentY - categoryView.originY
587 y: -contentY - units.gu(5)
589 onRefresh: {
590 refreshing = true
591 scopeView.scope.refresh()
592 }
593 anchors.left: parent.left
594 anchors.right: parent.right
596 Connections {
597 target: scopeView
598 onProcessingChanged: if (!scopeView.processing) pullToRefresh.refreshing = false
599 }
601 style: PullToRefreshScopeStyle {
602 anchors.fill: parent
603 activationThreshold: units.gu(14)
604 }
605 }
606 }
608 AbstractButton {
609 id: floatingSeeLess
610 objectName: "floatingSeeLess"
612 property Item companionTo: companionBase ? companionBase.seeAllButton : null
613 property Item companionBase: null
614 property bool showBecausePosition: false
615 property real yOffset: 0
617 anchors {
618 left: categoryView.left
619 right: categoryView.right
620 }
621 y: parent.height - height + yOffset
622 height: seeLessLabel.font.pixelSize + units.gu(4)
623 visible: companionTo && showBecausePosition
625 onClicked: categoryView.expandedCategoryId = "";
627 function updateVisibility() {
628 var companionPos = companionTo.mapToItem(floatingSeeLess, 0, 0);
629 showBecausePosition = companionPos.y > 0;
631 var posToBase = floatingSeeLess.mapToItem(companionBase, 0, -yOffset).y;
632 yOffset = Math.max(0, companionBase.item.collapsedHeight - posToBase);
633 yOffset = Math.min(yOffset, height);
635 if (!showBecausePosition && categoryView.expandedCategoryId === "") {
636 companionBase = null;
637 }
638 }
640 Label {
641 id: seeLessLabel
642 text: i18n.tr("See less")
643 anchors {
644 centerIn: parent
645 verticalCenterOffset: units.gu(-0.5)
646 }
647 fontSize: "small"
648 font.weight: Font.Bold
649 color: scopeStyle ? scopeStyle.foreground : Theme.palette.normal.baseText
650 }
652 Connections {
653 target: floatingSeeLess.companionTo ? categoryView : null
654 onContentYChanged: floatingSeeLess.updateVisibility();
655 }
657 Connections {
658 target: floatingSeeLess.companionTo
659 onYChanged: floatingSeeLess.updateVisibility();
660 }
661 }
663 LimitProxyModel {
664 id: previewLimitModel
665 }
667 Loader {
668 id: subPageLoader
669 objectName: "subPageLoader"
670 visible: x != width
671 width: parent.width
672 height: parent.height
673 anchors.left: categoryView.right
675 property bool open: false
676 property var scope: scopeView.scope
677 property var scopeStyle: scopeView.scopeStyle
678 property int initialIndex: -1
679 property var model: null
681 readonly property bool processing: item && item.processing || false
682 readonly property int count: item && item.count || 0
683 readonly property int currentIndex: item && item.currentIndex || 0
684 readonly property var currentItem: item && item.currentItem || null
686 property string subPage: ""
687 readonly property bool subPageShown: visible && status === Loader.Ready
689 function openSubPage(page) {
690 subPage = page;
691 }
693 function closeSubPage() {
694 open = false;
695 }
697 source: switch(subPage) {
698 case "preview": return "PreviewListView.qml";
699 case "settings": return "ScopeSettingsPage.qml";
700 default: return "";
701 }
703 onLoaded: {
704 item.scope = Qt.binding(function() { return subPageLoader.scope; } )
705 item.scopeStyle = Qt.binding(function() { return subPageLoader.scopeStyle; } )
706 if (subPage == "preview") {
707 item.open = Qt.binding(function() { return subPageLoader.open; } )
708 item.initialIndex = Qt.binding(function() { return subPageLoader.initialIndex; } )
709 item.model = Qt.binding(function() { return subPageLoader.model; } )
710 }
711 open = true;
712 }
714 onOpenChanged: pageHeaderLoader.item.unfocus()
716 onVisibleChanged: if (!visible) subPage = ""
718 Connections {
719 target: subPageLoader.item
720 onBackClicked: subPageLoader.closeSubPage()
721 }
722 }