Compare commits

..

15 Commits
1.0.0 ... 1.0.2

Author SHA1 Message Date
e2cefb6114 update 2023-03-09 16:44:24 +08:00
bdf4da81dd update 2023-03-09 15:49:37 +08:00
f4cebb5c14 update 2023-03-09 13:23:39 +08:00
88b15b92ea update 2023-03-09 11:50:40 +08:00
d43e661388 update 2023-03-09 01:18:46 +08:00
5df0efbc58 update 2023-03-08 18:15:06 +08:00
818b588daa update 2023-03-08 18:14:21 +08:00
3fbd494724 update 2023-03-08 16:13:52 +08:00
b864be5f67 update 2023-03-08 16:09:18 +08:00
a81e7958c5 update 2023-03-08 16:02:09 +08:00
b1d57c43c4 update 2023-03-08 15:56:53 +08:00
02adfb4b0e update 2023-03-08 15:33:48 +08:00
97e0d614d0 update 2023-03-08 15:22:43 +08:00
46caf288d8 update 2023-03-08 15:17:31 +08:00
aceb3fee7b update 2023-03-08 14:53:28 +08:00
37 changed files with 626 additions and 155 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

View File

@ -1,6 +1,7 @@
name: Ubuntu
# Qt官方没有linux平台的x86包
on:
workflow_dispatch:
push:
paths:
- '*.pro'
@ -20,11 +21,8 @@ jobs:
os: [ubuntu-18.04,ubuntu-20.04]
qt_ver: [5.15.2]
qt_arch: [gcc_64]
exclude:
# Qt6.2.2 need GLIBC_2.28, ubuntu 18.04 have not.
- os: ubuntu-18.04
qt_ver: 6.2.2
qt_arch: gcc_64
env:
targetName: example
steps:
- name: Install Qt
uses: jurplel/install-qt-action@v2
@ -32,11 +30,38 @@ jobs:
version: ${{ matrix.qt_ver }}
cached: 'false'
- name: ubuntu install GL library
run: sudo apt-get install -y libglew-dev libglfw3-dev
run: sudo apt-get install -y libglew-dev libglfw3-dev qml-module-qtquick-controls qml-module-qtquick-controls2
- uses: actions/checkout@v2
with:
fetch-depth: 1
- name: build ubuntu
run: |
qmake
make
make
- name: install QT linux deploy
uses: miurahr/install-linuxdeploy-action@v1
with:
plugins: qt appimage
# 打包
- name: package
run: |
# make sure Qt plugin finds QML sources so it can deploy the imported files
export QML_SOURCES_PATHS=src
# 拷贝依赖
linuxdeploy-x86_64.AppImage --plugin=qt --output=appimage --create-desktop-file --icon-file=${targetName}.svg --executable=bin/release/${targetName} --appdir bin/release/
mv ${{ env.targetName }}-*.AppImage ${{ env.targetName }}.AppImage
# 上传artifacts
- uses: actions/upload-artifact@v2
with:
name: ${{ env.targetName }}_${{ matrix.os }}_${{matrix.qt_ver}}
path: ${{ env.targetName }}.AppImage
# tag 上传Release
- name: uploadRelease
if: startsWith(github.event.ref, 'refs/tags/')
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ${{ env.targetName }}.AppImage
asset_name: ${{ env.targetName }}_${{ matrix.os }}_${{ matrix.qt_ver }}.AppImage
tag: ${{ github.ref }}
overwrite: true

View File

@ -27,6 +27,7 @@ jobs:
qt_tools_mingw_install: mingw810_64
env:
targetName: example.exe
fileName: example
steps:
- name: Install Qt
uses: jurplel/install-qt-action@v2
@ -65,7 +66,7 @@ jobs:
- name: package
id: package
env:
archiveName: ${{ matrix.qt_ver }}-${{ matrix.qt_target }}-${{ matrix.qt_arch }}
archiveName: ${{ env.fileName }}-${{ matrix.qt_ver }}-${{ matrix.qt_arch }}
shell: pwsh
run: |
& scripts\windows-mingw-publish.ps1 ${env:archiveName} ${env:targetName}

View File

@ -32,6 +32,7 @@ jobs:
qt_arch_install: msvc2019_64
env:
targetName: example.exe
fileName: example
# 步骤
steps:
# 安装Qt
@ -66,7 +67,7 @@ jobs:
- name: package
id: package
env:
archiveName: ${{ matrix.qt_ver }}-${{ matrix.qt_target }}-${{ matrix.qt_arch }}
archiveName: ${{ env.fileName }}-${{ matrix.qt_ver }}-${{ matrix.qt_arch }}
msvcArch: ${{ matrix.msvc_arch }}
shell: pwsh
run: |

View File

