add frameless dialog & demo

And some other minor fixes.

Signed-off-by: Yuhang Zhao <2546789017@qq.com>
This commit is contained in:
Yuhang Zhao 2022-09-02 14:10:33 +08:00
parent 5018d9ea82
commit b9f65aa783
24 changed files with 649 additions and 33 deletions

View File

@ -26,6 +26,7 @@ if(FRAMELESSHELPER_BUILD_WIDGETS AND TARGET Qt${QT_VERSION_MAJOR}::Widgets)
add_subdirectory(widget)
add_subdirectory(mainwindow)
add_subdirectory(openglwidget)
add_subdirectory(dialog)
endif()
if(FRAMELESSHELPER_BUILD_QUICK AND TARGET Qt${QT_VERSION_MAJOR}::Quick AND ${QT_VERSION_MAJOR} GREATER_EQUAL 6)

View File

@ -0,0 +1,52 @@
#[[
MIT License
Copyright (C) 2022 by wangwenx190 (Yuhang Zhao)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
set(SOURCES
dialog.h
dialog.cpp
main.cpp
)
if(WIN32)
enable_language(RC)
list(APPEND SOURCES ../example.rc ../example.manifest)
endif()
add_executable(Dialog ${SOURCES})
target_link_libraries(Dialog PRIVATE
Qt${QT_VERSION_MAJOR}::Widgets
FramelessHelper::Widgets
)
target_compile_definitions(Dialog PRIVATE
QT_NO_KEYWORDS
)
include(../../src/core/cmakehelper.cmake)
setup_gui_app(Dialog)
setup_compile_params(Dialog)
if(FRAMELESSHELPER_EXAMPLES_DEPLOYQT)
deploy_qt_runtime(Dialog)
endif()

103
examples/dialog/dialog.cpp Normal file
View File

@ -0,0 +1,103 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "dialog.h"
#include <QtWidgets/qlabel.h>
#include <QtWidgets/qlineedit.h>
#include <QtWidgets/qcheckbox.h>
#include <QtWidgets/qdialogbuttonbox.h>
#include <QtWidgets/qpushbutton.h>
#include <QtWidgets/qboxlayout.h>
#include <QtWidgets/qfileiconprovider.h>
#include <StandardTitleBar>
#include <FramelessWidgetsHelper>
#include <StandardSystemButton>
#include <private/framelesswidgetshelper_p.h>
FRAMELESSHELPER_USE_NAMESPACE
using namespace Global;
Dialog::Dialog(QWidget *parent) : FramelessDialog(parent)
{
setupUi();
}
Dialog::~Dialog() = default;
void Dialog::setupUi()
{
setWindowTitle(tr("Qt Dialog demo"));
setWindowIcon(QFileIconProvider().icon(QFileIconProvider::Computer));
titleBar = new StandardTitleBar(this);
titleBar->setWindowIconVisible(true);
titleBar->maximizeButton()->hide();
label = new QLabel(tr("Find &what:"));
lineEdit = new QLineEdit;
label->setBuddy(lineEdit);
caseCheckBox = new QCheckBox(tr("Match &case"));
fromStartCheckBox = new QCheckBox(tr("Search from &start"));
fromStartCheckBox->setChecked(true);
findButton = new QPushButton(tr("&Find"));
findButton->setDefault(true);
moreButton = new QPushButton(tr("&More"));
moreButton->setCheckable(true);
moreButton->setAutoDefault(false);
extension = new QWidget;
wholeWordsCheckBox = new QCheckBox(tr("&Whole words"));
backwardCheckBox = new QCheckBox(tr("Search &backward"));
searchSelectionCheckBox = new QCheckBox(tr("Search se&lection"));
buttonBox = new QDialogButtonBox(Qt::Vertical);
buttonBox->addButton(findButton, QDialogButtonBox::ActionRole);
buttonBox->addButton(moreButton, QDialogButtonBox::ActionRole);
connect(moreButton, &QPushButton::toggled, extension, &QWidget::setVisible);
QVBoxLayout *extensionLayout = new QVBoxLayout;
extensionLayout->setContentsMargins(0, 0, 0, 0);
extensionLayout->addWidget(wholeWordsCheckBox);
extensionLayout->addWidget(backwardCheckBox);
extensionLayout->addWidget(searchSelectionCheckBox);
extension->setLayout(extensionLayout);
QHBoxLayout *topLeftLayout = new QHBoxLayout;
topLeftLayout->addWidget(label);
topLeftLayout->addWidget(lineEdit);
QVBoxLayout *leftLayout = new QVBoxLayout;
leftLayout->addLayout(topLeftLayout);
leftLayout->addWidget(caseCheckBox);
leftLayout->addWidget(fromStartCheckBox);
QGridLayout *controlsLayout = new QGridLayout;
controlsLayout->setContentsMargins(11, 11, 11, 11);
controlsLayout->addLayout(leftLayout, 0, 0);
controlsLayout->addWidget(buttonBox, 0, 1);
controlsLayout->addWidget(extension, 1, 0, 1, 2);
controlsLayout->setRowStretch(2, 1);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->setSizeConstraint(QLayout::SetFixedSize);
mainLayout->addWidget(titleBar);
mainLayout->addLayout(controlsLayout);
setLayout(mainLayout);
extension->hide();
FramelessWidgetsHelper *helper = FramelessWidgetsHelper::get(this);
helper->setTitleBarWidget(titleBar);
helper->setSystemButton(titleBar->minimizeButton(), SystemButtonType::Minimize);
helper->setSystemButton(titleBar->maximizeButton(), SystemButtonType::Maximize);
helper->setSystemButton(titleBar->closeButton(), SystemButtonType::Close);
FramelessWidgetsHelperPrivate::get(helper)->setProperty(FRAMELESSHELPER_BYTEARRAY_LITERAL("FRAMELESSHELPER_DONT_OVERRIDE_CURSOR"), true);
}

