This commit is contained in:
朱子楚\zhuzi 2023-12-22 01:30:25 +08:00
parent 217ceabbaa
commit fa6b5cfc45
6 changed files with 227 additions and 60 deletions

View File

@ -13,6 +13,7 @@ static inline QByteArray qtNativeEventType()
static const auto result = "windows_generic_MSG";
return result;
}
static inline bool isCompositionEnabled(){
typedef HRESULT (WINAPI* DwmIsCompositionEnabledPtr)(BOOL *pfEnabled);
HMODULE module = LoadLibraryW(L"dwmapi.dll");
@ -52,14 +53,14 @@ static inline void showShadow(HWND hwnd){
#endif
FramelessEventFilter::FramelessEventFilter(QQuickWindow* window){
_window = window;
_current = window->winId();
FramelessEventFilter::FramelessEventFilter(FluFramelessHelper* helper){
_helper = helper;
_current = _helper->window->winId();
}
bool FramelessEventFilter::nativeEventFilter(const QByteArray &eventType, void *message, QT_NATIVE_EVENT_RESULT_TYPE *result){
#ifdef Q_OS_WIN
if ((eventType != qtNativeEventType()) || !message || !result || !_window) {
if ((eventType != qtNativeEventType()) || !message || _helper.isNull() || _helper->window.isNull()) {
return false;
}
const auto msg = static_cast<const MSG *>(message);
@ -102,6 +103,25 @@ bool FramelessEventFilter::nativeEventFilter(const QByteArray &eventType, void *
return true;
}
return false;
}else if(uMsg == WM_NCHITTEST){
if(_helper->hoverMaxBtn() && _helper->resizeable()){
*result = HTMAXBUTTON;
return true;
}
return false;
}else if(uMsg == WM_NCLBUTTONDBLCLK || uMsg == WM_NCLBUTTONDOWN){
if(_helper->hoverMaxBtn() && _helper->resizeable()){
QMouseEvent event = QMouseEvent(QEvent::MouseButtonPress, QPoint(), QPoint(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
QGuiApplication::sendEvent(_helper->maximizeButton(),&event);
return true;
}
return false;
}else if(uMsg == WM_NCLBUTTONUP || uMsg == WM_NCRBUTTONUP){
if(_helper->hoverMaxBtn() && _helper->resizeable()){
QMouseEvent event = QMouseEvent(QEvent::MouseButtonRelease, QPoint(), QPoint(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
QGuiApplication::sendEvent(_helper->maximizeButton(),&event);
}
return false;
}
return false;
#endif
@ -111,56 +131,60 @@ bool FramelessEventFilter::nativeEventFilter(const QByteArray &eventType, void *
FluFramelessHelper::FluFramelessHelper(QObject *parent)
: QObject{parent}
{
}
void FluFramelessHelper::classBegin(){
}
void FluFramelessHelper::updateCursor(int edges){
void FluFramelessHelper::_updateCursor(int edges){
switch (edges) {
case 0:
_window->setCursor(Qt::ArrowCursor);
window->setCursor(Qt::ArrowCursor);
break;
case Qt::LeftEdge:
case Qt::RightEdge:
_window->setCursor(Qt::SizeHorCursor);
window->setCursor(Qt::SizeHorCursor);
break;
case Qt::TopEdge:
case Qt::BottomEdge:
_window->setCursor(Qt::SizeVerCursor);
window->setCursor(Qt::SizeVerCursor);
break;
case Qt::LeftEdge | Qt::TopEdge:
case Qt::RightEdge | Qt::BottomEdge:
_window->setCursor(Qt::SizeFDiagCursor);
window->setCursor(Qt::SizeFDiagCursor);
break;
case Qt::RightEdge | Qt::TopEdge:
case Qt::LeftEdge | Qt::BottomEdge:
_window->setCursor(Qt::SizeBDiagCursor);
window->setCursor(Qt::SizeBDiagCursor);
break;
}
}
bool FluFramelessHelper::eventFilter(QObject *obj, QEvent *ev){
if (!_window.isNull() && _window->flags() & Qt::FramelessWindowHint) {
if (!window.isNull() && window->flags() & Qt::FramelessWindowHint) {
static int edges = 0;
const int margin = 8;
switch (ev->type()) {
case QEvent::MouseButtonPress:
if(edges!=0){
updateCursor(edges);
_window->startSystemResize(Qt::Edges(edges));
QMouseEvent *event = static_cast<QMouseEvent*>(ev);
if(event->button() == Qt::LeftButton){
_updateCursor(edges);
window->startSystemResize(Qt::Edges(edges));
}
}
break;
case QEvent::MouseButtonRelease:
edges = 0;
updateCursor(edges);
_updateCursor(edges);
break;
case QEvent::MouseMove: {
if(_window->visibility() == QWindow::Maximized || _window->visibility() == QWindow::FullScreen){
if(_maximized() || _fullScreen()){
break;
}
if(_window->width() == _window->maximumWidth() && _window->width() == _window->minimumWidth() && _window->height() == _window->maximumHeight() && _window->height() == _window->minimumHeight()){
if(!resizeable()){
break;
}
QMouseEvent *event = static_cast<QMouseEvent*>(ev);
@ -170,10 +194,10 @@ bool FluFramelessHelper::eventFilter(QObject *obj, QEvent *ev){
#else
event->position().toPoint();
#endif
if(p.x() >= margin && p.x() <= (_window->width() - margin) && p.y() >= margin && p.y() <= (_window->height() - margin)){
if(p.x() >= margin && p.x() <= (window->width() - margin) && p.y() >= margin && p.y() <= (window->height() - margin)){
if(edges != 0){
edges = 0;
updateCursor(edges);
_updateCursor(edges);
}
break;
}
@ -181,16 +205,16 @@ bool FluFramelessHelper::eventFilter(QObject *obj, QEvent *ev){
if ( p.x() < margin ) {
edges |= Qt::LeftEdge;
}
if ( p.x() > (_window->width() - margin) ) {
if ( p.x() > (window->width() - margin) ) {
edges |= Qt::RightEdge;
}
if ( p.y() < margin ) {
edges |= Qt::TopEdge;
}
if ( p.y() > (_window->height() - margin) ) {
if ( p.y() > (window->height() - margin) ) {
edges |= Qt::BottomEdge;
}
updateCursor(edges);
_updateCursor(edges);
break;
}
default:
@ -203,57 +227,123 @@ bool FluFramelessHelper::eventFilter(QObject *obj, QEvent *ev){
void FluFramelessHelper::componentComplete(){
auto o = parent();
while (nullptr != o) {
_window = (QQuickWindow*)o;
window = (QQuickWindow*)o;
o = o->parent();
}
if(!_window.isNull()){
_window->setFlags(Qt::FramelessWindowHint|Qt::Window|Qt::WindowTitleHint|Qt::WindowMinMaxButtonsHint|Qt::WindowCloseButtonHint);
if(!window.isNull()){
window->setFlags(Qt::FramelessWindowHint|Qt::Window|Qt::WindowTitleHint|Qt::WindowMinMaxButtonsHint|Qt::WindowCloseButtonHint);
#ifdef Q_OS_WIN
_nativeEvent =new FramelessEventFilter(_window);
_nativeEvent =new FramelessEventFilter(this);
qApp->installNativeEventFilter(_nativeEvent);
HWND hwnd = reinterpret_cast<HWND>(_window->winId());
HWND hwnd = reinterpret_cast<HWND>(window->winId());
SetWindowPos(hwnd,0,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOOWNERZORDER);
showShadow(hwnd);
#endif
_stayTop = QQmlProperty(_window,"stayTop");
_stayTop = QQmlProperty(window,"stayTop");
_onStayTopChange();
_stayTop.connectNotifySignal(this,SLOT(_onStayTopChange()));
_screen = QQmlProperty(_window,"screen");
_screen = QQmlProperty(window,"screen");
_screen.connectNotifySignal(this,SLOT(_onScreenChanged()));
_window->installEventFilter(this);
window->installEventFilter(this);
}
}
void FluFramelessHelper::_onScreenChanged(){
#ifdef Q_OS_WIN
HWND hwnd = reinterpret_cast<HWND>(_window->winId());
HWND hwnd = reinterpret_cast<HWND>(window->winId());
SetWindowPos(hwnd,0,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOOWNERZORDER);
RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
#endif
}
void FluFramelessHelper::showSystemMenu(){
#ifdef Q_OS_WIN
QPoint point = QCursor::pos();
HWND hwnd = reinterpret_cast<HWND>(window->winId());
DWORD style = GetWindowLongPtr(hwnd,GWL_STYLE);
SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | WS_MAXIMIZEBOX);
const HMENU hMenu = ::GetSystemMenu(hwnd, FALSE);
DeleteMenu(hMenu, SC_MOVE, MF_BYCOMMAND);
DeleteMenu(hMenu, SC_SIZE, MF_BYCOMMAND);
if(_maximized() || _fullScreen()){
EnableMenuItem(hMenu,SC_RESTORE,MFS_ENABLED);
}else{
EnableMenuItem(hMenu,SC_RESTORE,MFS_DISABLED);
}
if(resizeable() && !_maximized() && !_fullScreen()){
EnableMenuItem(hMenu,SC_MAXIMIZE,MFS_ENABLED);
}else{
EnableMenuItem(hMenu,SC_MAXIMIZE,MFS_DISABLED);
}
const int result = TrackPopupMenu(hMenu, (TPM_RETURNCMD | (QGuiApplication::isRightToLeft() ? TPM_RIGHTALIGN : TPM_LEFTALIGN)), point.x(), point.y(), 0, hwnd, nullptr);
if (result != FALSE) {
PostMessageW(hwnd, WM_SYSCOMMAND, result, 0);
}
SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_THICKFRAME | WS_CAPTION &~ WS_SYSMENU);
#endif
}
void FluFramelessHelper::_onStayTopChange(){
bool isStayTop = _stayTop.read().toBool();
#ifdef Q_OS_WIN
HWND hwnd = reinterpret_cast<HWND>(_window->winId());
HWND hwnd = reinterpret_cast<HWND>(window->winId());
DWORD style = GetWindowLongPtr(hwnd,GWL_STYLE);
SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_THICKFRAME | WS_CAPTION &~ WS_SYSMENU);
SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_THICKFRAME | WS_CAPTION | WS_MAXIMIZEBOX &~ WS_SYSMENU);
if(isStayTop){
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
}else{
SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
}
#else
_window->setFlag(Qt::WindowStaysOnTopHint,isStayTop);
window->setFlag(Qt::WindowStaysOnTopHint,isStayTop);
#endif
}
FluFramelessHelper::~FluFramelessHelper(){
if (!_window.isNull()) {
_window->setFlags(Qt::Window);
if (!window.isNull()) {
window->setFlags(Qt::Window);
#ifdef Q_OS_WIN
qApp->removeNativeEventFilter(_nativeEvent);
delete _nativeEvent;
#endif
_window->removeEventFilter(this);
window->removeEventFilter(this);
}
}
bool FluFramelessHelper::hoverMaxBtn(){
QVariant appBar = window->property("appBar");
if(appBar.isNull()){
return false;
}
QVariant var;
QMetaObject::invokeMethod(appBar.value<QObject*>(), "maximizeButtonHover",Q_RETURN_ARG(QVariant, var));
if(var.isNull()){
return false;
}
return var.toBool();
}
QObject* FluFramelessHelper::maximizeButton(){
QVariant appBar = window->property("appBar");
if(appBar.isNull()){
return nullptr;
}
QVariant var;
QMetaObject::invokeMethod(appBar.value<QObject*>(), "maximizeButton",Q_RETURN_ARG(QVariant, var));
if(var.isNull()){
return nullptr;
}
return var.value<QObject*>();
}
bool FluFramelessHelper::resizeable(){
return !(window->width() == window->maximumWidth() && window->width() == window->minimumWidth() && window->height() == window->maximumHeight() && window->height() == window->minimumHeight());
}
bool FluFramelessHelper::_maximized(){
return window->visibility() == QWindow::Maximized;
}
bool FluFramelessHelper::_fullScreen(){
return window->visibility() == QWindow::FullScreen;
}

View File

@ -15,13 +15,15 @@ using QT_NATIVE_EVENT_RESULT_TYPE = long;
using QT_ENTER_EVENT_TYPE = QEvent;
#endif
class FluFramelessHelper;
class FramelessEventFilter : public QAbstractNativeEventFilter
{
public:
FramelessEventFilter(QQuickWindow* window);
FramelessEventFilter(FluFramelessHelper* helper);
bool nativeEventFilter(const QByteArray &eventType, void *message, QT_NATIVE_EVENT_RESULT_TYPE *result) override;
public:
QQuickWindow* _window = nullptr;
QPointer<FluFramelessHelper> _helper = nullptr;
qint64 _current = 0;
};
@ -34,14 +36,21 @@ public:
~FluFramelessHelper();
void classBegin() override;
void componentComplete() override;
bool hoverMaxBtn();
bool resizeable();
QObject* maximizeButton();
Q_INVOKABLE void showSystemMenu();
protected:
bool eventFilter(QObject *obj, QEvent *event) override;
private:
void updateCursor(int edges);
void _updateCursor(int edges);
bool _maximized();
bool _fullScreen();
Q_SLOT void _onStayTopChange();
Q_SLOT void _onScreenChanged();
public:
QPointer<QQuickWindow> window = nullptr;
private:
QPointer<QQuickWindow> _window = nullptr;
FramelessEventFilter* _nativeEvent = nullptr;
QQmlProperty _stayTop;
QQmlProperty _screen;

View File

@ -64,6 +64,11 @@ Rectangle{
FluTheme.darkMode = FluThemeType.Dark
}
}
property var systemMenuListener: function(){
if(d.win instanceof FluWindow){
d.win.showSystemMenu()
}
}
id:control
color: Qt.rgba(0,0,0,0)
height: visible ? 30 : 0
@ -71,6 +76,7 @@ Rectangle{
z: 65535
Item{
id:d
property bool hoverMaxBtn: false
property var win: Window.window
property bool stayTop: {
if(d.win instanceof FluWindow){
@ -83,14 +89,23 @@ Rectangle{
}
MouseArea{
anchors.fill: parent
onPositionChanged: {
d.win.startSystemMove()
}
onDoubleClicked: {
if(d.resizable){
btn_maximize.clicked()
onPositionChanged:
(mouse)=>{
d.win.startSystemMove()
}
onDoubleClicked:
(mouse)=>{
if(d.resizable && Qt.LeftButton){
btn_maximize.clicked()
}
}
acceptedButtons: Qt.LeftButton|Qt.RightButton
onClicked:
(mouse)=>{
if (mouse.button === Qt.RightButton){
control.systemMenuListener()
}
}
}
}
Row{
anchors{
@ -218,7 +233,7 @@ Rectangle{
if(pressed){
return maximizePressColor
}
return hovered ? maximizeHoverColor : maximizeNormalColor
return d.hoverMaxBtn ? maximizeHoverColor : maximizeNormalColor
}
Layout.alignment: Qt.AlignVCenter
visible: d.resizable && !isMac && showMaximize
@ -263,4 +278,17 @@ Rectangle{
function darkButton(){
return btn_dark
}
function maximizeButtonHover(){
var hover = false;
var pos = btn_maximize.mapToGlobal(0,0)
if(btn_maximize.visible){
var rect = Qt.rect(pos.x,pos.y,btn_maximize.width,btn_maximize.height)
pos = FluTools.cursorPos()
if(pos.x>rect.x && pos.x<(rect.x+rect.width) && pos.y>rect.y && pos.y<(rect.y+rect.height)){
hover = true;
}
}
d.hoverMaxBtn = hover
return hover;
}
}

View File

@ -50,6 +50,7 @@ Window {
event.accepted = false
}
}
signal showSystemMenu
signal initArgument(var argument)
signal firstVisible()
id:window
@ -58,7 +59,7 @@ Window {
moveWindowToDesktopCenter()
useSystemAppBar = FluApp.useSystemAppBar
if(!useSystemAppBar){
loader_frameless.sourceComponent = com_frameless
loader_frameless_helper.sourceComponent = com_frameless
}
lifecycle.onCompleted(window)
initArgument(argument)
@ -71,6 +72,11 @@ Window {
Component.onDestruction: {
lifecycle.onDestruction()
}
onShowSystemMenu: {
if(loader_frameless_helper.item){
loader_frameless_helper.item.showSystemMenu()
}
}
onVisibleChanged: {
if(visible && d.isFirstVisible){
window.firstVisible()
@ -165,7 +171,7 @@ Window {
}
}
FluLoader{
id:loader_frameless
id:loader_frameless_helper
}
Item{
id:layout_container

View File

@ -64,6 +64,11 @@ Rectangle{
FluTheme.darkMode = FluThemeType.Dark
}
}
property var systemMenuListener: function(){
if(d.win instanceof FluWindow){
d.win.showSystemMenu()
}
}
id:control
color: Qt.rgba(0,0,0,0)
height: visible ? 30 : 0
@ -71,6 +76,7 @@ Rectangle{
z: 65535
Item{
id:d
property bool hoverMaxBtn: false
property var win: Window.window
property bool stayTop: {
if(d.win instanceof FluWindow){
@ -83,14 +89,23 @@ Rectangle{
}
MouseArea{
anchors.fill: parent
onPositionChanged: {
d.win.startSystemMove()
}
onDoubleClicked: {
if(d.resizable){
btn_maximize.clicked()
onPositionChanged:
(mouse)=>{
d.win.startSystemMove()
}
onDoubleClicked:
(mouse)=>{
if(d.resizable && Qt.LeftButton){
btn_maximize.clicked()
}
}
acceptedButtons: Qt.LeftButton|Qt.RightButton
onClicked:
(mouse)=>{
if (mouse.button === Qt.RightButton){
control.systemMenuListener()
}
}
}
}
Row{
anchors{
@ -218,7 +233,7 @@ Rectangle{
if(pressed){
return maximizePressColor
}
return hovered ? maximizeHoverColor : maximizeNormalColor
return d.hoverMaxBtn ? maximizeHoverColor : maximizeNormalColor
}
Layout.alignment: Qt.AlignVCenter
visible: d.resizable && !isMac && showMaximize
@ -263,4 +278,17 @@ Rectangle{
function darkButton(){
return btn_dark
}
function maximizeButtonHover(){
var hover = false;
var pos = btn_maximize.mapToGlobal(0,0)
if(btn_maximize.visible){
var rect = Qt.rect(pos.x,pos.y,btn_maximize.width,btn_maximize.height)
pos = FluTools.cursorPos()
if(pos.x>rect.x && pos.x<(rect.x+rect.width) && pos.y>rect.y && pos.y<(rect.y+rect.height)){
hover = true;
}
}
d.hoverMaxBtn = hover
return hover;
}
}

View File

@ -49,6 +49,7 @@ Window {
event.accepted = false
}
}
signal showSystemMenu
signal initArgument(var argument)
signal firstVisible()
id:window
@ -57,7 +58,7 @@ Window {
moveWindowToDesktopCenter()
useSystemAppBar = FluApp.useSystemAppBar
if(!useSystemAppBar){
loader_frameless.sourceComponent = com_frameless
loader_frameless_helper.sourceComponent = com_frameless
}
lifecycle.onCompleted(window)
initArgument(argument)
@ -70,6 +71,11 @@ Window {
Component.onDestruction: {
lifecycle.onDestruction()
}
onShowSystemMenu: {
if(loader_frameless_helper.item){
loader_frameless_helper.item.showSystemMenu()
}
}
onVisibleChanged: {
if(visible && d.isFirstVisible){
window.firstVisible()
@ -164,7 +170,7 @@ Window {
}
}
FluLoader{
id:loader_frameless
id:loader_frameless_helper
}
Item{
id:layout_container