Kirigami2

PageRow.qml
1 /*
2  * SPDX-FileCopyrightText: 2016 Marco Martin <[email protected]>
3  *
4  * SPDX-License-Identifier: LGPL-2.0-or-later
5  */
6 
7 import QtQuick 2.15
8 import QtQuick.Layouts 1.2
9 import QtQml.Models 2.2
10 import QtQuick.Templates 2.0 as QT
11 import QtQuick.Controls 2.0 as QQC2
12 import org.kde.kirigami 2.20 as Kirigami
13 import "private/globaltoolbar" as GlobalToolBar
14 import "templates" as KT
15 
16 /**
17  * PageRow implements a row-based navigation model, which can be used
18  * with a set of interlinked information pages. Items are pushed in the
19  * back of the row and the view scrolls until that row is visualized.
20  * A PageRow can show a single page or a multiple set of columns, depending
21  * on the window width: on a phone a single column should be fullscreen,
22  * while on a tablet or a desktop more than one column should be visible.
23  *
24  * @inherits QtQuick.Controls.Control
25  */
26 QT.Control {
27  id: root
28 
29 //BEGIN PROPERTIES
30  /**
31  * @brief This property holds the number of items currently pushed onto the view.
32  * @property int depth
33  */
34  property alias depth: columnView.count
35 
36  /**
37  * @brief This property holds the last page in the row.
38  * @property Page lastItem
39  */
40  readonly property Item lastItem: columnView.contentChildren.length > 0 ? columnView.contentChildren[columnView.contentChildren.length - 1] : null
41 
42  /**
43  * @brief This property holds the currently visible/active page.
44  *
45  * Because of the ability to display multiple pages, it will hold the currently active page.
46  *
47  * @property Page currentItem
48  */
49  property alias currentItem: columnView.currentItem
50 
51  /**
52  * @brief This property holds the index of the currently active page.
53  * @see currentItem
54  * @property int currentIndex
55  */
56  property alias currentIndex: columnView.currentIndex
57 
58  /**
59  * @brief This property sets the initial page for this PageRow.
60  * @property Page initialPage
61  */
62  property variant initialPage
63 
64  /**
65  * @brief This property holds the main ColumnView of this Row.
66  * @property ColumnView contentItem
67  */
68  contentItem: columnView
69 
70  /**
71  * @brief This property holds the ColumnView that this PageRow owns.
72  *
73  * Generally, you shouldn't need to change the value of this property.
74  *
75  * @property ColumnView columnView
76  * @since 2.12
77  */
78  property alias columnView: columnView
79 
80  /**
81  * @brief This property holds the present pages in the PageRow.
82  * @property list<Page> items
83  * @since 2.6
84  */
85  property alias items: columnView.contentChildren;
86 
87  /**
88  * @brief This property holds all visible pages in the PageRow,
89  * excluding those which are scrolled away.
90  * @property list<Page> visibleItems
91  * @since 2.6
92  */
93  property alias visibleItems: columnView.visibleItems
94 
95  /**
96  * @brief This property holds the first page in the PageRow that is at least partially visible.
97  * @note Pages before that one (the one contained in the property) will be out of the viewport.
98  * @see ColumnView::firstVisibleItem
99  * @property Item firstVisibleItem
100  * @since 2.6
101  */
102  property alias firstVisibleItem: columnView.firstVisibleItem
103 
104  /**
105  * @brief This property holds the last page in the PageRow that is at least partially visible.
106  * @note Pages after that one (the one contained in the property) will be out of the viewport.
107  * @see ColumnView::lastVisibleItem
108  * @property Item lastVisibleItem
109  * @since 2.6
110  */
111  property alias lastVisibleItem: columnView.lastVisibleItem
112 
113  /**
114  * @brief This property holds the default width for a column.
115  *
116  * default: ``20 * Kirigami.Units.gridUnit``
117  *
118  * @note Pages can override it using implicitWidth, Layout.fillWidth, Layout.minimumWidth etc.
119  */
120  property int defaultColumnWidth: Kirigami.Units.gridUnit * 20
121 
122  /**
123  * @brief This property sets whether it is possible to go back/forward
124  * by swiping with a gesture on the content view.
125  *
126  * default: ``true``
127  *
128  * @property bool interactive
129  */
130  property alias interactive: columnView.interactive
131 
132  /**
133  * @brief This property tells whether the PageRow is wide enough to show multiple pages.
134  * @since 5.37
135  */
136  readonly property bool wideMode: root.width >= root.defaultColumnWidth*2 && depth >= 2
137 
138  /**
139  * @brief This property sets whether the separators between pages should be displayed.
140  *
141  * default: ``true``
142  *
143  * @property bool separatorVisible
144  * @since 5.38
145  */
146  property alias separatorVisible: columnView.separatorVisible
147 
148  /**
149  * @brief This property sets the appearance of an optional global toolbar for the whole PageRow.
150  *
151  * It's a grouped property comprised of the following properties:
152  * * style (``Kirigami.ApplicationHeaderStyle``): can have the following values:
153  * * ``Auto``: Depending on application formfactor, it can behave automatically like other values, such as a Breadcrumb on mobile and ToolBar on desktop.
154  * * ``Breadcrumb``: It will show a breadcrumb of all the page titles in the stack, for easy navigation.
155  * * ``Titles``: Each page will only have its own title on top.
156  * * ``TabBar``: The global toolbar will look like a TabBar for choosing which page to display.
157  * * ``ToolBar``: Each page will have the title on top together buttons and menus to represent all of the page actions. Not available on Mobile systems.
158  * * ``None``: No global toolbar will be shown.
159  *
160  * * ``actualStyle``: This will represent the actual style of the toolbar; it can be different from style in the case style is Auto.
161  * * ``showNavigationButtons``: OR flags combination of Kirigami.ApplicationHeaderStyle.ShowBackButton and Kirigami.ApplicationHeaderStyle.ShowForwardButton.
162  * * ``toolbarActionAlignment: Qt::Alignment``: How to horizontally align the actions when using the ToolBar style. Note that anything but Qt.AlignRight will cause the title to be hidden (default: ``Qt.AlignRight``).
163  * * ``minimumHeight: int`` Minimum height of the header, which will be resized when scrolling. Only in Mobile mode (default: ``preferredHeight``, sliding but no scaling).
164  * * ``preferredHeight: int`` The height the toolbar will usually have.
165  * * ``maximumHeight: int `` The height the toolbar will have in mobile mode when the app is in reachable mode (default: preferredHeight * 1.5).
166  * * ``leftReservedSpace: int, readonly`` How many pixels of extra space are reserved at the left of the page toolbar (typically for navigation buttons or a drawer handle).
167  * * ``rightReservedSpace: int, readonly`` How many pixels of extra space are reserved at the right of the page toolbar (typically for a drawer handle).
168  *
169  * @property org::kde::kirigami::private::globaltoolbar::PageRowGlobalToolBarStyleGroup globalToolBar
170  * @since 5.48
171  */
172  readonly property alias globalToolBar: globalToolBar
173 
174  /**
175  * @brief This property assigns a drawer as an internal left sidebar for this PageRow.
176  *
177  * In this case, when open and not modal, the drawer contents will be in the same layer as the base pagerow.
178  * Pushing any other layer on top will cover the sidebar.
179  *
180  * @since 5.84
181  */
182  // TODO KF6: globaldrawer should use actions also used by this sidebar instead of reparenting globaldrawer contents?
183  property OverlayDrawer leftSidebar
184 
185  /**
186  * @brief This property holds the modal layers.
187  *
188  * Sometimes an application needs a modal page that always covers all the rows.
189  * For instance the full screen image of an image viewer or a settings page.
190  *
191  * @property QtQuick.Controls.StackView layers
192  * @since 5.38
193  */
194  property alias layers: layersStack
195 
196  /**
197  * @brief This property holds whether to automatically pop pages at the top of the stack if they are not visible.
198  *
199  * If a user navigates to a previous page on the stack (ex. pressing back button) and pages above
200  * it on the stack are not visible, they will be popped if this property is true.
201  *
202  * @since 5.101
203  */
204  property bool popHiddenPages: false
205 //END PROPERTIES
206 
207 //BEGIN FUNCTIONS
208  /**
209  * @brief Pushes a page on the stack.
210  *
211  * The page can be defined as a component, item or string.
212  * If an item is used then the page will get re-parented.
213  * If a string is used then it is interpreted as a url that is used to load a page
214  * component.
215  * The last pushed page will become the current item.
216  *
217  * @param page The page can also be given as an array of pages.
218  * In this case all those pages will
219  * be pushed onto the stack. The items in the stack can be components, items or
220  * strings just like for single pages.
221  * Additionally an object can be used, which specifies a page and an optional
222  * properties property.
223  * This can be used to push multiple pages while still giving each of
224  * them properties.
225  * When an array is used the transition animation will only be to the last page.
226  *
227  * @param properties The properties argument is optional and allows defining a
228  * map of properties to set on the page. If page is actually an array of pages, properties should also be an array of key/value maps
229  * @return The new created page (or the last one if it was an array)
230  */
231  function push(page, properties) {
232  const item = insertPage(depth, page, properties);
233  currentIndex = depth - 1;
234  return item;
235  }
236 
237  /**
238  * @brief Pushes a page as a new dialog on desktop and as a layer on mobile.
239  * @param page The page can be defined as a component, item or string. If an item is
240  * used then the page will get re-parented. If a string is used then it
241  * is interpreted as a url that is used to load a page component. Once
242  * pushed the page gains the methods `closeDialog` allowing to close itself.
243  * Kirigami only supports calling `closeDialog` once.
244  * @param properties The properties given when initializing the page.
245  * @param windowProperties The properties given to the initialized window on desktop.
246  * @return Returns a newly created page.
247  */
248  function pushDialogLayer(page, properties = {}, windowProperties = {}) {
249  let item;
250  if (Kirigami.Settings.isMobile) {
251  if (QQC2.ApplicationWindow.window.width > Kirigami.Units.gridUnit * 40) {
252  // open as a QQC2.Dialog
253  const dialog = Qt.createQmlObject(`
254  import QtQuick 2.15;
255  import QtQuick.Controls 2.15;
256  import QtQuick.Layouts 1.15;
257  import org.kde.kirigami 2.20 as Kirigami;
258  Kirigami.Dialog {
259  id: dialog
260  modal: true;
261  leftPadding: 0; rightPadding: 0; topPadding: 0; bottomPadding: 0;
262  clip: true
263  header: Kirigami.AbstractApplicationHeader {
264  pageRow: null
265  page: null
266  minimumHeight: Kirigami.Units.gridUnit * 1.6
267  maximumHeight: Kirigami.Units.gridUnit * 1.6
268  preferredHeight: Kirigami.Units.gridUnit * 1.6
269 
270  Keys.onEscapePressed: event => {
271  if (dialog.opened) {
272  dialog.close();
273  } else {
274  event.accepted = false;
275  }
276  }
277 
278  contentItem: RowLayout {
279  width: parent.width
280  Kirigami.Heading {
281  Layout.leftMargin: Kirigami.Units.largeSpacing
282  text: dialog.title
283  elide: Text.ElideRight
284  }
285  Item {
286  Layout.fillWidth: true;
287  }
288  Kirigami.Icon {
289  id: closeIcon
290  Layout.alignment: Qt.AlignVCenter
291  Layout.rightMargin: Kirigami.Units.largeSpacing
292  Layout.preferredHeight: Kirigami.Units.iconSizes.smallMedium
293  Layout.preferredWidth: Kirigami.Units.iconSizes.smallMedium
294  source: closeMouseArea.containsMouse ? "window-close" : "window-close-symbolic"
295  active: closeMouseArea.containsMouse
296  MouseArea {
297  id: closeMouseArea
298  hoverEnabled: true
299  anchors.fill: parent
300  onClicked: mouse => dialog.close();
301  }
302  }
303  }
304  }
305  contentItem: Control { topPadding: 0; leftPadding: 0; rightPadding: 0; bottomPadding: 0; }
306  }`, QQC2.ApplicationWindow.overlay);
307  dialog.width = Qt.binding(() => QQC2.ApplicationWindow.window.width - Kirigami.Units.gridUnit * 5);
308  dialog.height = Qt.binding(() => QQC2.ApplicationWindow.window.height - Kirigami.Units.gridUnit * 5);
309  dialog.x = Kirigami.Units.gridUnit * 2.5;
310  dialog.y = Kirigami.Units.gridUnit * 2.5;
311 
312  if (typeof page === "string") {
313  // url => load component and then load item from component
314  const component = Qt.createComponent(Qt.resolvedUrl(page));
315  item = component.createObject(dialog.contentItem, properties);
316  component.destroy();
317  dialog.contentItem.contentItem = item
318  } else if (page instanceof Component) {
319  item = page.createObject(dialog.contentItem, properties);
320  dialog.contentItem.contentItem = item
321  } else if (page instanceof Item) {
322  item = page;
323  page.parent = dialog.contentItem;
324  }
325  dialog.title = Qt.binding(() => item.title);
326 
327  // Pushing a PageRow is supported but without PageRow toolbar
328  if (item.globalToolBar && item.globalToolBar.style) {
329  item.globalToolBar.style = Kirigami.ApplicationHeaderStyle.None
330  }
331  Object.defineProperty(item, 'closeDialog', {
332  value: function() {
333  dialog.close();
334  }
335  });
336  dialog.open();
337  } else {
338  // open as a layer
339  item = layers.push(page, properties);
340  Object.defineProperty(item, 'closeDialog', {
341  value: function() {
342  layers.pop();
343  }
344  });
345  }
346  } else {
347  // open as a new window
348  if (!windowProperties.modality) {
349  windowProperties.modality = Qt.WindowModal;
350  }
351  if (!windowProperties.height) {
352  windowProperties.height = Kirigami.Units.gridUnit * 30;
353  }
354  if (!windowProperties.width) {
355  windowProperties.width = Kirigami.Units.gridUnit * 50;
356  }
357  if (!windowProperties.minimumWidth) {
358  windowProperties.minimumWidth = Kirigami.Units.gridUnit * 20;
359  }
360  if (!windowProperties.minimumHeight) {
361  windowProperties.minimumHeight = Kirigami.Units.gridUnit * 15;
362  }
363  if (!windowProperties.flags) {
364  windowProperties.flags = Qt.Dialog | Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowCloseButtonHint;
365  }
366  const windowComponent = Qt.createComponent(Qt.resolvedUrl("./ApplicationWindow.qml"));
367  const window = windowComponent.createObject(root, windowProperties);
368  windowComponent.destroy();
369  item = window.pageStack.push(page, properties);
370  Object.defineProperty(item, 'closeDialog', {
371  value: function() {
372  window.close();
373  }
374  });
375  }
376  item.Keys.escapePressed.connect(event => item.closeDialog());
377  return item;
378  }
379 
380  /**
381  * @brief Inserts a new page or a list of new pages at an arbitrary position.
382  *
383  * The page can be defined as a component, item or string.
384  * If an item is used then the page will get re-parented.
385  * If a string is used then it is interpreted as a url that is used to load a page
386  * component.
387  * The current Page will not be changed, currentIndex will be adjusted
388  * accordingly if needed to keep the same current page.
389  *
390  * @param page The page can also be given as an array of pages.
391  * In this case all those pages will
392  * be pushed onto the stack. The items in the stack can be components, items or
393  * strings just like for single pages.
394  * Additionally an object can be used, which specifies a page and an optional
395  * properties property.
396  * This can be used to push multiple pages while still giving each of
397  * them properties.
398  * When an array is used the transition animation will only be to the last page.
399  *
400  * @param properties The properties argument is optional and allows defining a
401  * map of properties to set on the page. If page is actually an array of pages, properties should also be an array of key/value maps
402  * @return The new created page (or the last one if it was an array)
403  * @since 2.7
404  */
405  function insertPage(position, page, properties) {
406  if (!page) {
407  return null
408  }
409  //don't push again things already there
410  if (page.createObject === undefined && typeof page !== "string" && columnView.containsItem(page)) {
411  print("The item " + page + " is already in the PageRow");
412  return null;
413  }
414 
415  position = Math.max(0, Math.min(depth, position));
416 
417  columnView.pop(columnView.currentItem);
418 
419  // figure out if more than one page is being pushed
420  let pages;
421  let propsArray = [];
422  if (page instanceof Array) {
423  pages = page;
424  page = pages.pop();
425  //compatibility with pre-qqc1 api, can probably be removed
426  if (page.createObject === undefined && page.parent === undefined && typeof page !== "string") {
427  properties = properties || page.properties;
428  page = page.page;
429  }
430  }
431  if (properties instanceof Array) {
432  propsArray = properties;
433  properties = propsArray.pop();
434  } else {
435  propsArray = [properties];
436  }
437 
438  // push any extra defined pages onto the stack
439  if (pages) {
440  for (let i = 0; i < pages.length; i++) {
441  let tPage = pages[i];
442  let tProps = propsArray[i];
443  //compatibility with pre-qqc1 api, can probably be removed
444  if (tPage.createObject === undefined && tPage.parent === undefined && typeof tPage !== "string") {
445  if (columnView.containsItem(tPage)) {
446  print("The item " + page + " is already in the PageRow");
447  continue;
448  }
449  tProps = tPage.properties;
450  tPage = tPage.page;
451  }
452 
453  pagesLogic.initAndInsertPage(position, tPage, tProps);
454  ++position;
455  }
456  }
457 
458  // initialize the page
459  const pageItem = pagesLogic.initAndInsertPage(position, page, properties);
460 
461  pagePushed(pageItem);
462 
463  return pageItem;
464  }
465 
466  /**
467  * Move the page at position fromPos to the new position toPos
468  * If needed, currentIndex will be adjusted
469  * in order to keep the same current page.
470  * @since 2.7
471  */
472  function movePage(fromPos, toPos) {
473  columnView.moveItem(fromPos, toPos);
474  }
475 
476  /**
477  * @brief Remove the given page.
478  * @param page The page can be given both as integer position or by reference
479  * @return The page that has just been removed
480  * @since 2.7
481  */
482  function removePage(page) {
483  if (depth === 0) {
484  return null;
485  }
486 
487  return columnView.removeItem(page);
488  }
489 
490  /**
491  * @brief Pops a page off the stack.
492  * @param page If page is specified then the stack is unwound to that page,
493  * to unwind to the first page specify page as null.
494  * @return The page instance that was popped off the stack.
495  */
496  function pop(page) {
497  if (depth === 0) {
498  return null;
499  }
500 
501  return columnView.pop(page);
502  }
503 
504  /**
505  * @brief Replaces a page on the stack.
506  * @param page The page can also be given as an array of pages.
507  * In this case all those pages will
508  * be pushed onto the stack. The items in the stack can be components, items or
509  * strings just like for single pages.
510  * the current page and all pagest after it in the stack will be removed.
511  * Additionally an object can be used, which specifies a page and an optional
512  * properties property.
513  * This can be used to push multiple pages while still giving each of
514  * them properties.
515  * When an array is used the transition animation will only be to the last page.
516  * @param properties The properties argument is optional and allows defining a
517  * map of properties to set on the page.
518  * @see push() for details.
519  */
520  function replace(page, properties) {
521  if (!page) {
522  return null;
523  }
524 
525  // Remove all pages on top of the one being replaced.
526  if (currentIndex >= 0) {
527  columnView.pop(columnView.contentChildren[currentIndex]);
528  } else {
529  console.warn("There's no page to replace");
530  }
531 
532  // Figure out if more than one page is being pushed.
533  let pages;
534  let propsArray = [];
535  if (page instanceof Array) {
536  pages = page;
537  page = pages.shift();
538  }
539  if (properties instanceof Array) {
540  propsArray = properties;
541  properties = propsArray.shift();
542  } else {
543  propsArray = [properties];
544  }
545 
546  // Replace topmost page.
547  let pageItem = pagesLogic.initPage(page, properties);
548  if (depth > 0)
549  columnView.replaceItem(depth - 1, pageItem);
550  else {
551  console.log("Calling replace on empty PageRow", pageItem)
552  columnView.addItem(pageItem)
553  }
554  pagePushed(pageItem);
555 
556  // Push any extra defined pages onto the stack.
557  if (pages) {
558  for (let i = 0; i < pages.length; i++) {
559  const tPage = pages[i];
560  const tProps = propsArray[i];
561 
562  pageItem = pagesLogic.initPage(tPage, tProps);
563  columnView.addItem(pageItem);
564  pagePushed(pageItem);
565  }
566  }
567 
568  currentIndex = depth - 1;
569  return pageItem;
570  }
571 
572  /**
573  * @brief Clears the page stack.
574  *
575  * Destroy (or reparent) all the pages contained.
576  */
577  function clear() {
578  return columnView.clear();
579  }
580 
581  /**
582  * @return the page at idx
583  * @param idx the depth of the page we want
584  */
585  function get(idx) {
586  return columnView.contentChildren[idx];
587  }
588 
589  /**
590  * Go back to the previous index and scroll to the left to show one more column.
591  */
592  function flickBack() {
593  if (depth > 1) {
594  currentIndex = Math.max(0, currentIndex - 1);
595  }
596  }
597 
598  /**
599  * Acts as if you had pressed the "back" button on Android or did Alt-Left on desktop,
600  * "going back" in the layers and page row. Results in a layer being popped if available,
601  * or the currentIndex being set to currentIndex-1 if not available.
602  *
603  * @param event Optional, an event that will be accepted if a page is successfully
604  * "backed" on
605  */
606  function goBack(event = null) {
607  const backEvent = {accepted: false}
608 
609  if (layersStack.depth >= 1) {
610  try { // app code might be screwy, but we still want to continue functioning if it throws an exception
611  layersStack.currentItem.backRequested(backEvent)
612  } catch (error) {}
613 
614  if (!backEvent.accepted) {
615  if (layersStack.depth > 1) {
616  layersStack.pop()
617  if (event) event.accepted = true
618  return
619  }
620  }
621  }
622 
623  if (root.currentIndex >= 1) {
624  try { // app code might be screwy, but we still want to continue functioning if it throws an exception
625  root.currentItem.backRequested(backEvent)
626  } catch (error) {}
627 
628  if (!backEvent.accepted) {
629  if (root.depth > 1) {
630  root.currentIndex = Math.max(0, root.currentIndex - 1)
631  if (event) event.accepted = true
632  }
633  }
634  }
635  }
636 
637  /**
638  * Acts as if you had pressed the "forward" shortcut on desktop,
639  * "going forward" in the page row. Results in the active page
640  * becoming the next page in the row from the current active page,
641  * i.e. currentIndex + 1.
642  */
643  function goForward() {
644  root.currentIndex = Math.min(root.depth-1, root.currentIndex + 1)
645  }
646 //END FUNCTIONS
647 
648 //BEGIN signals & signal handlers
649  /**
650  * @brief Emitted when a page has been inserted anywhere.
651  * @param position where the page has been inserted
652  * @param page the new page
653  * @since 2.7
654  */
655  signal pageInserted(int position, Item page)
656 
657  /**
658  * @brief Emitted when a page has been pushed to the bottom.
659  * @param page the new page
660  * @since 2.5
661  */
662  signal pagePushed(Item page)
663 
664  /**
665  * @brief Emitted when a page has been removed from the row.
666  * @param page the page that has been removed: at this point it's still valid,
667  * but may be auto deleted soon.
668  * @since 2.5
669  */
670  signal pageRemoved(Item page)
671 
672  onLeftSidebarChanged: {
673  if (leftSidebar && !leftSidebar.modal) {
674  modalConnection.onModalChanged();
675  }
676  }
677 
678  Keys.onReleased: {
679  if (event.key === Qt.Key_Back) {
680  this.goBack(event)
681  }
682  }
683 
684  onInitialPageChanged: {
685  if (initialPage) {
686  clear();
687  push(initialPage, null)
688  }
689  }
690 /*
691  onActiveFocusChanged: {
692  if (activeFocus) {
693  layersStack.currentItem.forceActiveFocus()
694  if (columnView.activeFocus) {
695  print("SSS"+columnView.currentItem)
696  columnView.currentItem.forceActiveFocus();
697  }
698  }
699  }
700 */
701 //END signals & signal handlers
702 
703  Connections {
704  id: modalConnection
705  target: root.leftSidebar
706  function onModalChanged() {
707  if (leftSidebar.modal) {
708  const sidebar = sidebarControl.contentItem;
709  const background = sidebarControl.background;
710  sidebarControl.contentItem = null;
711  leftSidebar.contentItem = sidebar;
712  sidebarControl.background = null;
713  leftSidebar.background = background;
714 
715  sidebar.visible = true;
716  background.visible = true;
717  } else {
718  const sidebar = leftSidebar.contentItem
719  const background = leftSidebar.background
720  leftSidebar.contentItem=null
721  sidebarControl.contentItem = sidebar
722  leftSidebar.background=null
723  sidebarControl.background = background
724 
725  sidebar.visible = true;
726  background.visible = true;
727  }
728  }
729  }
730 
731  implicitWidth: contentItem.implicitWidth + leftPadding + rightPadding
732  implicitHeight: contentItem.implicitHeight + topPadding + bottomPadding
733 
734  Shortcut {
735  sequences: [ StandardKey.Back ]
736  onActivated: root.goBack()
737  }
738  Shortcut {
739  sequences: [ StandardKey.Forward ]
740  onActivated: root.goForward()
741  }
742 
743  Keys.forwardTo: [currentItem]
744 
745  GlobalToolBar.PageRowGlobalToolBarStyleGroup {
746  id: globalToolBar
747  readonly property int leftReservedSpace: globalToolBarUI.item ? globalToolBarUI.item.leftReservedSpace : 0
748  readonly property int rightReservedSpace: globalToolBarUI.item ? globalToolBarUI.item.rightReservedSpace : 0
749  readonly property int height: globalToolBarUI.height
750  readonly property Item leftHandleAnchor: globalToolBarUI.item ? globalToolBarUI.item.leftHandleAnchor : null
751  readonly property Item rightHandleAnchor: globalToolBarUI.item ? globalToolBarUI.item.rightHandleAnchor : null
752  }
753 
754  QQC2.StackView {
755  id: layersStack
756  z: 99
757  anchors {
758  fill: parent
759  }
760  // placeholder as initial item
761  initialItem: columnViewLayout
762 
763  function clear() {
764  // don't let it kill the main page row
765  const d = layersStack.depth;
766  for (let i = 1; i < d; ++i) {
767  pop();
768  }
769  }
770 
771  popEnter: Transition {
772  OpacityAnimator {
773  from: 0
774  to: 1
775  duration: Kirigami.Units.longDuration
776  easing.type: Easing.InOutCubic
777  }
778  }
779  popExit: Transition {
780  ParallelAnimation {
781  OpacityAnimator {
782  from: 1
783  to: 0
784  duration: Kirigami.Units.longDuration
785  easing.type: Easing.InOutCubic
786  }
787  YAnimator {
788  from: 0
789  to: height/2
790  duration: Kirigami.Units.longDuration
791  easing.type: Easing.InCubic
792  }
793  }
794  }
795 
796  pushEnter: Transition {
797  ParallelAnimation {
798  // NOTE: It's a PropertyAnimation instead of an Animator because with an animator the item will be visible for an instant before starting to fade
799  PropertyAnimation {
800  property: "opacity"
801  from: 0
802  to: 1
803  duration: Kirigami.Units.longDuration
804  easing.type: Easing.InOutCubic
805  }
806  YAnimator {
807  from: height/2
808  to: 0
809  duration: Kirigami.Units.longDuration
810  easing.type: Easing.OutCubic
811  }
812  }
813  }
814 
815 
816  pushExit: Transition {
817  OpacityAnimator {
818  from: 1
819  to: 0
820  duration: Kirigami.Units.longDuration
821  easing.type: Easing.InOutCubic
822  }
823  }
824 
825  replaceEnter: Transition {
826  ParallelAnimation {
827  OpacityAnimator {
828  from: 0
829  to: 1
830  duration: Kirigami.Units.longDuration
831  easing.type: Easing.InOutCubic
832  }
833  YAnimator {
834  from: height/2
835  to: 0
836  duration: Kirigami.Units.longDuration
837  easing.type: Easing.OutCubic
838  }
839  }
840  }
841 
842  replaceExit: Transition {
843  ParallelAnimation {
844  OpacityAnimator {
845  from: 1
846  to: 0
847  duration: Kirigami.Units.longDuration
848  easing.type: Easing.InCubic
849  }
850  YAnimator {
851  from: 0
852  to: -height/2
853  duration: Kirigami.Units.longDuration
854  easing.type: Easing.InOutCubic
855  }
856  }
857  }
858  }
859 
860  Loader {
861  id: globalToolBarUI
862  anchors {
863  left: parent.left
864  top: parent.top
865  right: parent.right
866  }
867  z: 100
868  property QT.Control pageRow: root
869  active: (!firstVisibleItem || firstVisibleItem.globalToolBarStyle !== Kirigami.ApplicationHeaderStyle.None) &&
870  (globalToolBar.actualStyle !== Kirigami.ApplicationHeaderStyle.None || (firstVisibleItem && firstVisibleItem.globalToolBarStyle === Kirigami.ApplicationHeaderStyle.ToolBar))
871  visible: active
872  height: active ? implicitHeight : 0
873  // If load is asynchronous, it will fail to compute the initial implicitHeight
874  // https://bugs.kde.org/show_bug.cgi?id=442660
875  asynchronous: false
876  source: Qt.resolvedUrl("private/globaltoolbar/PageRowGlobalToolBarUI.qml");
877  }
878 
879  QtObject {
880  id: pagesLogic
881  readonly property var componentCache: new Array()
882 
883  function getPageComponent(page) {
884  let pageComp;
885 
886  if (page.createObject) {
887  // page defined as component
888  pageComp = page;
889  } else if (typeof page === "string") {
890  // page defined as string (a url)
891  pageComp = pagesLogic.componentCache[page];
892  if (!pageComp) {
893  pageComp = pagesLogic.componentCache[page] = Qt.createComponent(page);
894  }
895  } else if (typeof page === "object" && !(page instanceof Item) && page.toString !== undefined) {
896  // page defined as url (QML value type, not a string)
897  pageComp = pagesLogic.componentCache[page.toString()];
898  if (!pageComp) {
899  pageComp = pagesLogic.componentCache[page.toString()] = Qt.createComponent(page.toString());
900  }
901  }
902 
903  return pageComp
904  }
905 
906  function initPage(page, properties) {
907  const pageComp = getPageComponent(page, properties);
908 
909  if (pageComp) {
910  // instantiate page from component
911  // FIXME: parent directly to columnView or root?
912  page = pageComp.createObject(null, properties || {});
913 
914  if (pageComp.status === Component.Error) {
915  throw new Error("Error while loading page: " + pageComp.errorString());
916  }
917  } else {
918  // copy properties to the page
919  for (const prop in properties) {
920  if (properties.hasOwnProperty(prop)) {
921  page[prop] = properties[prop];
922  }
923  }
924  }
925  return page;
926  }
927 
928  function initAndInsertPage(position, page, properties) {
929  page = initPage(page, properties);
930  columnView.insertItem(position, page);
931  return page;
932  }
933  }
934 
935  RowLayout {
936  id: columnViewLayout
937  spacing: 1
938  readonly property alias columnView: columnView
939  QQC2.Control {
940  id: sidebarControl
941  Layout.fillHeight: true
942  visible: contentItem !== null && root.leftDrawer && root.leftDrawer.visible
943  leftPadding: root.leftSidebar ? root.leftSidebar.leftPadding : 0
944  topPadding: root.leftSidebar ? root.leftSidebar.topPadding : 0
945  rightPadding: root.leftSidebar ? root.leftSidebar.rightPadding : 0
946  bottomPadding: root.leftSidebar ? root.leftSidebar.bottomPadding : 0
947  }
948  Kirigami.ColumnView {
949  id: columnView
950  Layout.fillWidth: true
951  Layout.fillHeight: true
952 
953  topPadding: globalToolBarUI.item && globalToolBarUI.item.breadcrumbVisible
954  ? globalToolBarUI.height : 0
955 
956  // Internal hidden api for Page
957  readonly property Item __pageRow: root
958  acceptsMouse: Kirigami.Settings.isMobile
959  columnResizeMode: root.wideMode ? Kirigami.ColumnView.FixedColumns : Kirigami.ColumnView.SingleColumn
960  columnWidth: root.defaultColumnWidth
961 
962  onItemInserted: root.pageInserted(position, item);
963  onItemRemoved: root.pageRemoved(item);
964 
965  onVisibleItemsChanged: {
966  // implementation of `popHiddenPages` option
967  if (root.popHiddenPages) {
968  // manually fetch lastItem here rather than use root.lastItem property, since that binding may not have updated yet
969  let lastItem = columnView.contentChildren[columnView.contentChildren.length - 1];
970  let lastVisibleItem = columnView.lastVisibleItem;
971 
972  // pop every page that isn't visible and at the top of the stack
973  while (lastItem && columnView.lastVisibleItem &&
974  lastItem !== columnView.lastVisibleItem && columnView.containsItem(lastItem)) {
975  root.pop();
976  }
977  }
978  }
979  }
980  }
981 
982  Rectangle {
983  anchors.bottom: parent.bottom
984  height: Kirigami.Units.smallSpacing
985  x: (columnView.width - width) * (columnView.contentX / (columnView.contentWidth - columnView.width))
986  width: columnView.width * (columnView.width/columnView.contentWidth)
987  color: Kirigami.Theme.textColor
988  opacity: 0
989  onXChanged: {
990  opacity = 0.3
991  scrollIndicatorTimer.restart();
992  }
993  Behavior on opacity {
994  OpacityAnimator {
995  duration: Kirigami.Units.longDuration
996  easing.type: Easing.InOutQuad
997  }
998  }
999  Timer {
1000  id: scrollIndicatorTimer
1001  interval: Kirigami.Units.longDuration * 4
1002  onTriggered: parent.opacity = 0;
1003  }
1004  }
1005 }
QTextStream & right(QTextStream &stream)
QTextStream & left(QTextStream &stream)
KGuiItem properties()
bool close()
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
KGuiItem clear()
const QList< QKeySequence > & replace()
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
QTextStream & left(QTextStream &s)
Overlay Drawers are used to expose additional UI elements needed for small secondary tasks for which ...
QTextStream & right(QTextStream &s)
void destroy(bool destroyWindow, bool destroySubWindows)
const QList< QKeySequence > & print()
KJOBWIDGETS_EXPORT QWidget * window(KJob *job)
virtual QVariant get(ScriptableExtension *callerPrincipal, quint64 objId, const QString &propName)
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.