This commit is contained in:
zhuzihcu 2023-03-02 18:21:43 +08:00
parent 91be0e4da2
commit 1b5223a7d5
23 changed files with 746 additions and 361 deletions

12
example/InstallHelper.cpp Normal file
View File

@ -0,0 +1,12 @@
#include "InstallHelper.h"
InstallHelper::InstallHelper(QObject *parent)
: QObject{parent}
{
installing(false);
}
void InstallHelper::install(const QString& path,bool isHome,bool isStartMenu){
installing(true);
qDebug()<<path;
}

20
example/InstallHelper.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef INSTALLHELPER_H
#define INSTALLHELPER_H
#include <QObject>
#include <QDebug>
#include "stdafx.h"
class InstallHelper : public QObject
{
Q_OBJECT
Q_PROPERTY_AUTO(bool,installing)
public:
explicit InstallHelper(QObject *parent = nullptr);
Q_INVOKABLE void install(const QString& path,bool isHome,bool isStartMenu);
signals:
};
#endif // INSTALLHELPER_H

View File

@ -1,5 +1,8 @@
import QtQuick 2.15 import QtQuick 2.15
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
import QtQuick.Dialogs 1.3 as Dialogs
import Qt.labs.platform 1.1
import UI 1.0
import FluentUI 1.0 import FluentUI 1.0
FluWindow { FluWindow {
@ -7,15 +10,37 @@ FluWindow {
id:window id:window
width: 800 width: 800
height: 400 height: 400
maximumSize: Qt.size(800,400) minimumWidth:800
minimumSize: Qt.size(800,400) maximumWidth:800
minimumHeight:400
maximumHeight:400
title:"安装向导" title:"安装向导"
property string installPath: "C:\\Program Files"
property string installName: "FluentUI"
FluAppBar{ FluAppBar{
id:appbar id:appbar
title: "安装向导" title: "安装向导"
} }
Item{
id:data
InstallHelper{
id:helper
}
Dialogs.FileDialog {
id: fileDialog
selectFolder: true
folder: "file:///"+installPath
onAccepted: {
installPath = String(fileDialog.fileUrls[0]).replace("file:///","").replace(RegExp("/",'g'),"\\")
}
}
}
ColumnLayout{ ColumnLayout{
width: parent.width width: parent.width
@ -36,17 +61,36 @@ FluWindow {
} }
FluTextBox{ FluTextBox{
id:textbox_path
Layout.preferredHeight: 40 Layout.preferredHeight: 40
Layout.fillWidth: true Layout.fillWidth: true
text:"C:\\Program Files\\RustDesk" text:installPath+ "\\" +installName
readOnly:true readOnly:true
} }
FluButton{ FluButton{
text:"更改路径" text:"更改路径"
Layout.rightMargin: 30 Layout.rightMargin: 30
onClicked: {
fileDialog.open()
} }
} }
}
FluCheckBox{
id:checkbox_startmenu
Layout.topMargin: 20
Layout.leftMargin: 30
checked: true
text:"创建启动菜单快捷方式"
}
FluCheckBox{
id:checkbox_home
Layout.leftMargin: 30
Layout.topMargin: 5
checked: true
text:"创建桌面图标"
}
Item{ Item{
width: 1 width: 1
@ -77,6 +121,9 @@ FluWindow {
} }
FluFilledButton{ FluFilledButton{
text:"同意并安装" text:"同意并安装"
onClicked: {
helper.install(textbox_path.text,checkbox_home.checked,checkbox_startmenu.checked)
}
} }
FluButton{ FluButton{
text:"不安装直接运行" text:"不安装直接运行"
@ -88,4 +135,34 @@ FluWindow {
} }
} }
} }
Rectangle{
anchors.fill: parent
visible: helper.installing
color: "#80000000"
MouseArea{
anchors.fill: parent
hoverEnabled: true
}
FluProgressBar{
id:progress
anchors.centerIn: parent
}
FluText{
text:"正在安装..."
color:"#FFFFFF"
font.pixelSize: 20
anchors{
horizontalCenter: progress.horizontalCenter
bottom: progress.top
bottomMargin: 10
}
}
}
} }

View File

