RinUI 0.0.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. RinUI/__init__.py +4 -0
  2. RinUI/__pycache__/__init__.cpython-38.pyc +0 -0
  3. RinUI/assets/fonts/FluentSystemIcons-Index.js +5256 -0
  4. RinUI/assets/fonts/FluentSystemIcons-Resizable.ttf +0 -0
  5. RinUI/assets/img/default_app_icon.png +0 -0
  6. RinUI/components/Base.qml +79 -0
  7. RinUI/components/BasicInput/Button.qml +148 -0
  8. RinUI/components/BasicInput/CheckBox.qml +99 -0
  9. RinUI/components/BasicInput/ComboBox.qml +160 -0
  10. RinUI/components/BasicInput/DropDownButton.qml +21 -0
  11. RinUI/components/BasicInput/Hyperlink.qml +18 -0
  12. RinUI/components/BasicInput/RadioButton.qml +95 -0
  13. RinUI/components/BasicInput/Slider.qml +212 -0
  14. RinUI/components/BasicInput/Switch.qml +102 -0
  15. RinUI/components/BasicInput/ToggleButton.qml +11 -0
  16. RinUI/components/BasicInput/ToolButton.qml +31 -0
  17. RinUI/components/ContextMenu.qml +184 -0
  18. RinUI/components/DateAndTime/PickerView.qml +217 -0
  19. RinUI/components/DateAndTime/TimePicker.qml +115 -0
  20. RinUI/components/DialogsAndFlyouts/Dialog.qml +106 -0
  21. RinUI/components/DialogsAndFlyouts/DialogButtonBox.qml +47 -0
  22. RinUI/components/DialogsAndFlyouts/Flyout.qml +144 -0
  23. RinUI/components/DialogsAndFlyouts/Popup.qml +106 -0
  24. RinUI/components/FocusIndicator.qml +33 -0
  25. RinUI/components/IconWidget.qml +52 -0
  26. RinUI/components/Indicator.qml +90 -0
  27. RinUI/components/Layout/Expander.qml +160 -0
  28. RinUI/components/Layout/SettingExpander.qml +67 -0
  29. RinUI/components/Layout/SettingItem.qml +71 -0
  30. RinUI/components/ListAndCollections/Clip.qml +22 -0
  31. RinUI/components/ListAndCollections/Frame.qml +44 -0
  32. RinUI/components/ListAndCollections/ListView.qml +105 -0
  33. RinUI/components/ListAndCollections/ListViewDelegate.qml +83 -0
  34. RinUI/components/ListAndCollections/SettingCard.qml +73 -0
  35. RinUI/components/ListAndCollections/TableView.qml +82 -0
  36. RinUI/components/ListAndCollections/TableViewDelegate.qml +89 -0
  37. RinUI/components/MenusAndToolbars/Menu.qml +149 -0
  38. RinUI/components/MenusAndToolbars/MenuBar.qml +43 -0
  39. RinUI/components/MenusAndToolbars/MenuItem.qml +119 -0
  40. RinUI/components/MenusAndToolbars/MenuItemGroup.qml +43 -0
  41. RinUI/components/MenusAndToolbars/MenuSeparator.qml +14 -0
  42. RinUI/components/MenusAndToolbars/ToolSeparator.qml +17 -0
  43. RinUI/components/Navigation/ErrorPage.qml +48 -0
  44. RinUI/components/Navigation/NavigationBar.qml +179 -0
  45. RinUI/components/Navigation/NavigationItem.qml +193 -0
  46. RinUI/components/Navigation/NavigationSubItem.qml +103 -0
  47. RinUI/components/Navigation/NavigationView.qml +210 -0
  48. RinUI/components/Navigation/SelectorBar.qml +58 -0
  49. RinUI/components/ScrollBar.qml +163 -0
  50. RinUI/components/ScrollView.qml +13 -0
  51. RinUI/components/Shadow.qml +48 -0
  52. RinUI/components/StatusAndInfo/InfoBadge.qml +78 -0
  53. RinUI/components/StatusAndInfo/InfoBar.qml +246 -0
  54. RinUI/components/StatusAndInfo/ProgressBar.qml +127 -0
  55. RinUI/components/StatusAndInfo/Toast.qml +237 -0
  56. RinUI/components/StatusAndInfo/ToolTip.qml +93 -0
  57. RinUI/components/Text/SpinBox.qml +134 -0
  58. RinUI/components/Text/Text.qml +44 -0
  59. RinUI/components/Text/TextField.qml +94 -0
  60. RinUI/components/Text/TextInput.qml +29 -0
  61. RinUI/components/Utils/Blur.qml +42 -0
  62. RinUI/components/qmldir +76 -0
  63. RinUI/config/rin_ui.json +8 -0
  64. RinUI/core/__init__.py +3 -0
  65. RinUI/core/__pycache__/__init__.cpython-38.pyc +0 -0
  66. RinUI/core/__pycache__/config.cpython-38.pyc +0 -0
  67. RinUI/core/__pycache__/launcher.cpython-38.pyc +0 -0
  68. RinUI/core/__pycache__/theme.cpython-38.pyc +0 -0
  69. RinUI/core/config.py +109 -0
  70. RinUI/core/launcher.py +144 -0
  71. RinUI/core/theme.py +342 -0
  72. RinUI/hooks/__init__.py +3 -0
  73. RinUI/hooks/hook-RinUI.py +3 -0
  74. RinUI/qmldir +92 -0
  75. RinUI/themes/dark.qml +137 -0
  76. RinUI/themes/light.qml +137 -0
  77. RinUI/themes/qmldir +7 -0
  78. RinUI/themes/theme.qml +126 -0
  79. RinUI/themes/utils.qml +28 -0
  80. RinUI/utils/Animation.qml +12 -0
  81. RinUI/utils/FloatLayer.qml +123 -0
  82. RinUI/utils/FontIconLoader.qml +14 -0
  83. RinUI/utils/Position.qml +19 -0
  84. RinUI/utils/Severity.qml +13 -0
  85. RinUI/utils/Typography.qml +17 -0
  86. RinUI/utils/qmldir +5 -0
  87. RinUI/windows/CtrlBtn.qml +119 -0
  88. RinUI/windows/FluentPage.qml +92 -0
  89. RinUI/windows/FluentWindow.qml +32 -0
  90. RinUI/windows/FluentWindowBase.qml +157 -0
  91. RinUI/windows/TitleBar.qml +132 -0
  92. RinUI/windows/qmldir +8 -0
  93. RinUI/windows/window/ApplicationWindow.qml +9 -0
  94. RinUI/windows/window/Window.qml +112 -0
  95. rinui-0.0.9.data/data/LICENSE +21 -0
  96. rinui-0.0.9.data/data/README.md +90 -0
  97. rinui-0.0.9.dist-info/LICENSE +21 -0
  98. rinui-0.0.9.dist-info/METADATA +105 -0
  99. rinui-0.0.9.dist-info/RECORD +102 -0
  100. rinui-0.0.9.dist-info/WHEEL +5 -0
  101. rinui-0.0.9.dist-info/entry_points.txt +2 -0
  102. rinui-0.0.9.dist-info/top_level.txt +1 -0
