Compare commits

..

3 Commits
1.2.2 ... 1.2.3

Author SHA1 Message Date
3a46fd11c6 update 2023-04-10 22:07:30 +08:00
70f048f629 update 2023-04-10 22:05:20 +08:00
1f2d0d9b9f update 2023-04-10 18:17:22 +08:00
16 changed files with 469 additions and 157 deletions

50
example/T_Settings.qml Normal file
View File

@ -0,0 +1,50 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Window
import QtQuick.Controls
import FluentUI
import "qrc:///global/"
import "./component"
FluScrollablePage{
title:"Settings"
leftPadding:10
rightPadding:10
bottomPadding:20
spacing: 0
FluArea{
Layout.fillWidth: true
Layout.topMargin: 20
height: 168
paddings: 10
ColumnLayout{
spacing: 10
anchors{
verticalCenter: parent.verticalCenter
left: parent.left
}
FluText{
text:"NavigationView Display Mode"
fontStyle: FluText.BodyStrong
Layout.bottomMargin: 4
}
Repeater{
id:repeater
model: [{title:"Open",mode:FluNavigationView.Open},{title:"Compact",mode:FluNavigationView.Compact},{title:"Minimal",mode:FluNavigationView.Minimal},{title:"Auto",mode:FluNavigationView.Auto}]
delegate: FluRadioButton{
selected : MainEvent.displayMode===modelData.mode
text:modelData.title
onClicked:{
MainEvent.displayMode = modelData.mode
}
}
}
}
}
}

View File

