FluentUI/src/controls/FluNavigationView.qml

568 lines
17 KiB
QML
Raw Normal View History

2023-03-30 21:52:55 +08:00
import QtQuick
import QtQuick.Window
import QtQuick.Controls
import QtQuick.Layouts
import FluentUI
2023-03-10 18:08:32 +08:00
Item {
2023-03-28 17:53:46 +08:00
property alias logo : image_logo.source
2023-03-30 17:16:57 +08:00
property string title: ""
2023-03-10 18:08:32 +08:00
property FluObject items
property FluObject footerItems
property int displayMode: width<=700 ? FluNavigationView.Minimal : FluNavigationView.Open
2023-03-28 17:53:46 +08:00
property bool displaMinimalMenu : false
2023-03-30 17:16:57 +08:00
property Component autoSuggestBox
2023-03-17 14:05:27 +08:00
2023-03-27 18:24:35 +08:00
id:root
2023-03-10 18:08:32 +08:00
onDisplayModeChanged: {
if(displayMode === FluNavigationView.Minimal){
anim_navi.enabled = false
2023-03-28 17:53:46 +08:00
displaMinimalMenu = false
2023-03-10 18:08:32 +08:00
timer_anim_enable.restart()
}
}
Timer{
id:timer_anim_enable
interval: 150
onTriggered: {
anim_navi.enabled = true
}
}
enum DisplayMode {
Minimal,
Open,
Auto
}
property var window : {
if(Window.window == null)
return null
return Window.window
}
Component{
id:com_panel_item_separatorr
FluDivider{
width: nav_list.width
2023-03-31 22:05:25 +08:00
height: {
if(model.parent){
return model.parent.isExpand ? 1 : 0
}
return 1
}
Behavior on height {
NumberAnimation{
duration: 150
}
}
2023-03-10 18:08:32 +08:00
}
}
Component{
id:com_panel_item_header
Item{
2023-03-31 22:05:25 +08:00
height: {
if(model.parent){
return model.parent.isExpand ? 30 : 0
}
return 30
}
Behavior on height {
NumberAnimation{
duration: 150
}
}
2023-03-10 18:08:32 +08:00
width: nav_list.width
FluText{
text:model.title
fontStyle: FluText.BodyStrong
anchors{
bottom: parent.bottom
left:parent.left
leftMargin: 10
}
}
}
}
Component{
2023-03-31 22:05:25 +08:00
id:com_panel_item_expander
2023-03-10 18:08:32 +08:00
Item{
height: 38
width: nav_list.width
2023-03-31 22:05:25 +08:00
Rectangle{
radius: 4
anchors{
top: parent.top
bottom: parent.bottom
left: parent.left
right: parent.right
topMargin: 2
bottomMargin: 2
leftMargin: 6
rightMargin: 6
}
Rectangle{
width: 3
height: 18
radius: 1.5
color: FluTheme.primaryColor.dark
visible: {
for(var i=0;i<model.children.length;i++){
var item = model.children[i]
if(item.idx === nav_list.currentIndex && !model.isExpand){
return true
}
}
return false
}
anchors{
verticalCenter: parent.verticalCenter
}
}
FluIcon{
rotation: model.isExpand?0:180
iconSource:FluentIcons.ChevronUp
iconSize: 15
anchors{
verticalCenter: parent.verticalCenter
right: parent.right
rightMargin: 12
}
Behavior on rotation {
NumberAnimation{
duration: 150
}
}
}
MouseArea{
id:item_mouse
hoverEnabled: true
anchors.fill: parent
onClicked: {
model.isExpand = !model.isExpand
}
}
color: {
if(FluTheme.dark){
if(item_mouse.containsMouse){
return Qt.rgba(1,1,1,0.03)
}
if((nav_list.currentIndex === position)&&type===0){
return Qt.rgba(1,1,1,0.06)
}
return Qt.rgba(0,0,0,0)
}else{
if(item_mouse.containsMouse){
return Qt.rgba(0,0,0,0.03)
}
if(nav_list.currentIndex === position&&type===0){
return Qt.rgba(0,0,0,0.06)
}
return Qt.rgba(0,0,0,0)
}
}
FluIcon{
id:item_icon
iconSource: {
if(model.icon){
return model.icon
}
return 0
}
width: 30
height: 30
iconSize: 15
anchors{
verticalCenter: parent.verticalCenter
left:parent.left
leftMargin: 3
}
}
FluText{
id:item_title
text:model.title
anchors{
verticalCenter: parent.verticalCenter
left:item_icon.right
}
}
}
}
}
Component{
id:com_panel_item
Item{
Behavior on height {
NumberAnimation{
duration: 150
}
}
clip: true
height: {
if(model.parent){
return model.parent.isExpand ? 38 : 0
}
return 38
}
width: nav_list.width
2023-03-10 18:08:32 +08:00
Rectangle{
radius: 4
anchors{
top: parent.top
bottom: parent.bottom
left: parent.left
right: parent.right
topMargin: 2
bottomMargin: 2
leftMargin: 6
rightMargin: 6
}
MouseArea{
id:item_mouse
hoverEnabled: true
anchors.fill: parent
onClicked: {
if(type===0){
model.repTap()
if(nav_list.currentIndex !== position){
nav_list.currentIndex = position
model.tap()
}
}else{
model.tap()
}
2023-03-28 17:53:46 +08:00
displaMinimalMenu = false
2023-03-10 18:08:32 +08:00
}
}
color: {
2023-03-28 21:37:10 +08:00
if(FluTheme.dark){
2023-03-10 18:08:32 +08:00
if(item_mouse.containsMouse){
2023-03-11 21:03:16 +08:00
return Qt.rgba(1,1,1,0.03)
}
if((nav_list.currentIndex === position)&&type===0){
return Qt.rgba(1,1,1,0.06)
2023-03-10 18:08:32 +08:00
}
return Qt.rgba(0,0,0,0)
}else{
if(item_mouse.containsMouse){
return Qt.rgba(0,0,0,0.03)
}
if(nav_list.currentIndex === position&&type===0){
return Qt.rgba(0,0,0,0.06)
}
return Qt.rgba(0,0,0,0)
}
}
2023-03-29 15:43:23 +08:00
FluIcon{
id:item_icon
iconSource: {
if(model.icon){
return model.icon
}
return 0
}
width: 30
height: 30
iconSize: 15
anchors{
verticalCenter: parent.verticalCenter
left:parent.left
leftMargin: 3
}
}
2023-03-10 18:08:32 +08:00
FluText{
2023-03-29 15:43:23 +08:00
id:item_title
2023-03-10 18:08:32 +08:00
text:model.title
anchors{
verticalCenter: parent.verticalCenter
2023-03-29 15:43:23 +08:00
left:item_icon.right
2023-03-10 18:08:32 +08:00
}
}
}
}
}
Item {
id:nav_app_bar
width: parent.width
2023-03-28 17:53:46 +08:00
height: 50
2023-03-14 18:23:12 +08:00
z:999
2023-03-10 18:08:32 +08:00
RowLayout{
height:parent.height
spacing: 0
FluIconButton{
2023-03-12 21:49:11 +08:00
iconSource: FluentIcons.ChromeBack
2023-03-10 18:08:32 +08:00
Layout.leftMargin: 5
2023-03-28 17:53:46 +08:00
Layout.preferredWidth: 40
Layout.preferredHeight: 40
2023-03-10 18:08:32 +08:00
Layout.alignment: Qt.AlignVCenter
disabled: nav_swipe.depth === 1
2023-03-11 14:43:07 +08:00
iconSize: 13
2023-03-10 18:08:32 +08:00
onClicked: {
nav_swipe.pop()
nav_list.stackIndex.pop()
var index = nav_list.stackIndex[nav_list.stackIndex.length-1]
nav_list.enableStack = false
nav_list.currentIndex = index
nav_list.enableStack = true
}
}
FluIconButton{
2023-03-28 17:53:46 +08:00
id:btn_nav
2023-03-12 21:49:11 +08:00
iconSource: FluentIcons.GlobalNavButton
2023-03-11 00:29:06 +08:00
iconSize: 15
2023-03-28 17:53:46 +08:00
Layout.preferredWidth: 40
Layout.preferredHeight: 40
2023-03-10 18:08:32 +08:00
visible: displayMode === FluNavigationView.Minimal
Layout.alignment: Qt.AlignVCenter
onClicked: {
2023-03-28 17:53:46 +08:00
displaMinimalMenu = !displaMinimalMenu
2023-03-10 18:08:32 +08:00
}
}
2023-03-28 17:53:46 +08:00
Image{
id:image_logo
Layout.preferredHeight: 20
Layout.preferredWidth: 20
Layout.leftMargin: {
if(btn_nav.visible){
return 12
}
return 5
}
Layout.alignment: Qt.AlignVCenter
}
FluText{
Layout.alignment: Qt.AlignVCenter
2023-03-30 17:16:57 +08:00
text:root.title
2023-03-28 17:53:46 +08:00
Layout.leftMargin: 12
fontStyle: FluText.Body
2023-03-10 18:08:32 +08:00
}
}
}
Item{
anchors{
left: displayMode === FluNavigationView.Minimal ? parent.left : layout_list.right
top: nav_app_bar.bottom
right: parent.right
bottom: parent.bottom
}
StackView{
id:nav_swipe
anchors.fill: parent
clip: true
popEnter : Transition{}
2023-03-11 20:33:29 +08:00
popExit : Transition {
NumberAnimation { properties: "y"; from: 0; to: nav_swipe.height; duration: 200 }
}
pushEnter: Transition {
NumberAnimation { properties: "y"; from: nav_swipe.height; to: 0; duration: 200 }
}
2023-03-10 18:08:32 +08:00
pushExit : Transition{}
replaceEnter : Transition{}
replaceExit : Transition{}
}
}
MouseArea{
anchors.fill: parent
2023-03-28 17:53:46 +08:00
enabled: (displayMode === FluNavigationView.Minimal && displaMinimalMenu)
2023-03-10 18:08:32 +08:00
onClicked: {
2023-03-28 17:53:46 +08:00
displaMinimalMenu = false
2023-03-10 18:08:32 +08:00
}
}
Rectangle{
id:layout_list
width: 300
2023-03-30 17:16:57 +08:00
border.color: FluTheme.dark ? Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(226/255,230/255,234/255,1)
border.width: displayMode === FluNavigationView.Minimal ? 1 : 0
color: {
if(displayMode === FluNavigationView.Minimal){
return FluTheme.dark ? Qt.rgba(61/255,61/255,61/255,1) : Qt.rgba(243/255,243/255,243/255,1)
}
if(window && window.active){
return FluTheme.dark ? Qt.rgba(32/255,32/255,32/255,1) : Qt.rgba(238/255,244/255,249/255,1)
}
return FluTheme.dark ? Qt.rgba(32/255,32/255,32/255,1) : Qt.rgba(243/255,243/255,243/255,1)
}
2023-03-10 18:08:32 +08:00
anchors{
2023-03-14 18:23:12 +08:00
top: parent.top
2023-03-10 18:08:32 +08:00
bottom: parent.bottom
}
x: {
if(displayMode !== FluNavigationView.Minimal)
return 0
2023-03-28 17:53:46 +08:00
return (displayMode === FluNavigationView.Minimal && displaMinimalMenu) ? 0 : -width
2023-03-10 18:08:32 +08:00
}
Behavior on x{
id:anim_navi
NumberAnimation{
duration: 150
}
}
2023-03-28 17:53:46 +08:00
2023-03-10 18:08:32 +08:00
Behavior on color{
ColorAnimation {
duration: 300
}
}
2023-03-11 20:33:29 +08:00
Item{
id:layout_header
width: layout_list.width
2023-03-14 18:23:12 +08:00
y:nav_app_bar.height
2023-03-30 17:16:57 +08:00
height: {
if(loader_auto_suggest_box.item){
return loader_auto_suggest_box.item.height
2023-03-11 20:33:29 +08:00
}
2023-03-30 17:16:57 +08:00
return 0
}
Loader{
id:loader_auto_suggest_box
anchors.horizontalCenter: parent.horizontalCenter
sourceComponent: autoSuggestBox
2023-03-11 20:33:29 +08:00
}
}
2023-03-30 17:16:57 +08:00
2023-03-10 18:08:32 +08:00
ListView{
id:nav_list
property bool enableStack: true
property var stackIndex: []
clip: true
anchors{
2023-03-11 20:33:29 +08:00
top: layout_header.bottom
2023-03-29 15:43:23 +08:00
topMargin: 6
2023-03-10 18:08:32 +08:00
left: parent.left
right: parent.right
bottom: layout_footer.top
}
2023-03-30 17:16:57 +08:00
highlightMoveDuration: 150
2023-03-10 18:08:32 +08:00
currentIndex: -1
onCurrentIndexChanged: {
if(enableStack){
stackIndex.push(currentIndex)
}
}
2023-04-04 15:09:34 +08:00
highlight: Item{
clip: true
Rectangle{
height: 18
radius: 1.5
color: FluTheme.primaryColor.dark
width: 3
anchors{
verticalCenter: parent.verticalCenter
left: parent.left
leftMargin: 6
}
}
}
2023-03-14 18:23:12 +08:00
ScrollBar.vertical: FluScrollBar {}
2023-03-31 22:05:25 +08:00
model:handleItems()
2023-03-10 18:08:32 +08:00
delegate: Loader{
property var model: modelData
property var position: index
property int type: 0
sourceComponent: {
if(modelData instanceof FluPaneItem){
return com_panel_item
}
if(modelData instanceof FluPaneItemHeader){
return com_panel_item_header
}
if(modelData instanceof FluPaneItemSeparator){
return com_panel_item_separatorr
}
2023-03-31 22:05:25 +08:00
if(modelData instanceof FluPaneItemExpander){
return com_panel_item_expander
}
2023-03-10 18:08:32 +08:00
}
}
}
ListView{
id:layout_footer
width: layout_list.width
height: childrenRect.height
anchors.bottom: parent.bottom
model: {
if(footerItems){
return footerItems.children
}
}
currentIndex: -1
delegate: Loader{
property var model: modelData
property var position: index
property int type: 1
sourceComponent: {
if(modelData instanceof FluPaneItem){
return com_panel_item
}
if(modelData instanceof FluPaneItemHeader){
return com_panel_item_header
}
if(modelData instanceof FluPaneItemSeparator){
return com_panel_item_separatorr
}
}
}
}
}
2023-03-31 22:05:25 +08:00
function handleItems(){
var idx = 0
var data = []
if(items){
for(var i=0;i<items.children.length;i++){
var item = items.children[i]
item.idx = idx
data.push(item)
idx++
if(item instanceof FluPaneItemExpander){
for(var j=0;j<item.children.length;j++){
var itemChild = item.children[j]
itemChild.parent = item
itemChild.idx = idx
data.push(itemChild)
idx++
}
}
}
}
return data
}
function getItems(){
return nav_list.model
}
2023-03-10 18:08:32 +08:00
function push(url){
nav_swipe.push(url)
}
function setCurrentIndex(index){
nav_list.currentIndex = index
}
2023-03-30 17:16:57 +08:00
function getCurrentIndex(){
return nav_list.currentIndex
2023-03-29 18:10:34 +08:00
}
2023-03-10 18:08:32 +08:00
}