@@ -0,0 +1,119 @@
1
+ import QtQuick 2.15
2
+ import QtQuick.Controls.Basic 2.15
3
+ import QtQuick.Layouts 2.15
4
+ import "../../themes"
5
+ import "../../components"
6
+
7
+
8
+ MenuItem {
9
+ id: root
10
+ implicitWidth: {
11
+ const leftMargin = 16;
12
+ const arrowWidth = arrow.visible ? arrow.width + 16 : root.checked ? indicator.width + 16 : 0;
13
+ const rightMargin = 16;
14
+ return leftMargin + contentItem.implicitWidth + arrowWidth + rightMargin;
15
+ }
16
+ implicitHeight: Math.max(implicitContentHeight + topPadding + bottomPadding,
17
+ 34)
18
+
19
+ property MenuItemGroup group // 组
20
+
21
+ checkable: group
22
+ checked: group ? group.checkedButton === root : false
23
+
24
+ onGroupChanged: {
25
+ if (group)
26
+ group.register(root)
27
+ }
28
+
29
+ Component.onDestruction: {
30
+ if (group)
31
+ group.unregister(root)
32
+ }
33
+
34
+ onTriggered: {
35
+ if (group)
36
+ group.updateCheck(root)
37
+ }
38
+
39
+ property var parentMenu: undefined
40
+
41
+ // accessibility
42
+ FocusIndicator {
43
+ control: parent
44
+ anchors.margins: 5
45
+ anchors.topMargin: 0
46
+ anchors.bottomMargin: 0
47
+ }
48
+
49
+ arrow: IconWidget {
50
+ anchors.verticalCenter: parent.verticalCenter
51
+ anchors.right: parent.right
52
+ anchors.margins: 16
53
+ color: Theme.currentTheme.colors.textSecondaryColor
54
+ visible: root.subMenu
55
+ icon: "ic_fluent_chevron_right_20_regular"
56
+ size: 12
57
+ }
58
+
59
+ indicator: IconWidget {
60
+ id: indicator
61
+ anchors.verticalCenter: parent.verticalCenter
62
+ anchors.left: parent.left
63
+ anchors.margins: 18
64
+ icon: group ? group.exclusive ? "ic_fluent_circle_20_filled" : "ic_fluent_checkmark_20_filled"
65
+ : "ic_fluent_checkmark_20_filled"
66
+ width: 16
67
+ size: group ? group.exclusive ? 7 : 16 : 16
68
+ visible: root.checked
69
+ }
70
+
71
+ // 内容 / Content //
72
+ contentItem: RowLayout {
73
+ id: row
74
+ spacing: 16
75
+ anchors.verticalCenter: parent.verticalCenter
76
+ anchors.left: parent.left
77
+ anchors.right: parent.right
78
+ anchors.leftMargin: (iconWidget.size ? 16 : 0) + (checkable ? indicator.width + 16 : 0)
79
+ anchors.margins: 16
80
+
81
+ IconWidget {
82
+ id: iconWidget
83
+ size: icon || source ? menuText.font.pixelSize * 1.25 : 0 // 图标大小 / Icon Size
84
+ icon: root.icon.name
85
+ source: root.icon.source
86
+ }
87
+ Text {
88
+ id: menuText
89
+ Layout.fillWidth: true
90
+ Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
91
+ typography: Typography.Body
92
+ text: root.text
93
+ wrapMode: Text.NoWrap
94
+ }
95
+ Text {
96
+ id: shortcutText
97
+ Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
98
+ typography: Typography.Caption
99
+ text: root.action ? root.action.shortcut : ""
100
+ color: Theme.currentTheme.colors.textSecondaryColor
101
+ visible: text
102
+ }
103
+ }
104
+
105
+ // 背景 / Background //
106
+ background: Rectangle {
107
+ anchors.fill: parent
108
+ anchors.margins: 5
109
+ anchors.topMargin: 0
110
+ anchors.bottomMargin: 0
111
+ radius: Theme.currentTheme.appearance.buttonRadius
112
+ color: enabled ? pressed ? Theme.currentTheme.colors.subtleTertiaryColor
113
+ : hovered
114
+ ? Theme.currentTheme.colors.subtleSecondaryColor
115
+ : "transparent" : "transparent"
116
+
117
+ Behavior on color { ColorAnimation { duration: Utils.animationSpeed; easing.type: Easing.OutQuart } }
118
+ }
119
+ }
@@ -0,0 +1,43 @@
1
+ import QtQuick 2.15
2
+
3
+ QtObject {
4
+ id: root
5
+
6
+ // 用法同ButtonGroup
7
+ property bool exclusive: true
8
+ property var items: []
9
+ property Item checkedItem: null
10
+ readonly property int checkState: {
11
+ let count = 0;
12
+ for (let b of items)
13
+ if (b.checked)
14
+ count++;
15
+ return count === 0 ? Qt.Unchecked :
16
+ count === items.length ? Qt.Checked :
17
+ Qt.PartiallyChecked;
18
+ }
19
+
20
+
21
+ // 方法 / functions //
22
+ function register(button) {
23
+ if (items.indexOf(button) === -1)
24
+ items.push(button)
25
+ }
26
+
27
+ function unregister(button) {
28
+ const idx = items.indexOf(button)
29
+ if (idx !== -1)
30
+ items.splice(idx, 1)
31
+ }
32
+
33
+ function updateCheck(button) {
34
+ if (exclusive) {
35
+ for (let b of items) {
36
+ b.checked = (b === button)
37
+ }
38
+ checkedItem = button
39
+ } else {
40
+ checkedItem = button.checked ? button : null
41
+ }
42
+ }
43
+ }
@@ -0,0 +1,14 @@
1
+ import QtQuick 2.15
2
+ import QtQuick.Controls.Basic 2.15
3
+ import "../../themes"
4
+ import "../../components"
5
+
6
+
7
+ MenuSeparator {
8
+ id: root
9
+
10
+ contentItem: Rectangle {
11
+ implicitHeight: 1
12
+ color: Theme.currentTheme.colors.dividerBorderColor
13
+ }
14
+ }
@@ -0,0 +1,17 @@
1
+ import QtQuick 2.15
2
+ import QtQuick.Controls.Basic 2.15
3
+ import "../../themes"
4
+ import "../../components"
5
+
6
+
7
+ ToolSeparator {
8
+ padding: vertical ? 2 : 2
9
+ topPadding: vertical ? 2 : 2
10
+ bottomPadding: vertical ? 2 : 2
11
+
12
+ contentItem: Rectangle {
13
+ implicitWidth: parent.vertical ? 1 : 16
14
+ implicitHeight: parent.vertical ? 16 : 1
15
+ color: Theme.currentTheme.colors.dividerBorderColor
16
+ }
17
+ }
@@ -0,0 +1,48 @@
1
+ import QtQuick 2.15
2
+ import QtQuick.Controls 2.15
3
+ import QtQuick.Layouts 2.15
4
+ import RinUI
5
+
6
+ FluentPage {
7
+ id: errorPage
8
+
9
+ property string errorMessage: "ERROR_MSG"
10
+ property string page: "page"
11
+
12
+ spacing: 4
13
+
14
+ Item {
15
+ height: 64
16
+ }
17
+
18
+ Text {
19
+ Layout.alignment: Qt.AlignHCenter
20
+ typography: Typography.Subtitle
21
+ text: qsTr("Sorry, something went wrong!")
22
+ }
23
+
24
+ Text {
25
+ Layout.preferredWidth: parent.width * 0.8
26
+ Layout.alignment: Qt.AlignHCenter
27
+ horizontalAlignment: Text.AlignHCenter
28
+ wrapMode: Text.WordWrap
29
+ typography: Typography.Body
30
+ text: page + qsTr(" load failed! \n\n Because of ") + errorMessage + qsTr("\nPlease try again later.")
31
+ }
32
+
33
+ Item {
34
+ height: 8
35
+ }
36
+
37
+
38
+ Button {
39
+ Layout.alignment: Qt.AlignHCenter
40
+ id: retryButton
41
+ text: qsTr("Retry")
42
+ // buttonType: "primary"
43
+ onClicked: {
44
+ // 重新加载 Home 页
45
+ safePush(errorPage.page, true)
46
+ }
47
+ }
48
+ }
@@ -0,0 +1,179 @@
1
+ import QtQuick 2.15
2
+ import QtQuick.Controls 2.15
3
+ import "../../components"
4
+ import "../../themes"
5
+
6
+
7
+ Item {
8
+ id: navigationBar
9
+ // implicitWidth: collapsed ? 40 : expandWidth
10
+ height: parent.height
11
+
12
+ property bool collapsed: false
13
+ property var navigationItems: [
14
+ // {title: "Title", page: "path/to/page.qml", icon: undefined}
15
+ ]
16
+
17
+ // property int currentSubIndex: -1
18
+ property bool titleBarEnabled: true
19
+ property int expandWidth: 280
20
+ property int minimumExpandWidth: 900
21
+
22
+ property alias windowTitle: titleLabel.text
23
+ property alias windowIcon: iconLabel.source
24
+ property int windowWidth: minimumExpandWidth
25
+ property var stackView: parent.stackView
26
+
27
+ property string currentPage: "" // 当前页面的URL
28
+ property var lastPages: [] // 历史页面的URL栈
29
+
30
+ function isNotOverMinimumWidth() { // 判断窗口是否小于最小宽度
31
+ return windowWidth < minimumExpandWidth;
32
+ }
33
+
34
+ // 展开收缩动画 //
35
+ width: collapsed ? 40 : expandWidth
36
+ implicitWidth: isNotOverMinimumWidth() ? 40 : collapsed ? 40 : expandWidth
37
+
38
+ Behavior on width {
39
+ NumberAnimation {
40
+ duration: Utils.animationSpeed
41
+ easing.type: Easing.OutQuint
42
+ }
43
+ }
44
+ Behavior on implicitWidth {
45
+ NumberAnimation {
46
+ duration: Utils.animationSpeed
47
+ easing.type: Easing.OutQuint
48
+ }
49
+ }
50
+
51
+ Rectangle {
52
+ id: background
53
+ anchors.fill: parent
54
+ anchors.margins: -5
55
+ anchors.topMargin: -title.height
56
+ radius: Theme.currentTheme.appearance.windowRadius
57
+ color: Theme.currentTheme.colors.backgroundAcrylicColor
58
+ border.color: Theme.currentTheme.colors.flyoutBorderColor
59
+ z: -1
60
+ visible: isNotOverMinimumWidth() && !collapsed ? 1 : 0
61
+
62
+ Behavior on visible {
63
+ NumberAnimation {
64
+ duration: collapsed ? Utils.animationSpeed / 2 : 50
65
+ }
66
+ }
67
+
68
+ layer.enabled: true
69
+ layer.effect: Shadow {
70
+ style: "flyout"
71
+ source: background
72
+ }
73
+ }
74
+
75
+ Row {
76
+ id: title
77
+ anchors.left: parent.left
78
+ anchors.bottom: parent.top
79
+ height: titleBarHeight
80
+ spacing: 16
81
+ visible: navigationBar.titleBarEnabled
82
+
83
+ // 返回按钮
84
+ ToolButton {
85
+ flat: true
86
+ anchors.verticalCenter: parent.verticalCenter
87
+ icon.name: "ic_fluent_arrow_left_20_regular"
88
+ onClicked: navigationView.safePop()
89
+ width: 40
90
+ height: 40
91
+ size: 16
92
+ enabled: lastPages.length > 1
93
+
94
+ ToolTip {
95
+ parent: parent
96
+ delay: 500
97
+ visible: parent.hovered
98
+ text: qsTr("Back")
99
+ }
100
+ }
101
+
102
+ //图标
103
+ IconWidget {
104
+ id: iconLabel
105
+ size: 16
106
+ anchors.verticalCenter: parent.verticalCenter
107
+ }
108
+
109
+ //标题
110
+ Text {
111
+ id: titleLabel
112
+ anchors.verticalCenter: parent.verticalCenter
113
+
114
+ typography: Typography.Caption
115
+ // text: title
116
+ }
117
+ }
118
+
119
+ // 收起切换按钮
120
+ ToolButton {
121
+ id: collapseButton
122
+ flat: true
123
+ width: 40
124
+ height: 38
125
+ // icon.name: collapsed ? "ic_fluent_chevron_right_20_regular" : "ic_fluent_chevron_left_20_regular"
126
+ icon.name: "ic_fluent_navigation_20_regular"
127
+ size: 19
128
+
129
+ onClicked: {
130
+ collapsed = !collapsed
131
+ }
132
+
133
+ ToolTip {
134
+ parent: parent
135
+ delay: 500
136
+ visible: parent.hovered && !parent.pressed
137
+ text: collapsed ? qsTr("Open Navigation") : qsTr("Close Navigation")
138
+ }
139
+ }
140
+
141
+ Flickable {
142
+ id: flickable
143
+ anchors.fill: parent
144
+ anchors.topMargin: 40
145
+ contentWidth: parent.width
146
+ contentHeight: navigationColumn.implicitHeight
147
+ clip: true
148
+
149
+ Column {
150
+ id: navigationColumn
151
+ width: flickable.width
152
+ spacing: 2
153
+
154
+ Repeater {
155
+ model: navigationBar.navigationItems
156
+ delegate: NavigationItem {
157
+ id: item
158
+ itemData: modelData
159
+ currentPage: navigationBar.stackView
160
+
161
+ // 子菜单重置
162
+ Connections {
163
+ target: navigationBar
164
+ function onCollapsedChanged() {
165
+ if (!navigationBar.collapsed) {
166
+ return
167
+ }
168
+ item.collapsed = navigationBar.collapsed
169
+ }
170
+ }
171
+ }
172
+ }
173
+ }
174
+
175
+ ScrollBar.vertical: ScrollBar {
176
+ policy: ScrollBar.AsNeeded
177
+ }
178
+ }
179
+ }
@@ -0,0 +1,193 @@
1
+ import QtQuick 2.15
2
+ import QtQuick.Controls 2.15
3
+ import "../../themes"
4
+ import "../../components"
5
+
6
+ Item {
7
+ id: navigationItems
8
+ property var itemData
9
+ readonly property bool subItem: itemData.subItems && itemData.subItems.length > 0
10
+ property var currentPage
11
+ property bool highlighted: String(navigationBar.currentPage) === String(itemData.page) || (collapsed && subItemHighlighted)
12
+
13
+ property bool subItemHighlighted: {
14
+ if (!subItem) return false;
15
+ for (let i = 0; i < itemData.subItems.length; i++) {
16
+ if (String(itemData.subItems[i].page) === String(navigationBar.currentPage)) {
17
+ return true;
18
+ }
19
+ }
20
+ return false;
21
+ }
22
+ property bool collapsed: true // 是否折叠
23
+
24
+ height: 40 + (!collapsed && subItem ? subItemsColumn.height : 0)
25
+ width: parent ? parent.width : 200
26
+
27
+ Button {
28
+ id: itemBtn
29
+ // anchors.fill: parent
30
+ width: parent.width
31
+ height: 37
32
+ anchors.topMargin: 2
33
+ anchors.bottomMargin: 2
34
+ clip: true
35
+ flat: true
36
+ accessibliityIndicator: false
37
+ background.opacity: navigationItems.highlighted ? 1 : hovered ? 1 : 0
38
+
39
+ // accessibility
40
+ FocusIndicator {
41
+ control: parent
42
+ anchors.margins: 2
43
+ }
44
+
45
+ Row {
46
+ id: left
47
+ spacing: 16
48
+ anchors.left: parent.left
49
+ anchors.verticalCenter: parent.verticalCenter
50
+ anchors.leftMargin: 11
51
+ anchors.topMargin: 6
52
+ anchors.bottomMargin: 8
53
+
54
+ IconWidget {
55
+ id: icon
56
+ anchors.verticalCenter: parent.verticalCenter
57
+ size: itemData.icon || itemData.source ? 19 : 0
58
+ icon: itemData.icon || ""
59
+ source: itemData.source || ""
60
+ }
61
+
62
+ Text {
63
+ id: text
64
+ anchors.verticalCenter: parent.verticalCenter
65
+ typography: Typography.Body
66
+ text: itemData.title
67
+ clip: true
68
+ opacity: navigationBar.collapsed ? 0 : 1
69
+ wrapMode: Text.NoWrap
70
+ horizontalAlignment: Text.AlignLeft
71
+
72
+ Behavior on x {
73
+ NumberAnimation {
74
+ duration: Utils.appearanceSpeed
75
+ easing.type: Easing.InOutQuint
76
+ }
77
+ }
78
+
79
+ Behavior on opacity {
80
+ NumberAnimation {
81
+ duration: Utils.appearanceSpeed
82
+ }
83
+ }
84
+ }
85
+ }
86
+
87
+ // 提示
88
+ ToolTip {
89
+ visible: navigationBar.collapsed && itemBtn.hovered
90
+ delay: 500
91
+ text: itemData.title
92
+ }
93
+
94
+ Indicator {
95
+ id: indicator
96
+ y: (itemBtn.height + 3) / 2 - indicator.height / 2 - 2
97
+ currentItemHeight: itemBtn.height + 3
98
+ visible: highlighted ? 1 : 0
99
+ width: 3
100
+ }
101
+
102
+ // 展开按钮
103
+ ToolButton {
104
+ id: expandBtn
105
+ focusPolicy: Qt.NoFocus
106
+ anchors.right: parent.right
107
+ anchors.verticalCenter: parent.verticalCenter
108
+ width: parent.height
109
+ height: parent.height
110
+ hoverable: false
111
+ size: 14
112
+ icon.name: "ic_fluent_chevron_down_20_filled"
113
+ accessibliityIndicator: false
114
+
115
+ FocusIndicator {
116
+ control: parent
117
+ anchors.margins: 2
118
+ }
119
+
120
+ transform: Rotation {
121
+ angle: collapsed ? 0 : 180 ; origin.x: 37/2; origin.y: 37/2
122
+ Behavior on angle { NumberAnimation { duration: Utils.animationSpeed; easing.type: Easing.OutQuint } }
123
+ }
124
+
125
+ visible: subItem && !navigationBar.collapsed
126
+ opacity: 0.7
127
+
128
+ onClicked: { collapsed = !collapsed }
129
+ }
130
+
131
+ onClicked: {
132
+ if (subItem) {
133
+ if (!navigationBar.collapsed) {
134
+ collapsed = !collapsed
135
+ } else {
136
+ subMenu.open()
137
+ }
138
+ }
139
+ if (itemData.page && currentPage && !navigationItems.highlighted) {
140
+ // 记录上一次的索引
141
+ navigationView.safePush(itemData.page, true)
142
+ }
143
+ }
144
+ }
145
+
146
+ // 动画 / Animation //
147
+ Behavior on height { NumberAnimation { duration: Utils.animationSpeed; easing.type: Easing.OutQuint } }
148
+
149
+ // 折叠菜单
150
+ Menu {
151
+ id: subMenu
152
+ position: Position.Right
153
+ Repeater {
154
+ id: subMenuRepeater
155
+ model: itemData.subItems
156
+ delegate: MenuItem {
157
+ text: modelData.title
158
+ onClicked: {
159
+ if (modelData.page && navigationItems.currentPage) {
160
+ navigationView.safePush(modelData.page)
161
+ }
162
+ }
163
+ }
164
+ }
165
+ }
166
+
167
+ // 递归处理子项
168
+ Column {
169
+ id: subItemsColumn
170
+ opacity: !collapsed && subItem
171
+ spacing: 2
172
+ anchors.top: itemBtn.bottom
173
+ width: parent.width
174
+ anchors.topMargin: 1
175
+ anchors.leftMargin: 16
176
+ Behavior on opacity { NumberAnimation { duration: Utils.animationSpeed; easing.type: Easing.OutQuint } }
177
+
178
+ Repeater {
179
+ id: subItemsRepeater
180
+ model: itemData.subItems
181
+ delegate: NavigationSubItem {
182
+ id: subItems
183
+ itemData: modelData
184
+ currentPage: navigationItems.currentPage
185
+ }
186
+ }
187
+
188
+ Item {
189
+ width: parent.width
190
+ height: 1
191
+ }
192
+ }
193
+ }