46
examples/dialog/dialog.h Normal file
View File

@ -0,0 +1,46 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#pragma once
#include <FramelessDialog>
QT_BEGIN_NAMESPACE
class QCheckBox;
class QDialogButtonBox;
class QGroupBox;
class QLabel;
class QLineEdit;
class QPushButton;
QT_END_NAMESPACE
FRAMELESSHELPER_BEGIN_NAMESPACE
class StandardTitleBar;
FRAMELESSHELPER_END_NAMESPACE
class Dialog : public FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessDialog)
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(Dialog)
public:
explicit Dialog(QWidget *parent = nullptr);
~Dialog() override;
private:
void setupUi();
private:
FRAMELESSHELPER_PREPEND_NAMESPACE(StandardTitleBar) *titleBar = nullptr;
QLabel *label = nullptr;
QLineEdit *lineEdit = nullptr;
QCheckBox *caseCheckBox = nullptr;
QCheckBox *fromStartCheckBox = nullptr;
QCheckBox *wholeWordsCheckBox = nullptr;
QCheckBox *searchSelectionCheckBox = nullptr;
QCheckBox *backwardCheckBox = nullptr;
QDialogButtonBox *buttonBox = nullptr;
QPushButton *findButton = nullptr;
QPushButton *moreButton = nullptr;
QWidget *extension = nullptr;
};

58
examples/dialog/main.cpp Normal file
View File