@ -5,17 +5,22 @@ import FluentUI
FluObject{
id:footer_items
property var navigationView
FluPaneItemSeparator{}
FluPaneItem{
title:"意见反馈"
onTap:{
Qt.openUrlExternally("https://github.com/zhuzichu520/FluentUI/issues/new")
}
}
FluPaneItem{
title:"关于"
onTap:{
title:"About"
icon:FluentIcons.Contact
tapFunc:function(){
FluApp.navigate("/about")
}
}
FluPaneItem{
title:"Settings"
icon:FluentIcons.Settings
onTap:{
navigationView.push("qrc:/T_Settings.qml")
}
}
}

View File

@ -308,7 +308,6 @@ FluObject{
if(navigationView.getCurrentIndex() === i){
return
}
item.tap()
navigationView.setCurrentIndex(i)
if(item.parent){
item.parent.isExpand = true

View File

@ -0,0 +1,10 @@
pragma Singleton
import QtQuick
import FluentUI
QtObject {
property int displayMode : FluNavigationView.Auto
}

View File

@ -1,2 +1,3 @@
singleton ItemsOriginal 1.0 ItemsOriginal.qml
singleton ItemsFooter 1.0 ItemsFooter.qml
singleton MainEvent 1.0 MainEvent.qml

View File

@ -36,7 +36,7 @@ FluWindow {
fontStyle: FluText.Title
}
FluText{
text:"v1.2.2"
text:"v1.2.3"
fontStyle: FluText.Body
Layout.alignment: Qt.AlignBottom
}

View File

@ -15,9 +15,10 @@ FluWindow {
FluAppBar{
id:appbar
z:10
z:9
showDark: true
width:parent.width
darkText: "Dark Mode"
}
FluNavigationView{
@ -25,15 +26,16 @@ FluWindow {
anchors.fill: parent
items: ItemsOriginal
footerItems:ItemsFooter
z:11
displayMode:MainEvent.displayMode
logo: "qrc:/res/image/favicon.ico"
z: 11
title:"FluentUI"
autoSuggestBox:FluAutoSuggestBox{
width: 280
anchors.centerIn: parent
iconSource: FluentIcons.Zoom
iconSource: FluentIcons.Search
items: ItemsOriginal.getSearchData()
placeholderText: "查找"
placeholderText: "Search"
onItemClicked:
(data)=>{
ItemsOriginal.startPageByItem(data)
@ -41,8 +43,8 @@ FluWindow {
}
Component.onCompleted: {
ItemsOriginal.navigationView = nav_view
ItemsFooter.navigationView = nav_view
nav_view.setCurrentIndex(0)
nav_view.push("qrc:/T_Home.qml")
}
}

View File

@ -158,5 +158,7 @@
<file>component/CodeExpander.qml</file>
<file>T_TableView.qml</file>
<file>T_StatusView.qml</file>
<file>T_Settings.qml</file>
<file>global/MainEvent.qml</file>
</qresource>
</RCC>

View File

@ -5,6 +5,7 @@ import FluentUI
FluTextBox{
property var items:[]
property string emptyText: "没有找到结果"
property int autoSuggestBoxReplacement: FluentIcons.Search
signal itemClicked(var data)
signal handleClicked
QtObject{

View File

@ -6,48 +6,112 @@ import FluentUI
Item {
property alias logo : image_logo.source
property string title: ""
property FluObject items
property FluObject footerItems
property int displayMode: width<=700 ? FluNavigationView.Minimal : FluNavigationView.Open
property bool displaMinimalMenu : false
property Component autoSuggestBox
id:root
onDisplayModeChanged: {
if(displayMode === FluNavigationView.Minimal){
anim_navi.enabled = false
displaMinimalMenu = false
timer_anim_enable.restart()
}
}
Timer{
id:timer_anim_enable
interval: 150
onTriggered: {
anim_navi.enabled = true
}
}
enum DisplayMode {
Minimal,
Open,
Compact,
Minimal,
Auto
}
property url logo
property string title: ""
property FluObject items
property FluObject footerItems
property int displayMode: FluNavigationView.Auto
property Component autoSuggestBox
property var window : {
if(Window.window == null)
return null
return Window.window
}
id:control
QtObject{
id:d
property bool enableStack: true
property int displayMode: {
if(control.displayMode !==FluNavigationView.Auto){
return control.displayMode
}
if(control.width<=700){
return FluNavigationView.Minimal
}else if(control.width<=900){
return FluNavigationView.Compact
}else{
return FluNavigationView.Open
}
}
property var stackItems: []
property bool enableNavigationPanel: false
property bool isCompact: d.displayMode === FluNavigationView.Compact
property bool isMinimal: d.displayMode === FluNavigationView.Minimal
property bool isCompactAndPanel: d.displayMode === FluNavigationView.Compact && d.enableNavigationPanel
property bool isCompactAndNotPanel:d.displayMode === FluNavigationView.Compact && !d.enableNavigationPanel
property bool isMinimalAndPanel: d.displayMode === FluNavigationView.Minimal && d.enableNavigationPanel
onIsCompactAndNotPanelChanged: {
collapseAll()
}
onDisplayModeChanged: {
if(d.displayMode === FluNavigationView.Compact){
collapseAll()
}
if(d.displayMode === FluNavigationView.Minimal){
anim_layout_list_x.enabled = false
d.enableNavigationPanel = false
timer_anim_x_enable.restart()
}
}
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++
}
}
}
if(footerItems){
var comEmpty = Qt.createComponent("FluPaneItemEmpty.qml");
for(var k=0;k<footerItems.children.length;k++){
var itemFooter = footerItems.children[k]
if (comEmpty.status === Component.Ready) {
var objEmpty = comEmpty.createObject(items,{idx:idx});
itemFooter.idx = idx;
data.push(objEmpty)
idx++
}
}
}
}
return data
}
}
Component{
id:com_panel_item_empty
Item{
visible: false
}
}
Component{
id:com_panel_item_separatorr
FluDivider{
width: nav_list.width
width: layout_list.width
height: {
if(model.parent){
return model.parent.isExpand ? 1 : 0
@ -76,7 +140,7 @@ Item {
duration: 150
}
}
width: nav_list.width
width: layout_list.width
FluText{
text:model.title
fontStyle: FluText.BodyStrong
@ -93,8 +157,7 @@ Item {
id:com_panel_item_expander
Item{
height: 38
width: nav_list.width
width: layout_list.width
Rectangle{
radius: 4
anchors{
@ -134,6 +197,18 @@ Item {
right: parent.right
rightMargin: 12
}
opacity: {
if(d.isCompactAndNotPanel){
return false
}
return true
}
visible:opacity
Behavior on opacity {
NumberAnimation{
duration: 220
}
}
Behavior on rotation {
NumberAnimation{
duration: 150
@ -145,12 +220,16 @@ Item {
hoverEnabled: true
anchors.fill: parent
onClicked: {
if(d.isCompactAndNotPanel){
control_popup.showPopup(Qt.point(50,mapToItem(control,0,0).y),model.children)
return
}
model.isExpand = !model.isExpand
}
}
color: {
if(FluTheme.dark){
if((nav_list.currentIndex === position)&&type===0){
if((nav_list.currentIndex === idx)&&type===0){
return Qt.rgba(1,1,1,0.06)
}
if(item_mouse.containsMouse){
@ -158,7 +237,7 @@ Item {
}
return Qt.rgba(0,0,0,0)
}else{
if(nav_list.currentIndex === position&&type===0){
if(nav_list.currentIndex === idx&&type===0){
return Qt.rgba(0,0,0,0.06)
}
if(item_mouse.containsMouse){
@ -167,7 +246,6 @@ Item {
return Qt.rgba(0,0,0,0)
}
}
FluIcon{
id:item_icon
iconSource: {
@ -185,10 +263,21 @@ Item {
leftMargin: 3
}
}
FluText{
id:item_title
text:model.title
opacity: {
if(d.isCompactAndNotPanel){
return false
}
return true
}
visible:opacity
Behavior on opacity {
NumberAnimation{
duration: 220
}
}
anchors{
verticalCenter: parent.verticalCenter
left:item_icon.right
@ -219,8 +308,7 @@ Item {
}
return 38
}
width: nav_list.width
width: layout_list.width
Rectangle{
radius: 4
anchors{
@ -238,33 +326,55 @@ Item {
hoverEnabled: true
anchors.fill: parent
onClicked: {
if(type===0){
if(type === 0){
if(model.tapFunc){
model.tapFunc()
return
}
if(nav_list.currentIndex !== position){
nav_list.currentIndex = position
model.tap()
}else{
nav_list.currentIndex = idx
layout_footer.currentIndex = -1
if(d.isMinimal || d.isCompact){
d.enableNavigationPanel = false
}
}
}else{
model.tap()
if(model.tapFunc){
model.tapFunc()
}else{
model.tap()
d.stackItems.push(model)
nav_list.currentIndex = nav_list.count-layout_footer.count+idx
layout_footer.currentIndex = idx
if(d.isMinimal || d.isCompact){
d.enableNavigationPanel = false
}
}
}
displaMinimalMenu = false
}
}
color: {
if(FluTheme.dark){
if((nav_list.currentIndex === position)&&type===0){
return Qt.rgba(1,1,1,0.06)
if(type===0){
if(nav_list.currentIndex === idx){
return Qt.rgba(1,1,1,0.06)
}
}else{
if(nav_list.currentIndex === (nav_list.count-layout_footer.count+idx)){
return Qt.rgba(1,1,1,0.06)
}
}
if(item_mouse.containsMouse){
return Qt.rgba(1,1,1,0.03)
}
return Qt.rgba(0,0,0,0)
}else{
if(nav_list.currentIndex === position&&type===0){
return Qt.rgba(0,0,0,0.06)
if(type===0){
if(nav_list.currentIndex === idx){
return Qt.rgba(0,0,0,0.06)
}
}else{
if(nav_list.currentIndex === (nav_list.count-layout_footer.count+idx)){
return Qt.rgba(0,0,0,0.06)
}
}
if(item_mouse.containsMouse){
return Qt.rgba(0,0,0,0.03)
@ -272,7 +382,6 @@ Item {
return Qt.rgba(0,0,0,0)
}
}
FluIcon{
id:item_icon
iconSource: {
@ -290,10 +399,21 @@ Item {
leftMargin: 3
}
}
FluText{
id:item_title
text:model.title
opacity: {
if(d.isCompactAndNotPanel){
return false
}
return true
}
visible:opacity
Behavior on opacity {
NumberAnimation{
duration: 220
}
}
color:{
if(item_mouse.pressed){
return FluTheme.dark ? FluColors.Grey80 : FluColors.Grey120
@ -309,6 +429,7 @@ Item {
}
}
Item {
id:nav_app_bar
width: parent.width
@ -327,11 +448,17 @@ Item {
iconSize: 13
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
d.stackItems.pop()
var item = d.stackItems[d.stackItems.length-1]
d.enableStack = false
if(item.idx<(nav_list.count - layout_footer.count)){
layout_footer.currentIndex = -1
}else{
console.debug(item.idx-(nav_list.count-layout_footer.count))
layout_footer.currentIndex = item.idx-(nav_list.count-layout_footer.count)
}
nav_list.currentIndex = item.idx
d.enableStack = true
}
}
FluIconButton{
@ -340,17 +467,17 @@ Item {
iconSize: 15
Layout.preferredWidth: 40
Layout.preferredHeight: 40
visible: displayMode === FluNavigationView.Minimal
visible: d.isMinimal
Layout.alignment: Qt.AlignVCenter
onClicked: {
displaMinimalMenu = !displaMinimalMenu
d.enableNavigationPanel = !d.enableNavigationPanel
}
}
Image{
id:image_logo
Layout.preferredHeight: 20
Layout.preferredWidth: 20
source: control.logo
Layout.leftMargin: {
if(btn_nav.visible){
return 12
@ -361,7 +488,7 @@ Item {
}
FluText{
Layout.alignment: Qt.AlignVCenter
text:root.title
text:control.title
Layout.leftMargin: 12
fontStyle: FluText.Body
}
@ -370,12 +497,12 @@ Item {
Item{
anchors{
left: displayMode === FluNavigationView.Minimal ? parent.left : layout_list.right
left: d.isMinimal || d.isCompactAndPanel ? parent.left : layout_list.right
top: nav_app_bar.bottom
right: parent.right
bottom: parent.bottom
leftMargin: d.isCompactAndPanel ? 50 : 0
}
StackView{
id:nav_swipe
anchors.fill: parent
@ -395,19 +522,39 @@ Item {
MouseArea{
anchors.fill: parent
enabled: (displayMode === FluNavigationView.Minimal && displaMinimalMenu)
enabled: d.isMinimalAndPanel||d.isCompactAndPanel
onClicked: {
displaMinimalMenu = false
d.enableNavigationPanel = false
}
}
Rectangle{
id:layout_list
width: 300
width: {
if(d.isCompactAndNotPanel){
return 50
}
return 300
}
Behavior on width{
NumberAnimation{
duration: 150
}
}
Behavior on x{
id:anim_layout_list_x
NumberAnimation{
duration: 150
}
}
anchors{
top: parent.top
bottom: parent.bottom
}
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
border.width: d.isMinimal || d.isCompactAndPanel ? 1 : 0
color: {
if(displayMode === FluNavigationView.Minimal){
if(d.isMinimal || d.isCompactAndPanel){
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){
@ -415,64 +562,62 @@ Item {
}
return FluTheme.dark ? Qt.rgba(32/255,32/255,32/255,1) : Qt.rgba(243/255,243/255,243/255,1)
}
anchors{
top: parent.top
bottom: parent.bottom
}
x: {
if(displayMode !== FluNavigationView.Minimal)
return 0
return (displayMode === FluNavigationView.Minimal && displaMinimalMenu) ? 0 : -width
}
Behavior on x{
id:anim_navi
NumberAnimation{
duration: 150
}
}
Behavior on color{
ColorAnimation {
duration: 300
}
}
x: {
if(d.displayMode !== FluNavigationView.Minimal)
return 0
return d.isMinimalAndPanel ? 0 : -width
}
Item{
id:layout_header
width: layout_list.width
clip: true
y:nav_app_bar.height
height: {
if(loader_auto_suggest_box.item){
return loader_auto_suggest_box.item.height
}
return 0
}
height: 38
Loader{
id:loader_auto_suggest_box
anchors.horizontalCenter: parent.horizontalCenter
anchors.centerIn: parent
sourceComponent: autoSuggestBox
visible: {
if(d.isCompactAndNotPanel){
return false
}
return true
}
}
FluIconButton{
visible:d.isCompactAndNotPanel
hoverColor: FluTheme.dark ? Qt.rgba(1,1,1,0.03) : Qt.rgba(0,0,0,0.03)
pressedColor: FluTheme.dark ? Qt.rgba(1,1,1,0.03) : Qt.rgba(0,0,0,0.03)
normalColor: FluTheme.dark ? Qt.rgba(0,0,0,0) : Qt.rgba(0,0,0,0)
width:38
height:34
x:6
y:2
iconSize: 15
iconSource: {
if(loader_auto_suggest_box.item){
return loader_auto_suggest_box.item.autoSuggestBoxReplacement
}
return 0
}
onClicked: {
d.enableNavigationPanel = !d.enableNavigationPanel
}
}
}
ListView{
id:nav_list
property bool enableStack: true
property var stackIndex: []
clip: true
anchors{
top: layout_header.bottom
topMargin: 6
left: parent.left
right: parent.right
bottom: layout_footer.top
}
ScrollBar.vertical: FluScrollBar {}
model:d.handleItems()
highlightMoveDuration: 150
currentIndex: -1
onCurrentIndexChanged: {
if(enableStack){
stackIndex.push(currentIndex)
}
}
highlight: Item{
clip: true
Rectangle{
@ -487,11 +632,26 @@ Item {
}
}
}
ScrollBar.vertical: FluScrollBar {}
model:handleItems()
onCurrentIndexChanged: {
if(d.enableStack){
var item = model[currentIndex]
if(item instanceof FluPaneItem){
item.tap()
d.stackItems.push(item)
}
}
}
currentIndex: -1
anchors{
top: layout_header.bottom
topMargin: 6
left: parent.left
right: parent.right
bottom: layout_footer.top
}
delegate: Loader{
property var model: modelData
property var position: index
property var idx: index
property int type: 0
sourceComponent: {
if(modelData instanceof FluPaneItem){
@ -506,24 +666,43 @@ Item {
if(modelData instanceof FluPaneItemExpander){
return com_panel_item_expander
}
if(modelData instanceof FluPaneItemEmpty){
return com_panel_item_empty
}
}
}
}
ListView{
id:layout_footer
clip: true
width: layout_list.width
height: childrenRect.height
anchors.bottom: parent.bottom
interactive: false
currentIndex: -1
model: {
if(footerItems){
return footerItems.children
}
}
currentIndex: -1
highlightMoveDuration: 150
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
}
}
}
delegate: Loader{
property var model: modelData
property var position: index
property var idx: index
property int type: 1
sourceComponent: {
if(modelData instanceof FluPaneItem){
@ -540,27 +719,91 @@ Item {
}
}
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++
Popup{
property var childModel
id:control_popup
enter: Transition {
NumberAnimation {
property: "opacity"
from:0
to:1
duration: 150
}
}
background: Rectangle{
width: 160
radius: 4
FluShadow{
radius: 4
}
color: FluTheme.dark ? Qt.rgba(51/255,48/255,48/255,1) : Qt.rgba(248/255,250/255,253/255,1)
height: 38*Math.min(Math.max(list_view.count,1),8)
ListView{
id:list_view
anchors.fill: parent
clip: true
currentIndex: -1
model: control_popup.childModel
ScrollBar.vertical: FluScrollBar {}
delegate:FluControl{
width: 160
padding:10
background: Rectangle{
color: {
if(hovered){
return FluTheme.dark ? Qt.rgba(63/255,60/255,61/255,1) : Qt.rgba(237/255,237/255,242/255,1)
}
return FluTheme.dark ? Qt.rgba(51/255,48/255,48/255,1) : Qt.rgba(0,0,0,0)
}
}
contentItem: FluText{
text:modelData.title
anchors{
verticalCenter: parent.verticalCenter
}
}
onClicked: {
if(modelData.tapFunc){
modelData.tapFunc()
}else{
nav_list.currentIndex = idx
layout_footer.currentIndex = -1
if(d.isMinimal || d.isCompact){
d.enableNavigationPanel = false
}
}
control_popup.close()
}
}
}
}
return data
function showPopup(pos,model){
control_popup.x = pos.x
control_popup.y = pos.y
control_popup.childModel = model
control_popup.open()
}
}
Timer{
id:timer_anim_x_enable
interval: 150
onTriggered: {
anim_layout_list_x.enabled = true
}
}
function collapseAll(){
for(var i=0;i<nav_list.model.length;i++){
var item = nav_list.model[i]
if(item instanceof FluPaneItemExpander){
item.isExpand = false
}
}
}
function setCurrentIndex(index){
nav_list.currentIndex = index
}
function getItems(){
@ -571,10 +814,6 @@ Item {
nav_swipe.push(url)
}
function setCurrentIndex(index){
nav_list.currentIndex = index
}
function getCurrentIndex(){
return nav_list.currentIndex
}

View File

@ -0,0 +1,7 @@
import QtQuick
QtObject {
readonly property string key : FluApp.uuid()
property var parent
property int idx
}

View File

@ -2,12 +2,10 @@
import FluentUI
FluObject {
readonly property int flag : 3
readonly property string key : FluApp.uuid()
property string title
property int icon
property bool isExpand: false
property var parent
property int idx
signal tap
}

View File

@ -1,7 +1,6 @@
import QtQuick
QtObject {
readonly property int flag : 1
readonly property string key : FluApp.uuid()
property string title
property var parent

View File

@ -1,7 +1,6 @@
import QtQuick
QtObject {
readonly property int flag : 2
readonly property string key : FluApp.uuid()
property var parent
property int idx

View File

@ -10,7 +10,6 @@ ScrollBar {
property color handlePressColor: Qt.darker(handleNormalColor)
property bool expand: false
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
implicitContentWidth + leftPadding + rightPadding)
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
@ -144,7 +143,7 @@ ScrollBar {
iconSize: 8
anchors.top: parent.top
anchors.topMargin: 4
visible:vertical && expand
visible:vertical && expand && item_react.opacity
onClicked:{
decrease()
}
@ -152,7 +151,7 @@ ScrollBar {
FluIconButton{
id:btn_bottom
iconSource: FluentIcons.CaretSolidDown
visible:vertical && expand
visible:vertical && expand && item_react.opacity
width:10
height:10
iconSize: 8
@ -167,7 +166,7 @@ ScrollBar {
FluIconButton{
id:btn_left
iconSource: FluentIcons.CaretSolidLeft
visible:!vertical && expand
visible:!vertical && expand && item_react.opacity
width:10
height:10
iconSize: 8
@ -182,7 +181,7 @@ ScrollBar {
FluIconButton{
id:btn_right
iconSource: FluentIcons.CaretSolidRight
visible:!vertical && expand
visible:!vertical && expand && item_react.opacity
width:10
height:10
iconSize: 8

View File

@ -71,5 +71,6 @@
<file>controls/FluPagination.qml</file>
<file>controls/FluToggleButton.qml</file>
<file>controls/FluStatusView.qml</file>
<file>controls/FluPaneItemEmpty.qml</file>
</qresource>
</RCC>