Kirigami2

templates/ApplicationHeader.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.Controls 2.0 as QQC2
9 import QtQuick.Layouts 1.2
10 import "private"
11 import org.kde.kirigami 2.4
12 
13 
26  id: header
27 
38  property int headerStyle: ApplicationHeaderStyle.Auto
39 
44  property bool backButtonEnabled: (!titleList.isTabBar && (!Settings.isMobile || Qt.platform.os == "ios"))
45 
46  onBackButtonEnabledChanged: {
47  if (backButtonEnabled && !titleList.backButton) {
48  var component = Qt.createComponent(Qt.resolvedUrl("private/BackButton.qml"));
49  titleList.backButton = component.createObject(navButtons);
50  component = Qt.createComponent(Qt.resolvedUrl("private/ForwardButton.qml"));
51  titleList.forwardButton = component.createObject(navButtons, {"headerFlickable": titleList});
52  } else if (titleList.backButton) {
53  titleList.backButton.destroy();
54  titleList.forwardButton.destroy();
55  }
56  }
57  property Component pageDelegate: Component {
58  Row {
59  height: parent.height
60 
61  spacing: Units.smallSpacing
62 
64 
65  Icon {
66  //in tabbar mode this is just a spacer
67  visible: !titleList.wideMode && ((typeof(modelData) != "undefined" && modelData > 0) || titleList.internalHeaderStyle == ApplicationHeaderStyle.TabBar)
68  anchors.verticalCenter: parent.verticalCenter
69  height: Units.iconSizes.small
70  width: height
71  selected: header.background && header.background.color && header.background.color === Theme.highlightColor
72  source: titleList.isTabBar ? "" : (LayoutMirroring.enabled ? "go-next-symbolic-rtl" : "go-next-symbolic")
73  }
74 
75  Heading {
76  id: title
77  width: Math.min(parent.width, Math.min(titleList.width, implicitWidth)) + Units.smallSpacing
78  anchors.verticalCenter: parent.verticalCenter
79  opacity: current ? 1 : 0.4
80  //Scaling animate NativeRendering is too slow
81  renderType: Text.QtRendering
82  color: header.background && header.background.color && header.background.color === Theme.highlightColor ? Theme.highlightedTextColor : Theme.textColor
83  elide: Text.ElideRight
84  text: page ? page.title : ""
85  font.pointSize: -1
86  font.pixelSize: Math.max(1, titleList.height * 0.7)
87  verticalAlignment: Text.AlignVCenter
88  wrapMode: Text.NoWrap
89  Rectangle {
90  anchors {
91  bottom: parent.bottom
92  left: parent.left
93  right: parent.right
94  }
95  height: Units.smallSpacing
96  color: title.color
97  opacity: 0.6
98  visible: titleList.isTabBar && current
99  }
100  }
101  }
102  }
103 
104  Component.onCompleted: print("Warning: ApplicationHeader is deprecated, remove and use the automatic internal toolbar instead.")
105 
106  Rectangle {
107  anchors {
108  right: titleList.left
109  verticalCenter: parent.verticalCenter
110  }
111  visible: titleList.x > 0 && !titleList.atXBeginning
112  height: parent.height * 0.7
113  color: Theme.highlightedTextColor
114  width: Math.ceil(Units.smallSpacing / 6)
115  opacity: 0.4
116  }
117 
118  QQC2.StackView {
119  id: stack
120  anchors {
121  fill: parent
122  leftMargin: navButtons.width
123  rightMargin: __appWindow.contextDrawer && __appWindow.contextDrawer.handleVisible && __appWindow.contextDrawer.handle && __appWindow.contextDrawer.handle.y == 0 ? __appWindow.contextDrawer.handle.width : 0
124  }
125  initialItem: titleList
126 
127  popEnter: Transition {
128  YAnimator {
129  from: -height
130  to: 0
131  duration: Units.longDuration
132  easing.type: Easing.OutCubic
133  }
134  }
135  popExit: Transition {
136  YAnimator {
137  from: 0
138  to: height
139  duration: Units.longDuration
140  easing.type: Easing.OutCubic
141  }
142  }
143 
144  pushEnter: Transition {
145  YAnimator {
146  from: height
147  to: 0
148  duration: Units.longDuration
149  easing.type: Easing.OutCubic
150  }
151  }
152 
153  pushExit: Transition {
154  YAnimator {
155  from: 0
156  to: -height
157  duration: Units.longDuration
158  easing.type: Easing.OutCubic
159  }
160  }
161 
162  replaceEnter: Transition {
163  YAnimator {
164  from: height
165  to: 0
166  duration: Units.longDuration
167  easing.type: Easing.OutCubic
168  }
169  }
170 
171  replaceExit: Transition {
172  YAnimator {
173  from: 0
174  to: -height
175  duration: Units.longDuration
176  easing.type: Easing.OutCubic
177  }
178  }
179  }
180  Separator {
181  id: separator
182  height: parent.height * 0.6
183  visible: navButtons.width > 0
184  anchors {
185  verticalCenter: parent.verticalCenter
186  left: navButtons.right
187  }
188  }
189  Separator {
190  height: parent.height * 0.6
191  visible: stack.anchors.rightMargin > 0
192  anchors {
193  verticalCenter: parent.verticalCenter
194  right: parent.right
195  rightMargin: stack.anchors.rightMargin
196  }
197  }
198  Repeater {
199  model: pageRow.layers.depth -1
200  delegate: Loader {
201  sourceComponent: header.pageDelegate
202  readonly property Page page: pageRow.layers.get(modelData+1)
203  readonly property bool current: true;
204  Component.onCompleted: stack.push(this)
205  Component.onDestruction: stack.pop()
206  }
207  }
208 
209  Row {
210  id: navButtons
211  anchors {
212  left: parent.left
213  top: parent.top
214  bottom: parent.bottom
215  topMargin: Units.smallSpacing
216  bottomMargin: Units.smallSpacing
217  }
218  Item {
219  height: parent.height
220  width: (applicationWindow().header && applicationWindow().header.toString().indexOf("ToolBarApplicationHeader") === 0) && __appWindow.globalDrawer && __appWindow.globalDrawer.handleVisible && __appWindow.globalDrawer.handle && __appWindow.globalDrawer.handle.y === 0 ? __appWindow.globalDrawer.handle.width : 0
221  }
222  }
223 
224  Flickable {
225  id: titleList
226  readonly property bool wideMode: pageRow.hasOwnProperty("wideMode") ? pageRow.wideMode : __appWindow.wideScreen
227  property int internalHeaderStyle: header.headerStyle == ApplicationHeaderStyle.Auto ? (titleList.wideMode ? ApplicationHeaderStyle.Titles : ApplicationHeaderStyle.Breadcrumb) : header.headerStyle
228  //if scrolling the titlebar should scroll also the pages and vice versa
229  property bool scrollingLocked: (header.headerStyle == ApplicationHeaderStyle.Titles || titleList.wideMode)
230  //uses this to have less strings comparisons
231  property bool scrollMutex
232  property bool isTabBar: header.headerStyle == ApplicationHeaderStyle.TabBar
233 
234  property Item backButton
235  property Item forwardButton
236  clip: true
237 
238 
239  boundsBehavior: Flickable.StopAtBounds
240  readonly property alias model: mainRepeater.model
241  contentWidth: contentItem.width
242  contentHeight: height
243 
244  readonly property int currentIndex: pageRow && pageRow.currentIndex !== undefined ? pageRow.currentIndex : 0
245  readonly property int count: mainRepeater.count
246 
247  function gotoIndex(idx) {
248  //don't actually scroll in widescreen mode
249  if (titleList.wideMode || contentItem.children.length < 2) {
250  return;
251  }
252  listScrollAnim.running = false
253  var pos = titleList.contentX;
254  var destPos;
255  titleList.contentX = Math.max((contentItem.children[idx].x + contentItem.children[idx].width) - titleList.width, Math.min(titleList.contentX, contentItem.children[idx].x));
256  destPos = titleList.contentX;
257  listScrollAnim.from = pos;
258  listScrollAnim.to = destPos;
259  listScrollAnim.running = true;
260  }
261 
262  NumberAnimation {
263  id: listScrollAnim
264  target: titleList
265  property: "contentX"
266  duration: Units.longDuration
267  easing.type: Easing.InOutQuad
268  }
269  Timer {
270  id: contentXSyncTimer
271  interval: 0
272  onTriggered: {
273  titleList.contentX = pageRow.contentItem.contentX - pageRow.contentItem.originX + titleList.originX;
274  }
275  }
276  onCountChanged: contentXSyncTimer.restart();
277  onCurrentIndexChanged: gotoIndex(currentIndex);
278  onModelChanged: gotoIndex(currentIndex);
279  onContentWidthChanged: gotoIndex(currentIndex);
280 
281  onContentXChanged: {
282  if (movingHorizontally && !titleList.scrollMutex && titleList.scrollingLocked && !pageRow.contentItem.moving) {
283  titleList.scrollMutex = true;
284  pageRow.contentItem.contentX = titleList.contentX - titleList.originX + pageRow.contentItem.originX;
285  titleList.scrollMutex = false;
286  }
287  }
288  onHeightChanged: {
289  titleList.returnToBounds()
290  }
291  onMovementEnded: {
292  if (titleList.scrollingLocked) {
293  //this will trigger snap as well
294  pageRow.contentItem.flick(0,0);
295  }
296  }
297  onFlickEnded: movementEnded();
298 
299  NumberAnimation {
300  id: scrollTopAnimation
301  target: pageRow.currentItem && pageRow.currentItem.flickable ? pageRow.currentItem.flickable : null
302  property: "contentY"
303  to: 0
304  duration: Units.longDuration
305  easing.type: Easing.InOutQuad
306  }
307 
308  Row {
309  id: contentItem
310  spacing: 0
311  Repeater {
312  id: mainRepeater
313  model: pageRow.depth
314  delegate: MouseArea {
315  id: delegate
316  readonly property int currentIndex: index
317  readonly property var currentModelData: modelData
318  clip: true
319 
320  width: {
321  //more columns shown?
322  if (titleList.scrollingLocked && delegateLoader.page) {
323  return delegateLoader.page.width - (index == 0 ? navButtons.width : 0) - (index == pageRow.depth-1 ? stack.anchors.rightMargin : 0);
324  } else {
325  return Math.min(titleList.width, delegateLoader.implicitWidth + Units.smallSpacing);
326  }
327  }
328 
329  height: titleList.height
330  onClicked: {
331  if (pageRow.currentIndex === modelData) {
332  //scroll up if current otherwise make current
333  if (!pageRow.currentItem.flickable) {
334  return;
335  }
336  if (pageRow.currentItem.flickable.contentY > -__appWindow.header.height) {
337  scrollTopAnimation.to = -pageRow.currentItem.flickable.topMargin;
338  scrollTopAnimation.running = true;
339  }
340 
341  } else {
342  pageRow.currentIndex = modelData;
343  }
344  }
345 
346  Loader {
347  id: delegateLoader
348  height: parent.height
349  x: titleList.wideMode || headerStyle == ApplicationHeaderStyle.Titles ? (Math.min(delegate.width - implicitWidth, Math.max(0, titleList.contentX - delegate.x))) : 0
350  width: parent.width - x
351 
352  Connections {
353  target: delegateLoader.page
354  Component.onDestruction: delegateLoader.sourceComponent = null
355  }
356 
357  sourceComponent: header.pageDelegate
358 
359  readonly property Page page: pageRow.get(modelData)
360  //NOTE: why not use ListViewCurrentIndex? because listview itself resets
361  //currentIndex in some situations (since here we are using an int as a model,
362  //even more often) so the property binding gets broken
363  readonly property bool current: pageRow.currentIndex === index
364  readonly property int index: parent.currentIndex
365  readonly property var modelData: parent.currentModelData
366  }
367  }
368  }
369  }
370  Connections {
371  target: titleList.scrollingLocked ? pageRow.contentItem : null
372  onContentXChanged: {
373  if (!titleList.dragging && !titleList.movingHorizontally && !titleList.scrollMutex) {
374  titleList.contentX = pageRow.contentItem.contentX - pageRow.contentItem.originX + titleList.originX;
375  }
376  }
377  }
378  }
379 }
A heading label used for subsections of texts.
Definition: Heading.qml:36
QTextStream & right(QTextStream &stream)
An item that can be used as a title for the application.
QTextStream & left(QTextStream &stream)
Class for rendering an icon in UI.
Definition: icon.h:26
QTextStream & left(QTextStream &s)
bool isMobile
True if we are running on a small mobile device such as a mobile phone This is used when we want to d...
Definition: settings.h:32
This class contains global kirigami settings about the current device setup It is exposed to QML as t...
Definition: settings.h:16
const QList< QKeySequence > & print()
int smallSpacing
units.smallSpacing is the amount of spacing that should be used around smaller UI elements...
QtObject iconSizes
units.iconSizes provides access to platform-dependent icon sizing
A set of named colors for the application.
QTextStream & right(QTextStream &s)
int longDuration
units.longDuration should be used for longer, screen-covering animations, for opening and closing of ...
Page is a container for all the app pages: everything pushed to the ApplicationWindow stackView shoul...
Definition: Page.qml:19
A set of values to define semantically sizes and durations.
A visual separator.
Definition: Separator.qml:18
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Sat Jul 4 2020 22:38:08 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.