Compare commits

...

30 Commits
1.2.0 ... 1.2.4

Author SHA1 Message Date
2acb3c34bd update 2023-04-11 23:43:20 +08:00
1df3b4ba96 update 2023-04-11 23:39:20 +08:00
5ea3cb1054 Merge pull request #39 from zhuzichu520/dev1.2.4
update
2023-04-11 23:17:51 +08:00
5afd2ec518 update 2023-04-11 23:12:31 +08:00
59527fc954 Merge pull request #38 from zhuzichu520/dev1.2.4
update
2023-04-11 18:06:06 +08:00
c26fdfaee3 update 2023-04-11 18:05:07 +08:00
878dbd668b Merge pull request #36 from zhuzichu520/dev1.2.4
update
2023-04-11 16:31:35 +08:00
b10a0752fb update 2023-04-11 16:30:54 +08:00
51e73e7f8e fix bug 2023-04-11 16:30:07 +08:00
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
09e87e0fb8 update 2023-04-09 19:29:50 +08:00
0603a7603d update 2023-04-09 18:08:37 +08:00
d9a4f01e20 update 2023-04-09 17:59:00 +08:00
93709cd1dd update 2023-04-08 21:23:03 +08:00
0000e557a7 update 2023-04-08 20:44:27 +08:00
d2183e350e update 2023-04-08 20:26:49 +08:00
47caf4bb52 update 2023-04-08 20:20:44 +08:00
6fb9ee41fb update 2023-04-08 20:12:28 +08:00
b349c22434 update 2023-04-08 20:08:26 +08:00
12fa3487bb Merge pull request #29 from LiangLiang723/win32_cmake_fix
🐛 修复 windows 下生成 dll 库带 linux 前缀导致查找不到控件
2023-04-08 19:57:14 +08:00
a76806645a 🐛 修复 windows 下生成 dll 库带 linux 前缀导致查找不到控件 2023-04-08 19:45:39 +08:00
738db25c2c Merge pull request #28 from LiangLiang723/win32_cmake_fix
🐛 修复 windows 下生成 dll 库带 linux 前缀导致查找不到控件
2023-04-08 19:43:35 +08:00
9f8a5f5646 🐛 修复 windows 下生成 dll 库带 linux 前缀导致查找不到控件 2023-04-08 19:32:35 +08:00
c59c07e756 update 2023-04-07 20:48:04 +08:00
2084b5afa3 update 2023-04-07 20:19:18 +08:00
f8d717f41b update 2023-04-07 18:27:50 +08:00
be58fc5e7d update 2023-04-06 19:55:53 +08:00
1c0bc7208a update 2023-04-06 19:27:37 +08:00
75 changed files with 1757 additions and 876 deletions

View File

@ -38,7 +38,7 @@ jobs:
arch: ${{ matrix.qt_arch }}
modules: 'qt5compat qtmultimedia qtshadertools qtimageformats'
- name: ubuntu install GL library
run: sudo apt-get install -y libglew-dev libglfw3-dev qml-module-qtquick-controls qml-module-qtquick-controls2
run: sudo apt-get install -y libxkbcommon-x11-dev libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1-dev libxcb-randr0-dev libxcb-shape0-dev libxcb-xfixes0-dev libxcb-xinerama0-dev libxcb-sync-dev libxcb-render-util0-dev libxcb-shm0-dev
- uses: actions/checkout@v2
with:
fetch-depth: 1

View File

