2 * Copyright (C) 2013-2015 Canonical, Ltd.
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.
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.
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/>.
18 import Ubuntu.Components 1.3
19 import Ubuntu.Components.Popups 1.3
20 import "../Components/SearchHistoryModel"
24 import "../Components"
25 import "../Components/ListItems" as ListItems
30 property bool forceNonInteractive: false
31 property var scope: null
32 property UnitySortFilterProxyModel categories: categoryFilter
33 property bool isCurrent: false
34 property alias moving: categoryView.moving
35 property bool hasBackAction: false
36 property bool enableHeightBehaviorOnNextCreation: false
37 property var categoryView: categoryView
38 readonly property alias subPageShown: subPageLoader.subPageShown
39 readonly property alias extraPanelShown: peExtraPanel.visible
40 property int paginationCount: 0
41 property int paginationIndex: 0
42 property bool visibleToParent: false
43 property alias pageHeaderTotallyVisible: categoryView.pageHeaderTotallyVisible
44 property var holdingList: null
45 property bool wasCurrentOnMoveStart: false
46 property var filtersPopover: null
48 property var scopeStyle: ScopeStyle {
49 style: scope ? scope.customizations : {}
52 readonly property bool processing: scope ? (scope.searchInProgress || scope.activationInProgress || subPageLoader.processing) : false
57 floatingSeeLess.companionBase = null;
60 function positionAtBeginning() {
61 categoryView.positionAtBeginning()
64 function showHeader() {
65 categoryView.showHeader()
68 function closePreview() {
69 subPageLoader.closeSubPage()
72 function resetSearch() {
73 categoryView.pageHeader.resetSearch()
76 property var maybePreviewResult;
77 property string maybePreviewCategoryId;
79 function clearMaybePreviewData() {
80 scopeView.maybePreviewResult = undefined;
81 scopeView.maybePreviewCategoryId = "";
84 function itemClicked(result, categoryId) {
85 scopeView.maybePreviewResult = result;
86 scopeView.maybePreviewCategoryId = categoryId;
88 scope.activate(result, categoryId);
91 function itemPressedAndHeld(result, categoryId) {
92 clearMaybePreviewData();
94 openPreview(result, categoryId);
97 function openPreview(result, categoryId) {
98 var previewModel = scope.preview(result, categoryId);
100 subPageLoader.previewModel = previewModel;
101 subPageLoader.openSubPage("preview");
108 value: isCurrent && !subPageLoader.open && (Qt.application.state == Qt.ApplicationActive)
111 UnitySortFilterProxyModel {
113 model: scope ? scope.categories : null
114 dynamicSortFilter: true
115 filterRole: Categories.RoleCount
120 onIsCurrentChanged: {
121 if (!holdingList || !holdingList.moving) {
122 wasCurrentOnMoveStart = scopeView.isCurrent;
124 categoryView.pageHeader.resetSearch();
125 subPageLoader.closeSubPage();
126 if (filtersPopover) {
127 PopupUtils.close(filtersPopover)
128 scopeView.filtersPopover = null;
133 target: scopeView.scope
134 property: "searchQuery"
135 value: categoryView.pageHeader.searchQuery
140 target: categoryView.pageHeader
141 property: "searchQuery"
142 value: scopeView.scope ? scopeView.scope.searchQuery : ""
147 target: scopeView.scope
148 onShowDash: subPageLoader.closeSubPage()
149 onHideDash: subPageLoader.closeSubPage()
150 onPreviewRequested: { // (QVariant const& result)
151 if (result === scopeView.maybePreviewResult) {
153 scopeView.maybePreviewCategoryId);
155 clearMaybePreviewData();
164 wasCurrentOnMoveStart = scopeView.isCurrent;
171 color: scopeView.scopeStyle ? scopeView.scopeStyle.background : "transparent"
172 visible: color != "transparent"
177 objectName: "categoryListView"
178 interactive: !forceNonInteractive
180 x: subPageLoader.open ? -width : 0
182 Behavior on x { UbuntuNumberAnimation { } }
184 height: floatingSeeLess.visible ? parent.height - floatingSeeLess.height + floatingSeeLess.yOffset
186 clip: height != parent.height
188 model: scopeView.categories
189 forceNoClip: subPageLoader.open
192 property string expandedCategoryId: ""
193 property int runMaximizeAfterSizeChanges: 0
195 readonly property bool pageHeaderTotallyVisible:
196 ((headerItemShownHeight == 0 && categoryView.contentY <= categoryView.originY) || (headerItemShownHeight == categoryView.pageHeader.height))
198 onExpandedCategoryIdChanged: {
199 var firstCreated = firstCreatedIndex();
200 var shrinkingAny = false;
201 var shrinkHeightDifference = 0;
202 for (var i = 0; i < createdItemCount(); ++i) {
203 var baseItem = item(firstCreated + i);
204 if (baseItem.expandable) {
205 var shouldExpand = baseItem.category === expandedCategoryId;
206 if (shouldExpand != baseItem.expanded) {
208 if (!subPageLoader.open) {
209 var animateShrinking = !shouldExpand && baseItem.y + baseItem.item.collapsedHeight + baseItem.seeAllButton.height < categoryView.height;
210 var animateGrowing = shouldExpand && baseItem.y + baseItem.height < categoryView.height;
211 animate = shrinkingAny || animateShrinking || animateGrowing;
216 shrinkHeightDifference = baseItem.item.expandedHeight - baseItem.item.collapsedHeight;
219 if (shouldExpand && !subPageLoader.open) {
221 categoryView.maximizeVisibleArea(firstCreated + i, baseItem.item.expandedHeight + baseItem.seeAllButton.height);
223 // If the space that shrinking is smaller than the one we need to grow we'll call maximizeVisibleArea
224 // after the shrink/grow animation ends
225 var growHeightDifference = baseItem.item.expandedHeight - baseItem.item.collapsedHeight;
226 if (growHeightDifference > shrinkHeightDifference) {
227 runMaximizeAfterSizeChanges = 2;
229 runMaximizeAfterSizeChanges = 0;
234 baseItem.expand(shouldExpand, animate);
240 delegate: DashCategoryBase {
242 objectName: "dashCategory" + category
244 property Item seeAllButton: seeAll
246 readonly property bool expandable: {
247 if (categoryView.model.count === 1) return false;
248 if (cardTool.template && cardTool.template["collapsed-rows"] === 0) return false;
249 if (item && item.expandedHeight > item.collapsedHeight) return true;
252 property bool expanded: false
253 readonly property string category: categoryId
254 readonly property string headerLink: model.headerLink
255 readonly property var item: rendererLoader.item
257 function expand(expand, animate) {
258 heightBehaviour.enabled = animate;
264 objectName: "cardTool"
265 count: results ? results.count : 0
266 template: model.renderer
267 components: model.components
268 viewWidth: parent.width
271 onExpandableChanged: {
272 // This can happen with the VJ that doesn't know how height it will be on creation
273 // so doesn't set expandable until a bit too late for onLoaded
275 var shouldExpand = baseItem.category === categoryView.expandedCategoryId;
276 baseItem.expand(shouldExpand, false /*animate*/);
280 onHeightChanged: rendererLoader.updateRanges();
281 onYChanged: rendererLoader.updateRanges();
289 topMargin: name != "" ? 0 : units.gu(2)
295 animation: UbuntuNumberAnimation {
296 duration: UbuntuAnimation.FastDuration
299 heightBehaviour.enabled = false
300 if (categoryView.runMaximizeAfterSizeChanges > 0) {
301 categoryView.runMaximizeAfterSizeChanges--;
302 if (categoryView.runMaximizeAfterSizeChanges == 0) {
303 var firstCreated = categoryView.firstCreatedIndex();
304 for (var i = 0; i < categoryView.createdItemCount(); ++i) {
305 var baseItem = categoryView.item(firstCreated + i);
306 if (baseItem.category === categoryView.expandedCategoryId) {
307 categoryView.maximizeVisibleArea(firstCreated + i, baseItem.item.expandedHeight + baseItem.seeAllButton.height);
318 readonly property bool expanded: baseItem.expanded || !baseItem.expandable
319 height: expanded ? item.expandedHeight : item.collapsedHeight
322 switch (cardTool.categoryLayout) {
323 case "carousel": return "CardCarousel.qml";
324 case "vertical-journal": return "CardVerticalJournal.qml";
325 case "horizontal-list": return "CardHorizontalList.qml";
327 default: return "CardGrid.qml";
332 if (item.enableHeightBehavior !== undefined && item.enableHeightBehaviorOnNextCreation !== undefined) {
333 item.enableHeightBehavior = scopeView.enableHeightBehaviorOnNextCreation;
334 scopeView.enableHeightBehaviorOnNextCreation = false;
336 item.model = Qt.binding(function() { return results })
337 item.objectName = Qt.binding(function() { return categoryId })
338 item.scopeStyle = scopeView.scopeStyle;
339 if (baseItem.expandable) {
340 var shouldExpand = baseItem.category === categoryView.expandedCategoryId;
341 baseItem.expand(shouldExpand, false /*animate*/);
344 clickScopeSizingHacks();
345 if (scope && (scope.id === "clickscope" || scope.id === "libertine-scope.ubuntu_libertine-scope")) {
346 if (scope.id === "libertine-scope.ubuntu_libertine-scope" || categoryId === "predefined" || categoryId === "local") {
347 cardTool.artShapeSize = Qt.binding(function() { return Qt.size(units.gu(8), units.gu(7.5)) });
348 cardTool.artShapeStyle = "icon";
350 // Should be ubuntu store icon
351 cardTool.artShapeStyle = "flat";
352 item.backgroundShapeStyle = "shadow";
355 item.cardTool = cardTool;
358 Component.onDestruction: {
359 if (item.enableHeightBehavior !== undefined && item.enableHeightBehaviorOnNextCreation !== undefined) {
360 scopeView.enableHeightBehaviorOnNextCreation = item.enableHeightBehaviorOnNextCreation;
363 // FIXME: directly connecting to onUnitsChanged cause a compile error:
364 // Cannot assign to non-existent property "onUnitsChanged"
365 // Until the units object is reworked to properly do all we need, let's go through a intermediate property
366 property int pxpgu: units.gu(1);
367 onPxpguChanged: clickScopeSizingHacks();
369 function clickScopeSizingHacks() {
371 ((scope.id === "clickscope" && (categoryId === "predefined" || categoryId === "local")) ||
372 scope.id === "libertine-scope.ubuntu_libertine-scope")) {
374 if (scopeView.width > units.gu(45)) {
375 if (scopeView.width >= units.gu(70)) {
376 cardTool.cardWidth = units.gu(11);
377 item.minimumHorizontalSpacing = units.gu(5);
380 cardTool.cardWidth = units.gu(10);
383 cardTool.cardWidth = units.gu(12);
385 item.minimumHorizontalSpacing = item.defaultMinimumHorizontalSpacing;
390 target: rendererLoader.item
391 onClicked: { // (int index, var result, var item, var itemModel)
392 scopeView.itemClicked(result, baseItem.category);
395 onPressAndHold: { // (int index, var result, var itemModel)
396 scopeView.itemPressedAndHeld(result, baseItem.category);
399 onAction: { // (int index, var result, var actionId)
400 scope.activateAction(result, baseItem.category, actionId);
403 function categoryItemCount() {
404 var categoryItemCount = -1;
405 if (!rendererLoader.expanded && !seeAllLabel.visible && target.collapsedItemCount > 0) {
406 categoryItemCount = target.collapsedItemCount;
408 return categoryItemCount;
413 onOriginYChanged: rendererLoader.updateRanges();
414 onContentYChanged: rendererLoader.updateRanges();
415 onHeightChanged: rendererLoader.updateRanges();
416 onContentHeightChanged: rendererLoader.updateRanges();
420 onIsCurrentChanged: rendererLoader.updateRanges();
421 onVisibleToParentChanged: rendererLoader.updateRanges();
422 onWidthChanged: rendererLoader.clickScopeSizingHacks();
426 onMovingChanged: if (!moving) rendererLoader.updateRanges();
429 function updateRanges() {
430 // Don't want to create stress by requesting more items during scope
431 // changes so unless you're not part of the visible scopes just return.
432 // For the visible scopes we need to do some work, the previously non visible
433 // scope needs to adjust its ranges so that we define the new visible range,
434 // that still means no creation/destruction of delegates, it's just about changing
435 // the culling of the items so they are actually visible
436 if (holdingList && holdingList.moving && !scopeView.visibleToParent) {
440 if (categoryView.moving) {
441 // Do not update the range if we are overshooting up or down, since we'll come back
442 // to the stable position and delete/create items without any reason
443 if (categoryView.contentY < categoryView.originY) {
445 } else if (categoryView.contentHeight - categoryView.originY > categoryView.height &&
446 categoryView.contentY + categoryView.height > categoryView.contentHeight) {
451 if (item && item.hasOwnProperty("displayMarginBeginning")) {
452 var buffer = wasCurrentOnMoveStart ? categoryView.height * 1.5 : 0;
453 var onViewport = baseItem.y + baseItem.height > 0 &&
454 baseItem.y < categoryView.height;
455 var onBufferViewport = baseItem.y + baseItem.height > -buffer &&
456 baseItem.y < categoryView.height + buffer;
458 if (item.growsVertically) {
459 // A item view creates its delegates synchronously from
460 // -displayMarginBeginning
462 // height + displayMarginEnd
463 // Around that area it adds the cacheBuffer area where delegates are created async
465 // We adjust displayMarginBeginning and displayMarginEnd so
466 // * In non visible scopes nothing is considered visible and we set cacheBuffer
467 // so that creates the items that would be in the viewport asynchronously
468 // * For the current scope set the visible range to the viewport and then
469 // use cacheBuffer to create extra items for categoryView.height * 1.5
470 // to make scrolling nicer by mantaining a higher number of
472 // * For non current but visible scopes (i.e. when the user changes from one scope
473 // to the next, we set the visible range to the viewport so
474 // items are not culled (invisible) but still use no cacheBuffer
475 // (it will be set once the scope is the current one)
476 var displayMarginBeginning = baseItem.y + rendererLoader.anchors.topMargin;
477 displayMarginBeginning = -Math.max(-displayMarginBeginning, 0);
478 displayMarginBeginning = -Math.min(-displayMarginBeginning, baseItem.height);
479 displayMarginBeginning = Math.round(displayMarginBeginning);
480 var displayMarginEnd = -baseItem.height + seeAll.height + categoryView.height - baseItem.y;
481 displayMarginEnd = -Math.max(-displayMarginEnd, 0);
482 displayMarginEnd = -Math.min(-displayMarginEnd, baseItem.height);
483 displayMarginEnd = Math.round(displayMarginEnd);
485 if (onBufferViewport && (scopeView.isCurrent || scopeView.visibleToParent)) {
486 item.displayMarginBeginning = displayMarginBeginning;
487 item.displayMarginEnd = displayMarginEnd;
488 if (holdingList && holdingList.moving) {
489 // If we are moving we need to reset the cache buffer of the
490 // view that was not visible (i.e. !wasCurrentOnMoveStart) to 0 since
491 // otherwise the cache buffer we had set to preload the items of the
492 // visible range will trigger some item creations and we want move to
493 // be as smooth as possible meaning no need creations
494 if (!wasCurrentOnMoveStart) {
495 item.cacheBuffer = 0;
498 // Protect us against cases where the item hasn't yet been positioned
499 if (!(categoryView.contentY === 0 && baseItem.y === 0 && index !== 0)) {
500 item.cacheBuffer = categoryView.height * 1.5;
504 var visibleRange = baseItem.height + displayMarginEnd + displayMarginBeginning;
505 if (visibleRange < 0) {
506 item.displayMarginBeginning = displayMarginBeginning;
507 item.displayMarginEnd = displayMarginEnd;
508 item.cacheBuffer = 0;
510 // This should be visibleRange/2 in each of the properties
511 // but some item views still (like GridView) like creating sync delegates even if
512 // the visible range is 0 so let's make sure the visible range is negative
513 item.displayMarginBeginning = displayMarginBeginning - visibleRange;
514 item.displayMarginEnd = displayMarginEnd - visibleRange;
515 item.cacheBuffer = visibleRange;
519 if (!onBufferViewport) {
520 // If not on the buffered viewport, don't load anything
521 item.displayMarginBeginning = 0;
522 item.displayMarginEnd = -item.innerWidth;
523 item.cacheBuffer = 0;
525 if (onViewport && (scopeView.isCurrent || scopeView.visibleToParent)) {
526 // If on the buffered viewport and the viewport and the on a visible scope
527 // Set displayMargin so that cards are rendered
528 // And if not moving the parent list also give it some extra asynchronously
530 item.displayMarginBeginning = 0;
531 item.displayMarginEnd = 0;
532 if (holdingList && holdingList.moving) {
533 // If we are moving we need to reset the cache buffer of the
534 // view that was not visible (i.e. !wasCurrentOnMoveStart) to 0 since
535 // otherwise the cache buffer we had set to preload the items of the
536 // visible range will trigger some item creations and we want move to
537 // be as smooth as possible meaning no need creations
538 if (!wasCurrentOnMoveStart) {
539 item.cacheBuffer = 0;
542 item.cacheBuffer = baseItem.width * 1.5;
545 // If on the buffered viewport but either not in the real viewport
546 // or in the viewport of the non current scope, use displayMargin + cacheBuffer
547 // to render asynchronously the width of cards
548 item.displayMarginBeginning = 0;
549 item.displayMarginEnd = -item.innerWidth;
550 item.cacheBuffer = item.innerWidth;
562 top: rendererLoader.bottom
566 height: baseItem.expandable && !baseItem.headerLink ? seeAllLabel.font.pixelSize + units.gu(4) : 0
570 if (categoryView.expandedCategoryId !== baseItem.category) {
571 categoryView.expandedCategoryId = baseItem.category;
572 floatingSeeLess.companionBase = baseItem;
574 categoryView.expandedCategoryId = "";
580 text: baseItem.expanded ? i18n.tr("Show less") : i18n.tr("Show all")
583 verticalCenterOffset: units.gu(-0.5)
586 font.weight: Font.Bold
587 color: scopeStyle ? scopeStyle.foreground : theme.palette.normal.baseText
598 fillMode: Image.Stretch
599 source: "graphics/dash_divider_top_lightgrad.png"
604 // FIXME Should not rely on model.count but view.count, but ListViewWithPageHeader doesn't expose it yet.
605 visible: index != categoryView.model.count - 1
607 bottom: seeAll.bottom
611 fillMode: Image.Stretch
612 source: "graphics/dash_divider_top_darkgrad.png"
617 sectionProperty: "name"
618 sectionDelegate: ListItems.Header {
619 objectName: "dashSectionHeader" + (delegate ? delegate.category : "")
620 property int delegateIndex: -1
621 readonly property var delegate: categoryView.item(delegateIndex)
622 width: categoryView.width
623 height: text != "" ? units.gu(5) : 0
624 color: scopeStyle ? scopeStyle.foreground : theme.palette.normal.baseText
625 iconName: delegate && delegate.headerLink ? "go-next" : ""
627 if (delegate.headerLink) scopeView.scope.performQuery(delegate.headerLink);
631 pageHeader: DashPageHeader {
632 objectName: "scopePageHeader"
634 title: scopeView.scope ? scopeView.scope.name : ""
635 extraPanel: peExtraPanel
636 searchHistory: SearchHistoryModel
637 searchHint: scopeView.scope && scopeView.scope.searchHint || i18n.ctr("Label: Hint for dash search line edit", "Search")
638 scopeHasFilters: scopeView.scope.filters != null
639 activeFiltersCount: scopeView.scope.activeFiltersCount
640 showBackButton: scopeView.hasBackAction
641 searchEntryEnabled: true
642 settingsEnabled: scopeView.scope && scopeView.scope.settings && scopeView.scope.settings.count > 0 || false
643 favoriteEnabled: scopeView.scope && scopeView.scope.id !== "clickscope"
644 favorite: scopeView.scope && scopeView.scope.favorite
645 navigationTag: scopeView.scope ? scopeView.scope.primaryNavigationTag : ""
646 scopeStyle: scopeView.scopeStyle
647 paginationCount: scopeView.paginationCount
648 paginationIndex: scopeView.paginationIndex
650 onBackClicked: scopeView.backClicked()
651 onSettingsClicked: subPageLoader.openSubPage("settings")
652 onFavoriteClicked: scopeView.scope.favorite = !scopeView.scope.favorite
653 onSearchTextFieldFocused: scopeView.showHeader()
654 onClearSearch: { // keepPanelOpen
655 var panelOpen = peExtraPanel.visible;
656 resetSearch(keepPanelOpen);
657 scopeView.scope.resetPrimaryNavigationTag();
658 peExtraPanel.resetNavigation();
659 if ((panelOpen || searchHistory.count > 0) && keepPanelOpen) {
663 onShowFiltersPopup: { // item
664 extraPanel.visible = false;
665 scopeView.filtersPopover = PopupUtils.open(Qt.resolvedUrl("FiltersPopover.qml"), item, { "contentWidth": scopeView.width - units.gu(2) } );
666 scopeView.filtersPopover.Component.onDestruction.connect(function () {
667 categoryView.pageHeader.closePopup(false, true);
668 categoryView.pageHeader.unfocus(true); // remove the focus from the search field
673 PageHeaderExtraPanel {
675 objectName: "peExtraPanel"
676 width: parent.width >= units.gu(60) ? units.gu(40) : parent.width
678 top: categoryView.pageHeader.bottom
679 topMargin: -categoryView.pageHeader.signatureLineHeight
684 searchHistory: SearchHistoryModel
685 scope: scopeView.scope
686 windowHeight: scopeView.height
688 onHistoryItemClicked: {
689 SearchHistoryModel.addQuery(text);
690 categoryView.pageHeader.searchQuery = text;
691 categoryView.pageHeader.unfocus();
694 onDashNavigationLeafClicked: {
695 categoryView.pageHeader.closePopup();
696 categoryView.pageHeader.unfocus();
699 onExtraPanelOptionSelected: {
700 categoryView.pageHeader.closePopup();
701 categoryView.pageHeader.unfocus();
707 id: pullToRefreshClippingItem
708 anchors.left: parent.left
709 anchors.right: parent.right
710 anchors.bottom: parent.bottom
711 height: parent.height - pullToRefresh.contentY - categoryView.pageHeader.height
716 objectName: "pullToRefresh"
719 readonly property real contentY: categoryView.contentY - categoryView.originY
720 y: -contentY - units.gu(5)
724 scopeView.scope.refresh()
726 anchors.left: parent.left
727 anchors.right: parent.right
731 onProcessingChanged: if (!scopeView.processing) pullToRefresh.refreshing = false
734 style: PullToRefreshScopeStyle {
736 activationThreshold: Math.min(units.gu(14), scopeView.height / 5)
743 objectName: "floatingSeeLess"
745 property Item companionTo: companionBase ? companionBase.seeAllButton : null
746 property Item companionBase: null
747 property bool showBecausePosition: false
748 property real yOffset: 0
751 left: categoryView.left
752 right: categoryView.right
754 y: parent.height - height + yOffset
755 height: seeLessLabel.font.pixelSize + units.gu(4)
756 visible: companionTo && showBecausePosition
758 onClicked: categoryView.expandedCategoryId = "";
760 function updateVisibility() {
761 var companionPos = companionTo.mapToItem(floatingSeeLess, 0, 0);
762 showBecausePosition = companionPos.y > 0;
764 var posToBase = floatingSeeLess.mapToItem(companionBase, 0, -yOffset).y;
765 yOffset = Math.max(0, companionBase.item.collapsedHeight - posToBase);
766 yOffset = Math.min(yOffset, height);
768 if (!showBecausePosition && categoryView.expandedCategoryId === "") {
769 companionBase = null;
775 text: i18n.tr("Show less")
778 verticalCenterOffset: units.gu(-0.5)
781 font.weight: Font.Bold
782 color: scopeStyle ? scopeStyle.foreground : theme.palette.normal.baseText
786 target: floatingSeeLess.companionTo ? categoryView : null
787 onContentYChanged: floatingSeeLess.updateVisibility();
791 target: floatingSeeLess.companionTo
792 onYChanged: floatingSeeLess.updateVisibility();
797 id: previewLimitModel
802 objectName: "subPageLoader"
805 height: parent.height
806 anchors.left: categoryView.right
808 property bool open: false
809 property var scope: scopeView.scope
810 property var scopeStyle: scopeView.scopeStyle
811 property int initialIndex: -1
812 property var previewModel;
814 readonly property bool processing: item && item.processing || false
815 readonly property int count: item && item.count || 0
816 readonly property var currentItem: item && item.currentItem || null
818 property string subPage: ""
819 readonly property bool subPageShown: visible && status === Loader.Ready
821 function openSubPage(page) {
825 function closeSubPage() {
829 source: switch(subPage) {
830 case "preview": return "PreviewView.qml";
831 case "settings": return "ScopeSettingsPage.qml";
836 item.scope = Qt.binding(function() { return subPageLoader.scope; } )
837 item.scopeStyle = Qt.binding(function() { return subPageLoader.scopeStyle; } )
838 if (subPage == "preview") {
839 item.open = Qt.binding(function() { return subPageLoader.open; } )
840 item.previewModel = subPageLoader.previewModel;
841 subPageLoader.previewModel = null;
846 onOpenChanged: categoryView.pageHeader.unfocus()
848 onVisibleChanged: if (!visible) subPage = ""
851 target: subPageLoader.item
852 onBackClicked: subPageLoader.closeSubPage()