diff --git a/src/controls/AbstractApplicationWindow.qml b/src/controls/AbstractApplicationWindow.qml index ca886133..513b4598 100644 --- a/src/controls/AbstractApplicationWindow.qml +++ b/src/controls/AbstractApplicationWindow.qml @@ -1,298 +1,298 @@ /* * 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 2.0 as QQC2 import "templates/private" import org.kde.kirigami 2.0 import QtGraphicalEffects 1.0 /** * A window that provides some basic features needed for all apps * Use this class only if you need a custom content for your application, * different from the Page Row behavior recomended by the HIG and provided * by ApplicationWindow. * It is recomended to use ApplicationWindow instead * @see ApplicationWindow * * It's usually used as a root QML component for the application. * It provides support for a central page stack, side drawers and * a top ApplicationHeader, as well as basic support for the * Android back button * * Example usage: * @code * import org.kde.kirigami 2.0 as Kirigami * * Kirigami.ApplicationWindow { * [...] * globalDrawer: Kirigami.GlobalDrawer { * actions: [ * Kirigami.Action { * text: "View" * iconName: "view-list-icons" * Kirigami.Action { * text: "action 1" * } * Kirigami.Action { * text: "action 2" * } * Kirigami.Action { * text: "action 3" * } * }, * Kirigami.Action { * text: "Sync" * iconName: "folder-sync" * } * ] * } * * contextDrawer: Kirigami.ContextDrawer { * id: contextDrawer * } * * pageStack: PageStack { * ... * } * [...] * } * @endcode * * @inherit QtQuick.Controls.ApplicationWindow */ QQC2.ApplicationWindow { id: root /** * pageStack: StackView * Readonly. * The stack used to allocate the pages and to manage the transitions * between them. * Put a container here, such as QQuickControls PageStack */ property Item pageStack /** * Shows a little passive notification at the bottom of the app window * lasting for few seconds, with an optional action button. * * @param message The text message to be shown to the user. * @param timeout How long to show the message: * possible values: "short", "long" or the number of milliseconds * @param actionText Text in the action button, if any. * @param callBack A JavaScript function that will be executed when the * user clicks the button. */ function showPassiveNotification(message, timeout, actionText, callBack) { if (!internal.__passiveNotification) { var component = Qt.createComponent("templates/private/PassiveNotification.qml"); internal.__passiveNotification = component.createObject(overlay.parent); } internal.__passiveNotification.showNotification(message, timeout, actionText, callBack); } /** * Hide the passive notification, if any is shown */ function hidePassiveNotification() { if(internal.__passiveNotification) { internal.__passiveNotification.hideNotification(); } } /** * @returns a pointer to this application window * can be used anywhere in the application. */ function applicationWindow() { return root; } /** * header: ApplicationHeader * 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 * * Layout.minimumHeight: default is 0, i.e. hidden * * Layout.preferredHeight: default is Units.gridUnit * 1.6 * * Layout.maximumHeight: default is Units.gridUnit * 3 * * To achieve a titlebar that stays completely fixed just set the 3 sizes as the same * //FIXME: this should become an actual ApplicationHeader */ header: undefined /** * controlsVisible: bool * This property controls wether the standard chrome of the app, such * as the Action button, the drawer handles and the application * header should be visible or not. */ property bool controlsVisible: true /** - * globalDrawer: AbstractDrawer + * globalDrawer: OverlayDrawer * The drawer for global actions, that will be opened by sliding from the * left screen edge or by dragging the ActionButton to the right. * It is recommended to use the GlobalDrawer class here */ - property AbstractDrawer globalDrawer + property OverlayDrawer globalDrawer /** * wideScreen: bool * If true the application is considered to be in "widescreen" mode, such as on desktops or horizontal tablets. * Different styles can have an own logic for deciding this */ property bool wideScreen: width >= Units.gridUnit * 60 /** - * contextDrawer: AbstractDrawer + * contextDrawer: OverlayDrawer * The drawer for context-dependednt actions, that will be opened by sliding from the * right screen edge or by dragging the ActionButton to the left. * It is recommended to use the ContextDrawer class here. * The contents of the context drawer should depend from what page is * loaded in the main pageStack * * Example usage: * @code * import org.kde.kirigami 2.0 as Kirigami * * Kirigami.ApplicationWindow { * [...] * contextDrawer: Kirigami.ContextDrawer { * id: contextDrawer * } * [...] * } * @endcode * * @code * import org.kde.kirigami 2.0 as Kirigami * * Kirigami.Page { * [...] * contextualActions: [ * Kirigami.Action { * iconName: "edit" * text: "Action text" * onTriggered: { * // do stuff * } * }, * Kirigami.Action { * iconName: "edit" * text: "Action text" * onTriggered: { * // do stuff * } * } * ] * [...] * } * @endcode * * When this page will be the current one, the context drawer will visualize * contextualActions defined as property in that page. */ - property AbstractDrawer contextDrawer + property OverlayDrawer contextDrawer /** * reachableMode: bool * When true the application is in reachable mode for single hand use. * the whole content of the application is moved down the screen to be * reachable with the thumb. if wideScreen is true, tis property has * no effect. */ property bool reachableMode: false MouseArea { parent: contentItem.parent z: -1 anchors.fill: parent onClicked: root.reachableMode = false; visible: root.reachableMode Rectangle { anchors.fill: parent color: Qt.rgba(0, 0, 0, 0.3) opacity: 0.15 Icon { anchors.horizontalCenter: parent.horizontalCenter y: x width: Units.iconSizes.large height: width source: "go-up" } } } contentItem.anchors.left: contentItem.parent.left contentItem.anchors.right: contentItem.parent.right contentItem.anchors.topMargin: root.wideScreen && header && controlsVisible ? header.height : 0 contentItem.anchors.leftMargin: root.globalDrawer && (root.globalDrawer.modal === false) ? root.globalDrawer.contentItem.width * root.globalDrawer.position : 0 contentItem.anchors.rightMargin: root.contextDrawer && root.contextDrawer.modal === false ? root.contextDrawer.contentItem.width * root.contextDrawer.position : 0 contentItem.transform: Translate { Behavior on y { NumberAnimation { duration: Units.longDuration easing.type: Easing.InOutQuad } } y: root.reachableMode && !root.wideScreen ? root.height/2 : 0 x: root.globalDrawer && root.globalDrawer.modal === true && root.globalDrawer.toString().indexOf("SplitDrawer") === 0 ? root.globalDrawer.contentItem.width * root.globalDrawer.position : 0 } //Don't want overscroll in landscape mode onWidthChanged: { if (width > height) { root.reachableMode = false; } } Binding { when: globalDrawer !== undefined && root.visible target: globalDrawer property: "parent" value: overlay } Binding { when: contextDrawer !== undefined && root.visible target: contextDrawer property: "parent" value: overlay } onPageStackChanged: pageStack.parent = contentItem; width: Units.gridUnit * 30 height: Units.gridUnit * 45 visible: true QtObject { id: internal property Item __passiveNotification } Shortcut { sequence: StandardKey.Quit onActivated: root.close() } } diff --git a/src/controls/templates/OverlayDrawer.qml b/src/controls/templates/OverlayDrawer.qml index 5b9713e6..3ac80172 100644 --- a/src/controls/templates/OverlayDrawer.qml +++ b/src/controls/templates/OverlayDrawer.qml @@ -1,127 +1,269 @@ /* * Copyright 2012 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.1 import QtQuick.Templates 2.0 as T2 import org.kde.kirigami 2.0 import "private" /** * Overlay Drawers are used to expose additional UI elements needed for * small secondary tasks for which the main UI elements are not needed. * For example in Okular Active, an Overlay Drawer is used to display * thumbnails of all pages within a document along with a search field. * This is used for the distinct task of navigating to another page. * */ -AbstractDrawer { +T2.Drawer { id: root z: modal ? (drawerOpen ? 100 : 99 ): 0 - //default paddings - leftPadding: Units.smallSpacing - topPadding: Units.smallSpacing - rightPadding: Units.smallSpacing - bottomPadding: Units.smallSpacing - //BEGIN Properties + /** + * drawerOpen: bool + * true when the drawer is open and visible + */ + property bool drawerOpen: false + + /** + * enabled: bool + * This property holds whether the item receives mouse and keyboard events. By default this is true. + */ + property bool enabled: true + + /** + * peeking: true + * When true the drawer is in a state between open and closed. the drawer is visible but not completely open. + * This is usually the case when the user is dragging the drawer from a screen + * edge, so the user is "peeking" what's in the drawer + */ + property bool peeking: false + + /** + * animating: Bool + * True during an animation of a drawer either opening or closing + */ + readonly property bool animating : enterAnimation.animating || exitAnimation.animating || positionResetAnim.running + /** * handleVisible: bool * If true, a little handle will be visible to make opening the drawer easier * Currently supported only on left and right drawers */ property bool handleVisible: typeof(applicationWindow)===typeof(Function) && applicationWindow() ? applicationWindow().controlsVisible : true /** * handle: Item * Readonly property that points to the item that will act as a physical * handle for the Drawer **/ readonly property Item handle: MouseArea { id: drawerHandle z: 2000000 preventStealing: true parent: applicationWindow().overlay.parent property int startX property int mappedStartX onPressed: { root.peeking = true; startX = mouse.x; mappedStartX = mapToItem(parent, startX, 0).x } onPositionChanged: { var pos = mapToItem(parent, mouse.x - startX, mouse.y); switch(root.edge) { case Qt.LeftEdge: root.position = pos.x/root.contentItem.width; break; case Qt.RightEdge: root.position = (root.parent.width - pos.x - width)/root.contentItem.width; break; default: } } onReleased: { root.peeking = false; if (Math.abs(mapToItem(parent, mouse.x, 0).x - mappedStartX) < Qt.styleHints.startDragDistance) { root.drawerOpen = !root.drawerOpen; } } onCanceled: { root.peeking = false } x: { switch(root.edge) { case Qt.LeftEdge: return root.background.width * root.position; case Qt.RightEdge: return drawerHandle.parent.width - (root.background.width * root.position) - width; default: return 0; } } anchors.bottom: parent.bottom visible: root.enabled && (root.edge == Qt.LeftEdge || root.edge == Qt.RightEdge) width: Units.iconSizes.medium + Units.smallSpacing * 2 height: width opacity: root.handleVisible ? 1 : 0 Behavior on opacity { NumberAnimation { duration: Units.longDuration easing.type: Easing.InOutQuad } } transform: Translate { id: translateTransform x: root.handleVisible ? 0 : (root.edge == Qt.LeftEdge ? -drawerHandle.width : drawerHandle.width) Behavior on x { NumberAnimation { duration: Units.longDuration easing.type: !root.handleVisible ? Easing.OutQuad : Easing.InQuad } } } } //END Properties + + +//BEGIN reassign properties + //default paddings + leftPadding: Units.smallSpacing + topPadding: Units.smallSpacing + rightPadding: Units.smallSpacing + bottomPadding: Units.smallSpacing + + parent: modal ? T2.ApplicationWindow.overlay : T2.ApplicationWindow.contentItem + height: edge == Qt.LeftEdge || edge == Qt.RightEdge ? applicationWindow().height : Math.min(contentItem.implicitHeight, Math.round(applicationWindow().height*0.8)) + width: edge == Qt.TopEdge || edge == Qt.BottomEdge ? applicationWindow().width : Math.min(contentItem.implicitWidth, Math.round(applicationWindow().width*0.8)) + edge: Qt.LeftEdge + modal: true + + dragMargin: enabled && (edge == Qt.LeftEdge || edge == Qt.RightEdge) ? Qt.styleHints.startDragDistance : 0 + + implicitWidth: Math.max(background ? background.implicitWidth : 0, contentWidth + leftPadding + rightPadding) + implicitHeight: Math.max(background ? background.implicitHeight : 0, contentHeight + topPadding + bottomPadding) + + contentWidth: contentItem.implicitWidth || (contentChildren.length === 1 ? contentChildren[0].implicitWidth : 0) + contentHeight: contentItem.implicitHeight || (contentChildren.length === 1 ? contentChildren[0].implicitHeight : 0) + + enter: Transition { + SequentialAnimation { + id: enterAnimation + /*NOTE: why this? the running status of the enter transition is not relaible and + * the SmoothedAnimation is always marked as non running, + * so the only way to get to a reliable animating status is with this + */ + property bool animating + ScriptAction { + script: enterAnimation.animating = true + } + SmoothedAnimation { + velocity: 5 + } + ScriptAction { + script: enterAnimation.animating = false + } + } + } + + exit: Transition { + SequentialAnimation { + id: exitAnimation + property bool animating + ScriptAction { + script: exitAnimation.animating = true + } + SmoothedAnimation { + velocity: 5 + } + ScriptAction { + script: exitAnimation.animating = false + } + } + } +//END reassign properties + + +//BEGIN signal handlers + onPositionChanged: { + if (peeking) { + visible = true + } + } + onVisibleChanged: { + if (peeking) { + visible = true + } else { + drawerOpen = visible; + } + } + onPeekingChanged: { + if (peeking) { + root.enter.enabled = false; + root.exit.enabled = false; + } else { + positionResetAnim.to = position > 0.5 ? 1 : 0; + positionResetAnim.running = true + root.enter.enabled = true; + root.exit.enabled = true; + } + } + onDrawerOpenChanged: { + //sync this property only when the component is properly loaded + if (!__internal.completed) { + return; + } + positionResetAnim.running = false; + if (drawerOpen) { + open(); + } else { + close(); + } + } + + Component.onCompleted: { + //if defined as drawerOpen by default in QML, don't animate + if (root.drawerOpen) { + root.enter.enabled = false; + root.visible = true; + root.position = 1; + root.enter.enabled = true; + } + __internal.completed = true; + } +//END signal handlers + + //this is as hidden as it can get here + property QtObject __internal: QtObject { + //here in order to not be accessible from outside + property bool completed: false + property NumberAnimation positionResetAnim: NumberAnimation { + id: positionResetAnim + target: root + to: 0 + property: "position" + duration: (root.position)*Units.longDuration + } + } } diff --git a/src/controls/templates/private/AbstractDrawer.qml b/src/controls/templates/private/AbstractDrawer.qml deleted file mode 100644 index ba8bd9e5..00000000 --- a/src/controls/templates/private/AbstractDrawer.qml +++ /dev/null @@ -1,163 +0,0 @@ -/* - * 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.1 -import QtQuick.Templates 2.0 as T2 -import QtGraphicalEffects 1.0 -import org.kde.kirigami 2.0 - -T2.Drawer { - id: root - - parent: modal ? T2.ApplicationWindow.overlay : T2.ApplicationWindow.contentItem - height: edge == Qt.LeftEdge || edge == Qt.RightEdge ? applicationWindow().height : Math.min(contentItem.implicitHeight, Math.round(applicationWindow().height*0.8)) - width: edge == Qt.TopEdge || edge == Qt.BottomEdge ? applicationWindow().width : Math.min(contentItem.implicitWidth, Math.round(applicationWindow().width*0.8)) - edge: Qt.LeftEdge - modal: true - - dragMargin: enabled && (edge == Qt.LeftEdge || edge == Qt.RightEdge) ? Qt.styleHints.startDragDistance : 0 - - /** - * drawerOpen: bool - * true when the drawer is open and visible - */ - property bool drawerOpen: false - - /** - * enabled: bool - * This property holds whether the item receives mouse and keyboard events. By default this is true. - */ - property bool enabled: true - - /** - * peeking: true - * When true the drawer is in a state between open and closed. the drawer is visible but not completely open. - * This is usually the case when the user is dragging the drawer from a screen - * edge, so the user is "peeking" what's in the drawer - */ - property bool peeking: false - - /** - * animating: Bool - * True during an animation of a drawer either opening or closing - */ - readonly property bool animating : enterAnimation.animating || exitAnimation.animating || positionResetAnim.running - - onPositionChanged: { - if (peeking) { - visible = true - } - } - onVisibleChanged: { - if (peeking) { - visible = true - } else { - drawerOpen = visible; - } - } - onPeekingChanged: { - if (peeking) { - root.enter.enabled = false; - root.exit.enabled = false; - } else { - positionResetAnim.to = position > 0.5 ? 1 : 0; - positionResetAnim.running = true - root.enter.enabled = true; - root.exit.enabled = true; - } - } - onDrawerOpenChanged: { - //sync this property only when the component is properly loaded - if (!__internal.completed) { - return; - } - positionResetAnim.running = false; - if (drawerOpen) { - open(); - } else { - close(); - } - } - - Component.onCompleted: { - //if defined as drawerOpen by default in QML, don't animate - if (root.drawerOpen) { - root.enter.enabled = false; - root.visible = true; - root.position = 1; - root.enter.enabled = true; - } - __internal.completed = true; - } - - - //this is as hidden as it can get here - property QtObject __internal: QtObject { - //here in order to not be accessible from outside - property bool completed: false - property NumberAnimation positionResetAnim: NumberAnimation { - id: positionResetAnim - target: root - to: 0 - property: "position" - duration: (root.position)*Units.longDuration - } - } - implicitWidth: Math.max(background ? background.implicitWidth : 0, contentWidth + leftPadding + rightPadding) - implicitHeight: Math.max(background ? background.implicitHeight : 0, contentHeight + topPadding + bottomPadding) - - contentWidth: contentItem.implicitWidth || (contentChildren.length === 1 ? contentChildren[0].implicitWidth : 0) - contentHeight: contentItem.implicitHeight || (contentChildren.length === 1 ? contentChildren[0].implicitHeight : 0) - - enter: Transition { - SequentialAnimation { - id: enterAnimation - /*NOTE: why this? the running status of the enter transition is not relaible and - * the SmoothedAnimation is always marked as non running, - * so the only way to get to a reliable animating status is with this - */ - property bool animating - ScriptAction { - script: enterAnimation.animating = true - } - SmoothedAnimation { - velocity: 5 - } - ScriptAction { - script: enterAnimation.animating = false - } - } - } - exit: Transition { - SequentialAnimation { - id: exitAnimation - property bool animating - ScriptAction { - script: exitAnimation.animating = true - } - SmoothedAnimation { - velocity: 5 - } - ScriptAction { - script: exitAnimation.animating = false - } - } - } -} -