Kirigami2

SwipeNavigator.qml
1 /*
2  * SPDX-FileCopyrightText: 2020 Carson Black <[email protected]>
3  *
4  * SPDX-License-Identifier: LGPL-2.0-or-later
5  */
6 
7 import QtQuick 2.12
8 import QtQuick.Layouts 1.12
9 import QtQuick.Controls 2.12 as QQC2
10 import org.kde.kirigami 2.13 as Kirigami
11 import "." as SN
12 
13 
14 //TODO KF6: remove all of this?
15 /**
16  * @brief SwipeNavigator is a control providing for lateral navigation.
17  * @include swipenavigator/main.qml
18  * @inherit QtQuick.Item
19  */
20 Item {
21  id: swipeNavigatorRoot
22 
23 //BEGIN properties
24  /**
25  * @brief This property holds the pages to swipe between.
26  */
27  default property list<Kirigami.Page> pages
28 
29  /**
30  * @brief This property holds the StackView that is holding the core item,
31  * which allows users of SwipeNavigator to push pages on top of it.
32  *
33  * @property QtQuick.Controls.StackView stackView
34  */
35  property alias layers: stackView
36 
37  /**
38  * @brief This property sets whether SwipeNavigator should be presented in large format,
39  * which is suitable for televisions.
40  *
41  * default: ``false``
42  */
43  property bool big: false
44 
45  /**
46  * @brief This property holds the item that will be displayed before the tabs.
47  * @property Item header
48  */
49  property Component header: Item {visible: false}
50 
51  /**
52  * @brief This property holds the item that will be displayed after the tabs.
53  * @property Item footer
54  */
55  property Component footer: Item {visible: false}
56 
57  /**
58  * @brief This property holds the initial tab index of the SwipeNavigator.
59  *
60  * default: ``0``
61  */
62  property int initialIndex: 0
63 
64  /**
65  * @brief This property holds the currently displayed page in the SwipeNavigator.
66  * @property int currentIndex
67  */
68  property alias currentIndex: columnView.currentIndex
69 //END properties
70 
71  /**
72  * @brief Pushes a page as a new dialog on desktop and as a layer on mobile.
73  * @param page The page can be defined as a component, item or string. If an item is
74  * used then the page will get re-parented. If a string is used then it
75  * is interpreted as a url that is used to load a page component.
76  * @param properties The properties given when initializing the page.
77  * @param windowProperties The properties given to the initialized window on desktop.
78  * @return The newly created page
79  */
80  function pushDialogLayer(page, properties = {}, windowProperties = {}) {
81  let item;
82  if (Kirigami.Settings.isMobile) {
83  item = layers.push(page, properties);
84  } else {
85  const windowComponent = Qt.createComponent(Qt.resolvedUrl("./ApplicationWindow.qml"));
86  if (!windowProperties.modality) {
87  windowProperties.modality = Qt.WindowModal;
88  }
89  if (!windowProperties.height) {
90  windowProperties.height = Kirigami.Units.gridUnit * 30;
91  }
92  if (!windowProperties.width) {
93  windowProperties.width = Kirigami.Units.gridUnit * 50;
94  }
95  if (!windowProperties.minimumWidth) {
96  windowProperties.minimumWidth = Kirigami.Units.gridUnit * 20;
97  }
98  if (!windowProperties.minimumHeight) {
99  windowProperties.minimumHeight = Kirigami.Units.gridUnit * 15;
100  }
101  if (!windowProperties.flags) {
102  windowProperties.flags = Qt.Dialog | Qt.WindowCloseButtonHint;
103  }
104  const window = windowComponent.createObject(swipeNavigatorRoot, windowProperties);
105  windowComponent.destroy();
106  item = window.pageStack.push(page, properties);
107  }
108  item.Keys.escapePressed.connect(event => item.closeDialog());
109  return item;
110  }
111 
112  implicitWidth: stackView.implicitWidth
113  implicitHeight: stackView.implicitHeight
114 
115  QtObject {
116  id: _gridManager
117  readonly property bool tall: (_header.width + __main.implicitWidth + Math.abs(__main.offset) + _footer.width) > swipeNavigatorRoot.width
118  readonly property int rowOne: Kirigami.Settings.isMobile ? 1 : 0
119  readonly property int rowTwo: Kirigami.Settings.isMobile ? 0 : 1
120  readonly property int rowDirection: Kirigami.Settings.isMobile ? 1 : -1
121  property Item item: Item {
122  states: [
123  State {
124  name: "small"
125  when: !_gridManager.tall
126  },
127  State {
128  name: "tall"
129  when: _gridManager.tall
130  }
131  ]
132  transitions: [
133  Transition {
134  to: "tall"
135  ScriptAction {
136  script: {
137  // Let's take these out of the layout first...
138  _dummyOne.visible = false
139  _dummyTwo.visible = false
140  // Now we move the header and footer up
141  _header.Layout.row += _gridManager.rowDirection
142  _footer.Layout.row += _gridManager.rowDirection
143  // Now that the header and footer are out of the way,
144  // let's expand the tabs
145  __main.Layout.column--
146  __main.Layout.columnSpan = 3
147  }
148  }
149  },
150  Transition {
151  to: "small"
152  ScriptAction {
153  script: {
154  // Let's move the tabs back to where they belong
155  __main.Layout.columnSpan = 1
156  __main.Layout.column++
157  // Move the header and footer down into the empty space
158  _header.Layout.row -= _gridManager.rowDirection
159  _footer.Layout.row -= _gridManager.rowDirection
160 
161  // Now we can bring these guys back in
162  _dummyOne.visible = false
163  _dummyTwo.visible = false
164  }
165  }
166  }
167  ]
168  }
169  }
170 
171 
172  QQC2.StackView {
173  id: stackView
174 
175  anchors.fill: parent
176 
177  function clear() {
178  // don't let it kill the main page row
179  const d = stackView.depth;
180  for (let i = 1; i < d; ++i) {
181  pop();
182  }
183  }
184 
185  initialItem: SN.TabViewLayout {
186  bar: QQC2.ToolBar {
187  id: topToolBar
188 
189  padding: 0
190  bottomPadding: 1
191 
192  GridLayout {
193  id: _grid
194 
195  rowSpacing: 0
196  columnSpacing: 0
197  anchors.fill: parent
198  rows: 2
199  columns: 3
200 
201  // Row one
202  Item {
203  id: _spacer
204  Layout.row: 0
205  Layout.column: 1
206  Layout.fillWidth: true
207  }
208  Item {
209  id: _dummyOne
210  Layout.row: 0
211  Layout.column: 0
212  }
213  Item {
214  id: _dummyTwo
215  Layout.row: 0
216  Layout.column: 2
217  }
218 
219  // Row two
220  Loader {
221  id: _header
222  sourceComponent: swipeNavigatorRoot.header
223  Layout.row: 1
224  Layout.column: 0
225  }
226  SN.PrivateSwipeTabBar {
227  id: __main
228  readonly property int offset: _header.width - _footer.width
229  readonly property int effectiveOffset: _gridManager.tall ? 0 : offset
230  Layout.rightMargin: effectiveOffset > 0 ? effectiveOffset : 0
231  Layout.leftMargin: effectiveOffset < 0 ? -effectiveOffset : 0
232  Layout.fillHeight: true
233  Layout.fillWidth: true//Kirigami.Settings.isMobile && swipeNavigatorRoot.height > swipeNavigatorRoot.width
234  Layout.alignment: Qt.AlignHCenter
235  Layout.row: 1
236  Layout.column: 1
237 
238  }
239  Loader {
240  id: _footer
241  sourceComponent: swipeNavigatorRoot.footer
242  Layout.row: 1
243  Layout.column: 2
244  }
245  }
246 
247  Accessible.role: Accessible.PageTabList
248  }
249  contentItem: Kirigami.ColumnView {
250  id: columnView
251  Layout.fillWidth: true
252  Layout.fillHeight: true
253  Layout.row: Kirigami.Settings.isMobile ? 0 : 1
254 
255  columnResizeMode: Kirigami.ColumnView.SingleColumn
256 
257  contentChildren: swipeNavigatorRoot.pages
258 
259  Component.onCompleted: {
260  columnView.currentIndex = swipeNavigatorRoot.initialIndex
261  }
262  // We only want the current page to be focusable, so we
263  // disable the inactive pages.
264  onCurrentIndexChanged: {
265  Array.from(swipeNavigatorRoot.pages).forEach(item => item.enabled = false)
266  swipeNavigatorRoot.pages[currentIndex].enabled = true
267  }
268  }
269  }
270  popEnter: Transition {
271  OpacityAnimator {
272  from: 0
273  to: 1
274  duration: Kirigami.Units.longDuration
275  easing.type: Easing.InOutCubic
276  }
277  }
278  popExit: Transition {
279  ParallelAnimation {
280  OpacityAnimator {
281  from: 1
282  to: 0
283  duration: Kirigami.Units.longDuration
284  easing.type: Easing.InOutCubic
285  }
286  YAnimator {
287  from: 0
288  to: height/2
289  duration: Kirigami.Units.longDuration
290  easing.type: Easing.InCubic
291  }
292  }
293  }
294 
295  pushEnter: Transition {
296  ParallelAnimation {
297  // 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
298  PropertyAnimation {
299  property: "opacity"
300  from: 0
301  to: 1
302  duration: Kirigami.Units.longDuration
303  easing.type: Easing.InOutCubic
304  }
305  YAnimator {
306  from: height/2
307  to: 0
308  duration: Kirigami.Units.longDuration
309  easing.type: Easing.OutCubic
310  }
311  }
312  }
313 
314 
315  pushExit: Transition {
316  OpacityAnimator {
317  from: 1
318  to: 0
319  duration: Kirigami.Units.longDuration
320  easing.type: Easing.InOutCubic
321  }
322  }
323 
324  replaceEnter: Transition {
325  ParallelAnimation {
326  OpacityAnimator {
327  from: 0
328  to: 1
329  duration: Kirigami.Units.longDuration
330  easing.type: Easing.InOutCubic
331  }
332  YAnimator {
333  from: height/2
334  to: 0
335  duration: Kirigami.Units.longDuration
336  easing.type: Easing.OutCubic
337  }
338  }
339  }
340 
341  replaceExit: Transition {
342  ParallelAnimation {
343  OpacityAnimator {
344  from: 1
345  to: 0
346  duration: Kirigami.Units.longDuration
347  easing.type: Easing.InCubic
348  }
349  YAnimator {
350  from: 0
351  to: -height/2
352  duration: Kirigami.Units.longDuration
353  easing.type: Easing.InOutCubic
354  }
355  }
356  }
357  }
358 }
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
KGuiItem clear()
QString name(StandardShortcut id)
void destroy(bool destroyWindow, bool destroySubWindows)
KJOBWIDGETS_EXPORT QWidget * window(KJob *job)
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Tue Feb 7 2023 04:14:24 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.