@ -4,12 +4,11 @@
这是一个漂亮的Fluent组件库使用QML插件开发的
## status
## 编译状态
| [Windows][win-link]| [Ubuntu][ubuntu-link]|[MacOS][macos-link]|
|---------------|---------------|-----------------|
| ![win-badge] | ![ubuntu-badge] | ![macos-badge] |
[win-link]: https://github.com/zhuzichu520/FluentUI/actions?query=workflow%3AWindows "WindowsAction"
[win-badge]: https://github.com/zhuzichu520/FluentUI/workflows/Windows/badge.svg "Windows"
@ -19,6 +18,18 @@
[macos-link]: https://github.com/zhuzichu520/FluentUI/actions?query=workflow%3AMacOS "MacOSAction"
[macos-badge]: https://github.com/zhuzichu520/FluentUI/workflows/MacOS/badge.svg "MacOS"
## 发布
|[已发布][release-link]|[下载][download-link]|下载次数|
|:--:|:--:|:--:|
|![release-badge] |![download-badge]|![download-latest]|
[release-link]: https://github.com/zhuzichu520/FluentUI/releases "Release status"
[release-badge]: https://img.shields.io/github/release/zhuzichu520/FluentUI.svg?style=flat-square "Release status"
[download-link]: https://github.com/zhuzichu520/FluentUI/releases/latest "Download status"
[download-badge]: https://img.shields.io/github/downloads/zhuzichu520/FluentUI/total.svg "Download status"
[download-latest]: https://img.shields.io/github/downloads/zhuzichu520/FluentUI/latest/total.svg "latest status"
## 支持的组件
|目录|说明|备注|

