diff --git a/src/controls/ToolBarApplicationHeader.qml b/src/controls/ToolBarApplicationHeader.qml index b172d4c9..5d106aaa 100644 --- a/src/controls/ToolBarApplicationHeader.qml +++ b/src/controls/ToolBarApplicationHeader.qml @@ -1,172 +1,155 @@ /* * Copyright 2015 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 or * (at your option) any later version. * * 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 Library General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.5 import QtQuick.Controls 1.3 as Controls import QtQuick.Layouts 1.2 import QtQuick.Controls.Private 1.0 import "private" import org.kde.kirigami 2.0 /** * */ ApplicationHeader { id: header preferredHeight: 38 maximumHeight: preferredHeight //FIXME: needs a property difinition to have its own type in qml property string _internal: "" pageDelegate: Item { id: delegateItem - readonly property Page page: applicationWindow().pageStack.get(modelData) - property ListView view: ListView.view readonly property bool current: __appWindow.pageStack.currentIndex == index property Row layout - width: { - //more columns shown? - if (__appWindow.wideScreen) { - if (modelData == 0 && view.x > 0) { - return page.width - view.x; - } else { - return page.width; - } - } else { - return Math.min(view.width, delegateRoot.implicitWidth + Units.smallSpacing); - } - } - height: view.height + implicitWidth: layout.width > 0 ? layout.width : heading.width + width: parent.width + height: parent.height - Component.onCompleted: { - layout = toolbarComponent.createObject(delegateItem) - } - Component { - id: toolbarComponent - Row { - id: layout - anchors.verticalCenter: parent.verticalCenter - x: __appWindow.wideScreen ? (Math.min(delegateItem.width - width, Math.max(0, delegateItem.view.contentX - delegateItem.x))) : 0 - spacing: 2 + Row { + id: layout + anchors.verticalCenter: parent.verticalCenter + spacing: 2 - Rectangle { - anchors.verticalCenter: parent.verticalCenter - color: Theme.textColor - opacity: 0.3 - width: Units.devicePixelRatio - height: parent.height * 0.6 - } - PrivateActionToolButton { - anchors.verticalCenter: parent.verticalCenter - action: page && page.actions ? page.actions.left : null - showText: false - } - PrivateActionToolButton { - anchors.verticalCenter: parent.verticalCenter - action: page && page.actions ? page.actions.main : null - showText: false - } - PrivateActionToolButton { - anchors.verticalCenter: parent.verticalCenter - action: page && page.actions ? page.actions.right : null - showText: false - } - Rectangle { + Rectangle { + anchors.verticalCenter: parent.verticalCenter + color: Theme.textColor + opacity: 0.3 + width: Units.devicePixelRatio + height: parent.height * 0.6 + } + PrivateActionToolButton { + anchors.verticalCenter: parent.verticalCenter + action: page && page.actions ? page.actions.left : null + showText: false + } + PrivateActionToolButton { + anchors.verticalCenter: parent.verticalCenter + action: page && page.actions ? page.actions.main : null + showText: false + } + PrivateActionToolButton { + anchors.verticalCenter: parent.verticalCenter + action: page && page.actions ? page.actions.right : null + showText: false + } + Rectangle { + anchors.verticalCenter: parent.verticalCenter + color: Theme.textColor + opacity: 0.3 + width: Units.devicePixelRatio + height: parent.height * 0.6 + visible: page && page.actions && (page.actions.left || page.actions.main || page.actions.right) + } + Repeater { + id: repeater + model: page && page.actions.contextualActions ? page.actions.contextualActions : null + delegate: PrivateActionToolButton { anchors.verticalCenter: parent.verticalCenter - color: Theme.textColor - opacity: 0.3 - width: Units.devicePixelRatio - height: parent.height * 0.6 - } - Repeater { - id: repeater - model: page && page.actions.contextualActions ? page.actions.contextualActions : null - delegate: PrivateActionToolButton { - anchors.verticalCenter: parent.verticalCenter - action: modelData - visible: modelData.visible && x+layout.x+width*2 < delegateItem.width - } + action: modelData + visible: modelData.visible && x+layout.x+width*2 < delegateItem.width } } } + Heading { - x: __appWindow.wideScreen ? (Math.min(delegateItem.width - width, Math.max(0, delegateItem.view.contentX - delegateItem.x))) : 0 + id: heading anchors.verticalCenter: parent.verticalCenter visible: layout.width <= 0 opacity: delegateItem.current ? 1 : 0.4 color: Theme.textColor elide: Text.ElideRight text: page ? page.title : "" font.pixelSize: parent.height / 1.6 } Controls.ToolButton { id: moreButton anchors { right: parent.right verticalCenter: parent.verticalCenter } //TODO: we need a kebab icon iconName: "application-menu" visible: menu.visibleChildren > 0 onClicked: page.actions.main menu: Controls.Menu { id: menu property int visibleChildren: 0 Instantiator { model: page && page.actions.contextualActions ? page.actions.contextualActions : null delegate: Controls.MenuItem { text: modelData ? modelData.text : "" iconName: modelData.iconName shortcut: modelData.shortcut onTriggered: modelData.trigger(); //skip the 3 buttons and 2 separators visible: !layout.children[index+5].visible && modelData.visible enabled: modelData.enabled onVisibleChanged: { if (visible) { menu.visibleChildren++; } else { menu.visibleChildren = Math.max(0, menu.visibleChildren-1); } } } onObjectAdded: { menu.insertItem(index, object); if (object.visible) { menu.visibleChildren++; } } onObjectRemoved: { menu.removeItem(object); if (object.visible) { menu.visibleChildren = Math.max(0, menu.visibleChildren-1); } } } } } } } diff --git a/src/controls/templates/ApplicationHeader.qml b/src/controls/templates/ApplicationHeader.qml index 8e068e80..ea617503 100644 --- a/src/controls/templates/ApplicationHeader.qml +++ b/src/controls/templates/ApplicationHeader.qml @@ -1,238 +1,245 @@ /* * Copyright 2015 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 or * (at your option) any later version. * * 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 Library General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.5 import QtQuick.Layouts 1.2 import QtQuick.Controls.Private 1.0 import "private" import org.kde.kirigami 2.0 /** * An item that can be used as a title for the application. * Scrolling the main page will make it taller or shorter (trough the point of going away) * It's a behavior similar to the typical mobile web browser adressbar * the minimum, preferred and maximum heights of the item can be controlled with * * minimumHeight: default is 0, i.e. hidden * * preferredHeight: default is Units.gridUnit * 1.6 * * maximumHeight: default is Units.gridUnit * 3 * * To achieve a titlebar that stays completely fixed just set the 3 sizes as the same */ AbstractApplicationHeader { id: header /** * headerStyle: string * The way the separator between pages should be drawn in the header. * Allowed values are: * * Breadcrumb: the pages are hyerarchical and the separator will look like a > * * TabBar: the pages are intended to behave like tabbar pages * and the separator will look limke a dot. * * When the heaer is in wide screen mode, no separator will be drawn. */ property int headerStyle: ApplicationHeaderStyle.Auto - property alias pageDelegate: titleList.delegate + property Component pageDelegate: Component { + Row { + height: parent.height + + spacing: Units.smallSpacing + + Icon { + //in tabbar mode this is just a spacer + visible: !__appWindow.wideScreen && (modelData > 0 || titleList.internalHeaderStyle == ApplicationHeaderStyle.TabBar) + height: title.height + width: height + selected: header.background && header.background.color && header.background.color == Theme.highlightColor + source: titleList.isTabBar ? "" : "go-next" + } + + Heading { + id: title + width:Math.min(titleList.width, implicitWidth) + anchors.verticalCenter: parent.verticalCenter + opacity: current ? 1 : 0.4 + //Scaling animate NativeRendering is too slow + renderType: Text.QtRendering + color: header.background && header.background.color && header.background.color == Theme.highlightColor ? Theme.highlightedTextColor : Theme.textColor + elide: Text.ElideRight + text: page ? page.title : "" + font.pixelSize: titleList.height / 1.6 + height: parent.height + Rectangle { + anchors { + bottom: parent.bottom + left: parent.left + right: parent.right + } + height: Units.smallSpacing + color: title.color + opacity: 0.6 + visible: titleList.isTabBar && current + } + } + } + } Rectangle { anchors { right: titleList.left verticalCenter: parent.verticalCenter } visible: titleList.x > 0 && !titleList.atXBeginning height: parent.height * 0.7 color: Theme.highlightedTextColor width: Math.ceil(Units.smallSpacing / 6) opacity: 0.4 } ListView { id: titleList property int internalHeaderStyle: header.headerStyle == ApplicationHeaderStyle.Auto ? (__appWindow.wideScreen ? ApplicationHeaderStyle.Titles : ApplicationHeaderStyle.Breadcrumb) : header.headerStyle //uses this to have less strings comparisons property bool isTabBar: header.headerStyle == ApplicationHeaderStyle.TabBar Component.onCompleted: { //only iOS and desktop systems put the back button on top left corner if (!titleList.isTabBar && (!Settings.isMobile || Qt.platform.os == "ios")) { var component = Qt.createComponent(Qt.resolvedUrl("private/BackButton.qml")); titleList.backButton = component.createObject(titleList.parent); } } property Item backButton clip: true - anchors { - fill: parent - leftMargin: (backButton ? backButton.width : (titleList.isTabBar ? 0 : Units.smallSpacing*2)) - } + anchors.fill: parent cacheBuffer: width ? Math.max(0, width * count) : 0 displayMarginBeginning: __appWindow.pageStack.width * count orientation: ListView.Horizontal boundsBehavior: Flickable.StopAtBounds model: __appWindow.pageStack.depth spacing: 0 currentIndex: __appWindow.pageStack && __appWindow.pageStack.currentIndex !== undefined ? __appWindow.pageStack.currentIndex : 0 function gotoIndex(idx) { //don't actually scroll in widescreen mode if (__appWindow.wideScreen) { return; } listScrollAnim.running = false var pos = titleList.contentX; var destPos; titleList.positionViewAtIndex(idx, ListView.Center); destPos = titleList.contentX; listScrollAnim.from = pos; listScrollAnim.to = destPos; listScrollAnim.running = true; } NumberAnimation { id: listScrollAnim target: titleList property: "contentX" duration: Units.longDuration easing.type: Easing.InOutQuad } Timer { id: contentXSyncTimer interval: 0 onTriggered: { titleList.contentX = __appWindow.pageStack.contentItem.contentX - __appWindow.pageStack.contentItem.originX + titleList.originX; } } onCountChanged: contentXSyncTimer.restart(); onCurrentIndexChanged: gotoIndex(currentIndex); onModelChanged: gotoIndex(currentIndex); onContentWidthChanged: gotoIndex(currentIndex); onContentXChanged: { if (__appWindow.wideScreen && !__appWindow.pageStack.contentItem.moving && titleList.moving) { __appWindow.pageStack.contentItem.contentX = titleList.contentX } } onHeightChanged: { titleList.returnToBounds() } onMovementEnded: { if (__appWindow.wideScreen) { - __appWindow.pageStack.contentItem.movementEnded(); + //this will trigger snap as well + __appWindow.pageStack.contentItem.flick(0,0); } } NumberAnimation { id: scrollTopAnimation target: __appWindow.pageStack.currentItem && __appWindow.pageStack.currentItem.flickable ? __appWindow.pageStack.currentItem.flickable : null property: "contentY" to: 0 duration: Units.longDuration easing.type: Easing.InOutQuad } delegate: MouseArea { id: delegate - readonly property Page page: __appWindow.pageStack.get(modelData) - //NOTE: why not use ListViewCurrentIndex? because listview itself resets - //currentIndex in some situations (since here we are using an int as a model, - //even more often) so the property binding gets broken - readonly property bool current: __appWindow.pageStack.currentIndex == index + readonly property int currentIndex: index + readonly property var currentModelData: modelData + clip: true width: { //more columns shown? - if (__appWindow.wideScreen && page) { - if (modelData == 0 && titleList.backButton) { - return page.width - titleList.backButton.width; - } else { - return page.width; - } + if (__appWindow.wideScreen && delegateLoader.page) { + return delegateLoader.page.width; } else { - return Math.min(titleList.width, delegateRoot.implicitWidth + Units.smallSpacing); + return Math.min(titleList.width, delegateLoader.implicitWidth + Units.smallSpacing); } } height: titleList.height onClicked: { if (__appWindow.pageStack.currentIndex == modelData) { //scroll up if current otherwise make current if (!__appWindow.pageStack.currentItem.flickable) { return; } if (__appWindow.pageStack.currentItem.flickable.contentY > -__appWindow.header.height) { scrollTopAnimation.to = -__appWindow.pageStack.currentItem.flickable.topMargin; scrollTopAnimation.running = true; } } else { __appWindow.pageStack.currentIndex = modelData; } } - Row { - id: delegateRoot - x: Units.smallSpacing + __appWindow.wideScreen ? (Math.min(delegate.width - width, Math.max(0, titleList.contentX - delegate.x))) : 0 + Loader { + id: delegateLoader height: parent.height - - spacing: Units.smallSpacing - - Icon { - //in tabbar mode this is just a spacer - visible: !__appWindow.wideScreen && (modelData > 0 || titleList.internalHeaderStyle == ApplicationHeaderStyle.TabBar) - height: title.height - width: height - selected: header.background && header.background.color && header.background.color == Theme.highlightColor - source: titleList.isTabBar ? "" : "go-next" - } - - Heading { - id: title - width:Math.min(titleList.width, implicitWidth) - anchors.verticalCenter: parent.verticalCenter - opacity: delegate.current ? 1 : 0.4 - //Scaling animate NativeRendering is too slow - renderType: Text.QtRendering - color: header.background && header.background.color && header.background.color == Theme.highlightColor ? Theme.highlightedTextColor : Theme.textColor - elide: Text.ElideRight - text: page ? page.title : "" - font.pixelSize: titleList.height / 1.6 - height: parent.height - Rectangle { - anchors { - bottom: parent.bottom - left: parent.left - right: parent.right - } - height: Units.smallSpacing - color: title.color - opacity: 0.6 - visible: titleList.isTabBar && delegate.current - } - } + x: Units.smallSpacing + __appWindow.wideScreen ? (Math.min(delegate.width - implicitWidth, Math.max(0, titleList.contentX - delegate.x + (titleList.backButton ? titleList.backButton.width : 0)))) : 0 + width: parent.width - x + + sourceComponent: header.pageDelegate + + readonly property Page page: __appWindow.pageStack.get(modelData) + //NOTE: why not use ListViewCurrentIndex? because listview itself resets + //currentIndex in some situations (since here we are using an int as a model, + //even more often) so the property binding gets broken + readonly property bool current: __appWindow.pageStack.currentIndex == index + readonly property int index: parent.currentIndex + readonly property var modelData: parent.currentModelData } } Connections { target: __appWindow.wideScreen ? __appWindow.pageStack.contentItem : null onContentXChanged: { if (!titleList.contentItem.moving) { titleList.contentX = __appWindow.pageStack.contentItem.contentX - __appWindow.pageStack.contentItem.originX + titleList.originX; } } } } } diff --git a/src/controls/templates/private/PageRow.qml b/src/controls/templates/private/PageRow.qml index fa7af1e2..6bfc361c 100644 --- a/src/controls/templates/private/PageRow.qml +++ b/src/controls/templates/private/PageRow.qml @@ -1,497 +1,395 @@ /* * Copyright 2016 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * 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 Library General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.5 import QtQuick.Layouts 1.2 +import QtQml.Models 2.2 import QtQuick.Templates 2.0 as T import org.kde.kirigami 2.0 -Item { +T.Control { id: root //BEGIN PROPERTIES /** * This property holds the number of items currently pushed onto the view */ readonly property alias depth: pagesLogic.count /** * The last Page in the Row */ readonly property Item lastItem: pagesLogic.count ? pagesLogic.get(pagesLogic.count - 1).page : null /** * The currently visible Item */ - readonly property Item currentItem: mainFlickable.currentItem + readonly property Item currentItem: mainView.currentItem.page /** * the index of the currently visible Item */ - property alias currentIndex: mainFlickable.currentIndex + property alias currentIndex: mainView.currentIndex /** * The initial item when this PageRow is created */ property variant initialPage /** * The main flickable of this Row */ - property alias contentItem: mainFlickable +// property alias contentItem: mainView + contentItem: mainView /** * The default width for a column * default is wide enough for 30 grid units. * Pages can override it with their Layout.fillWidth, * implicitWidth Layout.minimumWidth etc. */ property int defaultColumnWidth: Units.gridUnit * 20 /** * interactive: bool * If true it will be possible to go back/forward by dragging the * content themselves with a gesture. * Otherwise the only way to go back will be programmatically * default: true */ - property alias interactive: mainFlickable.interactive +// property alias interactive: mainView.interactive //END PROPERTIES //BEGIN FUNCTIONS /** * Pushes a page on the stack. * The page can be defined as a component, item or string. * If an item is used then the page will get re-parented. * If a string is used then it is interpreted as a url that is used to load a page * component. * * @param page The page can also be given as an array of pages. * In this case all those pages will * be pushed onto the stack. The items in the stack can be components, items or * strings just like for single pages. * Additionally an object can be used, which specifies a page and an optional * properties property. * This can be used to push multiple pages while still giving each of * them properties. * When an array is used the transition animation will only be to the last page. * * @param properties The properties argument is optional and allows defining a * map of properties to set on the page. * @return The new created page */ function push(page, properties) { pop(currentItem); // figure out if more than one page is being pushed var pages; if (page instanceof Array) { pages = page; page = pages.pop(); if (page.createObject === undefined && page.parent === undefined && typeof page != "string") { properties = properties || page.properties; page = page.page; } } // push any extra defined pages onto the stack if (pages) { var i; for (i = 0; i < pages.length; i++) { var tPage = pages[i]; var tProps; if (tPage.createObject === undefined && tPage.parent === undefined && typeof tPage != "string") { tProps = tPage.properties; tPage = tPage.page; } var container = pagesLogic.initPage(tPage, tProps); pagesLogic.append(container); } } // initialize the page var container = pagesLogic.initPage(page, properties); pagesLogic.append(container); container.visible = container.page.visible = true; - mainFlickable.currentIndex = container.level; + mainView.currentIndex = container.level; return container.page } /** * Pops a page off the stack. * @param page If page is specified then the stack is unwound to that page, * to unwind to the first page specify * page as null. * @return The page instance that was popped off the stack. */ function pop(page) { if (depth == 0) { return; } var oldPage = pagesLogic.get(pagesLogic.count-1).page; if (page !== undefined) { // an unwind target has been specified - pop until we find it while (page != oldPage && pagesLogic.count > 1) { pagesLogic.remove(oldPage.parent.level); oldPage = pagesLogic.get(pagesLogic.count-1).page; } } else { pagesLogic.remove(pagesLogic.count-1); } } /** * Replaces a page on the stack. * @param page The page can also be given as an array of pages. * In this case all those pages will * be pushed onto the stack. The items in the stack can be components, items or * strings just like for single pages. * Additionally an object can be used, which specifies a page and an optional * properties property. * This can be used to push multiple pages while still giving each of * them properties. * When an array is used the transition animation will only be to the last page. * @param properties The properties argument is optional and allows defining a * map of properties to set on the page. * @see push() for details. */ function replace(page, properties) { if (currentIndex>=1) pop(pagesLogic.get(currentIndex-1).page); else if (currentIndex==0) pop(); else console.warn("There's no page to replace"); return push(page, properties); } /** * Clears the page stack. * Destroy (or reparent) all the pages contained. */ function clear() { return pagesLogic.clear(); } function get(idx) { return pagesLogic.get(idx).page; } //END FUNCTIONS - QtObject { - id: pagesLogic - - readonly property int count: mainLayout.children.length - property var componentCache - - property int roundedDefaultColumnWidth: root.width < root.defaultColumnWidth*2 ? root.width : root.defaultColumnWidth - - //NOTE:seems to only work if the array is defined in a declarative way, - //the Object in an imperative way, espacially on Android - Component.onCompleted: { - componentCache = {}; - } - - //TODO: remove? - function get(id) { - return mainLayout.children[id]; - } - - function append(item) { - //FIXME: seems that for one loop the x of the item would continue to be 0 - item.x = item.level * roundedDefaultColumnWidth; - item.parent = mainLayout; - } - - function clear () { - while (mainLayout.children.length > 0) { - remove(0); - } - } - - function remove(id) { - if (id < 0 || id >= count) { - print("Tried to remove an invalid page index:" + id); - return; - } - - var item = mainLayout.children[id]; - if (item.owner) { - item.page.parent = item.owner; + ListView { + id: mainView + z: 99 + anchors.fill: parent + boundsBehavior: Flickable.StopAtBounds + orientation: Qt.Horizontal + snapMode: ListView.SnapToItem + interactive: root.interactive + currentIndex: root.currentIndex + rightMargin: count > 1 ? pagesLogic.get(count-1).page.width - pagesLogic.get(count-1).width : 0 + preferredHighlightBegin: 0 + preferredHighlightEnd: 0 + highlightMoveDuration: Units.longDuration + onMovementEnded: currentIndex = indexAt(contentX, 0) + onFlickEnded: onMovementEnded(); + model: ObjectModel { + id: pagesLogic + property var componentCache + + property int roundedDefaultColumnWidth: root.width < root.defaultColumnWidth*2 ? root.width : root.defaultColumnWidth + + //NOTE:seems to only work if the array is defined in a declarative way, + //the Object in an imperative way, espacially on Android + Component.onCompleted: { + componentCache = {}; } - //FIXME: why reparent ing is necessary? - //is destroy just an async deleteLater() that isn't executed immediately or it actually leaks? - item.parent = root; - item.destroy(); - } - - function initPage(page, properties) { - var container = containerComponent.createObject(mainLayout, { - "level": pagesLogic.count, - "page": page - }); - - var pageComp; - if (page.createObject) { - // page defined as component - pageComp = page; - } else if (typeof page == "string") { - // page defined as string (a url) - pageComp = pagesLogic.componentCache[page]; - if (!pageComp) { - pageComp = pagesLogic.componentCache[page] = Qt.createComponent(page); + function initPage(page, properties) { + var container = containerComponent.createObject(mainView, { + "level": pagesLogic.count, + "page": page + }); + + var pageComp; + if (page.createObject) { + // page defined as component + pageComp = page; + } else if (typeof page == "string") { + // page defined as string (a url) + pageComp = pagesLogic.componentCache[page]; + if (!pageComp) { + pageComp = pagesLogic.componentCache[page] = Qt.createComponent(page); + } } - } - if (pageComp) { - if (pageComp.status == Component.Error) { - throw new Error("Error while loading page: " + pageComp.errorString()); + if (pageComp) { + if (pageComp.status == Component.Error) { + throw new Error("Error while loading page: " + pageComp.errorString()); + } else { + // instantiate page from component + page = pageComp.createObject(container.pageParent, properties || {}); + } } else { - // instantiate page from component - page = pageComp.createObject(container.pageParent, properties || {}); - } - } else { - // copy properties to the page - for (var prop in properties) { - if (properties.hasOwnProperty(prop)) { - page[prop] = properties[prop]; + // copy properties to the page + for (var prop in properties) { + if (properties.hasOwnProperty(prop)) { + page[prop] = properties[prop]; + } } } - } - - container.page = page; - if (page.parent == null || page.parent == container.pageParent) { - container.owner = null; - } else { - container.owner = page.parent; - } - - // the page has to be reparented - if (page.parent != container) { - page.parent = container; - } - - return container; - } - } - NumberAnimation { - id: scrollAnim - target: mainFlickable - property: "contentX" - duration: Units.longDuration - easing.type: Easing.InOutQuad - } - - Flickable { - id: mainFlickable - anchors.fill: parent - boundsBehavior: Flickable.StopAtBounds - contentWidth: mainLayout.childrenRect.width - contentHeight: height - readonly property Item currentItem: { - var idx = Math.min(currentIndex, pagesLogic.count-1) - return idx>=0 ? pagesLogic.get(idx).page : null - } - //clip only when the app has a sidebar - clip: root.x > 0 - - property int currentIndex: 0 - property int firstVisibleLevel: Math.round (contentX / pagesLogic.roundedDefaultColumnWidth) - - flickDeceleration: Units.gridUnit * 50 - onCurrentItemChanged: { - var itemX = pagesLogic.roundedDefaultColumnWidth * currentIndex; - - if (itemX >= contentX && mainFlickable.currentItem && itemX + mainFlickable.currentItem.width <= contentX + mainFlickable.width) { - return; - } - - //this catches 0 and NaN (sometimes at startup width can oddly be nan - if (!mainFlickable.width) { - return; - } - scrollAnim.running = false; - scrollAnim.from = contentX; - if (itemX < contentX || !mainFlickable.currentItem) { - scrollAnim.to = Math.max(0, Math.min(itemX, mainFlickable.contentWidth - mainFlickable.width)); - } else { - scrollAnim.to = Math.max(0, Math.min(itemX - mainFlickable.width + mainFlickable.currentItem.width, mainFlickable.contentWidth - mainFlickable.width)); - } - scrollAnim.running = true; - } - onMovementEnded: { - if (mainLayout.childrenRect.width == 0) { - return; - } - - scrollAnim.running = false; - scrollAnim.from = contentX; - scrollAnim.to = pagesLogic.roundedDefaultColumnWidth * firstVisibleLevel - scrollAnim.running = true; - - var mappedCurrentItemPos = currentItem.mapToItem(mainFlickable, 0, 0); - - //is the current item out of view? - if (mappedCurrentItemPos.x < 0) { - currentIndex = firstVisibleLevel; - } else if (mappedCurrentItemPos.x + currentItem.width > mainFlickable.width) { - currentIndex = Math.min(root.depth-1, firstVisibleLevel + Math.floor(mainFlickable.width/pagesLogic.roundedDefaultColumnWidth)-1); - } - } - onFlickEnded: movementEnded(); - - Row { - id: mainLayout - add: Transition { - NumberAnimation { - property: "y" - from: mainFlickable.height - to: 0 - duration: Units.shortDuration - easing.type: Easing.InOutQuad - } - } - onChildrenChanged: { - mainFlickable.currentIndex = Math.min(mainFlickable.currentIndex, children.length-1); - } - onWidthChanged: { - //current item in view - if (children[mainFlickable.currentIndex].x >= mainFlickable.contentX && - children[mainFlickable.currentIndex].x + children[mainFlickable.currentIndex].width <= mainFlickable.contentX + mainFlickable.width) { - mainFlickable.contentX = pagesLogic.roundedDefaultColumnWidth * mainFlickable.firstVisibleLevel; + container.page = page; + if (page.parent == null || page.parent == container.pageParent) { + container.owner = null; } else { - mainFlickable.contentX = Math.max(0, Math.min(width - mainFlickable.width, mainFlickable.currentIndex * pagesLogic.roundedDefaultColumnWidth)); + container.owner = page.parent; + } + + // the page has to be reparented + if (page.parent != container) { + page.parent = container; } - + + return container; } - //onChildrenChanged: mainFlickable.contentX = pagesLogic.roundedDefaultColumnWidth * mainFlickable.firstVisibleLevel } - T.ScrollIndicator.horizontal: T.ScrollIndicator { anchors { left: parent.left right: parent.right bottom: parent.bottom } height: Units.smallSpacing contentItem: Rectangle { height: Units.smallSpacing width: Units.smallSpacing color: Theme.textColor opacity: 0 onXChanged: { opacity = 0.3 scrollIndicatorTimer.restart(); } Behavior on opacity { OpacityAnimator { duration: Units.longDuration easing.type: Easing.InOutQuad } } Timer { id: scrollIndicatorTimer interval: Units.longDuration * 4 onTriggered: parent.opacity = 0; } } } } Component { id: containerComponent MouseArea { id: container - height: mainFlickable.height + height: mainView.height width: root.width - state: root.width < root.defaultColumnWidth*2 ? "vertical" : (container.level >= pagesLogic.count - 1 ? "last" : "middle"); + state: page ? (root.width < root.defaultColumnWidth*2 || pagesLogic.count < 2 ? "vertical" : (container.level >= pagesLogic.count - 1 ? "last" : "middle")) : ""; property int level readonly property int hint: page && page.implicitWidth ? page.implicitWidth : root.defaultColumnWidth readonly property int roundedHint: Math.floor(root.width/hint) > 0 ? root.width/Math.floor(root.width/hint) : root.width property Item page property Item owner onPageChanged: { page.parent = container; page.anchors.fill = container; } drag.filterChildren: true onClicked: root.currentIndex = level; onFocusChanged: { if (focus) { root.currentIndex = level; } } Rectangle { z: 999 anchors { top: parent.top bottom: parent.bottom right: parent.right } width: Math.ceil(Units.smallSpacing / 5) color: Theme.textColor opacity: 0.3 visible: container.level < root.depth-1 } states: [ State { name: "vertical" PropertyChanges { target: container width: root.width } + PropertyChanges { + target: container.page.anchors + rightMargin: 0 + } }, State { name: "last" PropertyChanges { target: container - width: { - var page = pagesLogic.get(container.level-1); - Math.max(roundedHint, root.width - (page == undefined ? 0 : page.width)) + width: pagesLogic.roundedDefaultColumnWidth + } + PropertyChanges { + target: container.page.anchors + rightMargin: { + return -(root.width - pagesLogic.roundedDefaultColumnWidth*2); } } }, State { name: "middle" PropertyChanges { target: container width: pagesLogic.roundedDefaultColumnWidth } + PropertyChanges { + target: container.page.anchors + rightMargin: 0 + } } ] } } Component.onCompleted: { if (initialPage) { push(initialPage, null) } } }