diff --git a/example/InstallHelper.cpp b/example/InstallHelper.cpp new file mode 100644 index 0000000..4a92eb6 --- /dev/null +++ b/example/InstallHelper.cpp @@ -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()< +#include +#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 diff --git a/example/Installer.qml b/example/Installer.qml index 227ca83..5bd8b55 100644 --- a/example/Installer.qml +++ b/example/Installer.qml @@ -1,5 +1,8 @@ import QtQuick 2.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 FluWindow { @@ -7,15 +10,37 @@ FluWindow { id:window width: 800 height: 400 - maximumSize: Qt.size(800,400) - minimumSize: Qt.size(800,400) + minimumWidth:800 + maximumWidth:800 + minimumHeight:400 + maximumHeight:400 title:"安装向导" + property string installPath: "C:\\Program Files" + property string installName: "FluentUI" + FluAppBar{ id:appbar 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{ width: parent.width @@ -36,18 +61,37 @@ FluWindow { } FluTextBox{ + id:textbox_path Layout.preferredHeight: 40 Layout.fillWidth: true - text:"C:\\Program Files\\RustDesk" + text:installPath+ "\\" +installName readOnly:true } FluButton{ text:"更改路径" 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{ width: 1 Layout.fillHeight: true @@ -77,6 +121,9 @@ FluWindow { } FluFilledButton{ text:"同意并安装" + onClicked: { + helper.install(textbox_path.text,checkbox_home.checked,checkbox_startmenu.checked) + } } FluButton{ 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 + } + } + + } + } diff --git a/example/MainPage.qml b/example/MainPage.qml index 6fac333..5274d19 100644 --- a/example/MainPage.qml +++ b/example/MainPage.qml @@ -3,6 +3,7 @@ import QtQuick.Window 2.15 import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 import QtGraphicalEffects 1.15 + import FluentUI 1.0 FluWindow { @@ -10,51 +11,79 @@ FluWindow { width: 800 height: 600 title: "FluentUI" - minimumSize: Qt.size(600,400) - // property var maximumSize + minimumWidth: 600 + minimumHeight: 400 FluAppBar{ id:appbar title: "FluentUI" } - ListModel{ - id:nav_items - ListElement{ - text:"Buttons" - page:"qrc:/T_Buttons.qml" + Item{ + id:data + + ListModel{ + id:nav_items + ListElement{ + text:"Buttons" + page:"qrc:/T_Buttons.qml" + } + ListElement{ + text:"TextBox" + page:"qrc:/T_TextBox.qml" + } + ListElement{ + text:"ToggleSwitch" + page:"qrc:/T_ToggleSwitch.qml" + } + ListElement{ + text:"Slider" + page:"qrc:/T_Slider.qml" + } + ListElement{ + text:"InfoBar" + page:"qrc:/T_InfoBar.qml" + } + ListElement{ + text:"Progress" + page:"qrc:/T_Progress.qml" + } + ListElement{ + text:"Rectangle" + page:"qrc:/T_Rectangle.qml" + } + ListElement{ + text:"Awesome" + page:"qrc:/T_Awesome.qml" + } + ListElement{ + text:"Typography" + page:"qrc:/T_Typography.qml" + } } - ListElement{ - text:"TextBox" - page:"qrc:/T_TextBox.qml" + + FluMenu{ + id:menu + FluMenuItem{ + text:"123" + } + FluMenuItem{ + text:"456" + } } - ListElement{ - text:"ToggleSwitch" - page:"qrc:/T_ToggleSwitch.qml" + + } + + FluIconButton{ + icon:FluentIcons.FA_navicon + anchors{ + left: parent.left + bottom: parent.bottom + leftMargin: 12 + bottomMargin: 12 } - ListElement{ - text:"Slider" - page:"qrc:/T_Slider.qml" - } - ListElement{ - text:"InfoBar" - page:"qrc:/T_InfoBar.qml" - } - ListElement{ - text:"Progress" - page:"qrc:/T_Progress.qml" - } - ListElement{ - text:"Rectangle" - page:"qrc:/T_Rectangle.qml" - } - ListElement{ - text:"Awesome" - page:"qrc:/T_Awesome.qml" - } - ListElement{ - text:"Typography" - page:"qrc:/T_Typography.qml" + onClicked:{ + menu.popup() } } @@ -64,8 +93,9 @@ FluWindow { top: appbar.bottom bottom: parent.bottom topMargin: 20 - bottomMargin: 20 + bottomMargin: 52 } + clip: true width: 160 model: nav_items delegate: Item{ diff --git a/example/T_Awesome.qml b/example/T_Awesome.qml index 783bb67..cf6335c 100644 --- a/example/T_Awesome.qml +++ b/example/T_Awesome.qml @@ -1,4 +1,5 @@ import QtQuick 2.15 +import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 import QtQuick.Window 2.15 import FluentUI 1.0 @@ -13,18 +14,32 @@ Item { FluTextBox{ id:text_box - placeholderText: "搜索" + placeholderText: "请输入关键字" anchors{ topMargin: 20 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{ + id:grid_view cellWidth: 120 cellHeight: 60 clip: true model:FluApp.awesomelist() + ScrollBar.vertical: FluScrollBar {} anchors{ topMargin: 10 top:text_box.bottom diff --git a/example/example.pro b/example/example.pro index a1d3a41..00baeda 100644 --- a/example/example.pro +++ b/example/example.pro @@ -4,6 +4,7 @@ CONFIG += c++11 DEFINES += QT_DEPRECATED_WARNINGS QT_NO_WARNING_OUTPUT SOURCES += \ + InstallHelper.cpp \ main.cpp RESOURCES += qml.qrc @@ -42,3 +43,7 @@ else: unix:!android: target.path = /opt/$${TARGET}/bin # PRE_TARGETDEPS += $$OUT_PWD/../bin/FluentUI/lib$${LIBNAME}.a ### 注意:静态库 .so .dylib .dll 是自动安装的Qt qml plugin目录中,不需要此步配置 + +HEADERS += \ + InstallHelper.h \ + stdafx.h diff --git a/example/main.cpp b/example/main.cpp index c84c43b..b82a794 100644 --- a/example/main.cpp +++ b/example/main.cpp @@ -1,5 +1,6 @@ #include #include +#include "InstallHelper.h" #if defined(STATICLIB) #include @@ -7,6 +8,10 @@ int main(int argc, char *argv[]) { + QCoreApplication::setOrganizationName("ZhuZiChu"); + QCoreApplication::setOrganizationDomain("https://zhuzichu520.github.io"); + QCoreApplication::setApplicationName("FluentUI"); + qputenv("QSG_RENDER_LOOP","basic"); QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) @@ -16,12 +21,15 @@ int main(int argc, char *argv[]) #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Round); #endif -// QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + // QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); QQmlApplicationEngine engine; - #if defined(STATICLIB) - FluentUI::create(&engine); - #endif + + qmlRegisterType("UI",1,0,"InstallHelper"); + +#if defined(STATICLIB) + FluentUI::create(&engine); +#endif const QUrl url(QStringLiteral("qrc:/App.qml")); QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app, [url](QObject *obj, const QUrl &objUrl) { diff --git a/example/stdafx.h b/example/stdafx.h new file mode 100644 index 0000000..8919941 --- /dev/null +++ b/example/stdafx.h @@ -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 diff --git a/src/FluApp.cpp b/src/FluApp.cpp index 8b9a3d1..8150745 100644 --- a/src/FluApp.cpp +++ b/src/FluApp.cpp @@ -26,8 +26,6 @@ FluApp::FluApp(QObject *parent) isFps(true); } - - void FluApp::setAppWindow(QWindow *window){ appWindow = window; } @@ -43,7 +41,7 @@ void FluApp::navigate(const QString& route){ } bool isAppWindow = route==initialRoute(); 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) { if (status == QQuickView::Status::Ready) { Q_EMIT windowReady(view); @@ -65,17 +63,26 @@ bool FluApp::equalsWindow(FramelessView *view,QWindow *window){ return view->winId() == window->winId(); } -QJsonArray FluApp::awesomelist() +QJsonArray FluApp::awesomelist(const QString& keyword) { QJsonArray arr; QMetaEnum enumType = Fluent_Awesome::staticMetaObject.enumerator(Fluent_Awesome::staticMetaObject.indexOfEnumerator("Fluent_AwesomeType")); for(int i=0; i < enumType.keyCount(); ++i){ - QJsonObject obj; QString name = enumType.key(i); int icon = enumType.value(i); - obj.insert("name",name); - obj.insert("icon",icon); - arr.append(obj); + if(keyword.isEmpty()){ + QJsonObject obj; + obj.insert("name",name); + obj.insert("icon",icon); + 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; } diff --git a/src/FluApp.h b/src/FluApp.h index 14d420c..935bc57 100644 --- a/src/FluApp.h +++ b/src/FluApp.h @@ -32,7 +32,7 @@ public: 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); diff --git a/src/Fluent.cpp b/src/Fluent.cpp index 0ee7da4..b235c6b 100644 --- a/src/Fluent.cpp +++ b/src/Fluent.cpp @@ -30,6 +30,10 @@ void Fluent::registerTypes(const char *uri){ qmlRegisterType(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/FluDropShadow.qml"),uri,major,minor,"FluDropShadow"); qmlRegisterType(QUrl("qrc:/com.zhuzichu/controls/FluTooltip.qml"),uri,major,minor,"FluTooltip"); diff --git a/src/FramelessView_unix.cpp b/src/FramelessView_unix.cpp index 55b68da..895d270 100644 --- a/src/FramelessView_unix.cpp +++ b/src/FramelessView_unix.cpp @@ -9,6 +9,7 @@ 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) @@ -58,6 +59,10 @@ void FramelessView::moveToScreenCenter() setGeometry(geo); update(); } +void FramelessView::closeDeleteLater(){ + d->m_deleteLater = true; +} + bool FramelessView::isMax() const { return d->m_isMax; @@ -95,7 +100,6 @@ bool FramelessView::nativeEvent(const QByteArray &eventType, void *message, qint #else bool FramelessView::nativeEvent(const QByteArray &eventType, void *message, long *result) #endif - { return Super::nativeEvent(eventType, message, result); } diff --git a/src/FramelessView_win.cpp b/src/FramelessView_win.cpp index 2104406..0710735 100644 --- a/src/FramelessView_win.cpp +++ b/src/FramelessView_win.cpp @@ -1,93 +1,17 @@ #include "FramelessView.h" - #include #include #include #include -#include -#include -#include -#include // Fixes error C2504: 'IUnknown' : base class undefined -#include -#include -#include -#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* FramelessView::windowCache = new QMap; - - -enum class Style : DWORD +class FramelessViewPrivate { - windowed = WS_OVERLAPPEDWINDOW | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX, - aero_borderless = WS_POPUP | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX, - basic_borderless = WS_POPUP | WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX +public: + bool m_isMax = false; + 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) { @@ -98,53 +22,10 @@ static bool isFullWin(QQuickView* win) 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