1
example.svg Normal file
View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1678260749060" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="11227" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32"><path d="M0 0m170.663111 0l682.652445 0q170.663111 0 170.663111 170.663111l0 682.652445q0 170.663111-170.663111 170.663111l-682.652445 0q-170.663111 0-170.663111-170.663111l0-682.652445q0-170.663111 170.663111-170.663111Z" fill="#EBF2FF" p-id="11228"></path><path d="M512.351993 787.674257c182.822858 0 330.926439 98.568613 331.033103 231.312514 0 1.674632-0.031999 3.338597-0.106664 5.002563H181.43622c-0.074665-1.663965-0.106664-3.327931-0.106664-5.002563 0-132.743901 148.210246-231.312514 331.022437-231.312514z" fill="#7D99C8" p-id="11229"></path><path d="M592.264994 721.158309v79.817004h-0.810649c-5.546551 30.079373-38.847191 53.204225-79.102352 53.204225-40.244495 0-73.545134-23.114185-79.091686-53.193559h-0.81065V721.158309h159.815337z" fill="#FBDBCB" p-id="11230"></path><path d="M508.213412 765.626716c-44.105748-1.226641-85.363555-19.306264-118.568196-56.916147-22.826191-25.866128-35.199267-53.054895-37.097894-81.576968l-21.610217-1.610633a59.988084 59.988084 0 0 1-55.433511-56.585488l-0.319994-5.72788a34.932606 34.932606 0 0 1 43.124435-35.839254l0.949314-9.098477a608.008666 608.008666 0 0 1 40.031166-159.015354l10.389117-25.866127H655.026354l10.389117 25.866127a608.008666 608.008666 0 0 1 40.031166 159.015354l0.95998 9.098477a34.932606 34.932606 0 0 1 43.103102 35.839254l-0.309327 5.72788a59.988084 59.988084 0 0 1-55.433512 56.585488l-21.610217 1.610633c-1.909294 28.522072-14.271703 55.710839-37.097893 81.576968-34.239287 38.793858-77.054395 56.798817-122.717444 56.980146z" fill="#FFE7DB" p-id="11231"></path><path d="M546.911273 182.822858a10.666444 10.666444 0 0 1 1.429303 4.31991c0.533322 5.055895 0.714652 9.226474 0.533322 12.501073l-0.202662 2.346618 3.221266-0.543989c21.876878-3.733256 38.452532-9.877128 49.705631-18.452949l2.197288-1.73863a10.666444 10.666444 0 0 1 16.682319 3.98925c8.767817 20.650236 11.125102 40.681819 7.039853 60.116081l-0.373325 1.578633c4.746568 1.514635 9.589134 3.199933 14.506364 5.066562 27.882086 10.581113 57.769463 26.367451 76.883732 75.65709 20.212912 52.084248 18.826274 116.562905-4.149247 193.425304l-1.855961 6.101206H701.425387c-0.47999-51.732256-8.319827-90.526114-23.540843-116.392242-15.221016-25.866128-29.716714-46.185704-43.487094-60.969396-41.513802 10.399783-79.85967 15.914335-115.037603 16.564988l-6.997188 0.063998v0.191996c-37.065894 0-77.737047-5.546551-122.024124-16.628986-13.77038 10.954438-28.266078 31.220683-43.487094 60.7774-15.221016 29.556718-23.060853 68.414575-23.540843 116.584238h-11.114435c-24.746151-79.571676-26.740776-146.076957-6.005209-199.52651 8.746484-22.548864 19.732922-38.079207 31.764672-49.321639 18.079623-20.660903 40.180496-36.009916 66.302619-46.068374a424.716485 424.716485 0 0 1 32.319326-11.029103c25.25814-9.034478 44.991063-16.767651 59.209433-23.210183a366.221704 366.221704 0 0 0 29.151393-14.869024l7.359847-4.266578a10.666444 10.666444 0 0 1 14.623695 3.733256z" fill="#7D99C8" p-id="11232"></path></svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -6,8 +6,11 @@ FluWindow {
width: 500
height: 600
minimumWidth: 300
minimumHeight: 400
minimumWidth: 500
minimumHeight: 600
maximumWidth: 500
maximumHeight: 600
title:"关于"
FluAppBar{
@ -31,7 +34,7 @@ FluWindow {
fontStyle: FluText.Title
}
FluText{
text:"v1.0.0"
text:"v1.0.2"
fontStyle: FluText.Body
Layout.alignment: Qt.AlignBottom
}

View File

@ -18,7 +18,6 @@ FluWindow {
id:appbar
title: "FluentUI"
showDark: true
showFps: true
}
ListModel{
@ -55,6 +54,10 @@ FluWindow {
text:"Rectangle"
page:"qrc:/T_Rectangle.qml"
}
ListElement{
text:"Expander"
page:"qrc:/T_Expander.qml"
}
ListElement{
text:"TreeView"
page:"qrc:/T_TreeView.qml"
@ -88,7 +91,7 @@ FluWindow {
FluMenuItem{
text:"意见反馈"
onClicked:{
showInfo("正在建设中...")
Qt.openUrlExternally("https://github.com/zhuzichu520/FluentUI/issues/new")
}
}
FluMenuItem{
@ -111,6 +114,7 @@ FluWindow {
topMargin: 20
bottomMargin: 52
}
ScrollBar.vertical: ScrollBar { }
boundsBehavior: Flickable.StopAtBounds
clip: true
width: 160

View File

@ -127,6 +127,30 @@ Item {
text:"Disabled"
}
}
FluDivider{
Layout.fillWidth: true ; height:1
}
RowLayout{
Layout.topMargin: 20
width: parent.width
FluCheckBox{
disabled:icon_button_check.checked
}
Item{
height: 1
Layout.fillWidth: true
}
FluToggleSwitch{
id:icon_button_check
Layout.alignment: Qt.AlignRight
}
FluText{
text:"Disabled"
}
}
FluDivider{
Layout.fillWidth: true ; height:1
}
}
}
}

78
example/T_Expander.qml Normal file
View File

@ -0,0 +1,78 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtQuick.Window 2.15
import QtGraphicalEffects 1.15
import FluentUI 1.0
Item {
FluText{
id:title
text:"Expander"
fontStyle: FluText.TitleLarge
}
ScrollView{
clip: true
width: parent.width
contentWidth: parent.width
anchors{
top: title.bottom
bottom: parent.bottom
}
Column{
spacing: 5
Item{
width: 1
height: 20
}
FluExpander{
headerText:"打开一个单选框"
Item{
anchors.fill: parent
ColumnLayout{
spacing: 8
anchors{
top: parent.top
left: parent.left
topMargin: 15
leftMargin: 15
}
Repeater{
id:repeater
property int selecIndex : 0
model: 3
delegate: FluRadioButton{
checked : repeater.selecIndex===index
text:"RodioButton_"+index
onClicked:{
repeater.selecIndex = index
}
}
}
}
}
}
FluExpander{
Layout.topMargin: 20
headerText:"打开一个滑动文本框"
Item{
anchors.fill: parent
ScrollView{
id:scrollview
width: parent.width
height: parent.height
contentWidth: parent.width
FluText{
id:test
width: scrollview.width
wrapMode: Text.WrapAnywhere
padding: 14
text:"先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以塞忠谏之路也。宫中府中,俱为一体;陟罚臧否,不宜异同。若有作奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理,不宜偏私,使内外异法也。侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下。愚以为宫中之事,事无大小,悉以咨之,然后施行,必能裨补阙漏,有所广益。将军向宠,性行淑均,晓畅军事,试用于昔日,先帝称之曰能,是以众议举宠为督。愚以为营中之事,悉以咨之,必能使行阵和睦,优劣得所。亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息痛恨于桓、灵也。侍中、尚书、长史、参军,此悉贞良死节之臣,愿陛下亲之信之,则汉室之隆,可计日而待也。臣本布衣,躬耕于南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间,尔来二十有一年矣。先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧叹,恐托付不效,以伤先帝之明;故五月渡泸,深入不毛。今南方已定,兵甲已足,当奖率三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都。此臣所以报先帝而忠陛下之职分也。至于斟酌损益,进尽忠言,则攸之、祎、允之任也。愿陛下托臣以讨贼兴复之效,不效,则治臣之罪,以告先帝之灵。若无兴德之言,则责攸之、祎、允等之慢,以彰其咎;陛下亦宜自谋,以咨诹善道,察纳雅言,深追先帝遗诏。臣不胜受恩感激。今当远离,临表涕零,不知所言。"
}
}
}
}
}
}
}

View File

@ -26,6 +26,12 @@ Item {
Layout.leftMargin: 15
value: 50
}
FluSlider{
orientation:FluSlider.Vertical
Layout.topMargin: 20
Layout.leftMargin: 15
value: 50
}
}
}
}

View File

@ -31,6 +31,33 @@ Item {
Layout.preferredWidth: 300
placeholderText: "多行输入框"
}
FluAutoSuggestBox{
Layout.topMargin: 20
values:generateRandomNames(100)
placeholderText: "AutoSuggestBox"
Layout.preferredWidth: 300
}
}
}
function generateRandomNames(numNames) {
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
const names = [];
function generateRandomName() {
const nameLength = Math.floor(Math.random() * 5) + 4;
let name = '';
for (let i = 0; i < nameLength; i++) {
const letterIndex = Math.floor(Math.random() * 26);
name += alphabet.charAt(letterIndex);
}
return name;
}
for (let i = 0; i < numNames; i++) {
const name = generateRandomName();
names.push(name);
}
return names;
}
}

