Kirigami2

Page.qml
1 /*
2  * SPDX-FileCopyrightText: 2015 Marco Martin <[email protected]>
3  *
4  * SPDX-License-Identifier: LGPL-2.0-or-later
5  */
6 
7 import QtQuick 2.5
8 import QtQuick.Layouts 1.2
9 import QtQuick.Templates 2.1 as T2
10 import QtQuick.Controls 2.1 as QQC2
11 import org.kde.kirigami 2.10 as Kirigami
12 import "private" as P
13 
14 /**
15  * Page is a container for all the app pages: everything pushed to the
16  * ApplicationWindow's pageStack should be a Page.
17  *
18  * @see ScrollablePage
19  * For content that should be scrollable, such as ListViews, use ScrollablePage instead.
20  * @inherit QtQuick.Controls.Page
21  */
22 QQC2.Page {
23  id: root
24 
25 //BEGIN properties
26  /**
27  * @brief The default content padding is 1 gridUnit.
28  * // TODO: check if this is not displayed in the generated api doc.
29  */
30  padding: Kirigami.Units.gridUnit
31 
32  /**
33  * @brief The bottom content padding.
34  *
35  * default: `this is bound to the height of the floating action buttons when present; if not, then verticalPadding.`
36  * // TODO: check if this is not displayed in the generated api doc.
37  */
38  bottomPadding: actionButtons.item ? actionButtons.height : verticalPadding
39 
40  /**
41  * @brief If the central element of the page is a Flickable
42  * (ListView and Gridview as well) you can set it there.
43  *
44  * Normally, you wouldn't need to do that, but just use the
45  * ScrollablePage element instead.
46  *
47  * Use this if your flickable has some non standard properties, such as not covering the whole Page.
48  *
49  * @see ScrollablePage
50  */
51  property Flickable flickable
52 
53  /**
54  * @brief Defines the contextual actions for the page:
55  * an easy way to assign actions in the right sliding panel
56  *
57  * Example usage:
58  * @code
59  * import org.kde.kirigami 2.4 as Kirigami
60  *
61  * Kirigami.ApplicationWindow {
62  * [...]
63  * contextDrawer: Kirigami.ContextDrawer {
64  * id: contextDrawer
65  * }
66  * [...]
67  * }
68  * @endcode
69  *
70  * @code
71  * import org.kde.kirigami 2.4 as Kirigami
72  *
73  * Kirigami.Page {
74  * [...]
75  * actions.contextualActions: [
76  * Kirigami.Action {
77  * icon.name: "edit"
78  * text: "Action text"
79  * onTriggered: {
80  * // do stuff
81  * }
82  * },
83  * Kirigami.Action {
84  * icon.name: "edit"
85  * text: "Action text"
86  * onTriggered: {
87  * // do stuff
88  * }
89  * }
90  * ]
91  * [...]
92  * }
93  * @endcode
94  *
95  * @warning This will likely be removed someday.
96  * @property list<QtQml.QtObject> contextualActions
97  */
98  // TODO: remove
99  property alias contextualActions: actionsGroup.contextualActions
100 
101  /**
102  * @brief An optional single action for the action button.
103  *
104  * Example usage:
105  * @code
106  * import org.kde.kirigami 2.4 as Kirigami
107  * Kirigami.Page {
108  * actions.main: Kirigami.Action {
109  * icon.name: "edit"
110  * onTriggered: {
111  * // do stuff
112  * }
113  * }
114  * }
115  * @endcode
116  * @warning This will likely be removed someday.
117  * @property Action mainAction
118  */
119  //TODO: remove
120  property alias mainAction: actionsGroup.main
121 
122  /**
123  * @brief An optional extra action at the left of the main action button.
124  *
125  * Example usage:
126  *
127  * @code
128  * import org.kde.kirigami 2.4 as Kirigami
129  * Kirigami.Page {
130  * actions.left: Kirigami.Action {
131  * icon.name: "edit"
132  * onTriggered: {
133  * // do stuff
134  * }
135  * }
136  * }
137  * @endcode
138  * @warning This will likely be removed someday.
139  * @property Action leftAction
140  */
141  // TODO: remove
142  property alias leftAction: actionsGroup.left
143 
144  /**
145  * @brief An optional extra action at the right of the main action button.
146  *
147  * Example usage:
148  * @code
149  * import org.kde.kirigami 2.4 as Kirigami
150  * Kirigami.Page {
151  * actions.right: Kirigami.Action {
152  * icon.name: "edit"
153  * onTriggered: {
154  * // do stuff
155  * }
156  * }
157  * }
158  * @endcode
159  * @warning This will likely be removed someday.
160  * @property Action rightAction
161  */
162  // TODO: remove
163  property alias rightAction: actionsGroup.right
164 
165  /**
166  * @brief This property holds the actions group.
167  * @code
168  * import org.kde.kirigami 2.4 as Kirigami
169  * Kirigami.Page {
170  * actions {
171  * main: Kirigami.Action {...}
172  * left: Kirigami.Action {...}
173  * right: Kirigami.Action {...}
174  * contextualActions: [
175  * Kirigami.Action {...},
176  * Kirigami.Action {...}
177  * ]
178  * }
179  * }
180  * @endcode
181  * @property org::kde::kirigami::private::PageActionPropertyGroup actions
182  */
183  readonly property alias actions: actionsGroup
184 
185  /**
186  * Emitted when a visualization for the actions is about to be shown,
187  * such as the toolbar menu or the contextDrawer.
188  *
189  * @since 2.7
190  */
191  signal contextualActionsAboutToShow
192 
193  /**
194  * @brief This property tells us if it is the currently active page.
195  *
196  * Specifies if it's the currently selected page in the window's pages row, or if layers
197  * are used whether this is the topmost item on the layers stack. If the page is
198  * not attached to either a column view or a stack view, expect this to be true.
199  *
200  * @since 2.1
201  */
202  //TODO KF6: remove this or at least all the assumptions about the internal tree structure of items
203  readonly property bool isCurrentPage: Kirigami.ColumnView.view
204  ? (Kirigami.ColumnView.index === Kirigami.ColumnView.view.currentIndex && Kirigami.ColumnView.view.parent.parent.currentItem === Kirigami.ColumnView.view.parent)
205  : (parent && parent instanceof QQC2.StackView
206  ? parent.currentItem === root
207  : true)
208 
209  /**
210  * An item which stays on top of every other item in the page,
211  * if you want to make sure some elements are completely in a
212  * layer on top of the whole content, parent items to this one.
213  * It's a "local" version of ApplicationWindow's overlay
214  *
215  * @property Item overlay
216  * @since 2.5
217  */
218  readonly property alias overlay: overlayItem
219 
220  /**
221  * @brief This holds the icon that represents this page.
222  * @property var icon
223  */
224  property P.ActionIconGroup icon: P.ActionIconGroup {}
225 
226  /**
227  * @brief Whether this page needs user attention.
228  */
229  property bool needsAttention
230 
231  /**
232  * @brief Progress of a task this page is doing.
233  *
234  * Set to undefined to indicate that there are no ongoing tasks.
235  *
236  * default: ``undefined``
237  *
238  * @property real progress
239  */
240  property var progress: undefined
241 
242  /**
243  * @brief The delegate which will be used to draw the page title.
244  *
245  * It can be customized to put any kind of Item in there.
246  *
247  * @since 2.7
248  */
249  property Component titleDelegate: Component {
250  id: defaultTitleDelegate
251  P.DefaultPageTitleDelegate {
252  text: root.title
253  }
254  }
255 
256  /**
257  * The item used as global toolbar for the page
258  * present only if we are in a PageRow as a page or as a layer,
259  * and the style is either Titles or ToolBar.
260  *
261  * @since 2.5
262  */
263  readonly property Item globalToolBarItem: globalToolBar.item
264 
265  /**
266  * The style for the automatically generated global toolbar: by default the Page toolbar is the one set globally in the PageRow in its globalToolBar.style property.
267  * A single page can override the application toolbar style for itself.
268  * It is discouraged to use this, except very specific exceptions, like a chat
269  * application which can't have controls on the bottom except the text field.
270  * If the Page is not in a PageRow, by default the toolbar will be invisible,
271  * so has to be explicitly set to Kirigami.ApplicationHeaderStyle.ToolBar if
272  * desired to be used in that case.
273  */
274  property int globalToolBarStyle: {
275  if (globalToolBar.row && !globalToolBar.stack) {
276  return globalToolBar.row.globalToolBar.actualStyle;
277  } else if (globalToolBar.stack) {
278  return Kirigami.Settings.isMobile ? Kirigami.ApplicationHeaderStyle.Titles : Kirigami.ApplicationHeaderStyle.ToolBar;
279  } else {
280  return Kirigami.ApplicationHeaderStyle.None;
281  }
282  }
283 //END properties
284 
285 //BEGIN signal and signal handlers
286  /**
287  * @brief Emitted when the application requests a Back action.
288  *
289  * For instance a global "back" shortcut or the Android
290  * Back button has been pressed.
291  * The page can manage the back event by itself,
292  * and if it set event.accepted = true, it will stop the main
293  * application to manage the back event.
294  */
295  signal backRequested(var event);
296 
297 
298  // Look for sheets and cose them
299  // FIXME: port Sheets to Popup?
300  onBackRequested: event => {
301  let item;
302  for (const i in root.resources) {
303  item = root.resources[i];
304  if (item.hasOwnProperty("close") && item.hasOwnProperty("sheetOpen") && item.sheetOpen) {
305  item.close()
306  event.accepted = true;
307  return;
308  }
309  }
310  }
311 
312  // NOTE: contentItem will be created if not existing (and contentChildren of Page would become its children) This with anchors enforces the geometry we want, where globalToolBar is a super-header, on top of header
313  contentItem: Item {
314  anchors {
315  top: (root.header && root.header.visible)
316  ? root.header.bottom
317  : (globalToolBar.visible ? globalToolBar.bottom : parent.top)
318  topMargin: root.topPadding + root.spacing
319  bottom: (root.footer && root.footer.visible) ? root.footer.top : parent.bottom
320  bottomMargin: root.bottomPadding + root.spacing
321  }
322  }
323 
324  background: Rectangle {
325  color: Kirigami.Theme.backgroundColor
326  }
327 
328  implicitHeight: ((header && header.visible) ? header.implicitHeight : 0) + ((footer && footer.visible) ? footer.implicitHeight : 0) + contentItem.implicitHeight + topPadding + bottomPadding
329  implicitWidth: contentItem.implicitWidth + leftPadding + rightPadding
330 
331  // FIXME: on material the shadow would bleed over
332  clip: root.header !== null;
333 
334  onHeaderChanged: {
335  if (header) {
336  header.anchors.top = Qt.binding(() => globalToolBar.visible ? globalToolBar.bottom : root.top);
337  }
338  }
339 
340  Component.onCompleted: {
341  headerChanged();
342  parentChanged(root.parent);
343  globalToolBar.syncSource();
344  actionButtons.pageComplete = true
345  }
346 
347  onParentChanged: {
348  if (!parent) {
349  return;
350  }
351  globalToolBar.stack = null;
352  globalToolBar.row = null;
353 
354  if (root.Kirigami.ColumnView.view) {
355  globalToolBar.row = root.Kirigami.ColumnView.view.__pageRow;
356  }
357  if (root.T2.StackView.view) {
358  globalToolBar.stack = root.T2.StackView.view;
359  globalToolBar.row = root.T2.StackView.view ? root.T2.StackView.view.parent : null;
360  }
361  if (globalToolBar.row) {
362  root.globalToolBarStyleChanged.connect(globalToolBar.syncSource);
363  globalToolBar.syncSource();
364  }
365  }
366 //END signals and signal handlers
367 
368  // in data in order for them to not be considered for contentItem, contentChildren, contentData
369  data: [
370  P.PageActionPropertyGroup {
371  id: actionsGroup
372  },
373 
374  Item {
375  id: overlayItem
376  parent: root
377  z: 9997
378  anchors {
379  fill: parent
380  topMargin: globalToolBar.height
381  }
382  },
383  // global top toolbar if we are in a PageRow (in the row or as a layer)
384  Loader {
385  id: globalToolBar
386  z: 9999
387  height: item ? item.implicitHeight : 0
388  anchors {
389  left: parent.left
390  right: parent.right
391  top: parent.top
392  }
393  property Kirigami.PageRow row
394  property T2.StackView stack
395 
396  // don't load async so that on slower devices we don't have the page content height changing while loading in
397  // otherwise, it looks unpolished and jumpy
398  asynchronous: false
399 
400  visible: active
401  active: (root.titleDelegate !== defaultTitleDelegate || root.globalToolBarStyle === Kirigami.ApplicationHeaderStyle.ToolBar || root.globalToolBarStyle === Kirigami.ApplicationHeaderStyle.Titles)
402  onActiveChanged: {
403  if (active) {
404  syncSource();
405  }
406  }
407 
408  function syncSource() {
409  if (root.globalToolBarStyle !== Kirigami.ApplicationHeaderStyle.ToolBar &&
410  root.globalToolBarStyle !== Kirigami.ApplicationHeaderStyle.Titles &&
411  root.titleDelegate !== defaultTitleDelegate) {
412  sourceComponent = root.titleDelegate;
413  } else if (active) {
414  const url = root.globalToolBarStyle === Kirigami.ApplicationHeaderStyle.ToolBar
415  ? "private/globaltoolbar/ToolBarPageHeader.qml"
416  : "private/globaltoolbar/TitlesPageHeader.qml";
417  // TODO: find container reliably, remove assumption
418  setSource(Qt.resolvedUrl(url), {
419  pageRow: Qt.binding(() => row),
420  page: root,
421  current: Qt.binding(() => {
422  if (!row && !stack) {
423  return true;
424  } else if (stack) {
425  return stack;
426  } else {
427  return row.currentIndex === root.Kirigami.ColumnView.level;
428  }
429  }),
430  });
431  }
432  }
433  },
434  // bottom action buttons
435  Loader {
436  id: actionButtons
437  z: 9999
438  parent: root
439  anchors {
440  left: parent.left
441  right: parent.right
442  bottom: parent.bottom
443  }
444  // if the page doesn't inherit, assume it has custom colors we want to use them here too
445  Kirigami.Theme.inherit: !root.Kirigami.Theme.inherit
446  Kirigami.Theme.colorSet: Kirigami.Theme.Button
447 
448  // It should be T2.Page, Qt 5.7 doesn't like it
449  property Item page: root
450  height: item ? item.implicitHeight : 0
451 
452  asynchronous: true
453 
454  property bool pageComplete: false
455 
456  active: {
457  // Important! Do not do anything until the page has been
458  // completed, so we are sure what the globalToolBarStyle is,
459  // otherwise we risk creating the content and then discarding it.
460  if (!pageComplete) {
461  return false;
462  }
463 
464  if ((globalToolBar.row && globalToolBar.row.globalToolBar.actualStyle === Kirigami.ApplicationHeaderStyle.ToolBar)
465  || root.globalToolBarStyle === Kirigami.ApplicationHeaderStyle.ToolBar
466  || root.globalToolBarStyle === Kirigami.ApplicationHeaderStyle.None) {
467  return false;
468  }
469 
470  if (!root.actions.main && !root.actions.left && !root.actions.right && root.actions.contextualActions.length === 0) {
471  return false;
472  }
473 
474  // Legacy
475  if (typeof applicationWindow === "undefined") {
476  return true;
477  }
478 
479  if (applicationWindow().header && applicationWindow().header.toString().indexOf("ToolBarApplicationHeader") !== -1) {
480  return false;
481  }
482 
483  if (applicationWindow().footer && applicationWindow().footer.toString().indexOf("ToolBarApplicationHeader") !== -1) {
484  return false;
485  }
486 
487  return true;
488  }
489 
490  source: Qt.resolvedUrl("./private/ActionButton.qml")
491  }
492  ]
493 
494  Layout.fillWidth: true
495 }
QTextStream & right(QTextStream &stream)
QTextStream & left(QTextStream &stream)
QTextStream & left(QTextStream &s)
QTextStream & right(QTextStream &s)
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Sun Jan 29 2023 04:11:03 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.