diff --git a/example/T_Buttons.qml b/example/T_Buttons.qml index 8fd2093..f410da2 100644 --- a/example/T_Buttons.qml +++ b/example/T_Buttons.qml @@ -23,6 +23,7 @@ FluScrollablePage{ disabled:text_button_switch.selected text:"Text Button" onClicked: { + console.debug(Screen.devicePixelRatio) showInfo("点击Text Button") } anchors{ diff --git a/example/T_Theme.qml b/example/T_Theme.qml index b19046c..09d2214 100644 --- a/example/T_Theme.qml +++ b/example/T_Theme.qml @@ -46,16 +46,6 @@ FluScrollablePage{ FluTheme.isDark = !FluTheme.isDark } } - FluText{ - text:"无边框" - Layout.topMargin: 20 - } - FluToggleSwitch{ - selected: FluTheme.isFrameless - clickFunc:function(){ - FluTheme.isFrameless = !FluTheme.isFrameless - } - } FluText{ text:"native文本渲染" Layout.topMargin: 20 diff --git a/src/FluTheme.cpp b/src/FluTheme.cpp index d3b13a5..1ecf38e 100644 --- a/src/FluTheme.cpp +++ b/src/FluTheme.cpp @@ -18,6 +18,6 @@ FluTheme::FluTheme(QObject *parent) primaryColor(FluColors::getInstance()->Blue()); textSize(13); isNativeText(false); - isFrameless(false); + isFrameless(true); isDark(false); } diff --git a/src/FramelessView.h b/src/FramelessView.h index 61d07fb..bf485d8 100644 --- a/src/FramelessView.h +++ b/src/FramelessView.h @@ -18,7 +18,6 @@ public: void closeDeleteLater(); bool isMax() const; bool isFull() const; - void refreshWindow(); QQuickItem *titleItem() const; static QMap *windowCache; diff --git a/src/FramelessView_unix.cpp b/src/FramelessView_unix.cpp index d19067c..d7a9818 100644 --- a/src/FramelessView_unix.cpp +++ b/src/FramelessView_unix.cpp @@ -16,7 +16,8 @@ public: FramelessView::FramelessView(QWindow *parent) : Super(parent), d(new FramelessViewPrivate) { - refreshWindow(); + setFlags( Qt::Window | Qt::FramelessWindowHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint); + setResizeMode(SizeRootObjectToView); setIsMax(windowState() == Qt::WindowMaximized); setIsFull(windowState() == Qt::WindowFullScreen); connect(this, &QWindow::windowStateChanged, this, [&](Qt::WindowState state) { @@ -24,19 +25,6 @@ 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() diff --git a/src/FramelessView_win.cpp b/src/FramelessView_win.cpp index feaa7a3..d1484cf 100644 --- a/src/FramelessView_win.cpp +++ b/src/FramelessView_win.cpp @@ -4,6 +4,61 @@ #include #include #include +#include + +#include +#include +#pragma comment(lib, "Dwmapi.lib") +#pragma comment(lib, "User32.lib") + +static bool isMaxWin(QWindow* win) +{ + return win->windowState() == Qt::WindowMaximized; +} +static bool isFullWin(QQuickView* win) +{ + return win->windowState() == Qt::WindowFullScreen; +} + +static long hitTest(RECT winrect, long x, long y, int borderWidth) +{ + if ((x >= winrect.left) && (x < winrect.left + borderWidth) && (y >= winrect.top) && (y < winrect.top + borderWidth)) + { + return HTTOPLEFT; + } + else if (x < winrect.right && x >= winrect.right - borderWidth && y >= winrect.top && y < winrect.top + borderWidth) + { + return HTTOPRIGHT; + } + else if (x >= winrect.left && x < winrect.left + borderWidth && y < winrect.bottom && y >= winrect.bottom - borderWidth) + { + return HTBOTTOMLEFT; + } + else if (x < winrect.right && x >= winrect.right - borderWidth && y < winrect.bottom && y >= winrect.bottom - borderWidth) + { + return HTBOTTOMRIGHT; + } + else if (x >= winrect.left && x < winrect.left + borderWidth) + { + return HTLEFT; + } + else if (x < winrect.right && x >= winrect.right - borderWidth) + { + return HTRIGHT; + } + else if (y >= winrect.top && y < winrect.top + borderWidth) + { + return HTTOP; + } + else if (y < winrect.bottom && y >= winrect.bottom - borderWidth) + { + return HTBOTTOM; + } + else + { + return 0; + } +} class FramelessViewPrivate { @@ -11,12 +66,14 @@ public: bool m_isMax = false; bool m_isFull = false; bool m_deleteLater = false; + bool m_isFirst = true; QQuickItem *m_titleItem = nullptr; }; FramelessView::FramelessView(QWindow *parent) : Super(parent), d(new FramelessViewPrivate) { - refreshWindow(); + setFlags( Qt::Window | Qt::FramelessWindowHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint); + setResizeMode(SizeRootObjectToView); setIsMax(windowState() == Qt::WindowMaximized); setIsFull(windowState() == Qt::WindowFullScreen); connect(this, &QWindow::windowStateChanged, this, [&](Qt::WindowState state) { @@ -24,19 +81,6 @@ 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() @@ -46,7 +90,13 @@ FramelessView::~FramelessView() void FramelessView::showEvent(QShowEvent *e) { + static const MARGINS shadow_state[2] { { 0, 0, 0, 0 }, { 1, 1, 1, 1 } }; + ::DwmExtendFrameIntoClientArea((HWND)(winId()), &shadow_state[true]); Super::showEvent(e); + if(d->m_isFirst){ + QTimer::singleShot(150,this,[=](){ setFlag(Qt::FramelessWindowHint,false); }); + } + setFlag(Qt::FramelessWindowHint,false); } QRect FramelessView::calcCenterGeo(const QRect &screenGeo, const QSize &normalSize) @@ -124,13 +174,46 @@ bool FramelessView::nativeEvent(const QByteArray &eventType, void *message, long #endif { MSG* msg = static_cast(message); - if (msg->message == WM_WINDOWPOSCHANGING) + if (!msg || !msg->hwnd) { - WINDOWPOS* wp = reinterpret_cast(msg->lParam); - if (wp != nullptr && (wp->flags & SWP_NOSIZE) == 0) + return false; + } + if (msg->message == WM_NCHITTEST) + { + 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)) { - wp->flags |= SWP_NOCOPYBITS; - *result = 0; + *result = hitTest(winrect, x, y, 4); + if (0 != *result) + { + return true; + } + } + }else if (msg->message == WM_NCCALCSIZE) + { + const auto mode = static_cast(msg->wParam); + const auto clientRect = mode ? &(reinterpret_cast(msg->lParam)->rgrc[0]) : reinterpret_cast(msg->lParam); + if (mode == TRUE) + { + *result = WVR_REDRAW; + if (!isMaxWin(this) && !isFullWin(this)) + { + if (clientRect->top != 0) + { + clientRect->top -= 0.1; + } + } + else + { + if (clientRect->top != 0) + { + clientRect->top += 0.1; + } + } return true; } } diff --git a/src/WindowHelper.cpp b/src/WindowHelper.cpp index 72a4edf..5767b94 100644 --- a/src/WindowHelper.cpp +++ b/src/WindowHelper.cpp @@ -40,6 +40,9 @@ void WindowHelper::updateWindow(){ this->window->setFlag(Qt::Window,false); this->window->setFlag(Qt::Window,true); } +void WindowHelper::setOpacity(qreal opacity){ + this->window->setOpacity(opacity); +} void WindowHelper::setModality(int type){ if(type == 0){ this->window->setModality(Qt::NonModal); diff --git a/src/WindowHelper.h b/src/WindowHelper.h index c31184b..8a6a97e 100644 --- a/src/WindowHelper.h +++ b/src/WindowHelper.h @@ -24,6 +24,7 @@ public: Q_INVOKABLE QJsonObject getArgument(); Q_INVOKABLE QVariant getPageRegister(); Q_INVOKABLE void updateWindow(); + Q_INVOKABLE void setOpacity(qreal opacity); Q_INVOKABLE void setModality(int type); Q_INVOKABLE QVariant createRegister(const QString& path); diff --git a/src/controls/FluAppBar.qml b/src/controls/FluAppBar.qml index 7bc39ef..843b138 100644 --- a/src/controls/FluAppBar.qml +++ b/src/controls/FluAppBar.qml @@ -7,7 +7,7 @@ import FluentUI 1.0 Rectangle{ property string title: "标题" - property color textColor: FluTheme.isDark ? "#000000" : "#FFFFFF" + property color textColor: FluTheme.isDark ? "#FFFFFF" : "#000000" property bool showDark: false property bool showFps: false property var window: Window.window @@ -20,13 +20,9 @@ Rectangle{ } id:root - color: { - if(Window.window == null) - return borerlessColor - return Window.window.active ? borerlessColor : Qt.lighter(borerlessColor,1.1) - } + color: Qt.rgba(0,0,0,0) visible: FluTheme.isFrameless - height: visible ? 34 : 0 + height: visible ? 30 : 0 width: { if(parent==null) return 200 @@ -62,7 +58,7 @@ Rectangle{ RowLayout{ anchors.right: parent.right - height: 30 + height: root.height spacing: 0 TFpsMonitor{ @@ -92,10 +88,13 @@ Rectangle{ } FluIconButton{ - iconSource : FluentIcons.ChromeMinimizeContrast + width: 40 + height: 30 + iconSource : FluentIcons.ChromeMinimize Layout.alignment: Qt.AlignVCenter - iconSize: 15 + iconSize: 11 text:"最小化" + radius: 0 textColor: root.textColor color:hovered ? "#20000000" : "#00000000" onClicked: { @@ -103,41 +102,41 @@ Rectangle{ } } FluIconButton{ + width: 40 + height: 30 property bool isRestore:{ if(window == null) return false return Window.Maximized === window.visibility } - iconSource : isRestore ? FluentIcons.ChromeRestoreContrast : FluentIcons.ChromeMaximizeContrast + iconSource : isRestore ? FluentIcons.ChromeRestore : FluentIcons.ChromeMaximize color:hovered ? "#20000000" : "#00000000" Layout.alignment: Qt.AlignVCenter visible: resizable + radius: 0 textColor: root.textColor text:isRestore?"向下还原":"最大化" - iconSize: 15 + iconSize: 11 onClicked: { toggleMaximized() } } FluIconButton{ - iconSource : FluentIcons.ChromeCloseContrast + iconSource : FluentIcons.ChromeClose Layout.alignment: Qt.AlignVCenter text:"关闭" - iconSize: 13 - textColor: root.textColor - color:hovered ? "#20000000" : "#00000000" + width: 40 + height: 30 + radius: 0 + iconSize: 10 + textColor: hovered ? Qt.rgba(1,1,1,1) : root.textColor + color:hovered ? Qt.rgba(251/255,115/255,115/255,1) : "#00000000" onClicked: { Window.window.close() } } } - FluDivider{ - width: parent.width; - height: 1; - anchors.bottom: parent.bottom; - } - function toggleMaximized() { if(!resizable) return diff --git a/src/controls/FluIconButton.qml b/src/controls/FluIconButton.qml index 297cd36..cd64819 100644 --- a/src/controls/FluIconButton.qml +++ b/src/controls/FluIconButton.qml @@ -7,6 +7,7 @@ Button { property int iconSize: 20 property int iconSource property bool disabled: false + property int radius:4 property color hoverColor: FluTheme.isDark ? Qt.rgba(62/255,62/255,62/255,1) : Qt.rgba(0,0,0,0.03) property color normalColor: FluTheme.isDark ? Qt.rgba(0,0,0,0) : Qt.rgba(0,0,0,0) property color disableColor: FluTheme.isDark ? Qt.rgba(59/255,59/255,59/255,1) : Qt.rgba(0,0,0,0) @@ -40,7 +41,7 @@ Button { focusPolicy:Qt.TabFocus Keys.onSpacePressed: control.visualFocus&&clicked() background: Rectangle{ - radius: 4 + radius: control.radius color:control.color FluFocusRectangle{ visible: control.visualFocus diff --git a/src/controls/FluWindow.qml b/src/controls/FluWindow.qml index b6fb225..fb867b7 100644 --- a/src/controls/FluWindow.qml +++ b/src/controls/FluWindow.qml @@ -20,12 +20,7 @@ Item { return null return Window.window } - property int borderless:{ - if(!FluTheme.isFrameless){ - return 0 - } - return (window && (window.visibility === Window.Maximized)) ? 0 : 4 - } + property color color: { if(window && window.active){ return FluTheme.isDark ? Qt.rgba(32/255,32/255,32/255,1) : Qt.rgba(238/255,244/255,249/255,1) @@ -35,32 +30,17 @@ Item { id:root - - - FluWindowResize{ - border:borderless - } - Behavior on opacity{ NumberAnimation{ duration: 100 } } - Rectangle{ - property color borerlessColor : FluTheme.isDark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark - color: (window && window.active) ? borerlessColor : Qt.lighter(borerlessColor,1.1) - border.width: 1 - anchors.fill: parent - radius: 4 - border.color:FluTheme.isDark ? Qt.darker(FluTheme.primaryColor.lighter,1.3) : Qt.lighter(FluTheme.primaryColor.dark,1.2) - } - Rectangle{ id:container color:root.color anchors.fill: parent - anchors.margins: borderless + anchors.margins: (window && (window.visibility === Window.Maximized)) ? 8/Screen.devicePixelRatio : 0 clip: true Behavior on color{ ColorAnimation { @@ -69,24 +49,13 @@ Item { } } - Component.onCompleted: { - updateWindowSize() + Rectangle{ + border.width: 1 + anchors.fill: parent + color: Qt.rgba(0,0,0,0,) + border.color:FluTheme.isDark ? Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(226/255,230/255,234/255,1) } - Connections{ - target: FluTheme - function onIsFramelessChanged(){ - updateWindowSize() - } - } - - function updateWindowSize(){ - if(FluTheme.isFrameless){ - height = height + 34 - }else{ - height = height - 34 - } - } Connections{ target: FluApp