View File

@ -59,6 +59,17 @@ Item {
FluTheme.isDark = !FluTheme.isDark
}
}
FluText{
text:"无边框"
fontStyle: FluText.Subtitle
Layout.topMargin: 20
}
FluToggleSwitch{
checked: FluTheme.isFrameless
onClickFunc:function(){
FluTheme.isFrameless = !FluTheme.isFrameless
}
}
}
}
}

View File

@ -2,6 +2,7 @@
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QDir>
#include <QQuickWindow>
#include <QProcess>
QMap<QString, QVariant> properties(){
@ -16,6 +17,7 @@ int main(int argc, char *argv[])
QCoreApplication::setOrganizationDomain("https://zhuzichu520.github.io");
QCoreApplication::setApplicationName("FluentUI");
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
// QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
QMapIterator<QString, QVariant> iterator(properties());

View File

@ -29,5 +29,6 @@
<file>T_Theme.qml</file>
<file>T_Dialog.qml</file>
<file>T_TreeView.qml</file>
<file>T_Expander.qml</file>
</qresource>
</RCC>

View File

@ -59,7 +59,7 @@ void FluApp::navigate(const QString& route){
view->setSource((routes().value(route).toString()));
if(isAppWindow){
QObject::connect(view->engine(), &QQmlEngine::quit, qApp, &QCoreApplication::quit);
QObject::connect(qApp, &QGuiApplication::aboutToQuit, qApp, [&view](){view->setSource({});});
// QObject::connect(qApp, &QGuiApplication::aboutToQuit, qApp, [&view](){view->setSource({});});
}else{
view->closeDeleteLater();
}

View File

@ -16,5 +16,6 @@ FluTheme::FluTheme(QObject *parent)
: QObject{parent}
{
primaryColor(FluColors::getInstance()->Blue());
isFrameless(true);
isDark(false);
}

View File

@ -9,6 +9,7 @@ class FluTheme : public QObject
{
Q_OBJECT
Q_PROPERTY_AUTO(FluColorSet*,primaryColor)
Q_PROPERTY_AUTO(bool,isFrameless);
Q_PROPERTY_AUTO(bool,isDark);
public:
explicit FluTheme(QObject *parent = nullptr);

View File

@ -33,6 +33,8 @@ void Fluent::registerTypes(const char *uri){
qmlRegisterType<WindowHelper>(uri,major,minor,"WindowHelper");
qmlRegisterType<FluColorSet>(uri,major,minor,"FluColorSet");
qmlRegisterType(QUrl("qrc:/com.zhuzichu/controls/FluAutoSuggestBox.qml"),uri,major,minor,"FluAutoSuggestBox");
qmlRegisterType(QUrl("qrc:/com.zhuzichu/controls/FluExpander.qml"),uri,major,minor,"FluExpander");
qmlRegisterType(QUrl("qrc:/com.zhuzichu/controls/FluTreeView.qml"),uri,major,minor,"FluTreeView");
qmlRegisterType(QUrl("qrc:/com.zhuzichu/controls/FluContentDialog.qml"),uri,major,minor,"FluContentDialog");
qmlRegisterType(QUrl("qrc:/com.zhuzichu/controls/FluMenuItem.qml"),uri,major,minor,"FluMenuItem");

View File

@ -21,6 +21,7 @@ public:
void closeDeleteLater();
bool isMax() const;
bool isFull() const;
void refreshWindow();
QQuickItem *titleItem() const;
static QMap<WId,FramelessView*> *windowCache;
@ -38,6 +39,7 @@ signals:
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

View File

@ -3,6 +3,7 @@
#include <QQuickItem>
#include <QScreen>
#include <QWindow>
#include <FluTheme.h>
class FramelessViewPrivate
{
@ -12,10 +13,10 @@ public:
bool m_deleteLater = false;
QQuickItem *m_titleItem = nullptr;
};
FramelessView::FramelessView(QWindow *parent) : Super(parent), d(new FramelessViewPrivate)
{
setFlags(Qt::CustomizeWindowHint | Qt::Window | Qt::FramelessWindowHint | Qt::WindowMinMaxButtonsHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint);
setResizeMode(SizeRootObjectToView);
refreshWindow();
setIsMax(windowState() == Qt::WindowMaximized);
setIsFull(windowState() == Qt::WindowFullScreen);
connect(this, &QWindow::windowStateChanged, this, [&](Qt::WindowState state) {
@ -23,15 +24,31 @@ FramelessView::FramelessView(QWindow *parent) : Super(parent), d(new FramelessVi
setIsMax(windowState() == Qt::WindowMaximized);
setIsFull(windowState() == Qt::WindowFullScreen);
});
connect(FluTheme::getInstance(),&FluTheme::isFramelessChanged,this,[=](){
refreshWindow();
});
}
void FramelessView::refreshWindow(){
if(FluTheme::getInstance()->isFrameless()){
setFlags(Qt::CustomizeWindowHint | Qt::Window | Qt::FramelessWindowHint | Qt::WindowMinMaxButtonsHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint);
}else{
setFlags(Qt::Window);
}
setResizeMode(SizeViewToRootObject);
setResizeMode(SizeRootObjectToView);
}
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();
@ -46,9 +63,9 @@ QRect FramelessView::calcCenterGeo(const QRect &screenGeo, const QSize &normalSi
y = screenGeo.y();
h = screenGeo.height();
}
return { x, y, w, h };
}
void FramelessView::moveToScreenCenter()
{
auto geo = calcCenterGeo(screen()->availableGeometry(), size());
@ -58,6 +75,7 @@ void FramelessView::moveToScreenCenter()
setGeometry(geo);
update();
}
void FramelessView::closeDeleteLater(){
d->m_deleteLater = true;
}
@ -66,14 +84,17 @@ 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)
@ -82,6 +103,7 @@ void FramelessView::setIsMax(bool isMax)
d->m_isMax = isMax;
emit isMaxChanged(d->m_isMax);
}
void FramelessView::setIsFull(bool isFull)
{
if(d->m_isFull == isFull)
@ -90,6 +112,7 @@ void FramelessView::setIsFull(bool isFull)
d->m_isFull = isFull;
emit isFullChanged(d->m_isFull);
}
void FramelessView::setTitleItem(QQuickItem *item)
{
d->m_titleItem = item;
@ -107,3 +130,15 @@ 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

@ -3,27 +3,7 @@
#include <QQuickItem>
#include <QScreen>
#include <QWindow>
#include <VersionHelpers.h>
#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")
static bool isMaxWin(QWindow* win)
{
return win->windowState() == Qt::WindowMaximized;
}
static bool isFullWin(QQuickView* win)
{
return win->windowState() == Qt::WindowFullScreen;
}
#include <FluTheme.h>
class FramelessViewPrivate
{
@ -33,10 +13,10 @@ public:
bool m_deleteLater = false;
QQuickItem *m_titleItem = nullptr;
};
FramelessView::FramelessView(QWindow *parent) : Super(parent), d(new FramelessViewPrivate)
{
setFlags(Qt::CustomizeWindowHint | Qt::Window | Qt::FramelessWindowHint | Qt::WindowMinMaxButtonsHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint);
setResizeMode(SizeRootObjectToView);
refreshWindow();
setIsMax(windowState() == Qt::WindowMaximized);
setIsFull(windowState() == Qt::WindowFullScreen);
connect(this, &QWindow::windowStateChanged, this, [&](Qt::WindowState state) {
@ -44,15 +24,31 @@ FramelessView::FramelessView(QWindow *parent) : Super(parent), d(new FramelessVi
setIsMax(windowState() == Qt::WindowMaximized);
setIsFull(windowState() == Qt::WindowFullScreen);
});
connect(FluTheme::getInstance(),&FluTheme::isFramelessChanged,this,[=](){
refreshWindow();
});
}
void FramelessView::refreshWindow(){
if(FluTheme::getInstance()->isFrameless()){
setFlags(Qt::CustomizeWindowHint | Qt::Window | Qt::FramelessWindowHint | Qt::WindowMinMaxButtonsHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint);
}else{
setFlags(Qt::Window);
}
setResizeMode(SizeViewToRootObject);
setResizeMode(SizeRootObjectToView);
}
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();
@ -67,9 +63,9 @@ QRect FramelessView::calcCenterGeo(const QRect &screenGeo, const QSize &normalSi
y = screenGeo.y();
h = screenGeo.height();
}
return { x, y, w, h };
}
void FramelessView::moveToScreenCenter()
{
auto geo = calcCenterGeo(screen()->availableGeometry(), size());
@ -79,6 +75,7 @@ void FramelessView::moveToScreenCenter()
setGeometry(geo);
update();
}
void FramelessView::closeDeleteLater(){
d->m_deleteLater = true;
}
@ -87,14 +84,17 @@ 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)
@ -103,6 +103,7 @@ void FramelessView::setIsMax(bool isMax)
d->m_isMax = isMax;
emit isMaxChanged(d->m_isMax);
}
void FramelessView::setIsFull(bool isFull)
{
if(d->m_isFull == isFull)
@ -111,6 +112,7 @@ void FramelessView::setIsFull(bool isFull)
d->m_isFull = isFull;
emit isFullChanged(d->m_isFull);
}
void FramelessView::setTitleItem(QQuickItem *item)
{
d->m_titleItem = item;
@ -121,43 +123,16 @@ bool FramelessView::nativeEvent(const QByteArray &eventType, void *message, qint
bool FramelessView::nativeEvent(const QByteArray &eventType, void *message, long *result)
#endif
{
#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);
#else
const auto msg = static_cast<LPMSG>(message);
#endif
if (!msg || !msg->hwnd)
MSG* msg = static_cast<MSG*>(message);
if (msg->message == WM_WINDOWPOSCHANGING)
{
return false;
}
switch (msg->message)
{
case WM_NCCALCSIZE: {
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)
WINDOWPOS* wp = reinterpret_cast<WINDOWPOS*>(msg->lParam);
if (wp != nullptr && (wp->flags & SWP_NOSIZE) == 0)
{
*result = WVR_REDRAW;
//规避 拖动border进行resize时界面闪烁
if (!isMaxWin(this) && !isFullWin(this))
{
if (clientRect->top != 0)
{
clientRect->top -= 0.1;
}
}
else
{
if (clientRect->top != 0)
{
clientRect->top += 0.1;
}
}
wp->flags |= SWP_NOCOPYBITS;
*result = 0;
return true;
}
break;
}
}
return Super::nativeEvent(eventType, message, result);
}
@ -166,3 +141,15 @@ 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

@ -13,13 +13,15 @@ Rectangle{
return borerlessColor
return Window.window.active ? borerlessColor : Qt.lighter(borerlessColor,1.1)
}
height: 50
visible: FluTheme.isFrameless
height: visible ? 50 : 0
width: {
if(parent==null)
return 200
return parent.width
}
z: 65535
clip: true
property string title: "标题"
property color textColor: FluTheme.isDark ? "#000000" : "#FFFFFF"
property bool showDark: false

View File

@ -0,0 +1,134 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
TextField{
id:input
width: 300
property var values:[]
color: FluTheme.isDark ? "#FFFFFF" : "#1A1A1A"
selectionColor: {
if(FluTheme.isDark){
return FluTheme.primaryColor.lighter
}else{
return FluTheme.primaryColor.dark
}
}
rightPadding: 30
selectByMouse: true
background: FluTextBoxBackground{
inputItem: input
FluIconButton{
icon:FluentIcons.FA_close
iconSize: 14
width: 20
height: 20
opacity: 0.5
visible: input.text !== ""
anchors{
verticalCenter: parent.verticalCenter
right: parent.right
rightMargin: 5
}
onClicked:{
input.text = ""
}
}
}
Component.onCompleted: {
searchData()
}
Popup{
id:input_popup
visible: input.focus
y:input.height
onClosed: {
input.focus = false
}
background: Rectangle{
width: input.width
radius: 4
FluShadow{
radius: 4
}
color: FluTheme.isDark ? Qt.rgba(51/255,48/255,48/255,1) : Qt.rgba(243/255,241/255,240/255,1)
height: 38*Math.min(Math.max(list_view.count,1),8)
ListView{
id:list_view
signal closePopup
anchors.fill: parent
boundsBehavior: ListView.StopAtBounds
clip: true
header: Item{
width: input.width
height: visible ? 38 : 0
visible: list_view.count === 0
FluText{
text:"没有找到结果"
anchors{
verticalCenter: parent.verticalCenter
left: parent.left
leftMargin: 15
}
}
}
ScrollBar.vertical: ScrollBar { }
delegate: Item{
height: 38
width: input.width
Rectangle{
anchors.fill: parent
anchors.topMargin: 2
anchors.bottomMargin: 2
anchors.leftMargin: 5
anchors.rightMargin: 5
color: {
if(item_mouse.containsMouse){
return FluTheme.isDark ? Qt.rgba(63/255,60/255,61/255,1) : Qt.rgba(234/255,234/255,234/255,1)
}
return FluTheme.isDark ? Qt.rgba(51/255,48/255,48/255,1) : Qt.rgba(243/255,241/255,240/255,1)
}
radius: 3
MouseArea{
id:item_mouse
anchors.fill: parent
hoverEnabled: true
onClicked: {
input_popup.close()
input.text = modelData
}
}
FluText{
text:modelData
anchors{
verticalCenter: parent.verticalCenter
left: parent.left
leftMargin: 10
}
}
}
}
}
}
}
onTextChanged: {
searchData()
}
function searchData(){
var result = []
values.map(function(item){
if(item.indexOf(input.text)!==-1){
result.push(item)
}
})
list_view.model = result
}
}

View File

@ -7,8 +7,8 @@ Rectangle {
property string text: "Standard Button"
property int startPadding : 15
property int endPadding : 15
property int topPadding: 8
property int bottomPadding: 8
property int topPadding: 5
property int bottomPadding: 5
property bool disabled: false
property color primaryColor : "#0064B0"
signal clicked
@ -62,9 +62,8 @@ Rectangle {
id:button_mouse
anchors.fill: parent
hoverEnabled: true
enabled: !disabled
onClicked: {
if(disabled)
return
button.clicked()
}
}

View File

@ -9,9 +9,26 @@ Item {
property string text: "Check Box"
property var checkClicked
property bool hovered: mouse_area.containsMouse
property bool disabled: false
width: childrenRect.width
height: childrenRect.height
property color borderNormalColor: FluTheme.isDark ? Qt.rgba(160/255,160/255,160/255,1) : Qt.rgba(136/255,136/255,136/255,1)
property color borderCheckedColor: FluTheme.isDark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark
property color borderHoverColor: FluTheme.isDark ? Qt.rgba(167/255,167/255,167/255,1) : Qt.rgba(135/255,135/255,135/255,1)
property color borderDisableColor: FluTheme.isDark ? Qt.rgba(82/255,82/255,82/255,1) : Qt.rgba(199/255,199/255,199/255,1)
property color normalColor: FluTheme.isDark ? Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(247/255,247/255,247/255,1)
property color checkedColor: FluTheme.isDark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark
property color hoverColor: FluTheme.isDark ? Qt.rgba(62/255,62/255,62/255,1) : Qt.rgba(244/255,244/255,244/255,1)
property color checkedHoverColor: FluTheme.isDark ? Qt.darker(checkedColor,1.1) : Qt.lighter(checkedColor,1.1)
property color checkedDisableColor: FluTheme.isDark ? Qt.rgba(82/255,82/255,82/255,1) : Qt.rgba(199/255,199/255,199/255,1)
property color disableColor: FluTheme.isDark ? Qt.rgba(50/255,50/255,50/255,1) : Qt.rgba(253/255,253/255,253/255,1)
RowLayout{
spacing: 4
Rectangle{
@ -19,46 +36,32 @@ Item {
height: 22
radius: 4
border.color: {
if(FluTheme.isDark){
if(checked){
return FluTheme.primaryColor.lighter
}
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 FluTheme.primaryColor.dark
}
return Qt.rgba(136/255,136/255,136/255,1)
if(disabled){
return borderDisableColor
}
if(checked){
return borderCheckedColor
}
if(hovered){
return borderHoverColor
}
return borderNormalColor
}
border.width: 1
color: {
if(FluTheme.isDark){
if(checked){
if(mouse_area.containsMouse){
return Qt.rgba(74/255,149/255,207/255,1)
}
return FluTheme.primaryColor.lighter
if(checked){
if(disabled){
return checkedDisableColor
}
if(mouse_area.containsMouse){
return Qt.rgba(62/255,62/255,62/255,1)
if(hovered){
return checkedHoverColor
}
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 FluTheme.primaryColor.dark
}
if(mouse_area.containsMouse){
return Qt.rgba(244/255,244/255,244/255,1)
}
return Qt.rgba(247/255,247/255,247/255,1)
return checkedColor
}
if(hovered){
return hoverColor
}
return normalColor
}
FluIcon {
@ -79,6 +82,7 @@ Item {
id:mouse_area
anchors.fill: parent
hoverEnabled: true
enabled: !disabled
onClicked: {
if(checkClicked){
checkClicked()

View File

@ -23,10 +23,12 @@ Popup {
anchors.centerIn: Overlay.overlay
closePolicy: Popup.CloseOnEscape
background: Rectangle {
id:layout_content
implicitWidth:minWidth
implicitHeight: text_title.height + text_message.height + layout_actions.height
color:FluTheme.isDark ? Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(249/255,249/255,249/255,1)
radius:5
FluShadow{
radius: 5
}

View File

@ -0,0 +1,82 @@
import QtQuick 2.15
import FluentUI 1.0
Item {
property string headerText: "Titlte"
property bool expand: false
id:root
height: layout_header.height + container.height
width: 400
property int contentHeight : 300
default property alias content: container.data
Rectangle{
id:layout_header
width: parent.width
height: 50
radius: 4
border.color: FluTheme.isDark ? Qt.rgba(53/255,53/255,53/255,1) : Qt.rgba(240/255,240/255,240/255,1)
color: FluTheme.isDark ? Qt.rgba(61/255,61/255,61/255,1) : Qt.rgba(254/255,254/255,254/255,1)
MouseArea{
id:root_mouse
anchors.fill: parent
hoverEnabled: true
onClicked: {
expand = !expand
}
}
FluText{
text: headerText
anchors{
verticalCenter: parent.verticalCenter
left: parent.left
leftMargin: 15
}
}
FluIconButton{
anchors{
verticalCenter: parent.verticalCenter
right: parent.right
rightMargin: 15
}
hoverColor: FluTheme.isDark ? Qt.rgba(73/255,73/255,73/255,1) : Qt.rgba(245/255,245/255,245/255,1)
normalColor: FluTheme.isDark ? Qt.rgba(61/255,61/255,61/255,1) : Qt.rgba(254/255,254/255,254/255,1)
hovered: root_mouse.containsMouse
icon: expand ? FluentIcons.FA_angle_up : FluentIcons.FA_angle_down
onClicked: {
expand = !expand
}
}
}
Rectangle{
id:container
width: parent.width
clip: true
anchors{
top: layout_header.bottom
left: layout_header.left
}
radius: 4
border.color: FluTheme.isDark ? Qt.rgba(53/255,53/255,53/255,1) : Qt.rgba(240/255,240/255,240/255,1)
color: FluTheme.isDark ? Qt.rgba(57/255,57/255,57/255,1) : Qt.rgba(249/255,249/255,249/255,1)
height: expand ? contentHeight : 0
Behavior on height {
NumberAnimation{
duration: 150
}
}
}
}

View File

@ -8,8 +8,8 @@ Rectangle {
property string text: "Filled Button"
property int startPadding : 15
property int endPadding : 15
property int topPadding: 8
property int bottomPadding: 8
property int topPadding: 5
property int bottomPadding: 5
property bool disabled: false
signal clicked

View File

@ -13,6 +13,11 @@ Rectangle {
signal clicked
property bool disabled: false
property bool hovered: button_mouse.containsMouse
property color hoverColor: FluTheme.isDark ? Qt.rgba(62/255,62/255,62/255,1) : Qt.rgba(244/255,244/255,244/255,1)
property color normalColor: FluTheme.isDark ? Qt.rgba(50/255,50/255,50/255,1) : Qt.rgba(1,1,1,1)
property color disableColor: FluTheme.isDark ? Qt.rgba(59/255,59/255,59/255,1) : Qt.rgba(1,1,1,1)
property color textColor: {
if(FluTheme.isDark){
if(disabled){
@ -29,17 +34,10 @@ Rectangle {
radius: 4
color: {
if(FluTheme.isDark){
if(disabled){
return Qt.rgba(59/255,59/255,59/255,1)
}
return button_mouse.containsMouse ? Qt.rgba(62/255,62/255,62/255,1) : Qt.rgba(50/255,50/255,50/255,1)
}else{
if(disabled){
return Qt.rgba(1,1,1,1)
}
return button_mouse.containsMouse ? Qt.rgba(244/255,244/255,244/255,1) : Qt.rgba(1,1,1,1)
if(disabled){
return disableColor
}
return (hovered || button_mouse.containsMouse) ? hoverColor : normalColor
}
Text {
@ -69,7 +67,7 @@ Rectangle {
if(button.text === ""){
return false
}
return button_mouse.containsMouse
return (hovered || button_mouse.containsMouse)
}
delay: 1000
}

View File

@ -11,6 +11,7 @@ Popup {
implicitHeight: container.height
color:FluTheme.isDark ? Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(237/255,237/255,237/255,1)
radius: 5
FluShadow{
radius: 5
}

View File

@ -4,8 +4,8 @@ import QtQuick.Controls 2.12
FluRectangle {
id: control
width: 180
height: 6
width: 150
height: 5
radius: [3,3,3,3]
clip: true
color: FluTheme.isDark ? Qt.rgba(41/255,41/255,41/255,1) : Qt.rgba(214/255,214/255,214/255,1)

View File

@ -4,13 +4,13 @@ import QtQuick.Controls 2.12
Rectangle {
id: control
width: 60
height: 60
radius: 30
width: 44
height: 44
radius: 22
border.width: linWidth
color: "#00000000"
border.color: FluTheme.isDark ? Qt.rgba(41/255,41/255,41/255,1) : Qt.rgba(214/255,214/255,214/255,1)
property real linWidth : 6
property real linWidth : 5
property real progress: 0.25
property bool indeterminate: true
readonly property real radius2 : radius - linWidth/2

View File

@ -6,23 +6,40 @@ Item{
id:root
property int lineWidth: 6
property int dotSize: 30
property int lineWidth: 5
property int dotSize: 26
property int value: 50
enum Orientation {
Horizontal,
Vertical
}
height: control.height
width: control.width
property int orientation: FluSlider.Horizontal
property bool isHorizontal: orientation === FluSlider.Horizontal
Component.onCompleted: {
dot.x =value/100*control.width - dotSize/2
root.value = Qt.binding(function(){
return (dot.x+15)/control.width*100
})
if(isHorizontal){
dot.x =value/100*control.width - dotSize/2
root.value = Qt.binding(function(){
return (dot.x+dotSize/2)/control.width*100
})
}else{
dot.y =value/100*control.height - dotSize/2
root.value = Qt.binding(function(){
return (dot.y+dotSize/2)/control.height*100
})
}
}
FluRectangle {
id: control
width: 300
height: root.lineWidth
width: isHorizontal ? 200 : root.lineWidth
height: isHorizontal ? root.lineWidth : 200
radius: [3,3,3,3]
clip: true
anchors.verticalCenter: parent.verticalCenter
@ -30,8 +47,8 @@ Item{
Rectangle{
id:rect
radius: 3
width: control.width*(value/100)
height: control.height
width: isHorizontal ? control.width*(value/100) : control.width
height: isHorizontal ? control.height : control.height*(value/100)
color:FluTheme.isDark ? FluTheme.primaryColor.lighter :FluTheme.primaryColor.dark
}
}
@ -40,11 +57,12 @@ Item{
id:dot
width: dotSize
height: dotSize
FluShadow{
radius: 15
}
radius: 15
anchors.verticalCenter: parent.verticalCenter
FluShadow{
radius: dotSize/2
}
radius: dotSize/2
anchors.verticalCenter: isHorizontal ? parent.verticalCenter : undefined
anchors.horizontalCenter: isHorizontal ? undefined :parent.horizontalCenter
color:FluTheme.isDark ? Qt.rgba(69/255,69/255,69/255,1) :Qt.rgba(1,1,1,1)
Rectangle{
width: dotSize/2
@ -65,9 +83,11 @@ Item{
hoverEnabled: true
drag {
target: dot
axis: Drag.XAxis
minimumX: -dotSize/2
maximumX: control.width - dotSize/2
axis: isHorizontal ? Drag.XAxis : Drag.YAxis
minimumX: isHorizontal ? -dotSize/2 : 0
maximumX: isHorizontal ? (control.width - dotSize/2) : 0
minimumY: isHorizontal ? 0 : -dotSize/2
maximumY: isHorizontal ? 0 : (control.height - dotSize/2)
}
onPressed: {
tool_tip.visible = true

View File

@ -22,6 +22,9 @@ Item {
property int maximumHeight
property int borderless:{
if(!FluTheme.isFrameless){
return 0
}
if(window === null)
return 4
if(Window.window.visibility === Window.Maximized){
@ -29,6 +32,7 @@ Item {
}
return 4
}
default property alias content: container.data
FluWindowResize{
@ -46,7 +50,7 @@ Item {
color: {
if(window === null)
return borerlessColor
return window.active ? borerlessColor : Qt.lighter(borerlessColor,1.1)
return window.active ? borerlessColor : Qt.lighter(borerlessColor,1.1)
}
border.width: 1
anchors.fill: parent

0
src/macos_install.sh Normal file → Executable file
View File

View File

@ -34,5 +34,7 @@
<file>controls/FluTextButton.qml</file>
<file>controls/FluContentDialog.qml</file>
<file>controls/FluTreeView.qml</file>
<file>controls/FluExpander.qml</file>
<file>controls/FluAutoSuggestBox.qml</file>
</qresource>
</RCC>