@ -62,6 +62,12 @@
|FluDatePicker|日期选择器||
|FluMenu|菜单Popup||
|FluNavigationView|响应式导航布局||
|FluScrollbar|滚动条||
|FluToggleButton|开关按钮||
|FluPagination|分页组件||
|FluTableView|表格组件||
|FluMediaPlayer|播放器||
|FluFlipView|FlipView||
# 部分效果预览
@ -77,6 +83,10 @@
![](doc/preview/theme.png)
## TableView表格组件
![](doc/preview/tableview.png)
## FluTreeView树组件
![](doc/preview/treeview.png)
@ -85,13 +95,5 @@
![](doc/preview/carousel.png)
## InfoBar提示框组件
![](doc/preview/infobar.png)
## 多窗口路由跳转
![](doc/preview/multiwindow.png)
### ⚡ Visitor count
![](https://profile-counter.glitch.me/zhuzichu520-FluentUI/count.svg)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 383 KiB

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 382 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 204 KiB

After

Width:  |  Height:  |  Size: 218 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

BIN
doc/preview/tableview.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 49 KiB

View File

@ -11,7 +11,7 @@ Window {
//初始化一个MediaPlayer解决macos切换到T_MediaPalyer页面崩溃问题
MediaPlayer{}
Component.onCompleted: {
FluApp.init(app,properties)
FluApp.init(app)
FluTheme.frameless = ("windows" === Qt.platform.os)
FluTheme.dark = false
FluApp.routes = {

7
example/AppInfo.cpp Normal file
View File

@ -0,0 +1,7 @@
#include "AppInfo.h"
AppInfo::AppInfo(QObject *parent)
: QObject{parent}
{
version("1.2.4");
}

15
example/AppInfo.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef APPINFO_H
#define APPINFO_H
#include <QObject>
#include "stdafx.h"
class AppInfo : public QObject
{
Q_OBJECT
Q_PROPERTY_AUTO(QString,version)
public:
explicit AppInfo(QObject *parent = nullptr);
};
#endif // APPINFO_H

View File

@ -7,6 +7,8 @@ set(CMAKE_AUTOUIC ON)
set(CMAKE_CXX_STANDARD 17)
file(TO_CMAKE_PATH "/" PATH_SEPARATOR)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(platform 64)
else()
@ -43,21 +45,18 @@ qt_add_resources(QT_RESOURCES ${RESOURCES})
add_executable(${PROJECT_NAME} ${SOURCES} ${HEADERS} ${QT_RESOURCES} ${RC_ICONS})
if(WIN32)
if(platform EQUAL 32)
file(GLOB DLL_FILES ${CMAKE_CURRENT_SOURCE_DIR}/../third/Win_x86/*.dll)
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
${DLL_FILES}
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
)
if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
set(DLLPATH ${CMAKE_CURRENT_SOURCE_DIR}/../third/msvc/*.dll)
else()
file(GLOB DLL_FILES ${CMAKE_CURRENT_SOURCE_DIR}/../third/Win_x64/*.dll)
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
${DLL_FILES}
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
)
set(DLLPATH ${CMAKE_CURRENT_SOURCE_DIR}/../third/mingw/*.dll)
endif()
string(REPLACE "/" ${PATH_SEPARATOR} DLLPATH "${DLLPATH}")
file(GLOB DLL_FILES ${DLLPATH})
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
${DLL_FILES}
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
)
endif()
target_compile_definitions(${PROJECT_NAME} PRIVATE

View File

@ -141,6 +141,47 @@ FluScrollablePage{
}'
}
FluArea{
Layout.fillWidth: true
height: 68
Layout.topMargin: 20
paddings: 10
FluToggleButton{
disabled:toggle_button_switch.selected
text:"Toggle Button"
onClicked: {
selected = !selected
}
anchors{
verticalCenter: parent.verticalCenter
left: parent.left
}
}
Row{
spacing: 5
anchors{
verticalCenter: parent.verticalCenter
right: parent.right
}
FluToggleSwitch{
id:toggle_button_switch
Layout.alignment: Qt.AlignRight
text:"Disabled"
}
}
}
CodeExpander{
Layout.fillWidth: true
code:'FluToggleButton{
text:"Toggle Button"
onClicked: {
selected = !selected
}
}'
}
FluArea{
Layout.fillWidth: true

View File

@ -52,12 +52,12 @@ FluScrollablePage{
}
CodeExpander{
Layout.fillWidth: true
code:' showInfo("这是一个Info样式的InfoBar")
code:'showInfo("这是一个Info样式的InfoBar")
showWarning("这是一个Warning样式的InfoBar")
showWarning("这是一个Warning样式的InfoBar")
showError("这是一个Error样式的InfoBar")
showError("这是一个Error样式的InfoBar")
showSuccess("这是一个Success样式的InfoBar这是一个Success样式的InfoBar")'
showSuccess("这是一个Success样式的InfoBar这是一个Success样式的InfoBar")'
}
}

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
}
}
}
}
}
}

91
example/T_StatusView.qml Normal file
View File

@ -0,0 +1,91 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Window
import FluentUI
import "./component"
FluScrollablePage{
title:"StatusView"
leftPadding:10
rightPadding:10
bottomPadding:20
spacing: 0
FluArea{
id:layout_actions
Layout.fillWidth: true
Layout.topMargin: 20
height: 50
paddings: 10
RowLayout{
spacing: 14
FluDropDownButton{
id:btn_status_mode
Layout.preferredWidth: 140
text:"Loading"
items:[
FluMenuItem{
text:"Loading"
onClicked: {
btn_status_mode.text = text
status_view.statusMode = FluStatusView.Loading
}
},
FluMenuItem{
text:"Empty"
onClicked: {
btn_status_mode.text = text
status_view.statusMode = FluStatusView.Empty
}
},
FluMenuItem{
text:"Error"
onClicked: {
btn_status_mode.text = text
status_view.statusMode = FluStatusView.Error
}
},
FluMenuItem{
text:"Success"
onClicked: {
btn_status_mode.text = text
status_view.statusMode = FluStatusView.Success
}
}
]
}
}
}
FluArea{
Layout.fillWidth: true
Layout.topMargin: 10
height: 380
paddings: 10
FluStatusView{
id:status_view
anchors.fill: parent
onErrorClicked:{
showError("点击重新加载")
}
Rectangle {
anchors.fill: parent
color:FluTheme.primaryColor.dark
}
}
}
CodeExpander{
Layout.fillWidth: true
code:'FluStatusView{
anchors.fill: parent
statusMode: FluStatusView.Loading
Rectangle{
anchors.fill: parent
color:FluTheme.primaryColor.dark
}
}'
}
}

160
example/T_TableView.qml Normal file
View File

@ -0,0 +1,160 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Window
import FluentUI
import "./component"
FluScrollablePage{
title:"TableView"
leftPadding:10
rightPadding:10
bottomPadding:20
spacing: 0
Component.onCompleted: {
const columns = [
{
title: '姓名',
dataIndex: 'name',
width:100
},
{
title: '年龄',
dataIndex: 'age',
width:100
},
{
title: '住址',
dataIndex: 'address',
width:200
},
{
title: '别名',
dataIndex: 'nickname',
width:100
},
{
title: '操作',
dataIndex: 'action',
width:100
},
];
table_view.columns = columns
loadData(1,10)
}
FluTableView{
id:table_view
Layout.fillWidth: true
Layout.topMargin: 20
pageCurrent:1
pageCount:10
itemCount: 1000
onRequestPage:
(page,count)=> {
loadData(page,count)
}
}
Component{
id:com_action
Item{
Row{
anchors.centerIn: parent
spacing: 10
FluFilledButton{
text:"编辑"
topPadding:3
bottomPadding:3
leftPadding:3
rightPadding:3
onClicked:{
console.debug(dataModel.index)
showSuccess(JSON.stringify(dataObject))
}
}
FluFilledButton{
text:"删除"
topPadding:3
bottomPadding:3
leftPadding:3
rightPadding:3
onClicked:{
showError(JSON.stringify(dataObject))
}
}
}
}
}
function loadData(page,count){
const dataSource = []
for(var i=0;i<count;i++){
dataSource.push({
name: "孙悟空%1".arg(((page-1)*count+i)),
age: 500,
address: "钟灵毓秀的花果山,如神仙仙境的水帘洞",
nickname: "齐天大圣",
action:com_action
})
}
table_view.dataSource = dataSource
}
CodeExpander{
Layout.fillWidth: true
Layout.topMargin: 10
code:'FluTableView{
id:table_view
Layout.fillWidth: true
Layout.topMargin: 20
width:parent.width
pageCurrent:1
pageCount:10
itemCount: 1000
onRequestPage:
(page,count)=> {
loadData(page,count)
}
Component.onCompleted: {
const columns = [
{
title: "姓名",
dataIndex: "name",
width:100
},
{
title: "年龄",
dataIndex: "age",
width:100
},
{
title: "住址",
dataIndex: "address",
width:200
},
{
title: "别名",
dataIndex: "nickname",
width:100
}
];
table_view.columns = columns
const dataSource = [
{
name: "孙悟空”,
age: 500,
address:"钟灵毓秀的花果山,如神仙仙境的水帘洞",
nickname:"齐天大圣"
}
];
table_view.dataSource = columns
}
}'
}
}

View File

@ -15,8 +15,9 @@ FluExpander{
id:content
width:parent.width
readOnly:true
text:code
text:highlightQmlCode(code)
focus:false
textFormat: FluMultilineTextBox.RichText
KeyNavigation.priority: KeyNavigation.BeforeItem
background:Rectangle{
radius: 4
@ -40,4 +41,90 @@ FluExpander{
}
}
function htmlEncode(e){
var i,s;
for(i in s={
"&":/&/g,//""//":/"/g,"'":/'/g,
"<":/</g,">":/>/g,"<br/>":/\n/g,
" ":/ /g," ":/\t/g
})e=e.replace(s[i],i);
return e;
}
function highlightQmlCode(code) {
// 定义 QML 关键字列表
var qmlKeywords = [
"FluTextButton",
"FluAppBar",
"FluAutoSuggestBox",
"FluBadge",
"FluButton",
"FluCalendarPicker",
"FluCalendarView",
"FluCarousel",
"FluCheckBox",
"FluColorPicker",
"FluColorView",
"FluComboBox",
"FluContentDialog",
"FluContentPage",
"FluControl",
"FluDatePicker",
"FluDivider",
"FluDropDownButton",
"FluExpander",
"FluFilledButton",
"FluFlipView",
"FluFocusRectangle",
"FluIcon",
"FluIconButton",
"FluInfoBar",
"FluItem",
"FluMediaPlayer",
"FluMenu",
"FluMenuItem",
"FluMultilineTextBox",
"FluNavigationView",
"FluObject",
"FluPaneItem",
"FluPaneItemExpander",
"FluPaneItemHeader",
"FluPaneItemSeparator",
"FluPivot",
"FluPivotItem",
"FluProgressBar",
"FluProgressRing",
"FluRadioButton",
"FluRectangle",
"FluScrollablePage",
"FluScrollBar",
"FluShadow",
"FluSlider",
"FluTabView",
"FluText",
"FluTextArea",
"FluTextBox",
"FluTextBoxBackground",
"FluTextBoxMenu",
"FluTextButton",
"FluTextFiled",
"FluTimePicker",
"FluToggleSwitch",
"FluTooltip",
"FluTreeView",
"FluWindow",
"FluWindowResize",
"FluToggleButton",
"FluTableView",
"FluColors",
"FluTheme",
"FluStatusView"
];
code = code.replace(/\n/g, "<br>");
code = code.replace(/ /g, "&nbsp;");
return code.replace(RegExp("\\b(" + qmlKeywords.join("|") + ")\\b", "g"), "<span style='color: #c23a80'>$1</span>");
}
}

View File

@ -3,10 +3,12 @@ CONFIG += c++17
DEFINES += QT_DEPRECATED_WARNINGS QT_NO_WARNING_OUTPUT
HEADERS += \
ChatController.h
ChatController.h \
AppInfo.h
SOURCES += \
ChatController.cpp \
AppInfo.cpp \
main.cpp
RESOURCES += qml.qrc
@ -23,13 +25,15 @@ CONFIG(debug,debug|release) {
}
win32 {
contains(QT_ARCH, i386) {
COPYDLL = $$absolute_path($${_PRO_FILE_PWD_}/../third/Win_x86/*.dll) $$DESTDIR
contains(QMAKE_CC, cl) {
COPYDLL = $$absolute_path($${_PRO_FILE_PWD_}/../third/msvc/*.dll) $$DESTDIR
QMAKE_PRE_LINK += $$QMAKE_COPY $$replace(COPYDLL, /, $$QMAKE_DIR_SEP)
} else {
COPYDLL = $$absolute_path($${_PRO_FILE_PWD_}/../third/Win_x64/*.dll) $$DESTDIR
COPYDLL = $$absolute_path($${_PRO_FILE_PWD_}/../third/mingw/*.dll) $$DESTDIR
QMAKE_PRE_LINK += $$QMAKE_COPY $$replace(COPYDLL, /, $$QMAKE_DIR_SEP)
}
}
qnx: target.path = /tmp/$${TARGET}/bin

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

@ -118,6 +118,12 @@ FluObject{
navigationView.push("qrc:/T_Rectangle.qml")
}
}
FluPaneItem{
title:"StatusView"
onTap:{
navigationView.push("qrc:/T_StatusView.qml")
}
}
FluPaneItem{
title:"Carousel"
onTap:{
@ -184,6 +190,16 @@ FluObject{
navigationView.push("qrc:/T_TreeView.qml")
}
}
FluPaneItem{
title:"TableView"
image:"qrc:/res/image/control/DataGrid.png"
recentlyAdded:true
order:4
desc:"The TableView control provides a flexible way to display a collection of data in rows and columns"
onTap:{
navigationView.push("qrc:/T_TableView.qml")
}
}
FluPaneItem{
title:"MultiWindow"
onTap:{
@ -285,21 +301,7 @@ FluObject{
}
function startPageByItem(data){
var items = navigationView.getItems();
for(var i=0;i<items.length;i++){
var item = items[i]
if(item.key === data.key){
if(navigationView.getCurrentIndex() === i){
return
}
item.tap()
navigationView.setCurrentIndex(i)
if(item.parent){
item.parent.isExpand = true
}
return
}
}
navigationView.startPageByItem(data)
}
}

View File

@ -0,0 +1,9 @@
pragma Singleton
import QtQuick
import QtQuick.Controls
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

@ -5,14 +5,9 @@
#include <QQuickWindow>
#include <QQuickStyle>
#include <QProcess>
#include "AppInfo.h"
#include "ChatController.h"
QMap<QString, QVariant> properties(){
QMap<QString, QVariant> map;
// map["installHelper"] = QVariant::fromValue(QVariant::fromValue(InstallHelper::getInstance()));
return map;
}
int main(int argc, char *argv[])
{
QCoreApplication::setOrganizationName("ZhuZiChu");
@ -21,17 +16,8 @@ int main(int argc, char *argv[])
QQuickStyle::setStyle("Basic");
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
qmlRegisterType<ChatController>("Controller",1,0,"ChatController");
QMapIterator<QString, QVariant> iterator(properties());
while (iterator.hasNext()) {
iterator.next();
QString key = iterator.key();
QVariant value = iterator.value();
engine.rootContext()->setContextProperty(key,value);
}
engine.rootContext()->setContextProperty("properties",properties());
engine.rootContext()->setContextProperty("appInfo",new AppInfo());
const QUrl url(QStringLiteral("qrc:/App.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {

View File

@ -5,6 +5,8 @@ import FluentUI
FluWindow {
id:window
width: 500
height: 600
minimumWidth: 500
@ -36,7 +38,7 @@ FluWindow {
fontStyle: FluText.Title
}
FluText{
text:"v1.2.0"
text:"v%1".arg(appInfo.version)
fontStyle: FluText.Body
Layout.alignment: Qt.AlignBottom
}

View File

@ -12,8 +12,6 @@ FluWindow {
minimumHeight: 400
maximumWidth: 400
maximumHeight: 400
modality:2
title:"登录"
onInitArgument:

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

@ -156,5 +156,9 @@
<file>T_FlipView.qml</file>
<file>T_Pivot.qml</file>
<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

@ -10,6 +10,10 @@ set(QT_INSTALL_QML ${Qt6Core_DIR}/../../../qml)
set(TARGET_TYPE SHARED)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/FluentUI)
if(WIN32)
set(CMAKE_SHARED_LIBRARY_PREFIX "")
endif()
set(TARGET_RESOURCES res.qrc)
set(TARGET_SOURCES
Def.cpp

View File

@ -7,8 +7,20 @@
#include <QTimer>
#include <QUuid>
#include <QClipboard>
#include "FluTheme.h"
#include "Def.h"
#ifdef Q_OS_WIN
#include <dwmapi.h>
#include <Windows.h>
#include <windowsx.h>
static bool isCompositionEnabled()
{
BOOL composition_enabled = FALSE;
bool success = ::DwmIsCompositionEnabled(&composition_enabled) == S_OK;
return composition_enabled && success;
}
#endif
FluApp* FluApp::m_instance = nullptr;
@ -25,12 +37,16 @@ FluApp::FluApp(QObject *parent)
{
}
void FluApp::init(QWindow *window,QMap<QString, QVariant> properties){
void FluApp::init(QQuickWindow *window){
this->appWindow = window;
this->properties = properties;
}
void FluApp::run(){
#ifdef Q_OS_WIN
if(!isCompositionEnabled()){
FluTheme::getInstance()->frameless(false);
}
#endif
navigate(initialRoute());
}
@ -39,38 +55,23 @@ void FluApp::navigate(const QString& route,const QJsonObject& argument,FluRegist
qErrnoWarning("没有找到当前路由");
return;
}
bool isAppWindow = route == initialRoute();
FramelessView *view = new FramelessView();
QQmlEngine *engine = qmlEngine(appWindow);
QQmlComponent component(engine, routes().value(route).toString());
QVariantMap properties;
if(fluRegister){
properties.insert("pageRegister",QVariant::fromValue(fluRegister));
}
properties.insert("argument",argument);
QQuickWindow *view = qobject_cast<QQuickWindow*>(component.createWithInitialProperties(properties));
wnds.insert(view->winId(),view);
if(fluRegister){
fluRegister->to(view);
view->setProperty("pageRegister",QVariant::fromValue(fluRegister));
}
view->setProperty("argument",argument);
QMapIterator<QString, QVariant> iterator(properties);
while (iterator.hasNext()) {
iterator.next();
QString key = iterator.key();
QVariant value = iterator.value();
view->engine()->rootContext()->setContextProperty(key,value);
}
view->setColor(QColor(Qt::transparent));
QObject::connect(view, &QQuickView::statusChanged, view, [&](QQuickView::Status status) {
if (status == QQuickView::Status::Ready) {
Q_EMIT windowReady(view);
view->moveToScreenCenter();
view->show();
}
});
view->setSource((routes().value(route).toString()));
if(isAppWindow){
QObject::connect(view->engine(), &QQmlEngine::quit, qApp, &QCoreApplication::quit);
}else{
view->closeDeleteLater();
if(view->maximumWidth()==view->minimumWidth()&&view->maximumHeight()==view->minimumHeight()){
view->resize(view->minimumSize());
}
}
bool FluApp::equalsWindow(FramelessView *view,QWindow *window){
return view->winId() == window->winId();
view->show();
}
QJsonArray FluApp::awesomelist(const QString& keyword)

View File

@ -8,7 +8,6 @@
#include <QJsonObject>
#include <QQmlEngine>
#include "FluRegister.h"
#include "FramelessView.h"
#include "stdafx.h"
class FluApp : public QObject
@ -27,11 +26,7 @@ public:
Q_INVOKABLE void navigate(const QString& route,const QJsonObject& argument = {},FluRegister* fluRegister = nullptr);
Q_INVOKABLE void init(QWindow *window,QMap<QString, QVariant> properties);
Q_SIGNAL void windowReady(FramelessView *view);
Q_INVOKABLE bool equalsWindow(FramelessView *view,QWindow *window);
Q_INVOKABLE void init(QQuickWindow *window);
Q_INVOKABLE QJsonArray awesomelist(const QString& keyword = "");
@ -39,11 +34,11 @@ public:
Q_INVOKABLE QString uuid();
public:
QMap<quint64, QQuickWindow*> wnds;
private:
QMap<QString, QVariant> properties;
static FluApp* m_instance;
QWindow *appWindow;
};
#endif // FLUAPP_H

View File

@ -2,15 +2,15 @@
#define FLUREGISTER_H
#include <QObject>
#include <FramelessView.h>
#include <QQuickWindow>
#include <QJsonObject>
#include "stdafx.h"
class FluRegister : public QObject
{
Q_OBJECT
Q_PROPERTY_AUTO(FramelessView*,from)
Q_PROPERTY_AUTO(FramelessView*,to)
Q_PROPERTY_AUTO(QQuickWindow*,from)
Q_PROPERTY_AUTO(QQuickWindow*,to)
Q_PROPERTY_AUTO(QString,path);
public:
explicit FluRegister(QObject *parent = nullptr);

View File

@ -5,6 +5,7 @@
#include <QGuiApplication>
#include <QQuickWindow>
#include "FluColors.h"
#include "NativeEventFilter.h"
#include "FluTheme.h"
#include "WindowHelper.h"
#include "FluApp.h"
@ -33,6 +34,10 @@ void Fluent::registerTypes(const char *uri){
qmlRegisterType<WindowHelper>(uri,major,minor,"WindowHelper");
qmlRegisterType<FluColorSet>(uri,major,minor,"FluColorSet");
qmlRegisterType(QUrl("qrc:/com.zhuzichu/controls/FluStatusView.qml"),uri,major,minor,"FluStatusView");
qmlRegisterType(QUrl("qrc:/com.zhuzichu/controls/FluPagination.qml"),uri,major,minor,"FluPagination");
qmlRegisterType(QUrl("qrc:/com.zhuzichu/controls/FluToggleButton.qml"),uri,major,minor,"FluToggleButton");
qmlRegisterType(QUrl("qrc:/com.zhuzichu/controls/FluTableView.qml"),uri,major,minor,"FluTableView");
qmlRegisterType(QUrl("qrc:/com.zhuzichu/controls/FluPivotItem.qml"),uri,major,minor,"FluPivotItem");
qmlRegisterType(QUrl("qrc:/com.zhuzichu/controls/FluPivot.qml"),uri,major,minor,"FluPivot");
qmlRegisterType(QUrl("qrc:/com.zhuzichu/controls/FluFlipView.qml"),uri,major,minor,"FluFlipView");
@ -91,6 +96,8 @@ void Fluent::registerTypes(const char *uri){
void Fluent::initializeEngine(QQmlEngine *engine, const char *uri)
{
nativeEvent = new NativeEventFilter();
qApp->installNativeEventFilter(nativeEvent);
Q_UNUSED(engine)
Q_UNUSED(uri)
#ifdef Q_OS_WIN

View File

@ -3,17 +3,25 @@
#include <QObject>
#include <QQmlEngine>
#include "NativeEventFilter.h"
class Fluent: public QObject
{
Q_OBJECT
public:
Q_INVOKABLE QString version() const;
~Fluent(){
if (nativeEvent != Q_NULLPTR) {
delete nativeEvent;
nativeEvent = Q_NULLPTR;
}
}
void registerTypes(const char *uri);
void initializeEngine(QQmlEngine *engine, const char *uri);
static Fluent *getInstance();
private:
static Fluent* m_instance;
NativeEventFilter *nativeEvent = Q_NULLPTR;
};
#endif // FLUENT_H

View File

@ -19,7 +19,7 @@ HEADERS += \
FluTheme.h \
Fluent.h \
FluentUI.h \
FramelessView.h \
NativeEventFilter.h \
WindowHelper.h \
qml_plugin.h \
stdafx.h
@ -33,16 +33,12 @@ SOURCES += \
FluTheme.cpp \
Fluent.cpp \
FluentUI.cpp \
NativeEventFilter.cpp \
WindowHelper.cpp \
qml_plugin.cpp \
win32 {
LIBS += -ldwmapi -luser32
SOURCES += \
FramelessView_win.cpp
} else {
SOURCES += \
FramelessView_unix.cpp
}
DEFINES += VERSION_IN=\\\"1.0.0\\\"

View File

@ -1,56 +0,0 @@
#pragma once
#include <QMouseEvent>
#include <QQuickView>
#include <QRegion>
class FramelessViewPrivate;
class FramelessView : public QQuickView
{
Q_OBJECT
using Super = QQuickView;
Q_PROPERTY(bool isMax READ isMax NOTIFY isMaxChanged)
Q_PROPERTY(bool isFull READ isFull NOTIFY isFullChanged)
public:
explicit FramelessView(QWindow *parent = nullptr);
~FramelessView();
void moveToScreenCenter();
void closeDeleteLater();
bool isMax() const;
bool isFull() const;
QQuickItem *titleItem() const;
static QMap<WId,FramelessView*> *windowCache;
static QRect calcCenterGeo(const QRect &screenGeo, const QSize &normalSize);
public slots:
void setIsMax(bool isMax);
void setIsFull(bool isFull);
void setTitleItem(QQuickItem* item);
signals:
void isMaxChanged(bool isMax);
void isFullChanged(bool isFull);
void mousePressed(int xPos, int yPos, int button);
protected:
void showEvent(QShowEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
bool event(QEvent *ev) override;
# if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result) override;
# else
bool nativeEvent(const QByteArray &eventType, void *message, long *result) override;
# endif
void mousePressEvent(QMouseEvent* event) override
{
# if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
emit mousePressed(event->position().x(), event->position().y(), event->button());
#else
emit mousePressed(event->x(), event->y(), event->button());
#endif
Super::mousePressEvent(event);
}
private:
FramelessViewPrivate *d;
};

View File

@ -1,138 +0,0 @@
#include "FramelessView.h"
#include <QGuiApplication>
#include <QQuickItem>
#include <QScreen>
#include <QWindow>
#include <FluTheme.h>
class FramelessViewPrivate
{
public:
bool m_isMax = false;
bool m_isFull = false;
bool m_deleteLater = false;
QQuickItem *m_titleItem = nullptr;
};
FramelessView::FramelessView(QWindow *parent) : Super(parent), d(new FramelessViewPrivate)
{
if(FluTheme::getInstance()->frameless()){
setFlags( Qt::Window | Qt::FramelessWindowHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint);
}
setResizeMode(SizeRootObjectToView);
setIsMax(windowState() == Qt::WindowMaximized);
setIsFull(windowState() == Qt::WindowFullScreen);
connect(this, &QWindow::windowStateChanged, this, [&](Qt::WindowState state) {
(void)state;
setIsMax(windowState() == Qt::WindowMaximized);
setIsFull(windowState() == Qt::WindowFullScreen);
});
connect(FluTheme::getInstance(),&FluTheme::framelessChanged,this,[=](){
setFlag(Qt::Window,false);
setFlag(Qt::Window,true);
});
}
FramelessView::~FramelessView()
{
delete d;
}
void FramelessView::showEvent(QShowEvent *e)
{
Super::showEvent(e);
}
QRect FramelessView::calcCenterGeo(const QRect &screenGeo, const QSize &normalSize)
{
int w = normalSize.width();
int h = normalSize.height();
int x = screenGeo.x() + (screenGeo.width() - w) / 2;
int y = screenGeo.y() + (screenGeo.height() - h) / 2;
if (screenGeo.width() < w) {
x = screenGeo.x();
w = screenGeo.width();
}
if (screenGeo.height() < h) {
y = screenGeo.y();
h = screenGeo.height();
}
return { x, y, w, h };
}
void FramelessView::moveToScreenCenter()
{
auto geo = calcCenterGeo(screen()->availableGeometry(), size());
if (minimumWidth() > geo.width() || minimumHeight() > geo.height()) {
setMinimumSize(geo.size());
}
setGeometry(geo);
update();
}
void FramelessView::closeDeleteLater(){
d->m_deleteLater = true;
}
bool FramelessView::isMax() const
{
return d->m_isMax;
}
bool FramelessView::isFull() const
{
return d->m_isFull;
}
QQuickItem *FramelessView::titleItem() const
{
return d->m_titleItem;
}
void FramelessView::setIsMax(bool isMax)
{
if (d->m_isMax == isMax)
return;
d->m_isMax = isMax;
emit isMaxChanged(d->m_isMax);
}
void FramelessView::setIsFull(bool isFull)
{
if(d->m_isFull == isFull)
return;
d->m_isFull = isFull;
emit isFullChanged(d->m_isFull);
}
void FramelessView::setTitleItem(QQuickItem *item)
{
d->m_titleItem = item;
}
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
bool FramelessView::nativeEvent(const QByteArray &eventType, void *message, qintptr *result)
#else
bool FramelessView::nativeEvent(const QByteArray &eventType, void *message, long *result)
#endif
{
return Super::nativeEvent(eventType, message, result);
}
void FramelessView::resizeEvent(QResizeEvent *e)
{
Super::resizeEvent(e);
}
bool FramelessView::event(QEvent *ev)
{
if (ev->type() == QEvent::Close) {
if(d->m_deleteLater){
deleteLater();
ev->setAccepted(false);
}
}
return QQuickWindow::event(ev);
}

View File

@ -1,268 +0,0 @@
#include "FramelessView.h"
#include <QGuiApplication>
#include <QQuickItem>
#include <QScreen>
#include <QWindow>
#include <FluTheme.h>
#include <QTimer>
#include <dwmapi.h>
#include <windowsx.h>
#pragma comment(lib, "Dwmapi.lib")
#pragma comment(lib, "User32.lib")
static bool isCompositionEnabled()
{
BOOL composition_enabled = FALSE;
bool success = ::DwmIsCompositionEnabled(&composition_enabled) == S_OK;
return composition_enabled && success;
}
static bool isMaxWin(QWindow* win)
{
return win->windowState() == Qt::WindowMaximized;
}
static bool isFullWin(QQuickView* win)
{
return win->windowState() == Qt::WindowFullScreen;
}
static bool isFixWin(QQuickView* win)
{
return win->minimumWidth() == win->maximumWidth() && win->minimumHeight() == win->maximumHeight();
}
static long hitTest(RECT winrect, long x, long y, int borderWidth)
{
if ((x >= winrect.left) && (x < winrect.left + borderWidth) && (y >= winrect.top) && (y < winrect.top + borderWidth))
{
return HTTOPLEFT;
}
else if (x < winrect.right && x >= winrect.right - borderWidth && y >= winrect.top && y < winrect.top + borderWidth)
{
return HTTOPRIGHT;
}
else if (x >= winrect.left && x < winrect.left + borderWidth && y < winrect.bottom && y >= winrect.bottom - borderWidth)
{
return HTBOTTOMLEFT;
}
else if (x < winrect.right && x >= winrect.right - borderWidth && y < winrect.bottom && y >= winrect.bottom - borderWidth)
{
return HTBOTTOMRIGHT;
}
else if (x >= winrect.left && x < winrect.left + borderWidth)
{
return HTLEFT;
}
else if (x < winrect.right && x >= winrect.right - borderWidth)
{
return HTRIGHT;
}
else if (y >= winrect.top && y < winrect.top + borderWidth)
{
return HTTOP;
}
else if (y < winrect.bottom && y >= winrect.bottom - borderWidth)
{
return HTBOTTOM;
}
else
{
return 0;
}
}
class FramelessViewPrivate
{
public:
bool m_isMax = false;
bool m_isFull = false;
bool m_deleteLater = false;
bool m_isFirst = true;
QQuickItem *m_titleItem = nullptr;
};
FramelessView::FramelessView(QWindow *parent) : Super(parent), d(new FramelessViewPrivate)
{
if(!isCompositionEnabled()){
FluTheme::getInstance()->frameless(false);
}
if(FluTheme::getInstance()->frameless()){
setFlag(Qt::FramelessWindowHint,true);
}
setResizeMode(SizeRootObjectToView);
setIsMax(windowState() == Qt::WindowMaximized);
setIsFull(windowState() == Qt::WindowFullScreen);
connect(this, &QWindow::windowStateChanged, this, [=](Qt::WindowState state) {
(void)state;
setIsMax(windowState() == Qt::WindowMaximized);
setIsFull(windowState() == Qt::WindowFullScreen);
});
connect(FluTheme::getInstance(),&FluTheme::framelessChanged,this,[=](){
setFlag(Qt::Window,false);
setFlag(Qt::Window,true);
});
}
FramelessView::~FramelessView()
{
delete d;
}
void FramelessView::showEvent(QShowEvent *e)
{
static const MARGINS shadow_state[2] { { 0, 0, 0, 0 }, { 1, 1, 1, 1 } };
::DwmExtendFrameIntoClientArea((HWND)(winId()), &shadow_state[true]);
if(FluTheme::getInstance()->frameless()){
setFlag(Qt::FramelessWindowHint,false);
}
Super::showEvent(e);
}
QRect FramelessView::calcCenterGeo(const QRect &screenGeo, const QSize &normalSize)
{
int w = normalSize.width();
int h = normalSize.height();
int x = screenGeo.x() + (screenGeo.width() - w) / 2;
int y = screenGeo.y() + (screenGeo.height() - h) / 2;
if (screenGeo.width() < w) {
x = screenGeo.x();
w = screenGeo.width();
}
if (screenGeo.height() < h) {
y = screenGeo.y();
h = screenGeo.height();
}
return { x, y, w, h };
}
void FramelessView::moveToScreenCenter()
{
auto geo = calcCenterGeo(screen()->availableGeometry(), size());
if (minimumWidth() > geo.width() || minimumHeight() > geo.height()) {
setMinimumSize(geo.size());
}
setGeometry(geo);
update();
}
void FramelessView::closeDeleteLater(){
d->m_deleteLater = true;
}
bool FramelessView::isMax() const
{
return d->m_isMax;
}
bool FramelessView::isFull() const
{
return d->m_isFull;
}
QQuickItem *FramelessView::titleItem() const
{
return d->m_titleItem;
}
void FramelessView::setIsMax(bool isMax)
{
if (d->m_isMax == isMax)
return;
d->m_isMax = isMax;
emit isMaxChanged(d->m_isMax);
}
void FramelessView::setIsFull(bool isFull)
{
if(d->m_isFull == isFull)
return;
d->m_isFull = isFull;
emit isFullChanged(d->m_isFull);
}
int count = 1;
void FramelessView::setTitleItem(QQuickItem *item)
{
d->m_titleItem = item;
}
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
bool FramelessView::nativeEvent(const QByteArray &eventType, void *message, qintptr *result)
#else
bool FramelessView::nativeEvent(const QByteArray &eventType, void *message, long *result)
#endif
{
MSG* msg = static_cast<MSG*>(message);
if (!msg || !msg->hwnd)
{
return false;
}
if (msg->message == WM_NCHITTEST && FluTheme::getInstance()->frameless())
{
RECT winrect;
GetWindowRect(HWND(winId()), &winrect);
long x = GET_X_LPARAM(msg->lParam);
long y = GET_Y_LPARAM(msg->lParam);
*result = 0;
if (!isMaxWin(this) && !isFullWin(this) && !isFixWin(this))
{
*result = hitTest(winrect, x, y, 4);
if (0 != *result)
{
return true;
}
}
}else if (msg->message == WM_NCCALCSIZE && FluTheme::getInstance()->frameless())
{
const auto mode = static_cast<BOOL>(msg->wParam);
const auto clientRect = mode ? &(reinterpret_cast<LPNCCALCSIZE_PARAMS>(msg->lParam)->rgrc[0]) : reinterpret_cast<LPRECT>(msg->lParam);
if (mode == TRUE)
{
*result = WVR_REDRAW;
if (!isMaxWin(this) && !isFullWin(this))
{
if (clientRect->top != 0)
{
clientRect->top -= 0.1;
}
}
else
{
if (clientRect->top != 0)
{
clientRect->top += 0.1;
}
}
return true;
}
}else if (msg->message == WM_WINDOWPOSCHANGING)
{
WINDOWPOS* wp = reinterpret_cast<WINDOWPOS*>(msg->lParam);
if (wp != nullptr && (wp->flags & SWP_NOSIZE) == 0)
{
wp->flags |= SWP_NOCOPYBITS;
*result = 0;
return true;
}
}
return Super::nativeEvent(eventType, message, result);
}
void FramelessView::resizeEvent(QResizeEvent *e)
{
Super::resizeEvent(e);
}
bool FramelessView::event(QEvent *ev)
{
if (ev->type() == QEvent::Close) {
if(d->m_deleteLater){
deleteLater();
ev->setAccepted(false);
}
}
return QQuickWindow::event(ev);
}

81
src/NativeEventFilter.cpp Normal file
View File

@ -0,0 +1,81 @@
#include "NativeEventFilter.h"
#include "FluTheme.h"
#include "FluApp.h"
#ifdef Q_OS_WIN
#include <Windows.h>
#include <windowsx.h>
#endif
bool NativeEventFilter::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result)
{
#ifdef Q_OS_WIN
if (eventType == "windows_generic_MSG" && FluTheme::getInstance()->frameless()) {
MSG* msg = static_cast<MSG *>(message);
if (msg == Q_NULLPTR)
return false;
switch(msg->message) {
case WM_COMMAND: {
SendMessage(msg->hwnd, WM_SYSCOMMAND, msg->wParam, msg->lParam);
*result = DefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam);
return true;
}
case WM_NCCALCSIZE:{
NCCALCSIZE_PARAMS& params = *reinterpret_cast<NCCALCSIZE_PARAMS*>(msg->lParam);
if (params.rgrc[0].top != 0)
params.rgrc[0].top -= 1;
*result = WVR_REDRAW;
return true;
}
case WM_NCHITTEST: {
auto view = FluApp::getInstance()->wnds[(WId)msg->hwnd];
bool isResize = !(view->maximumWidth()==view->minimumWidth()&&view->maximumHeight()==view->minimumHeight());
const LONG borderWidth = 8;
RECT winrect;
GetWindowRect(msg->hwnd, &winrect);
long x = GET_X_LPARAM(msg->lParam);
long y = GET_Y_LPARAM(msg->lParam);
if (x >= winrect.left && x < winrect.left + borderWidth &&
y < winrect.bottom && y >= winrect.bottom - borderWidth && isResize) {
*result = HTBOTTOMLEFT;
return true;
}
if (x < winrect.right && x >= winrect.right - borderWidth &&
y < winrect.bottom && y >= winrect.bottom - borderWidth && isResize) {
*result = HTBOTTOMRIGHT;
return true;
}
if (x >= winrect.left && x < winrect.left + borderWidth &&
y >= winrect.top && y < winrect.top + borderWidth && isResize) {
*result = HTTOPLEFT;
return true;
}
if (x < winrect.right && x >= winrect.right - borderWidth &&
y >= winrect.top && y < winrect.top + borderWidth && isResize) {
*result = HTTOPRIGHT;
return true;
}
if (x >= winrect.left && x < winrect.left + borderWidth && isResize) {
*result = HTLEFT;
return true;
}
if (x < winrect.right && x >= winrect.right - borderWidth && isResize) {
*result = HTRIGHT;
return true;
}
if (y < winrect.bottom && y >= winrect.bottom - borderWidth && isResize) {
*result = HTBOTTOM;
return true;
}
if (y >= winrect.top && y < winrect.top + borderWidth && isResize) {
*result = HTTOP;
return true;
}
return false;
}
default:
break;
}
}
#endif
return false;
}

14
src/NativeEventFilter.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef NATIVEEVENTFILTER_H
#define NATIVEEVENTFILTER_H
#include <QObject>
#include <QAbstractNativeEventFilter>
class NativeEventFilter : public QAbstractNativeEventFilter
{
public:
bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override;
};
#endif // NATIVEEVENTFILTER_H

View File

@ -1,6 +1,19 @@
#include "WindowHelper.h"
#include "FluRegister.h"
#include "FluApp.h"
#include "FluTheme.h"
#ifdef Q_OS_WIN
#include <dwmapi.h>
#include <Windows.h>
#include <windowsx.h>
enum class Style : DWORD
{
windowed = (WS_OVERLAPPEDWINDOW | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CLIPCHILDREN),
aero_borderless = (WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CLIPCHILDREN)
};
#endif
WindowHelper::WindowHelper(QObject *parent)
: QObject{parent}
@ -8,51 +21,18 @@ WindowHelper::WindowHelper(QObject *parent)
}
void WindowHelper::setTitle(const QString& text){
window->setTitle(text);
}
void WindowHelper::initWindow(FramelessView* window){
void WindowHelper::initWindow(QQuickWindow* window){
this->window = window;
}
QJsonObject WindowHelper::getArgument(){
return window->property("argument").toJsonObject();
}
QVariant WindowHelper::getPageRegister(){
return window->property("pageRegister");
}
void WindowHelper::setMinimumWidth(int width){
this->window->setMinimumWidth(width);
}
void WindowHelper::setMaximumWidth(int width){
this->window->setMaximumWidth(width);
}
void WindowHelper::setMinimumHeight(int height){
this->window->setMinimumHeight(height);
}
void WindowHelper::setMaximumHeight(int height){
this->window->setMaximumHeight(height);
}
void WindowHelper::updateWindow(){
this->window->setFlag(Qt::Window,false);
this->window->setFlag(Qt::Window,true);
}
void WindowHelper::setOpacity(qreal opacity){
this->window->setOpacity(opacity);
}
void WindowHelper::setModality(int type){
if(type == 0){
this->window->setModality(Qt::NonModal);
}else if(type == 1){
this->window->setModality(Qt::WindowModal);
}else if(type == 2){
this->window->setModality(Qt::ApplicationModal);
}else{
this->window->setModality(Qt::NonModal);
#ifdef Q_OS_WIN
if(FluTheme::getInstance()->frameless()){
HWND wnd = (HWND)window->winId();
SetWindowLongPtr(wnd, GWL_STYLE, static_cast<LONG>(Style::aero_borderless));
const MARGINS shadow_on = { 1, 1, 1, 1 };
DwmExtendFrameIntoClientArea(wnd, &shadow_on);
SetWindowPos(wnd, Q_NULLPTR, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE);
ShowWindow(wnd, SW_SHOW);
}
#endif
}
QVariant WindowHelper::createRegister(const QString& path){
@ -61,3 +41,10 @@ QVariant WindowHelper::createRegister(const QString& path){
p->path(path);
return QVariant::fromValue(p);
}
void WindowHelper::destoryWindow(){
if(this->window){
FluApp::getInstance()->wnds.remove(this->window->winId());
this->window->deleteLater();
}
}

View File

@ -6,7 +6,6 @@
#include <QQuickItem>
#include <QWindow>
#include <QJsonObject>
#include "FramelessView.h"
class WindowHelper : public QObject
{
@ -15,22 +14,12 @@ class WindowHelper : public QObject
public:
explicit WindowHelper(QObject *parent = nullptr);
Q_INVOKABLE void initWindow(FramelessView* window);
Q_INVOKABLE void setTitle(const QString& text);
Q_INVOKABLE void setMinimumWidth(int width);
Q_INVOKABLE void setMaximumWidth(int width);
Q_INVOKABLE void setMinimumHeight(int height);
Q_INVOKABLE void setMaximumHeight(int height);
Q_INVOKABLE QJsonObject getArgument();
Q_INVOKABLE QVariant getPageRegister();
Q_INVOKABLE void updateWindow();
Q_INVOKABLE void setOpacity(qreal opacity);
Q_INVOKABLE void setModality(int type);
Q_INVOKABLE void initWindow(QQuickWindow* window);
Q_INVOKABLE void destoryWindow();
Q_INVOKABLE QVariant createRegister(const QString& path);
private:
FramelessView* window;
QQuickWindow* window;
};
#endif // WINDOWHELPER_H

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

@ -43,11 +43,17 @@ FluControl {
if(disabled){
return Qt.rgba(131/255,131/255,131/255,1)
}
if(pressed){
return Qt.rgba(162/255,162/255,162/255,1)
}
return Qt.rgba(1,1,1,1)
}else{
if(disabled){
return Qt.rgba(160/255,160/255,160/255,1)
}
if(pressed){
return Qt.rgba(96/255,96/255,96/255,1)
}
return Qt.rgba(0,0,0,1)
}
}

View File

@ -12,10 +12,13 @@ FluControl {
property color borderSelectedColor: FluTheme.dark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark
property color borderHoverColor: FluTheme.dark ? Qt.rgba(167/255,167/255,167/255,1) : Qt.rgba(135/255,135/255,135/255,1)
property color borderDisableColor: FluTheme.dark ? Qt.rgba(82/255,82/255,82/255,1) : Qt.rgba(199/255,199/255,199/255,1)
property color borderPressedColor: FluTheme.dark ? Qt.rgba(90/255,90/255,90/255,1) : Qt.rgba(191/255,191/255,191/255,1)
property color normalColor: FluTheme.dark ? Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(247/255,247/255,247/255,1)
property color selectedColor: FluTheme.dark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark
property color hoverColor: FluTheme.dark ? Qt.rgba(62/255,62/255,62/255,1) : Qt.rgba(244/255,244/255,244/255,1)
property color selectedHoverColor: FluTheme.dark ? Qt.darker(selectedColor,1.1) : Qt.lighter(selectedColor,1.1)
property color hoverColor: FluTheme.dark ? Qt.rgba(72/255,72/255,72/255,1) : Qt.rgba(236/255,236/255,236/255,1)
property color selectedHoverColor: FluTheme.dark ? Qt.darker(selectedColor,1.15) : Qt.lighter(selectedColor,1.15)
property color selectedPreesedColor: FluTheme.dark ? Qt.darker(selectedColor,1.3) : Qt.lighter(selectedColor,1.3)
property color selectedDisableColor: FluTheme.dark ? Qt.rgba(82/255,82/255,82/255,1) : Qt.rgba(199/255,199/255,199/255,1)
property color disableColor: FluTheme.dark ? Qt.rgba(50/255,50/255,50/255,1) : Qt.rgba(253/255,253/255,253/255,1)
@ -52,6 +55,9 @@ FluControl {
if(selected){
return borderSelectedColor
}
if(pressed){
return borderPressedColor
}
if(hovered){
return borderHoverColor
}
@ -63,6 +69,9 @@ FluControl {
if(disabled){
return selectedDisableColor
}
if(pressed){
return selectedPreesedColor
}
if(hovered){
return selectedHoverColor
}

View File

@ -58,11 +58,17 @@ FluControl {
if(disabled){
return Qt.rgba(131/255,131/255,131/255,1)
}
if(pressed){
return Qt.rgba(162/255,162/255,162/255,1)
}
return Qt.rgba(1,1,1,1)
}else{
if(disabled){
return Qt.rgba(160/255,160/255,160/255,1)
}
if(pressed){
return Qt.rgba(96/255,96/255,96/255,1)
}
return Qt.rgba(0,0,0,1)
}
}

View File

@ -8,6 +8,7 @@ FluControl {
property color normalColor: FluTheme.dark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark
property color hoverColor: FluTheme.dark ? Qt.darker(normalColor,1.1) : Qt.lighter(normalColor,1.1)
property color disableColor: FluTheme.dark ? Qt.rgba(82/255,82/255,82/255,1) : Qt.rgba(199/255,199/255,199/255,1)
property color pressedColor: FluTheme.dark ? Qt.darker(normalColor,1.2) : Qt.lighter(normalColor,1.2)
id: control
enabled: !disabled
@ -27,6 +28,9 @@ FluControl {
if(disabled){
return disableColor
}
if(pressed){
return pressedColor
}
return hovered ? hoverColor :normalColor
}
}
@ -44,6 +48,5 @@ FluControl {
return Qt.rgba(1,1,1,1)
}
}
font.pixelSize: 14
}
}

View File

@ -9,12 +9,16 @@ FluControl {
property bool disabled: false
property int radius:4
property color hoverColor: FluTheme.dark ? Qt.rgba(62/255,62/255,62/255,1) : Qt.rgba(0,0,0,0.03)
property color pressedColor: FluTheme.dark ? Qt.rgba(62/255,62/255,62/255,1) : Qt.rgba(0,0,0,0.06)
property color normalColor: FluTheme.dark ? Qt.rgba(0,0,0,0) : Qt.rgba(0,0,0,0)
property color disableColor: FluTheme.dark ? Qt.rgba(0,0,0,0) : Qt.rgba(0,0,0,0)
property color color: {
if(disabled){
return disableColor
}
if(pressed){
return pressedColor
}
return hovered ? hoverColor : normalColor
}
property color iconColor: {
@ -34,6 +38,7 @@ FluControl {
id:control
width: 30
height: 30
clip: true
implicitWidth: width
implicitHeight: height
padding: 0

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,29 +220,32 @@ 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 === idx)&&type===0){
return Qt.rgba(1,1,1,0.06)
}
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(nav_list.currentIndex === idx&&type===0){
return Qt.rgba(0,0,0,0.06)
}
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: {
@ -185,14 +263,31 @@ 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
}
color:{
if(item_mouse.pressed){
return FluTheme.dark ? FluColors.Grey80 : FluColors.Grey120
}
return FluTheme.dark ? FluColors.White : FluColors.Grey220
}
}
}
}
@ -213,8 +308,7 @@ Item {
}
return 38
}
width: nav_list.width
width: layout_list.width
Rectangle{
radius: 4
anchors{
@ -232,41 +326,62 @@ 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(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)
}
if((nav_list.currentIndex === position)&&type===0){
return Qt.rgba(1,1,1,0.06)
}
return Qt.rgba(0,0,0,0)
}else{
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)
}
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: {
@ -284,10 +399,27 @@ 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
}
return FluTheme.dark ? FluColors.White : FluColors.Grey220
}
anchors{
verticalCenter: parent.verticalCenter
left:item_icon.right
@ -297,6 +429,7 @@ Item {
}
}
Item {
id:nav_app_bar
width: parent.width
@ -315,30 +448,40 @@ 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{
id:btn_nav
iconSource: FluentIcons.GlobalNavButton
iconSize: 15
Layout.preferredWidth: 40
Layout.preferredWidth: d.isMinimal ? 40 : 0
Layout.preferredHeight: 40
visible: displayMode === FluNavigationView.Minimal
Layout.alignment: Qt.AlignVCenter
onClicked: {
displaMinimalMenu = !displaMinimalMenu
d.enableNavigationPanel = !d.enableNavigationPanel
}
Behavior on Layout.preferredWidth{
NumberAnimation{
duration: 220
}
}
}
Image{
id:image_logo
Layout.preferredHeight: 20
Layout.preferredWidth: 20
source: control.logo
Layout.leftMargin: {
if(btn_nav.visible){
return 12
@ -349,7 +492,7 @@ Item {
}
FluText{
Layout.alignment: Qt.AlignVCenter
text:root.title
text:control.title
Layout.leftMargin: 12
fontStyle: FluText.Body
}
@ -358,12 +501,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
@ -383,19 +526,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){
@ -403,64 +566,74 @@ 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
opacity: {
if(d.isCompactAndNotPanel){
return false
}
return true
}
visible: opacity
Behavior on opacity{
NumberAnimation{
duration: 100
}
}
}
FluIconButton{
visible:opacity
opacity:d.isCompactAndNotPanel
Behavior on opacity{
NumberAnimation{
duration: 220
}
}
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{
@ -475,11 +648,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){
@ -494,24 +682,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){
@ -528,27 +735,92 @@ 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(){
@ -559,12 +831,25 @@ Item {
nav_swipe.push(url)
}
function setCurrentIndex(index){
nav_list.currentIndex = index
}
function getCurrentIndex(){
return nav_list.currentIndex
}
function startPageByItem(data){
var items = getItems();
for(var i=0;i<items.length;i++){
var item = items[i]
if(item.key === data.key){
if(getCurrentIndex() === i){
return
}
setCurrentIndex(i)
if(item.parent && !d.isCompactAndNotPanel){
item.parent.isExpand = true
}
return
}
}
}
}

View File

@ -0,0 +1,112 @@
import QtQuick
import QtQuick.Controls
import FluentUI
import QtQuick.Layouts
Item {
id: control
signal requestPage(int page,int count)
property string previousText: "<上一页"
property string nextText: "下一页>"
property int pageCurrent: 0
property int itemCount: 0
property int pageCount: itemCount>0?Math.ceil(itemCount/__itemPerPage):0
property int __itemPerPage: 10
property int pageButtonCount: 5
property int __pageButtonHalf: Math.floor(pageButtonCount/2)+1
implicitHeight: 40
implicitWidth: content.width
Row{
id: content
height: control.height
spacing: 25
padding: 10
FluToggleButton{
visible: control.pageCount>1
disabled: control.pageCurrent<=1
text:control.previousText
onClicked: {
control.calcNewPage(control.pageCurrent-1);
}
}
Row{
spacing: 5
FluToggleButton{
property int pageNumber:1
visible: control.pageCount>0
enabled: control.pageCurrent>1
selected: pageNumber === control.pageCurrent
text:String(pageNumber)
onClicked: {
control.calcNewPage(pageNumber);
}
}
FluText{
visible: (control.pageCount>control.pageButtonCount&&
control.pageCurrent>control.__pageButtonHalf)
text: "..."
}
Repeater{
id: button_repeator
model: (control.pageCount<2)?0:(control.pageCount>=control.pageButtonCount)?(control.pageButtonCount-2):(control.pageCount-2)
delegate:FluToggleButton{
property int pageNumber: {
return (control.pageCurrent<=control.__pageButtonHalf)
?(2+index)
:(control.pageCount-control.pageCurrent<=control.pageButtonCount-control.__pageButtonHalf)
?(control.pageCount-button_repeator.count+index)
:(control.pageCurrent+2+index-control.__pageButtonHalf)
}
text:String(pageNumber)
selected: pageNumber === control.pageCurrent
onClicked: {
control.calcNewPage(pageNumber);
}
}
}
FluText{
visible: (control.pageCount>control.pageButtonCount&&
control.pageCount-control.pageCurrent>control.pageButtonCount-control.__pageButtonHalf)
text: "..."
}
FluToggleButton{
property int pageNumber:control.pageCount
visible: control.pageCount>1
selected: pageNumber === control.pageCurrent
text:String(pageNumber)
onClicked: {
control.calcNewPage(pageNumber);
}
}
}
FluToggleButton{
visible: control.pageCount>1
disabled: control.pageCurrent>=control.pageCount
text:control.nextText
onClicked: {
control.calcNewPage(control.pageCurrent+1);
}
}
}
function calcNewPage(page)
{
if(!page)
return
let page_num=Number(page)
if(page_num<1||page_num>control.pageCount||page_num===control.pageCurrent)
return
control.pageCurrent=page_num
control.requestPage(page_num,control.__itemPerPage)
}
}

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

@ -9,14 +9,6 @@ Item{
property bool shadow: true
default property alias contentItem: container.data
Rectangle{
id:container
width: control.width
height: control.height
opacity: 0
color:control.color
}
FluShadow{
anchors.fill: container
radius: control.radius[0]
@ -28,6 +20,14 @@ Item{
}
}
Rectangle{
id:container
width: control.width
height: control.height
opacity: 0
color:control.color
}
Canvas {
id: canvas
anchors.fill: parent

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

@ -0,0 +1,108 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import FluentUI
Item{
enum StatusMode {
Loading,
Empty,
Error,
Success
}
default property alias content: container.data
property int statusMode: FluStatusView.Loading
signal errorClicked
Item{
id:container
anchors.fill: parent
visible: statusMode === FluStatusView.Success
}
FluArea{
paddings: 0
border.width: 0
anchors.fill: container
visible: opacity
opacity: statusMode === FluStatusView.Loading
Behavior on opacity {
NumberAnimation { duration: 150 }
}
ColumnLayout{
anchors.centerIn: parent
visible: statusMode === FluStatusView.Loading
FluProgressRing{
indeterminate: true
Layout.alignment: Qt.AlignHCenter
}
FluText{
text:"正在加载..."
Layout.alignment: Qt.AlignHCenter
}
}
}
FluArea{
paddings: 0
border.width: 0
anchors.fill: container
visible: opacity
opacity: statusMode === FluStatusView.Empty
Behavior on opacity {
NumberAnimation { duration: 150 }
}
ColumnLayout{
anchors.centerIn: parent
visible: statusMode === FluStatusView.Empty
FluText{
text:"空空如也"
fontStyle: FluText.BodyStrong
Layout.alignment: Qt.AlignHCenter
}
}
}
FluArea{
paddings: 0
border.width: 0
anchors.fill: container
visible: opacity
opacity: statusMode === FluStatusView.Error
Behavior on opacity {
NumberAnimation { duration: 150 }
}
ColumnLayout{
anchors.centerIn: parent
FluText{
text:"页面出错了..."
fontStyle: FluText.BodyStrong
Layout.alignment: Qt.AlignHCenter
}
FluFilledButton{
id:btn_error
Layout.alignment: Qt.AlignHCenter
text:"重新加载"
onClicked:{
errorClicked.call()
}
}
}
}
function showSuccessView(){
statusMode = FluStatusView.Success
}
function showLoadingView(){
statusMode = FluStatusView.Loading
}
function showEmptyView(){
statusMode = FluStatusView.Empty
}
function showErrorView(){
statusMode = FluStatusView.Error
}
}

View File

@ -0,0 +1,193 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import FluentUI
Item {
id:control
property var columns : []
property var dataSource : []
property int pageCurrent: 1
property int itemCount: 1000
property int pageCount: 10
property int itemHeight: 56
signal requestPage(int page,int count)
implicitHeight: layout_coumns.height + layout_table.height
MouseArea{
anchors.fill: parent
preventStealing: true
}
ListModel{
id:model_coumns
}
ListModel{
id:model_data_source
}
onColumnsChanged: {
model_coumns.clear()
model_coumns.append(columns)
}
onDataSourceChanged: {
model_data_source.clear()
model_data_source.append(dataSource)
}
FluRectangle{
id:layout_coumns
height: control.itemHeight
width: parent.width
color:FluTheme.dark ? Qt.rgba(50/255,50/255,50/255,1) : Qt.rgba(247/255,247/255,247/255,1)
radius: [12,12,0,0]
Row{
id:list_coumns
spacing: 0
anchors.fill: parent
Repeater{
model: model_coumns
delegate: Item{
height: list_coumns.height
width: model.width
FluText{
text:model.title
anchors{
verticalCenter: parent.verticalCenter
left: parent.left
leftMargin: 14
}
fontStyle: FluText.BodyStrong
}
FluDivider{
width: 1
height: 40
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
visible: index !== list_coumns.count-1
}
}
}
}
}
Rectangle{
anchors.fill: layout_table
color: FluTheme.dark ? Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(251/255,251/255,253/255,1)
}
ListView{
id:layout_table
anchors{
top: layout_coumns.bottom
left: parent.left
right: parent.right
}
height: contentHeight
clip:true
footer: Item{
height: 50
width: layout_table.width
FluPagination{
id:pagination
height: 40
pageCurrent: control.pageCurrent
itemCount: control.itemCount
pageCount: control.pageCount
onRequestPage:
(page,count)=> {
control.requestPage(page,count)
}
anchors{
verticalCenter: parent.verticalCenter
right: parent.right
}
}
}
model:model_data_source
delegate: Item{
height: list_coumns.height
width: layout_table.width
property var model_values : getObjectValues(index)
property var itemObject: getObject(index)
property var listModel: model
Row{
spacing: 0
anchors.fill: parent
Repeater{
model: model_values
delegate:Item{
height: list_coumns.height
width: modelData.width
Loader{
property var model : modelData
property var dataModel : listModel
property var dataObject : itemObject
anchors.fill: parent
sourceComponent: {
if(model.itemData instanceof Component){
return model.itemData
}
return com_text
}
}
}
}
}
FluDivider{
width: parent.width
height: 1
anchors.right: parent.right
anchors.bottom: parent.bottom
}
}
}
Component{
id:com_text
Item{
MouseArea{
id:item_mouse
hoverEnabled: true
anchors.fill: parent
}
FluText{
text:String(model.itemData)
width: parent.width - 14
elide: Text.ElideRight
anchors{
verticalCenter: parent.verticalCenter
left: parent.left
leftMargin: 14
}
FluTooltip{
visible: item_mouse.containsMouse
text:parent.text
delay: 1000
}
}
}
}
function getObject(index){
return model_data_source.get(index)
}
function getObjectValues(index) {
var obj = model_data_source.get(index)
if(!obj)
return
var data = []
for(var i=0;i<model_coumns.count;i++){
var item = model_coumns.get(i)
data.push({itemData:obj[item.dataIndex],width:item.width})
}
return data;
}
}

View File

@ -4,7 +4,7 @@ import FluentUI
Text {
property int fontStyle: FluText.Body
property color textColor: FluTheme.dark ? "#FFFFFF" : "#1A1A1A"
property color textColor: FluTheme.dark ? FluColors.White : FluColors.Grey220
property int pixelSize : FluTheme.textSize
enum FontStyle {

View File

@ -6,7 +6,8 @@ FluControl {
property bool disabled: false
property color normalColor: FluTheme.dark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark
property color hoverColor: FluTheme.dark ? Qt.darker(normalColor,1.3) : Qt.lighter(normalColor,1.3)
property color hoverColor: FluTheme.dark ? Qt.darker(normalColor,1.15) : Qt.lighter(normalColor,1.15)
property color pressedColor: FluTheme.dark ? Qt.darker(normalColor,1.3) : Qt.lighter(normalColor,1.3)
property color disableColor: FluTheme.dark ? Qt.rgba(82/255,82/255,82/255,1) : Qt.rgba(199/255,199/255,199/255,1)
property bool textBold: true
@ -36,6 +37,9 @@ FluControl {
if(disabled){
return disableColor
}
if(pressed){
return pressedColor
}
return hovered ? hoverColor :normalColor
}
}

View File

@ -0,0 +1,100 @@
import QtQuick
import QtQuick.Controls
import FluentUI
FluControl {
property bool disabled: false
property bool selected: false
property color normalColor: {
if(selected){
return FluTheme.dark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark
}else{
return FluTheme.dark ? Qt.rgba(62/255,62/255,62/255,1) : Qt.rgba(254/255,254/255,254/255,1)
}
}
property color hoverColor: {
if(selected){
return FluTheme.dark ? Qt.darker(normalColor,1.1) : Qt.lighter(normalColor,1.1)
}else{
return FluTheme.dark ? Qt.rgba(68/255,68/255,68/255,1) : Qt.rgba(251/255,251/255,251/255,1)
}
}
property color disableColor: {
if(selected){
return FluTheme.dark ? Qt.rgba(82/255,82/255,82/255,1) : Qt.rgba(199/255,199/255,199/255,1)
}else{
return FluTheme.dark ? Qt.rgba(59/255,59/255,59/255,1) : Qt.rgba(252/255,252/255,252/255,1)
}
}
property color pressedColor: FluTheme.dark ? Qt.darker(normalColor,1.2) : Qt.lighter(normalColor,1.2)
id: control
enabled: !disabled
topPadding:5
bottomPadding:5
leftPadding:15
rightPadding:15
Keys.onSpacePressed: control.visualFocus&&clicked()
focusPolicy:Qt.TabFocus
background: Rectangle{
radius: 4
border.color: FluTheme.dark ? "#505050" : "#DFDFDF"
border.width: selected ? 0 : 1
FluFocusRectangle{
visible: control.visualFocus
radius:8
}
color:{
if(disabled){
return disableColor
}
if(selected){
if(pressed){
return pressedColor
}
}
return hovered ? hoverColor :normalColor
}
}
contentItem: FluText {
text: control.text
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
color: {
if(selected){
if(FluTheme.dark){
if(disabled){
return Qt.rgba(173/255,173/255,173/255,1)
}
return Qt.rgba(0,0,0,1)
}else{
return Qt.rgba(1,1,1,1)
}
}else{
if(FluTheme.dark){
if(disabled){
return Qt.rgba(131/255,131/255,131/255,1)
}
if(!selected){
if(pressed){
return Qt.rgba(162/255,162/255,162/255,1)
}
}
return Qt.rgba(1,1,1,1)
}else{
if(disabled){
return Qt.rgba(160/255,160/255,160/255,1)
}
if(!selected){
if(pressed){
return Qt.rgba(96/255,96/255,96/255,1)
}
}
return Qt.rgba(0,0,0,1)
}
}
}
}
}

View File

@ -1,46 +1,23 @@
import QtQuick
import QtQuick.Window
import QtQuick.Controls
import QtQuick.Layouts
import FluentUI
Item {
ApplicationWindow {
property string title: "FluentUI"
property int minimumWidth
property int maximumWidth
property int minimumHeight
property int maximumHeight
property int modality:0
signal initArgument(var argument)
property var pageRegister
id:window
default property alias content: container.data
property var window : {
if(Window.window == null)
return null
return Window.window
}
property color color: {
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)
property var argument:({})
property var pageRegister
signal initArgument(var argument)
background: Rectangle{
color: {
if(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)
}
return FluTheme.dark ? Qt.rgba(32/255,32/255,32/255,1) : Qt.rgba(243/255,243/255,243/255,1)
}
id:root
Behavior on opacity{
NumberAnimation{
duration: 100
}
}
Rectangle{
id:container
color:root.color
anchors.fill: parent
anchors.margins: (window && (window.visibility === Window.Maximized) && FluTheme.frameless) ? 8/Screen.devicePixelRatio : 0
clip: true
Behavior on color{
ColorAnimation {
duration: 300
@ -48,47 +25,31 @@ Item {
}
}
Rectangle{
border.width: 1
Item{
id:container
anchors.fill: parent
color: Qt.rgba(0,0,0,0,)
border.color:FluTheme.dark ? Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(226/255,230/255,234/255,1)
anchors.margins: window.visibility === Window.Maximized && FluTheme.frameless ? 8/Screen.devicePixelRatio : 0
clip: true
}
Connections{
target: FluApp
function onWindowReady(view){
if(FluApp.equalsWindow(view,window)){
helper.initWindow(view)
initArgument(helper.getArgument())
pageRegister = helper.getPageRegister()
helper.setTitle(title)
if(minimumWidth){
helper.setMinimumWidth(minimumWidth)
}
if(maximumWidth){
helper.setMaximumWidth(maximumWidth)
}
if(minimumHeight){
helper.setMinimumHeight(minimumHeight)
}
if(maximumHeight){
helper.setMaximumHeight(maximumHeight)
}
helper.setModality(root.modality);
helper.updateWindow()
}
onClosing:
(event)=>{
//销毁窗口,释放资源
helper.destoryWindow()
}
FluInfoBar{
id:infoBar
root: window
}
WindowHelper{
id:helper
}
FluInfoBar{
id:infoBar
root: root
Component.onCompleted: {
helper.initWindow(window)
initArgument(argument)
}
function showSuccess(text,duration,moremsg){
@ -107,10 +68,6 @@ Item {
infoBar.showError(text,duration,moremsg);
}
function close(){
window.close()
}
function registerForPageResult(path){
return helper.createRegister(path)
}

View File

@ -67,5 +67,10 @@
<file>controls/FluControl.qml</file>
<file>controls/FluTextFiled.qml</file>
<file>controls/FluTextArea.qml</file>
<file>controls/FluTableView.qml</file>
<file>controls/FluPagination.qml</file>
<file>controls/FluToggleButton.qml</file>
<file>controls/FluStatusView.qml</file>
<file>controls/FluPaneItemEmpty.qml</file>
</qresource>
</RCC>

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
third/mingw/libstdc++-6.dll Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.