diff --git a/examples/gallery/contents/ui/ExampleApp.qml b/examples/gallery/contents/ui/ExampleApp.qml
--- a/examples/gallery/contents/ui/ExampleApp.qml
+++ b/examples/gallery/contents/ui/ExampleApp.qml
@@ -20,7 +20,7 @@
import QtQuick 2.1
import QtQuick.Controls 2.0 as Controls
import QtQuick.Layouts 1.2
-import org.kde.kirigami 2.0 as Kirigami
+import org.kde.kirigami 2.1 as Kirigami
import "gallery"
Kirigami.ApplicationWindow {
diff --git a/kirigami.qrc b/kirigami.qrc
--- a/kirigami.qrc
+++ b/kirigami.qrc
@@ -34,6 +34,8 @@
src/controls/Units.qml
src/controls/SwipeListItem.qml
src/controls/ApplicationWindow.qml
+ src/controls/AbstractApplicationItem.qml
+ src/controls/ApplicationItem.qml
src/controls/ApplicationHeader.qml
src/controls/Heading.qml
src/controls/ScrollablePage.qml
diff --git a/src/controls/AbstractApplicationItem.qml b/src/controls/AbstractApplicationItem.qml
new file mode 100644
--- /dev/null
+++ b/src/controls/AbstractApplicationItem.qml
@@ -0,0 +1,344 @@
+/*
+ * Copyright 2017 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.Templates 2.0 as T2
+import QtQuick.Window 2.2
+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 ApplicationItem.
+ * It is recomended to use ApplicationItem instead
+ * @see ApplicationItem
+ *
+ * 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.ApplicationItem {
+ * [...]
+ * 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.Item
+ */
+Item {
+ 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
+
+ property alias overlay: overlayRoot
+ Item {
+ anchors.fill: parent
+ parent: root.parent
+ z: 99999999
+ Item {
+ id: overlayRoot
+ z: -1
+ anchors.fill: parent
+ }
+ Window.onWindowChanged: {
+ globalDrawer.visible = globalDrawer.drawerOpen
+ contextDrawer.visible = contextDrawer.drawerOpen
+ }
+ }
+
+ /**
+ * copatibility with Applicationwindow
+ */
+ readonly property Item activeFocusItem: Window.activeFocusItem
+ /**
+ * 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
+ */
+ property Item header
+ onHeaderChanged: header.parent = contentItemRoot
+
+ /**
+ * footer: ApplicationHeader
+ * An item that can be used as a footer for the application.
+ */
+ property Item footer
+ onFooterChanged: footer.parent = contentItemRoot
+
+ /**
+ * 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: 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 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: 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.ApplicationItem {
+ * [...]
+ * 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 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: Item
+ * This property holds the Item of the main part of the Application UI
+ */
+ default property alias __data: contentItemRoot.data
+ readonly property Item contentItem: Item {
+ id: contentItemRoot
+ parent: root
+ anchors.fill: parent
+
+ anchors.left: contentItem.parent.left
+ anchors.right: contentItem.parent.right
+ anchors.topMargin: root.wideScreen && header && controlsVisible ? header.height : 0
+ anchors.leftMargin: root.globalDrawer && (root.globalDrawer.modal === false) ? root.globalDrawer.contentItem.width * root.globalDrawer.position : 0
+ anchors.rightMargin: root.contextDrawer && root.contextDrawer.modal === false ? root.contextDrawer.contentItem.width * root.contextDrawer.position : 0
+ 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
+ }
+
+ Binding {
+ when: root.header
+ target: root.header
+ property: "y"
+ value: -root.header.height
+ }
+ }
+
+ //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/ApplicationItem.qml b/src/controls/ApplicationItem.qml
new file mode 100644
--- /dev/null
+++ b/src/controls/ApplicationItem.qml
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2017 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 "templates/private"
+import org.kde.kirigami 2.0 as Kirigami
+import QtGraphicalEffects 1.0
+
+/**
+ * A window that provides some basic features needed for all apps
+ * This version is an Item as opposed to a window, it's intended
+ * for use into a QQuickView
+ *
+ * It's usually used as a root QML component for the application.
+ * It's based around the PageRow component, the application will be
+ * about pages adding and removal.
+ * For most of the usages, this class should be used instead
+ * of AbstractApplicationWidnow
+ * @see AbstractApplicationWidnow
+ *
+ * Example usage:
+ * @code
+ * import org.kde.kirigami 2.0 as Kirigami
+ *
+ * Kirigami.ApplicationItem {
+ * [...]
+ * 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.initialPage: Kirigami.Page {
+ * mainAction: Kirigami.Action {
+ * iconName: "edit"
+ * onTriggered: {
+ * // do stuff
+ * }
+ * }
+ * contextualActions: [
+ * Kirigami.Action {
+ * iconName: "edit"
+ * text: "Action text"
+ * onTriggered: {
+ * // do stuff
+ * }
+ * },
+ * Kirigami.Action {
+ * iconName: "edit"
+ * text: "Action text"
+ * onTriggered: {
+ * // do stuff
+ * }
+ * }
+ * ]
+ * [...]
+ * }
+ * [...]
+ * }
+ * @endcode
+ *
+*/
+AbstractApplicationItem {
+ id: root
+
+ /**
+ * pageStack: StackView
+ * Readonly.
+ * The stack used to allocate the pages and to manage the transitions
+ * between them.
+ * It's using a PageRow, while having the same API as PageStack,
+ * it positions the pages as adjacent columns, with as many columns
+ * as can fit in the screen. An handheld device would usually have a single
+ * fullscreen column, a tablet device would have many tiled columns.
+ */
+ property alias pageStack: __pageStack
+
+ //redefines here as here we can know a pointer to PageRow
+ wideScreen: width >= applicationWindow().pageStack.defaultColumnWidth*2
+
+ PageRow {
+ id: __pageStack
+ anchors {
+ fill: parent
+ //HACK: workaround a bug in android iOS keyboard management
+ bottomMargin: ((Qt.platform.os == "android" || Qt.platform.os == "ios") || !Qt.inputMethod.visible) ? 0 : Qt.inputMethod.keyboardRectangle.height
+ onBottomMarginChanged: {
+ if (bottomMargin > 0) {
+ root.reachableMode = false;
+ }
+ }
+ }
+ //FIXME
+ onCurrentIndexChanged: root.reachableMode = false;
+
+ function goBack() {
+ //NOTE: drawers are handling the back button by themselves
+ var backEvent = {accepted: false}
+ if (root.pageStack.currentIndex >= 1) {
+ root.pageStack.currentItem.backRequested(backEvent);
+ if (!backEvent.accepted) {
+ root.pageStack.flickBack();
+ backEvent.accepted = true;
+ }
+ }
+
+ if (Kirigami.Settings.isMobile && !backEvent.accepted && Qt.platform.os !== "ios") {
+ Qt.quit();
+ }
+ }
+ function goForward() {
+ root.pageStack.currentIndex = Math.min(root.pageStack.depth-1, root.pageStack.currentIndex + 1);
+ }
+ Keys.onBackPressed: {
+ goBack();
+ event.accepted = true
+ }
+ Shortcut {
+ sequence: "Forward"
+ onActivated: __pageStack.goForward();
+ }
+ Shortcut {
+ sequence: StandardKey.Forward
+ onActivated: __pageStack.goForward();
+ }
+ Shortcut {
+ sequence: StandardKey.Back
+ onActivated: __pageStack.goBack();
+ }
+
+ Rectangle {
+ z: -1
+ anchors.fill: parent
+ color: Kirigami.Theme.backgroundColor
+ }
+ focus: true
+ }
+}
diff --git a/src/kirigamiplugin.cpp b/src/kirigamiplugin.cpp
--- a/src/kirigamiplugin.cpp
+++ b/src/kirigamiplugin.cpp
@@ -103,6 +103,8 @@
//2.1
qmlRegisterType(componentUrl(QStringLiteral("AbstractItemViewHeader.qml")), uri, 2, 1, "AbstractItemViewHeader");
qmlRegisterType(componentUrl(QStringLiteral("ItemViewHeader.qml")), uri, 2, 1, "ItemViewHeader");
+ qmlRegisterType(componentUrl(QStringLiteral("AbstractApplicationItem.qml")), uri, 2, 1, "AbstractApplicationItem");
+ qmlRegisterType(componentUrl(QStringLiteral("ApplicationItem.qml")), uri, 2, 1, "ApplicationItem");
qmlProtectModule(uri, 2);
}