@ -0,0 +1,58 @@
/*
* MIT License
*
* Copyright (C) 2022 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <QtWidgets/qapplication.h>
#include <framelessconfig_p.h>
#include <clocale>
#include "dialog.h"
FRAMELESSHELPER_USE_NAMESPACE
int main(int argc, char *argv[])
{
std::setlocale(LC_ALL, "en_US.UTF-8");
// Not necessary, but better call this function, before the construction
// of any Q(Core|Gui)Application instances.
FramelessHelper::Widgets::initialize();
QApplication application(argc, argv);
// Must be called after QGuiApplication has been constructed, we are using
// some private functions from QPA which won't be available until there's
// a QGuiApplication instance.
FramelessHelper::Core::setApplicationOSThemeAware(true, false);
FramelessConfig::instance()->set(Global::Option::WindowUseRoundCorners);
FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow);
Dialog dialog;
dialog.show();
const int exec = QCoreApplication::exec();
FramelessHelper::Widgets::uninitialize();
return exec;
}

View File

@ -452,6 +452,10 @@ using GetWindowIdCallback = std::function<WId()>;
using ShouldIgnoreMouseEventsCallback = std::function<bool(const QPoint &)>;
using ShowSystemMenuCallback = std::function<void(const QPoint &)>;
using GetCurrentApplicationTypeCallback = std::function<ApplicationType()>;
using SetPropertyCallback = std::function<void(const QByteArray &, const QVariant &)>;
using GetPropertyCallback = std::function<QVariant(const QByteArray &, const QVariant &)>;
using SetCursorCallback = std::function<void(const QCursor &)>;
using UnsetCursorCallback = std::function<void()>;
struct SystemParameters
{
@ -477,6 +481,10 @@ struct SystemParameters
ShouldIgnoreMouseEventsCallback shouldIgnoreMouseEvents = nullptr;
ShowSystemMenuCallback showSystemMenu = nullptr;
GetCurrentApplicationTypeCallback getCurrentApplicationType = nullptr;
SetPropertyCallback setProperty = nullptr;
GetPropertyCallback getProperty = nullptr;
SetCursorCallback setCursor = nullptr;
UnsetCursorCallback unsetCursor = nullptr;
[[nodiscard]] inline bool isValid() const
{
@ -502,6 +510,10 @@ struct SystemParameters
Q_ASSERT(shouldIgnoreMouseEvents);
Q_ASSERT(showSystemMenu);
Q_ASSERT(getCurrentApplicationType);
Q_ASSERT(setProperty);
Q_ASSERT(getProperty);
Q_ASSERT(setCursor);
Q_ASSERT(unsetCursor);
return (getWindowFlags && setWindowFlags && getWindowSize
&& setWindowSize && getWindowPosition && setWindowPosition
&& getWindowScreen && isWindowFixedSize && setWindowFixedSize
@ -509,7 +521,8 @@ struct SystemParameters
&& windowToScreen && screenToWindow && isInsideSystemButtons
&& isInsideTitleBarDraggableArea && getWindowDevicePixelRatio
&& setSystemButtonState && getWindowId && shouldIgnoreMouseEvents
&& showSystemMenu && getCurrentApplicationType);
&& showSystemMenu && getCurrentApplicationType && setProperty
&& getProperty && setCursor && unsetCursor);
}
};

View File

@ -71,6 +71,9 @@ public:
Q_NODISCARD bool isBlurBehindWindowEnabled() const;
void setBlurBehindWindowEnabled(const bool value, const QColor &color);
void setProperty(const QByteArray &name, const QVariant &value);
Q_NODISCARD QVariant getProperty(const QByteArray &name, const QVariant &defaultValue = {});
protected:
Q_NODISCARD bool eventFilter(QObject *object, QEvent *event) override;

View File

@ -0,0 +1 @@
#include <framelessdialog.h>

View File

@ -0,0 +1,53 @@
/*
* MIT License
*
* Copyright (C) 2022 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include "framelesshelperwidgets_global.h"
#include <QtCore/qloggingcategory.h>
#include <QtWidgets/qdialog.h>
FRAMELESSHELPER_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcFramelessDialog)
class FramelessDialogPrivate;
class FRAMELESSHELPER_WIDGETS_API FramelessDialog : public QDialog
{
Q_OBJECT
Q_DECLARE_PRIVATE(FramelessDialog)
Q_DISABLE_COPY_MOVE(FramelessDialog)
public:
explicit FramelessDialog(QWidget *parent = nullptr);
~FramelessDialog() override;
private:
QScopedPointer<FramelessDialogPrivate> d_ptr;
};
FRAMELESSHELPER_END_NAMESPACE
Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessDialog))

View File

@ -0,0 +1,61 @@
/*
* MIT License
*
* Copyright (C) 2022 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include "framelesshelperwidgets_global.h"
#include "framelessdialog.h"
#include <QtCore/qobject.h>
#include <QtCore/qpointer.h>
FRAMELESSHELPER_BEGIN_NAMESPACE
class WidgetsSharedHelper;
class FRAMELESSHELPER_WIDGETS_API FramelessDialogPrivate : public QObject
{
Q_OBJECT
Q_DECLARE_PUBLIC(FramelessDialog)
Q_DISABLE_COPY_MOVE(FramelessDialogPrivate)
public:
explicit FramelessDialogPrivate(FramelessDialog *q);
~FramelessDialogPrivate() override;
Q_NODISCARD static FramelessDialogPrivate *get(FramelessDialog *pub);
Q_NODISCARD static const FramelessDialogPrivate *get(const FramelessDialog *pub);
Q_NODISCARD WidgetsSharedHelper *widgetsSharedHelper() const;
private:
void initialize();
private:
QPointer<FramelessDialog> q_ptr = nullptr;
QScopedPointer<WidgetsSharedHelper> m_helper;
};
FRAMELESSHELPER_END_NAMESPACE
Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessDialogPrivate))

View File

@ -67,6 +67,9 @@ public:
Q_NODISCARD bool isBlurBehindWindowEnabled() const;
void setBlurBehindWindowEnabled(const bool enable, const QColor &color);
void setProperty(const QByteArray &name, const QVariant &value);
Q_NODISCARD QVariant getProperty(const QByteArray &name, const QVariant &defaultValue = {});
private:
Q_NODISCARD QRect mapWidgetGeometryToScene(const QWidget * const widget) const;
Q_NODISCARD bool isInSystemButtons(const QPoint &pos, Global::SystemButtonType *button) const;

View File

@ -22,12 +22,14 @@ HEADERS += \
$$WIDGETS_PUB_INC_DIR/standardsystembutton.h \
$$WIDGETS_PUB_INC_DIR/framelesswidgetshelper.h \
$$WIDGETS_PUB_INC_DIR/standardtitlebar.h \
$$WIDGETS_PUB_INC_DIR/framelessdialog.h \
$$WIDGETS_PRIV_INC_DIR/framelesswidgetshelper_p.h \
$$WIDGETS_PRIV_INC_DIR/standardsystembutton_p.h \
$$WIDGETS_PRIV_INC_DIR/standardtitlebar_p.h \
$$WIDGETS_PRIV_INC_DIR/framelesswidget_p.h \
$$WIDGETS_PRIV_INC_DIR/framelessmainwindow_p.h \
$$WIDGETS_PRIV_INC_DIR/widgetssharedhelper_p.h
$$WIDGETS_PRIV_INC_DIR/widgetssharedhelper_p.h \
$$WIDGETS_PRIV_INC_DIR/framelessdialog_p.h
SOURCES += \
$$WIDGETS_SRC_DIR/framelessmainwindow.cpp \
@ -36,4 +38,5 @@ SOURCES += \
$$WIDGETS_SRC_DIR/standardsystembutton.cpp \
$$WIDGETS_SRC_DIR/standardtitlebar.cpp \
$$WIDGETS_SRC_DIR/widgetssharedhelper.cpp \
$$WIDGETS_SRC_DIR/framelesshelperwidgets_global.cpp
$$WIDGETS_SRC_DIR/framelesshelperwidgets_global.cpp \
$$WIDGETS_SRC_DIR/framelessdialog.cpp

View File

@ -41,6 +41,8 @@ Q_LOGGING_CATEGORY(lcFramelessHelperQt, "wangwenx190.framelesshelper.core.impl.q
using namespace Global;
FRAMELESSHELPER_BYTEARRAY_CONSTANT2(DontOverrideCursor, "FRAMELESSHELPER_DONT_OVERRIDE_CURSOR")
struct QtHelperData
{
SystemParameters params = {};
@ -151,6 +153,7 @@ bool FramelessHelperQt::eventFilter(QObject *object, QEvent *event)
const bool windowFixedSize = data.params.isWindowFixedSize();
const bool ignoreThisEvent = data.params.shouldIgnoreMouseEvents(scenePos);
const bool insideTitleBar = data.params.isInsideTitleBarDraggableArea(scenePos);
const bool dontOverrideCursor = data.params.getProperty(kDontOverrideCursor, false).toBool();
switch (type) {
case QEvent::MouseButtonPress: {
if (button == Qt::LeftButton) {
@ -188,16 +191,16 @@ bool FramelessHelperQt::eventFilter(QObject *object, QEvent *event)
}
} break;
case QEvent::MouseMove: {
if (!windowFixedSize) {
if (!windowFixedSize && !dontOverrideCursor) {
const Qt::CursorShape cs = Utils::calculateCursorShape(window, scenePos);
if (cs == Qt::ArrowCursor) {
if (data.cursorShapeChanged) {
window->unsetCursor();
data.params.unsetCursor();
const QMutexLocker locker(&g_qtHelper()->mutex);
g_qtHelper()->data[windowId].cursorShapeChanged = false;
}
} else {
window->setCursor(cs);
data.params.setCursor(cs);
const QMutexLocker locker(&g_qtHelper()->mutex);
g_qtHelper()->data[windowId].cursorShapeChanged = true;
}

View File

@ -73,6 +73,7 @@ FRAMELESSHELPER_STRING_CONSTANT(SetWindowPos)
FRAMELESSHELPER_STRING_CONSTANT(TrackMouseEvent)
FRAMELESSHELPER_STRING_CONSTANT(FindWindowW)
FRAMELESSHELPER_STRING_CONSTANT(UnregisterClassW)
FRAMELESSHELPER_BYTEARRAY_CONSTANT2(DontOverrideCursor, "FRAMELESSHELPER_DONT_OVERRIDE_CURSOR")
struct Win32HelperData
{
@ -657,7 +658,6 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
// modify the `RECT` that `lParam` points to, so that its value upon
// our return is the new client area. We must return 0 if `wParam`
// is `FALSE`.
//
// If `wParam` is `TRUE`, `lParam` points to a `NCCALCSIZE_PARAMS`
// struct. This struct contains an array of 3 `RECT`s, the first of
// which has the exact same meaning as the `RECT` that is pointed to
@ -668,19 +668,31 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
// implement an elaborate client-area preservation technique, and
// simply return 0, which means "preserve the entire old client area
// and align it with the upper-left corner of our new client area".
const auto clientRect = ((static_cast<BOOL>(wParam) == FALSE)
? reinterpret_cast<LPRECT>(lParam)
: &(reinterpret_cast<LPNCCALCSIZE_PARAMS>(lParam))->rgrc[0]);
const auto clientRect = ((static_cast<BOOL>(wParam) == FALSE) ?
reinterpret_cast<LPRECT>(lParam) : &(reinterpret_cast<LPNCCALCSIZE_PARAMS>(lParam))->rgrc[0]);
if (frameBorderVisible) {
// Store the original top before the default window proc applies the default frame.
// Store the original top margin before the default window procedure applies the default frame.
const LONG originalTop = clientRect->top;
// Apply the default frame.
// Apply the default frame because we don't want to remove the whole window frame,
// we still need the standard window frame (the resizable frame border and the frame
// shadow) for the left, bottom and right edges.
// If we return 0 here directly, the whole window frame will be removed (which means
// there will be no resizable frame border and the frame shadow will also disappear),
// and that's also how most applications customize their title bars on Windows. It's
// totally OK but since we want to preserve as much original frame as possible, we
// can't use that solution.
const LRESULT ret = DefWindowProcW(hWnd, WM_NCCALCSIZE, wParam, lParam);
if (ret != 0) {
*result = ret;
return true;
}
// Re-apply the original top from before the size of the default frame was applied.
// Re-apply the original top from before the size of the default frame was applied,
// and the whole top frame (the title bar and the top border) is gone now.
// For the top frame, we only has 2 choices: (1) remove the top frame entirely, or
// (2) don't touch it at all. We can't preserve the top border by adjusting the top
// margin here. If we try to modify the top margin, the original title bar will
// always be painted by DWM regardless what margin we set, so here we can only remove
// the top frame entirely and use some special technique to bring the top border back.
clientRect->top = originalTop;
}
const bool max = IsMaximized(hWnd);
@ -915,12 +927,13 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
(GetAsyncKeyState(VK_RBUTTON) < 0) : (GetAsyncKeyState(VK_LBUTTON) < 0));
const bool isTitleBar = (data.params.isInsideTitleBarDraggableArea(qtScenePos) && leftButtonPressed);
const bool isFixedSize = data.params.isWindowFixedSize();
const bool dontOverrideCursor = data.params.getProperty(kDontOverrideCursor, false).toBool();
if (frameBorderVisible) {
// This will handle the left, right and bottom parts of the frame
// because we didn't change them.
const LRESULT originalRet = DefWindowProcW(hWnd, WM_NCHITTEST, 0, lParam);
if (originalRet != HTCLIENT) {
*result = originalRet;
*result = (dontOverrideCursor ? HTBORDER : originalRet);
return true;
}
if (full) {
@ -937,7 +950,10 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
// the little border at the top which the user can use to move or
// resize the window.
if (isTop && !isFixedSize) {
*result = HTTOP;
// Return HTCLIENT instead of HTBORDER here, because the mouse is
// inside our homemade title bar now, return HTCLIENT to let our
// title bar can still capture mouse events.
*result = (dontOverrideCursor ? HTCLIENT : HTTOP);
return true;
}
if (isTitleBar) {
@ -970,6 +986,13 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
const int scaledFrameSizeX = qRound(qreal(frameSizeX) * scaleFactor);
const bool isLeft = (nativeLocalPos.x < scaledFrameSizeX);
const bool isRight = (nativeLocalPos.x >= (width - scaledFrameSizeX));
if (dontOverrideCursor && (isTop || isBottom || isLeft || isRight)) {
// Return HTCLIENT instead of HTBORDER here, because the mouse is
// inside the window now, return HTCLIENT to let the controls
// inside our window can still capture mouse events.
*result = HTCLIENT;
return true;
}
if (isTop) {
if (isLeft) {
*result = HTTOPLEFT;

View File

@ -384,9 +384,9 @@ static inline void expblur(QImage &img, qreal radius, const bool improvedQuality
if (p) {
p->save();
p->setRenderHints(QPainter::Antialiasing |
QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
p->scale(scale, scale);
p->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing
| QPainter::SmoothPixmapTransform, quality);
#if (QT_VERSION >= QT_VERSION_CHECK(6, 2, 0))
const QSize imageSize = blurImage.deviceIndependentSize().toSize();
#else
@ -527,15 +527,20 @@ void MicaMaterialPrivate::maybeGenerateBlurredWallpaper(const bool force)
const QRect desktopRect = {desktopOriginPoint, size};
if (aspectStyle == WallpaperAspectStyle::Tile) {
QPainter bufferPainter(&buffer);
const QBrush brush(image);
bufferPainter.fillRect(desktopRect, brush);
bufferPainter.setRenderHints(QPainter::Antialiasing |
QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
bufferPainter.fillRect(desktopRect, QBrush(image));
} else {
QPainter bufferPainter(&buffer);
bufferPainter.setRenderHints(QPainter::Antialiasing |
QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
const QRect rect = alignedRect(Qt::LeftToRight, Qt::AlignCenter, image.size(), desktopRect);
bufferPainter.drawImage(rect.topLeft(), image);
}
g_micaMaterialData()->mutex.lock();
QPainter painter(&g_micaMaterialData()->blurredWallpaper);
painter.setRenderHints(QPainter::Antialiasing |
QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
#if 1
qt_blurImage(&painter, buffer, kDefaultBlurRadius, true, false);
#else
@ -555,6 +560,8 @@ void MicaMaterialPrivate::updateMaterialBrush()
fillColor.setAlphaF(0.9f);
micaTexture.fill(fillColor);
QPainter painter(&micaTexture);
painter.setRenderHints(QPainter::Antialiasing |
QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
painter.setOpacity(tintOpacity);
const QRect rect = {QPoint(0, 0), micaTexture.size()};
painter.fillRect(rect, tintColor);
@ -573,6 +580,8 @@ void MicaMaterialPrivate::paint(QPainter *painter, const QSize &size, const QPoi
}
static constexpr const QPoint originPoint = {0, 0};
painter->save();
painter->setRenderHints(QPainter::Antialiasing |
QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
g_micaMaterialData()->mutex.lock();
painter->drawPixmap(originPoint, g_micaMaterialData()->blurredWallpaper, QRect(pos, size));
g_micaMaterialData()->mutex.unlock();

View File

@ -761,7 +761,7 @@ void Utils::showSystemMenu(const WId windowId, const QPoint &pos, const bool sel
// Tweak the menu items according to the current window status.
const bool maxOrFull = (IsMaximized(hWnd) || isFullScreen(windowId));
const bool fixedSize = isWindowFixedSize();
EnableMenuItem(hMenu, SC_RESTORE, (MF_BYCOMMAND | ((maxOrFull && !fixedSize) ? MFS_ENABLED : MFS_DISABLED)));
EnableMenuItem(hMenu, SC_RESTORE, (MF_BYCOMMAND | ((maxOrFull && !fixedSize) ? MFS_ENABLED : MFS_GRAYED)));
// The first menu item should be selected by default if the menu is brought
// up by keyboard. I don't know how to pre-select a menu item but it seems
// highlight can do the job. However, there's an annoying issue if we do
@ -772,10 +772,10 @@ void Utils::showSystemMenu(const WId windowId, const QPoint &pos, const bool sel
// highlight bar to indicate the current selected menu item, which will make
// the menu look kind of weird. Currently I don't know how to fix this issue.
HiliteMenuItem(hWnd, hMenu, SC_RESTORE, (MF_BYCOMMAND | (selectFirstEntry ? MFS_HILITE : MFS_UNHILITE)));
EnableMenuItem(hMenu, SC_MOVE, (MF_BYCOMMAND | (!maxOrFull ? MFS_ENABLED : MFS_DISABLED)));
EnableMenuItem(hMenu, SC_SIZE, (MF_BYCOMMAND | ((!maxOrFull && !fixedSize) ? MFS_ENABLED : MFS_DISABLED)));
EnableMenuItem(hMenu, SC_MOVE, (MF_BYCOMMAND | (!maxOrFull ? MFS_ENABLED : MFS_GRAYED)));
EnableMenuItem(hMenu, SC_SIZE, (MF_BYCOMMAND | ((!maxOrFull && !fixedSize) ? MFS_ENABLED : MFS_GRAYED)));
EnableMenuItem(hMenu, SC_MINIMIZE, (MF_BYCOMMAND | MFS_ENABLED));
EnableMenuItem(hMenu, SC_MAXIMIZE, (MF_BYCOMMAND | ((!maxOrFull && !fixedSize) ? MFS_ENABLED : MFS_DISABLED)));
EnableMenuItem(hMenu, SC_MAXIMIZE, (MF_BYCOMMAND | ((!maxOrFull && !fixedSize) ? MFS_ENABLED : MFS_GRAYED)));
EnableMenuItem(hMenu, SC_CLOSE, (MF_BYCOMMAND | MFS_ENABLED));
// The default menu item will appear in bold font. There can only be one default

View File

@ -196,6 +196,10 @@ void FramelessQuickHelperPrivate::attachToWindow()
params.shouldIgnoreMouseEvents = [this](const QPoint &pos) -> bool { return shouldIgnoreMouseEvents(pos); };
params.showSystemMenu = [this](const QPoint &pos) -> void { showSystemMenu(pos); };
params.getCurrentApplicationType = []() -> ApplicationType { return ApplicationType::Quick; };
params.setProperty = [this](const QByteArray &name, const QVariant &value) -> void { setProperty(name, value); };
params.getProperty = [this](const QByteArray &name, const QVariant &defaultValue) -> QVariant { return getProperty(name, defaultValue); };
params.setCursor = [window](const QCursor &cursor) -> void { window->setCursor(cursor); };
params.unsetCursor = [window]() -> void { window->unsetCursor(); };
g_quickHelper()->mutex.lock();
data->params = params;
@ -454,6 +458,36 @@ void FramelessQuickHelperPrivate::setBlurBehindWindowEnabled(const bool value, c
}
}
void FramelessQuickHelperPrivate::setProperty(const QByteArray &name, const QVariant &value)
{
Q_ASSERT(!name.isEmpty());
Q_ASSERT(value.isValid());
if (name.isEmpty() || !value.isValid()) {
return;
}
Q_Q(FramelessQuickHelper);
QQuickWindow * const window = q->window();
if (!window) {
return;
}
window->setProperty(name.constData(), value);
}
QVariant FramelessQuickHelperPrivate::getProperty(const QByteArray &name, const QVariant &defaultValue)
{
Q_ASSERT(!name.isEmpty());
if (name.isEmpty()) {
return {};
}
Q_Q(FramelessQuickHelper);
const QQuickWindow * const window = q->window();
if (!window) {
return {};
}
const QVariant value = window->property(name.constData());
return (value.isValid() ? value : defaultValue);
}
bool FramelessQuickHelperPrivate::eventFilter(QObject *object, QEvent *event)
{
Q_ASSERT(object);

View File

@ -85,8 +85,8 @@ void QuickImageItemPrivate::paint(QPainter *painter) const
return;
}
painter->save();
painter->setRenderHints(QPainter::Antialiasing
| QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
painter->setRenderHints(QPainter::Antialiasing |
QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
switch (m_source.userType()) {
case QMetaType::QUrl:
fromUrl(m_source.toUrl(), painter);

View File

@ -35,6 +35,7 @@ set(PUBLIC_HEADERS
${INCLUDE_PREFIX}/standardsystembutton.h
${INCLUDE_PREFIX}/framelesswidgetshelper.h
${INCLUDE_PREFIX}/standardtitlebar.h
${INCLUDE_PREFIX}/framelessdialog.h
)
set(PUBLIC_HEADERS_ALIAS
@ -44,6 +45,7 @@ set(PUBLIC_HEADERS_ALIAS
${INCLUDE_PREFIX}/StandardSystemButton
${INCLUDE_PREFIX}/FramelessWidgetsHelper
${INCLUDE_PREFIX}/StandardTitleBar
${INCLUDE_PREFIX}/FramelessDialog
)
set(PRIVATE_HEADERS
@ -53,6 +55,7 @@ set(PRIVATE_HEADERS
${INCLUDE_PREFIX}/private/framelesswidget_p.h
${INCLUDE_PREFIX}/private/framelessmainwindow_p.h
${INCLUDE_PREFIX}/private/widgetssharedhelper_p.h
${INCLUDE_PREFIX}/private/framelessdialog_p.h
)
set(SOURCES
@ -63,6 +66,7 @@ set(SOURCES
standardtitlebar.cpp
widgetssharedhelper.cpp
framelesshelperwidgets_global.cpp
framelessdialog.cpp
)
if(WIN32 AND NOT FRAMELESSHELPER_BUILD_STATIC)

View File

@ -0,0 +1,91 @@
/*
* MIT License
*
* Copyright (C) 2022 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "framelessdialog.h"
#include "framelessdialog_p.h"
#include "framelesswidgetshelper.h"
#include "widgetssharedhelper_p.h"
#include <utils.h>
FRAMELESSHELPER_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcFramelessDialog, "wangwenx190.framelesshelper.widgets.framelessdialog")
#define INFO qCInfo(lcFramelessDialog)
#define DEBUG qCDebug(lcFramelessDialog)
#define WARNING qCWarning(lcFramelessDialog)
#define CRITICAL qCCritical(lcFramelessDialog)
using namespace Global;
FramelessDialogPrivate::FramelessDialogPrivate(FramelessDialog *q) : QObject(q)
{
Q_ASSERT(q);
if (!q) {
return;
}
q_ptr = q;
initialize();
}
FramelessDialogPrivate::~FramelessDialogPrivate() = default;
FramelessDialogPrivate *FramelessDialogPrivate::get(FramelessDialog *pub)
{
Q_ASSERT(pub);
if (!pub) {
return nullptr;
}
return pub->d_func();
}
const FramelessDialogPrivate *FramelessDialogPrivate::get(const FramelessDialog *pub)
{
Q_ASSERT(pub);
if (!pub) {
return nullptr;
}
return pub->d_func();
}
void FramelessDialogPrivate::initialize()
{
Q_Q(FramelessDialog);
FramelessWidgetsHelper::get(q)->extendsContentIntoTitleBar();
m_helper.reset(new WidgetsSharedHelper(this));
m_helper->setup(q);
}
WidgetsSharedHelper *FramelessDialogPrivate::widgetsSharedHelper() const
{
return (m_helper.isNull() ? nullptr : m_helper.data());
}
FramelessDialog::FramelessDialog(QWidget *parent)
: QDialog(parent), d_ptr(new FramelessDialogPrivate(this))
{
}
FramelessDialog::~FramelessDialog() = default;
FRAMELESSHELPER_END_NAMESPACE

View File

@ -0,0 +1 @@
#include "../../include/FramelessHelper/Widgets/framelessdialog.h"

View File

@ -0,0 +1 @@
#include "../../include/FramelessHelper/Widgets/private/framelessdialog_p.h"

View File

@ -28,6 +28,8 @@
#include "framelesswidget_p.h"
#include "framelessmainwindow.h"
#include "framelessmainwindow_p.h"
#include "framelessdialog.h"
#include "framelessdialog_p.h"
#include "widgetssharedhelper_p.h"
#include <QtCore/qmutex.h>
#include <QtCore/qhash.h>
@ -87,6 +89,11 @@ Q_GLOBAL_STATIC(WidgetsHelper, g_widgetsHelper)
return mainWindowPriv->widgetsSharedHelper();
}
}
if (const auto dialog = qobject_cast<FramelessDialog *>(window)) {
if (const auto dialogPriv = FramelessDialogPrivate::get(dialog)) {
return dialogPriv->widgetsSharedHelper();
}
}
return nullptr;
}
@ -230,6 +237,36 @@ void FramelessWidgetsHelperPrivate::setBlurBehindWindowEnabled(const bool enable
}
}
void FramelessWidgetsHelperPrivate::setProperty(const QByteArray &name, const QVariant &value)
{
Q_ASSERT(!name.isEmpty());
Q_ASSERT(value.isValid());
if (name.isEmpty() || !value.isValid()) {
return;
}
QWidget * const window = getWindow();
Q_ASSERT(window);
if (!window) {
return;
}
window->setProperty(name.constData(), value);
}
QVariant FramelessWidgetsHelperPrivate::getProperty(const QByteArray &name, const QVariant &defaultValue)
{
Q_ASSERT(!name.isEmpty());
if (name.isEmpty()) {
return {};
}
const QWidget * const window = getWindow();
Q_ASSERT(window);
if (!window) {
return {};
}
const QVariant value = window->property(name.constData());
return (value.isValid() ? value : defaultValue);
}
void FramelessWidgetsHelperPrivate::setTitleBarWidget(QWidget *widget)
{
Q_ASSERT(widget);
@ -332,6 +369,10 @@ void FramelessWidgetsHelperPrivate::attachToWindow()
params.shouldIgnoreMouseEvents = [this](const QPoint &pos) -> bool { return shouldIgnoreMouseEvents(pos); };
params.showSystemMenu = [this](const QPoint &pos) -> void { showSystemMenu(pos); };
params.getCurrentApplicationType = []() -> ApplicationType { return ApplicationType::Widgets; };
params.setProperty = [this](const QByteArray &name, const QVariant &value) -> void { setProperty(name, value); };
params.getProperty = [this](const QByteArray &name, const QVariant &defaultValue) -> QVariant { return getProperty(name, defaultValue); };
params.setCursor = [window](const QCursor &cursor) -> void { window->setCursor(cursor); };
params.unsetCursor = [window]() -> void { window->unsetCursor(); };
g_widgetsHelper()->mutex.lock();
data->params = params;

View File

@ -146,9 +146,17 @@ void WidgetsSharedHelper::changeEventHandler(QEvent *event)
return;
}
updateContentsMargins();
if (const auto mo = m_targetWidget->metaObject()) {
if (const int idx = mo->indexOfSignal(QMetaObject::normalizedSignature("hiddenChanged()").constData()); idx >= 0) {
QMetaObject::invokeMethod(m_targetWidget, "hiddenChanged");
}
if (const int idx = mo->indexOfSignal(QMetaObject::normalizedSignature("normalChanged()").constData()); idx >= 0) {
QMetaObject::invokeMethod(m_targetWidget, "normalChanged");
}
if (const int idx = mo->indexOfSignal(QMetaObject::normalizedSignature("zoomedChanged()").constData()); idx >= 0) {
QMetaObject::invokeMethod(m_targetWidget, "zoomedChanged");
}
}
#ifdef Q_OS_WINDOWS
const WId windowId = m_targetWidget->winId();
static const bool isWin11OrGreater = Utils::isWindowsVersionOrGreater(WindowsVersion::_11_21H2);
@ -181,6 +189,10 @@ void WidgetsSharedHelper::paintEventHandler(QPaintEvent *event)
if (shouldDrawFrameBorder()) {
QPainter painter(m_targetWidget);
painter.save();
painter.setRenderHints(QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
// We can't enable antialiasing here, because the border is only 1px height,
// it's too thin and antialiasing will break it's painting.
painter.setRenderHint(QPainter::Antialiasing, false);
QPen pen = {};
pen.setColor(Utils::getFrameBorderColor(m_targetWidget->isActiveWindow()));
pen.setWidth(kDefaultWindowFrameBorderThickness);