diff --git a/README.md b/README.md index 879650a..6b9011b 100644 --- a/README.md +++ b/README.md @@ -17,10 +17,14 @@ You can join our [Discord channel](https://discord.gg/grrM4Tmesy) to communicate ## Highlights v2.2 -- Common: Added blur behind window feature for Windows (7~11), Linux and macOS. -- CMake: Implemented CMake package support. It's now possible to use `find_package` to find FramelessHelper. +- Common: Added blur behind window feature for Windows (7~11), Linux and macOS. On Windows 11 and macOS, FramelessHelper will make use of the blur effect provided by the OS to get the best appearance and performance, while on Windows 7~10 and Linux, FramelessHelper will use a homemade blur effect to provide as much consistent experience as possible. +- Common: Added window icon support. It's now possible to set the window icon's image, size and visibility for the standard title bar control. +- Windows: If you are using Qt 6.4+, your Qt Widgets applications will now automatically switch to light/dark theme if the OS theme changes. It requires you are using the default palette provided by Qt. Qt Quick applications will not be affected. +- Linux: FramelessHelper is now theme-aware. If you change your OS theme, FramelessHelper will now emit the theme change signal and refresh it's internal palette. +- Build system: Implemented CMake package support. It's now possible to use `find_package` to find FramelessHelper. +- Build system: Implemented limited QMake support. FramelessHelper doesn't provide complete QMake project, to decrease the maintainance burden, but you can use the .pri files to directly embed FramelessHelper into your own application. - Examples: Enabled blur behind window and round window corner by default. -- Common: Migrated to categorized logging output. +- Common: Migrated to categorized logging output. You can now enable or disable some specific debug messages using QLoggingCategory. - Common: Internal code improvements & bug fixes. ## Highlights v2.1 diff --git a/examples/mainwindow/mainwindow.cpp b/examples/mainwindow/mainwindow.cpp index c720ae3..8b80518 100644 --- a/examples/mainwindow/mainwindow.cpp +++ b/examples/mainwindow/mainwindow.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -118,4 +119,5 @@ QMenuBar::item:pressed { }); setWindowTitle(tr("FramelessHelper demo application - Qt MainWindow")); + setWindowIcon(QFileIconProvider().icon(QFileIconProvider::Computer)); } diff --git a/examples/openglwidget/mainwindow.cpp b/examples/openglwidget/mainwindow.cpp index 831e524..76e8530 100644 --- a/examples/openglwidget/mainwindow.cpp +++ b/examples/openglwidget/mainwindow.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -64,9 +65,11 @@ void MainWindow::closeEvent(QCloseEvent *event) void MainWindow::initialize() { - resize(800, 600); setWindowTitle(tr("FramelessHelper demo application - QOpenGLWidget")); + setWindowIcon(QFileIconProvider().icon(QFileIconProvider::Computer)); + resize(800, 600); m_titleBar = new StandardTitleBar(this); + m_titleBar->setWindowIconVisible(true); m_glWidget = new GLWidget(this); const auto mainLayout = new QVBoxLayout(this); mainLayout->setSpacing(0); diff --git a/examples/quick/CMakeLists.txt b/examples/quick/CMakeLists.txt index abb2c77..c660c6a 100644 --- a/examples/quick/CMakeLists.txt +++ b/examples/quick/CMakeLists.txt @@ -32,7 +32,7 @@ set(SOURCES ) if(${QT_VERSION} VERSION_LESS 6.2) - list(APPEND SOURCES qml.qrc) + list(APPEND SOURCES resources.qrc) endif() if(WIN32) @@ -56,6 +56,10 @@ if(${QT_VERSION} VERSION_GREATER_EQUAL 6.2) #ENABLE_TYPE_COMPILER # We can't use it for now due to it still can't compile singletons. # There's some hope to get it supported in Qt 6.5. ) + qt_add_resources(Quick resources + PREFIX "/Demo" + FILES "images/microsoft.svg" + ) endif() target_link_libraries(Quick PRIVATE diff --git a/examples/quick/MainWindow.qml b/examples/quick/MainWindow.qml index 80c1a73..07f6311 100644 --- a/examples/quick/MainWindow.qml +++ b/examples/quick/MainWindow.qml @@ -111,5 +111,7 @@ FramelessWindow { left: parent.left right: parent.right } + windowIcon: "qrc:///Demo/images/microsoft.svg" + windowIconVisible: true } } diff --git a/examples/quick/images/microsoft.png b/examples/quick/images/microsoft.png new file mode 100644 index 0000000..3cb3b5c Binary files /dev/null and b/examples/quick/images/microsoft.png differ diff --git a/examples/quick/images/microsoft.svg b/examples/quick/images/microsoft.svg new file mode 100644 index 0000000..ffdefbf --- /dev/null +++ b/examples/quick/images/microsoft.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/examples/quick/qml.qrc b/examples/quick/resources.qrc similarity index 69% rename from examples/quick/qml.qrc rename to examples/quick/resources.qrc index e9b1d1b..15e67e9 100644 --- a/examples/quick/qml.qrc +++ b/examples/quick/resources.qrc @@ -1,5 +1,6 @@ + images/microsoft.svg MainWindow.qml diff --git a/examples/widget/widget.cpp b/examples/widget/widget.cpp index ffeac1c..aef6491 100644 --- a/examples/widget/widget.cpp +++ b/examples/widget/widget.cpp @@ -35,6 +35,7 @@ #endif #include #include +#include #include #include #include @@ -83,8 +84,10 @@ void Widget::closeEvent(QCloseEvent *event) void Widget::initialize() { setWindowTitle(tr("FramelessHelper demo application - Qt Widgets")); + setWindowIcon(QFileIconProvider().icon(QFileIconProvider::Computer)); resize(800, 600); m_titleBar.reset(new StandardTitleBar(this)); + m_titleBar->setWindowIconVisible(true); m_clockLabel.reset(new QLabel(this)); m_clockLabel->setFrameShape(QFrame::NoFrame); QFont clockFont = font(); diff --git a/include/FramelessHelper/Core/framelesshelpercore_global.h b/include/FramelessHelper/Core/framelesshelpercore_global.h index e622444..28ecb2b 100644 --- a/include/FramelessHelper/Core/framelesshelpercore_global.h +++ b/include/FramelessHelper/Core/framelesshelpercore_global.h @@ -206,9 +206,9 @@ Q_NAMESPACE_EXPORT(FRAMELESSHELPER_CORE_API) [[maybe_unused]] static constexpr const int kDefaultWindowFrameBorderThickness = 1; [[maybe_unused]] static constexpr const int kDefaultTitleBarFontPointSize = 11; [[maybe_unused]] static constexpr const int kDefaultTitleBarContentsMargin = 10; -[[maybe_unused]] static constexpr const int kDefaultWindowIconSize = 16; +[[maybe_unused]] static constexpr const QSize kDefaultWindowIconSize = {16, 16}; [[maybe_unused]] static constexpr const QSize kDefaultSystemButtonSize = {qRound(qreal(kDefaultTitleBarHeight) * 1.5), kDefaultTitleBarHeight}; -[[maybe_unused]] static constexpr const QSize kDefaultSystemButtonIconSize = {kDefaultWindowIconSize, kDefaultWindowIconSize}; +[[maybe_unused]] static constexpr const QSize kDefaultSystemButtonIconSize = kDefaultWindowIconSize; [[maybe_unused]] static constexpr const QSize kDefaultWindowSize = {160, 160}; // Value taken from QPA. #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) diff --git a/include/FramelessHelper/Quick/QuickImageItem b/include/FramelessHelper/Quick/QuickImageItem new file mode 100644 index 0000000..10f9102 --- /dev/null +++ b/include/FramelessHelper/Quick/QuickImageItem @@ -0,0 +1 @@ +#include diff --git a/include/FramelessHelper/Quick/private/quickimageitem_p.h b/include/FramelessHelper/Quick/private/quickimageitem_p.h new file mode 100644 index 0000000..b6d61ea --- /dev/null +++ b/include/FramelessHelper/Quick/private/quickimageitem_p.h @@ -0,0 +1,74 @@ +/* + * 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 "framelesshelperquick_global.h" +#include +#include +#include +#include +#include + +FRAMELESSHELPER_BEGIN_NAMESPACE + +class QuickImageItem; + +class FRAMELESSHELPER_QUICK_API QuickImageItemPrivate : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(QuickImageItemPrivate) + Q_DECLARE_PUBLIC(QuickImageItem) + +public: + explicit QuickImageItemPrivate(QuickImageItem *q); + ~QuickImageItemPrivate() override; + + Q_NODISCARD static QuickImageItemPrivate *get(QuickImageItem *q); + Q_NODISCARD static const QuickImageItemPrivate *get(const QuickImageItem *q); + + void paint(QPainter *painter); + + Q_NODISCARD QVariant source() const; + void setSource(const QVariant &value); + +private: + void initialize(); + void fromUrl(const QUrl &value, QPainter *painter); + void fromString(const QString &value, QPainter *painter); + void fromImage(const QImage &value, QPainter *painter); + void fromPixmap(const QPixmap &value, QPainter *painter); + void fromIcon(const QIcon &value, QPainter *painter); + Q_NODISCARD QRect paintArea() const; + +private: + QPointer q_ptr = nullptr; + QVariant m_source = {}; + QPixmap m_pixmap = {}; + QIcon m_icon = {}; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(QuickImageItemPrivate)) diff --git a/include/FramelessHelper/Quick/private/quickstandardtitlebar_p.h b/include/FramelessHelper/Quick/private/quickstandardtitlebar_p.h index 1fd0787..23e1049 100644 --- a/include/FramelessHelper/Quick/private/quickstandardtitlebar_p.h +++ b/include/FramelessHelper/Quick/private/quickstandardtitlebar_p.h @@ -37,6 +37,7 @@ QT_END_NAMESPACE FRAMELESSHELPER_BEGIN_NAMESPACE class QuickStandardSystemButton; +class QuickImageItem; class FRAMELESSHELPER_QUICK_API QuickStandardTitleBar : public QQuickRectangle { @@ -53,6 +54,9 @@ class FRAMELESSHELPER_QUICK_API QuickStandardTitleBar : public QQuickRectangle Q_PROPERTY(bool extended READ isExtended WRITE setExtended NOTIFY extendedChanged FINAL) Q_PROPERTY(bool hideWhenClose READ isHideWhenClose WRITE setHideWhenClose NOTIFY hideWhenCloseChanged FINAL) Q_PROPERTY(QuickChromePalette* chromePalette READ chromePalette CONSTANT FINAL) + Q_PROPERTY(QSizeF windowIconSize READ windowIconSize WRITE setWindowIconSize NOTIFY windowIconSizeChanged FINAL) + Q_PROPERTY(bool windowIconVisible READ windowIconVisible WRITE setWindowIconVisible NOTIFY windowIconVisibleChanged FINAL) + Q_PROPERTY(QVariant windowIcon READ windowIcon WRITE setWindowIcon NOTIFY windowIconChanged FINAL) public: explicit QuickStandardTitleBar(QQuickItem *parent = nullptr); @@ -74,6 +78,15 @@ public: Q_NODISCARD QuickChromePalette *chromePalette() const; + Q_NODISCARD QSizeF windowIconSize() const; + void setWindowIconSize(const QSizeF &value); + + Q_NODISCARD bool windowIconVisible() const; + void setWindowIconVisible(const bool value); + + Q_NODISCARD QVariant windowIcon() const; + void setWindowIcon(const QVariant &value); + protected: void itemChange(const ItemChange change, const ItemChangeData &value) override; Q_NODISCARD bool eventFilter(QObject *object, QEvent *event) override; @@ -87,11 +100,15 @@ private Q_SLOTS: void clickMaximizeButton(); void clickCloseButton(); void retranslateUi(); + void updateWindowIcon(); Q_SIGNALS: void titleLabelAlignmentChanged(); void extendedChanged(); void hideWhenCloseChanged(); + void windowIconSizeChanged(); + void windowIconVisibleChanged(); + void windowIconChanged(); private: void initialize(); @@ -99,6 +116,7 @@ private: private: Qt::Alignment m_labelAlignment = {}; + QScopedPointer m_windowIcon; QScopedPointer m_windowTitleLabel; QScopedPointer m_systemButtonsRow; QScopedPointer m_minimizeButton; diff --git a/include/FramelessHelper/Quick/quickimageitem.h b/include/FramelessHelper/Quick/quickimageitem.h new file mode 100644 index 0000000..afb26d8 --- /dev/null +++ b/include/FramelessHelper/Quick/quickimageitem.h @@ -0,0 +1,67 @@ +/* + * 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 "framelesshelperquick_global.h" +#include +#include + +FRAMELESSHELPER_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcQuickImageItem) + +class QuickImageItemPrivate; + +class FRAMELESSHELPER_QUICK_API QuickImageItem : public QQuickPaintedItem +{ + Q_OBJECT +#ifdef QML_NAMED_ELEMENT + QML_NAMED_ELEMENT(ImageItem) +#endif + Q_DISABLE_COPY_MOVE(QuickImageItem) + Q_DECLARE_PRIVATE(QuickImageItem) + + Q_PROPERTY(QVariant source READ source WRITE setSource NOTIFY sourceChanged FINAL) + +public: + explicit QuickImageItem(QQuickItem *parent = nullptr); + ~QuickImageItem() override; + + void paint(QPainter *painter) override; + + Q_NODISCARD QVariant source() const; + void setSource(const QVariant &value); + +Q_SIGNALS: + void sourceChanged(); + +private: + QScopedPointer d_ptr; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(QuickImageItem)) +QML_DECLARE_TYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(QuickImageItem)) diff --git a/include/FramelessHelper/Widgets/private/standardtitlebar_p.h b/include/FramelessHelper/Widgets/private/standardtitlebar_p.h index 6c42023..bc99938 100644 --- a/include/FramelessHelper/Widgets/private/standardtitlebar_p.h +++ b/include/FramelessHelper/Widgets/private/standardtitlebar_p.h @@ -67,6 +67,12 @@ public: Q_NODISCARD bool titleLabelVisible() const; void setTitleLabelVisible(const bool value); + Q_NODISCARD QSize windowIconSize() const; + void setWindowIconSize(const QSize &value); + + Q_NODISCARD bool windowIconVisible() const; + void setWindowIconVisible(const bool value); + public Q_SLOTS: void updateMaximizeButton(); void updateTitleBarColor(); @@ -90,6 +96,8 @@ private: bool m_hideWhenClose = false; QScopedPointer m_chromePalette; bool m_titleLabelVisible = true; + QSize m_windowIconSize = {}; + bool m_windowIconVisible = false; }; FRAMELESSHELPER_END_NAMESPACE diff --git a/include/FramelessHelper/Widgets/standardtitlebar.h b/include/FramelessHelper/Widgets/standardtitlebar.h index dc7696f..9378cef 100644 --- a/include/FramelessHelper/Widgets/standardtitlebar.h +++ b/include/FramelessHelper/Widgets/standardtitlebar.h @@ -49,6 +49,8 @@ class FRAMELESSHELPER_WIDGETS_API StandardTitleBar : public QWidget Q_PROPERTY(bool hideWhenClose READ isHideWhenClose WRITE setHideWhenClose NOTIFY hideWhenCloseChanged FINAL) Q_PROPERTY(ChromePalette* chromePalette READ chromePalette CONSTANT FINAL) Q_PROPERTY(bool titleLabelVisible READ titleLabelVisible WRITE setTitleLabelVisible NOTIFY titleLabelVisibleChanged FINAL) + Q_PROPERTY(QSize windowIconSize READ windowIconSize WRITE setWindowIconSize NOTIFY windowIconSizeChanged FINAL) + Q_PROPERTY(bool windowIconVisible READ windowIconVisible WRITE setWindowIconVisible NOTIFY windowIconVisibleChanged FINAL) public: explicit StandardTitleBar(QWidget *parent = nullptr); @@ -72,6 +74,12 @@ public: Q_NODISCARD bool titleLabelVisible() const; void setTitleLabelVisible(const bool value); + Q_NODISCARD QSize windowIconSize() const; + void setWindowIconSize(const QSize &value); + + Q_NODISCARD bool windowIconVisible() const; + void setWindowIconVisible(const bool value); + protected: void paintEvent(QPaintEvent *event) override; @@ -80,6 +88,8 @@ Q_SIGNALS: void titleLabelAlignmentChanged(); void hideWhenCloseChanged(); void titleLabelVisibleChanged(); + void windowIconSizeChanged(); + void windowIconVisibleChanged(); private: QScopedPointer d_ptr; diff --git a/src/core/micamaterial.cpp b/src/core/micamaterial.cpp index 0738312..08e632b 100644 --- a/src/core/micamaterial.cpp +++ b/src/core/micamaterial.cpp @@ -385,9 +385,8 @@ static inline void expblur(QImage &img, qreal radius, const bool improvedQuality if (p) { p->save(); p->scale(scale, scale); - p->setRenderHint(QPainter::Antialiasing, false); - p->setRenderHint(QPainter::TextAntialiasing, false); - p->setRenderHint(QPainter::SmoothPixmapTransform, false); + 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 @@ -538,7 +537,7 @@ void MicaMaterialPrivate::maybeGenerateBlurredWallpaper(const bool force) g_micaMaterialData()->mutex.lock(); QPainter painter(&g_micaMaterialData()->blurredWallpaper); #if 1 - qt_blurImage(&painter, buffer, kDefaultBlurRadius, false, false); + qt_blurImage(&painter, buffer, kDefaultBlurRadius, true, false); #else painter.drawImage(desktopOriginPoint, buffer); #endif diff --git a/src/quick/CMakeLists.txt b/src/quick/CMakeLists.txt index 7125ff3..2a1f613 100644 --- a/src/quick/CMakeLists.txt +++ b/src/quick/CMakeLists.txt @@ -38,6 +38,7 @@ set(PUBLIC_HEADERS ${INCLUDE_PREFIX}/framelessquickutils.h ${INCLUDE_PREFIX}/quickchromepalette.h ${INCLUDE_PREFIX}/quickmicamaterial.h + ${INCLUDE_PREFIX}/quickimageitem.h ) set(PUBLIC_HEADERS_ALIAS @@ -47,6 +48,7 @@ set(PUBLIC_HEADERS_ALIAS ${INCLUDE_PREFIX}/FramelessQuickUtils ${INCLUDE_PREFIX}/QuickChromePalette ${INCLUDE_PREFIX}/QuickMicaMaterial + ${INCLUDE_PREFIX}/QuickImageItem ) set(PRIVATE_HEADERS @@ -56,6 +58,7 @@ set(PRIVATE_HEADERS ${INCLUDE_PREFIX}/private/framelessquickwindow_p.h ${INCLUDE_PREFIX}/private/framelessquickwindow_p_p.h ${INCLUDE_PREFIX}/private/quickmicamaterial_p.h + ${INCLUDE_PREFIX}/private/quickimageitem_p.h ) set(SOURCES @@ -68,6 +71,7 @@ set(SOURCES quickchromepalette.cpp global.cpp quickmicamaterial.cpp + quickimageitem.cpp ) if(WIN32 AND NOT FRAMELESSHELPER_BUILD_STATIC) diff --git a/src/quick/framelessquickmodule.cpp b/src/quick/framelessquickmodule.cpp index 9223c70..df20630 100644 --- a/src/quick/framelessquickmodule.cpp +++ b/src/quick/framelessquickmodule.cpp @@ -27,6 +27,7 @@ #include "framelessquickutils.h" #include "quickchromepalette.h" #include "quickmicamaterial.h" +#include "quickimageitem.h" #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) # include "quickstandardsystembutton_p.h" # include "quickstandardtitlebar_p.h" @@ -86,6 +87,7 @@ void FramelessHelper::Quick::registerTypes(QQmlEngine *engine) qmlRegisterType(QUICK_URI_EXPAND("FramelessHelper")); qmlRegisterType(QUICK_URI_EXPAND("MicaMaterial")); + qmlRegisterType(QUICK_URI_EXPAND("ImageItem")); #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) qmlRegisterType(QUICK_URI_EXPAND("StandardSystemButton")); diff --git a/src/quick/global.cpp b/src/quick/global.cpp index 337a6a0..3995753 100644 --- a/src/quick/global.cpp +++ b/src/quick/global.cpp @@ -34,6 +34,8 @@ # include "framelessquickwindow_p_p.h" # include "quickmicamaterial.h" # include "quickmicamaterial_p.h" +# include "quickimageitem.h" +# include "quickimageitem_p.h" #endif // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) FRAMELESSHELPER_BEGIN_NAMESPACE @@ -89,6 +91,9 @@ void initialize() qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); #endif // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) } diff --git a/src/quick/quickimageitem.cpp b/src/quick/quickimageitem.cpp new file mode 100644 index 0000000..120d431 --- /dev/null +++ b/src/quick/quickimageitem.cpp @@ -0,0 +1,235 @@ +/* + * 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 "quickimageitem.h" +#include "quickimageitem_p.h" +#include +#include +#include + +FRAMELESSHELPER_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcQuickImageItem, "wangwenx190.framelesshelper.quick.quickimageitem") +#define INFO qCInfo(lcQuickImageItem) +#define DEBUG qCDebug(lcQuickImageItem) +#define WARNING qCWarning(lcQuickImageItem) +#define CRITICAL qCCritical(lcQuickImageItem) + +using namespace Global; + +FRAMELESSHELPER_STRING_CONSTANT2(QrcPrefix, "qrc:") +FRAMELESSHELPER_STRING_CONSTANT2(FileSystemPrefix, ":") +FRAMELESSHELPER_STRING_CONSTANT2(UrlPrefix, ":///") +FRAMELESSHELPER_STRING_CONSTANT2(FilePathPrefix, ":/") + +QuickImageItemPrivate::QuickImageItemPrivate(QuickImageItem *q) : QObject(q) +{ + Q_ASSERT(q); + if (!q) { + return; + } + q_ptr = q; + initialize(); +} + +QuickImageItemPrivate::~QuickImageItemPrivate() = default; + +QuickImageItemPrivate *QuickImageItemPrivate::get(QuickImageItem *q) +{ + Q_ASSERT(q); + if (!q) { + return nullptr; + } + return q->d_func(); +} + +const QuickImageItemPrivate *QuickImageItemPrivate::get(const QuickImageItem *q) +{ + Q_ASSERT(q); + if (!q) { + return nullptr; + } + return q->d_func(); +} + +void QuickImageItemPrivate::paint(QPainter *painter) +{ + Q_ASSERT(painter); + if (!painter) { + return; + } + if (!m_source.isValid()) { + return; + } + painter->save(); + painter->setRenderHints(QPainter::Antialiasing + | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform); + switch (m_source.userType()) { + case QMetaType::QUrl: + fromUrl(m_source.toUrl(), painter); + break; + case QMetaType::QString: + fromString(m_source.toString(), painter); + break; + case QMetaType::QImage: + fromImage(qvariant_cast(m_source), painter); + break; + case QMetaType::QPixmap: + fromPixmap(qvariant_cast(m_source), painter); + break; + case QMetaType::QIcon: + fromIcon(qvariant_cast(m_source), painter); + break; + default: + WARNING << "Unsupported type:" << m_source.typeName(); + break; + } + painter->restore(); +} + +QVariant QuickImageItemPrivate::source() const +{ + return m_source; +} + +void QuickImageItemPrivate::setSource(const QVariant &value) +{ + Q_ASSERT(value.isValid()); + if (!value.isValid()) { + return; + } + if (m_source == value) { + return; + } + m_source = value; + Q_Q(QuickImageItem); + q->update(); + Q_EMIT q->sourceChanged(); +} + +void QuickImageItemPrivate::initialize() +{ + Q_Q(QuickImageItem); + q->setAntialiasing(true); + q->setSmooth(true); + q->setMipmap(true); + q->setClip(true); +} + +void QuickImageItemPrivate::fromUrl(const QUrl &value, QPainter *painter) +{ + Q_ASSERT(value.isValid()); + Q_ASSERT(painter); + if (!value.isValid() || !painter) { + return; + } + fromString((value.isLocalFile() ? value.toLocalFile() : value.toString()), painter); +} + +void QuickImageItemPrivate::fromString(const QString &value, QPainter *painter) +{ + Q_ASSERT(!value.isEmpty()); + Q_ASSERT(painter); + if (value.isEmpty() || !painter) { + return; + } + return fromPixmap(QPixmap([&value]() -> QString { + QString path = value; + if (path.startsWith(kQrcPrefix, Qt::CaseInsensitive)) { + path.replace(kQrcPrefix, kFileSystemPrefix, Qt::CaseInsensitive); + } + if (path.startsWith(kUrlPrefix, Qt::CaseInsensitive)) { + path.replace(kUrlPrefix, kFilePathPrefix, Qt::CaseInsensitive); + } + return path; + }()), painter); +} + +void QuickImageItemPrivate::fromImage(const QImage &value, QPainter *painter) +{ + Q_ASSERT(!value.isNull()); + Q_ASSERT(painter); + if (value.isNull() || !painter) { + return; + } + fromPixmap(QPixmap::fromImage(value), painter); +} + +void QuickImageItemPrivate::fromPixmap(const QPixmap &value, QPainter *painter) +{ + Q_ASSERT(!value.isNull()); + Q_ASSERT(painter); + if (value.isNull() || !painter) { + return; + } + painter->drawPixmap(paintArea(), value); +} + +void QuickImageItemPrivate::fromIcon(const QIcon &value, QPainter *painter) +{ + Q_ASSERT(!value.isNull()); + Q_ASSERT(painter); + if (value.isNull() || !painter) { + return; + } + value.paint(painter, paintArea()); +} + +QRect QuickImageItemPrivate::paintArea() const +{ + Q_Q(const QuickImageItem); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) + const QSize size = q->size().toSize(); +#else + const QSize size = {qRound(q->width()), qRound(q->height())}; +#endif + return {QPoint(0, 0), size}; +} + +QuickImageItem::QuickImageItem(QQuickItem *parent) + : QQuickPaintedItem(parent), d_ptr(new QuickImageItemPrivate(this)) +{ +} + +QuickImageItem::~QuickImageItem() = default; + +void QuickImageItem::paint(QPainter *painter) +{ + Q_D(QuickImageItem); + d->paint(painter); +} + +QVariant QuickImageItem::source() const +{ + Q_D(const QuickImageItem); + return d->source(); +} + +void QuickImageItem::setSource(const QVariant &value) +{ + Q_D(QuickImageItem); + d->setSource(value); +} + +FRAMELESSHELPER_END_NAMESPACE diff --git a/src/quick/quickimageitem.h b/src/quick/quickimageitem.h new file mode 100644 index 0000000..01f435c --- /dev/null +++ b/src/quick/quickimageitem.h @@ -0,0 +1 @@ +#include "../../include/FramelessHelper/Quick/quickimageitem.h" diff --git a/src/quick/quickimageitem_p.h b/src/quick/quickimageitem_p.h new file mode 100644 index 0000000..cb6238f --- /dev/null +++ b/src/quick/quickimageitem_p.h @@ -0,0 +1 @@ +#include "../../include/FramelessHelper/Quick/private/quickimageitem_p.h" diff --git a/src/quick/quickmicamaterial.cpp b/src/quick/quickmicamaterial.cpp index a51820e..ae83aa4 100644 --- a/src/quick/quickmicamaterial.cpp +++ b/src/quick/quickmicamaterial.cpp @@ -174,6 +174,8 @@ void QuickMicaMaterialPrivate::initialize() { Q_Q(QuickMicaMaterial); q->setFlag(QuickMicaMaterial::ItemHasContents); + q->setSmooth(true); + q->setAntialiasing(true); q->setClip(true); } diff --git a/src/quick/quickstandardsystembutton.cpp b/src/quick/quickstandardsystembutton.cpp index d032341..e556edc 100644 --- a/src/quick/quickstandardsystembutton.cpp +++ b/src/quick/quickstandardsystembutton.cpp @@ -197,6 +197,10 @@ void QuickStandardSystemButton::initialize() { FramelessManagerPrivate::initializeIconFont(); + setAntialiasing(true); + setSmooth(true); + setClip(true); + setImplicitWidth(kDefaultSystemButtonSize.width()); setImplicitHeight(kDefaultSystemButtonSize.height()); diff --git a/src/quick/quickstandardtitlebar.cpp b/src/quick/quickstandardtitlebar.cpp index 7b2126a..c30558f 100644 --- a/src/quick/quickstandardtitlebar.cpp +++ b/src/quick/quickstandardtitlebar.cpp @@ -24,6 +24,7 @@ #include "quickstandardtitlebar_p.h" #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +#include "quickimageitem.h" #include "quickstandardsystembutton_p.h" #include "framelessquickwindow_p.h" #include @@ -78,7 +79,11 @@ void QuickStandardTitleBar::setTitleLabelAlignment(const Qt::Alignment value) labelAnchors->setBottomMargin(kDefaultTitleBarContentsMargin); } if (m_labelAlignment & Qt::AlignLeft) { - labelAnchors->setLeft(titleBarPriv->left()); + if (m_windowIcon->isVisible()) { + labelAnchors->setLeft(QQuickItemPrivate::get(m_windowIcon.data())->right()); + } else { + labelAnchors->setLeft(titleBarPriv->left()); + } labelAnchors->setLeftMargin(kDefaultTitleBarContentsMargin); } if (m_labelAlignment & Qt::AlignRight) { @@ -152,6 +157,69 @@ QuickChromePalette *QuickStandardTitleBar::chromePalette() const return m_chromePalette.data(); } +QSizeF QuickStandardTitleBar::windowIconSize() const +{ +#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) + return m_windowIcon->size(); +#else + return {m_windowIcon->width(), m_windowIcon->height()}; +#endif +} + +void QuickStandardTitleBar::setWindowIconSize(const QSizeF &value) +{ + Q_ASSERT(!value.isEmpty()); + if (value.isEmpty()) { + return; + } + if (windowIconSize() == value) { + return; + } +#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) + m_windowIcon->setSize(value); +#else + m_windowIcon->setWidth(value.width()); + m_windowIcon->setHeight(value.height()); +#endif + Q_EMIT windowIconSizeChanged(); +} + +bool QuickStandardTitleBar::windowIconVisible() const +{ + return m_windowIcon->isVisible(); +} + +void QuickStandardTitleBar::setWindowIconVisible(const bool value) +{ + if (m_windowIcon->isVisible() == value) { + return; + } + m_windowIcon->setVisible(value); + QQuickAnchors *labelAnchors = QQuickItemPrivate::get(m_windowTitleLabel.data())->anchors(); + if (value) { + labelAnchors->setLeft(QQuickItemPrivate::get(m_windowIcon.data())->right()); + } else { + labelAnchors->setLeft(QQuickItemPrivate::get(this)->left()); + } +} + +QVariant QuickStandardTitleBar::windowIcon() const +{ + return m_windowIcon->source(); +} + +void QuickStandardTitleBar::setWindowIcon(const QVariant &value) +{ + Q_ASSERT(value.isValid()); + if (!value.isValid()) { + return; + } + if (m_windowIcon->source() == value) { + return; + } + m_windowIcon->setSource(value); +} + void QuickStandardTitleBar::updateMaximizeButton() { const QQuickWindow * const w = window(); @@ -270,8 +338,25 @@ void QuickStandardTitleBar::retranslateUi() qobject_cast(qmlAttachedPropertiesObject(m_closeButton.data()))->setText(tr("Close")); } +void QuickStandardTitleBar::updateWindowIcon() +{ + // The user has set an icon explicitly, don't override it. + if (m_windowIcon->source().isValid()) { + return; + } + const QIcon icon = (window() ? window()->icon() : QIcon()); + if (icon.isNull()) { + return; + } + m_windowIcon->setSource(icon); +} + void QuickStandardTitleBar::initialize() { + setSmooth(true); + setClip(true); + setAntialiasing(true); + m_chromePalette.reset(new QuickChromePalette(this)); connect(m_chromePalette.data(), &ChromePalette::titleBarColorChanged, this, &QuickStandardTitleBar::updateTitleBarColor); @@ -283,6 +368,16 @@ void QuickStandardTitleBar::initialize() b->setColor(kDefaultTransparentColor); setHeight(kDefaultTitleBarHeight); + const QQuickItemPrivate * const thisPriv = QQuickItemPrivate::get(this); + + m_windowIcon.reset(new QuickImageItem(this)); + QQuickAnchors * const iconAnchors = QQuickItemPrivate::get(m_windowIcon.data())->anchors(); + iconAnchors->setLeft(thisPriv->left()); + iconAnchors->setLeftMargin(kDefaultTitleBarContentsMargin); + iconAnchors->setVerticalCenter(thisPriv->verticalCenter()); + connect(m_windowIcon.data(), &QuickImageItem::visibleChanged, this, &QuickStandardTitleBar::windowIconVisibleChanged); + connect(m_windowIcon.data(), &QuickImageItem::sourceChanged, this, &QuickStandardTitleBar::windowIconChanged); + m_windowTitleLabel.reset(new QQuickLabel(this)); QFont f = m_windowTitleLabel->font(); f.setPointSize(kDefaultTitleBarFontPointSize); @@ -290,7 +385,6 @@ void QuickStandardTitleBar::initialize() m_systemButtonsRow.reset(new QQuickRow(this)); QQuickAnchors * const rowAnchors = QQuickItemPrivate::get(m_systemButtonsRow.data())->anchors(); - const QQuickItemPrivate * const thisPriv = QQuickItemPrivate::get(this); rowAnchors->setTop(thisPriv->top()); rowAnchors->setRight(thisPriv->right()); m_minimizeButton.reset(new QuickStandardSystemButton(QuickGlobal::SystemButtonType::Minimize, m_systemButtonsRow.data())); @@ -300,6 +394,8 @@ void QuickStandardTitleBar::initialize() m_closeButton.reset(new QuickStandardSystemButton(QuickGlobal::SystemButtonType::Close, m_systemButtonsRow.data())); connect(m_closeButton.data(), &QuickStandardSystemButton::clicked, this, &QuickStandardTitleBar::clickCloseButton); + setWindowIconSize(kDefaultWindowIconSize); + setWindowIconVisible(false); setTitleLabelAlignment(Qt::AlignLeft | Qt::AlignVCenter); retranslateUi(); updateAll(); @@ -342,6 +438,7 @@ bool QuickStandardTitleBar::eventFilter(QObject *object, QEvent *event) void QuickStandardTitleBar::updateAll() { + updateWindowIcon(); updateMaximizeButton(); updateTitleLabelText(); updateTitleBarColor(); diff --git a/src/widgets/standardtitlebar.cpp b/src/widgets/standardtitlebar.cpp index a149611..c5672bb 100644 --- a/src/widgets/standardtitlebar.cpp +++ b/src/widgets/standardtitlebar.cpp @@ -26,9 +26,8 @@ #include "standardtitlebar_p.h" #include "standardsystembutton.h" #include +#include #include -#include -#include FRAMELESSHELPER_BEGIN_NAMESPACE @@ -126,16 +125,6 @@ void StandardTitleBarPrivate::paintTitleBar(QPaintEvent *event) { Q_UNUSED(event); Q_Q(StandardTitleBar); -#if 0 - // This block of code ensures that our widget can still apply the stylesheet correctly. - // Enabling the "Qt::WA_StyledBackground" attribute can also achieve the same - // effect, but since it's documented as only for internal uses, we use the - // public way to do that instead. - QStyleOption option; - option.initFrom(q); - QStylePainter painter(q); - painter.drawPrimitive(QStyle::PE_Widget, option); -#else if (!m_window || m_chromePalette.isNull()) { return; } @@ -151,6 +140,17 @@ void StandardTitleBarPrivate::paintTitleBar(QPaintEvent *event) painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform); painter.fillRect(QRect(QPoint(0, 0), q->size()), backgroundColor); + int titleLabelLeftOffset = 0; + if (m_windowIconVisible) { + const QIcon icon = m_window->windowIcon(); + if (!icon.isNull()) { + const QSize size = (m_windowIconSize.isEmpty() ? kDefaultWindowIconSize : m_windowIconSize); + const int y = qRound(qreal(q->height() - size.height()) / qreal(2)); + const QRect rect = {QPoint(kDefaultTitleBarContentsMargin, y), size}; + titleLabelLeftOffset = (rect.left() + rect.width()); + icon.paint(&painter, rect); + } + } if (m_titleLabelVisible) { const QString text = m_window->windowTitle(); if (!text.isEmpty()) { @@ -161,12 +161,12 @@ void StandardTitleBarPrivate::paintTitleBar(QPaintEvent *event) }(); painter.setPen(foregroundColor); painter.setFont(font); - const QRect rect = [this, q]() -> QRect { + const QRect rect = [this, q, titleLabelLeftOffset]() -> QRect { const int w = q->width(); int leftMargin = 0; int rightMargin = 0; if (m_labelAlignment & Qt::AlignLeft) { - leftMargin = kDefaultTitleBarContentsMargin; + leftMargin = (kDefaultTitleBarContentsMargin + titleLabelLeftOffset); } if (m_labelAlignment & Qt::AlignRight) { rightMargin = (w - m_minimizeButton->x() + kDefaultTitleBarContentsMargin); @@ -181,7 +181,6 @@ void StandardTitleBarPrivate::paintTitleBar(QPaintEvent *event) } } painter.restore(); -#endif } bool StandardTitleBarPrivate::titleLabelVisible() const @@ -200,6 +199,42 @@ void StandardTitleBarPrivate::setTitleLabelVisible(const bool value) Q_EMIT q->titleLabelVisibleChanged(); } +QSize StandardTitleBarPrivate::windowIconSize() const +{ + return m_windowIconSize; +} + +void StandardTitleBarPrivate::setWindowIconSize(const QSize &value) +{ + Q_ASSERT(!value.isEmpty()); + if (value.isEmpty()) { + return; + } + if (m_windowIconSize == value) { + return; + } + m_windowIconSize = value; + Q_Q(StandardTitleBar); + q->update(); + Q_EMIT q->windowIconSizeChanged(); +} + +bool StandardTitleBarPrivate::windowIconVisible() const +{ + return m_windowIconVisible; +} + +void StandardTitleBarPrivate::setWindowIconVisible(const bool value) +{ + if (m_windowIconVisible == value) { + return; + } + m_windowIconVisible = value; + Q_Q(StandardTitleBar); + q->update(); + Q_EMIT q->windowIconVisibleChanged(); +} + void StandardTitleBarPrivate::updateMaximizeButton() { const bool max = m_window->isMaximized(); @@ -286,6 +321,10 @@ void StandardTitleBarPrivate::initialize() this, &StandardTitleBarPrivate::updateChromeButtonColor); q->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); q->setFixedHeight(kDefaultTitleBarHeight); + connect(m_window, &QWidget::windowIconChanged, this, [q](const QIcon &icon){ + Q_UNUSED(icon); + q->update(); + }); connect(m_window, &QWidget::windowTitleChanged, this, [q](const QString &title){ Q_UNUSED(title); q->update(); @@ -415,6 +454,30 @@ void StandardTitleBar::setTitleLabelVisible(const bool value) d->setTitleLabelVisible(value); } +QSize StandardTitleBar::windowIconSize() const +{ + Q_D(const StandardTitleBar); + return d->windowIconSize(); +} + +void StandardTitleBar::setWindowIconSize(const QSize &value) +{ + Q_D(StandardTitleBar); + d->setWindowIconSize(value); +} + +bool StandardTitleBar::windowIconVisible() const +{ + Q_D(const StandardTitleBar); + return d->windowIconVisible(); +} + +void StandardTitleBar::setWindowIconVisible(const bool value) +{ + Q_D(StandardTitleBar); + d->setWindowIconVisible(value); +} + void StandardTitleBar::paintEvent(QPaintEvent *event) { Q_D(StandardTitleBar);