@ -3,6 +3,7 @@ import QtQuick.Window 2.15
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
import QtGraphicalEffects 1.15 import QtGraphicalEffects 1.15
import FluentUI 1.0 import FluentUI 1.0
FluWindow { FluWindow {
@ -10,14 +11,17 @@ FluWindow {
width: 800 width: 800
height: 600 height: 600
title: "FluentUI" title: "FluentUI"
minimumSize: Qt.size(600,400) minimumWidth: 600
// property var maximumSize minimumHeight: 400
FluAppBar{ FluAppBar{
id:appbar id:appbar
title: "FluentUI" title: "FluentUI"
} }
Item{
id:data
ListModel{ ListModel{
id:nav_items id:nav_items
ListElement{ ListElement{
@ -58,14 +62,40 @@ FluWindow {
} }
} }
FluMenu{
id:menu
FluMenuItem{
text:"123"
}
FluMenuItem{
text:"456"
}
}
}
FluIconButton{
icon:FluentIcons.FA_navicon
anchors{
left: parent.left
bottom: parent.bottom
leftMargin: 12
bottomMargin: 12
}
onClicked:{
menu.popup()
}
}
ListView{ ListView{
id:nav_list id:nav_list
anchors{ anchors{
top: appbar.bottom top: appbar.bottom
bottom: parent.bottom bottom: parent.bottom
topMargin: 20 topMargin: 20
bottomMargin: 20 bottomMargin: 52
} }
clip: true
width: 160 width: 160
model: nav_items model: nav_items
delegate: Item{ delegate: Item{

View File

@ -1,4 +1,5 @@
import QtQuick 2.15 import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
import QtQuick.Window 2.15 import QtQuick.Window 2.15
import FluentUI 1.0 import FluentUI 1.0
@ -13,18 +14,32 @@ Item {
FluTextBox{ FluTextBox{
id:text_box id:text_box
placeholderText: "搜索" placeholderText: "请输入关键字"
anchors{ anchors{
topMargin: 20 topMargin: 20
top:title.bottom top:title.bottom
} }
} }
FluFilledButton{
text:"搜索"
anchors{
left: text_box.right
verticalCenter: text_box.verticalCenter
leftMargin: 14
}
onClicked: {
grid_view.model = FluApp.awesomelist(text_box.text)
}
}
GridView{ GridView{
id:grid_view
cellWidth: 120 cellWidth: 120
cellHeight: 60 cellHeight: 60
clip: true clip: true
model:FluApp.awesomelist() model:FluApp.awesomelist()
ScrollBar.vertical: FluScrollBar {}
anchors{ anchors{
topMargin: 10 topMargin: 10
top:text_box.bottom top:text_box.bottom

View File

@ -4,6 +4,7 @@ CONFIG += c++11
DEFINES += QT_DEPRECATED_WARNINGS QT_NO_WARNING_OUTPUT DEFINES += QT_DEPRECATED_WARNINGS QT_NO_WARNING_OUTPUT
SOURCES += \ SOURCES += \
InstallHelper.cpp \
main.cpp main.cpp
RESOURCES += qml.qrc RESOURCES += qml.qrc
@ -42,3 +43,7 @@ else: unix:!android: target.path = /opt/$${TARGET}/bin
# PRE_TARGETDEPS += $$OUT_PWD/../bin/FluentUI/lib$${LIBNAME}.a # PRE_TARGETDEPS += $$OUT_PWD/../bin/FluentUI/lib$${LIBNAME}.a
### 注意:静态库 .so .dylib .dll 是自动安装的Qt qml plugin目录中不需要此步配置 ### 注意:静态库 .so .dylib .dll 是自动安装的Qt qml plugin目录中不需要此步配置
HEADERS += \
InstallHelper.h \
stdafx.h

View File

@ -1,5 +1,6 @@
#include <QGuiApplication> #include <QGuiApplication>
#include <QQmlApplicationEngine> #include <QQmlApplicationEngine>
#include "InstallHelper.h"
#if defined(STATICLIB) #if defined(STATICLIB)
#include <FluentUI.h> #include <FluentUI.h>
@ -7,6 +8,10 @@
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
QCoreApplication::setOrganizationName("ZhuZiChu");
QCoreApplication::setOrganizationDomain("https://zhuzichu520.github.io");
QCoreApplication::setApplicationName("FluentUI");
qputenv("QSG_RENDER_LOOP","basic"); qputenv("QSG_RENDER_LOOP","basic");
QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
@ -19,6 +24,9 @@ int main(int argc, char *argv[])
// QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); // QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv); QGuiApplication app(argc, argv);
QQmlApplicationEngine engine; QQmlApplicationEngine engine;
qmlRegisterType<InstallHelper>("UI",1,0,"InstallHelper");
#if defined(STATICLIB) #if defined(STATICLIB)
FluentUI::create(&engine); FluentUI::create(&engine);
#endif #endif

21
example/stdafx.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef STDAFX_H
#define STDAFX_H
#define Q_PROPERTY_AUTO(TYPE, M) \
Q_PROPERTY(TYPE M MEMBER _##M NOTIFY M##Changed) \
public: \
Q_SIGNAL void M##Changed(); \
void M(TYPE in_##M) \
{ \
_##M = in_##M; \
Q_EMIT M##Changed(); \
} \
TYPE M() \
{ \
return _##M; \
} \
\
private: \
TYPE _##M;
#endif // STDAFX_H

View File

@ -26,8 +26,6 @@ FluApp::FluApp(QObject *parent)
isFps(true); isFps(true);
} }
void FluApp::setAppWindow(QWindow *window){ void FluApp::setAppWindow(QWindow *window){
appWindow = window; appWindow = window;
} }
@ -43,7 +41,7 @@ void FluApp::navigate(const QString& route){
} }
bool isAppWindow = route==initialRoute(); bool isAppWindow = route==initialRoute();
FramelessView *view = new FramelessView(); FramelessView *view = new FramelessView();
view->setColor(isDark() ? QColor(0,0,0,1) : QColor(255, 255, 255, 1)); view->setColor(QColor(0,0,0,0));
QObject::connect(view, &QQuickView::statusChanged, view, [&](QQuickView::Status status) { QObject::connect(view, &QQuickView::statusChanged, view, [&](QQuickView::Status status) {
if (status == QQuickView::Status::Ready) { if (status == QQuickView::Status::Ready) {
Q_EMIT windowReady(view); Q_EMIT windowReady(view);
@ -65,17 +63,26 @@ bool FluApp::equalsWindow(FramelessView *view,QWindow *window){
return view->winId() == window->winId(); return view->winId() == window->winId();
} }
QJsonArray FluApp::awesomelist() QJsonArray FluApp::awesomelist(const QString& keyword)
{ {
QJsonArray arr; QJsonArray arr;
QMetaEnum enumType = Fluent_Awesome::staticMetaObject.enumerator(Fluent_Awesome::staticMetaObject.indexOfEnumerator("Fluent_AwesomeType")); QMetaEnum enumType = Fluent_Awesome::staticMetaObject.enumerator(Fluent_Awesome::staticMetaObject.indexOfEnumerator("Fluent_AwesomeType"));
for(int i=0; i < enumType.keyCount(); ++i){ for(int i=0; i < enumType.keyCount(); ++i){
QJsonObject obj;
QString name = enumType.key(i); QString name = enumType.key(i);
int icon = enumType.value(i); int icon = enumType.value(i);
if(keyword.isEmpty()){
QJsonObject obj;
obj.insert("name",name); obj.insert("name",name);
obj.insert("icon",icon); obj.insert("icon",icon);
arr.append(obj); arr.append(obj);
}else{
if(name.mid(3).contains(keyword)){
QJsonObject obj;
obj.insert("name",name);
obj.insert("icon",icon);
arr.append(obj);
}
}
} }
return arr; return arr;
} }

View File

@ -32,7 +32,7 @@ public:
Q_INVOKABLE bool equalsWindow(FramelessView *view,QWindow *window); Q_INVOKABLE bool equalsWindow(FramelessView *view,QWindow *window);
Q_INVOKABLE QJsonArray awesomelist(); Q_INVOKABLE QJsonArray awesomelist(const QString& keyword = "");
Q_INVOKABLE void clipText(const QString& text); Q_INVOKABLE void clipText(const QString& text);

View File

@ -30,6 +30,10 @@ void Fluent::registerTypes(const char *uri){
qmlRegisterType<WindowHelper>(uri,major,minor,"WindowHelper"); qmlRegisterType<WindowHelper>(uri,major,minor,"WindowHelper");
qmlRegisterType(QUrl("qrc:/com.zhuzichu/controls/FluMenu.qml"),uri,major,minor,"FluMenu");
qmlRegisterType(QUrl("qrc:/com.zhuzichu/controls/FluMenuItem.qml"),uri,major,minor,"FluMenuItem");
qmlRegisterType(QUrl("qrc:/com.zhuzichu/controls/FluScrollBar.qml"),uri,major,minor,"FluScrollBar");
qmlRegisterType(QUrl("qrc:/com.zhuzichu/controls/FluMultiLineTextBox.qml"),uri,major,minor,"FluMultiLineTextBox"); qmlRegisterType(QUrl("qrc:/com.zhuzichu/controls/FluMultiLineTextBox.qml"),uri,major,minor,"FluMultiLineTextBox");
qmlRegisterType(QUrl("qrc:/com.zhuzichu/controls/FluDropShadow.qml"),uri,major,minor,"FluDropShadow"); qmlRegisterType(QUrl("qrc:/com.zhuzichu/controls/FluDropShadow.qml"),uri,major,minor,"FluDropShadow");
qmlRegisterType(QUrl("qrc:/com.zhuzichu/controls/FluTooltip.qml"),uri,major,minor,"FluTooltip"); qmlRegisterType(QUrl("qrc:/com.zhuzichu/controls/FluTooltip.qml"),uri,major,minor,"FluTooltip");

View File

@ -9,6 +9,7 @@ class FramelessViewPrivate
public: public:
bool m_isMax = false; bool m_isMax = false;
bool m_isFull = false; bool m_isFull = false;
bool m_deleteLater = false;
QQuickItem *m_titleItem = nullptr; QQuickItem *m_titleItem = nullptr;
}; };
FramelessView::FramelessView(QWindow *parent) : Super(parent), d(new FramelessViewPrivate) FramelessView::FramelessView(QWindow *parent) : Super(parent), d(new FramelessViewPrivate)
@ -58,6 +59,10 @@ void FramelessView::moveToScreenCenter()
setGeometry(geo); setGeometry(geo);
update(); update();
} }
void FramelessView::closeDeleteLater(){
d->m_deleteLater = true;
}
bool FramelessView::isMax() const bool FramelessView::isMax() const
{ {
return d->m_isMax; return d->m_isMax;
@ -95,7 +100,6 @@ bool FramelessView::nativeEvent(const QByteArray &eventType, void *message, qint
#else #else
bool FramelessView::nativeEvent(const QByteArray &eventType, void *message, long *result) bool FramelessView::nativeEvent(const QByteArray &eventType, void *message, long *result)
#endif #endif
{ {
return Super::nativeEvent(eventType, message, result); return Super::nativeEvent(eventType, message, result);
} }

View File

@ -1,93 +1,17 @@
#include "FramelessView.h" #include "FramelessView.h"
#include <QGuiApplication> #include <QGuiApplication>
#include <QQuickItem> #include <QQuickItem>
#include <QScreen> #include <QScreen>
#include <QWindow> #include <QWindow>
#include <VersionHelpers.h> class FramelessViewPrivate
#include <WinUser.h>
#include <dwmapi.h>
#include <objidl.h> // Fixes error C2504: 'IUnknown' : base class undefined
#include <windows.h>
#include <windowsx.h>
#include <wtypes.h>
#pragma comment(lib, "Dwmapi.lib") // Adds missing library, fixes error LNK2019: unresolved
#pragma comment(lib, "User32.lib")
#pragma comment(lib, "Gdi32.lib")
// we cannot just use WS_POPUP style
// WS_THICKFRAME: without this the window cannot be resized and so aero snap, de-maximizing and minimizing won't work
// WS_SYSMENU: enables the context menu with the move, close, maximize, minize... commands (shift + right-click on the task bar item)
// WS_CAPTION: enables aero minimize animation/transition
// WS_MAXIMIZEBOX, WS_MINIMIZEBOX: enable minimize/maximize
QMap<WId,FramelessView*>* FramelessView::windowCache = new QMap<WId,FramelessView*>;
enum class Style : DWORD
{ {
windowed = WS_OVERLAPPEDWINDOW | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX, public:
aero_borderless = WS_POPUP | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX, bool m_isMax = false;
basic_borderless = WS_POPUP | WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX bool m_isFull = false;
bool m_deleteLater = false;
QQuickItem *m_titleItem = nullptr;
}; };
static bool isCompositionEnabled()
{
BOOL composition_enabled = FALSE;
bool success = ::DwmIsCompositionEnabled(&composition_enabled) == S_OK;
return composition_enabled && success;
}
static Style selectBorderLessStyle()
{
return isCompositionEnabled() ? Style::aero_borderless : Style::basic_borderless;
}
static void setShadow(HWND handle, bool enabled)
{
if (isCompositionEnabled())
{
static const MARGINS shadow_state[2] { { 0, 0, 0, 0 }, { 1, 1, 1, 1 } };
::DwmExtendFrameIntoClientArea(handle, &shadow_state[enabled]);
}
}
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;
}
}
static bool isMaxWin(QWindow* win) static bool isMaxWin(QWindow* win)
{ {
@ -98,53 +22,10 @@ static bool isFullWin(QQuickView* win)
return win->windowState() == Qt::WindowFullScreen; return win->windowState() == Qt::WindowFullScreen;
} }
class FramelessViewPrivate
{
public:
bool m_firstRun = true;
bool m_isMax = false;
bool m_isFull = false;
bool m_deleteLater = false;
QQuickItem* m_titleItem = nullptr;
HMENU mMenuHandler = NULL;
bool borderless = true; // is the window currently borderless
bool borderless_resize = true; // should the window allow resizing by dragging the borders while borderless
bool borderless_drag = true; // should the window allow moving my dragging the client area
bool borderless_shadow = true; // should the window display a native aero shadow while borderless
void setBorderLess(HWND handle, bool enabled)
{
auto newStyle = enabled ? selectBorderLessStyle() : Style::windowed;
auto oldStyle = static_cast<Style>(::GetWindowLongPtrW(handle, GWL_STYLE));
if (oldStyle != newStyle)
{
borderless = enabled;
//todo 有待研究这个
// ::SetWindowLongPtrW(handle, GWL_STYLE, static_cast<LONG>(newStyle));
// when switching between borderless and windowed, restore appropriate shadow state FramelessView::FramelessView(QWindow *parent) : Super(parent), d(new FramelessViewPrivate)
setShadow(handle, borderless_shadow && (newStyle != Style::windowed));
// redraw frame
::SetWindowPos(handle, nullptr, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE);
::ShowWindow(handle, SW_SHOW);
}
}
void setBorderLessShadow(HWND handle, bool enabled)
{ {
if (borderless) setFlags(Qt::CustomizeWindowHint | Qt::Window | Qt::FramelessWindowHint | Qt::WindowMinMaxButtonsHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint);
{
borderless_shadow = enabled;
setShadow(handle, enabled);
}
}
};
FramelessView::FramelessView(QWindow* parent)
: QQuickView(parent)
, d(new FramelessViewPrivate)
{
//此处不需要设置flags
// setFlags(Qt::CustomizeWindowHint | Qt::Window | Qt::FramelessWindowHint | Qt::WindowMinMaxButtonsHint | Qt::WindowTitleHint |
// Qt::WindowSystemMenuHint);
setResizeMode(SizeRootObjectToView); setResizeMode(SizeRootObjectToView);
setIsMax(windowState() == Qt::WindowMaximized); setIsMax(windowState() == Qt::WindowMaximized);
@ -154,65 +35,14 @@ FramelessView::FramelessView(QWindow* parent)
setIsMax(windowState() == Qt::WindowMaximized); setIsMax(windowState() == Qt::WindowMaximized);
setIsFull(windowState() == Qt::WindowFullScreen); setIsFull(windowState() == Qt::WindowFullScreen);
}); });
QObject::connect(this, &QQuickView::statusChanged, this, [&](QQuickView::Status status) {
if (status == QQuickView::Status::Ready) {
FramelessView::windowCache->insert(this->winId(),this);
}
});
}
void FramelessView::showEvent(QShowEvent* e)
{
if (d->m_firstRun)
{
d->m_firstRun = false;
//第一次show的时候设置无边框。不在构造函数中设置。取winId会触发QWindowsWindow::create,直接创建win32窗口,引起错乱(win7 或者虚拟机启动即黑屏)。
d->setBorderLess((HWND)(winId()), d->borderless);
{
// Qt 5.15.2 的bug; 问题复现及解决方法当使用WM_NCCALCSIZE 修改非客户区大小后移动窗口到其他屏幕时qwindows.dll 源码 qwindowswindow.cpp:2447
// updateFullFrameMargins() 函数 处会调用qwindowswindow.cpp:2453 的
// calculateFullFrameMargins函数重新获取默认的非客户区大小导致最外层窗口移动屏幕时会触发resize消息引起40像素左右的黑边故此处创建Menu
// 使其调用qwindowswindow.cpp:2451 的 QWindowsContext::forceNcCalcSize() 函数计算非客户区大小
//已知负面效果: 引入win32 MENU后Qt程序中如果有alt开头的快捷键会不生效被Qt滤掉了需要修改Qt源码
// QWindowsKeyMapper::translateKeyEventInternal 中的
// if (msgType == WM_SYSKEYDOWN && (nModifiers & AltAny) != 0 && GetMenu(msg.hwnd) != nullptr)
// return false;
// 这两行屏蔽掉
d->mMenuHandler = ::CreateMenu();
::SetMenu((HWND)winId(), d->mMenuHandler);
}
}
Super::showEvent(e);
} }
FramelessView::~FramelessView() FramelessView::~FramelessView()
{ {
if (d->mMenuHandler != NULL)
{
::DestroyMenu(d->mMenuHandler);
}
FramelessView::windowCache->remove(this->winId());
delete d; delete d;
} }
void FramelessView::showEvent(QShowEvent *e)
bool FramelessView::isMax() const
{ {
return d->m_isMax; Super::showEvent(e);
}
bool FramelessView::isFull() const
{
return d->m_isFull;
}
QQuickItem* FramelessView::titleItem() const
{
return d->m_titleItem;
}
void FramelessView::setTitleItem(QQuickItem* item)
{
d->m_titleItem = item;
} }
QRect FramelessView::calcCenterGeo(const QRect &screenGeo, const QSize &normalSize) QRect FramelessView::calcCenterGeo(const QRect &screenGeo, const QSize &normalSize)
{ {
@ -220,13 +50,11 @@ QRect FramelessView::calcCenterGeo(const QRect& screenGeo, const QSize& normalSi
int h = normalSize.height(); int h = normalSize.height();
int x = screenGeo.x() + (screenGeo.width() - w) / 2; int x = screenGeo.x() + (screenGeo.width() - w) / 2;
int y = screenGeo.y() + (screenGeo.height() - h) / 2; int y = screenGeo.y() + (screenGeo.height() - h) / 2;
if (screenGeo.width() < w) if (screenGeo.width() < w) {
{
x = screenGeo.x(); x = screenGeo.x();
w = screenGeo.width(); w = screenGeo.width();
} }
if (screenGeo.height() < h) if (screenGeo.height() < h) {
{
y = screenGeo.y(); y = screenGeo.y();
h = screenGeo.height(); h = screenGeo.height();
} }
@ -236,18 +64,28 @@ QRect FramelessView::calcCenterGeo(const QRect& screenGeo, const QSize& normalSi
void FramelessView::moveToScreenCenter() void FramelessView::moveToScreenCenter()
{ {
auto geo = calcCenterGeo(screen()->availableGeometry(), size()); auto geo = calcCenterGeo(screen()->availableGeometry(), size());
if (minimumWidth() > geo.width() || minimumHeight() > geo.height()) if (minimumWidth() > geo.width() || minimumHeight() > geo.height()) {
{
setMinimumSize(geo.size()); setMinimumSize(geo.size());
} }
setGeometry(geo); setGeometry(geo);
update(); update();
} }
void FramelessView::closeDeleteLater(){ void FramelessView::closeDeleteLater(){
d->m_deleteLater = true; 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) void FramelessView::setIsMax(bool isMax)
{ {
if (d->m_isMax == isMax) if (d->m_isMax == isMax)
@ -264,50 +102,37 @@ void FramelessView::setIsFull(bool isFull)
d->m_isFull = isFull; d->m_isFull = isFull;
emit isFullChanged(d->m_isFull); emit isFullChanged(d->m_isFull);
} }
void FramelessView::resizeEvent(QResizeEvent* e) void FramelessView::setTitleItem(QQuickItem *item)
{ {
// SetWindowRgn(HWND(winId()), CreateRoundRectRgn(0, 0, width(), height(), 4, 4), true); d->m_titleItem = item;
Super::resizeEvent(e);
} }
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
bool FramelessView::nativeEvent(const QByteArray &eventType, void *message, qintptr *result) bool FramelessView::nativeEvent(const QByteArray &eventType, void *message, qintptr *result)
#else #else
bool FramelessView::nativeEvent(const QByteArray &eventType, void *message, long *result) bool FramelessView::nativeEvent(const QByteArray &eventType, void *message, long *result)
#endif #endif
{ {
const long border_width = 4;
if (!result) if (!result)
{ {
//防御式编程
//一般不会发生这种情况win7一些极端情况会传空指针进来。解决方案是升级驱动、切换到basic主题。
return false; return false;
} }
#if (QT_VERSION == QT_VERSION_CHECK(5, 11, 1)) #if (QT_VERSION == QT_VERSION_CHECK(5, 11, 1))
// Work-around a bug caused by typo which only exists in Qt 5.11.1
const auto msg = *reinterpret_cast<MSG**>(message); const auto msg = *reinterpret_cast<MSG**>(message);
#else #else
const auto msg = static_cast<LPMSG>(message); const auto msg = static_cast<LPMSG>(message);
#endif #endif
if (!msg || !msg->hwnd) if (!msg || !msg->hwnd)
{ {
return false; return false;
} }
switch (msg->message) switch (msg->message)
{ {
case WM_CLOSE:{
if(d->m_deleteLater){
deleteLater();
}
}
case WM_NCCALCSIZE: { case WM_NCCALCSIZE: {
#if 1 #if 1
const auto mode = static_cast<BOOL>(msg->wParam); 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); const auto clientRect = mode ? &(reinterpret_cast<LPNCCALCSIZE_PARAMS>(msg->lParam)->rgrc[0]) : reinterpret_cast<LPRECT>(msg->lParam);
if (mode == TRUE && d->borderless) if (mode == TRUE)
{ {
*result = WVR_REDRAW; *result = WVR_REDRAW;
//规避 拖动border进行resize时界面闪烁 //规避 拖动border进行resize时界面闪烁
@ -333,51 +158,11 @@ bool FramelessView::nativeEvent(const QByteArray& eventType, void* message, long
#endif #endif
break; break;
} }
case WM_NCACTIVATE: {
if (!isCompositionEnabled())
{
// Prevents window frame reappearing on window activation
// in "basic" theme, where no aero shadow is present.
*result = 1;
return true;
}
break;
}
case WM_NCHITTEST: {
if (d->borderless)
{
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))
{ //非最大化、非全屏时,进行命中测试,处理边框拖拽
if(!((maximumHeight()==minimumHeight())&&(maximumWidth()==minimumWidth()))){
*result = hitTest(winrect, x, y, border_width);
if (0 != *result)
{
return true;
}
}
}
if (d->m_titleItem)
{
auto titlePos = d->m_titleItem->mapToGlobal({ 0, 0 });
titlePos = mapFromGlobal(titlePos.toPoint());
auto titleRect = QRect(titlePos.x(), titlePos.y(), d->m_titleItem->width(), d->m_titleItem->height());
double dpr = qApp->devicePixelRatio();
QPoint pos = mapFromGlobal(QPoint(x / dpr, y / dpr));
if (titleRect.contains(pos))
{
*result = HTCAPTION;
return true;
}
}
return false;
}
break;
} // end case WM_NCHITTEST
} }
return Super::nativeEvent(eventType, message, result); return Super::nativeEvent(eventType, message, result);
} }
void FramelessView::resizeEvent(QResizeEvent *e)
{
Super::resizeEvent(e);
}

View File

@ -15,14 +15,15 @@ void WindowHelper::initWindow(FramelessView* window){
this->window = window; this->window = window;
} }
void WindowHelper::setMinimumSize(const QSize &size){ void WindowHelper::setMinimumWidth(int width){
this->window->setMinimumSize(size); this->window->setMinimumWidth(width);
} }
void WindowHelper::setMaximumSize(const QSize &size){ void WindowHelper::setMaximumWidth(int width){
this->window->setMaximumSize(size); this->window->setMaximumWidth(width);
} }
void WindowHelper::setMinimumHeight(int height){
void WindowHelper::refreshWindow(){ this->window->setMinimumHeight(height);
this->window->setFlag(Qt::NoDropShadowWindowHint,true); }
this->window->setFlag(Qt::NoDropShadowWindowHint,false); void WindowHelper::setMaximumHeight(int height){
this->window->setMaximumHeight(height);
} }

View File

@ -16,9 +16,10 @@ public:
Q_INVOKABLE void initWindow(FramelessView* window); Q_INVOKABLE void initWindow(FramelessView* window);
Q_INVOKABLE void setTitle(const QString& text); Q_INVOKABLE void setTitle(const QString& text);
Q_INVOKABLE void setMinimumSize(const QSize &size); Q_INVOKABLE void setMinimumWidth(int width);
Q_INVOKABLE void setMaximumSize(const QSize &size); Q_INVOKABLE void setMaximumWidth(int width);
Q_INVOKABLE void refreshWindow(); Q_INVOKABLE void setMinimumHeight(int height);
Q_INVOKABLE void setMaximumHeight(int height);
private: private:
FramelessView* window; FramelessView* window;

View File

@ -818,14 +818,21 @@ Module {
Parameter { name: "text"; type: "string" } Parameter { name: "text"; type: "string" }
} }
Method { Method {
name: "setMinimumSize" name: "setMinimumWidth"
Parameter { name: "size"; type: "QSize" } Parameter { name: "width"; type: "int" }
} }
Method { Method {
name: "setMaximumSize" name: "setMaximumWidth"
Parameter { name: "size"; type: "QSize" } Parameter { name: "width"; type: "int" }
}
Method {
name: "setMinimumHeight"
Parameter { name: "height"; type: "int" }
}
Method {
name: "setMaximumHeight"
Parameter { name: "height"; type: "int" }
} }
Method { name: "refreshWindow" }
} }
Component { Component {
prototype: "QQuickRectangle" prototype: "QQuickRectangle"
@ -883,6 +890,8 @@ Module {
exportMetaObjectRevisions: [0] exportMetaObjectRevisions: [0]
isComposite: true isComposite: true
defaultProperty: "data" defaultProperty: "data"
Property { name: "checked"; type: "bool" }
Property { name: "text"; type: "string" }
} }
Component { Component {
prototype: "QQuickItem" prototype: "QQuickItem"
@ -1040,6 +1049,22 @@ Module {
} }
Property { name: "children"; type: "QObject"; isList: true; isReadonly: true } Property { name: "children"; type: "QObject"; isList: true; isReadonly: true }
} }
Component {
prototype: "QQuickMenu"
name: "FluentUI/FluMenu 1.0"
exports: ["FluentUI/FluMenu 1.0"]
exportMetaObjectRevisions: [0]
isComposite: true
defaultProperty: "contentData"
}
Component {
prototype: "QQuickMenuItem"
name: "FluentUI/FluMenuItem 1.0"
exports: ["FluentUI/FluMenuItem 1.0"]
exportMetaObjectRevisions: [0]
isComposite: true
defaultProperty: "data"
}
Component { Component {
prototype: "QQuickTextArea" prototype: "QQuickTextArea"
name: "FluentUI/FluMultiLineTextBox 1.0" name: "FluentUI/FluMultiLineTextBox 1.0"
@ -1110,6 +1135,14 @@ Module {
Property { name: "borderWidth"; type: "int" } Property { name: "borderWidth"; type: "int" }
Property { name: "contentItem"; type: "QQuickItem"; isList: true; isReadonly: true } Property { name: "contentItem"; type: "QQuickItem"; isList: true; isReadonly: true }
} }
Component {
prototype: "QQuickScrollBar"
name: "FluentUI/FluScrollBar 1.0"
exports: ["FluentUI/FluScrollBar 1.0"]
exportMetaObjectRevisions: [0]
isComposite: true
defaultProperty: "data"
}
Component { Component {
prototype: "QQuickItem" prototype: "QQuickItem"
name: "FluentUI/FluSlider 1.0" name: "FluentUI/FluSlider 1.0"
@ -1180,18 +1213,21 @@ Module {
defaultProperty: "contentData" defaultProperty: "contentData"
} }
Component { Component {
prototype: "QQuickRectangle" prototype: "QQuickItem"
name: "FluentUI/FluWindow 1.0" name: "FluentUI/FluWindow 1.0"
exports: ["FluentUI/FluWindow 1.0"] exports: ["FluentUI/FluWindow 1.0"]
exportMetaObjectRevisions: [0] exportMetaObjectRevisions: [0]
isComposite: true isComposite: true
defaultProperty: "data" defaultProperty: "content"
Property { name: "isMax"; type: "bool" }
Property { name: "title"; type: "string" }
Property { name: "winId"; type: "string" }
Property { name: "minimumSize"; type: "QVariant" }
Property { name: "maximumSize"; type: "QVariant" }
Property { name: "window"; type: "QVariant" } Property { name: "window"; type: "QVariant" }
Property { name: "color"; type: "QColor" }
Property { name: "title"; type: "string" }
Property { name: "minimumWidth"; type: "int" }
Property { name: "maximumWidth"; type: "int" }
Property { name: "minimumHeight"; type: "int" }
Property { name: "maximumHeight"; type: "int" }
Property { name: "borderless"; type: "int" }
Property { name: "content"; type: "QQuickItem"; isList: true; isReadonly: true }
Method { Method {
name: "showSuccess" name: "showSuccess"
type: "QVariant" type: "QVariant"

View File

@ -1,6 +1,85 @@
import QtQuick 2.15 import QtQuick 2.15
import QtQuick.Layouts 1.15
import FluentUI 1.0 import FluentUI 1.0
Item { Item {
id:root
property bool checked: false
property string text: "Check Box"
width: childrenRect.width
height: childrenRect.height
RowLayout{
spacing: 4
Rectangle{
width: 22
height: 22
radius: 4
border.color: {
if(FluApp.isDark){
if(checked){
return Qt.rgba(76/255,160/255,224/255,1)
}
return Qt.rgba(160/255,160/255,160/255,1)
}else{
if(checked){
if(mouse_area.containsMouse){
return Qt.rgba(25/255,117/255,187/255,1)
}
return Qt.rgba(0/255,102/255,180/255,1)
}
return Qt.rgba(136/255,136/255,136/255,1)
}
}
border.width: 1
color: {
if(FluApp.isDark){
if(checked){
if(mouse_area.containsMouse){
return Qt.rgba(74/255,149/255,207/255,1)
}
return Qt.rgba(76/255,160/255,224/255,1)
}
if(mouse_area.containsMouse){
return Qt.rgba(62/255,62/255,62/255,1)
}
return Qt.rgba(45/255,45/255,45/255,1)
}else{
if(checked){
if(mouse_area.containsMouse){
return Qt.rgba(25/255,117/255,187/255,1)
}
return Qt.rgba(0/255,102/255,180/255,1)
}
if(mouse_area.containsMouse){
return Qt.rgba(244/255,244/255,244/255,1)
}
return Qt.rgba(247/255,247/255,247/255,1)
}
}
FluIcon {
anchors.centerIn: parent
icon: FluentIcons.FA_check
iconSize: 15
visible: checked
color: FluApp.isDark ? Qt.rgba(0,0,0,1) : Qt.rgba(1,1,1,1)
}
}
FluText{
text:root.text
}
}
MouseArea{
id:mouse_area
anchors.fill: parent
hoverEnabled: true
onClicked: {
checked = !checked
}
}
} }

41
src/controls/FluMenu.qml Normal file
View File

@ -0,0 +1,41 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Controls.impl 2.15
import QtQuick.Templates 2.15 as T
import QtQuick.Window 2.15
import QtGraphicalEffects 1.15
T.Menu {
id: control
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
contentWidth + leftPadding + rightPadding)
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
contentHeight + topPadding + bottomPadding)
margins: 0
delegate: FluMenuItem { }
contentItem: ListView {
implicitHeight: contentHeight
model: control.contentModel
interactive: Window.window ? contentHeight > Window.window.height : false
clip: true
currentIndex: control.currentIndex
ScrollIndicator.vertical: ScrollIndicator {}
}
background: Item {
implicitWidth: 122
implicitHeight: 30
Rectangle{
anchors.fill: parent
color: "#FFFFFF"
layer.effect: FluDropShadow{}
layer.enabled: true
}
}
}

View File

@ -0,0 +1,29 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Controls.impl 2.15
import QtQuick.Templates 2.15 as T
import QtQuick.Shapes 1.15
T.MenuItem {
id: control
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
implicitContentWidth + leftPadding + rightPadding)
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
implicitContentHeight + topPadding + bottomPadding,
implicitIndicatorHeight + topPadding + bottomPadding)
padding: 0
spacing: 6
contentItem: FluText {
text: control.text
}
background: Rectangle {
implicitWidth: 120
implicitHeight: 30
width: control.width
height: control.height
}
}

View File

@ -0,0 +1,7 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
ScrollBar {
}

View File

@ -2,27 +2,11 @@
import QtQuick.Window 2.15 import QtQuick.Window 2.15
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
import FluentUI 1.0 import FluentUI 1.0
import QtGraphicalEffects 1.15
Rectangle { Item {
id:root id:root
property bool isMax: {
if(Window.window == null)
return false
return Window.Maximized === Window.window.visibility
}
property string title: "FluentUI"
property string winId
property var minimumSize
property var maximumSize
Behavior on opacity{
NumberAnimation{
duration: 100
}
}
property var window : { property var window : {
if(Window.window == null) if(Window.window == null)
@ -30,17 +14,42 @@ Rectangle {
return Window.window return Window.window
} }
onIsMaxChanged: { property color color: FluApp.isDark ? "#202020" : "#F3F3F3"
if(isMax){ property string title: "FluentUI"
root.anchors.margins = 8*(1/Screen.devicePixelRatio) property int minimumWidth
root.anchors.fill = parent property int maximumWidth
}else{ property int minimumHeight
root.anchors.margins = 0 property int maximumHeight
root.anchors.fill = null property int borderless:{
if(Window.window == null)
return 4
if(Window.window.visibility === Window.Maximized){
return 0
}
return 4
}
default property alias content: container.children
FluWindowResize{}
Behavior on opacity{
NumberAnimation{
duration: 100
} }
} }
color : FluApp.isDark ? "#202020" : "#F3F3F3" Rectangle{
id:container
color:root.color
anchors.fill: parent
anchors.margins: borderless
layer.enabled: true
layer.effect: DropShadow {
radius: 5
samples: 4
color: "#40000000"
}
}
Component.onCompleted: { Component.onCompleted: {
@ -52,13 +61,18 @@ Rectangle {
if(FluApp.equalsWindow(view,window)){ if(FluApp.equalsWindow(view,window)){
helper.initWindow(view); helper.initWindow(view);
helper.setTitle(title); helper.setTitle(title);
if(minimumSize){ if(minimumWidth){
helper.setMinimumSize(minimumSize) helper.setMinimumWidth(minimumWidth)
} }
if(maximumSize){ if(maximumWidth){
helper.setMaximumSize(maximumSize) helper.setMaximumWidth(maximumWidth)
}
if(minimumHeight){
helper.setMinimumHeight(minimumHeight)
}
if(maximumHeight){
helper.setMaximumHeight(maximumHeight)
} }
helper.refreshWindow()
} }
} }
} }

View File

@ -0,0 +1,185 @@
import QtQuick 2.15
import QtQuick.Window 2.15
MouseArea {
property int border: 4
property bool fixedSize: {
if(Window.window == null)
return true
if(Window.window.visibility === Window.Maximized || Window.window.visibility === Window.FullScreen){
return true
}
return (Window.window.minimumWidth === Window.window.maximumWidth && Window.window.minimumHeight === Window.window.maximumHeight)
}
anchors.fill: parent
acceptedButtons: Qt.LeftButton
hoverEnabled: true
preventStealing: true
propagateComposedEvents: true
z: -65535
onReleased: {
Window.window.width = Window.window.width+1
Window.window.width = Window.window.width-1
}
onPressed :
(mouse)=> {
if (fixedSize) {
return;
}
var rc = Qt.rect(0, 0, 0, 0);
let e = 0;
//top-left
rc = Qt.rect(0, 0, border, border);
if (ptInRect(rc, mouse.x, mouse.y)) {
e = Qt.TopEdge | Qt.LeftEdge;
window.startSystemResize(e);
return;
}
//top
rc = Qt.rect(border, 0, window.width-border*2, border);
if (ptInRect(rc, mouse.x, mouse.y)) {
e = Qt.TopEdge;
window.startSystemResize(e);
return;
}
//top-right
rc = Qt.rect(window.width-border, 0, border, border);
if (ptInRect(rc, mouse.x, mouse.y)) {
e = Qt.TopEdge | Qt.RightEdge;
window.startSystemResize(e);
return;
}
//right
rc = Qt.rect(window.width-border, border, border, window.height-border*2);
if (ptInRect(rc, mouse.x, mouse.y)) {
e = Qt.RightEdge;
window.startSystemResize(e);
return;
}
//bottom-right
rc = Qt.rect(window.width-border, window.height-border, border, border);
if (ptInRect(rc, mouse.x, mouse.y)) {
e = Qt.BottomEdge | Qt.RightEdge;
window.startSystemResize(e);
return;
}
//bottom
rc = Qt.rect(border, window.height-border, window.width-border*2, border);
if (ptInRect(rc, mouse.x, mouse.y)) {
e = Qt.BottomEdge;
window.startSystemResize(e);
return;
}
//bottom_left
rc = Qt.rect(0, window.height-border,border, border);
if (ptInRect(rc, mouse.x, mouse.y)) {
e = Qt.BottomEdge | Qt.LeftEdge;
window.startSystemResize(e);
return;
}
//left
rc = Qt.rect(0, border,border, window.height-border*2);
if (ptInRect(rc, mouse.x, mouse.y)) {
e = Qt.LeftEdge;
window.startSystemResize(e);
return;
}
}
onPositionChanged:
(mouse)=> {
if (fixedSize) {
cursorShape = Qt.ArrowCursor;
return;
}
var rc = Qt.rect(0, 0, 0, 0);
//top-left
rc = Qt.rect(0, 0, border, border);
if (ptInRect(rc, mouse.x, mouse.y)) {
cursorShape = Qt.SizeFDiagCursor;
return;
}
//top
rc = Qt.rect(border, 0, window.width-border*2, border);
if (ptInRect(rc, mouse.x, mouse.y)) {
cursorShape = Qt.SizeVerCursor;
return;
}
//top-right
rc = Qt.rect(window.width-border, 0, border, border);
if (ptInRect(rc, mouse.x, mouse.y)) {
cursorShape = Qt.SizeBDiagCursor;
return;
}
//right
rc = Qt.rect(window.width-border, border, border, window.height-border*2);
if (ptInRect(rc, mouse.x, mouse.y)) {
cursorShape = Qt.SizeHorCursor;
return;
}
//bottom-right
rc = Qt.rect(window.width-border, window.height-border, border, border);
if (ptInRect(rc, mouse.x, mouse.y)) {
cursorShape = Qt.SizeFDiagCursor;
return;
}
//bottom
rc = Qt.rect(border, window.height-border, window.width-border*2, border);
if (ptInRect(rc, mouse.x, mouse.y)) {
cursorShape = Qt.SizeVerCursor;
return;
}
//bottom_left
rc = Qt.rect(0, window.height-border,border, border);
if (ptInRect(rc, mouse.x, mouse.y)) {
cursorShape = Qt.SizeBDiagCursor;
return;
}
//left
rc = Qt.rect(0, border,border, window.height-border*2);
if (ptInRect(rc, mouse.x, mouse.y)) {
cursorShape = Qt.SizeHorCursor;
return;
}
//default
cursorShape = Qt.ArrowCursor;
}
onExited: {
cursorShape = Qt.ArrowCursor;
}
function ptInRect(rc, x, y)
{
if ((rc.x <= x && x <= (rc.x + rc.width)) &&
(rc.y <= y && y <= (rc.y + rc.height))) {
return true;
}
return false;
}
}

View File

@ -27,5 +27,9 @@
<file>controls/TFpsMonitor.qml</file> <file>controls/TFpsMonitor.qml</file>
<file>controls/FluTextBoxBackground.qml</file> <file>controls/FluTextBoxBackground.qml</file>
<file>controls/FluMultiLineTextBox.qml</file> <file>controls/FluMultiLineTextBox.qml</file>
<file>controls/FluWindowResize.qml</file>
<file>controls/FluScrollBar.qml</file>
<file>controls/FluMenu.qml</file>
<file>controls/FluMenuItem.qml</file>
</qresource> </qresource>
</RCC> </RCC>