mirror of
https://github.com/zhuzichu520/FluentUI.git
synced 2025-07-01 23:51:48 +08:00
update
This commit is contained in:
21
example/App.qml
Normal file
21
example/App.qml
Normal file
@ -0,0 +1,21 @@
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Window 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtGraphicalEffects 1.15
|
||||
import FluentUI 1.0
|
||||
|
||||
Window {
|
||||
id:app
|
||||
|
||||
Component.onCompleted: {
|
||||
FluApp.setAppWindow(app)
|
||||
FluApp.routes = {
|
||||
"/":"qrc:/MainPage.qml",
|
||||
"/Setting":"qrc:/SettingPage.qml"
|
||||
}
|
||||
FluApp.initialRoute = "/"
|
||||
FluApp.run()
|
||||
}
|
||||
|
||||
}
|
102
example/MainPage.qml
Normal file
102
example/MainPage.qml
Normal file
@ -0,0 +1,102 @@
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Window 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtGraphicalEffects 1.15
|
||||
import FluentUI 1.0
|
||||
|
||||
Rectangle {
|
||||
id:rootwindow
|
||||
width: 800
|
||||
height: 600
|
||||
color : "#F3F3F3"
|
||||
|
||||
ListModel{
|
||||
id:nav_items
|
||||
ListElement{
|
||||
text:"Controls"
|
||||
page:"qrc:/T_Controls.qml"
|
||||
}
|
||||
ListElement{
|
||||
text:"Typography"
|
||||
page:"qrc:/T_Typography.qml"
|
||||
}
|
||||
}
|
||||
|
||||
ListView{
|
||||
id:nav_list
|
||||
anchors{
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
topMargin: 20
|
||||
bottomMargin: 20
|
||||
}
|
||||
width: 160
|
||||
model: nav_items
|
||||
delegate: Item{
|
||||
height: 38
|
||||
width: nav_list.width
|
||||
|
||||
Rectangle{
|
||||
color: {
|
||||
if(nav_list.currentIndex === index){
|
||||
return "#EAEAEB"
|
||||
}
|
||||
return item_mouse.containsMouse? "#EAEAEA" : "#00000000"
|
||||
}
|
||||
radius: 4
|
||||
anchors{
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
topMargin: 2
|
||||
bottomMargin: 2
|
||||
leftMargin: 6
|
||||
rightMargin: 6
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea{
|
||||
id:item_mouse
|
||||
hoverEnabled: true
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
nav_list.currentIndex = index
|
||||
}
|
||||
}
|
||||
|
||||
Text{
|
||||
text:model.text
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle{
|
||||
color: "#FFFFFF"
|
||||
radius: 10
|
||||
clip: true
|
||||
anchors{
|
||||
left: nav_list.right
|
||||
leftMargin: 2
|
||||
top: parent.top
|
||||
topMargin: 20
|
||||
right: parent.right
|
||||
rightMargin: 10
|
||||
bottom: parent.bottom
|
||||
bottomMargin: 20
|
||||
}
|
||||
border.width: 1
|
||||
border.color: "#EEEEEE"
|
||||
|
||||
Loader{
|
||||
anchors.fill: parent
|
||||
anchors.margins:20
|
||||
source: nav_items.get(nav_list.currentIndex).page
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
17
example/SettingPage.qml
Normal file
17
example/SettingPage.qml
Normal file
@ -0,0 +1,17 @@
|
||||
import QtQuick 2.15
|
||||
import FluentUI 1.0
|
||||
|
||||
Item {
|
||||
|
||||
width: 500
|
||||
height: 500
|
||||
|
||||
|
||||
FluText{
|
||||
text:"Display"
|
||||
fontStyle: FluText.Display
|
||||
anchors.centerIn: parent
|
||||
|
||||
}
|
||||
|
||||
}
|
52
example/T_Controls.qml
Normal file
52
example/T_Controls.qml
Normal file
@ -0,0 +1,52 @@
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtQuick.Window 2.15
|
||||
import FluentUI 1.0
|
||||
|
||||
Item {
|
||||
|
||||
ColumnLayout{
|
||||
spacing: 5
|
||||
|
||||
|
||||
FluText{
|
||||
text:"Controls"
|
||||
fontStyle: FluText.TitleLarge
|
||||
}
|
||||
|
||||
|
||||
|
||||
FluButton{
|
||||
|
||||
Layout.topMargin: 20
|
||||
}
|
||||
|
||||
FluFilledButton{
|
||||
onClicked:{
|
||||
FluApp.navigate("/Setting")
|
||||
console.debug("FluFilledButton:"+Window.window.x)
|
||||
}
|
||||
}
|
||||
|
||||
FluFilledButton{
|
||||
disabled: true
|
||||
onClicked:{
|
||||
console.debug("FluFilledButton-disabled")
|
||||
}
|
||||
}
|
||||
|
||||
FluIconButton{
|
||||
Component.onCompleted: {
|
||||
|
||||
}
|
||||
icon:FluentIcons.FA_android
|
||||
}
|
||||
|
||||
FluToggleSwitch{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
54
example/T_Typography.qml
Normal file
54
example/T_Typography.qml
Normal file
@ -0,0 +1,54 @@
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import FluentUI 1.0
|
||||
|
||||
Item {
|
||||
|
||||
ColumnLayout{
|
||||
|
||||
spacing: 5
|
||||
|
||||
FluText{
|
||||
text:"Display"
|
||||
fontStyle: FluText.Display
|
||||
}
|
||||
|
||||
FluText{
|
||||
text:"Title Large"
|
||||
fontStyle: FluText.TitleLarge
|
||||
}
|
||||
|
||||
FluText{
|
||||
text:"Title"
|
||||
fontStyle: FluText.Title
|
||||
}
|
||||
|
||||
FluText{
|
||||
text:"Subtitle"
|
||||
fontStyle: FluText.Subtitle
|
||||
}
|
||||
|
||||
FluText{
|
||||
text:"Body Large"
|
||||
fontStyle: FluText.BodyLarge
|
||||
}
|
||||
|
||||
FluText{
|
||||
text:"Body Strong"
|
||||
fontStyle: FluText.BodyStrong
|
||||
}
|
||||
|
||||
FluText{
|
||||
text:"Body"
|
||||
fontStyle: FluText.Body
|
||||
}
|
||||
|
||||
FluText{
|
||||
text:"Caption"
|
||||
fontStyle: FluText.Caption
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
56
example/TaoFrameLessView.h
Normal file
56
example/TaoFrameLessView.h
Normal file
@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include <QMouseEvent>
|
||||
#include <QQuickView>
|
||||
#include <QRegion>
|
||||
|
||||
//无边框窗口,主要用来实现自定义标题栏。
|
||||
//Windows平台支持拖动和改变大小,支持Aero效果
|
||||
//非Windows平台,去掉边框,不做其它处理。由Qml模拟resize和拖动。
|
||||
class TaoFrameLessViewPrivate;
|
||||
class TaoFrameLessView : 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 TaoFrameLessView(QWindow *parent = nullptr);
|
||||
~TaoFrameLessView();
|
||||
void moveToScreenCenter();
|
||||
bool isMax() const;
|
||||
bool isFull() const;
|
||||
QQuickItem *titleItem() const;
|
||||
|
||||
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;
|
||||
# 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:
|
||||
TaoFrameLessViewPrivate *d;
|
||||
};
|
106
example/TaoFrameLessView_unix.cpp
Normal file
106
example/TaoFrameLessView_unix.cpp
Normal file
@ -0,0 +1,106 @@
|
||||
#include "TaoFrameLessView.h"
|
||||
#include <QGuiApplication>
|
||||
#include <QQuickItem>
|
||||
#include <QScreen>
|
||||
#include <QWindow>
|
||||
|
||||
class TaoFrameLessViewPrivate
|
||||
{
|
||||
public:
|
||||
bool m_isMax = false;
|
||||
bool m_isFull = false;
|
||||
QQuickItem *m_titleItem = nullptr;
|
||||
};
|
||||
TaoFrameLessView::TaoFrameLessView(QWindow *parent) : Super(parent), d(new TaoFrameLessViewPrivate)
|
||||
{
|
||||
setFlags(Qt::CustomizeWindowHint | Qt::Window | Qt::FramelessWindowHint | Qt::WindowMinMaxButtonsHint | 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);
|
||||
});
|
||||
}
|
||||
TaoFrameLessView::~TaoFrameLessView()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
void TaoFrameLessView::showEvent(QShowEvent *e)
|
||||
{
|
||||
Super::showEvent(e);
|
||||
}
|
||||
QRect TaoFrameLessView::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 TaoFrameLessView::moveToScreenCenter()
|
||||
{
|
||||
auto geo = calcCenterGeo(screen()->availableGeometry(), size());
|
||||
if (minimumWidth() > geo.width() || minimumHeight() > geo.height()) {
|
||||
setMinimumSize(geo.size());
|
||||
}
|
||||
setGeometry(geo);
|
||||
update();
|
||||
}
|
||||
bool TaoFrameLessView::isMax() const
|
||||
{
|
||||
return d->m_isMax;
|
||||
}
|
||||
bool TaoFrameLessView::isFull() const
|
||||
{
|
||||
return d->m_isFull;
|
||||
}
|
||||
QQuickItem *TaoFrameLessView::titleItem() const
|
||||
{
|
||||
return d->m_titleItem;
|
||||
}
|
||||
void TaoFrameLessView::setIsMax(bool isMax)
|
||||
{
|
||||
if (d->m_isMax == isMax)
|
||||
return;
|
||||
|
||||
d->m_isMax = isMax;
|
||||
emit isMaxChanged(d->m_isMax);
|
||||
}
|
||||
void TaoFrameLessView::setIsFull(bool isFull)
|
||||
{
|
||||
if(d->m_isFull == isFull)
|
||||
return;
|
||||
|
||||
d->m_isFull = isFull;
|
||||
emit isFullChanged(d->m_isFull);
|
||||
}
|
||||
void TaoFrameLessView::setTitleItem(QQuickItem *item)
|
||||
{
|
||||
d->m_titleItem = item;
|
||||
}
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
bool TaoFrameLessView::nativeEvent(const QByteArray &eventType, void *message, qintptr *result)
|
||||
#else
|
||||
bool TaoFrameLessView::nativeEvent(const QByteArray &eventType, void *message, long *result)
|
||||
#endif
|
||||
|
||||
{
|
||||
return Super::nativeEvent(eventType, message, result);
|
||||
}
|
||||
|
||||
void TaoFrameLessView::resizeEvent(QResizeEvent *e)
|
||||
{
|
||||
Super::resizeEvent(e);
|
||||
}
|
361
example/TaoFrameLessView_win.cpp
Normal file
361
example/TaoFrameLessView_win.cpp
Normal file
@ -0,0 +1,361 @@
|
||||
#include "TaoFrameLessView.h"
|
||||
|
||||
#include <QGuiApplication>
|
||||
#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")
|
||||
// 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
|
||||
enum class Style : DWORD
|
||||
{
|
||||
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
|
||||
};
|
||||
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)
|
||||
{
|
||||
return win->windowState() == Qt::WindowMaximized;
|
||||
}
|
||||
static bool isFullWin(QQuickView* win)
|
||||
{
|
||||
return win->windowState() == Qt::WindowFullScreen;
|
||||
}
|
||||
|
||||
class TaoFrameLessViewPrivate
|
||||
{
|
||||
public:
|
||||
bool m_firstRun = true;
|
||||
bool m_isMax = false;
|
||||
bool m_isFull = 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;
|
||||
::SetWindowLongPtrW(handle, GWL_STYLE, static_cast<LONG>(newStyle));
|
||||
|
||||
// when switching between borderless and windowed, restore appropriate shadow state
|
||||
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)
|
||||
{
|
||||
borderless_shadow = enabled;
|
||||
setShadow(handle, enabled);
|
||||
}
|
||||
}
|
||||
};
|
||||
TaoFrameLessView::TaoFrameLessView(QWindow* parent)
|
||||
: QQuickView(parent)
|
||||
, d(new TaoFrameLessViewPrivate)
|
||||
{
|
||||
//此处不需要设置flags
|
||||
// setFlags(Qt::CustomizeWindowHint | Qt::Window | Qt::FramelessWindowHint | Qt::WindowMinMaxButtonsHint | 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);
|
||||
});
|
||||
}
|
||||
void TaoFrameLessView::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);
|
||||
}
|
||||
TaoFrameLessView::~TaoFrameLessView()
|
||||
{
|
||||
if (d->mMenuHandler != NULL)
|
||||
{
|
||||
::DestroyMenu(d->mMenuHandler);
|
||||
}
|
||||
delete d;
|
||||
}
|
||||
|
||||
bool TaoFrameLessView::isMax() const
|
||||
{
|
||||
return d->m_isMax;
|
||||
}
|
||||
|
||||
bool TaoFrameLessView::isFull() const
|
||||
{
|
||||
return d->m_isFull;
|
||||
}
|
||||
QQuickItem* TaoFrameLessView::titleItem() const
|
||||
{
|
||||
return d->m_titleItem;
|
||||
}
|
||||
void TaoFrameLessView::setTitleItem(QQuickItem* item)
|
||||
{
|
||||
d->m_titleItem = item;
|
||||
}
|
||||
QRect TaoFrameLessView::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 TaoFrameLessView::moveToScreenCenter()
|
||||
{
|
||||
auto geo = calcCenterGeo(screen()->availableGeometry(), size());
|
||||
if (minimumWidth() > geo.width() || minimumHeight() > geo.height())
|
||||
{
|
||||
setMinimumSize(geo.size());
|
||||
}
|
||||
setGeometry(geo);
|
||||
update();
|
||||
}
|
||||
|
||||
void TaoFrameLessView::setIsMax(bool isMax)
|
||||
{
|
||||
if (d->m_isMax == isMax)
|
||||
return;
|
||||
|
||||
d->m_isMax = isMax;
|
||||
emit isMaxChanged(d->m_isMax);
|
||||
}
|
||||
void TaoFrameLessView::setIsFull(bool isFull)
|
||||
{
|
||||
if (d->m_isFull == isFull)
|
||||
return;
|
||||
|
||||
d->m_isFull = isFull;
|
||||
emit isFullChanged(d->m_isFull);
|
||||
}
|
||||
void TaoFrameLessView::resizeEvent(QResizeEvent* e)
|
||||
{
|
||||
// SetWindowRgn(HWND(winId()), CreateRoundRectRgn(0, 0, width(), height(), 4, 4), true);
|
||||
Super::resizeEvent(e);
|
||||
}
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
bool TaoFrameLessView::nativeEvent(const QByteArray& eventType, void* message, qintptr* result)
|
||||
#else
|
||||
bool TaoFrameLessView::nativeEvent(const QByteArray& eventType, void* message, long* result)
|
||||
#endif
|
||||
|
||||
{
|
||||
const long border_width = 4;
|
||||
if (!result)
|
||||
{
|
||||
//防御式编程
|
||||
//一般不会发生这种情况,win7一些极端情况,会传空指针进来。解决方案是升级驱动、切换到basic主题。
|
||||
return false;
|
||||
}
|
||||
|
||||
#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)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
switch (msg->message)
|
||||
{
|
||||
case WM_NCCALCSIZE: {
|
||||
#if 1
|
||||
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 && d->borderless)
|
||||
{
|
||||
*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;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
*result = 0;
|
||||
return true;
|
||||
#endif
|
||||
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))
|
||||
{ //非最大化、非全屏时,进行命中测试,处理边框拖拽
|
||||
*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);
|
||||
}
|
@ -7,6 +7,14 @@ DEFINES += QT_DEPRECATED_WARNINGS QT_NO_WARNING_OUTPUT
|
||||
SOURCES += \
|
||||
main.cpp
|
||||
|
||||
win32 {
|
||||
SOURCES += \
|
||||
TaoFrameLessView_win.cpp
|
||||
} else {
|
||||
SOURCES += \
|
||||
TaoFrameLessView_unix.cpp
|
||||
}
|
||||
|
||||
RESOURCES += qml.qrc
|
||||
|
||||
|
||||
@ -19,28 +27,32 @@ else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||
#### 如果你正在使用静态库.a 那么你还需要将下面的配置注释取消掉。
|
||||
#### 其它项目使用方法也是如此。
|
||||
|
||||
# DEFINES += STATICLIB
|
||||
DEFINES += STATICLIB
|
||||
|
||||
# LIBNAME = FluentUI
|
||||
LIBNAME = FluentUI
|
||||
|
||||
# CONFIG(debug, debug|release) {
|
||||
# contains(QMAKE_HOST.os,Windows) {
|
||||
# LIBNAME = FluentUI
|
||||
# }else{
|
||||
# LIBNAME = FluentUI_debug
|
||||
# }
|
||||
# }
|
||||
CONFIG(debug, debug|release) {
|
||||
contains(QMAKE_HOST.os,Windows) {
|
||||
LIBNAME = FluentUI
|
||||
}else{
|
||||
LIBNAME = FluentUI_debug
|
||||
}
|
||||
}
|
||||
|
||||
# # Additional import path used to resolve QML modules in Qt Creator's code model
|
||||
# QML_IMPORT_PATH = $$OUT_PWD/../bin/
|
||||
# Additional import path used to resolve QML modules in Qt Creator's code model
|
||||
QML_IMPORT_PATH = $$OUT_PWD/../bin/
|
||||
|
||||
# # Additional import path used to resolve QML modules just for Qt Quick Designer
|
||||
# QML_DESIGNER_IMPORT_PATH = $$OUT_PWD/../bin/
|
||||
# Additional import path used to resolve QML modules just for Qt Quick Designer
|
||||
QML_DESIGNER_IMPORT_PATH = $$OUT_PWD/../bin/
|
||||
|
||||
# INCLUDEPATH += $$OUT_PWD/../bin/FluentUI/
|
||||
# DEPENDPATH += $$OUT_PWD/../bin/FluentUI/
|
||||
INCLUDEPATH += $$OUT_PWD/../bin/FluentUI/
|
||||
DEPENDPATH += $$OUT_PWD/../bin/FluentUI/
|
||||
|
||||
# LIBS += -L$$OUT_PWD/../bin/FluentUI/ -l$${LIBNAME}
|
||||
LIBS += -L$$OUT_PWD/../bin/FluentUI/ -l$${LIBNAME}
|
||||
# PRE_TARGETDEPS += $$OUT_PWD/../bin/FluentUI/lib$${LIBNAME}.a
|
||||
|
||||
### 注意:静态库 .so .dylib .dll 是自动安装的Qt qml plugin目录中,不需要此步配置
|
||||
|
||||
HEADERS += \
|
||||
Frameless.h \
|
||||
TaoFrameLessView.h
|
||||
|
@ -1,22 +1,52 @@
|
||||
#include <QGuiApplication>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <time.h>
|
||||
#include <QDebug>
|
||||
#include "TaoFrameLessView.h"
|
||||
|
||||
#if defined(STATICLIB)
|
||||
#include <FluentUI.h>
|
||||
#endif
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
clock_t start,finish;
|
||||
double totaltime;
|
||||
start=clock();
|
||||
qputenv("QSG_RENDER_LOOP","basic");
|
||||
QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
#endif
|
||||
QGuiApplication app(argc, argv);
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
|
||||
if (engine.rootObjects().isEmpty())
|
||||
return -1;
|
||||
finish=clock();
|
||||
totaltime=(double)(finish-start)/CLOCKS_PER_SEC;
|
||||
qDebug() << "startup time :" << totaltime << "s";
|
||||
#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) {
|
||||
if (!obj && url == objUrl)
|
||||
QCoreApplication::exit(-1);
|
||||
}, Qt::QueuedConnection);
|
||||
engine.load(url);
|
||||
|
||||
|
||||
|
||||
// TaoFrameLessView view;
|
||||
//#if defined(STATICLIB)
|
||||
// FluentUI::create(view.engine());
|
||||
//#endif
|
||||
// const QUrl url(QStringLiteral("qrc:/main.qml"));
|
||||
// QObject::connect(&view, &QQuickView::statusChanged, &view, [&](QQuickView::Status status) {
|
||||
// if (status == QQuickView::Status::Ready) {
|
||||
|
||||
// }
|
||||
// });
|
||||
// //qml call 'Qt.quit()' will emit engine::quit, here should call qApp->quit
|
||||
// QObject::connect(view.engine(), &QQmlEngine::quit, qApp, &QCoreApplication::quit);
|
||||
// //qml clear content before quit
|
||||
// QObject::connect(qApp, &QGuiApplication::aboutToQuit, qApp, [&view](){view.setSource({});});
|
||||
|
||||
// view.setSource(url);
|
||||
// view.moveToScreenCenter();
|
||||
// view.show();
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
|
@ -1,31 +0,0 @@
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Window 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import FluentUI 1.0
|
||||
|
||||
Window {
|
||||
id:rootwindow
|
||||
visible: true
|
||||
width: 480
|
||||
height: 700
|
||||
|
||||
ColumnLayout{
|
||||
spacing: 5
|
||||
|
||||
FluentUI{
|
||||
width: 20
|
||||
height: 20
|
||||
}
|
||||
|
||||
StandardButton{
|
||||
|
||||
}
|
||||
|
||||
FilledButton{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,5 +1,9 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>main.qml</file>
|
||||
<file>T_Controls.qml</file>
|
||||
<file>T_Typography.qml</file>
|
||||
<file>App.qml</file>
|
||||
<file>MainPage.qml</file>
|
||||
<file>SettingPage.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
Reference in New Issue
Block a user