1/*
2 *  SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org>
3 *
4 *  SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
7import QtQuick 2.12
8import QtQuick.Controls 2.0 as QQC2
9import QtQuick.Window 2.5
10import "templates/private"
11import org.kde.kirigami 2.4
12import QtGraphicalEffects 1.0
13
14/**
15 * A window that provides some basic features needed for all apps
16 * Use this class only if you need a custom content for your application,
17 * different from the Page Row behavior recommended by the HIG and provided
18 * by ApplicationWindow.
19 * It is recommended to use ApplicationWindow instead
20 * @see ApplicationWindow
21 *
22 * It's usually used as a root QML component for the application.
23 * It provides support for a central page stack, side drawers and
24 * a top ApplicationHeader, as well as basic support for the
25 * Android back button
26 *
27 * Setting a width and height property on the ApplicationWindow
28 * will set its initial size, but it won't set it as an automatically binding.
29 * to resize programmatically the ApplicationWindow they need to
30 * be assigned again in an imperative fashion
31 *
32 *
33 * Example usage:
34 * @code
35 * import org.kde.kirigami 2.4 as Kirigami
36 *
37 * Kirigami.ApplicationWindow {
38 *  [...]
39 *     globalDrawer: Kirigami.GlobalDrawer {
40 *         actions: [
41 *            Kirigami.Action {
42 *                text: "View"
43 *                icon.name: "view-list-icons"
44 *                Kirigami.Action {
45 *                        text: "action 1"
46 *                }
47 *                Kirigami.Action {
48 *                        text: "action 2"
49 *                }
50 *                Kirigami.Action {
51 *                        text: "action 3"
52 *                }
53 *            },
54 *            Kirigami.Action {
55 *                text: "Sync"
56 *                icon.name: "folder-sync"
57 *            }
58 *         ]
59 *     }
60 *
61 *     contextDrawer: Kirigami.ContextDrawer {
62 *         id: contextDrawer
63 *     }
64 *
65 *     pageStack: PageStack {
66 *         ...
67 *     }
68 *  [...]
69 * }
70 * @endcode
71 *
72 * @inherit QtQuick.Controls.ApplicationWindow
73 */
74QQC2.ApplicationWindow {
75    id: root
76
77    /**
78     * This property holds the stack used to allocate the pages and to manage the
79     * transitions between them.
80     *
81     * Put a container here, such as QtQuick.Controls.StackView.
82     */
83    property Item pageStack
84    LayoutMirroring.enabled: Qt.application.layoutDirection == Qt.RightToLeft
85    LayoutMirroring.childrenInherit: true
86
87    /**
88     * Shows a little passive notification at the bottom of the app window
89     * lasting for few seconds, with an optional action button.
90     *
91     * @param message The text message to be shown to the user.
92     * @param timeout How long to show the message:
93     *            possible values: "short", "long" or the number of milliseconds
94     * @param actionText Text in the action button, if any.
95     * @param callBack A JavaScript function that will be executed when the
96     *            user clicks the button.
97     */
98    function showPassiveNotification(message, timeout, actionText, callBack) {
99        if (!internal.__passiveNotification) {
100            var component = Qt.createComponent("templates/private/PassiveNotification.qml");
101            internal.__passiveNotification = component.createObject(overlay.parent);
102        }
103
104        internal.__passiveNotification.showNotification(message, timeout, actionText, callBack);
105    }
106
107   /**
108    * Hide the passive notification, if any is shown
109    */
110    function hidePassiveNotification() {
111        if(internal.__passiveNotification) {
112           internal.__passiveNotification.hideNotification();
113        }
114    }
115
116
117    /**
118     * @returns a pointer to this application window
119     * can be used anywhere in the application.
120     */
121    function applicationWindow() {
122        return root;
123    }
124
125   /**
126    * header: ApplicationHeader
127    * An item that can be used as a title for the application.
128    * Scrolling the main page will make it taller or shorter (through the point of going away)
129    * It's a behavior similar to the typical mobile web browser addressbar
130    * the minimum, preferred and maximum heights of the item can be controlled with
131    * * Layout.minimumHeight: default is 0, i.e. hidden
132    * * Layout.preferredHeight: default is Units.gridUnit * 1.6
133    * * Layout.maximumHeight: default is Units.gridUnit * 3
134    *
135    * To achieve a titlebar that stays completely fixed just set the 3 sizes as the same
136    * //FIXME: this should become an actual ApplicationHeader
137    */
138    //header: undefined
139
140    /**
141     * This property controls whether the standard chrome of the app, such
142     * as the Action button, the drawer handles and the application
143     * header should be visible or not.
144     */
145    property bool controlsVisible: true
146
147    /**
148     * This property holds the drawer for global actions, that will be opened by sliding from the
149     * left screen edge or by dragging the ActionButton to the right.
150     *
151     * It is recommended to use the GlobalDrawer class here.
152     */
153    property OverlayDrawer globalDrawer
154
155    /**
156     * This property holds whether the application is considered to be in "widescreen" mode, such
157     * as on desktops or horizontal tablets.
158     *
159     * Different styles can have an own logic for deciding this.
160     */
161    property bool wideScreen: width >= Units.gridUnit * 60
162
163    /**
164     * The drawer for context-dependent actions, that will be opened by sliding from the
165     * right screen edge or by dragging the ActionButton to the left.
166     * It is recommended to use the ContextDrawer class here.
167     *
168     * The contents of the context drawer should depend from what page is
169     * loaded in the main pageStack
170     *
171     * Example usage:
172     * @code
173     * import org.kde.kirigami 2.4 as Kirigami
174     *
175     * Kirigami.ApplicationWindow {
176     *  [...]
177     *     contextDrawer: Kirigami.ContextDrawer {
178     *         id: contextDrawer
179     *     }
180     *  [...]
181     * }
182     * @endcode
183     *
184     * @code
185     * import org.kde.kirigami 2.4 as Kirigami
186     *
187     * Kirigami.Page {
188     *   [...]
189     *     contextualActions: [
190     *         Kirigami.Action {
191     *             icon.name: "edit"
192     *             text: "Action text"
193     *             onTriggered: {
194     *                 // do stuff
195     *             }
196     *         },
197     *         Kirigami.Action {
198     *             icon.name: "edit"
199     *             text: "Action text"
200     *             onTriggered: {
201     *                 // do stuff
202     *             }
203     *         }
204     *     ]
205     *   [...]
206     * }
207     * @endcode
208     *
209     * When this page will be the current one, the context drawer will visualize
210     * contextualActions defined as property in that page.
211     */
212    property OverlayDrawer contextDrawer
213
214    /**
215     * This property holds whether the application is in reachable mode for single hand use.
216     * the whole content of the application is moved down the screen to be
217     * reachable with the thumb. if wideScreen is true, or reachableModeEnabled is false,
218     * tis property has no effect.
219     */
220    property bool reachableMode: false
221
222    /**
223     * This property holds whether the application will go into reachable mode on pull down.
224     */
225    property bool reachableModeEnabled: true
226
227    /**
228     * This property holds a standard action that will quit the application when triggered.
229     * Its properties have the following values:
230     *
231     * @code
232     * Action {
233     *     text: "Quit"
234     *     icon.name: "application-exit-symbolic";
235     *     shortcut: StandardKey.Quit
236     *     [...]
237     * @endcode
238     * @since 5.76
239     */
240    readonly property Action quitAction: _quitAction
241
242    color: Theme.backgroundColor
243
244    MouseArea {
245        parent: contentItem.parent
246        z: 0
247        anchors.fill: parent
248        onClicked: root.reachableMode = false;
249        visible: root.reachableMode && root.reachableModeEnabled
250        Rectangle {
251            anchors.fill: parent
252            color: Qt.rgba(0, 0, 0, 0.3)
253            opacity: 0.15
254            Icon {
255                anchors.horizontalCenter: parent.horizontalCenter
256                y: x
257                width: Units.iconSizes.large
258                height: width
259                source: "go-up"
260            }
261        }
262    }
263
264    contentItem.z: 1
265    contentItem.anchors.left: contentItem.parent.left
266    contentItem.anchors.right: contentItem.parent.right
267    contentItem.anchors.topMargin: root.wideScreen && header && controlsVisible ? header.height : 0
268    contentItem.anchors.leftMargin: root.globalDrawer && root.globalDrawer.modal === false && (!root.pageStack || root.pageStack.leftSidebar !== root.globalDrawer) ? root.globalDrawer.width * root.globalDrawer.position : 0
269    contentItem.anchors.rightMargin: root.contextDrawer && root.contextDrawer.modal === false ? root.contextDrawer.width * root.contextDrawer.position : 0
270
271    Binding {
272        when: menuBar !== undefined
273        target: menuBar
274        property: "x"
275        value: -contentItem.x
276    }
277    Binding {
278        when: header !== undefined
279        target: header
280        property: "x"
281        value: -contentItem.x
282    }
283    Binding {
284        when: footer !== undefined
285        target: footer
286        property: "x"
287        value: -contentItem.x
288    }
289
290    contentItem.transform: Translate {
291        Behavior on y {
292            NumberAnimation {
293                duration: Units.longDuration
294                easing.type: Easing.InOutQuad
295            }
296        }
297        y: root.reachableMode && root.reachableModeEnabled && !root.wideScreen ? root.height/2 : 0
298        x: root.globalDrawer && root.globalDrawer.modal === true && root.globalDrawer.toString().indexOf("SplitDrawer") === 0 ? root.globalDrawer.contentItem.width * root.globalDrawer.position : 0
299    }
300    //Don't want overscroll in landscape mode
301    onWidthChanged: {
302        if (width > height) {
303            root.reachableMode = false;
304        }
305    }
306    Binding {
307        when: globalDrawer !== undefined && root.visible
308        target: globalDrawer
309        property: "parent"
310        value: overlay
311    }
312    Binding {
313        when: contextDrawer !== undefined && root.visible
314        target: contextDrawer
315        property: "parent"
316        value: overlay
317    }
318    onPageStackChanged: pageStack.parent = contentItem;
319
320    width: Settings.isMobile ? Units.gridUnit * 30 : Units.gridUnit * 55
321    height: Settings.isMobile ? Units.gridUnit * 45 : Units.gridUnit * 40
322    visible: true
323
324    Component.onCompleted: {
325        // Explicitly break the binding as we need this to be set only at startup.
326        // if the bindings are active, after this the window is resized by the
327        // compositor and then the bindings are reevaluated, then the window
328        // size would reset ignoring what the compositor asked.
329        // see BUG 433849
330        root.width = root.width;
331        root.height = root.height;
332    }
333
334    QtObject {
335        id: internal
336        property QtObject __passiveNotification
337    }
338
339    Action {
340        id: _quitAction
341        text: qsTr("Quit")
342        icon.name: "application-exit";
343        shortcut: StandardKey.Quit
344        onTriggered: root.close()
345    }
346}
347