diff --git a/.gitignore b/.gitignore index 4d53e5ca..96f1605e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,8 @@ *.dll *.lib *.a -*.user -*.idea/ +*.user build/ -*build/ -build-mini-blur*/ opencv2/ .vscode/ OpenCV_static/ @@ -18,5 +15,4 @@ out/ *.py *.pyc *.exe -tests/ -benchmarks/ \ No newline at end of file +cmake-build-debug-mingw \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 8db03db0..93589b88 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,48 +1,172 @@ -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.10) project(QWidget_FancyUI VERSION 3.0.0 LANGUAGES CXX) -# 启用 C++17 和 Qt 的自动代码生成 set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_AUTOUIC ON) -# 在此设置你的 Qt 路径,并按实际按需开启静态编译选项 -# 已支持 Qt5 可设置 Qt5 路径 推荐 5.15 及以上版本 +unset(ENV{PATH}) + +# 在此设置你的Qt路径 Set your Qt path here if (MSVC) - set(CMAKE_PREFIX_PATH "D:/ProgramFiles/Qt/6.11.1/msvc2022_64") - # set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") # 仅当 Qt 为静态库且为 /MT 时开启,/MD 保持注释 -elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - set(CMAKE_PREFIX_PATH "D:/ProgramFiles/Qt/6.11.1/mingw_64") - # add_link_options(-static -static-libgcc -static-libstdc++) # MinGW 全静态链接时按需开启;静态 Qt 库本身不要求这些选项 + set(CMAKE_PREFIX_PATH "E:/Qt/6.7.2/msvc2019_64") # MSVC +elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + set(CMAKE_PREFIX_PATH "E:/Qt/6.7.2/mingw_64") # MinGW endif () -# 根据指定的 Qt 路径查找 Qt 5 或 Qt 6 及所需模块 -find_package(Threads REQUIRED) -find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core Gui Widgets Xml Svg) -find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Gui Widgets Xml Svg) -if (QT_VERSION_MAJOR EQUAL 6) - find_package(Qt6 REQUIRED COMPONENTS SvgWidgets) -endif () +find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets Xml Svg SvgWidgets REQUIRED) + +# 定义库的导出宏 +set(LIBRARY_NAME "QFancyUI") +if (WIN32) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) +endif() + +# 定义安装路径 +set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install" CACHE PATH "Installation prefix") + +# ============================================ +# 1. 创建动态库 +# ============================================ + +# 收集库的源文件(不包括示例) +file(GLOB_RECURSE FANCYUI_SOURCES + "${PROJECT_SOURCE_DIR}/src/Fancy/*.h" + "${PROJECT_SOURCE_DIR}/src/Fancy/*.cpp" + "${PROJECT_SOURCE_DIR}/src/resource/*.qrc" +) + +# 创建动态库 +qt_add_library(${LIBRARY_NAME} SHARED + ${FANCYUI_SOURCES} +) + +# 设置库版本 +set_target_properties(${LIBRARY_NAME} PROPERTIES + VERSION ${PROJECT_VERSION} + SOVERSION 3 + OUTPUT_NAME "QFancyUI" + DEBUG_POSTFIX "d" +) + +# 库的头文件包含路径 +target_include_directories(${LIBRARY_NAME} PUBLIC + $ + $ + $ +) + +# 链接 Qt 库 +target_link_libraries(${LIBRARY_NAME} PUBLIC + Qt6::Core + Qt6::Gui + Qt6::Widgets + Qt6::Xml + Qt6::Svg + Qt6::SvgWidgets +) -# 收集示例程序的源码、界面和资源 -file(GLOB_RECURSE EXAMPLE_SOURCES CONFIGURE_DEPENDS +# Windows 特定链接 +if (WIN32) + target_link_libraries(${LIBRARY_NAME} PRIVATE + user32.lib + dwmapi.lib + advapi32.lib + ) +endif() + +# ============================================ +# 2. 创建示例程序(可选) +# ============================================ +option(BUILD_EXAMPLES "Build example programs" ON) + +if (BUILD_EXAMPLES) + # 收集示例源文件 + file(GLOB_RECURSE EXAMPLE_SOURCES "${PROJECT_SOURCE_DIR}/Example/*.h" "${PROJECT_SOURCE_DIR}/Example/*.cpp" "${PROJECT_SOURCE_DIR}/Example/*.ui" + "${PROJECT_SOURCE_DIR}/Example/Example_Additional_Code/*.h" + "${PROJECT_SOURCE_DIR}/Example/Example_Additional_Code/*.cpp" "${PROJECT_SOURCE_DIR}/Example/Example_Resource/*.qrc" + "${PROJECT_SOURCE_DIR}/Example/Main_Program_Page_Code/*.h" + "${PROJECT_SOURCE_DIR}/Example/Main_Program_Page_Code/*.cpp" + "${PROJECT_SOURCE_DIR}/Example/Main_Program_Page_Code/*.ui" + ) + + # 创建示例程序 + add_executable(${PROJECT_NAME}_Example + ${EXAMPLE_SOURCES} + ) + + # 链接到我们创建的库 + target_link_libraries(${PROJECT_NAME}_Example PRIVATE + ${LIBRARY_NAME} + ) + + # 示例程序的头文件路径 + target_include_directories(${PROJECT_NAME}_Example PRIVATE + ${PROJECT_SOURCE_DIR}/Example + ${PROJECT_SOURCE_DIR}/Example/Example_Additional_Code + ${PROJECT_SOURCE_DIR}/Example/Main_Program_Page_Code + ) + + # 设置可执行文件输出目录 + set_target_properties(${PROJECT_NAME}_Example PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin + ) +endif() + +# ============================================ +# 3. 安装配置 +# ============================================ + +# 安装库文件 +install(TARGETS ${LIBRARY_NAME} + EXPORT ${LIBRARY_NAME}Targets + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib + RUNTIME DESTINATION bin ) -# 创建示例程序 -add_executable(${PROJECT_NAME} ${EXAMPLE_SOURCES}) +# 安装头文件 +install(DIRECTORY src/Fancy/ + DESTINATION include/${LIBRARY_NAME} + FILES_MATCHING PATTERN "*.h" + PATTERN "*.cpp" EXCLUDE +) + +install(DIRECTORY src/include/ + DESTINATION include/${LIBRARY_NAME} + FILES_MATCHING PATTERN "*.h" +) + +# 安装 CMake 配置文件 +install(EXPORT ${LIBRARY_NAME}Targets + FILE ${LIBRARY_NAME}Targets.cmake + NAMESPACE ${LIBRARY_NAME}:: + DESTINATION lib/cmake/${LIBRARY_NAME} +) + +# 创建配置文件 +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/${LIBRARY_NAME}Config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/${LIBRARY_NAME}Config.cmake + @ONLY +) -# 设置示例程序的头文件路径 -target_include_directories(${PROJECT_NAME} PRIVATE - "${PROJECT_SOURCE_DIR}/Example" - "${PROJECT_SOURCE_DIR}/Example/Example_Additional_Code" - "${PROJECT_SOURCE_DIR}/Example/Main_Program_Page_Code" +install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/${LIBRARY_NAME}Config.cmake + DESTINATION lib/cmake/${LIBRARY_NAME} ) -# 将 FancyUI 源码及依赖加入示例程序 -add_subdirectory(src) +# ============================================ +# 4. 调试信息输出(可选) +# ============================================ +# if (CMAKE_BUILD_TYPE STREQUAL "Debug") +# message(STATUS "Building ${LIBRARY_NAME} as shared library") +# message(STATUS "Qt version: ${Qt6_VERSION}") +# message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") +# message(STATUS "Install prefix: ${CMAKE_INSTALL_PREFIX}") +# endif() diff --git a/Example/Example_Additional_Code/DerivativeColorSelector.cpp b/Example/Example_Additional_Code/DerivativeColorSelector.cpp index 401e2895..18740ecc 100644 --- a/Example/Example_Additional_Code/DerivativeColorSelector.cpp +++ b/Example/Example_Additional_Code/DerivativeColorSelector.cpp @@ -7,12 +7,11 @@ #include #include #include -#include #include -#include "Core/Defs.h" -#include "Core/Palette/Palette.h" -#include "Widgets/TextLabel/TextLabel.h" +#include "Defs.h" +#include "Palette.h" +#include "TextLabel.h" DerivativeColorButton::DerivativeColorButton(QWidget *parent) : QPushButton(parent), @@ -62,11 +61,7 @@ void DerivativeColorButton::paintEvent(QPaintEvent *event) painter.drawText(r, Qt::AlignCenter, text()); } -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - void DerivativeColorButton::enterEvent(QEnterEvent *event) -#else - void DerivativeColorButton::enterEvent(QEvent *event) -#endif +void DerivativeColorButton::enterEvent(QEnterEvent *event) { _ani->setDirection(QAbstractAnimation::Direction::Forward); _ani->start(); @@ -104,9 +99,7 @@ DerivativeColorSelector::DerivativeColorSelector(QWidget *parent, const QColor & auto *layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); - QFont titleFont = _title->font(); - titleFont.setPointSize(12); - _title->setFont(titleFont); + _title->setFont(QFont(_title->font().family(), 12)); layout->addWidget(_title, 0, Qt::AlignHCenter); layout->addWidget(wrap_widget, 0, Qt::AlignHCenter); } diff --git a/Example/Example_Additional_Code/DerivativeColorSelector.h b/Example/Example_Additional_Code/DerivativeColorSelector.h index c3498b6b..6700e313 100644 --- a/Example/Example_Additional_Code/DerivativeColorSelector.h +++ b/Example/Example_Additional_Code/DerivativeColorSelector.h @@ -27,11 +27,7 @@ class DerivativeColorButton : public QPushButton protected: void paintEvent(QPaintEvent *) override; -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - void enterEvent(QEnterEvent *event) override; -#else - void enterEvent(QEvent *event) override; -#endif + void enterEvent(QEnterEvent *event) override; void leaveEvent(QEvent *event) override; diff --git a/Example/Example_Additional_Code/ExampleCodeCard.cpp b/Example/Example_Additional_Code/ExampleCodeCard.cpp index 8ea628a1..64bf3ecb 100644 --- a/Example/Example_Additional_Code/ExampleCodeCard.cpp +++ b/Example/Example_Additional_Code/ExampleCodeCard.cpp @@ -5,7 +5,6 @@ #include "ExampleCodeCard.h" #include -#include #include #include #include @@ -13,10 +12,10 @@ #include #include -#include "Core/Palette/Palette.h" -#include "Core/Theme/ThemeModeController.h" -#include "Icon/IconEngine.h" -#include "Widgets/PushButton/TransparentButton.h" +#include "IconEngine.h" +#include "palette.h" +#include "ThemeModeController.h" +#include "TransparentButton.h" RotaryArrow::RotaryArrow(QWidget *parent) : @@ -117,11 +116,7 @@ ExampleCodeCardTextEditStyle::ExampleCodeCardTextEditStyle(QTextEdit *parent) : setParent(parent); } -void ExampleCodeCardTextEditStyle::drawPrimitive( - PrimitiveElement element, - const QStyleOption *option, - QPainter *painter, - const QWidget *widget) const +void ExampleCodeCardTextEditStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const { using namespace fancy; if (element == PE_Frame) @@ -163,18 +158,19 @@ ExampleCodeCardTextEdit::ExampleCodeCardTextEdit(QWidget *parent) : _copy->setFixedSize(32, 32); _copy->setIcon(IconPark::Copy); + _copy->setDrawBorder(true); + _copy->setBorderWidth(0.5); connect(_copy, &TransparentButton::clicked, this, &ExampleCodeCardTextEdit::copyCode); } void ExampleCodeCardTextEdit::resizeEvent(QResizeEvent *event) { QTextEdit::resizeEvent(event); - constexpr int margin = 10; - const int top = qBound(2, (height() - _copy->height()) / 2, margin); - _copy->move(width() - margin - _copy->width(), top); + // _copy->move(width() - 10 - 32, height() / 2 - 32 / 2); + _copy->move(width() - 42, height() / 2 - 16); } -void ExampleCodeCardTextEdit::copyCode() const +void ExampleCodeCardTextEdit::copyCode() { QApplication::clipboard()->setText(toPlainText()); } @@ -184,7 +180,7 @@ ExampleCodeCard::ExampleCodeCard(QWidget *parent) : _code(new ExampleCodeCardTextEdit(this)), _title(new ExampleCodeCardButton(this)), _ani(new QVariantAnimation(this)), - _codeAni(new QPropertyAnimation(_code, "pos", this)), + _codeAni(new QPropertyAnimation(_code, "geometry", this)), _aniGroup(new QParallelAnimationGroup(this)) { _title->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); @@ -208,7 +204,7 @@ ExampleCodeCard::ExampleCodeCard(QWidget *parent) : connect(_aniGroup, &QParallelAnimationGroup::finished, this, &ExampleCodeCard::onAniFinished); } -void ExampleCodeCard::setHtmlCode(const QString &html) const +void ExampleCodeCard::setHtmlCode(const QString &html) { _code->setHtml(html); } @@ -217,7 +213,8 @@ void ExampleCodeCard::resizeEvent(QResizeEvent *event) { QWidget::resizeEvent(event); _title->resize(width(), ExampleCodeCard_ButtonHeight); - _code->resize(width(), _code->height()); + if (_title->_show) + _code->resize(width(), _code->height()); } void ExampleCodeCard::onAnimationChanged(const QVariant &val) @@ -226,10 +223,10 @@ void ExampleCodeCard::onAnimationChanged(const QVariant &val) setFixedHeight(h); } -void ExampleCodeCard::onAniFinished() const +void ExampleCodeCard::onAniFinished() { - _title->_show = _expanded; - if (!_expanded) + _title->_show = !_title->_show; + if (!_title->_show) { _code->document()->setTextWidth(-1); // 重置文本宽度 _code->hide(); @@ -240,40 +237,44 @@ void ExampleCodeCard::onAniFinished() const void ExampleCodeCard::clickedToShowCode() { - _expanded = !_expanded; - const auto direction = _expanded ? QAbstractAnimation::Forward : QAbstractAnimation::Backward; - if (_aniGroup->state() == QAbstractAnimation::Running) + if (!_title->_show) + _code->document()->setTextWidth(-1); + updateHeight(); + + const QSize codeSize{width(), _code->height()}; + if (_title->_show) { - // Reverse the shared timeline without resetting height, position or arrow angle. - _aniGroup->setDirection(direction); - return; - } + _ani->setStartValue(ExampleCodeCard_ButtonHeight + _code->height()); + _ani->setEndValue(ExampleCodeCard_ButtonHeight); + constexpr QPoint startPos{0, ExampleCodeCard_ButtonHeight}; - _code->document()->setTextWidth(-1); - updateHeight(); - _code->resize(width(), _code->height()); - _ani->setStartValue(ExampleCodeCard_ButtonHeight); - _ani->setEndValue(ExampleCodeCard_ButtonHeight + _code->height()); - _codeAni->setStartValue(QPoint(0, -_code->height() - ExampleCodeCard_ButtonHeight)); - _codeAni->setEndValue(QPoint(0, ExampleCodeCard_ButtonHeight)); - - // Keep the header's bottom corners square until fully collapsed. - _title->_show = true; - _title->_transparent = false; - _aniGroup->setDirection(direction); - _aniGroup->start(); - _code->show(); - _title->update(); + const QPoint endPos{0, 0 - _code->height() - ExampleCodeCard_ButtonHeight}; + _codeAni->setStartValue(QRect{startPos, codeSize}); + _codeAni->setEndValue(QRect{endPos, codeSize}); + } + else + { + _ani->setStartValue(ExampleCodeCard_ButtonHeight); + _ani->setEndValue(ExampleCodeCard_ButtonHeight + _code->height()); + const QPoint startPos{0, 0 - _code->height() - ExampleCodeCard_ButtonHeight}; + constexpr QPoint endPos{0, ExampleCodeCard_ButtonHeight}; + _codeAni->setStartValue(QRect{startPos, codeSize}); + _codeAni->setEndValue(QRect{endPos, codeSize}); + } + if (_aniGroup->state() != QAbstractAnimation::State::Running) + { + _aniGroup->start(); + _code->show(); + _title->_transparent = false; + } } -void ExampleCodeCard::updateHeight() const +void ExampleCodeCard::updateHeight() { _code->document()->adjustSize(); const int h = qRound(_code->document()->size().height()) + _code->contentsMargins().top() + _code->contentsMargins().bottom() + qRound(_code->document()->documentMargin() * 2); - // Leave room for the 32px copy button and 4px of padding on each side, - // including for a single line rendered with a small font. - _code->setFixedHeight(qMax(h, 40)); + _code->setFixedHeight(h); } diff --git a/Example/Example_Additional_Code/ExampleCodeCard.h b/Example/Example_Additional_Code/ExampleCodeCard.h index b7554518..f2115e72 100644 --- a/Example/Example_Additional_Code/ExampleCodeCard.h +++ b/Example/Example_Additional_Code/ExampleCodeCard.h @@ -7,10 +7,11 @@ #include #include +#include #include #include -#include "Widgets/PushButton/TransparentButton.h" +#include "TransparentButton.h" class QPropertyAnimation; class QSvgRenderer; @@ -46,7 +47,7 @@ private slots: private: QVariantAnimation *_ani; - QVector _lines; + QList _lines; int _angle; }; @@ -90,7 +91,7 @@ class ExampleCodeCardTextEdit : public QTextEdit void resizeEvent(QResizeEvent *event) override; private slots: - void copyCode() const; + void copyCode(); private: fancy::TransparentButton *_copy; @@ -104,7 +105,7 @@ class ExampleCodeCard : public QWidget public: explicit ExampleCodeCard(QWidget *parent); - void setHtmlCode(const QString &html) const; + void setHtmlCode(const QString &html); protected: void resizeEvent(QResizeEvent *event) override; @@ -112,19 +113,18 @@ class ExampleCodeCard : public QWidget private slots: void onAnimationChanged(const QVariant &val); - void onAniFinished() const; + void onAniFinished(); void clickedToShowCode(); private: - void updateHeight() const; + void updateHeight(); ExampleCodeCardTextEdit *_code; ExampleCodeCardButton *_title; QVariantAnimation *_ani; QPropertyAnimation *_codeAni; QParallelAnimationGroup *_aniGroup; - bool _expanded = false; // Target state, including while the animation is running. }; diff --git a/Example/Example_Additional_Code/ExampleControlCard.cpp b/Example/Example_Additional_Code/ExampleControlCard.cpp index 64909039..8c13c5ac 100644 --- a/Example/Example_Additional_Code/ExampleControlCard.cpp +++ b/Example/Example_Additional_Code/ExampleControlCard.cpp @@ -4,16 +4,18 @@ #include "ExampleControlCard.h" +#include #include +#include #include #include +#include "Defs.h" #include "ExampleCodeCard.h" -#include "Animation/ParallelAnimationGroupPool.h" -#include "Core/Defs.h" -#include "Core/Palette/Palette.h" -#include "Core/Theme/ThemeModeController.h" -#include "Widgets/TextLabel/TextLabel.h" +#include "palette.h" +#include "ParallelAnimationGroupPool.h" +#include "TextLabel.h" +#include "ThemeModeController.h" ExampleControlWidget::ExampleControlWidget(QWidget *parent) : QWidget(parent) @@ -53,25 +55,33 @@ ExampleControlCard::ExampleControlCard(QWidget *parent) : { _layout->setContentsMargins(0, 0, 0, 0); _layout->setSpacing(0); - // Grow the card before laying out an expanding code area. The default - // constraint allows a child widget's layout to temporarily compress its - // contents while the ancestor layouts are still updating their sizes. - _layout->setSizeConstraint(QLayout::SetMinimumSize); _layout->addWidget(_control); _layout->addWidget(_code); _layout->addStretch(1); _arrow->setFixedSize(30, 30); - QFont titleFont = _title->font(); - titleFont.setPointSize(10); - _title->setFont(titleFont); + _title->setFont(QFont(font().family(), 10)); _title->setAttribute(Qt::WA_TransparentForMouseEvents, true); - _arrow->_ani->setStartValue(0); - _arrow->_ani->setEndValue(180); _code->_aniGroup->addAnimation(_arrow->_ani); connect(_arrow, &QPushButton::clicked, _code->_title, &QPushButton::click); + connect(_code->_title, + &QPushButton::clicked, + this, + [this] { + if (_code->_title->_show) + { + _arrow->_ani->setStartValue(180); + _arrow->_ani->setEndValue(360); + } + else + { + _arrow->_ani->setStartValue(0); + _arrow->_ani->setEndValue(180); + } + } + ); } void ExampleControlCard::setTitleText(const QString &text) diff --git a/Example/Example_Additional_Code/ExampleControlCard.h b/Example/Example_Additional_Code/ExampleControlCard.h index fb90e03a..6fbc49fa 100644 --- a/Example/Example_Additional_Code/ExampleControlCard.h +++ b/Example/Example_Additional_Code/ExampleControlCard.h @@ -5,6 +5,8 @@ #ifndef QWIDGET_FANCYUI_EXAMPLECONTROLCARD_H #define QWIDGET_FANCYUI_EXAMPLECONTROLCARD_H +#include + #include "ExampleNavigationCard.h" class RotaryArrow; diff --git a/Example/Example_Additional_Code/ExampleDefs.h b/Example/Example_Additional_Code/ExampleDefs.h index 2301f88d..c1b9115a 100644 --- a/Example/Example_Additional_Code/ExampleDefs.h +++ b/Example/Example_Additional_Code/ExampleDefs.h @@ -10,7 +10,7 @@ #include #include -#include "Core/Defs.h" +#include "Defs.h" inline QString loadExampleCode(const QString &widgetName, fancy::Theme theme) diff --git a/Example/Example_Additional_Code/ExampleNavigationCard.cpp b/Example/Example_Additional_Code/ExampleNavigationCard.cpp index edcda792..d989e671 100644 --- a/Example/Example_Additional_Code/ExampleNavigationCard.cpp +++ b/Example/Example_Additional_Code/ExampleNavigationCard.cpp @@ -4,12 +4,12 @@ #include "ExampleNavigationCard.h" -#include "Core/Defs.h" -#include "Core/Palette/Palette.h" -#include "Core/Theme/ThemeModeController.h" -#include "Widgets/CenteredImage/CenteredImageWidget.h" -#include "Widgets/SvgWidget/SvgWidget.h" -#include "Widgets/TextLabel/TextLabel.h" +#include "CenteredImageWidget.h" +#include "Defs.h" +#include "Palette.h" +#include "SvgWidget.h" +#include "TextLabel.h" +#include "ThemeModeController.h" namespace fancy { diff --git a/Example/Example_Additional_Code/ExamplePageHeader.cpp b/Example/Example_Additional_Code/ExamplePageHeader.cpp index 28221031..76b0b00e 100644 --- a/Example/Example_Additional_Code/ExamplePageHeader.cpp +++ b/Example/Example_Additional_Code/ExamplePageHeader.cpp @@ -6,12 +6,11 @@ #include #include -#include -#include "Core/Palette/Palette.h" -#include "Core/Theme/ThemeModeController.h" -#include "Widgets/PushButton/PushButton.h" -#include "Widgets/TextLabel/TextLabel.h" +#include "Palette.h" +#include "PushButton.h" +#include "TextLabel.h" +#include "ThemeModeController.h" ExamplePageHeader::ExamplePageHeader(QWidget *parent) : QWidget(parent), diff --git a/Example/Example_Additional_Code/IntroductionCard.cpp b/Example/Example_Additional_Code/IntroductionCard.cpp index 128cfdf0..57e93bb0 100644 --- a/Example/Example_Additional_Code/IntroductionCard.cpp +++ b/Example/Example_Additional_Code/IntroductionCard.cpp @@ -6,14 +6,13 @@ #include #include -#include #include -#include "Core/ControlState/ControlState.h" -#include "Core/Palette/Palette.h" -#include "Core/Theme/ThemeModeController.h" -#include "Widgets/SvgWidget/SvgWidget.h" -#include "Widgets/TextLabel/TextLabel.h" +#include "ControlState.h" +#include "Palette.h" +#include "SvgWidget.h" +#include "TextLabel.h" +#include "ThemeModeController.h" namespace fancy @@ -84,11 +83,7 @@ namespace fancy update(); } -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) void IntroductionCard::enterEvent(QEnterEvent *event) -#else - void IntroductionCard::enterEvent(QEvent *event) -#endif { BlurCard::enterEvent(event); _state = VisualState::Hover; diff --git a/Example/Example_Additional_Code/IntroductionCard.h b/Example/Example_Additional_Code/IntroductionCard.h index 68971b5d..adfe9ada 100644 --- a/Example/Example_Additional_Code/IntroductionCard.h +++ b/Example/Example_Additional_Code/IntroductionCard.h @@ -4,9 +4,9 @@ #ifndef QWIDGET_FANCYUI_INTRODUCTIONCARD_H #define QWIDGET_FANCYUI_INTRODUCTIONCARD_H -#include +#include -#include "Widgets/BlurCard/BlurCard.h" +#include "BlurCard.h" namespace fancy @@ -36,11 +36,8 @@ namespace fancy void leaveEvent(QEvent *event) override; -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) void enterEvent(QEnterEvent *event) override; -#else - void enterEvent(QEvent *event) override; -#endif + void paintEvent(QPaintEvent *event) override; QUrl _url; diff --git a/Example/Example_Resource/ExampleSourceCode.json b/Example/Example_Resource/ExampleSourceCode.json index b111bf56..2f726ce0 100644 --- a/Example/Example_Resource/ExampleSourceCode.json +++ b/Example/Example_Resource/ExampleSourceCode.json @@ -43,10 +43,6 @@ "Light": "

fancy::CheckBox *checkBox = new fancy::CheckBox("小尺寸双态复选框", parent, true);
", "Dark": "

fancy::CheckBox *checkBox = new fancy::CheckBox("小尺寸双态复选框", parent, true);
" }, - "TristateCheckbox": { - "Light": "

fancy::CheckBox *selectAll = new fancy::CheckBox("全选", parent);
selectAll->setTristate(true);
selectAll->setAnimationEnabled(false);

fancy::CheckBox *item_1 = new fancy::CheckBox("选项 1", parent, true);
// 其余子项的创建、布局设置略......

// 全选与子项的状态联动逻辑略......
", - "Dark": "

fancy::CheckBox *selectAll = new fancy::CheckBox("全选", parent);
selectAll->setTristate(true);
selectAll->setAnimationEnabled(false);

fancy::CheckBox *item_1 = new fancy::CheckBox("选项 1", parent, true);
// 其余子项的创建、布局设置略......

// 全选与子项的状态联动逻辑略......
" - }, "RadioButton": { "Light": "

fancy::RadioButton *radioButton_1 = new fancy::RadioButton("选项 1", parent);
fancy::RadioButton *radioButton_2 = new fancy::RadioButton("选项 2", parent);
fancy::RadioButton *radioButton_3 = new fancy::RadioButton("选项 3", parent);
// 添加到布局、按钮组等中.......略
", "Dark": "

fancy::RadioButton *radioButton_1 = new fancy::RadioButton("选项 1", parent);
fancy::RadioButton *radioButton_2 = new fancy::RadioButton("选项 2", parent);
fancy::RadioButton *radioButton_3 = new fancy::RadioButton("选项 3", parent);
// 添加到布局、按钮组等中.......略
" diff --git a/Example/Example_Resource/Logos/ant-design.svg b/Example/Example_Resource/Logos/ant-design.svg deleted file mode 100644 index e9f8c2a9..00000000 --- a/Example/Example_Resource/Logos/ant-design.svg +++ /dev/null @@ -1,43 +0,0 @@ - - - - Group 28 Copy 5 - Created with Sketch. - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Example/Example_Resource/Logos/bootstrap.png b/Example/Example_Resource/Logos/bootstrap.png deleted file mode 100644 index 3643b912..00000000 Binary files a/Example/Example_Resource/Logos/bootstrap.png and /dev/null differ diff --git a/Example/Example_Resource/Logos/iconpark.png b/Example/Example_Resource/Logos/iconpark.png deleted file mode 100644 index af7d61f9..00000000 Binary files a/Example/Example_Resource/Logos/iconpark.png and /dev/null differ diff --git a/Example/Example_Resource/example.qrc b/Example/Example_Resource/example.qrc index 37f0e52c..a782ca2c 100644 --- a/Example/Example_Resource/example.qrc +++ b/Example/Example_Resource/example.qrc @@ -1,8 +1,5 @@ - Logos/iconpark.png - Logos/ant-design.svg - Logos/bootstrap.png Accessibility.png Acrylic.png AnimatedIcon.png diff --git a/Example/Main_Program_Page_Code/BasicInput/BasicInput.cpp b/Example/Main_Program_Page_Code/BasicInput.cpp similarity index 83% rename from Example/Main_Program_Page_Code/BasicInput/BasicInput.cpp rename to Example/Main_Program_Page_Code/BasicInput.cpp index 91ecf705..8a376b60 100644 --- a/Example/Main_Program_Page_Code/BasicInput/BasicInput.cpp +++ b/Example/Main_Program_Page_Code/BasicInput.cpp @@ -5,20 +5,24 @@ #include "BasicInput.h" #include +#include +#include +#include +#include "CheckBox.h" #include "ExampleControlCard.h" #include "ExampleDefs.h" -#include "../ui_general_display_page.h" -#include "Core/Theme/ThemeModeController.h" -#include "Icon/Icon.h" -#include "Widgets/CheckBox/CheckBox.h" -#include "Widgets/PushButton/PushButton.h" -#include "Widgets/PushButton/RippleButton.h" -#include "Widgets/PushButton/ThemeButton.h" -#include "Widgets/PushButton/TransparentButton.h" -#include "Widgets/RadioButton/RadioButton.h" -#include "Widgets/Slider/Slider.h" -#include "Widgets/TextLabel/TextLabel.h" +#include "Icon.h" +#include "PushButton.h" +#include "RadioButton.h" +#include "RippleButton.h" +#include "ScrollBar.h" +#include "Slider.h" +#include "TextLabel.h" +#include "ThemeButton.h" +#include "ThemeModeController.h" +#include "TransparentButton.h" +#include "ui_general_display_page.h" BasicInput::BasicInput(QWidget *parent) : QWidget(parent), @@ -205,55 +209,6 @@ BasicInput::BasicInput(QWidget *parent) : layout->addStretch(1); } - // TristateCheckbox - { - auto *checkBox_Card = makeCardLayout("三态复选框", "TristateCheckbox"); - auto *content = checkBox_Card->controlAreaWidget(); - auto *layout = qobject_cast(content->layout()); - auto *wrap = new QWidget(content); - auto *groupLayout = new QVBoxLayout(wrap); - groupLayout->setContentsMargins(0, 0, 0, 0); - groupLayout->setSpacing(12); - - auto *selectAll = new fancy::CheckBox("全选", wrap); - selectAll->setTristate(true); - selectAll->setAnimationEnabled(false); - groupLayout->addWidget(selectAll); - - auto *itemsLayout = new QVBoxLayout; - itemsLayout->setContentsMargins(24, 0, 0, 0); - itemsLayout->setSpacing(10); - QList items; - for (int i = 1; i <= 4; ++i) - { - auto *item = new fancy::CheckBox(QString("选项 %1").arg(i), wrap, true); - items.append(item); - itemsLayout->addWidget(item); - } - groupLayout->addLayout(itemsLayout); - layout->addWidget(wrap); - layout->addStretch(1); - - auto updateSelectAll = [selectAll, items]() { - int checkedCount = 0; - for (const auto *item : items) - checkedCount += item->isChecked() ? 1 : 0; - selectAll->setCheckState(checkedCount == 0 ? Qt::Unchecked - : checkedCount == items.size() ? Qt::Checked : Qt::PartiallyChecked); - }; - for (auto *item : items) - connect(item, &QCheckBox::toggled, selectAll, updateSelectAll); - - // clicked 仅响应用户操作,子项汇总引起的状态变化不会反向修改子项。 - // 从未选中或半选状态点击时选中全部,从全选状态点击时取消全部。 - connect(selectAll, &QCheckBox::clicked, wrap, [items, updateSelectAll](bool checked) { - for (auto *item : items) - item->setChecked(checked); - updateSelectAll(); - }); - updateSelectAll(); - } - // RadioButton { auto *radioButton_Card = makeCardLayout("单选按钮", "RadioButton"); diff --git a/Example/Main_Program_Page_Code/BasicInput/BasicInput.h b/Example/Main_Program_Page_Code/BasicInput.h similarity index 100% rename from Example/Main_Program_Page_Code/BasicInput/BasicInput.h rename to Example/Main_Program_Page_Code/BasicInput.h diff --git a/Example/Main_Program_Page_Code/Home.cpp b/Example/Main_Program_Page_Code/Home.cpp new file mode 100644 index 00000000..c390e36b --- /dev/null +++ b/Example/Main_Program_Page_Code/Home.cpp @@ -0,0 +1,78 @@ +// +// Created by TX on 2025/11/18. +// + +#include "Home.h" + +#include "Defs.h" +#include "ExampleNavigationCard.h" +#include "FlowLayout.h" +#include "IntroductionCard.h" +#include "Palette.h" +#include "ThemeModeController.h" +#include "ui_home.h" + +HomePage::HomePage(QWidget *parent) : + QWidget(parent), + _uiHome(new Ui::Home) +{ + _uiHome->setupUi(this); + + _uiHome->homeImage->setRadius(5); + _uiHome->homeImage->setFillBackground(true); + _uiHome->homeImage->setFillColor(fancy::ThemeModeController::controller().isAppLight() ? QColor(208, 217, 228) : QColor(2, 11, 32)); + _uiHome->homeImage->setPixmap(QPixmap(":/home.png")); + + auto github = new fancy::IntroductionCard(_uiHome->homeImage, _uiHome->homeImage); + github->setBlurRadius(30); + github->setIcon(fancy::iconId(fancy::BootstrapIcons::Github)); + github->setMainText("FancyUI On Github"); + github->setSubText("Explore the FancyUI source code\nand repository."); + github->setUrl(QUrl(R"(https://github.com/COLORREF/QWidget-FancyUI)")); + github->move(20, 260); + + auto bilibili = new fancy::IntroductionCard(_uiHome->homeImage, _uiHome->homeImage); + bilibili->setBlurRadius(30); + bilibili->setIcon(fancy::iconId(fancy::AntDesignIcons::Bilibili)); + bilibili->setMainText("Developer's homepage"); + bilibili->setSubText("Contact the developer \nor provide your feedback.\nLook forward to your attention."); + bilibili->setUrl(QUrl(R"(https://m.bilibili.com/space/1843315943)")); + bilibili->move(280, 260); + + auto onPixmapUpdate = [github, bilibili] { + github->blur(); + bilibili->blur(); + }; + auto onThemeChanged = [this, onPixmapUpdate] { + _uiHome->homeImage->setFillColor(fancy::ThemeModeController::controller().isAppLight() ? QColor(208, 217, 228) : QColor(2, 11, 32)); + onPixmapUpdate(); + }; + connect(_uiHome->homeImage, &fancy::CenteredImageWidget::SmoothPixmapUpdate, this, onPixmapUpdate); + connect(_uiHome->homeImage, &fancy::CenteredImageWidget::FastTPixmapUpdate, this, onPixmapUpdate); + connect(&fancy::Palette::palette(), &fancy::Palette::appThemeChange, this, onThemeChanged); + + auto button = new fancy::ExampleNavigationCard(_uiHome->homeImage); + button->setIcon(QPixmap(":/Button.png")); + button->setMainText("Button"); + button->setSubText("A control that responds to user input and\nraises a Click event."); + + auto acrylicBrush = new fancy::ExampleNavigationCard(_uiHome->homeImage); + acrylicBrush->setIcon(QPixmap(":/Acrylic.png")); + acrylicBrush->setMainText("AcrylicBrush"); + acrylicBrush->setSubText("A translucent material recommended for\npanel backgrounds."); + + auto hyperlinkButton = new fancy::ExampleNavigationCard(_uiHome->homeImage); + hyperlinkButton->setIcon(QPixmap(":/HyperlinkButton.png")); + hyperlinkButton->setMainText("HyperlinkButton"); + hyperlinkButton->setSubText("A button that appears as hyperlink text,\nand can navigate to a URl or handle a\nClick event."); + + auto flowLayout = new fancy::FlowLayout(_uiHome->exhibitionArea); + flowLayout->addWidget(button); + flowLayout->addWidget(acrylicBrush); + flowLayout->addWidget(hyperlinkButton); +} + +HomePage::~HomePage() +{ + delete _uiHome; +} diff --git a/Example/Main_Program_Page_Code/Home.h b/Example/Main_Program_Page_Code/Home.h new file mode 100644 index 00000000..dd26acb6 --- /dev/null +++ b/Example/Main_Program_Page_Code/Home.h @@ -0,0 +1,32 @@ +// +// Created by TX on 2025/11/18. +// + +#ifndef QWIDGET_FANCYUI_HOME_H +#define QWIDGET_FANCYUI_HOME_H +#include + +QT_BEGIN_NAMESPACE + +namespace Ui +{ + class Home; +} + +QT_END_NAMESPACE + +class HomePage : public QWidget +{ + Q_OBJECT + +public: + explicit HomePage(QWidget *parent); + + ~HomePage() override; + +private: + Ui::Home *_uiHome;; +}; + + +#endif //QWIDGET_FANCYUI_HOME_H diff --git a/Example/Main_Program_Page_Code/Home/Home.cpp b/Example/Main_Program_Page_Code/Home/Home.cpp deleted file mode 100644 index 371b1f96..00000000 --- a/Example/Main_Program_Page_Code/Home/Home.cpp +++ /dev/null @@ -1,114 +0,0 @@ -// -// Created by TX on 2025/11/18. -// - -#include "Home.h" - -#include "ExampleNavigationCard.h" -#include "IntroductionCard.h" -#include "ui_home.h" -#include "Core/Palette/Palette.h" -#include "Core/Theme/ThemeModeController.h" -#include "Layout/FlowLayout.h" -#include "Core/Defs.h" - -HomePage::HomePage(QWidget *parent) : - QWidget(parent), - _uiHome(new Ui::Home), - _githubCard(nullptr), - _bilibiliCard(nullptr), - _fastBlurSignalCount(-1) -{ - initializeUi(); - connect(_uiHome->homeImage, &fancy::CenteredImageWidget::FastTPixmapUpdate, this, &HomePage::onFastPixmapUpdate); - connect(_uiHome->homeImage, &fancy::CenteredImageWidget::SmoothPixmapUpdate, this, &HomePage::updateSmoothBlur); - connect(&fancy::Palette::palette(), &fancy::Palette::appThemeChange, this, &HomePage::onThemeChanged); -} - -void HomePage::initializeUi() -{ - _uiHome->setupUi(this); - - _uiHome->homeImage->setRadius(5); - _uiHome->homeImage->setFillBackground(true); - _uiHome->homeImage->setFillColor( - fancy::ThemeModeController::controller().isAppLight() - ? QColor(208, 217, 228) - : QColor(2, 11, 32) - ); - _uiHome->homeImage->setPixmap(QPixmap(":/home.png")); - - _githubCard = new fancy::IntroductionCard(_uiHome->homeImage, _uiHome->homeImage); - _githubCard->setBlurRadius(30); - _githubCard->setIcon(fancy::iconId(fancy::BootstrapIcons::Github)); - _githubCard->setMainText("FancyUI On Github"); - _githubCard->setSubText("Explore the FancyUI source code\nand repository."); - _githubCard->setUrl(QUrl(R"(https://github.com/COLORREF/QWidget-FancyUI)")); - _githubCard->move(20, 260); - - _bilibiliCard = new fancy::IntroductionCard(_uiHome->homeImage, _uiHome->homeImage); - _bilibiliCard->setBlurRadius(30); - _bilibiliCard->setIcon(fancy::iconId(fancy::AntDesignIcons::Bilibili)); - _bilibiliCard->setMainText("Developer's homepage"); - _bilibiliCard->setSubText("Contact the developer \nor provide your feedback.\nLook forward to your attention."); - _bilibiliCard->setUrl(QUrl(R"(https://m.bilibili.com/space/1843315943)")); - _bilibiliCard->move(280, 260); - - auto button = new fancy::ExampleNavigationCard(_uiHome->homeImage); - button->setIcon(QPixmap(":/Button.png")); - button->setMainText("Button"); - button->setSubText("A control that responds to user input and\nraises a Click event."); - - auto acrylicBrush = new fancy::ExampleNavigationCard(_uiHome->homeImage); - acrylicBrush->setIcon(QPixmap(":/Acrylic.png")); - acrylicBrush->setMainText("AcrylicBrush"); - acrylicBrush->setSubText("A translucent material recommended for\npanel backgrounds."); - - auto hyperlinkButton = new fancy::ExampleNavigationCard(_uiHome->homeImage); - hyperlinkButton->setIcon(QPixmap(":/HyperlinkButton.png")); - hyperlinkButton->setMainText("HyperlinkButton"); - hyperlinkButton->setSubText("A button that appears as hyperlink text,\nand can navigate to a URl or handle a\nClick event."); - - auto flowLayout = new fancy::FlowLayout(_uiHome->exhibitionArea); - flowLayout->addWidget(button); - flowLayout->addWidget(acrylicBrush); - flowLayout->addWidget(hyperlinkButton); -} - -void HomePage::onFastPixmapUpdate() -{ - if (_fastBlurSignalCount < 0 || ++_fastBlurSignalCount >= FastBlurMergeCount) - { - // 预览截图的有效 DPR 为 1,减少快速缩放期间的滤波像素数 - const qreal scale = 1.0 / qMax(1.0, _uiHome->homeImage->devicePixelRatioF()); - _githubCard->setResolutionScale(scale); - _bilibiliCard->setResolutionScale(scale); - _githubCard->blur(); - _bilibiliCard->blur(); - _fastBlurSignalCount = 0; - } -} - -void HomePage::updateSmoothBlur() -{ - _fastBlurSignalCount = -1; - _githubCard->setResolutionScale(1.0); - _bilibiliCard->setResolutionScale(1.0); - _githubCard->blur(); - _bilibiliCard->blur(); -} - -void HomePage::onThemeChanged() -{ - _uiHome->homeImage->setFillColor( - fancy::ThemeModeController::controller().isAppLight() - ? QColor(208, 217, 228) - : QColor(2, 11, 32) - ); - updateSmoothBlur(); -} - -HomePage::~HomePage() -{ - delete _uiHome; -} diff --git a/Example/Main_Program_Page_Code/Home/Home.h b/Example/Main_Program_Page_Code/Home/Home.h deleted file mode 100644 index cb4951f3..00000000 --- a/Example/Main_Program_Page_Code/Home/Home.h +++ /dev/null @@ -1,52 +0,0 @@ -// -// Created by TX on 2025/11/18. -// - -#ifndef QWIDGET_FANCYUI_HOME_H -#define QWIDGET_FANCYUI_HOME_H -#include - -QT_BEGIN_NAMESPACE - -namespace Ui -{ - class Home; -} - -QT_END_NAMESPACE - -namespace fancy -{ - class IntroductionCard; -} - -class HomePage : public QWidget -{ - Q_OBJECT - -public: - explicit HomePage(QWidget *parent); - - ~HomePage() override; - -private: - static constexpr int FastBlurMergeCount = 2; - - void initializeUi(); - -private slots: - void onFastPixmapUpdate(); - - void updateSmoothBlur(); - - void onThemeChanged(); - -private: - Ui::Home *_uiHome; - fancy::IntroductionCard *_githubCard; - fancy::IntroductionCard *_bilibiliCard; - int _fastBlurSignalCount; -}; - - -#endif //QWIDGET_FANCYUI_HOME_H diff --git a/Example/Main_Program_Page_Code/Icon/IconGallery.cpp b/Example/Main_Program_Page_Code/Icon/IconGallery.cpp deleted file mode 100644 index 8276b06c..00000000 --- a/Example/Main_Program_Page_Code/Icon/IconGallery.cpp +++ /dev/null @@ -1,361 +0,0 @@ -#include "IconGallery.h" -#include "IconListModel.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "Core/Palette/Palette.h" -#include "Core/Theme/ThemeModeController.h" -#include "Icon/Icon.h" -#include "Widgets/PushButton/PushButton.h" -#include "Widgets/ScrollBar/ScrollBar.h" - -namespace -{ - constexpr int TileSize = 96; - constexpr int TileSpacing = 8; - constexpr int IconSize = 28; - constexpr int GridPadding = 16; - constexpr int CaptionSize = 12; - constexpr int ControlRadius = 4; - constexpr int GalleryRadius = 8; - - struct GalleryColors - { - QColor background, card, stroke, text, secondary, hover, pressed, divider, control, controlStroke, innerStroke; - }; - - GalleryColors galleryColors() - { - if (fancy::ThemeModeController::controller().isAppLight()) - return { - QColor(243, 243, 243), - QColor(255, 255, 255, 179), - QColor(0, 0, 0, 15), - QColor(0, 0, 0, 228), - QColor(0, 0, 0, 158), - QColor(0, 0, 0, 9), - QColor(0, 0, 0, 6), - QColor(0, 0, 0, 15), - QColor(255, 255, 255, 179), - QColor(0, 0, 0, 15), - QColor(255, 255, 255) - }; - return { - QColor(32, 32, 32), - QColor(255, 255, 255, 13), - QColor(0, 0, 0, 25), - QColor(255, 255, 255), - QColor(255, 255, 255, 197), - QColor(255, 255, 255, 15), - QColor(255, 255, 255, 10), - QColor(255, 255, 255, 21), - QColor(255, 255, 255, 15), - QColor(255, 255, 255, 18), - QColor(69, 69, 69) - }; - } - - QPixmap renderIcon(const fancy::IconId &id, int size, qreal dpr, const QColor &color) - { - fancy::Icon icon; - icon.addIconEnum(id, QIcon::Normal, QIcon::Off, color); - return icon.pixmap(QSize(size, size), dpr); - } - - class IconTileDelegate final : public QStyledItemDelegate - { - public: - explicit IconTileDelegate(QObject *parent) : - QStyledItemDelegate(parent), - _cache(16 * 1024) {} - - QSize sizeHint(const QStyleOptionViewItem &, const QModelIndex &) const override - { - return {TileSize + TileSpacing, TileSize + TileSpacing}; - } - - void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override - { - const auto *model = static_cast(index.model()); - const auto *item = model->entry(index); - if (!item) - return; - const auto colors = galleryColors(); - const QRectF tile(option.rect.topLeft(), QSizeF(TileSize, TileSize)); - painter->save(); - painter->setRenderHint(QPainter::Antialiasing); - painter->setClipRect(option.rect); - painter->setPen(QPen(colors.stroke, 1)); - painter->setBrush(colors.card); - painter->drawRoundedRect(tile.adjusted(0.5, 0.5, -0.5, -0.5), ControlRadius, ControlRadius); - if (option.state & QStyle::State_MouseOver) - { - painter->setPen(Qt::NoPen); - painter->setBrush((QApplication::mouseButtons() & Qt::LeftButton) ? colors.pressed : colors.hover); - painter->drawRoundedRect(tile, ControlRadius, ControlRadius); - } - if (option.state & QStyle::State_Selected) - { - painter->setBrush(Qt::NoBrush); - painter->setPen(QPen(fancy::Palette::palette()[fancy::ColorRole::AppAccent], 3)); - painter->drawRoundedRect(tile.adjusted(1.5, 1.5, -1.5, -1.5), ControlRadius, ControlRadius); - painter->setPen(QPen(colors.innerStroke, 1)); - painter->drawRoundedRect(tile.adjusted(2.5, 2.5, -2.5, -2.5), 2, 2); - } - - const qreal dpr = painter->device()->devicePixelRatioF(); - const QString key = QStringLiteral("%1/%2/%3/%4").arg(static_cast(item->id.style)) - .arg(item->id.value).arg(dpr, 0, 'g', 12).arg(colors.text.rgba()); - const QPixmap *cached = _cache.object(key); - QPixmap pixmap; - if (cached) - pixmap = *cached; - else - { - pixmap = renderIcon(item->id, IconSize, dpr, colors.text); - const int cost = qMax(1, static_cast(std::ceil(pixmap.width() * pixmap.height() * 4.0 / 1024))); - _cache.insert(key, new QPixmap(pixmap), cost); - } - // Viewbox 的下边距为 16,图标中心位于格子中心上方 8 - painter->drawPixmap(QPointF(tile.x() + 34, tile.y() + 26), pixmap); - QFont font = option.font; - font.setPixelSize(CaptionSize); - painter->setFont(font); - painter->setPen(colors.secondary); - const QRectF label = tile.adjusted(8, 72, -8, -8); - painter->drawText(label, Qt::AlignCenter, QFontMetrics(font).elidedText(item->name, Qt::ElideRight, 80)); - if ((option.state & QStyle::State_HasFocus) && !(option.state & QStyle::State_Selected)) - { - painter->setBrush(Qt::NoBrush); - painter->setPen(QPen(colors.text, 1, Qt::DotLine)); - painter->drawRoundedRect(tile.adjusted(2, 2, -2, -2), 2, 2); - } - painter->restore(); - } - - private: - // 按 KiB 限制页面局部缓存;颜色和 DPR 属于键,不改变图标引擎的全局缓存策略 - mutable QCache _cache; - }; - - class IconGridView final : public QListView - { - public: - explicit IconGridView(QWidget *parent) : - QListView(parent) - { - setFrameShape(QFrame::NoFrame); - setViewportMargins(GridPadding, GridPadding, GridPadding - TileSpacing, GridPadding - TileSpacing); - setViewMode(QListView::IconMode); - setMovement(QListView::Static); - setResizeMode(QListView::Adjust); - setWrapping(true); - setGridSize({TileSize + TileSpacing, TileSize + TileSpacing}); - setUniformItemSizes(true); - // 等尺寸目录布局不解析 SVG;同步布局保证切换分类后可直接定位末尾的选中项 - setLayoutMode(QListView::SinglePass); - setSelectionMode(QAbstractItemView::SingleSelection); - setEditTriggers(QAbstractItemView::NoEditTriggers); - setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); - setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - setVerticalScrollBar(new fancy::ScrollBar(Qt::Vertical, this)); - setMouseTracking(true); - viewport()->setAttribute(Qt::WA_Hover); - setMinimumWidth(TileSize + GridPadding * 2 + 12); - setAccessibleName(QStringLiteral("图标列表")); - } - - QModelIndex indexAt(const QPoint &point) const override - { - const QModelIndex index = QListView::indexAt(point); - // 网格单元包含右侧和底部间距,间距不参与悬停和点击命中 - return index.isValid() && QRect(visualRect(index).topLeft(), QSize(TileSize, TileSize)).contains(point) - ? index - : QModelIndex(); - } - }; -} - -class IconDetailPanel final : public QWidget -{ -public: - explicit IconDetailPanel(QWidget *parent) : - QWidget(parent) - { - auto *layout = new QVBoxLayout(this); - layout->setContentsMargins(16, 106, 16, 16); - layout->setSpacing(8); - _name = addField(layout, QStringLiteral("图标名称")); - _library = addField(layout, QStringLiteral("图标库")); - _enum = addField(layout, QStringLiteral("枚举成员")); - _copy = new fancy::PushButton(QStringLiteral("复制枚举"), this); - layout->addWidget(_copy, 0, Qt::AlignLeft); - layout->addStretch(); - connect(_copy, &QPushButton::clicked, this, [this] { - QApplication::clipboard()->setText(_entry.enumName); - _copy->setText(QStringLiteral("已复制")); - QTimer::singleShot(1200, _copy, [this] { _copy->setText(QStringLiteral("复制枚举")); }); - }); - setMinimumWidth(216); - updateAppearance(); - } - - void setEntry(const IconEntry *entry) - { - _valid = entry != nullptr; - _entry = entry ? *entry : IconEntry{}; - _name->setText(_entry.name); - _library->setText(_entry.library); - // 在命名空间分隔符后允许换行,长枚举不会撑宽详情区 - _enum->setText(QString(_entry.enumName).replace(QStringLiteral("::"), QStringLiteral("::\u200b"))); - _copy->setEnabled(_valid); - _copy->setText(QStringLiteral("复制枚举")); - update(); - } - - void updateAppearance() - { - const auto colors = galleryColors(); - for (auto *label: findChildren()) - { - QPalette pal = label->palette(); - pal.setColor(QPalette::WindowText, label->property("caption").toBool() ? colors.secondary : colors.text); - label->setPalette(pal); - } - update(); - } - -protected: - void paintEvent(QPaintEvent *) override - { - const auto colors = galleryColors(); - QPainter painter(this); - painter.setRenderHint(QPainter::Antialiasing); - QPainterPath background; - background.addRoundedRect(QRectF(rect()), GalleryRadius, GalleryRadius); - background.addRect(QRectF(0, 0, width() / 2.0, height())); - background.setFillRule(Qt::WindingFill); - painter.fillPath(background, colors.card); - painter.setPen(colors.divider); - painter.drawLine(0, 0, 0, height()); - if (!_valid) - return; - painter.setPen(QPen(colors.controlStroke, 1)); - painter.setBrush(colors.control); - painter.drawRoundedRect(QRectF(16.5, 16.5, 65, 65), ControlRadius, ControlRadius); - painter.drawPixmap(QPointF(25, 25), renderIcon(_entry.id, 48, devicePixelRatioF(), colors.text)); - } - -private: - QLabel *addField(QVBoxLayout *layout, const QString &title) - { - auto *caption = new QLabel(title, this); - caption->setProperty("caption", true); - QFont font = caption->font(); - font.setPixelSize(CaptionSize); - caption->setFont(font); - layout->addWidget(caption); - auto *value = new QLabel(this); - value->setTextFormat(Qt::PlainText); - value->setWordWrap(true); - value->setTextInteractionFlags(Qt::TextSelectableByMouse); - font.setPixelSize(14); - value->setFont(font); - layout->addWidget(value); - layout->addSpacing(16); - return value; - } - - IconEntry _entry; - bool _valid = false; - QLabel *_name; - QLabel *_library; - QLabel *_enum; - fancy::PushButton *_copy; -}; - -IconGallery::IconGallery(QWidget *parent) : - QWidget(parent), - _model(new IconListModel(this)), - _view(new IconGridView(this)), - _details(new IconDetailPanel(this)) -{ - setObjectName(QStringLiteral("iconGallery")); - auto *layout = new QHBoxLayout(this); - layout->setContentsMargins(1, 1, 1, 1); - layout->setSpacing(0); - layout->addWidget(_view, 1); - // 详情较 Gallery 精简,采用 Fluent 图标页的窄侧栏思路,保留独立滚动 - auto *detailsScroll = new QScrollArea(this); - detailsScroll->setFrameShape(QFrame::NoFrame); - detailsScroll->setFixedWidth(240); - detailsScroll->setWidgetResizable(true); - detailsScroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - detailsScroll->setVerticalScrollBar(new fancy::ScrollBar(Qt::Vertical, detailsScroll)); - detailsScroll->setWidget(_details); - _details->setAutoFillBackground(false); - detailsScroll->setAutoFillBackground(false); - detailsScroll->viewport()->setAutoFillBackground(false); - layout->addWidget(detailsScroll); - _view->setModel(_model); - _view->setItemDelegate(new IconTileDelegate(_view)); - connect(_view->selectionModel(), &QItemSelectionModel::currentChanged, this, - [this](const QModelIndex &index) { _details->setEntry(_model->entry(index)); }); - connect(&fancy::Palette::palette(), &fancy::Palette::appThemeChange, this, &IconGallery::updateAppearance); - connect(&fancy::Palette::palette(), &fancy::Palette::appColorChange, this, &IconGallery::updateAppearance); - updateAppearance(); - setLibrary(std::nullopt); -} - -void IconGallery::setLibrary(std::optional library) -{ - const auto *entry = _model->entry(_view->currentIndex()); - const std::optional previous = entry ? std::optional(entry->id) : std::nullopt; - _model->setLibrary(library); - QModelIndex selected = _model->index(0, 0); - if (previous) - for (int row = 0; row < _model->rowCount(); ++row) - if (_model->entry(_model->index(row, 0))->id == *previous) - { - selected = _model->index(row, 0); - break; - } - _view->setCurrentIndex(selected); - _view->scrollTo(selected); - _details->setEntry(_model->entry(selected)); -} - -void IconGallery::updateAppearance() -{ - const auto colors = galleryColors(); - QPalette pal = palette(); - pal.setColor(QPalette::Base, colors.background); - pal.setColor(QPalette::Window, colors.background); - pal.setColor(QPalette::Text, colors.text); - setPalette(pal); - _view->setPalette(pal); - _details->updateAppearance(); - _view->viewport()->update(); - update(); -} - -void IconGallery::paintEvent(QPaintEvent *) -{ - const auto colors = galleryColors(); - QPainter painter(this); - painter.setRenderHint(QPainter::Antialiasing); - painter.setPen(QPen(colors.stroke, 1)); - painter.setBrush(colors.background); - painter.drawRoundedRect(QRectF(rect()).adjusted(0.5, 0.5, -0.5, -0.5), GalleryRadius, GalleryRadius); -} diff --git a/Example/Main_Program_Page_Code/Icon/IconGallery.h b/Example/Main_Program_Page_Code/Icon/IconGallery.h deleted file mode 100644 index 9c10c0ce..00000000 --- a/Example/Main_Program_Page_Code/Icon/IconGallery.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef EXAMPLE_ICONGALLERY_H -#define EXAMPLE_ICONGALLERY_H - -#include -#include -#include "Core/Defs.h" - -class IconListModel; -class QListView; -class IconDetailPanel; - -/** @brief 图标网格及详情容器,独立于页面导航和全局配色配置 */ -class IconGallery final : public QWidget -{ -public: - explicit IconGallery(QWidget *parent = nullptr); - - /** @brief - * 切换图标库,保留仍属于当前分类的选中图标;空值显示全部 - */ - void setLibrary(std::optional library); - -protected: - void paintEvent(QPaintEvent *event) override; - -private: - void updateAppearance(); - - IconListModel *_model; - QListView *_view; - IconDetailPanel *_details; -}; -#endif diff --git a/Example/Main_Program_Page_Code/Icon/IconListModel.cpp b/Example/Main_Program_Page_Code/Icon/IconListModel.cpp deleted file mode 100644 index 1a49c27e..00000000 --- a/Example/Main_Program_Page_Code/Icon/IconListModel.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "IconListModel.h" - -namespace -{ - template - void appendLibrary(QVector &entries, const QString &library, const QString &type) - { - for (const auto value : magic_enum::enum_values()) - { - const auto name = magic_enum::enum_name(value); - const QString text = QString::fromLatin1(name.data(), static_cast(name.size())); - entries.append({fancy::iconId(value), text, library, QStringLiteral("fancy::%1::%2").arg(type, text)}); - } - } -} - -IconListModel::IconListModel(QObject *parent) : QAbstractListModel(parent) -{ - appendLibrary(_entries, QStringLiteral("IconPark"), QStringLiteral("IconPark")); - appendLibrary(_entries, QStringLiteral("Ant Design"), QStringLiteral("AntDesignIcons")); - appendLibrary(_entries, QStringLiteral("Bootstrap"), QStringLiteral("BootstrapIcons")); - setLibrary(std::nullopt); -} - -int IconListModel::rowCount(const QModelIndex &parent) const -{ - return parent.isValid() ? 0 : static_cast(_rows.size()); -} - -QVariant IconListModel::data(const QModelIndex &index, int role) const -{ - const auto *item = entry(index); - if (!item) - return {}; - if (role == Qt::DisplayRole || role == Qt::AccessibleTextRole) - return item->name; - if (role == Qt::AccessibleDescriptionRole) - return item->enumName; - return {}; -} - -void IconListModel::setLibrary(std::optional library) -{ - beginResetModel(); - _rows.clear(); - for (int i = 0; i < _entries.size(); ++i) - if (!library || _entries[i].id.style == *library) - _rows.append(i); - endResetModel(); -} - -const IconEntry *IconListModel::entry(const QModelIndex &index) const -{ - if (!index.isValid() || index.model() != this || index.column() != 0 || index.row() < 0 || index.row() >= _rows.size()) - return nullptr; - return &_entries[_rows[index.row()]]; -} diff --git a/Example/Main_Program_Page_Code/Icon/IconListModel.h b/Example/Main_Program_Page_Code/Icon/IconListModel.h deleted file mode 100644 index 511c7d31..00000000 --- a/Example/Main_Program_Page_Code/Icon/IconListModel.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef EXAMPLE_ICONLISTMODEL_H -#define EXAMPLE_ICONLISTMODEL_H - -#include -#include -#include -#include "Core/Defs.h" - -/** @brief 图标目录中的一项,不持有渲染器或位图 */ -struct IconEntry -{ - fancy::IconId id; - QString name; - QString library; - QString enumName; -}; - -/** @brief 为图标展示提供枚举目录,分类切换仅更新可见行映射 */ -class IconListModel final : public QAbstractListModel -{ -public: - explicit IconListModel(QObject *parent = nullptr); - - int rowCount(const QModelIndex &parent = {}) const override; - - QVariant data(const QModelIndex &index, int role) const override; - - /** @brief - * 设置显示的图标库;空值表示所有图标 - */ - void setLibrary(std::optional library); - - /** - * @brief 返回当前可见行的数据;无效索引返回 nullptr - */ - const IconEntry *entry(const QModelIndex &index) const; - -private: - QVector _entries; - QVector _rows; -}; -#endif diff --git a/Example/Main_Program_Page_Code/Icon/IconPage.cpp b/Example/Main_Program_Page_Code/Icon/IconPage.cpp deleted file mode 100644 index d6c4dd5c..00000000 --- a/Example/Main_Program_Page_Code/Icon/IconPage.cpp +++ /dev/null @@ -1,65 +0,0 @@ -// -// Created by TX on 2025/11/19. -// - -#include "IconPage.h" - -#include -#include - -#include "../ui_general_display_page.h" - -#include "IconGallery.h" -#include "Widgets/SelectorBar/SelectorBar.h" -#include "Widgets/SelectorBar/SelectorBarItem.h" - -IconPage::IconPage(QWidget *parent) : - QWidget(parent), - _ui(new Ui::GeneralDisplayPage) -{ - _ui->setupUi(this); - _ui->header->setMainText("图标"); - _ui->header->setSubText("namespace fancy"); - // 网格和详情各自滚动,移除通用页面的外层滚动区,避免嵌套滚动和高度膨胀 - auto *contents = _ui->scrollArea->takeWidget(); - contents->setAutoFillBackground(false); - _ui->verticalLayout->removeWidget(_ui->scrollArea); - _ui->verticalLayout->addWidget(contents, 1); - delete _ui->scrollArea; - _ui->scrollArea = nullptr; - auto *contents_layout = new QVBoxLayout(_ui->contents); - contents_layout->setSpacing(12); - contents_layout->setContentsMargins(36, 0, 36, 28); - auto *selectorBar = new fancy::SelectorBar(_ui->contents); - QFont selectorFont = selectorBar->font(); - selectorFont.setPixelSize(18); - selectorBar->setFont(selectorFont); - auto *allItem = selectorBar->addItem(QStringLiteral("所有图标")); - selectorBar->addItem("IconPark", QIcon(QStringLiteral(":/Logos/iconpark.png"))); - selectorBar->addItem("Ant Design", QIcon(QStringLiteral(":/Logos/ant-design.svg"))); - selectorBar->addItem("Bootstrap", QIcon(QStringLiteral(":/Logos/bootstrap.png"))); - - for (int i = 0; i < selectorBar->itemCount(); ++i) - selectorBar->itemAt(i)->setIconSize(QSize(24, 24)); - - selectorBar->setSelectedItem(allItem); - contents_layout->addWidget(selectorBar); - - auto *gallery = new IconGallery(_ui->contents); - contents_layout->addWidget(gallery, 1); - connect(selectorBar, &fancy::SelectorBar::selectionChanged, gallery, [selectorBar, gallery](fancy::SelectorBarItem *item) { - if (item == selectorBar->itemAt(1)) - gallery->setLibrary(fancy::IconStyle::IconPark); - else if (item == selectorBar->itemAt(2)) - gallery->setLibrary(fancy::IconStyle::AntDesignIcons); - else if (item == selectorBar->itemAt(3)) - gallery->setLibrary(fancy::IconStyle::BootstrapIcons); - else - gallery->setLibrary(std::nullopt); - }); -} - -IconPage::~IconPage() -{ - delete _ui; -} diff --git a/Example/Main_Program_Page_Code/IconPage.cpp b/Example/Main_Program_Page_Code/IconPage.cpp new file mode 100644 index 00000000..ad2e0653 --- /dev/null +++ b/Example/Main_Program_Page_Code/IconPage.cpp @@ -0,0 +1,60 @@ +// +// Created by TX on 2025/11/19. +// + +#include "IconPage.h" + +#include + +#include "ExampleControlCard.h" +#include "TextLabel.h" +#include "TransparentButton.h" + +IconPage::IconPage(QWidget *parent) : + QWidget(parent), + _ui(new Ui::GeneralDisplayPage) +{ + _ui->setupUi(this); + _ui->header->setMainText("图标"); + _ui->header->setSubText("namespace fancy"); + auto *contents_layout = new QVBoxLayout(_ui->contents); + contents_layout->setSpacing(32); + + contents_layout->setContentsMargins(36, 28, 36, 28); + auto *tip = new fancy::TransparentButton(fancy::IconPark::Code, "编写中......", _ui->contents); + tip->setFont(QFont(tip->font().family(), 70)); + tip->setIconSize(QSize(265, 265)); + tip->setMinimumSize(1000, 365); + contents_layout->addWidget(tip); + + contents_layout->addStretch(1); +} + +IconPage::~IconPage() +{ + delete _ui; +} + +ExampleControlCard * IconPage::makeCardLayout(const QString &introductionText, const QString &objName) +{ + auto *peripheral = new QWidget(_ui->contents); + auto introductionLabel = new fancy::TextLabel(introductionText, peripheral); + QFont font(introductionLabel->font()); + font.setPointSizeF(10.5); + introductionLabel->setFont(font); + auto controlCard = new ExampleControlCard(peripheral); + controlCard->setObjectName(objName); + _controlCards.append(controlCard); + controlCard->setTitleText("源代码"); + auto *control_content_layout = new QHBoxLayout(controlCard->controlAreaWidget()); + control_content_layout->setContentsMargins(16, 12, 12, 16); + + auto *layout = new QVBoxLayout(peripheral); + layout->setSpacing(12); + layout->setContentsMargins(36, 0, 36, 0); + layout->addWidget(introductionLabel); + layout->addWidget(controlCard); + + _ui->contents->layout()->addWidget(peripheral); + return controlCard; +} diff --git a/Example/Main_Program_Page_Code/Icon/IconPage.h b/Example/Main_Program_Page_Code/IconPage.h similarity index 71% rename from Example/Main_Program_Page_Code/Icon/IconPage.h rename to Example/Main_Program_Page_Code/IconPage.h index 1e8cbefd..c1d67931 100644 --- a/Example/Main_Program_Page_Code/Icon/IconPage.h +++ b/Example/Main_Program_Page_Code/IconPage.h @@ -6,6 +6,7 @@ #define QWIDGET_FANCYUI_ICONPAGE_H #include +class ExampleControlCard; QT_BEGIN_NAMESPACE namespace Ui @@ -25,7 +26,10 @@ class IconPage : public QWidget ~IconPage() override; private: + ExampleControlCard *makeCardLayout(const QString &introductionText, const QString &objName); + Ui::GeneralDisplayPage *_ui; + QList _controlCards; }; diff --git a/Example/Main_Program_Page_Code/Palette/PalettePage.cpp b/Example/Main_Program_Page_Code/PalettePage.cpp similarity index 96% rename from Example/Main_Program_Page_Code/Palette/PalettePage.cpp rename to Example/Main_Program_Page_Code/PalettePage.cpp index 82dc238d..520e5f9b 100644 --- a/Example/Main_Program_Page_Code/Palette/PalettePage.cpp +++ b/Example/Main_Program_Page_Code/PalettePage.cpp @@ -5,14 +5,15 @@ #include "PalettePage.h" #include -#include "../ui_general_display_page.h" +#include #include "DerivativeColorSelector.h" #include "ExampleControlCard.h" #include "ExampleDefs.h" -#include "Core/Theme/ThemeModeController.h" -#include "Widgets/PushButton/PushButton.h" -#include "Widgets/TextLabel/TextLabel.h" +#include "PushButton.h" +#include "TextLabel.h" +#include "ThemeModeController.h" +#include "TransparentButton.h" PalettePage::PalettePage(QWidget *parent) : QWidget(parent), diff --git a/Example/Main_Program_Page_Code/Palette/PalettePage.h b/Example/Main_Program_Page_Code/PalettePage.h similarity index 100% rename from Example/Main_Program_Page_Code/Palette/PalettePage.h rename to Example/Main_Program_Page_Code/PalettePage.h diff --git a/Example/Main_Program_Page_Code/general_display_page.ui b/Example/Main_Program_Page_Code/general_display_page.ui index 6b3292f3..4ba67df1 100644 --- a/Example/Main_Program_Page_Code/general_display_page.ui +++ b/Example/Main_Program_Page_Code/general_display_page.ui @@ -61,7 +61,7 @@ fancy::ScrollArea QScrollArea -
Widgets/ScrollArea/ScrollArea.h
+
ScrollArea.h
1
diff --git a/Example/Main_Program_Page_Code/Home/home.ui b/Example/Main_Program_Page_Code/home.ui similarity index 96% rename from Example/Main_Program_Page_Code/Home/home.ui rename to Example/Main_Program_Page_Code/home.ui index 0fcb5706..d44b73b0 100644 --- a/Example/Main_Program_Page_Code/Home/home.ui +++ b/Example/Main_Program_Page_Code/home.ui @@ -100,7 +100,7 @@ - Fluent for Qt + Fluent for Qt6 QWidget FancyUI @@ -177,7 +177,6 @@ QWidget FancyUI 11 - 75 true @@ -188,6 +187,9 @@ QWidget FancyUI + + Qt::Orientation::Horizontal + 40 @@ -212,18 +214,18 @@ QWidget FancyUI fancy::CenteredImageWidget QWidget -
Widgets/CenteredImage/CenteredImageWidget.h
+
CenteredImageWidget.h
1
fancy::TextLabel QLabel -
Widgets/TextLabel/TextLabel.h
+
TextLabel.h
fancy::ScrollArea QScrollArea -
Widgets/ScrollArea/ScrollArea.h
+
ScrollArea.h
1
diff --git a/Example/main.cpp b/Example/main.cpp index 4fd73531..3903fa7e 100644 --- a/Example/main.cpp +++ b/Example/main.cpp @@ -1,22 +1,9 @@ #include -#include -#include #include "widget.h" int main(int argc, char *argv[]) { -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); - QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); - QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); -#endif - QApplication a(argc, argv); - - QFont font = QApplication::font(); - font.setFamilies({"Segoe UI", "Microsoft YaHei"}); - QApplication::setFont(font); - Widget w; w.show(); return QApplication::exec(); diff --git a/Example/widget.cpp b/Example/widget.cpp index 9f2d1b75..030fbafa 100644 --- a/Example/widget.cpp +++ b/Example/widget.cpp @@ -10,28 +10,31 @@ #include #include #include +#include #include #include #include #include #include -#include "Main_Program_Page_Code/Icon/IconPage.h" -#include "Main_Program_Page_Code/Palette/PalettePage.h" -#include "Core/Defs.h" -#include "utils/SvgParsing.h" -#include "Core/SystemAccessor.h" -#include "Core/Palette/Palette.h" -#include "Core/Theme/ThemeModeController.h" -#include "Icon/IconEngine.h" -#include "Icon/IconEnums.h" -#include "Layout/FlowLayout.h" -#include "Main_Program_Page_Code/BasicInput/BasicInput.h" -#include "Main_Program_Page_Code/Home/Home.h" -#include "Widgets/Sidebar/Sidebar.h" -#include "Widgets/Sidebar/SidebarButton.h" -#include "Widgets/TitleBar/TitleBar.h" -#include "Widgets/TitleBar/Button/MaximizeButton.h" +#include "Defs.h" +#include "FlowLayout.h" +#include "Icon.h" +#include "IconEngine.h" +#include "IconEnums.h" +#include "IconPage.h" +#include "MaximizeButton.h" +#include "Palette.h" +#include "PalettePage.h" +#include "Sidebar.h" +#include "SidebarButton.h" +#include "SvgParsing.h" +#include "SvgWidget.h" +#include "SystemAccessor.h" +#include "ThemeModeController.h" +#include "TitleBar.h" +#include "Main_Program_Page_Code/BasicInput.h" +#include "Main_Program_Page_Code/Home.h" Widget::Widget(QWidget *parent) : diff --git a/Example/widget.h b/Example/widget.h index 4f1a8265..032dc5ee 100644 --- a/Example/widget.h +++ b/Example/widget.h @@ -5,7 +5,7 @@ #ifndef WIDGET_H #define WIDGET_H -#include "Widgets/Window/StackWindow.h" +#include "StackWindow.h" class Widget : public fancy::StackWindow diff --git a/README.md b/README.md index 99b4accc..d78025e8 100644 --- a/README.md +++ b/README.md @@ -18,16 +18,17 @@

简体中文 | English

- --- # 更新说明 - - 目前正在进行全面重构更新,可编译示例程序先行体验 - 代码全面重构,更清晰、更易读…告别屎山🤔? -- 目前仅支持 Windows 系统,后续计划更新支持 Linux、Android 和 WebAssembly支持 -- 已初步适配 Qt5 ,推荐 Qt 5.15 版本及以上,更低版本未经测试 -- 更多的图标:引入IconPark、Ant Design 和 Bootstrap 图标,多达5400+图标支持 +- 更多的图标:引入`IconPark`、`Ant Design` 和` Bootstrap` 图标,多达5400+图标支持 +- 目前仅支持 Qt 6 和 Windows 系统,后续计划更新支持 Qt 5 、Linux、Android 和 WebAssembly支持 +- 暂时移除了 OpenCV 支持、部分尚不完善的类/功能等 +- 第三方库使用:~~[OpenCV](https://github.com/opencv/opencv)~~、[magic-enum](https://github.com/Neargye/magic_enum)、[IconPark](https://github.com/bytedance/iconpark)、[Bootstrap Icons](https://github.com/twbs/icons)、[Ant Design](https://github.com/ant-design/ant-design) +- 参考和致谢:[SAPC-APCA](https://github.com/Myndex/SAPC-APCA)、[apca-w3](https://github.com/Myndex/apca-w3)、[WinUI 3](https://github.com/microsoft/microsoft-ui-xaml) +- 不再使用 `QMake`,改为 `CMake` - 移除了项目之外的旧文件和Python文件 --- @@ -38,21 +39,15 @@ ![HomePreview_Dark](./docs/HomePreview_Dark.png) -![PalettePreview](./docs/PalettePerview.png) +![HomePreview_Dark](./docs/PalettePerview.png) # 示例程序编译指南 -在根目录的 `CMakeLists.txt` 中,按注释修改 Qt 路径,即可在 Visual Studio、QtCreator、Clion 等中直接开始编译! +在 `CMakeLists.txt` 中修改`Qt`路径,即可在 IDE(Visual Studio、QtCreator、Clion等)中直接开始编译! ![SetQtPath](./docs/SetQtPath.png) -# 第三方项目参考与致谢 - -- 使用:[magic_enum](https://github.com/Neargye/magic_enum)、[IconPark](https://github.com/bytedance/iconpark)、[Bootstrap Icons](https://github.com/twbs/icons)、[Ant Design Icons](https://github.com/ant-design/ant-design-icons) -- 参考:[SAPC-APCA](https://github.com/Myndex/SAPC-APCA)、[apca-w3](https://github.com/Myndex/apca-w3)、[Ant Design 色彩体系](https://2x.ant.design/docs/spec/colors-cn)、[WinUI 3](https://github.com/microsoft/microsoft-ui-xaml) -- 第三方项目及资源遵循其各自的许可证 - --- -最后编辑日期:2026-09-24 by @COLORREF +最后编辑日期:2025-11-19 by @COLORREF diff --git a/README_EN.md b/README_EN.md index 1c208e26..57711a30 100644 --- a/README_EN.md +++ b/README_EN.md @@ -22,13 +22,15 @@ English | 简体中文 --- # Update Notes - -- A comprehensive refactor is currently in progress; the demo application is available for an early look. -- The codebase has been extensively refactored to make it cleaner and easier to read... goodbye, spaghetti code 🤔? -- Windows is currently the only supported platform; Linux, Android, and WebAssembly support is planned. -- Initial Qt 5 support has been added. Qt 5.15 or later is recommended; earlier versions have not been tested. -- Includes more than 5,400 icons from IconPark, Ant Design Icons, and Bootstrap Icons. -- Removed legacy files and Python files unrelated to the project. +- A full-scale refactor and update is currently in progress. You can first try out the compilable demo program. +- Complete code refactoring — cleaner, more readable… saying goodbye to spaghetti code 🤔? +- More icons: integrated `IconPark`, `Ant Design`, and `Bootstrap` icon sets, with support for over 5400+ icons. +- Currently supports **Qt 6** and **Windows** only. Planned future updates will include support for **Qt 5**, **Linux**, **Android**, and **WebAssembly**. +- Compared to the previous version, **OpenCV** support and some incomplete classes/features have been temporarily removed. +- Third-party libraries used: ~~[OpenCV](https://github.com/opencv/opencv)~~, [magic-enum](https://github.com/Neargye/magic_enum), [IconPark](https://github.com/bytedance/iconpark), [Bootstrap Icons](https://github.com/twbs/icons), [Ant Design](https://github.com/ant-design/ant-design) +- References and acknowledgements: [SAPC-APCA](https://github.com/Myndex/SAPC-APCA), [apca-w3](https://github.com/Myndex/apca-w3), [WinUI 3](https://github.com/microsoft/microsoft-ui-xaml) +- Replaced `QMake` with `CMake` +- Removed legacy and external Python files from the project --- @@ -38,20 +40,14 @@ English | 简体中文 ![HomePreview_Dark](./docs/HomePreview_Dark.png) -![PalettePreview](./docs/PalettePerview.png) +![HomePreview_Dark](./docs/PalettePerview.png) # Demo Program Build Guide -In the root `CMakeLists.txt`, follow the comments to set the Qt path, then build directly with Visual Studio, Qt Creator, CLion, or another supported IDE. +Modify the `Qt` path in `CMakeLists.txt`, then you can directly start building in your IDE (Visual Studio, QtCreator, Clion, etc.)! ![SetQtPath](./docs/SetQtPath.png) -# Third-Party Projects, References, and Acknowledgements - -- Used: [magic_enum](https://github.com/Neargye/magic_enum), [IconPark](https://github.com/bytedance/iconpark), [Bootstrap Icons](https://github.com/twbs/icons), [Ant Design Icons](https://github.com/ant-design/ant-design-icons) -- Referenced: [SAPC-APCA](https://github.com/Myndex/SAPC-APCA), [apca-w3](https://github.com/Myndex/apca-w3), [Ant Design Color System](https://2x.ant.design/docs/spec/colors), [WinUI 3](https://github.com/microsoft/microsoft-ui-xaml) -- Third-party projects and assets remain subject to their respective licenses. - --- -Last edited: 2026-09-24 by @COLORREF +Last edited: 2025-11-19 by @COLORREF diff --git a/cmake/QFancyUIConfig.cmake.in b/cmake/QFancyUIConfig.cmake.in new file mode 100644 index 00000000..dd2a64fd --- /dev/null +++ b/cmake/QFancyUIConfig.cmake.in @@ -0,0 +1,18 @@ +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) + +# 查找 Qt6 依赖 +find_dependency(Qt6 6.5 REQUIRED COMPONENTS Core Gui Widgets Xml Svg SvgWidgets) + +# 包含目标文件 +include("${CMAKE_CURRENT_LIST_DIR}/QFancyUITargets.cmake") + +# 添加版本检查 +set(QFANCYUI_VERSION_MAJOR @PROJECT_VERSION_MAJOR@) +set(QFANCYUI_VERSION_MINOR @PROJECT_VERSION_MINOR@) +set(QFANCYUI_VERSION_PATCH @PROJECT_VERSION_PATCH@) +set(QFANCYUI_VERSION @PROJECT_VERSION@) + +# 检查版本兼容性 +check_required_components(QFancyUI) diff --git a/docs/HomePreview_Dark.png b/docs/HomePreview_Dark.png index a827a9e6..4a7a0f4d 100644 Binary files a/docs/HomePreview_Dark.png and b/docs/HomePreview_Dark.png differ diff --git a/docs/HomePreview_Light.png b/docs/HomePreview_Light.png index c3057c19..4b6a1073 100644 Binary files a/docs/HomePreview_Light.png and b/docs/HomePreview_Light.png differ diff --git a/docs/PalettePerview.png b/docs/PalettePerview.png index abe991ab..3a4f78f7 100644 Binary files a/docs/PalettePerview.png and b/docs/PalettePerview.png differ diff --git a/docs/SetQtPath.png b/docs/SetQtPath.png index aca56c38..9a4460de 100644 Binary files a/docs/SetQtPath.png and b/docs/SetQtPath.png differ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt deleted file mode 100644 index 310289df..00000000 --- a/src/CMakeLists.txt +++ /dev/null @@ -1,42 +0,0 @@ -# 收集 FancyUI 源码和 Qt 资源文件 -file(GLOB_RECURSE FANCYUI_SOURCES CONFIGURE_DEPENDS - "${CMAKE_CURRENT_SOURCE_DIR}/Fancy/*.h" - "${CMAKE_CURRENT_SOURCE_DIR}/Fancy/*.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/resource/*.qrc" -) - -# 将 源码和资源直接加入示例程序 -target_sources(${PROJECT_NAME} PRIVATE ${FANCYUI_SOURCES}) - -# 精简版 OpenCV 滤波实现直接编入应用,不生成额外的库。 -target_sources(${PROJECT_NAME} PRIVATE - "${CMAKE_CURRENT_SOURCE_DIR}/include/mini_blur/mini_blur.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/include/mini_blur/mini_blur_avx2.cpp" -) -target_link_libraries(${PROJECT_NAME} PRIVATE Threads::Threads) - -# 设置 FancyUI 源码及 magic_enum 的头文件路径 -target_include_directories(${PROJECT_NAME} PRIVATE - "${CMAKE_CURRENT_SOURCE_DIR}/Fancy" - "${CMAKE_CURRENT_SOURCE_DIR}/include/magic_enum" - "${CMAKE_CURRENT_SOURCE_DIR}/include/mini_blur" -) - -# 链接 Qt 通用模块 -target_link_libraries(${PROJECT_NAME} PRIVATE - Qt${QT_VERSION_MAJOR}::Core - Qt${QT_VERSION_MAJOR}::Gui - Qt${QT_VERSION_MAJOR}::Widgets - Qt${QT_VERSION_MAJOR}::Xml - Qt${QT_VERSION_MAJOR}::Svg -) - -# Qt 6 需要额外链接独立的 SvgWidgets 模块 -if (QT_VERSION_MAJOR EQUAL 6) - target_link_libraries(${PROJECT_NAME} PRIVATE Qt6::SvgWidgets) -endif () - -# 链接 Windows 平台所需的系统库 -if (WIN32) - target_link_libraries(${PROJECT_NAME} PRIVATE user32 dwmapi advapi32) -endif () diff --git a/src/Fancy/Animation/IndicatorAnimation.cpp b/src/Fancy/Animation/IndicatorAnimation.cpp deleted file mode 100644 index afc2912a..00000000 --- a/src/Fancy/Animation/IndicatorAnimation.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include "IndicatorAnimation.h" - -namespace fancy -{ - IndicatorAnimation::IndicatorAnimation(QObject *parent) : - QVariantAnimation(parent), - _reveal(QEasingCurve::BezierSpline), - _stretch(QEasingCurve::BezierSpline), - _settle(QEasingCurve::BezierSpline) - { - setStartValue(0.0); - setEndValue(1.0); - _reveal.addCubicBezierSegment({0, 0}, {0, 1}, {1, 1}); - _stretch.addCubicBezierSegment({0.9, 0.1}, {1, 0.2}, {1, 1}); - _settle.addCubicBezierSegment({0.1, 0.9}, {0.2, 1}, {1, 1}); - setEasingCurve(_reveal); - } - - void IndicatorAnimation::reveal(bool animated, qreal initialLength, int msecs) - { - stop(); - _mode = Mode::Reveal; - _initialLength = qMax(0.0, initialLength); - setDuration(qMax(0, msecs)); - setEasingCurve(_reveal); - if (animated) - start(); - else - setCurrentTime(duration()); - } - - qreal IndicatorAnimation::opacity() const - { - return _mode == Mode::Slide || duration() == 0 ? 1.0 : currentValue().toReal(); - } - - void IndicatorAnimation::slide(const QLineF &from, const QLineF &to, Qt::Orientation orientation, int msecs) - { - stop(); - _mode = Mode::Slide; - _orientation = orientation; - const auto normalized = [orientation](const QLineF &line) { - const bool reversed = orientation == Qt::Horizontal ? line.x1() > line.x2() : line.y1() > line.y2(); - return reversed ? QLineF(line.p2(), line.p1()) : line; - }; - _from = normalized(from); - _to = normalized(to); - setDuration(qMax(0, msecs)); - setEasingCurve(QEasingCurve::Linear); - start(); - } - - QLineF IndicatorAnimation::geometry(const QLineF &target) const - { - if (_mode == Mode::Slide) - { - if (duration() == 0) - return _to; - const qreal time = currentTime() / static_cast(duration()); - constexpr qreal split = 0.333; - const QLineF middle = _orientation == Qt::Horizontal - ? QLineF(qMin(_from.x1(), _to.x1()), _from.y1(), qMax(_from.x2(), _to.x2()), _from.y1()) - : QLineF(_from.x1(), qMin(_from.y1(), _to.y1()), _from.x1(), qMax(_from.y2(), _to.y2())); - const bool expanding = time < split; - const QLineF a = expanding ? _from : middle; - const QLineF b = expanding ? middle : _to; - const qreal progress = expanding ? _stretch.valueForProgress(time / split) : _settle.valueForProgress((time - split) / (1 - split)); - return {a.p1() + (b.p1() - a.p1()) * progress, a.p2() + (b.p2() - a.p2()) * progress}; - } - // 沿目标方向展开 - const qreal length = target.length(); - if (qFuzzyIsNull(length)) - return target; - const qreal initialLength = qMin(_initialLength, length); - const qreal halfLength = (initialLength + (length - initialLength) * opacity()) / 2; - const QPointF halfVector = (target.p2() - target.p1()) * (halfLength / length); - const QPointF center = target.center(); - return {center - halfVector, center + halfVector}; - } -} diff --git a/src/Fancy/Animation/IndicatorAnimation.h b/src/Fancy/Animation/IndicatorAnimation.h deleted file mode 100644 index b9028d1c..00000000 --- a/src/Fancy/Animation/IndicatorAnimation.h +++ /dev/null @@ -1,72 +0,0 @@ -#ifndef FANCY_INDICATORANIMATION_H -#define FANCY_INDICATORANIMATION_H - -#include -#include - -namespace fancy -{ - /** - * @brief 提供指示器入场与跨项滑动的几何和透明度动画 - */ - class IndicatorAnimation : public QVariantAnimation - { - Q_OBJECT - - public: - explicit IndicatorAnimation(QObject *parent = nullptr); - - /** - * @brief 从中心展开并淡入。 - * @param animated 为 false 时立即完成动画 - * @param initialLength 初始可见长度;绘制时限制在 0 到目标长度之间 - * @param msecs 本次动画时长,单位为毫秒,默认 167;非正值表示立即完成 - * @note 目标线段由 geometry() 提供,布局改变无需重启动画 - */ - void reveal(bool animated, qreal initialLength = 4.0, int msecs = 167); - - /** - * @brief 先拉伸后收拢至目标线段。 - * @param from 起始线段,与 to 使用同一坐标系。 - * @param to 目标线段 - * @param orientation 线段方向,默认为水平 - * @param msecs 本次时长,单位为毫秒,默认 600;非正值表示立即完成 - * @note 端点按水平从左到右、垂直从上到下的顺序统一;支持正反向切换 - * 快速改选时采用旧选中项还是当前动画几何作为 from,由调用者决定 - */ - void slide(const QLineF &from, const QLineF &to, Qt::Orientation orientation = Qt::Horizontal, int msecs = 600); - - [[nodiscard]] bool isSliding() const { return _mode == Mode::Slide && state() == Running; } - - /** - * @brief 返回最近一次 slide() 设置的目标线段 - */ - [[nodiscard]] QLineF target() const { return _to; } - - /** - * @brief 返回当前指示器线段的可见边界。 - * @param target 当前布局中的目标线段。入场及静态时使用它;滑动时使用 slide() 保存的端点。 - * @return 当前线段,端点表示可见边界。 - * @note 线宽、圆头补偿及裁剪由调用者处理。可将 valueChanged 连接到绘制层的 update()。 - */ - [[nodiscard]] QLineF geometry(const QLineF &target) const; - - /** - * @brief 返回当前透明度;滑动时恒为 1 - */ - [[nodiscard]] qreal opacity() const; - - private: - enum class Mode { Reveal, Slide }; - - Mode _mode = Mode::Reveal; - QLineF _from; - QLineF _to; - QEasingCurve _reveal; - QEasingCurve _stretch; - QEasingCurve _settle; - Qt::Orientation _orientation = Qt::Horizontal; - qreal _initialLength = 4.0; - }; -} -#endif diff --git a/src/Fancy/utils/BezierEasing.cpp b/src/Fancy/BezierEasing.cpp similarity index 100% rename from src/Fancy/utils/BezierEasing.cpp rename to src/Fancy/BezierEasing.cpp diff --git a/src/Fancy/utils/BezierEasing.h b/src/Fancy/BezierEasing.h similarity index 98% rename from src/Fancy/utils/BezierEasing.h rename to src/Fancy/BezierEasing.h index 915bbac5..ef8300e5 100644 --- a/src/Fancy/utils/BezierEasing.h +++ b/src/Fancy/BezierEasing.h @@ -5,7 +5,7 @@ #ifndef QWIDGET_FANCYUI_BEZIEREASING_H #define QWIDGET_FANCYUI_BEZIEREASING_H #include -#include +#include namespace fancy { @@ -36,7 +36,7 @@ namespace fancy static constexpr double kSampleStepSize = 1.0 / (kSplineTableSize - 1.0); double _mX1, _mY1, _mX2, _mY2; - QVector _sampleValues; + QList _sampleValues; constexpr static double A(double a1, double a2) { return 1.0 - 3.0 * a2 + 3.0 * a1; } constexpr static double B(double a1, double a2) { return 3.0 * a2 - 6.0 * a1; } diff --git a/src/Fancy/BlurCard.cpp b/src/Fancy/BlurCard.cpp new file mode 100644 index 00000000..7b2e35a0 --- /dev/null +++ b/src/Fancy/BlurCard.cpp @@ -0,0 +1,39 @@ +// +// Created by TX on 2025/10/30. +// + +#include "BlurCard.h" + +#include + +#include "Defs.h" + +namespace fancy +{ + BlurCard::BlurCard(QWidget *parent) : + BlurCard(parent, nullptr) {} + + BlurCard::BlurCard(QWidget *parent, QWidget *blurredObj) : + QPushButton(parent), + _blurRadius(10), + _radius(10), + _blurredObj(blurredObj) {} + + void BlurCard::blur() + { + if (_blurredObj) + setPixmap(QPixmap::fromImage(gaussianBlur(renderWidgetRegion(_blurredObj, getWidgetRectInAncestor(this, _blurredObj)), _blurRadius))); + } + + void BlurCard::paintEvent(QPaintEvent *event) + { + Q_UNUSED(event); + QPainter painter(this); + painter.setRenderHints(QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform); + QPainterPath path; + path.addRoundedRect(rect(), _radius, _radius); + painter.setClipPath(path); + if (!_pixmap.isNull()) + painter.drawPixmap(rect(), _pixmap); + } +} // fancy diff --git a/src/Fancy/Widgets/BlurCard/BlurCard.h b/src/Fancy/BlurCard.h similarity index 64% rename from src/Fancy/Widgets/BlurCard/BlurCard.h rename to src/Fancy/BlurCard.h index b0c0422a..d379bf0b 100644 --- a/src/Fancy/Widgets/BlurCard/BlurCard.h +++ b/src/Fancy/BlurCard.h @@ -5,7 +5,6 @@ #ifndef QWIDGET_FANCYUI_BLURCARD_H #define QWIDGET_FANCYUI_BLURCARD_H #include -#include namespace fancy { @@ -22,12 +21,10 @@ namespace fancy */ explicit BlurCard(QWidget *parent, QWidget *blurredObj); - void setBlurRadius(const int radius) { _blurRadius = radius; } // 模糊半径使用逻辑单位,截图时按实际采样密度换算 + void setBlurRadius(int radius) { _blurRadius = radius; } [[nodiscard]] int blurRadius() const { return _blurRadius; } - void setResolutionScale(qreal scale); // 原生分辨率为 1.0,低分辨率可用 0.5、0.25等,设置后调用 blur() 重新生成背景 - [[nodiscard]] qreal resolutionScale() const { return _resolutionScale; } void setBlurredObj(QWidget *obj) { _blurredObj = obj; } - void setRadius(const int radius) { _radius = radius; } + void setRadius(int radius) { _radius = radius; } [[nodiscard]] int radius() const { return _radius; } [[nodiscard]] QPixmap pixmap() const { return _pixmap; } void setPixmap(const QPixmap &pixmap) { _pixmap = pixmap; } @@ -42,10 +39,6 @@ namespace fancy int _blurRadius; int _radius; QWidget *_blurredObj; - qreal _resolutionScale; - QImage _blurStage1; - QImage _blurStage2; - QImage _blurOutput; }; } // fancy diff --git a/src/Fancy/Widgets/CenteredImage/CenteredImageWidget.cpp b/src/Fancy/CenteredImageWidget.cpp similarity index 98% rename from src/Fancy/Widgets/CenteredImage/CenteredImageWidget.cpp rename to src/Fancy/CenteredImageWidget.cpp index ec1e5ec6..8a273b7a 100644 --- a/src/Fancy/Widgets/CenteredImage/CenteredImageWidget.cpp +++ b/src/Fancy/CenteredImageWidget.cpp @@ -8,8 +8,8 @@ #include #include -#include "Core/Defs.h" -#include "Core/Palette/Palette.h" +#include "Defs.h" +#include "Palette.h" namespace fancy { diff --git a/src/Fancy/Widgets/CenteredImage/CenteredImageWidget.h b/src/Fancy/CenteredImageWidget.h similarity index 100% rename from src/Fancy/Widgets/CenteredImage/CenteredImageWidget.h rename to src/Fancy/CenteredImageWidget.h diff --git a/src/Fancy/CheckBox.cpp b/src/Fancy/CheckBox.cpp new file mode 100644 index 00000000..c3137fcf --- /dev/null +++ b/src/Fancy/CheckBox.cpp @@ -0,0 +1,24 @@ +// +// Created by TX on 2025/8/30. +// + +#include "CheckBox.h" +#include "CheckBoxStyle.h" + +namespace fancy +{ + CheckBox::CheckBox(QWidget *parent, bool smallIndicatorSize) : + QCheckBox(parent) + { + smallIndicatorSize ? setStyle(new CheckBoxStyle_SM(this)) : setStyle(new CheckBoxStyle(this)); + QFont font = this->font(); + font.setPointSize(smallIndicatorSize ? 10 : 12); + setFont(font); + } + + CheckBox::CheckBox(const QString &text, QWidget *parent, bool smallIndicatorSize) : + CheckBox(parent, smallIndicatorSize) + { + setText(text); + } +} // fancy diff --git a/src/Fancy/Widgets/CheckBox/CheckBox.h b/src/Fancy/CheckBox.h similarity index 81% rename from src/Fancy/Widgets/CheckBox/CheckBox.h rename to src/Fancy/CheckBox.h index 05591a7f..288db417 100644 --- a/src/Fancy/Widgets/CheckBox/CheckBox.h +++ b/src/Fancy/CheckBox.h @@ -16,10 +16,6 @@ namespace fancy explicit CheckBox(QWidget *parent = nullptr, bool smallIndicatorSize = false); explicit CheckBox(const QString &text, QWidget *parent = nullptr, bool smallIndicatorSize = false); - - void setAnimationEnabled(bool enabled); - - [[nodiscard]] bool isAnimationEnabled() const; }; } // fancy diff --git a/src/Fancy/CheckBoxStyle.cpp b/src/Fancy/CheckBoxStyle.cpp new file mode 100644 index 00000000..18973ccf --- /dev/null +++ b/src/Fancy/CheckBoxStyle.cpp @@ -0,0 +1,212 @@ +// +// Created by TX on 2025/8/26. +// + +#include "CheckBoxStyle.h" +#include +#include +#include "ControlState.h" +#include "Defs.h" +#include "palette.h" + + +/** + * + * @param CLASS 对象作用域 + * @param indicatorSide 指示器边长 + * @param p1x 勾第一个点的x坐标 计算公式:p1x = 0.23875 * indicatorSide + * @param p1y 勾第一个点的y坐标 计算公式:p1y = 0.58125 * indicatorSide + * @param p2x 勾第二个点的x坐标 计算公式:p2x = 0.42625 * indicatorSide + * @param p2y 勾第二个点的y坐标 计算公式:p2y = 0.8 * indicatorSide + * @param p3x 勾第三个点的x坐标 计算公式:p3x = 0.86375 * indicatorSide + * @param p3y 勾第三个点的y坐标 计算公式:p3y = 0.26875 * indicatorSide + * @param radiusAniTime 选中填充动画运行时间,单位:ms + * @param p1AniTime 勾选动画第一段时间,单位:ms + * @param p2AniTime 勾选动画第二段时间,单位:ms + * @param aniRadiusEnd 选中填充动画的结束值(半径动画的半径最大值)即选中方框外接圆半径 + * __________________ + * | | + * | /<-|-------p3 + * p1 -------|-->\ / | + * | \ / | + * p2-------|---->\ / | + * |________________| + * + */ +#define CHECKBOX_STYLE_INIT(CLASS,indicatorSide,p1x,p1y,p2x,p2y,p3x,p3y,radiusAniTime,p1AniTime,p2AniTime,aniRadiusEnd) \ + QProxyStyle(nullptr), \ + _checkbox_side(indicatorSide), \ + _flag(true), \ + _p1(p1x, p1y), \ + _p2(p2x, p2y), \ + _circum_radius(0), \ + _tick_p1(_p1), \ + _tick_p2(_p2), \ + _tick_animation_1(new QVariantAnimation(this)), \ + _tick_animation_2(new QVariantAnimation(this)), \ + _device(parent) \ +{ \ + this->setParent(parent);\ + auto *radius_animation = new QVariantAnimation(this); \ + radius_animation->setDuration(radiusAniTime); \ + radius_animation->setStartValue(0); \ + radius_animation->setEndValue(aniRadiusEnd); \ + _tick_animation_1->setDuration(p1AniTime); \ + _tick_animation_1->setStartValue(_p1); \ + _tick_animation_1->setEndValue(_p1); \ + _tick_animation_2->setDuration(p2AniTime); \ + _tick_animation_2->setStartValue(_p1); \ + _tick_animation_2->setEndValue(QPointF(p3x, p3y)); \ + auto *sequential_animation_group = new QSequentialAnimationGroup(this); \ + auto *parallel_animation_group = new QParallelAnimationGroup(this); \ + sequential_animation_group->addAnimation(_tick_animation_1); \ + sequential_animation_group->addAnimation(_tick_animation_2); \ + parallel_animation_group->addAnimation(radius_animation); \ + parallel_animation_group->addAnimation(sequential_animation_group); \ + auto reverseDirectionAndStart = [parallel_animation_group, this](bool checked) { \ + if (checked) \ + { \ + _tick_animation_1->setDuration(p1AniTime); \ + _tick_animation_2->setDuration(p2AniTime); \ + } \ + else \ + { \ + _tick_animation_1->setDuration(0); \ + _tick_animation_2->setDuration(0); \ + } \ + parallel_animation_group->setDirection(static_cast(!checked)); \ + parallel_animation_group->start(); \ + }; \ + connect(radius_animation, &QVariantAnimation::valueChanged, this, &CLASS::setCircumRadius); \ + connect(_tick_animation_1, &QVariantAnimation::valueChanged, this, &CLASS::setTickP1); \ + connect(_tick_animation_2, &QVariantAnimation::valueChanged, this, &CLASS::setTickP2); \ + connect(parent, &QCheckBox::toggled, radius_animation, reverseDirectionAndStart); \ + connect(&Palette::palette(), &Palette::appThemeChange, parent, QOverload<>::of(&QCheckBox::update)); \ + connect(&Palette::palette(), &Palette::appColorChange, parent, QOverload<>::of(&QCheckBox::update)); \ +} +#define CHECKBOX_STYLE_drawControl \ + if (element == CE_CheckBox) \ + { \ + const auto bopt = qstyleoption_cast(option); \ + if (!bopt) \ + return; \ + constexpr int RADIUS = 2; \ + const QRect &rect = bopt->rect; \ + const int adjust_Y = (rect.height() - _checkbox_side) / 2; \ + QRectF indicatorRect(rect.x(), rect.y() + adjust_Y, _checkbox_side, _checkbox_side); \ + indicatorRect.adjust(0.5, 0.5, -0.5, -0.5); \ + const QPointF center = indicatorRect.center() ;\ + const ControlState state(bopt->state, ControlType::IndicatorCheckable); \ + Palette &p = Palette::palette(); \ + QPen pen; \ + if (_flag) \ + { \ + _p1.setY(_p1.y() + adjust_Y); \ + _p2.setY(_p2.y() + adjust_Y); \ + _tick_p1 = _p1; \ + _tick_p2 = _p2; \ + _tick_animation_1->setStartValue(_p1); \ + _tick_animation_1->setEndValue(_p2); \ + _tick_animation_2->setStartValue(_p2); \ + _tick_animation_2->setEndValue(QPointF(0.86375 * _checkbox_side, 0.26875 * _checkbox_side + adjust_Y)); \ + _flag = false; \ + } \ + painter->save(); \ + painter->setRenderHint(QPainter::Antialiasing, true); \ + switch (state) \ + { \ + case VisualState::Normal : \ + pen.setColor(p[ColorRole::Border]); \ + break; \ + case VisualState::Pressed : \ + pen.setColor(p[ColorRole::AppAccent]); \ + pen.setWidth(2); \ + indicatorRect.adjust(0.5, 0.5, -0.5, -0.5); \ + break; \ + case VisualState::Hover : [[fallthrough]]; \ + case VisualState::Selected : [[fallthrough]]; \ + case VisualState::SelectedHover : [[fallthrough]]; \ + case VisualState::SelectedPressed : \ + pen.setColor(p[ColorRole::AppAccent]); \ + break; \ + case VisualState::Disabled : [[fallthrough]]; \ + case VisualState::SelectedDisabled : \ + pen.setColor(p[ColorRole::DisEnabled]); \ + break; \ + default : \ + return; \ + } \ + painter->setPen(pen); \ + painter->drawRoundedRect(indicatorRect, RADIUS, RADIUS); \ + QPainterPath clipPath; \ + clipPath.addRoundedRect(indicatorRect, RADIUS, RADIUS); \ + painter->setClipPath(clipPath); \ + painter->setBrush(p[ColorRole::AppAccent]); \ + if (!state._enable) \ + painter->setBrush(p[ColorRole::DisEnabled]); \ + painter->setPen(Qt::NoPen); \ + painter->drawEllipse(center, _circum_radius, _circum_radius); \ + if (state._on && _p1 != _tick_p1) \ + { \ + state._enable ? pen.setColor(p[ColorRole::AppAccentText]) : pen.setColor(Qt::GlobalColor::white); \ + pen.setWidthF(1.5); \ + if (state._over) \ + pen.setWidth(2); \ + if (state == VisualState::SelectedPressed) \ + pen.setWidth(1); \ + painter->setPen(pen); \ + painter->drawLine(_p1, _tick_p1); \ + if (_p2 != _tick_p2) \ + painter->drawLine(_p2, _tick_p2); \ + } \ + pen.setColor(p[state._enable ? ColorRole::Text : ColorRole::DisEnabledText]); \ + pen.setWidth(0); \ + painter->setPen(pen); \ + painter->setClipping(false); \ + const QRect textRect(rect.x() + _checkbox_side + 8, rect.y(), rect.width(), rect.height()); \ + painter->drawText(textRect, Qt::AlignmentFlag::AlignLeft | Qt::AlignmentFlag::AlignVCenter, bopt->text); \ + painter->restore(); \ + return; \ + } \ + QProxyStyle::drawControl(element, option, painter, widget); +#define CHECKBOX_STYLE_pixelMetric(indicatorSide) \ +switch (metric) \ +{ \ + case PM_CheckBoxLabelSpacing : \ + return 10; /* 实际使用8px,预留2px防止文字绘制超出边界*/ \ + case PM_IndicatorWidth : [[fallthrough]]; \ + case PM_IndicatorHeight : \ + return indicatorSide; \ + default : \ + break; \ +} \ +return QProxyStyle::pixelMetric(metric, option, widget); + +namespace fancy +{ + CheckBoxStyle::CheckBoxStyle(QCheckBox *parent) : + CHECKBOX_STYLE_INIT(CheckBoxStyle, 21, 5.01375, 12.20625, 8.95125, 16.8, 18.13875, 5.64375, 150, 130, 260, 15) + + void CheckBoxStyle::drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const + { + CHECKBOX_STYLE_drawControl + } + + int CheckBoxStyle::pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const + { + CHECKBOX_STYLE_pixelMetric(21) + } + + CheckBoxStyle_SM::CheckBoxStyle_SM(QCheckBox *parent) : + CHECKBOX_STYLE_INIT(CheckBoxStyle_SM, 16, 3.82, 9.3, 6.82, 12.8, 13.82, 4.3, 115, 100, 200, 12) + + void CheckBoxStyle_SM::drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const + { + CHECKBOX_STYLE_drawControl + } + + int CheckBoxStyle_SM::pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const + { + CHECKBOX_STYLE_pixelMetric(16) + } +} // fancy diff --git a/src/Fancy/Widgets/CheckBox/CheckBoxStyle.h b/src/Fancy/CheckBoxStyle.h similarity index 76% rename from src/Fancy/Widgets/CheckBox/CheckBoxStyle.h rename to src/Fancy/CheckBoxStyle.h index db95d70d..324ae6ee 100644 --- a/src/Fancy/Widgets/CheckBox/CheckBoxStyle.h +++ b/src/Fancy/CheckBoxStyle.h @@ -10,25 +10,18 @@ #include #include #include -#include #define CHECKBOX_STYLE_PRIVATE_DATA \ private:\ - bool _animation_enabled = true; \ - qreal _fill_radius; \ - int _checkbox_side; \ - QPointF _p1; \ - QPointF _p2; \ - qreal _circum_radius; \ - QPointF _tick_p1; \ - QPointF _tick_p2; \ - qreal _partial_progress; \ + mutable int _checkbox_side; \ + mutable bool _flag; \ + mutable QPointF _p1; \ + mutable QPointF _p2; \ + int _circum_radius; \ + mutable QPointF _tick_p1; \ + mutable QPointF _tick_p2; \ QVariantAnimation *_tick_animation_1; \ QVariantAnimation *_tick_animation_2; \ - QVariantAnimation *_partial_animation; \ - QVariantAnimation *_radius_animation; \ - QSequentialAnimationGroup *_tick_animation_group; \ - void syncCheckState(); \ QCheckBox* _device; #define CHECKBOX_STYLE_SET_TICK_P1 \ @@ -50,8 +43,6 @@ public: \ explicit Class(QCheckBox *parent); \ void drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const override; \ int pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const override; \ - void setAnimationEnabled(bool enabled); \ - bool isAnimationEnabled() const noexcept { return _animation_enabled; } \ int indicatorSide() const noexcept { return _checkbox_side; } \ CHECKBOX_STYLE_PRIVATE_SLOTS \ CHECKBOX_STYLE_PRIVATE_DATA diff --git a/src/Fancy/Widgets/TitleBar/Button/CloseButton.cpp b/src/Fancy/CloseButton.cpp similarity index 92% rename from src/Fancy/Widgets/TitleBar/Button/CloseButton.cpp rename to src/Fancy/CloseButton.cpp index dab40884..7ed7a87f 100644 --- a/src/Fancy/Widgets/TitleBar/Button/CloseButton.cpp +++ b/src/Fancy/CloseButton.cpp @@ -5,12 +5,11 @@ #include "CloseButton.h" #include -#include -#include "Core/Defs.h" -#include "Core/Palette/Palette.h" -#include "Icon/IconEngine.h" -#include "Widgets/SvgWidget/SvgWidget.h" +#include "Defs.h" +#include "IconEngine.h" +#include "Palette.h" +#include "SvgWidget.h" namespace fancy { @@ -40,11 +39,7 @@ namespace fancy } -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) void CloseButton::enterEvent(QEnterEvent *event) -#else - void CloseButton::enterEvent(QEvent *event) -#endif { svgWidget()->cache().first = IconEngine::singleRender(AntDesignIcons::Close, Qt::GlobalColor::white); svgWidget()->load(); diff --git a/src/Fancy/Widgets/TitleBar/Button/CloseButton.h b/src/Fancy/CloseButton.h similarity index 87% rename from src/Fancy/Widgets/TitleBar/Button/CloseButton.h rename to src/Fancy/CloseButton.h index a08dcad9..15a3edee 100644 --- a/src/Fancy/Widgets/TitleBar/Button/CloseButton.h +++ b/src/Fancy/CloseButton.h @@ -24,11 +24,7 @@ namespace fancy protected: [[nodiscard]] QBrush maskBrush() const override; -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) void enterEvent(QEnterEvent *event) override; -#else - void enterEvent(QEvent *event) override; -#endif void leaveEvent(QEvent *event) override; diff --git a/src/Fancy/Core/ControlState/ControlState.cpp b/src/Fancy/ControlState.cpp similarity index 62% rename from src/Fancy/Core/ControlState/ControlState.cpp rename to src/Fancy/ControlState.cpp index 579a6b8d..3f634dde 100644 --- a/src/Fancy/Core/ControlState/ControlState.cpp +++ b/src/Fancy/ControlState.cpp @@ -3,20 +3,19 @@ // #include "ControlState.h" -#include "Core/Defs.h" +#include "Defs.h" namespace fancy { ControlState::ControlState(QStyle::State state, ControlType type) : _state(VisualState::UnKnown) { - _enable = state.testFlag(QStyle::StateFlag::State_Enabled); - _on = state.testFlag(QStyle::StateFlag::State_On); - _off = state.testFlag(QStyle::StateFlag::State_Off); - _partial = state.testFlag(QStyle::StateFlag::State_NoChange); - _over = state.testFlag(QStyle::StateFlag::State_MouseOver); - _sunken = state.testFlag(QStyle::StateFlag::State_Sunken); - _focus = state.testFlag(QStyle::StateFlag::State_HasFocus); + _enable = state & QStyle::StateFlag::State_Enabled; + _on = state & QStyle::StateFlag::State_On; + _off = state & QStyle::StateFlag::State_Off; + _over = state & QStyle::StateFlag::State_MouseOver; + _sunken = state & QStyle::StateFlag::State_Sunken; + _focus = state & QStyle::StateFlag::State_HasFocus; _type = type; _state = visualState(); @@ -27,14 +26,13 @@ namespace fancy switch (_type) { case ControlType::IndicatorCheckable : - case ControlType::WidgetCheckable : { - const bool selected = _on || _partial; if (!_enable) - return selected ? VisualState::SelectedDisabled : VisualState::Disabled; - if (selected) + return _on ? VisualState::SelectedDisabled : VisualState::Disabled; + if (_on) return _over ? (_sunken ? VisualState::SelectedPressed : VisualState::SelectedHover) : VisualState::Selected; - return _over ? (_sunken ? VisualState::Pressed : VisualState::Hover) : VisualState::Normal; + if (_off) + return _over ? (_sunken ? VisualState::Pressed : VisualState::Hover) : VisualState::Normal; } case ControlType::Widget : { @@ -44,6 +42,14 @@ namespace fancy return _sunken ? VisualState::Pressed : VisualState::Hover; return VisualState::Normal; } + case ControlType::WidgetCheckable : + { + if (!_enable) + return _on ? VisualState::SelectedDisabled : VisualState::Disabled; + if (_on) + return _over ? (_sunken ? VisualState::SelectedPressed : VisualState::SelectedHover) : VisualState::Selected; + return _over ? (_sunken ? VisualState::Pressed : VisualState::Hover) : VisualState::Normal; + } case ControlType::TextEdit : { if (!_enable) diff --git a/src/Fancy/Core/ControlState/ControlState.h b/src/Fancy/ControlState.h similarity index 85% rename from src/Fancy/Core/ControlState/ControlState.h rename to src/Fancy/ControlState.h index b2d2025d..c4ff228b 100644 --- a/src/Fancy/Core/ControlState/ControlState.h +++ b/src/Fancy/ControlState.h @@ -32,11 +32,10 @@ namespace fancy VisualState _state; ControlType _type; bool _enable; // 启用 - bool _on; // 全选(State_On) + bool _on; // 选中 bool _off; // 未选中 - bool _partial; // 半选(State_NoChange) bool _over; // 悬浮 - bool _sunken; // 按下或具有凹陷外观 + bool _sunken; // 按下或具有凹陷外观s bool _focus; // 焦点 }; } // fancy diff --git a/src/Fancy/Core/Palette/Palette.cpp b/src/Fancy/Core/Palette/Palette.cpp deleted file mode 100644 index f7fd53d2..00000000 --- a/src/Fancy/Core/Palette/Palette.cpp +++ /dev/null @@ -1,426 +0,0 @@ -// -// Created by TX on 2025/8/8. -// - -#include "Palette.h" - -#include -#include -#include -#include -#include - -#include "utils/BezierEasing.h" -#include "Core/Defs.h" -#include "Core/SystemAccessor.h" -#include "Core/Theme/SystemThemeMonitor.h" -#include "Core/Theme/ThemeModeController.h" - - -namespace fancy -{ - Palette::Palette(QObject *parent) : - QObject(parent) - { - SystemThemeMonitor::monitor().start(); - - // 默认颜色组 - _group_theme = {ColorGroups::System, ThemeModeController::controller().appTheme()}; - loadSchemes(QColor(212,78,125)); - connect(&ThemeModeController::controller(), &ThemeModeController::appThemeChange, this, &Palette::onAppThemeChanged); - connect(&SystemThemeMonitor::monitor(), &SystemThemeMonitor::systemAccentColorsChanged, this, &Palette::onSystemAccentColorsChanged); - } - - Palette &Palette::palette() - { - static auto *p = new Palette(qApp); - return *p; - } - - Palette::~Palette() - { - if (SystemThemeMonitor::monitor().isRunning()) - SystemThemeMonitor::monitor().stop(); - SystemThemeMonitor::monitor().wait(); - } - - const QHash &Palette::colors() - { - return _schemes; - } - - const QColor &Palette::color(const ColorRole role) - { - return _schemes[_group_theme][role]; - } - - const QColor &Palette::color(ColorRole role, Theme theme) - { - return _schemes[{_group_theme.first, theme}][role]; - } - - const QColor &Palette::operator[](const ColorRole role) - { - return _schemes[_group_theme][role]; - } - - void Palette::setColorGroups(ColorGroups group, const QColor &custom) - { - _group_theme = {group, ThemeModeController::controller().appTheme()}; - setCustomColor(custom); - } - - void Palette::setCustomColor(const QColor &color) - { - if (_group_theme.first == ColorGroups::Custom) - { - if (color.isValid()) - loadSchemes(color); - else - loadSchemes(QColor(212, 78, 125)); - emit appColorChange(); - } - } - - QColor Palette::mix(const QColor &c1, const QColor &c2, double amount) - { - amount = qBound(0.0, amount, 1.0); - if (amount == 0.0) - return c1; - if (amount == 1.0) - return c2; - return QColor::fromRgbF( - c1.redF() + (c2.redF() - c1.redF()) * amount, - c1.greenF() + (c2.greenF() - c1.greenF()) * amount, - c1.blueF() + (c2.blueF() - c1.blueF()) * amount, - c1.alphaF() + (c2.alphaF() - c1.alphaF()) * amount - ); - } - - QColor Palette::darken(const QColor &c, double amount) - { - QColor hsl = c.toHsl(); - const double l = qBound(0.0, hsl.lightnessF() - amount, 1.0); - hsl.setHslF(hsl.hueF(), hsl.saturationF(), l, hsl.alphaF()); - return hsl; - } - - QColor Palette::spin(const QColor &c, double amount) - { -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - float h, s, l, alpha; -#else - qreal h, s, l, alpha; -#endif - c.getHslF(&h, &s, &l, &alpha); - h = std::fmod(h * 360.0 + amount, 360.0); - return QColor::fromHslF((h < 0 ? h + 360.0 : h) / 360.0, s, l, alpha); - } - - double Palette::relativeLuminance(const QColor &c, double gamma) - { - return 0.2126729 * gammaDecode(c.redF(), gamma) + - 0.7151522 * gammaDecode(c.greenF(), gamma) + - 0.0721750 * gammaDecode(c.blueF(), gamma); - } - - double Palette::wcag2Contrast(const QColor &c1, const QColor &c2, double gamma) - { - double l1 = relativeLuminance(c1, gamma); - double l2 = relativeLuminance(c2, gamma); - return l1 == l2 ? 1.0 : l2 < l1 ? (l1 + 0.05) / (l2 + 0.05) : (l2 + 0.05) / (l1 + 0.05); - } - - int Palette::apcaContrast(const QColor &bg, const QColor &text) - { - constexpr double mainTRC = 2.4; // sRGB 传输曲线指数 - constexpr double normBG = 0.56; // 正常极性:背景幂次 - constexpr double normTXT = 0.57; // 正常极性:文字幂次 - constexpr double revTXT = 0.62; // 反转极性:文字幂次 - constexpr double revBG = 0.65; // 反转极性:背景幂次 - constexpr double blkThrs = 0.022; // 黑位阈值(soft clamp 起始) - constexpr double blkClmp = 1.414; // 黑位夹钳指数 - constexpr double scaleBoW = 1.14; // 正常极性缩放 - constexpr double scaleWoB = 1.14; // 反转极性缩放 - constexpr double deltaYmin = 0.0005; // 极小差异阈值(避免除零/噪声) - constexpr double Wclamp = 0.1; - constexpr double Woffset = 0.027; // 反转极性:偏置 - - auto calcAPCAcontrast = [&](const QColor &textColor, const QColor &bgColor)-> int { - auto softclp = [&](double Yc)-> double { - if (Yc < 0) - return 0; - return Yc <= blkThrs ? Yc + std::pow((blkThrs - Yc), blkClmp) : Yc; - }; - - auto SAPC = [&](double Ybg, double Ytxt)-> double { - if (Ybg > Ytxt) - return (std::pow(Ybg, normBG) - std::pow(Ytxt, normTXT)) * scaleBoW; - return (std::pow(Ybg, revBG) - std::pow(Ytxt, revTXT)) * scaleWoB; - }; - - auto Lc = [&](double sapc)-> int { - if (qFabs(sapc) < Wclamp) - return 0.0; - return static_cast(std::lround(sapc > 0.0 ? (sapc - Woffset) * 100.0 : (sapc + Woffset) * 100.0)); - }; - - double Ytxt = softclp(relativeLuminance(textColor, mainTRC)); - double Ybg = softclp(relativeLuminance(bgColor, mainTRC)); - if (qFabs(Ybg - Ytxt) < deltaYmin) - return 0; - return Lc(SAPC(Ybg, Ytxt)); - }; - - return calcAPCAcontrast(text, bg); - } - - QColor Palette::apcaTextColor(const QColor &bg) - { - int LcBlack = apcaContrast(Qt::GlobalColor::black, bg); - int LcWhite = apcaContrast(Qt::GlobalColor::white, bg); - return qAbs(LcBlack) > qAbs(LcWhite) ? Qt::GlobalColor::black : Qt::GlobalColor::white; - } - - QColor Palette::colorPalette(const QColor &c, int index, int max) - { - constexpr double warmDark = 0.5; // 暖色变暗系数 - constexpr double warmRotate = -26.0; // 暖色旋转角度 - constexpr double coldDark = 0.55; // 冷色变暗系数 - constexpr double coldRotate = 10.0; // 冷色旋转角度 - - // 贝塞尔曲线控制点坐标 - constexpr double x1 = 0.26; - constexpr double x2 = 0.09; - constexpr double y1 = 0.37; - constexpr double y2 = 0.18; - - static BezierEasing colorEasing(x1, y1, x2, y2); - const int midIndex = max / 2 + 1; - - // 根据颜色特性生成的阴影颜色 - auto getShadeColor = [&](const QColor &ori)-> QColor { - // 暖色 - if (ori.redF() > ori.blueF()) - return spin(darken(ori, ori.lightnessF() * warmDark), warmRotate); - // 冷色 - return spin(darken(ori, ori.lightnessF() * coldDark), coldRotate); - }; - - const double currentEasing = colorEasing(static_cast(index) * (1.0 / static_cast(max))); - const double primaryEasing = colorEasing(static_cast(midIndex) / static_cast(max)); // 分界点 - - // 较浅的色调(与白色混合) - if (index <= midIndex) - return mix(Qt::GlobalColor::white, c, currentEasing / primaryEasing); - - // 较深的色调(与阴影色混合) - return mix(getShadeColor(c), c, 1 - (currentEasing - primaryEasing) / (1 - primaryEasing)); - } - - QList Palette::antDesign2ColorPalettes(const QColor &base) - { - QList colors; - for (int index = 1; index <= 10; index++) - colors.append(colorPalette(base, index, 10)); - return colors; - } - - void Palette::loadSchemes(const QColor &custom) - { - const QJsonObject group_obj = loadColorProfile(_group_theme.first); - static const QStringList theme_names = {"Light", "Dark"}; // 预定义的主题名称列表 - for (const QString &theme_name: theme_names) // 遍历每个主题 - { - // ------------------------------------------------------------ - // 1. 将主题名称字符串(如 "Light")转换为枚举值 Theme - // magic_enum::enum_cast 返回 std::optional - // ------------------------------------------------------------ - auto theme_enum = magic_enum::enum_cast(theme_name.toStdString()); - // 如果枚举转换失败(无效名称),或 group_obj 中不存在该主题键,则跳过 - if (!theme_enum.has_value() || !group_obj.contains(theme_name)) - continue; - - // 获取当前主题下的 json 对象(包含多个 ColorRole 的键值对) - const QJsonObject theme_obj = group_obj[theme_name].toObject(); - - // 获取该颜色组 + 主题对应的方案存储引用 - ColorsHash &scheme = _schemes[{_group_theme.first, theme_enum.value()}]; - - // ------------------------------------------------------------ - // 2. 记录需要延迟计算的 ColorRole,以 "$calc:" 开头(用户自定义颜色) - // 这些颜色依赖其他已解析的颜色值 - // ------------------------------------------------------------ - QList deferred_roles; - - // 第1次遍历,解析直接颜色值 - for (auto it = theme_obj.begin(); it != theme_obj.end(); ++it) - { - // 将 json 键转换为 ColorRole 枚举 - auto role_enum = magic_enum::enum_cast(it.key().toStdString()); - - // 枚举转换失败,说明 json 中有未知的角色名,记录警告并跳过 - if (!role_enum.has_value()) - { - qWarning() << "Palette: Unknown ColorRole in JSON:" << it.key(); - continue; - } - const QString raw = it.value().toString(); // 获取原始颜色值字符串 - - // 如果值以 "$calc:" 开头,说明需要运行时计算,先加入延迟列表,第二轮处理 - if (raw.startsWith("$calc:")) - { - deferred_roles.append(role_enum.value()); - continue; - } - scheme[role_enum.value()] = parseColorFromJsonStr(custom, raw); // 解析颜色,并存储 - } - - // 第2次遍遍历:处理所有延迟计算的颜色 - for (const ColorRole &role: deferred_roles) - { - switch (role) - { - case ColorRole::AppAccentText : - if (scheme.contains(ColorRole::AppAccent)) // AppAccentText 需要依赖已经解析好的 AppAccent 颜色 - scheme[role] = apcaTextColor(scheme[ColorRole::AppAccent]); - else - qWarning() << "Palette: AppAccent not resolved before AppAccentText"; // 如果 AppAccent 未被解析,无法计算,输出警告 - break; - default : // 其他 $calc 角色暂不支持,输出警告并显示角色名 - qWarning() << "Palette: Unsupported $calc role:" << magic_enum::enum_name(role).data(); - break; - } - } - } - } - - QJsonObject Palette::loadColorProfile(ColorGroups group) - { - QFile file(":/Profiles/Palette/ColorProfile.json"); - if (!file.open(QFile::ReadOnly)) - { - qCritical() << "Failed to open color profile file:" << file.errorString(); - return QJsonObject(); // 错误:返回空对象 - } - - // 解析 json - QJsonParseError parse_error; - const QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &parse_error); - file.close(); - if (parse_error.error != QJsonParseError::NoError) - { - qCritical() << "JSON parse error in color profile:" << parse_error.errorString() << "at offset" << parse_error.offset; - return QJsonObject(); - } - if (!doc.isObject()) - { - qCritical() << "Color profile JSON root is not an object"; - return QJsonObject(); - } - const QJsonObject root = doc.object(); - - // 从颜色组枚举获取 json 对象 - const auto group_name_view = magic_enum::enum_name(group); - if (group_name_view.empty()) - { - qCritical() << "Invalid enum value for group theme:" << static_cast>(group); - return QJsonObject(); - } - const QString group_name = QString::fromUtf8(group_name_view.data(), static_cast(group_name_view.size())); - - if (!root.contains(group_name)) // 从根对象中提取对应的组对象 - { - qWarning() << "Group" << group_name << "not found in color profile, using empty object"; - return QJsonObject(); - } - return root[group_name].toObject(); // 成功返回 - } - - QColor Palette::parseColorFromJsonStr(const QColor &custom, const QString &raw) - { - const QMap sys_colors = SystemAccessor::sysAccentColors(); - - // 套用 SysAccentPalette 枚举(含义一样),存储自定义颜色及其衍生色 - QMap cus_colors; - QList custom_colors = antDesign2ColorPalettes(custom); - for (int i = 0; i < 7; i++) // 只取7个颜色 - cus_colors[static_cast(i)] = custom_colors[i + 2]; - - // 十六进制颜色,以 # 开头,去掉开头 #,保留后面的十六进制字符串 - if (const QString hex = raw.mid(1); raw.startsWith("#")) - { - bool ok = false; // 用于判断字符串转整数是否成功 - - // 从 hex 中提取 R, G, B 分量,每分量占 2 位十六进制 - const int r = hex.mid(0, 2).toInt(&ok, 16); // 从位置0开始取2个字符转,由16进制转10进制整数 - const int g = hex.mid(2, 2).toInt(&ok, 16); // 从位置2开始 - const int b = hex.mid(4, 2).toInt(&ok, 16); // 从位置4开始 - if (const int a = hex.mid(6, 2).toInt(&ok, 16); hex.length() == 8) // hex 长度为 8,包含 Alpha 通道 - return QColor(r, g, b, a); - return QColor(r, g, b); - } - - // 系统调色板引用,以 "$sys:" 开头 - if (raw.startsWith("$sys:")) - { - // 提取枚举字符串并转换,从系统颜色映射表中取出对应颜色 - if (const auto accent = magic_enum::enum_cast(raw.mid(5).toStdString()); accent.has_value()) - return sys_colors.value(accent.value()); - qWarning() << "Palette: Unknown $sys key:" << raw; - return QColor(); - } - - // 用户自定义调色板引用,以 "$cus:" 开头 - if (raw.startsWith(QLatin1String("$cus:"))) - { - // 提取枚举名并转换,从用户颜色映射表取值 - if (const auto accent = magic_enum::enum_cast(raw.mid(5).toStdString()); accent.has_value()) - return cus_colors.value(accent.value()); - qWarning() << "Palette: Unknown $cus key:" << raw; - return QColor(); - } - - // 所有格式都不匹配 - qWarning() << "Palette: Unrecognized value format:" << raw; - return QColor(); - } - - void Palette::onAppThemeChanged(Theme theme) - { - _group_theme = {_group_theme.first, theme}; - emit appThemeChange(); - } - - void Palette::onSystemAccentColorsChanged(QMap colors) - { - const auto froup = _group_theme.first; - if (froup == ColorGroups::System) - { - ColorsHash &sys_light_scheme = _schemes[{ColorGroups::System, Theme::Light}]; - ColorsHash &sys_dark_scheme = _schemes[{ColorGroups::System, Theme::Dark}]; - - sys_light_scheme[ColorRole::Link] = colors[SysAccentPalette::AccentLight2]; - sys_light_scheme[ColorRole::ImportantLink] = colors[SysAccentPalette::AccentDark2]; - sys_light_scheme[ColorRole::AppAccent] = colors[SysAccentPalette::AccentDark1]; - sys_light_scheme[ColorRole::AppAccentText] = apcaTextColor(sys_light_scheme[ColorRole::AppAccent]); - sys_light_scheme[ColorRole::SysAccent] = colors[SysAccentPalette::Accent]; - - sys_dark_scheme[ColorRole::Link] = colors[SysAccentPalette::AccentLight3]; - sys_dark_scheme[ColorRole::ImportantLink] = colors[SysAccentPalette::AccentLight3]; - sys_dark_scheme[ColorRole::AppAccent] = colors[SysAccentPalette::AccentLight2]; - sys_dark_scheme[ColorRole::AppAccentText] = apcaTextColor(sys_dark_scheme[ColorRole::AppAccent]); - sys_dark_scheme[ColorRole::SysAccent] = colors[SysAccentPalette::Accent]; - } - else if (froup == ColorGroups::Default) - { - ColorsHash &def_light_scheme = _schemes[{ColorGroups::Default, Theme::Light}]; - ColorsHash &def_dark_scheme = _schemes[{ColorGroups::Default, Theme::Dark}]; - - def_light_scheme[ColorRole::SysAccent] = colors[SysAccentPalette::Accent]; - def_dark_scheme[ColorRole::SysAccent] = colors[SysAccentPalette::Accent]; - } - if (froup != ColorGroups::Custom) - emit appColorChange(); - } -} // fancy diff --git a/src/Fancy/Core/Defs.h b/src/Fancy/Defs.h similarity index 72% rename from src/Fancy/Core/Defs.h rename to src/Fancy/Defs.h index 0938ce4a..5949a96d 100644 --- a/src/Fancy/Core/Defs.h +++ b/src/Fancy/Defs.h @@ -1,23 +1,26 @@ -// +// // Created by TX on 2025/8/8. // #ifndef QWIDGET_FANCYUI_ENUM_H #define QWIDGET_FANCYUI_ENUM_H +#include +#include +#include +#include #include -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +#include +#include +#include +#include #include -#else -#include -#endif - #include #define MAGIC_ENUM_RANGE_MIN 0 #define MAGIC_ENUM_RANGE_MAX 3000 #include -#include "Icon/IconEnums.h" +#include "IconEnums.h" #define ABGR(a, b, g, r) ((COLORREF)(((a & 0xffu) << 24) | ((b & 0xffu) << 16) | ((g & 0xffu) << 8) | (r & 0xffu))) @@ -93,12 +96,6 @@ namespace fancy WindowBright, // 窗口高度对比色(关闭按钮悬浮时基础色) Border, // 通用边框色 Text, // 通用文本色 - SelectorBarItemTextHover, // SelectorBarItem 项目悬停时的文本色 - SelectorBarItemTextPressed, // SelectorBarItem 项目按下时的文本色 - SelectorBarItemTextSelected, // SelectorBarItem 项目选中时的文本色 - SelectorBarItemTextSelectedHover, // SelectorBarItem 项目选中悬停时的文本色 - SelectorBarItemTextSelectedPressed, // SelectorBarItem 项目选中按下时的文本色 - SelectorBarItemBackground, // SelectorBarItem 统一背景色,默认透明,不区分交互和禁用状态 Button, // 按钮背景色 ButtonBorder, // 按钮边框色 ButtonBottomLine, // 按钮底线色 @@ -347,13 +344,13 @@ namespace fancy quint64 value; // default - IconId() - : style(IconStyle::AntDesignIcons), - value(0) {} + IconId() : + style(IconStyle::AntDesignIcons), + value(0) {} - IconId(const IconStyle style, const quint64 value) - : style(style), - value(value) {} + IconId(const IconStyle style, const quint64 value) : + style(style), + value(value) {} bool operator==(const IconId &other) const { @@ -361,9 +358,133 @@ namespace fancy } }; + inline size_t qHash(const IconId &id, size_t seed = 0) { return ::qHash(id.style, seed) ^ ::qHash(id.value); } + #define ICONID(ICON_ENUM) inline IconId iconId(ICON_ENUM em) { return {IconStyle::ICON_ENUM, static_cast(em)}; } ICONID(AntDesignIcons) ICONID(BootstrapIcons) ICONID(IconPark) + + inline QLine bottomLine(const QRect &rect, int filletRadius) + { + if (int x = rect.center().x(); rect.width() - 2 * filletRadius <= 0) + return {x, rect.y() + rect.height(), x, rect.y() + rect.height()}; + return {rect.x() + filletRadius, rect.y() + rect.height(), rect.x() + rect.width() - filletRadius, rect.y() + rect.height()}; + } + + inline QImage renderWidgetRegion(QWidget *widget, const QRect ®ion) + { + if (!widget) + return {}; + // const qreal dpr = widget->devicePixelRatioF(); + QImage image(region.size() /* *dpr */, QImage::Format_ARGB32_Premultiplied); + // image.setDevicePixelRatio(dpr); + image.fill(Qt::GlobalColor::black); + if (const QRect intersected = region.intersected({{0, 0}, widget->size()}); !intersected.isEmpty()) + widget->render(&image, QPoint(0, 0), intersected, QWidget::RenderFlag::DrawWindowBackground); + return image; + } + + inline QRect getWidgetRectInAncestor(const QWidget *child, const QWidget *ancestor) + { + if (!child || !ancestor) + return {}; + return {child->mapTo(ancestor, QPoint{0, 0}), child->mapTo(ancestor, QPoint{child->width(), child->height()})}; + } + + inline QImage gaussianBlur(const QImage &src, int radius) + { + if (radius <= 0 || src.isNull()) + return src; + + const int width = src.width(); + const int height = src.height(); + const int kernelSize = 2 * radius + 1; + const qreal sigma = 0.3 * ((kernelSize - 1) * 0.5 - 1) + 0.8; + + // 生成高斯核 + QVector kernel(kernelSize); + qreal sum = 0.0; + for (int i = 0; i < kernelSize; ++i) + { + const qreal x = i - radius; + const qreal g = qExp(-(x * x) / (2.0 * sigma * sigma)); + kernel[i] = g; + sum += g; + } + for (auto &v: kernel) + v /= sum; + + QImage temp(src.size(), QImage::Format_ARGB32); + QImage dst(src.size(), QImage::Format_ARGB32); + + const int bytesPerLine = static_cast(src.bytesPerLine()); + const uchar *srcBits = src.constBits(); + uchar *tempBits = temp.bits(); + uchar *dstBits = dst.bits(); + + // -------- 横向模糊 -------- + for (int y = 0; y < height; ++y) + { + const uchar *srcLine = srcBits + y * bytesPerLine; + uchar *tempLine = tempBits + y * bytesPerLine; + + for (int x = 0; x < width; ++x) + { + double rSum = 0, gSum = 0, bSum = 0, aSum = 0; + + for (int k = -radius; k <= radius; ++k) + { + const int px = std::clamp(x + k, 0, width - 1); + const uchar *p = srcLine + px * 4; // RGBA + const double w = kernel[k + radius]; + + bSum += p[0] * w; + gSum += p[1] * w; + rSum += p[2] * w; + aSum += p[3] * w; + } + + uchar *d = tempLine + x * 4; + d[0] = static_cast(std::lround(bSum)); + d[1] = static_cast(std::lround(gSum)); + d[2] = static_cast(std::lround(rSum)); + d[3] = static_cast(std::lround(aSum)); + } + } + + // -------- 纵向模糊 -------- + for (int y = 0; y < height; ++y) + { + uchar *dstLine = dstBits + y * bytesPerLine; + + for (int x = 0; x < width; ++x) + { + double rSum = 0, gSum = 0, bSum = 0, aSum = 0; + + for (int k = -radius; k <= radius; ++k) + { + const int py = std::clamp(y + k, 0, height - 1); + const uchar *p = tempBits + py * bytesPerLine + x * 4; + const double w = kernel[k + radius]; + + bSum += p[0] * w; + gSum += p[1] * w; + rSum += p[2] * w; + aSum += p[3] * w; + } + + uchar *d = dstLine + x * 4; + d[0] = static_cast(std::lround(bSum)); + d[1] = static_cast(std::lround(gSum)); + d[2] = static_cast(std::lround(rSum)); + d[3] = static_cast(std::lround(aSum)); + } + } + + return dst; + } } + +inline size_t qHash(const QColor &color, size_t seed = 0) { return qHash(color.rgba(), seed); } #endif //QWIDGET_FANCYUI_ENUM_H diff --git a/src/Fancy/Layout/FlowLayout.cpp b/src/Fancy/FlowLayout.cpp similarity index 100% rename from src/Fancy/Layout/FlowLayout.cpp rename to src/Fancy/FlowLayout.cpp diff --git a/src/Fancy/Layout/FlowLayout.h b/src/Fancy/FlowLayout.h similarity index 100% rename from src/Fancy/Layout/FlowLayout.h rename to src/Fancy/FlowLayout.h diff --git a/src/Fancy/Icon/Icon.cpp b/src/Fancy/Icon.cpp similarity index 94% rename from src/Fancy/Icon/Icon.cpp rename to src/Fancy/Icon.cpp index 04cfd080..d0757760 100644 --- a/src/Fancy/Icon/Icon.cpp +++ b/src/Fancy/Icon.cpp @@ -84,13 +84,7 @@ namespace fancy QPixmap Icon::pixmap(const QSize &size, qreal devicePixelRatio, QIcon::Mode mode, QIcon::State state) const { -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) return _icon->pixmap(size, devicePixelRatio, mode, state); -#else - QPixmap pix = _engine->pixmap(size * devicePixelRatio, mode, state); - pix.setDevicePixelRatio(devicePixelRatio); - return pix; -#endif } QSize Icon::actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state) const diff --git a/src/Fancy/Icon/Icon.h b/src/Fancy/Icon.h similarity index 97% rename from src/Fancy/Icon/Icon.h rename to src/Fancy/Icon.h index bda67897..bd6393cd 100644 --- a/src/Fancy/Icon/Icon.h +++ b/src/Fancy/Icon.h @@ -5,9 +5,11 @@ #ifndef QWIDGET_FANCYUI_ICON_H #define QWIDGET_FANCYUI_ICON_H #include +#include +#include "ControlState.h" +#include "Defs.h" #include "IconEngine.h" -#include "Core/Defs.h" namespace fancy { diff --git a/src/Fancy/Icon/IconEngine.cpp b/src/Fancy/IconEngine.cpp similarity index 97% rename from src/Fancy/Icon/IconEngine.cpp rename to src/Fancy/IconEngine.cpp index c8785d1b..7625da31 100644 --- a/src/Fancy/Icon/IconEngine.cpp +++ b/src/Fancy/IconEngine.cpp @@ -5,9 +5,8 @@ #include "IconEngine.h" #include -#include -#include "Core/Palette/Palette.h" +#include "Palette.h" namespace fancy { @@ -56,20 +55,26 @@ namespace fancy if (const QString style_name(magic_enum::enum_name(style).data()); style_name == "AntDesignIcons") { for (const auto em: magic_enum::enum_values()) + { cache[{IconStyle::AntDesignIcons, static_cast(em)}] = new QSvgRenderer(singleRender(em, color), qApp); - return cache; + return cache; + } } else if (style_name == "BootstrapIcons") { for (const auto em: magic_enum::enum_values()) + { cache[{IconStyle::BootstrapIcons, static_cast(em)}] = new QSvgRenderer(singleRender(em, color), qApp); - return cache; + return cache; + } } else if (style_name == "IconPark") { - for (const auto em: magic_enum::enum_values()) - cache[{IconStyle::IconPark, static_cast(em)}] = new QSvgRenderer(singleRender(em, color), qApp); - return cache; + for (const auto em: magic_enum::enum_values()) + { + cache[{IconStyle::IconPark, static_cast(em)}] = new QSvgRenderer(singleRender(em, color), qApp); + return cache; + } } return cache; } diff --git a/src/Fancy/Icon/IconEngine.h b/src/Fancy/IconEngine.h similarity index 97% rename from src/Fancy/Icon/IconEngine.h rename to src/Fancy/IconEngine.h index eb78bdd3..a94a28f8 100644 --- a/src/Fancy/Icon/IconEngine.h +++ b/src/Fancy/IconEngine.h @@ -12,11 +12,14 @@ #include #include #include - +#include "Defs.h" #include "IconEnums.h" -#include "Core/Defs.h" -#include "utils/SvgParsing.h" -#include "utils/hash.h" +#include "SvgParsing.h" +#define MAGIC_ENUM_RANGE_MIN 0 +#define MAGIC_ENUM_RANGE_MAX 3000 +#include + + namespace fancy @@ -110,11 +113,7 @@ namespace fancy void stateOnUseAccentTextColor(bool use); -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) bool isNull() override; -#else - bool isNull(); -#endif IconEngine(); diff --git a/src/Fancy/Icon/IconEnums.h b/src/Fancy/IconEnums.h similarity index 99% rename from src/Fancy/Icon/IconEnums.h rename to src/Fancy/IconEnums.h index 8f334dfa..d9097b92 100644 --- a/src/Fancy/Icon/IconEnums.h +++ b/src/Fancy/IconEnums.h @@ -4,6 +4,7 @@ #ifndef QWIDGET_FANCYUI_ICONENUMS_H #define QWIDGET_FANCYUI_ICONENUMS_H +#include // 此文件中的枚举类由Python脚本自动生成 diff --git a/src/Fancy/Widgets/LineEdit/LineEdit.cpp b/src/Fancy/LineEdit.cpp similarity index 94% rename from src/Fancy/Widgets/LineEdit/LineEdit.cpp rename to src/Fancy/LineEdit.cpp index a6bc630b..58adc9ad 100644 --- a/src/Fancy/Widgets/LineEdit/LineEdit.cpp +++ b/src/Fancy/LineEdit.cpp @@ -4,9 +4,9 @@ #include "LineEdit.h" +#include "Defs.h" #include "LineEditStyle.h" -#include "Core/Defs.h" -#include "Core/Palette/Palette.h" +#include "palette.h" namespace fancy { diff --git a/src/Fancy/Widgets/LineEdit/LineEdit.h b/src/Fancy/LineEdit.h similarity index 100% rename from src/Fancy/Widgets/LineEdit/LineEdit.h rename to src/Fancy/LineEdit.h diff --git a/src/Fancy/Widgets/LineEdit/LineEditStyle.cpp b/src/Fancy/LineEditStyle.cpp similarity index 96% rename from src/Fancy/Widgets/LineEdit/LineEditStyle.cpp rename to src/Fancy/LineEditStyle.cpp index 4a6476f2..279f1a34 100644 --- a/src/Fancy/Widgets/LineEdit/LineEditStyle.cpp +++ b/src/Fancy/LineEditStyle.cpp @@ -6,15 +6,13 @@ #include #include -#include #include #include #include -#include "Core/Defs.h" -#include "Core/ControlState/ControlState.h" -#include "Core/Palette/Palette.h" -#include "utils/General.h" +#include "ControlState.h" +#include "Defs.h" +#include "palette.h" namespace fancy { diff --git a/src/Fancy/Widgets/LineEdit/LineEditStyle.h b/src/Fancy/LineEditStyle.h similarity index 100% rename from src/Fancy/Widgets/LineEdit/LineEditStyle.h rename to src/Fancy/LineEditStyle.h diff --git a/src/Fancy/Widgets/Window/MSWidget.cpp b/src/Fancy/MSWidget.cpp similarity index 93% rename from src/Fancy/Widgets/Window/MSWidget.cpp rename to src/Fancy/MSWidget.cpp index 46de0017..fa206944 100644 --- a/src/Fancy/Widgets/Window/MSWidget.cpp +++ b/src/Fancy/MSWidget.cpp @@ -4,13 +4,12 @@ #include "MSWidget.h" -#include #include #include -#include "Core/Defs.h" -#include "Core/Palette/Palette.h" -#include "Core/Theme/ThemeModeController.h" +#include "Defs.h" +#include "Palette.h" +#include "ThemeModeController.h" namespace fancy { diff --git a/src/Fancy/Widgets/Window/MSWidget.h b/src/Fancy/MSWidget.h similarity index 91% rename from src/Fancy/Widgets/Window/MSWidget.h rename to src/Fancy/MSWidget.h index 3e8dbe63..217454c0 100644 --- a/src/Fancy/Widgets/Window/MSWidget.h +++ b/src/Fancy/MSWidget.h @@ -16,7 +16,7 @@ namespace fancy Q_OBJECT public: - explicit MSWidget(QWidget *parent); + explicit MSWidget(QWidget *parent = nullptr); protected: void paintEvent(QPaintEvent *event) override; diff --git a/src/Fancy/Widgets/Window/MSWindow.cpp b/src/Fancy/MSWindow.cpp similarity index 94% rename from src/Fancy/Widgets/Window/MSWindow.cpp rename to src/Fancy/MSWindow.cpp index 4d7bdc6b..d7653461 100644 --- a/src/Fancy/Widgets/Window/MSWindow.cpp +++ b/src/Fancy/MSWindow.cpp @@ -9,11 +9,11 @@ #include #include -#include "Core/Defs.h" +#include "Defs.h" #include "MSWidget.h" -#include "Widgets/Sidebar/Sidebar.h" -#include "Widgets/Sidebar/SidebarButton.h" -#include "Widgets/TitleBar/TitleBar.h" +#include "Sidebar.h" +#include "SidebarButton.h" +#include "TitleBar.h" namespace fancy { diff --git a/src/Fancy/Widgets/Window/MSWindow.h b/src/Fancy/MSWindow.h similarity index 100% rename from src/Fancy/Widgets/Window/MSWindow.h rename to src/Fancy/MSWindow.h diff --git a/src/Fancy/Widgets/TitleBar/Button/MaximizeButton.cpp b/src/Fancy/MaximizeButton.cpp similarity index 95% rename from src/Fancy/Widgets/TitleBar/Button/MaximizeButton.cpp rename to src/Fancy/MaximizeButton.cpp index 8fd12422..d775be50 100644 --- a/src/Fancy/Widgets/TitleBar/Button/MaximizeButton.cpp +++ b/src/Fancy/MaximizeButton.cpp @@ -4,9 +4,9 @@ #include "MaximizeButton.h" -#include "Core/Defs.h" -#include "Core/Palette/Palette.h" -#include "Widgets/SvgWidget/SvgWidget.h" +#include "Defs.h" +#include "Palette.h" +#include "SvgWidget.h" namespace fancy { diff --git a/src/Fancy/Widgets/TitleBar/Button/MaximizeButton.h b/src/Fancy/MaximizeButton.h similarity index 100% rename from src/Fancy/Widgets/TitleBar/Button/MaximizeButton.h rename to src/Fancy/MaximizeButton.h diff --git a/src/Fancy/Widgets/TitleBar/Button/MinimizeButton.cpp b/src/Fancy/MinimizeButton.cpp similarity index 72% rename from src/Fancy/Widgets/TitleBar/Button/MinimizeButton.cpp rename to src/Fancy/MinimizeButton.cpp index e4c95515..e1bd72e0 100644 --- a/src/Fancy/Widgets/TitleBar/Button/MinimizeButton.cpp +++ b/src/Fancy/MinimizeButton.cpp @@ -4,10 +4,10 @@ #include "MinimizeButton.h" -#include "Core/Defs.h" -#include "Core/Palette/Palette.h" -#include "Icon/IconEngine.h" -#include "Widgets/SvgWidget/SvgWidget.h" +#include "Defs.h" +#include "IconEngine.h" +#include "Palette.h" +#include "SvgWidget.h" namespace fancy { diff --git a/src/Fancy/Widgets/TitleBar/Button/MinimizeButton.h b/src/Fancy/MinimizeButton.h similarity index 100% rename from src/Fancy/Widgets/TitleBar/Button/MinimizeButton.h rename to src/Fancy/MinimizeButton.h diff --git a/src/Fancy/Palette.cpp b/src/Fancy/Palette.cpp new file mode 100644 index 00000000..d55b770f --- /dev/null +++ b/src/Fancy/Palette.cpp @@ -0,0 +1,518 @@ +// +// Created by TX on 2025/8/8. +// + +#include "Palette.h" + +#include + +#include "BezierEasing.h" +#include "Defs.h" +#include "SystemAccessor.h" +#include "SystemThemeMonitor.h" +#include "ThemeModeController.h" + + +namespace fancy +{ + Palette::Palette(QObject *parent) : + QObject(parent) + { + SystemThemeMonitor::monitor().start(); + + // 默认颜色组 + _group_theme = {ColorGroups::System, ThemeModeController::controller().appTheme()}; + loadSchemes(QColor(212, 78, 125)); + + connect(&ThemeModeController::controller(), &ThemeModeController::appThemeChange, this, &Palette::onAppThemeChanged); + connect(&SystemThemeMonitor::monitor(), &SystemThemeMonitor::systemAccentColorsChanged, this, &Palette::onSystemAccentColorsChanged); + } + + Palette &Palette::palette() + { + static auto *p = new Palette(qApp); + return *p; + } + + Palette::~Palette() + { + if (SystemThemeMonitor::monitor().isRunning()) + SystemThemeMonitor::monitor().stop(); + SystemThemeMonitor::monitor().wait(); + } + + const QHash &Palette::colors() + { + return _schemes; + } + + const QColor &Palette::color(const ColorRole role) + { + return _schemes[_group_theme][role]; + } + + const QColor &Palette::color(ColorRole role, Theme theme) + { + return _schemes[{_group_theme.first, theme}][role]; + } + + const QColor &Palette::operator[](const ColorRole role) + { + return _schemes[_group_theme][role]; + } + + void Palette::setColorGroups(ColorGroups group, const QColor &custom) + { + _group_theme = {group, ThemeModeController::controller().appTheme()}; + setCustomColor(custom); + } + + void Palette::setCustomColor(const QColor &color) + { + if (_group_theme.first == ColorGroups::Custom) + { + if (color.isValid()) + loadSchemes(color); + else + loadSchemes(QColor(212, 78, 125)); + emit appColorChange(); + } + } + + QColor Palette::mix(const QColor &c1, const QColor &c2, double amount) + { + amount = qBound(0.0, amount, 1.0); + if (amount == 0.0) + return c1; + if (amount == 1.0) + return c2; + return QColor::fromRgbF( + c1.redF() + (c2.redF() - c1.redF()) * amount, + c1.greenF() + (c2.greenF() - c1.greenF()) * amount, + c1.blueF() + (c2.blueF() - c1.blueF()) * amount, + c1.alphaF() + (c2.alphaF() - c1.alphaF()) * amount + ); + } + + QColor Palette::darken(const QColor &c, double amount) + { + QColor hsl = c.toHsl(); + const double l = qBound(0.0, hsl.lightnessF() - amount, 1.0); + hsl.setHslF(hsl.hueF(), hsl.saturationF(), l, hsl.alphaF()); + return hsl; + } + + QColor Palette::spin(const QColor &c, double amount) + { + float h, s, l, alpha; + c.getHslF(&h, &s, &l, &alpha); + h = std::fmod(h * 360.0 + amount, 360.0); + return QColor::fromHslF((h < 0 ? h + 360.0 : h) / 360.0, s, l, alpha); + } + + double Palette::relativeLuminance(const QColor &c, double gamma) + { + return 0.2126729 * gammaDecode(c.redF(), gamma) + + 0.7151522 * gammaDecode(c.greenF(), gamma) + + 0.0721750 * gammaDecode(c.blueF(), gamma); + } + + double Palette::wcag2Contrast(const QColor &c1, const QColor &c2, double gamma) + { + double l1 = relativeLuminance(c1, gamma); + double l2 = relativeLuminance(c2, gamma); + return l1 == l2 ? 1.0 : l2 < l1 ? (l1 + 0.05) / (l2 + 0.05) : (l2 + 0.05) / (l1 + 0.05); + } + + int Palette::apcaContrast(const QColor &bg, const QColor &text) + { + constexpr double mainTRC = 2.4; // sRGB 传输曲线指数 + constexpr double normBG = 0.56; // 正常极性:背景幂次 + constexpr double normTXT = 0.57; // 正常极性:文字幂次 + constexpr double revTXT = 0.62; // 反转极性:文字幂次 + constexpr double revBG = 0.65; // 反转极性:背景幂次 + constexpr double blkThrs = 0.022; // 黑位阈值(soft clamp 起始) + constexpr double blkClmp = 1.414; // 黑位夹钳指数 + constexpr double scaleBoW = 1.14; // 正常极性缩放 + constexpr double scaleWoB = 1.14; // 反转极性缩放 + constexpr double deltaYmin = 0.0005; // 极小差异阈值(避免除零/噪声) + constexpr double Wclamp = 0.1; + constexpr double Woffset = 0.027; // 反转极性:偏置 + + auto calcAPCAcontrast = [](const QColor &textColor, const QColor &bgColor)-> int { + auto softclp = [](double Yc)-> double { + if (Yc < 0) + return 0; + return Yc <= blkThrs ? Yc + std::pow((blkThrs - Yc), blkClmp) : Yc; + }; + + auto SAPC = [](double Ybg, double Ytxt)-> double { + if (Ybg > Ytxt) + return (std::pow(Ybg, normBG) - std::pow(Ytxt, normTXT)) * scaleBoW; + return (std::pow(Ybg, revBG) - std::pow(Ytxt, revTXT)) * scaleWoB; + }; + + auto Lc = [](double sapc)-> int { + if (qFabs(sapc) < Wclamp) + return 0.0; + return static_cast(std::lround(sapc > 0.0 ? (sapc - Woffset) * 100.0 : (sapc + Woffset) * 100.0)); + }; + + double Ytxt = softclp(relativeLuminance(textColor, mainTRC)); + double Ybg = softclp(relativeLuminance(bgColor, mainTRC)); + if (qFabs(Ybg - Ytxt) < deltaYmin) + return 0; + return Lc(SAPC(Ybg, Ytxt)); + }; + + return calcAPCAcontrast(text, bg); + } + + QColor Palette::apcaTextColor(const QColor &bg) + { + int LcBlack = apcaContrast(Qt::GlobalColor::black, bg); + int LcWhite = apcaContrast(Qt::GlobalColor::white, bg); + return qAbs(LcBlack) > qAbs(LcWhite) ? Qt::GlobalColor::black : Qt::GlobalColor::white; + } + + QColor Palette::colorPalette(const QColor &c, int index, int max) + { + constexpr double warmDark = 0.5; // 暖色变暗系数 + constexpr double warmRotate = -26.0; // 暖色旋转角度 + constexpr double coldDark = 0.55; // 冷色变暗系数 + constexpr double coldRotate = 10.0; // 冷色旋转角度 + + // 贝塞尔曲线控制点坐标 + constexpr double x1 = 0.26; + constexpr double x2 = 0.09; + constexpr double y1 = 0.37; + constexpr double y2 = 0.18; + + static BezierEasing colorEasing(x1, y1, x2, y2); + const int midIndex = max / 2 + 1; + + // 根据颜色特性生成的阴影颜色 + auto getShadeColor = [](const QColor &ori)-> QColor { + // 暖色 + if (ori.redF() > ori.blueF()) + return spin(darken(ori, ori.lightnessF() * warmDark), warmRotate); + // 冷色 + return spin(darken(ori, ori.lightnessF() * coldDark), coldRotate); + }; + + const double currentEasing = colorEasing(static_cast(index) * (1.0 / static_cast(max))); + const double primaryEasing = colorEasing(static_cast(midIndex) / static_cast(max)); // 分界点 + + // 较浅的色调(与白色混合) + if (index <= midIndex) + return mix(Qt::GlobalColor::white, c, currentEasing / primaryEasing); + + // 较深的色调(与阴影色混合) + return mix(getShadeColor(c), c, 1 - (currentEasing - primaryEasing) / (1 - primaryEasing)); + } + + QList Palette::antDesign2ColorPalettes(const QColor &base) + { + QList colors; + for (int index = 1; index <= 10; index++) + colors.append(colorPalette(base, index, 10)); + return colors; + } + + void Palette::loadSchemes(const QColor &custom) + { + QMap sys_colors = SystemAccessor::sysAccentColors(); + + // 套用SysAccentPalette枚举(含义一样),存储自定义颜色及其衍生色 + QMap cus_colors; + QList custom_colors = antDesign2ColorPalettes(custom); + for (int i = 0; i < 7; i++) + cus_colors[static_cast(i)] = custom_colors[i + 2]; + + switch (_group_theme.first) + { + case ColorGroups::System : + { + ColorsHash &sys_light_scheme = _schemes[{ColorGroups::System, Theme::Light}]; + ColorsHash &sys_dark_scheme = _schemes[{ColorGroups::System, Theme::Dark}]; + + sys_light_scheme[ColorRole::Window] = QColor(243, 243, 243); + sys_light_scheme[ColorRole::WindowText] = Qt::GlobalColor::black; + sys_light_scheme[ColorRole::WindowBright] = QColor(196, 43, 28); + sys_light_scheme[ColorRole::Border] = QColor(131, 131, 131); + sys_light_scheme[ColorRole::Text] = Qt::GlobalColor::black; + sys_light_scheme[ColorRole::Button] = QColor(252, 252, 252); + sys_light_scheme[ColorRole::ButtonBorder] = QColor(217, 217, 217); + sys_light_scheme[ColorRole::ButtonBottomLine] = QColor(211, 211, 211); + sys_light_scheme[ColorRole::RadioButtonInnerCircle] = QColor(243, 243, 243); + sys_light_scheme[ColorRole::Link] = sys_colors[SysAccentPalette::AccentLight2]; + sys_light_scheme[ColorRole::ImportantLink] = sys_colors[SysAccentPalette::AccentDark2]; + sys_light_scheme[ColorRole::LinkVisited] = QColor(149, 79, 114); + sys_light_scheme[ColorRole::DisEnabled] = QColor(109, 109, 109); + sys_light_scheme[ColorRole::DisEnabledText] = QColor(0, 0, 0, 64); + sys_light_scheme[ColorRole::AppAccent] = sys_colors[SysAccentPalette::AccentDark1]; + sys_light_scheme[ColorRole::SysAccent] = sys_colors[SysAccentPalette::Accent]; + sys_light_scheme[ColorRole::AppAccentText] = apcaTextColor(sys_light_scheme[ColorRole::AppAccent]); + sys_light_scheme[ColorRole::MaskNormal] = Qt::GlobalColor::transparent; + sys_light_scheme[ColorRole::MaskHover] = QColor(0, 0, 0, 10); + sys_light_scheme[ColorRole::MaskPressed] = QColor(0, 0, 0, 6); + sys_light_scheme[ColorRole::MaskBrightPressed] = QColor(230, 220, 255, 24); + sys_light_scheme[ColorRole::MaskSelected] = QColor(90, 90, 90, 15); + sys_light_scheme[ColorRole::MaskSelectedHover] = QColor(73, 73, 73, 9); + sys_light_scheme[ColorRole::MaskSelectedPressed] = QColor(90, 90, 90, 15); + sys_light_scheme[ColorRole::MSWindow] = QColor(252, 252, 252, 170); + sys_light_scheme[ColorRole::MSWindowSplitLine] = QColor(229, 229, 229); + sys_light_scheme[ColorRole::ScrollBarNormal] = Qt::GlobalColor::transparent; + sys_light_scheme[ColorRole::ScrollBarFocus] = QColor(249, 249, 249); + sys_light_scheme[ColorRole::ScrollBarEndArrow] = QColor(138, 138, 138); + sys_light_scheme[ColorRole::ScrollBarSlider] = QColor(138, 138, 138); + sys_light_scheme[ColorRole::TextEditPanelNormal] = QColor(251, 251, 251); + sys_light_scheme[ColorRole::TextEditPanelFocus] = QColor(255, 255, 255); + sys_light_scheme[ColorRole::TextEditPanelHover] = QColor(246, 246, 246); + sys_light_scheme[ColorRole::TextEditBorder] = QColor(229, 229, 229); + sys_light_scheme[ColorRole::TextEditIndicatorLine] = QColor(134, 134, 134); + sys_light_scheme[ColorRole::PlaceholderText] = QColor(95, 95, 95); + + sys_dark_scheme[ColorRole::Window] = QColor(32, 32, 32); + sys_dark_scheme[ColorRole::WindowText] = Qt::GlobalColor::white; + sys_dark_scheme[ColorRole::WindowBright] = QColor(196, 43, 28); + sys_dark_scheme[ColorRole::Border] = QColor(131, 131, 131); + sys_dark_scheme[ColorRole::Text] = Qt::GlobalColor::white; + sys_dark_scheme[ColorRole::Button] = QColor(55, 55, 55); + sys_dark_scheme[ColorRole::ButtonBorder] = QColor(66, 66, 66); + sys_dark_scheme[ColorRole::ButtonBottomLine] = QColor(44, 44, 44); + sys_dark_scheme[ColorRole::RadioButtonInnerCircle] = QColor(32, 32, 32); + sys_dark_scheme[ColorRole::Link] = sys_colors[SysAccentPalette::AccentLight3]; + sys_dark_scheme[ColorRole::ImportantLink] = sys_colors[SysAccentPalette::AccentLight3]; + sys_dark_scheme[ColorRole::LinkVisited] = QColor(224, 158, 188); + sys_dark_scheme[ColorRole::DisEnabled] = QColor(180, 180, 180); + sys_dark_scheme[ColorRole::DisEnabledText] = QColor(255, 255, 255, 64); + sys_dark_scheme[ColorRole::AppAccent] = sys_colors[SysAccentPalette::AccentLight2]; + sys_dark_scheme[ColorRole::SysAccent] = sys_colors[SysAccentPalette::Accent]; + sys_dark_scheme[ColorRole::AppAccentText] = apcaTextColor(sys_dark_scheme[ColorRole::AppAccent]); + sys_dark_scheme[ColorRole::MaskNormal] = Qt::GlobalColor::transparent; + sys_dark_scheme[ColorRole::MaskHover] = QColor(255, 255, 255, 15); + sys_dark_scheme[ColorRole::MaskPressed] = QColor(255, 255, 255, 10); + sys_dark_scheme[ColorRole::MaskBrightPressed] = QColor(0, 0, 18, 22); + sys_dark_scheme[ColorRole::MaskSelected] = QColor(253, 253, 253, 15); + sys_dark_scheme[ColorRole::MaskSelectedHover] = QColor(185, 185, 185, 15); + sys_dark_scheme[ColorRole::MaskSelectedPressed] = QColor(253, 253, 253, 15); + sys_dark_scheme[ColorRole::MSWindow] = QColor(151, 151, 151, 15); + sys_dark_scheme[ColorRole::MSWindowSplitLine] = QColor(15, 15, 15, 45); + sys_dark_scheme[ColorRole::ScrollBarNormal] = Qt::GlobalColor::transparent; + sys_dark_scheme[ColorRole::ScrollBarFocus] = QColor(44, 44, 44); + sys_dark_scheme[ColorRole::ScrollBarEndArrow] = QColor(159, 159, 159); + sys_dark_scheme[ColorRole::ScrollBarSlider] = QColor(159, 159, 159); + sys_dark_scheme[ColorRole::TextEditPanelNormal] = QColor(45, 45, 45); + sys_dark_scheme[ColorRole::TextEditPanelFocus] = QColor(31, 31, 31); + sys_dark_scheme[ColorRole::TextEditPanelHover] = QColor(50, 50, 50); + sys_dark_scheme[ColorRole::TextEditBorder] = QColor(48, 48, 48); + sys_dark_scheme[ColorRole::TextEditIndicatorLine] = QColor(154, 154, 154); + sys_dark_scheme[ColorRole::PlaceholderText] = QColor(207, 207, 207); + } + break; + case ColorGroups::Default : + { + custom_colors = antDesign2ColorPalettes(QColor(212, 78, 125)); + for (int i = 0; i < 7; i++) + cus_colors[static_cast(i)] = custom_colors[i + 2]; + + ColorsHash &def_light_scheme = _schemes[{ColorGroups::Default, Theme::Light}]; + ColorsHash &def_dark_scheme = _schemes[{ColorGroups::Default, Theme::Dark}]; + + def_light_scheme[ColorRole::Window] = QColor(240, 243, 249); + def_light_scheme[ColorRole::WindowText] = Qt::GlobalColor::black; + def_light_scheme[ColorRole::WindowBright] = QColor(196, 43, 28); + def_light_scheme[ColorRole::Border] = QColor(160, 160, 160); + def_light_scheme[ColorRole::Text] = Qt::GlobalColor::black; + def_light_scheme[ColorRole::Button] = QColor(252, 252, 252); + def_light_scheme[ColorRole::ButtonBorder] = QColor(217, 217, 217); + def_light_scheme[ColorRole::ButtonBottomLine] = QColor(211, 211, 211); + def_light_scheme[ColorRole::RadioButtonInnerCircle] = QColor(240, 243, 249); + def_light_scheme[ColorRole::Link] = cus_colors[SysAccentPalette::AccentLight2]; + def_light_scheme[ColorRole::ImportantLink] = cus_colors[SysAccentPalette::AccentDark2]; + def_light_scheme[ColorRole::LinkVisited] = QColor(212, 98, 78); + def_light_scheme[ColorRole::DisEnabled] = QColor(109, 109, 109); + def_light_scheme[ColorRole::DisEnabledText] = QColor(0, 0, 0, 64); + def_light_scheme[ColorRole::AppAccent] = cus_colors[SysAccentPalette::AccentDark1]; + def_light_scheme[ColorRole::SysAccent] = sys_colors[SysAccentPalette::Accent]; + def_light_scheme[ColorRole::AppAccentText] = apcaTextColor(def_light_scheme[ColorRole::AppAccent]); + def_light_scheme[ColorRole::MaskNormal] = Qt::GlobalColor::transparent; + def_light_scheme[ColorRole::MaskHover] = QColor(0, 0, 0, 10); + def_light_scheme[ColorRole::MaskPressed] = QColor(0, 0, 0, 6); + def_light_scheme[ColorRole::MaskBrightPressed] = QColor(230, 220, 255, 24); + def_light_scheme[ColorRole::MaskSelected] = QColor(90, 90, 90, 15); + def_light_scheme[ColorRole::MaskSelectedHover] = QColor(73, 73, 73, 9); + def_light_scheme[ColorRole::MaskSelectedPressed] = QColor(90, 90, 90, 15); + def_light_scheme[ColorRole::MSWindow] = QColor(252, 252, 252, 170); + def_light_scheme[ColorRole::MSWindowSplitLine] = QColor(229, 229, 229); + def_light_scheme[ColorRole::ScrollBarNormal] = Qt::GlobalColor::transparent; + def_light_scheme[ColorRole::ScrollBarFocus] = QColor(249, 249, 249); + def_light_scheme[ColorRole::ScrollBarEndArrow] = QColor(138, 138, 138); + def_light_scheme[ColorRole::ScrollBarSlider] = QColor(138, 138, 138); + def_light_scheme[ColorRole::TextEditPanelNormal] = QColor(251, 251, 251); + def_light_scheme[ColorRole::TextEditPanelFocus] = QColor(255, 255, 255); + def_light_scheme[ColorRole::TextEditPanelHover] = QColor(246, 246, 246); + def_light_scheme[ColorRole::TextEditBorder] = QColor(229, 229, 229); + def_light_scheme[ColorRole::TextEditIndicatorLine] = QColor(134, 134, 134); + def_light_scheme[ColorRole::PlaceholderText] = QColor(95, 95, 95); + + def_dark_scheme[ColorRole::Window] = QColor(26, 32, 52); + def_dark_scheme[ColorRole::WindowText] = Qt::GlobalColor::white; + def_dark_scheme[ColorRole::WindowBright] = QColor(196, 43, 28); + def_dark_scheme[ColorRole::Border] = QColor(131, 131, 131); + def_dark_scheme[ColorRole::Text] = Qt::GlobalColor::white; + def_dark_scheme[ColorRole::Button] = QColor(55, 55, 55); + def_dark_scheme[ColorRole::ButtonBorder] = QColor(66, 66, 66); + def_dark_scheme[ColorRole::ButtonBottomLine] = QColor(44, 44, 44); + def_dark_scheme[ColorRole::RadioButtonInnerCircle] = QColor(26, 32, 52); + def_dark_scheme[ColorRole::Link] = cus_colors[SysAccentPalette::AccentLight3]; + def_dark_scheme[ColorRole::ImportantLink] = cus_colors[SysAccentPalette::AccentLight3]; + def_dark_scheme[ColorRole::LinkVisited] = QColor(212, 98, 78); + def_dark_scheme[ColorRole::DisEnabled] = QColor(180, 180, 180); + def_dark_scheme[ColorRole::DisEnabledText] = QColor(255, 255, 255, 64); + def_dark_scheme[ColorRole::AppAccent] = cus_colors[SysAccentPalette::AccentLight2]; + def_dark_scheme[ColorRole::SysAccent] = sys_colors[SysAccentPalette::Accent]; + def_dark_scheme[ColorRole::AppAccentText] = apcaTextColor(def_dark_scheme[ColorRole::AppAccent]); + def_dark_scheme[ColorRole::MaskNormal] = Qt::GlobalColor::transparent; + def_dark_scheme[ColorRole::MaskHover] = QColor(255, 255, 255, 15); + def_dark_scheme[ColorRole::MaskPressed] = QColor(255, 255, 255, 10); + def_dark_scheme[ColorRole::MaskBrightPressed] = QColor(0, 0, 18, 22); + def_dark_scheme[ColorRole::MaskSelected] = QColor(253, 253, 253, 15); + def_dark_scheme[ColorRole::MaskSelectedHover] = QColor(185, 185, 185, 15); + def_dark_scheme[ColorRole::MaskSelectedPressed] = QColor(253, 253, 253, 15); + def_dark_scheme[ColorRole::MSWindow] = QColor(151, 151, 151, 15); + def_dark_scheme[ColorRole::MSWindowSplitLine] = QColor(15, 15, 15, 45); + def_dark_scheme[ColorRole::ScrollBarNormal] = Qt::GlobalColor::transparent; + def_dark_scheme[ColorRole::ScrollBarFocus] = QColor(44, 44, 44); + def_dark_scheme[ColorRole::ScrollBarEndArrow] = QColor(159, 159, 159); + def_dark_scheme[ColorRole::ScrollBarSlider] = QColor(159, 159, 159); + def_dark_scheme[ColorRole::TextEditPanelNormal] = QColor(45, 45, 45); + def_dark_scheme[ColorRole::TextEditPanelFocus] = QColor(31, 31, 31); + def_dark_scheme[ColorRole::TextEditPanelHover] = QColor(50, 50, 50); + def_dark_scheme[ColorRole::TextEditBorder] = QColor(48, 48, 48); + def_dark_scheme[ColorRole::TextEditIndicatorLine] = QColor(154, 154, 154); + def_dark_scheme[ColorRole::PlaceholderText] = QColor(207, 207, 207); + } + break; + case ColorGroups::Custom : + { + ColorsHash &cus_light_scheme = _schemes[{ColorGroups::Custom, Theme::Light}]; + ColorsHash &cus_dark_scheme = _schemes[{ColorGroups::Custom, Theme::Dark}]; + + cus_light_scheme[ColorRole::Window] = QColor(243, 243, 243); + cus_light_scheme[ColorRole::WindowText] = Qt::GlobalColor::black; + cus_light_scheme[ColorRole::WindowBright] = QColor(196, 43, 28); + cus_light_scheme[ColorRole::Border] = QColor(131, 131, 131); + cus_light_scheme[ColorRole::Text] = Qt::GlobalColor::black; + cus_light_scheme[ColorRole::Button] = QColor(252, 252, 252); + cus_light_scheme[ColorRole::ButtonBorder] = QColor(217, 217, 217); + cus_light_scheme[ColorRole::ButtonBottomLine] = QColor(211, 211, 211); + cus_light_scheme[ColorRole::RadioButtonInnerCircle] = QColor(243, 243, 243); + cus_light_scheme[ColorRole::Link] = cus_colors[SysAccentPalette::AccentLight2]; + cus_light_scheme[ColorRole::ImportantLink] = cus_colors[SysAccentPalette::AccentDark2]; + cus_light_scheme[ColorRole::LinkVisited] = QColor(149, 79, 114); + cus_light_scheme[ColorRole::DisEnabled] = QColor(109, 109, 109); + cus_light_scheme[ColorRole::DisEnabledText] = QColor(0, 0, 0, 64); + cus_light_scheme[ColorRole::AppAccent] = cus_colors[SysAccentPalette::AccentDark1]; + cus_light_scheme[ColorRole::SysAccent] = sys_colors[SysAccentPalette::Accent]; + cus_light_scheme[ColorRole::AppAccentText] = apcaTextColor(cus_light_scheme[ColorRole::AppAccent]); + cus_light_scheme[ColorRole::MaskNormal] = Qt::GlobalColor::transparent; + cus_light_scheme[ColorRole::MaskHover] = QColor(0, 0, 0, 10); + cus_light_scheme[ColorRole::MaskPressed] = QColor(0, 0, 0, 6); + cus_light_scheme[ColorRole::MaskBrightPressed] = QColor(230, 220, 255, 24); + cus_light_scheme[ColorRole::MaskSelected] = QColor(90, 90, 90, 15); + cus_light_scheme[ColorRole::MaskSelectedHover] = QColor(73, 73, 73, 9); + cus_light_scheme[ColorRole::MaskSelectedPressed] = QColor(90, 90, 90, 15); + cus_light_scheme[ColorRole::MSWindow] = QColor(252, 252, 252, 170); + cus_light_scheme[ColorRole::MSWindowSplitLine] = QColor(229, 229, 229); + cus_light_scheme[ColorRole::ScrollBarNormal] = Qt::GlobalColor::transparent; + cus_light_scheme[ColorRole::ScrollBarFocus] = QColor(249, 249, 249); + cus_light_scheme[ColorRole::ScrollBarEndArrow] = QColor(138, 138, 138); + cus_light_scheme[ColorRole::ScrollBarSlider] = QColor(138, 138, 138); + cus_light_scheme[ColorRole::TextEditPanelNormal] = QColor(251, 251, 251); + cus_light_scheme[ColorRole::TextEditPanelFocus] = QColor(255, 255, 255); + cus_light_scheme[ColorRole::TextEditPanelHover] = QColor(246, 246, 246); + cus_light_scheme[ColorRole::TextEditBorder] = QColor(229, 229, 229); + cus_light_scheme[ColorRole::TextEditIndicatorLine] = QColor(134, 134, 134); + cus_light_scheme[ColorRole::PlaceholderText] = QColor(95, 95, 95); + + cus_dark_scheme[ColorRole::Window] = QColor(32, 32, 32); + cus_dark_scheme[ColorRole::WindowText] = Qt::GlobalColor::white; + cus_dark_scheme[ColorRole::WindowBright] = QColor(196, 43, 28); + cus_dark_scheme[ColorRole::Border] = QColor(131, 131, 131); + cus_dark_scheme[ColorRole::Text] = Qt::GlobalColor::white; + cus_dark_scheme[ColorRole::Button] = QColor(55, 55, 55); + cus_dark_scheme[ColorRole::ButtonBorder] = QColor(66, 66, 66); + cus_dark_scheme[ColorRole::ButtonBottomLine] = QColor(44, 44, 44); + cus_dark_scheme[ColorRole::RadioButtonInnerCircle] = QColor(32, 32, 32); + cus_dark_scheme[ColorRole::Link] = cus_colors[SysAccentPalette::AccentLight3]; + cus_dark_scheme[ColorRole::ImportantLink] = cus_colors[SysAccentPalette::AccentLight3]; + cus_dark_scheme[ColorRole::LinkVisited] = QColor(224, 158, 188); + cus_dark_scheme[ColorRole::DisEnabled] = QColor(180, 180, 180); + cus_dark_scheme[ColorRole::DisEnabledText] = QColor(255, 255, 255, 64); + cus_dark_scheme[ColorRole::AppAccent] = cus_colors[SysAccentPalette::AccentLight2]; + cus_dark_scheme[ColorRole::SysAccent] = sys_colors[SysAccentPalette::Accent]; + cus_dark_scheme[ColorRole::AppAccentText] = apcaTextColor(cus_dark_scheme[ColorRole::AppAccent]); + cus_dark_scheme[ColorRole::MaskNormal] = Qt::GlobalColor::transparent; + cus_dark_scheme[ColorRole::MaskHover] = QColor(255, 255, 255, 15); + cus_dark_scheme[ColorRole::MaskPressed] = QColor(255, 255, 255, 10); + cus_dark_scheme[ColorRole::MaskBrightPressed] = QColor(0, 0, 18, 22); + cus_dark_scheme[ColorRole::MaskSelected] = QColor(253, 253, 253, 15); + cus_dark_scheme[ColorRole::MaskSelectedHover] = QColor(185, 185, 185, 15); + cus_dark_scheme[ColorRole::MaskSelectedPressed] = QColor(253, 253, 253, 15); + cus_dark_scheme[ColorRole::MSWindow] = QColor(151, 151, 151, 15); + cus_dark_scheme[ColorRole::MSWindowSplitLine] = QColor(15, 15, 15, 45); + cus_dark_scheme[ColorRole::ScrollBarNormal] = Qt::GlobalColor::transparent; + cus_dark_scheme[ColorRole::ScrollBarFocus] = QColor(44, 44, 44); + cus_dark_scheme[ColorRole::ScrollBarEndArrow] = QColor(159, 159, 159); + cus_dark_scheme[ColorRole::ScrollBarSlider] = QColor(159, 159, 159); + cus_dark_scheme[ColorRole::TextEditPanelNormal] = QColor(45, 45, 45); + cus_dark_scheme[ColorRole::TextEditPanelFocus] = QColor(31, 31, 31); + cus_dark_scheme[ColorRole::TextEditPanelHover] = QColor(50, 50, 50); + cus_dark_scheme[ColorRole::TextEditBorder] = QColor(48, 48, 48); + cus_dark_scheme[ColorRole::TextEditIndicatorLine] = QColor(154, 154, 154); + cus_dark_scheme[ColorRole::PlaceholderText] = QColor(207, 207, 207); + } + break; + } + } + + void Palette::onAppThemeChanged(Theme theme) + { + _group_theme = {_group_theme.first, theme}; + emit appThemeChange(); + } + + void Palette::onSystemAccentColorsChanged(QMap colors) + { + const auto froup = _group_theme.first; + if (froup == ColorGroups::System) + { + ColorsHash &sys_light_scheme = _schemes[{ColorGroups::System, Theme::Light}]; + ColorsHash &sys_dark_scheme = _schemes[{ColorGroups::System, Theme::Dark}]; + + sys_light_scheme[ColorRole::Link] = colors[SysAccentPalette::AccentLight2]; + sys_light_scheme[ColorRole::ImportantLink] = colors[SysAccentPalette::AccentDark2]; + sys_light_scheme[ColorRole::AppAccent] = colors[SysAccentPalette::AccentDark1]; + sys_light_scheme[ColorRole::AppAccentText] = apcaTextColor(sys_light_scheme[ColorRole::AppAccent]); + sys_light_scheme[ColorRole::SysAccent] = colors[SysAccentPalette::Accent]; + + sys_dark_scheme[ColorRole::Link] = colors[SysAccentPalette::AccentLight3]; + sys_dark_scheme[ColorRole::ImportantLink] = colors[SysAccentPalette::AccentLight3]; + sys_dark_scheme[ColorRole::AppAccent] = colors[SysAccentPalette::AccentLight2]; + sys_dark_scheme[ColorRole::AppAccentText] = apcaTextColor(sys_dark_scheme[ColorRole::AppAccent]); + sys_dark_scheme[ColorRole::SysAccent] = colors[SysAccentPalette::Accent]; + } + else if (froup == ColorGroups::Default) + { + ColorsHash &def_light_scheme = _schemes[{ColorGroups::Default, Theme::Light}]; + ColorsHash &def_dark_scheme = _schemes[{ColorGroups::Default, Theme::Dark}]; + + def_light_scheme[ColorRole::SysAccent] = colors[SysAccentPalette::Accent]; + def_dark_scheme[ColorRole::SysAccent] = colors[SysAccentPalette::Accent]; + } + if (froup != ColorGroups::Custom) + emit appColorChange(); + } +} // fancy diff --git a/src/Fancy/Core/Palette/Palette.h b/src/Fancy/Palette.h similarity index 92% rename from src/Fancy/Core/Palette/Palette.h rename to src/Fancy/Palette.h index 222094e1..fb97d5b8 100644 --- a/src/Fancy/Core/Palette/Palette.h +++ b/src/Fancy/Palette.h @@ -8,8 +8,8 @@ #include #include #include +#include #include -#include "utils/hash.h" namespace fancy @@ -31,7 +31,7 @@ namespace fancy /** * @brief 返回全局唯一的调色板对象 - * @return Palette 的引用 + * @return Palettr的引用 */ static Palette &palette(); @@ -170,21 +170,6 @@ namespace fancy private: void loadSchemes(const QColor &custom = QColor()); - /** - * @brief 读取并解析颜色配置文件,返回对应颜色组的 json 对象 - * @param group 颜色组 - * @return 成功返回有效的 QJsonObject,失败返回空 QJsonObject - */ - static QJsonObject loadColorProfile(ColorGroups group); - - /** - * @brief 从 json 字符串解析 QColor - * @param custom 自定义颜色 - * @param raw 待解析的 json 值字符串 - * @return 解析后的颜色,解析失败返回空颜色 - */ - static QColor parseColorFromJsonStr(const QColor &custom, const QString &raw); - private slots: void onAppThemeChanged(Theme theme); diff --git a/src/Fancy/Animation/ParallelAnimationGroupPool.cpp b/src/Fancy/ParallelAnimationGroupPool.cpp similarity index 100% rename from src/Fancy/Animation/ParallelAnimationGroupPool.cpp rename to src/Fancy/ParallelAnimationGroupPool.cpp diff --git a/src/Fancy/Animation/ParallelAnimationGroupPool.h b/src/Fancy/ParallelAnimationGroupPool.h similarity index 100% rename from src/Fancy/Animation/ParallelAnimationGroupPool.h rename to src/Fancy/ParallelAnimationGroupPool.h diff --git a/src/Fancy/Widgets/PlainTextEdit/PlainTextEdit.cpp b/src/Fancy/PlainTextEdit.cpp similarity index 93% rename from src/Fancy/Widgets/PlainTextEdit/PlainTextEdit.cpp rename to src/Fancy/PlainTextEdit.cpp index d164c192..b4c2e4b7 100644 --- a/src/Fancy/Widgets/PlainTextEdit/PlainTextEdit.cpp +++ b/src/Fancy/PlainTextEdit.cpp @@ -4,10 +4,10 @@ #include "PlainTextEdit.h" +#include "Defs.h" +#include "palette.h" #include "PlainTextEditStyle.h" -#include "Core/Defs.h" -#include "Core/Palette/Palette.h" -#include "Widgets/ScrollBar/ScrollBar.h" +#include "ScrollBar.h" namespace fancy { diff --git a/src/Fancy/Widgets/PlainTextEdit/PlainTextEdit.h b/src/Fancy/PlainTextEdit.h similarity index 100% rename from src/Fancy/Widgets/PlainTextEdit/PlainTextEdit.h rename to src/Fancy/PlainTextEdit.h diff --git a/src/Fancy/Widgets/PlainTextEdit/PlainTextEditStyle.cpp b/src/Fancy/PlainTextEditStyle.cpp similarity index 96% rename from src/Fancy/Widgets/PlainTextEdit/PlainTextEditStyle.cpp rename to src/Fancy/PlainTextEditStyle.cpp index a5281ae5..2eccee55 100644 --- a/src/Fancy/Widgets/PlainTextEdit/PlainTextEditStyle.cpp +++ b/src/Fancy/PlainTextEditStyle.cpp @@ -4,16 +4,14 @@ #include "PlainTextEditStyle.h" -#include #include #include #include #include -#include "Core/Defs.h" -#include "Core/ControlState/ControlState.h" -#include "Core/Palette/Palette.h" -#include "utils/General.h" +#include "ControlState.h" +#include "Defs.h" +#include "palette.h" namespace fancy { diff --git a/src/Fancy/Widgets/PlainTextEdit/PlainTextEditStyle.h b/src/Fancy/PlainTextEditStyle.h similarity index 100% rename from src/Fancy/Widgets/PlainTextEdit/PlainTextEditStyle.h rename to src/Fancy/PlainTextEditStyle.h diff --git a/src/Fancy/Widgets/PushButton/PushButton.cpp b/src/Fancy/PushButton.cpp similarity index 98% rename from src/Fancy/Widgets/PushButton/PushButton.cpp rename to src/Fancy/PushButton.cpp index 743aacc2..462dadf8 100644 --- a/src/Fancy/Widgets/PushButton/PushButton.cpp +++ b/src/Fancy/PushButton.cpp @@ -3,8 +3,8 @@ // #include "PushButton.h" +#include "Icon.h" #include "PushButtonStyle.h" -#include "Icon/Icon.h" namespace fancy { diff --git a/src/Fancy/Widgets/PushButton/PushButton.h b/src/Fancy/PushButton.h similarity index 98% rename from src/Fancy/Widgets/PushButton/PushButton.h rename to src/Fancy/PushButton.h index b52b014c..c4e2cb1f 100644 --- a/src/Fancy/Widgets/PushButton/PushButton.h +++ b/src/Fancy/PushButton.h @@ -6,7 +6,7 @@ #define QWIDGET_FANCYUI_PUSHBUTTON_H #include -#include "Core/Defs.h" +#include "Defs.h" namespace fancy { diff --git a/src/Fancy/Widgets/PushButton/PushButtonStyle.cpp b/src/Fancy/PushButtonStyle.cpp similarity index 95% rename from src/Fancy/Widgets/PushButton/PushButtonStyle.cpp rename to src/Fancy/PushButtonStyle.cpp index a5ef469f..6752a387 100644 --- a/src/Fancy/Widgets/PushButton/PushButtonStyle.cpp +++ b/src/Fancy/PushButtonStyle.cpp @@ -5,14 +5,12 @@ #include "PushButtonStyle.h" #include -#include #include #include -#include "Core/Defs.h" -#include "Core/ControlState/ControlState.h" -#include "Core/Palette/Palette.h" -#include "utils/General.h" +#include "ControlState.h" +#include "Defs.h" +#include "Palette.h" namespace fancy { @@ -93,7 +91,7 @@ namespace fancy _parent->update(); } - int PushButtonStyle::radius() const + qreal PushButtonStyle::radius() const { return _buttonRadius; } diff --git a/src/Fancy/Widgets/PushButton/PushButtonStyle.h b/src/Fancy/PushButtonStyle.h similarity index 91% rename from src/Fancy/Widgets/PushButton/PushButtonStyle.h rename to src/Fancy/PushButtonStyle.h index ee6cbaed..aac613f3 100644 --- a/src/Fancy/Widgets/PushButton/PushButtonStyle.h +++ b/src/Fancy/PushButtonStyle.h @@ -24,9 +24,9 @@ namespace fancy void setRadius(int radius); - [[nodiscard]] int radius() const; + [[nodiscard]] qreal radius() const; private: - int _buttonRadius; + qreal _buttonRadius; QPushButton *_parent; }; } // fancy diff --git a/src/Fancy/Widgets/RadioButton/RadioButton.cpp b/src/Fancy/RadioButton.cpp similarity index 100% rename from src/Fancy/Widgets/RadioButton/RadioButton.cpp rename to src/Fancy/RadioButton.cpp diff --git a/src/Fancy/Widgets/RadioButton/RadioButton.h b/src/Fancy/RadioButton.h similarity index 100% rename from src/Fancy/Widgets/RadioButton/RadioButton.h rename to src/Fancy/RadioButton.h diff --git a/src/Fancy/Widgets/RadioButton/RadioButtonStyle.cpp b/src/Fancy/RadioButtonStyle.cpp similarity index 98% rename from src/Fancy/Widgets/RadioButton/RadioButtonStyle.cpp rename to src/Fancy/RadioButtonStyle.cpp index 5cc49d65..76db9819 100644 --- a/src/Fancy/Widgets/RadioButton/RadioButtonStyle.cpp +++ b/src/Fancy/RadioButtonStyle.cpp @@ -3,14 +3,12 @@ // #include "RadioButtonStyle.h" - -#include #include #include -#include "Core/Defs.h" -#include "Core/ControlState/ControlState.h" -#include "Core/Palette/Palette.h" +#include "ControlState.h" +#include "Defs.h" +#include "palette.h" namespace fancy diff --git a/src/Fancy/Widgets/RadioButton/RadioButtonStyle.h b/src/Fancy/RadioButtonStyle.h similarity index 100% rename from src/Fancy/Widgets/RadioButton/RadioButtonStyle.h rename to src/Fancy/RadioButtonStyle.h diff --git a/src/Fancy/Animation/RippleAnimation.cpp b/src/Fancy/RippleAnimation.cpp similarity index 100% rename from src/Fancy/Animation/RippleAnimation.cpp rename to src/Fancy/RippleAnimation.cpp diff --git a/src/Fancy/Animation/RippleAnimation.h b/src/Fancy/RippleAnimation.h similarity index 97% rename from src/Fancy/Animation/RippleAnimation.h rename to src/Fancy/RippleAnimation.h index 61f6e048..3d4f303d 100644 --- a/src/Fancy/Animation/RippleAnimation.h +++ b/src/Fancy/RippleAnimation.h @@ -5,8 +5,10 @@ #ifndef QWIDGET_FANCYUI_RIPPLEANIMATION_H #define QWIDGET_FANCYUI_RIPPLEANIMATION_H -#include +#include +#include #include + #include "ParallelAnimationGroupPool.h" class QPainter; diff --git a/src/Fancy/Widgets/PushButton/RippleButton.cpp b/src/Fancy/RippleButton.cpp similarity index 91% rename from src/Fancy/Widgets/PushButton/RippleButton.cpp rename to src/Fancy/RippleButton.cpp index ffc10b65..60cfb2eb 100644 --- a/src/Fancy/Widgets/PushButton/RippleButton.cpp +++ b/src/Fancy/RippleButton.cpp @@ -3,16 +3,18 @@ // #include "RippleButton.h" - #include #include #include #include +#include + +#include "ControlState.h" +#include "Defs.h" +#include "Icon.h" +#include "Palette.h" +#include "RippleAnimation.h" #include "RippleButtonStyle.h" -#include "Core/Defs.h" -#include "Core/ControlState/ControlState.h" -#include "Core/Palette/Palette.h" -#include "Icon/Icon.h" namespace fancy { diff --git a/src/Fancy/Widgets/PushButton/RippleButton.h b/src/Fancy/RippleButton.h similarity index 98% rename from src/Fancy/Widgets/PushButton/RippleButton.h rename to src/Fancy/RippleButton.h index 71ff795f..015d2983 100644 --- a/src/Fancy/Widgets/PushButton/RippleButton.h +++ b/src/Fancy/RippleButton.h @@ -4,6 +4,9 @@ #ifndef QWIDGET_FANCYUI_RIPPLEBUTTON_H #define QWIDGET_FANCYUI_RIPPLEBUTTON_H + +#include + #include "ThemeButton.h" diff --git a/src/Fancy/Widgets/PushButton/RippleButtonStyle.cpp b/src/Fancy/RippleButtonStyle.cpp similarity index 94% rename from src/Fancy/Widgets/PushButton/RippleButtonStyle.cpp rename to src/Fancy/RippleButtonStyle.cpp index 59131704..705b96ad 100644 --- a/src/Fancy/Widgets/PushButton/RippleButtonStyle.cpp +++ b/src/Fancy/RippleButtonStyle.cpp @@ -4,17 +4,14 @@ #include "RippleButtonStyle.h" #include -#include #include #include #include -#include -#include "Animation/RippleAnimation.h" -#include "Core/Defs.h" -#include "Core/ControlState/ControlState.h" -#include "Core/Palette/Palette.h" -#include "utils/General.h" +#include "ControlState.h" +#include "Defs.h" +#include "Palette.h" +#include "RippleAnimation.h" namespace fancy { @@ -117,11 +114,7 @@ namespace fancy if (_parent->isEnabled() && (event->type() == QEvent::Type::MouseButtonPress || event->type() == QEvent::Type::MouseButtonDblClick)) if (const QMouseEvent *mouseEvent = static_cast(event); mouseEvent->button() == Qt::MouseButton::LeftButton) -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) _ripple->start(mouseEvent->position()); -#else - _ripple->start(mouseEvent->localPos()); -#endif return QProxyStyle::eventFilter(obj, event); } diff --git a/src/Fancy/Widgets/PushButton/RippleButtonStyle.h b/src/Fancy/RippleButtonStyle.h similarity index 100% rename from src/Fancy/Widgets/PushButton/RippleButtonStyle.h rename to src/Fancy/RippleButtonStyle.h diff --git a/src/Fancy/Widgets/ScrollArea/ScrollArea.cpp b/src/Fancy/ScrollArea.cpp similarity index 93% rename from src/Fancy/Widgets/ScrollArea/ScrollArea.cpp rename to src/Fancy/ScrollArea.cpp index f53697bb..7282aa9c 100644 --- a/src/Fancy/Widgets/ScrollArea/ScrollArea.cpp +++ b/src/Fancy/ScrollArea.cpp @@ -5,7 +5,7 @@ #include "ScrollArea.h" #include "ScrollAreaStyle.h" -#include "Widgets/ScrollBar/ScrollBar.h" +#include "ScrollBar.h" namespace fancy { diff --git a/src/Fancy/Widgets/ScrollArea/ScrollArea.h b/src/Fancy/ScrollArea.h similarity index 100% rename from src/Fancy/Widgets/ScrollArea/ScrollArea.h rename to src/Fancy/ScrollArea.h diff --git a/src/Fancy/Widgets/ScrollArea/ScrollAreaStyle.cpp b/src/Fancy/ScrollAreaStyle.cpp similarity index 100% rename from src/Fancy/Widgets/ScrollArea/ScrollAreaStyle.cpp rename to src/Fancy/ScrollAreaStyle.cpp diff --git a/src/Fancy/Widgets/ScrollArea/ScrollAreaStyle.h b/src/Fancy/ScrollAreaStyle.h similarity index 100% rename from src/Fancy/Widgets/ScrollArea/ScrollAreaStyle.h rename to src/Fancy/ScrollAreaStyle.h diff --git a/src/Fancy/Widgets/ScrollBar/ScrollBar.cpp b/src/Fancy/ScrollBar.cpp similarity index 100% rename from src/Fancy/Widgets/ScrollBar/ScrollBar.cpp rename to src/Fancy/ScrollBar.cpp diff --git a/src/Fancy/Widgets/ScrollBar/ScrollBar.h b/src/Fancy/ScrollBar.h similarity index 100% rename from src/Fancy/Widgets/ScrollBar/ScrollBar.h rename to src/Fancy/ScrollBar.h diff --git a/src/Fancy/ScrollBarStyle.cpp b/src/Fancy/ScrollBarStyle.cpp new file mode 100644 index 00000000..32bd0bfd --- /dev/null +++ b/src/Fancy/ScrollBarStyle.cpp @@ -0,0 +1,139 @@ +// +// Created by TX on 2025/10/16. +// + +#include "ScrollBarStyle.h" + +#include + +#include "CheckBoxStyle.h" +#include "ControlState.h" +#include "Defs.h" +#include "Palette.h" + +namespace fancy +{ + ScrollBarStyle::ScrollBarStyle(QScrollBar *parent) : + QProxyStyle(nullptr) + { + setParent(parent); + parent->setAttribute(Qt::WidgetAttribute::WA_OpaquePaintEvent, false); + } + + void ScrollBarStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget) const + { + if (const auto *opt = qstyleoption_cast(option); control == CC_ScrollBar && opt) + { + auto &pal = Palette::palette(); + const ControlState state(opt->state, ControlType::Widget); + const bool isHorizontal = opt->orientation == Qt::Horizontal; + const bool isActive = state == VisualState::Hover || state == VisualState::Pressed || (state == VisualState::Disabled && state._over); + const QRectF sliderRect = subControlRect(control, option, SC_ScrollBarSlider, widget); + const QRectF grooveRect = subControlRect(control, option, SC_ScrollBarGroove, widget); + const QRectF rect = opt->rect; + const qreal center = isHorizontal ? sliderRect.center().y() : sliderRect.center().x(); // 中心坐标 + const qreal bgRadius = (isHorizontal ? opt->rect.height() : opt->rect.width()) / 2.0; // 背景圆角 + const QRectF addArrowRect = isHorizontal + ? QRectF{grooveRect.topRight(), rect.bottomRight()}.adjusted(4, 2, -1, -2) + : QRectF{grooveRect.bottomLeft(), rect.bottomRight()}.adjusted(2, 4, -2, -1); + const QRectF subArrowRect = isHorizontal + ? QRectF{rect.topLeft(), grooveRect.bottomLeft()}.adjusted(1, 2, -4, -2) + : QRectF{rect.topLeft(), grooveRect.topRight()}.adjusted(2, 1, -2, -4); + const QRectF slider = isActive + ? (isHorizontal + ? QRectF(sliderRect.left(), center - 3, sliderRect.width(), 6) + : QRectF(center - 3, sliderRect.top(), 6, sliderRect.height())) + : isHorizontal + ? QRectF(sliderRect.left(), center - 1, sliderRect.width(), 2) + : QRectF(center - 1, sliderRect.top(), 2, sliderRect.height()); + const qreal sdRadius = (isHorizontal ? slider.height() : slider.width()) / 2.0; // 滑块圆角 + + const auto drawArrow = [painter, &addArrowRect, &subArrowRect](const bool horizontal) { + if (horizontal) + { + ScrollBarStyle::drawArrow(painter, addArrowRect, Direction::Right); + ScrollBarStyle::drawArrow(painter, subArrowRect, Direction::Left); + } + else + { + ScrollBarStyle::drawArrow(painter, addArrowRect, Direction::Down); + ScrollBarStyle::drawArrow(painter, subArrowRect, Direction::Up); + } + }; + + painter->save(); + painter->setRenderHint(QPainter::Antialiasing, true); + painter->setPen(Qt::NoPen); + + // 绘制背景 + painter->setBrush(pal[isActive ? ColorRole::ScrollBarFocus : ColorRole::ScrollBarNormal]); + painter->drawRoundedRect(opt->rect, bgRadius, bgRadius); + + // 绘制滑块 + painter->setBrush(pal[ColorRole::ScrollBarSlider]); + painter->drawRoundedRect(slider, sdRadius, sdRadius); + + // 绘制箭头 + isActive ? drawArrow(isHorizontal) : void(); + painter->restore(); + return; + } + + QProxyStyle::drawComplexControl(control, option, painter, widget); + } + + + int ScrollBarStyle::pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const + { + switch (metric) + { + case PM_ScrollBarExtent : // 12 垂直滚动条的宽度和水平滚动条的高度 + return 12; + case PM_ScrollBarSliderMin : // 18 垂直滚动条滑块的最小高度和水平滚动条滑块的最小宽度 + return 18; + default : + break; + } + return QProxyStyle::pixelMetric(metric, option, widget); + } + + void ScrollBarStyle::drawArrow(QPainter *painter, const QRectF &arrowRect, const Direction dir) + { + QPainterPath path; + QPointF p1, p2, p3; + auto &pal = Palette::palette(); + switch (dir) + { + case Direction::Right : + p1 = arrowRect.topLeft(); + p2 = arrowRect.bottomLeft(); + p3 = QPointF(arrowRect.right(), arrowRect.center().y()); + break; + case Direction::Left : + p1 = arrowRect.topRight(); + p2 = arrowRect.bottomRight(); + p3 = QPointF(arrowRect.left(), arrowRect.center().y()); + break; + case Direction::Down : + p1 = arrowRect.topLeft(); + p2 = arrowRect.topRight(); + p3 = QPointF(arrowRect.center().x(), arrowRect.bottom()); + break; + case Direction::Up : + p1 = arrowRect.bottomLeft(); + p2 = arrowRect.bottomRight(); + p3 = QPointF(arrowRect.center().x(), arrowRect.top()); + break; + default : + return; + } + path.moveTo(p1); + path.lineTo(p2); + path.lineTo(p3); + path.closeSubpath(); + painter->fillPath(path, pal[ColorRole::ScrollBarSlider]); + painter->strokePath(path, QPen(pal[ColorRole::ScrollBarSlider], 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); + } + + +} // fancy diff --git a/src/Fancy/Widgets/ScrollBar/ScrollBarStyle.h b/src/Fancy/ScrollBarStyle.h similarity index 83% rename from src/Fancy/Widgets/ScrollBar/ScrollBarStyle.h rename to src/Fancy/ScrollBarStyle.h index 6120766a..85e6eb29 100644 --- a/src/Fancy/Widgets/ScrollBar/ScrollBarStyle.h +++ b/src/Fancy/ScrollBarStyle.h @@ -19,12 +19,11 @@ namespace fancy public: explicit ScrollBarStyle(QScrollBar *parent); + protected: void drawComplexControl(ComplexControl control, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget) const override; int pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const override; - QRect subControlRect(ComplexControl control, const QStyleOptionComplex *option, SubControl subControl, const QWidget *widget) const override; - static void drawArrow(QPainter *painter, const QRectF &arrowRect, Direction dir); }; } // fancy diff --git a/src/Fancy/Widgets/Sidebar/Sidebar.cpp b/src/Fancy/Sidebar.cpp similarity index 98% rename from src/Fancy/Widgets/Sidebar/Sidebar.cpp rename to src/Fancy/Sidebar.cpp index daab4033..593b478a 100644 --- a/src/Fancy/Widgets/Sidebar/Sidebar.cpp +++ b/src/Fancy/Sidebar.cpp @@ -8,8 +8,8 @@ #include #include -#include "Widgets/ScrollArea/ScrollArea.h" -#include "Widgets/ScrollBar/ScrollBar.h" +#include "ScrollArea.h" +#include "ScrollBar.h" #include "SidebarButton.h" namespace fancy diff --git a/src/Fancy/Widgets/Sidebar/Sidebar.h b/src/Fancy/Sidebar.h similarity index 100% rename from src/Fancy/Widgets/Sidebar/Sidebar.h rename to src/Fancy/Sidebar.h diff --git a/src/Fancy/Widgets/Sidebar/SidebarButton.cpp b/src/Fancy/SidebarButton.cpp similarity index 96% rename from src/Fancy/Widgets/Sidebar/SidebarButton.cpp rename to src/Fancy/SidebarButton.cpp index b7286e5a..a533008f 100644 --- a/src/Fancy/Widgets/Sidebar/SidebarButton.cpp +++ b/src/Fancy/SidebarButton.cpp @@ -9,12 +9,13 @@ #include #include +#include "Defs.h" +#include "Palette.h" #include "Sidebar.h" -#include "Core/Defs.h" -#include "Core/Palette/Palette.h" -#include "Widgets/SvgWidget/SvgWidget.h" -#include "Widgets/TextLabel/TextLabel.h" -#include "Widgets/TextLabel/TextLabelStyle.h" +#include "SvgWidget.h" +#include "TextLabel.h" +#include "TextLabelStyle.h" +#include "TransparentButtonStyle.h" namespace fancy { diff --git a/src/Fancy/Widgets/Sidebar/SidebarButton.h b/src/Fancy/SidebarButton.h similarity index 98% rename from src/Fancy/Widgets/Sidebar/SidebarButton.h rename to src/Fancy/SidebarButton.h index 607b689f..d112b18c 100644 --- a/src/Fancy/Widgets/Sidebar/SidebarButton.h +++ b/src/Fancy/SidebarButton.h @@ -5,7 +5,8 @@ #ifndef QWIDGET_FANCYUI_SIDEBARBUTTON_H #define QWIDGET_FANCYUI_SIDEBARBUTTON_H #include -#include "Widgets/PushButton/TransparentButton.h" + +#include "TransparentButton.h" class QVariantAnimation; diff --git a/src/Fancy/Widgets/Slider/Slider.cpp b/src/Fancy/Slider.cpp similarity index 99% rename from src/Fancy/Widgets/Slider/Slider.cpp rename to src/Fancy/Slider.cpp index ae73d9ef..da8186f6 100644 --- a/src/Fancy/Widgets/Slider/Slider.cpp +++ b/src/Fancy/Slider.cpp @@ -3,6 +3,7 @@ // #include "Slider.h" + #include "SliderStyle.h" namespace fancy diff --git a/src/Fancy/Widgets/Slider/Slider.h b/src/Fancy/Slider.h similarity index 100% rename from src/Fancy/Widgets/Slider/Slider.h rename to src/Fancy/Slider.h diff --git a/src/Fancy/Widgets/Slider/SliderIndicator.cpp b/src/Fancy/SliderIndicator.cpp similarity index 62% rename from src/Fancy/Widgets/Slider/SliderIndicator.cpp rename to src/Fancy/SliderIndicator.cpp index 349dbc50..83a0a859 100644 --- a/src/Fancy/Widgets/Slider/SliderIndicator.cpp +++ b/src/Fancy/SliderIndicator.cpp @@ -7,29 +7,20 @@ #include #include #include -#include "Core/Defs.h" -#include "Core/Palette/Palette.h" + +#include "Defs.h" +#include "Palette.h" namespace fancy { - namespace - { - constexpr qreal borderSpace = 1.0; - constexpr qreal normalRatio = 10.0 / 20.0; - constexpr qreal activeRatio = 14.0 / 20.0; - constexpr qreal pressedRatio = 8.0 / 20.0; - } - SliderIndicator::SliderIndicator(QWidget *parent) : QWidget(parent), _ani(new QVariantAnimation(this)) { QRectF rect_ = rect(); - const qreal inset = borderSpace / 2; - rect_.adjust(inset, inset, -inset, -inset); + rect_.adjust(0.5, 0.5, -0.5, -0.5); _r = qMin(rect_.width(), rect_.height()) / 2; - _center = rect_.center(); - _aniValue = normalRatio; + _aniValue = _r / 2; _ani->setStartValue(0.0f); _ani->setEndValue(0.0f); @@ -45,29 +36,21 @@ namespace fancy ); } - void SliderIndicator::animateState() - { - _ani->stop(); - _ani->setStartValue(_aniValue); - _ani->setEndValue(_pressed ? pressedRatio : (_isContains ? activeRatio : normalRatio)); - _ani->start(); - } - -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) void SliderIndicator::enterEvent(QEnterEvent *event) -#else - void SliderIndicator::enterEvent(QEvent *event) -#endif { _isContains = true; - animateState(); + _ani->setStartValue(_r / 2); + _ani->setEndValue(_r / 1.3); + _ani->start(); QWidget::enterEvent(event); } void SliderIndicator::leaveEvent(QEvent *event) { _isContains = false; - animateState(); + _ani->setStartValue(_aniValue); + _ani->setEndValue(_r / 2); + _ani->start(); QWidget::leaveEvent(event); } @@ -75,18 +58,20 @@ namespace fancy { if (event->button() == Qt::LeftButton) { - _pressed = true; - animateState(); + _ani->setStartValue(_aniValue); + _ani->setEndValue(_r / 2.8); + _ani->start(); } QWidget::mousePressEvent(event); } void SliderIndicator::mouseReleaseEvent(QMouseEvent *event) { - if (event->button() == Qt::LeftButton) + if (_isContains && event->button() == Qt::LeftButton) { - _pressed = false; - animateState(); + _ani->setStartValue(_r / 2.5); + _ani->setEndValue(_r / 1.3); + _ani->start(); } // 不在里面直接触发leaveEvent return QWidget::mouseReleaseEvent(event); @@ -96,10 +81,10 @@ namespace fancy { QWidget::resizeEvent(event); QRectF rect_ = rect(); - const qreal inset = borderSpace / 2; - rect_.adjust(inset, inset, -inset, -inset); - _r = qMax(0.0, qMin(rect_.width(), rect_.height()) / 2); + rect_.adjust(0.5, 0.5, -0.5, -0.5); + _r = qMin(rect_.width(), rect_.height()) / 2; _center = rect_.center(); + _aniValue = _r / 2; } void SliderIndicator::paintEvent(QPaintEvent *paint_event) @@ -107,11 +92,11 @@ namespace fancy QPainter painter(this); painter.setRenderHints(QPainter::RenderHint::Antialiasing); auto &palette = Palette::palette(); - painter.setPen(Qt::NoPen); + painter.setPen(QPen(palette[ColorRole::Border], 1)); painter.setBrush(palette[ColorRole::Button]); painter.drawEllipse(_center, _r, _r); painter.setPen(Qt::NoPen); painter.setBrush(palette[ColorRole::AppAccent]); - painter.drawEllipse(_center, _r * _aniValue, _r * _aniValue); + painter.drawEllipse(_center, _aniValue, _aniValue); } } // fancy diff --git a/src/Fancy/Widgets/Slider/SliderIndicator.h b/src/Fancy/SliderIndicator.h similarity index 78% rename from src/Fancy/Widgets/Slider/SliderIndicator.h rename to src/Fancy/SliderIndicator.h index ffa5d1d7..5fb5e184 100644 --- a/src/Fancy/Widgets/Slider/SliderIndicator.h +++ b/src/Fancy/SliderIndicator.h @@ -21,20 +21,12 @@ namespace fancy Qt::Orientation _orientation = Qt::Orientation::Horizontal; QVariantAnimation *_ani = nullptr; qreal _r; - qreal _aniValue; // 内圆与外圆的半径比,调整尺寸时保持动画比例 + qreal _aniValue; QPointF _center; bool _isContains = false; - bool _pressed = false; - - void animateState(); protected: -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) void enterEvent(QEnterEvent *event) override; -#else - void enterEvent(QEvent *event) override; -#endif - void leaveEvent(QEvent *event) override; diff --git a/src/Fancy/SliderStyle.cpp b/src/Fancy/SliderStyle.cpp new file mode 100644 index 00000000..784fd08c --- /dev/null +++ b/src/Fancy/SliderStyle.cpp @@ -0,0 +1,109 @@ +// +// Created by TX on 2025/9/26. +// + +#include "SliderStyle.h" +#include +#include + +#include "Defs.h" +#include "Palette.h" +#include "SliderIndicator.h" + +namespace fancy +{ + SliderStyle::SliderStyle(QSlider *parent, Qt::Orientation orientation) : + QProxyStyle(nullptr), + _indicator(new SliderIndicator(parent)) + { + parent->installEventFilter(this); + setParent(parent); + _indicator->_orientation = orientation; + + connect(&Palette::palette(), &Palette::appThemeChange, parent, QOverload<>::of(&QSlider::update)); + connect(&Palette::palette(), &Palette::appColorChange, parent, QOverload<>::of(&QSlider::update)); + } + + void SliderStyle::setOrientation(Qt::Orientation orientation) + { + _indicator->_orientation = orientation; + } + + bool SliderStyle::eventFilter(QObject *obj, QEvent *event) + { + if (obj == parent() && (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease)) + { + if (auto *mouseEvent = dynamic_cast(event)) + { + if (mouseEvent->button() == Qt::LeftButton && event->type() == QEvent::MouseButtonPress) + _indicator->mousePressEvent(mouseEvent); + else if (event->type() == QEvent::MouseButtonRelease && !_indicator->geometry().contains(mouseEvent->pos())) + _indicator->leaveEvent(event); + } + } + return QProxyStyle::eventFilter(obj, event); + } + + void SliderStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget) const + { + if (control == CC_Slider) + { + // 滑槽与指示器区域 + const QRect grooveRect = QProxyStyle::subControlRect(CC_Slider, qstyleoption_cast(option), SC_SliderGroove, widget); + const QRect handleRect = QProxyStyle::subControlRect(CC_Slider, qstyleoption_cast(option), SC_SliderHandle, widget); + + auto &palette = Palette::palette(); + painter->save(); + painter->setRenderHints(QPainter::Antialiasing); + + constexpr qreal penRatio = 0.278; + bool isHorizontal = _indicator->_orientation == Qt::Horizontal; + + qreal penWidth = (isHorizontal ? handleRect.height() : handleRect.width()) * penRatio; + qreal correctXY = penWidth / 2; // 修正画笔圆角半径误差 + qreal center = isHorizontal ? grooveRect.center().y() : grooveRect.center().x(); + + QLineF primaryLine = isHorizontal + ? QLineF(correctXY, center, handleRect.left() + correctXY, center) + : QLineF(center, handleRect.bottom() - correctXY, center, grooveRect.bottom() - correctXY); + + QLineF secondaryLine = isHorizontal + ? QLineF(handleRect.right() - correctXY, center, grooveRect.right() - correctXY, center) + : QLineF(center, correctXY, center, handleRect.top() + correctXY); + + // 画线 + QPen pen(palette[ColorRole::AppAccent], penWidth, Qt::PenStyle::SolidLine, Qt::PenCapStyle::RoundCap); + painter->setPen(pen); + painter->drawLine(primaryLine); + pen.setColor(palette[ColorRole::DisEnabled].darker(120)); + painter->setPen(pen); + painter->drawLine(secondaryLine); + + + // 更新指示器位置 + int side = isHorizontal ? grooveRect.height() : grooveRect.width(); + + int offset = (isHorizontal + ? handleRect.x() - (side - handleRect.width()) / 2 + : handleRect.y() - (side - handleRect.height()) / 2); + if (offset < 0) + offset = 0; + else if (isHorizontal && offset + side > widget->width()) + offset = widget->width() - grooveRect.height(); + + else if (!isHorizontal && offset + side > widget->height()) + offset = widget->height() - grooveRect.width(); + QRect indRect = (isHorizontal + ? QRect(QPoint(offset, 0), QSize(grooveRect.height(), grooveRect.height())) + : QRect(QPoint(0, offset), QSize(grooveRect.width(), grooveRect.width()))); + + if (_indicator->geometry() != indRect) + _indicator->setGeometry(indRect); + + painter->restore(); + return; + } + + return QProxyStyle::drawComplexControl(control, option, painter, widget); + } +} // fancy diff --git a/src/Fancy/Widgets/Slider/SliderStyle.h b/src/Fancy/SliderStyle.h similarity index 70% rename from src/Fancy/Widgets/Slider/SliderStyle.h rename to src/Fancy/SliderStyle.h index 4cc2e6c0..bb02f8bd 100644 --- a/src/Fancy/Widgets/Slider/SliderStyle.h +++ b/src/Fancy/SliderStyle.h @@ -24,11 +24,6 @@ namespace fancy bool eventFilter(QObject *obj, QEvent *event) override; - int pixelMetric(PixelMetric metric, const QStyleOption *option = nullptr, const QWidget *widget = nullptr) const override; - - QRect subControlRect(ComplexControl control, const QStyleOptionComplex *option, SubControl subControl, - const QWidget *widget = nullptr) const override; - protected: void drawComplexControl(ComplexControl control, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget) const override; diff --git a/src/Fancy/Widgets/Window/StackWindow.cpp b/src/Fancy/StackWindow.cpp similarity index 92% rename from src/Fancy/Widgets/Window/StackWindow.cpp rename to src/Fancy/StackWindow.cpp index 11978fb2..b5ac5456 100644 --- a/src/Fancy/Widgets/Window/StackWindow.cpp +++ b/src/Fancy/StackWindow.cpp @@ -7,10 +7,13 @@ #include #include -#include "Widgets//PushButton/TransparentButton.h" -#include "Widgets/Sidebar/Sidebar.h" -#include "Widgets/TitleBar/TitleBar.h" -#include "Widgets/TitleBar/Button/MaximizeButton.h" +#include "CloseButton.h" +#include "MaximizeButton.h" +#include "MinimizeButton.h" +#include "PushButton.h" +#include "Sidebar.h" +#include "TitleBar.h" +#include "TransparentButton.h" namespace fancy { diff --git a/src/Fancy/Widgets/Window/StackWindow.h b/src/Fancy/StackWindow.h similarity index 94% rename from src/Fancy/Widgets/Window/StackWindow.h rename to src/Fancy/StackWindow.h index 9138cc19..c8526024 100644 --- a/src/Fancy/Widgets/Window/StackWindow.h +++ b/src/Fancy/StackWindow.h @@ -5,7 +5,7 @@ #ifndef QWIDGET_FANCYUI_STACKWINDOW_H #define QWIDGET_FANCYUI_STACKWINDOW_H #include "MSWindow.h" -#include "Widgets/PushButton/PushButton.h" +#include "PushButton.h" namespace fancy { diff --git a/src/Fancy/utils/SvgParsing.h b/src/Fancy/SvgParsing.h similarity index 100% rename from src/Fancy/utils/SvgParsing.h rename to src/Fancy/SvgParsing.h diff --git a/src/Fancy/Widgets/SvgWidget/SvgWidget.cpp b/src/Fancy/SvgWidget.cpp similarity index 94% rename from src/Fancy/Widgets/SvgWidget/SvgWidget.cpp rename to src/Fancy/SvgWidget.cpp index 14cf4784..684073ba 100644 --- a/src/Fancy/Widgets/SvgWidget/SvgWidget.cpp +++ b/src/Fancy/SvgWidget.cpp @@ -6,9 +6,9 @@ #define MAGIC_ENUM_RANGE_MIN 0 #define MAGIC_ENUM_RANGE_MAX 3000 #include -#include "Core/Palette/Palette.h" -#include "Core/Theme/ThemeModeController.h" -#include "Icon/IconEngine.h" +#include "IconEngine.h" +#include "Palette.h" +#include "ThemeModeController.h" namespace fancy { diff --git a/src/Fancy/Widgets/SvgWidget/SvgWidget.h b/src/Fancy/SvgWidget.h similarity index 93% rename from src/Fancy/Widgets/SvgWidget/SvgWidget.h rename to src/Fancy/SvgWidget.h index 1b6d4f48..ef879554 100644 --- a/src/Fancy/Widgets/SvgWidget/SvgWidget.h +++ b/src/Fancy/SvgWidget.h @@ -7,9 +7,9 @@ #include -#include "Core/Defs.h" -#include "utils/SvgParsing.h" -#include "Icon/IconEnums.h" +#include "Defs.h" +#include "IconEnums.h" +#include "SvgParsing.h" namespace fancy { diff --git a/src/Fancy/Core/SystemAccessor.cpp b/src/Fancy/SystemAccessor.cpp similarity index 96% rename from src/Fancy/Core/SystemAccessor.cpp rename to src/Fancy/SystemAccessor.cpp index a87ece2d..53a54b49 100644 --- a/src/Fancy/Core/SystemAccessor.cpp +++ b/src/Fancy/SystemAccessor.cpp @@ -164,13 +164,6 @@ namespace fancy return ver; } - QSize SystemAccessor::windowResizeBorderSize() - { - const int width = ::GetSystemMetrics(SM_CXSIZEFRAME); - const int height = ::GetSystemMetrics(SM_CYSIZEFRAME); - return {qMax(1, width), qMax(1, height)}; - } - bool SystemAccessor::isWindows11() { auto [Major,Minor,Build] = windowsVersion(); diff --git a/src/Fancy/Core/SystemAccessor.h b/src/Fancy/SystemAccessor.h similarity index 95% rename from src/Fancy/Core/SystemAccessor.h rename to src/Fancy/SystemAccessor.h index 356da039..b481e93b 100644 --- a/src/Fancy/Core/SystemAccessor.h +++ b/src/Fancy/SystemAccessor.h @@ -7,7 +7,6 @@ #include #include -#include #ifdef Q_OS_WIN #define WIN32_LEAN_AND_MEAN #include @@ -96,12 +95,6 @@ namespace fancy */ static const WindowsVersion &windowsVersion(); //获取Windows版本信息 //单例模式,首次初始化 - /** - * 使用 GetSystemMetrics() 获取系统缩放边框尺寸,单位为 Win32 像素。 - * 不进行 Qt DPR 换算,也不保证按窗口所在显示器的 DPI 查询。 - */ - static QSize windowResizeBorderSize(); - /** * @return 是否是Windows11操作系统 若是则返回true否则返回false */ diff --git a/src/Fancy/Core/Theme/SystemThemeMonitor.cpp b/src/Fancy/SystemThemeMonitor.cpp similarity index 99% rename from src/Fancy/Core/Theme/SystemThemeMonitor.cpp rename to src/Fancy/SystemThemeMonitor.cpp index 99d3b0b3..2ca52525 100644 --- a/src/Fancy/Core/Theme/SystemThemeMonitor.cpp +++ b/src/Fancy/SystemThemeMonitor.cpp @@ -6,8 +6,8 @@ #include +#include "Defs.h" #include "ThemeModeController.h" -#include "Core/Defs.h" namespace fancy { @@ -18,7 +18,8 @@ namespace fancy { ::SetEvent(this->_hEvent_array[2]); } - + + SystemThemeMonitor &SystemThemeMonitor::monitor() { static auto *monitor = new SystemThemeMonitor(qApp); diff --git a/src/Fancy/Core/Theme/SystemThemeMonitor.h b/src/Fancy/SystemThemeMonitor.h similarity index 100% rename from src/Fancy/Core/Theme/SystemThemeMonitor.h rename to src/Fancy/SystemThemeMonitor.h diff --git a/src/Fancy/Widgets/TextEdit/TextEdit.cpp b/src/Fancy/TextEdit.cpp similarity index 92% rename from src/Fancy/Widgets/TextEdit/TextEdit.cpp rename to src/Fancy/TextEdit.cpp index d3717d41..42bbd19d 100644 --- a/src/Fancy/Widgets/TextEdit/TextEdit.cpp +++ b/src/Fancy/TextEdit.cpp @@ -4,10 +4,10 @@ #include "TextEdit.h" +#include "Defs.h" +#include "palette.h" +#include "ScrollBar.h" #include "TextEditStyle.h" -#include "Core/Defs.h" -#include "Core/Palette/Palette.h" -#include "Widgets/ScrollBar/ScrollBar.h" namespace fancy { diff --git a/src/Fancy/Widgets/TextEdit/TextEdit.h b/src/Fancy/TextEdit.h similarity index 100% rename from src/Fancy/Widgets/TextEdit/TextEdit.h rename to src/Fancy/TextEdit.h diff --git a/src/Fancy/Widgets/TextEdit/TextEditStyle.cpp b/src/Fancy/TextEditStyle.cpp similarity index 96% rename from src/Fancy/Widgets/TextEdit/TextEditStyle.cpp rename to src/Fancy/TextEditStyle.cpp index 52b685ed..f5c84646 100644 --- a/src/Fancy/Widgets/TextEdit/TextEditStyle.cpp +++ b/src/Fancy/TextEditStyle.cpp @@ -10,10 +10,9 @@ #include #include #include -#include "Core/Defs.h" -#include "Core/ControlState/ControlState.h" -#include "Core/Palette/Palette.h" -#include "utils/General.h" +#include "ControlState.h" +#include "Defs.h" +#include "palette.h" namespace fancy { diff --git a/src/Fancy/Widgets/TextEdit/TextEditStyle.h b/src/Fancy/TextEditStyle.h similarity index 100% rename from src/Fancy/Widgets/TextEdit/TextEditStyle.h rename to src/Fancy/TextEditStyle.h diff --git a/src/Fancy/Widgets/TextLabel/TextLabel.cpp b/src/Fancy/TextLabel.cpp similarity index 97% rename from src/Fancy/Widgets/TextLabel/TextLabel.cpp rename to src/Fancy/TextLabel.cpp index 773acdb6..08f76730 100644 --- a/src/Fancy/Widgets/TextLabel/TextLabel.cpp +++ b/src/Fancy/TextLabel.cpp @@ -3,7 +3,9 @@ // #include "TextLabel.h" +#include #include + #include "TextLabelStyle.h" namespace fancy diff --git a/src/Fancy/Widgets/TextLabel/TextLabel.h b/src/Fancy/TextLabel.h similarity index 100% rename from src/Fancy/Widgets/TextLabel/TextLabel.h rename to src/Fancy/TextLabel.h diff --git a/src/Fancy/Widgets/TextLabel/TextLabelStyle.cpp b/src/Fancy/TextLabelStyle.cpp similarity index 88% rename from src/Fancy/Widgets/TextLabel/TextLabelStyle.cpp rename to src/Fancy/TextLabelStyle.cpp index c746adc3..95632337 100644 --- a/src/Fancy/Widgets/TextLabel/TextLabelStyle.cpp +++ b/src/Fancy/TextLabelStyle.cpp @@ -4,10 +4,9 @@ #include "TextLabelStyle.h" #include -#include -#include "Core/Defs.h" -#include "Core/Palette/Palette.h" +#include "Defs.h" +#include "Palette.h" namespace fancy { @@ -22,13 +21,14 @@ namespace fancy void TextLabelStyle::drawItemText(QPainter *painter, const QRect &rect, int flags, const QPalette &pal, bool enabled, const QString &text, QPalette::ColorRole textRole) const { + Q_UNUSED(flags) Q_UNUSED(pal) Q_UNUSED(textRole) painter->save(); painter->setRenderHint(QPainter::RenderHint::Antialiasing, true); Palette &palette = Palette::palette(); painter->setPen(enabled ? palette[ColorRole::Text] : palette[ColorRole::DisEnabled]); - painter->drawText(rect, flags, text); + painter->drawText(rect, _parent->alignment(), text); painter->restore(); } } // fancy diff --git a/src/Fancy/Widgets/TextLabel/TextLabelStyle.h b/src/Fancy/TextLabelStyle.h similarity index 100% rename from src/Fancy/Widgets/TextLabel/TextLabelStyle.h rename to src/Fancy/TextLabelStyle.h diff --git a/src/Fancy/Widgets/PushButton/ThemeButton.cpp b/src/Fancy/ThemeButton.cpp similarity index 98% rename from src/Fancy/Widgets/PushButton/ThemeButton.cpp rename to src/Fancy/ThemeButton.cpp index a122b4ab..10782525 100644 --- a/src/Fancy/Widgets/PushButton/ThemeButton.cpp +++ b/src/Fancy/ThemeButton.cpp @@ -4,8 +4,8 @@ #include "ThemeButton.h" +#include "Icon.h" #include "ThemePushButtonStyle.h" -#include "Icon/Icon.h" namespace fancy { diff --git a/src/Fancy/Widgets/PushButton/ThemeButton.h b/src/Fancy/ThemeButton.h similarity index 98% rename from src/Fancy/Widgets/PushButton/ThemeButton.h rename to src/Fancy/ThemeButton.h index ec76af00..5af78eb8 100644 --- a/src/Fancy/Widgets/PushButton/ThemeButton.h +++ b/src/Fancy/ThemeButton.h @@ -5,7 +5,7 @@ #ifndef QWIDGET_FANCYUI_THEMEBUTTON_H #define QWIDGET_FANCYUI_THEMEBUTTON_H #include -#include "Core/Defs.h" +#include "Defs.h" namespace fancy { diff --git a/src/Fancy/Core/Theme/ThemeModeController.cpp b/src/Fancy/ThemeModeController.cpp similarity index 99% rename from src/Fancy/Core/Theme/ThemeModeController.cpp rename to src/Fancy/ThemeModeController.cpp index 7f451bca..19cf2614 100644 --- a/src/Fancy/Core/Theme/ThemeModeController.cpp +++ b/src/Fancy/ThemeModeController.cpp @@ -3,8 +3,8 @@ // #include "ThemeModeController.h" +#include "Defs.h" #include "SystemThemeMonitor.h" -#include "Core/Defs.h" namespace fancy { diff --git a/src/Fancy/Core/Theme/ThemeModeController.h b/src/Fancy/ThemeModeController.h similarity index 100% rename from src/Fancy/Core/Theme/ThemeModeController.h rename to src/Fancy/ThemeModeController.h diff --git a/src/Fancy/Widgets/PushButton/ThemePushButtonStyle.cpp b/src/Fancy/ThemePushButtonStyle.cpp similarity index 95% rename from src/Fancy/Widgets/PushButton/ThemePushButtonStyle.cpp rename to src/Fancy/ThemePushButtonStyle.cpp index e5b52064..57061c41 100644 --- a/src/Fancy/Widgets/PushButton/ThemePushButtonStyle.cpp +++ b/src/Fancy/ThemePushButtonStyle.cpp @@ -3,16 +3,13 @@ // #include "ThemePushButtonStyle.h" - -#include #include #include #include -#include "Core/Defs.h" -#include "Core/ControlState/ControlState.h" -#include "Core/Palette/Palette.h" -#include "utils/General.h" +#include "ControlState.h" +#include "Defs.h" +#include "Palette.h" namespace fancy { diff --git a/src/Fancy/Widgets/PushButton/ThemePushButtonStyle.h b/src/Fancy/ThemePushButtonStyle.h similarity index 100% rename from src/Fancy/Widgets/PushButton/ThemePushButtonStyle.h rename to src/Fancy/ThemePushButtonStyle.h diff --git a/src/Fancy/Widgets/TitleBar/TitleBar.cpp b/src/Fancy/TitleBar.cpp similarity index 93% rename from src/Fancy/Widgets/TitleBar/TitleBar.cpp rename to src/Fancy/TitleBar.cpp index 4dc5f7be..59b0ee0b 100644 --- a/src/Fancy/Widgets/TitleBar/TitleBar.cpp +++ b/src/Fancy/TitleBar.cpp @@ -11,10 +11,10 @@ #include #include -#include "Widgets/TextLabel/TextLabel.h" -#include "Widgets/TitleBar/Button/CloseButton.h" -#include "Widgets/TitleBar/Button/MaximizeButton.h" -#include "Widgets/TitleBar/Button/MinimizeButton.h" +#include "CloseButton.h" +#include "MaximizeButton.h" +#include "MinimizeButton.h" +#include "TextLabel.h" namespace fancy { @@ -37,10 +37,7 @@ namespace fancy size_policy.setHeightForWidth(sizePolicy().hasHeightForWidth()); setSizePolicy(size_policy); - QFont titleFont = _textLabel->font(); - titleFont.setPointSize(9); - _textLabel->setFont(titleFont); - + _textLabel->setFont(QFont(_textLabel->font().family(), 9)); _iconLabel->setFixedSize(25, 20); _iconLabel->setScaledContents(true); _iconLabel->setContentsMargins(0, 0, 5, 0); diff --git a/src/Fancy/Widgets/TitleBar/TitleBar.h b/src/Fancy/TitleBar.h similarity index 100% rename from src/Fancy/Widgets/TitleBar/TitleBar.h rename to src/Fancy/TitleBar.h diff --git a/src/Fancy/Widgets/PushButton/TransparentButton.cpp b/src/Fancy/TransparentButton.cpp similarity index 98% rename from src/Fancy/Widgets/PushButton/TransparentButton.cpp rename to src/Fancy/TransparentButton.cpp index b9f7e066..77ea82f9 100644 --- a/src/Fancy/Widgets/PushButton/TransparentButton.cpp +++ b/src/Fancy/TransparentButton.cpp @@ -3,8 +3,8 @@ // #include "TransparentButton.h" +#include "Icon.h" #include "TransparentButtonStyle.h" -#include "Icon/Icon.h" namespace fancy { diff --git a/src/Fancy/Widgets/PushButton/TransparentButton.h b/src/Fancy/TransparentButton.h similarity index 98% rename from src/Fancy/Widgets/PushButton/TransparentButton.h rename to src/Fancy/TransparentButton.h index cf125d7f..fbffd9c9 100644 --- a/src/Fancy/Widgets/PushButton/TransparentButton.h +++ b/src/Fancy/TransparentButton.h @@ -6,7 +6,7 @@ #define QWIDGET_FANCYUI_TRANSPARENTBUTTON_H #include -#include "Core/Defs.h" +#include "Defs.h" namespace fancy { diff --git a/src/Fancy/Widgets/PushButton/TransparentButtonStyle.cpp b/src/Fancy/TransparentButtonStyle.cpp similarity index 96% rename from src/Fancy/Widgets/PushButton/TransparentButtonStyle.cpp rename to src/Fancy/TransparentButtonStyle.cpp index ed5d075b..a8ca99da 100644 --- a/src/Fancy/Widgets/PushButton/TransparentButtonStyle.cpp +++ b/src/Fancy/TransparentButtonStyle.cpp @@ -3,15 +3,13 @@ // #include "TransparentButtonStyle.h" - -#include #include #include #include -#include "Core/Defs.h" -#include "Core/ControlState/ControlState.h" -#include "Core/Palette/Palette.h" +#include "ControlState.h" +#include "Defs.h" +#include "Palette.h" namespace fancy { diff --git a/src/Fancy/Widgets/PushButton/TransparentButtonStyle.h b/src/Fancy/TransparentButtonStyle.h similarity index 100% rename from src/Fancy/Widgets/PushButton/TransparentButtonStyle.h rename to src/Fancy/TransparentButtonStyle.h diff --git a/src/Fancy/Widgets/TitleBar/Button/UniversalTitleBarButton.cpp b/src/Fancy/UniversalTitleBarButton.cpp similarity index 93% rename from src/Fancy/Widgets/TitleBar/Button/UniversalTitleBarButton.cpp rename to src/Fancy/UniversalTitleBarButton.cpp index b56e5f7c..9a37e754 100644 --- a/src/Fancy/Widgets/TitleBar/Button/UniversalTitleBarButton.cpp +++ b/src/Fancy/UniversalTitleBarButton.cpp @@ -4,12 +4,11 @@ #include "UniversalTitleBarButton.h" #include -#include -#include "Core/Defs.h" -#include "Core/Palette/Palette.h" -#include "Icon/IconEngine.h" -#include "Widgets/SvgWidget/SvgWidget.h" +#include "Defs.h" +#include "IconEngine.h" +#include "Palette.h" +#include "SvgWidget.h" namespace fancy { @@ -50,11 +49,7 @@ namespace fancy } } -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) void UniversalTitleBarButton::enterEvent(QEnterEvent *event) -#else - void UniversalTitleBarButton::enterEvent(QEvent *event) -#endif { _state = TitleBtnState::Hover; update(); diff --git a/src/Fancy/Widgets/TitleBar/Button/UniversalTitleBarButton.h b/src/Fancy/UniversalTitleBarButton.h similarity index 94% rename from src/Fancy/Widgets/TitleBar/Button/UniversalTitleBarButton.h rename to src/Fancy/UniversalTitleBarButton.h index 29042e2c..5bd0c10e 100644 --- a/src/Fancy/Widgets/TitleBar/Button/UniversalTitleBarButton.h +++ b/src/Fancy/UniversalTitleBarButton.h @@ -49,11 +49,7 @@ namespace fancy protected: [[nodiscard]] virtual QBrush maskBrush() const; -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) void enterEvent(QEnterEvent *event) override; -#else - void enterEvent(QEvent *event) override; -#endif void leaveEvent(QEvent *event) override; diff --git a/src/Fancy/Widgets/BlurCard/BlurCard.cpp b/src/Fancy/Widgets/BlurCard/BlurCard.cpp deleted file mode 100644 index 11c35930..00000000 --- a/src/Fancy/Widgets/BlurCard/BlurCard.cpp +++ /dev/null @@ -1,68 +0,0 @@ -// -// Created by TX on 2025/10/30. -// - -#include "BlurCard.h" - -#include -#include - -#include "Core/Defs.h" -#include "utils/General.h" -#include "mini_blur_qimage.h" - -namespace fancy -{ - BlurCard::BlurCard(QWidget *parent) : - BlurCard(parent, nullptr) {} - - BlurCard::BlurCard(QWidget *parent, QWidget *blurredObj) : - QPushButton(parent), - _blurRadius(10), - _radius(10), - _blurredObj(blurredObj), - _resolutionScale(1.0) {} - - void BlurCard::setResolutionScale(qreal scale) - { - if (qIsFinite(scale) && scale > 0) - _resolutionScale = scale; - } - - void BlurCard::blur() - { - if (_blurredObj) - { - const QImage image = renderWidgetRegion(_blurredObj, getWidgetRectInAncestor(this, _blurredObj), _resolutionScale); - const int pixelRadius = _blurRadius > 0 - ? qMax(1, qRound(qMin(static_cast(_blurRadius) * image.devicePixelRatio(), 2047.0))) - : 0; - if (image.isNull() || pixelRadius == 0) - setPixmap(QPixmap::fromImage(image)); - else - { - // 同一调用线程内的卡片复用一个 Context;首次使用时创建 3 个工作线程。 - static thread_local mini_blur::Context blurContext(4); - const int kernelSize = pixelRadius * 2 + 1; - constexpr auto border = mini_blur::Border::Replicate; - mini_blur::boxBlurTo(blurContext, image, _blurStage1, kernelSize, kernelSize, border); - mini_blur::boxBlurTo(blurContext, _blurStage1, _blurStage2, kernelSize, kernelSize, border); - mini_blur::boxBlurTo(blurContext, _blurStage2, _blurOutput, kernelSize, kernelSize, border); - setPixmap(QPixmap::fromImage(_blurOutput)); - } - update(); - } - } - - void BlurCard::paintEvent(QPaintEvent *event) - { - Q_UNUSED(event); - QPainter painter(this); - painter.setRenderHints(QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform); - QPainterPath path; - path.addRoundedRect(rect(), _radius, _radius); - painter.setClipPath(path); - if (!_pixmap.isNull()) - painter.drawPixmap(rect(), _pixmap); - } -} // fancy diff --git a/src/Fancy/Widgets/CheckBox/CheckBox.cpp b/src/Fancy/Widgets/CheckBox/CheckBox.cpp deleted file mode 100644 index 443985ab..00000000 --- a/src/Fancy/Widgets/CheckBox/CheckBox.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// -// Created by TX on 2025/8/30. -// - -#include "CheckBox.h" -#include "CheckBoxStyle.h" - -namespace fancy -{ - CheckBox::CheckBox(QWidget *parent, bool smallIndicatorSize) : - QCheckBox(parent) - { - smallIndicatorSize ? setStyle(new CheckBoxStyle_SM(this)) : setStyle(new CheckBoxStyle(this)); - QFont font = this->font(); - font.setPointSize(smallIndicatorSize ? 10 : 12); - setFont(font); - } - - CheckBox::CheckBox(const QString &text, QWidget *parent, bool smallIndicatorSize) : - CheckBox(parent, smallIndicatorSize) - { - setText(text); - } - - void CheckBox::setAnimationEnabled(bool enabled) - { - if (auto *checkBoxStyle = qobject_cast(style())) - checkBoxStyle->setAnimationEnabled(enabled); - else if (auto *smallCheckBoxStyle = qobject_cast(style())) - smallCheckBoxStyle->setAnimationEnabled(enabled); - } - - bool CheckBox::isAnimationEnabled() const - { - if (auto *checkBoxStyle = qobject_cast(style())) - return checkBoxStyle->isAnimationEnabled(); - if (auto *smallCheckBoxStyle = qobject_cast(style())) - return smallCheckBoxStyle->isAnimationEnabled(); - return false; - } -} // fancy diff --git a/src/Fancy/Widgets/CheckBox/CheckBoxStyle.cpp b/src/Fancy/Widgets/CheckBox/CheckBoxStyle.cpp deleted file mode 100644 index b2b11bf8..00000000 --- a/src/Fancy/Widgets/CheckBox/CheckBoxStyle.cpp +++ /dev/null @@ -1,319 +0,0 @@ -// -// Created by TX on 2025/8/26. -// - -#include "CheckBoxStyle.h" -#include -#include "Core/Defs.h" -#include "Core/ControlState/ControlState.h" -#include "Core/Palette/Palette.h" - -#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) -#define CHECKBOX_STATE_CHANGED &QCheckBox::checkStateChanged -#else -#define CHECKBOX_STATE_CHANGED &QCheckBox::stateChanged -#endif - -/** - * - * @param CLASS 对象作用域 - * @param indicatorSide 指示器边长 - * @param p1x 勾第一个点的x坐标 计算公式:p1x = 0.23875 * indicatorSide - * @param p1y 勾第一个点的y坐标 计算公式:p1y = 0.58125 * indicatorSide - * @param p2x 勾第二个点的x坐标 计算公式:p2x = 0.42625 * indicatorSide - * @param p2y 勾第二个点的y坐标 计算公式:p2y = 0.8 * indicatorSide - * @param p3x 勾第三个点的x坐标 计算公式:p3x = 0.86375 * indicatorSide - * @param p3y 勾第三个点的y坐标 计算公式:p3y = 0.26875 * indicatorSide - * @param radiusAniTime 选中填充动画运行时间,单位:ms - * @param p1AniTime 勾选动画第一段时间,单位:ms - * @param p2AniTime 勾选动画第二段时间,单位:ms - * @param partialAniTime 半选横线动画时间,单位:ms - * @param aniRadiusEnd 选中填充动画的结束值(半径动画的半径最大值)即选中方框外接圆半径 - * __________________ - * | | - * | /<-|-------p3 - * p1 -------|-->\ / | - * | \ / | - * p2-------|---->\ / | - * |________________| - * - */ -#define CHECKBOX_STYLE_INIT(CLASS,indicatorSide,p1x,p1y,p2x,p2y,p3x,p3y,radiusAniTime,p1AniTime,p2AniTime,partialAniTime,aniRadiusEnd) \ - QProxyStyle(nullptr), \ - _fill_radius(aniRadiusEnd), \ - _checkbox_side(indicatorSide), \ - _p1(p1x, p1y), \ - _p2(p2x, p2y), \ - _circum_radius(0), \ - _tick_p1(_p1), \ - _tick_p2(_p2), \ - _partial_progress(0.0), \ - _tick_animation_1(new QVariantAnimation(this)), \ - _tick_animation_2(new QVariantAnimation(this)), \ - _partial_animation(new QVariantAnimation(this)), \ - _radius_animation(new QVariantAnimation(this)), \ - _tick_animation_group(new QSequentialAnimationGroup(this)), \ - _device(parent) \ -{ \ - this->setParent(parent);\ - _radius_animation->setDuration(radiusAniTime); \ - _radius_animation->setStartValue(0); \ - _radius_animation->setEndValue(aniRadiusEnd); \ - _tick_animation_1->setDuration(p1AniTime); \ - _tick_animation_1->setStartValue(_p1); \ - _tick_animation_1->setEndValue(_p2); \ - _tick_animation_2->setDuration(p2AniTime); \ - _tick_animation_2->setStartValue(_p2); \ - _tick_animation_2->setEndValue(QPointF(p3x, p3y)); \ - _tick_animation_group->addAnimation(_tick_animation_1); \ - _tick_animation_group->addAnimation(_tick_animation_2); \ - _partial_animation->setDuration(partialAniTime); \ - _partial_animation->setStartValue(0.0); \ - _partial_animation->setEndValue(1.0); \ - connect(_radius_animation, &QVariantAnimation::valueChanged, this, &CLASS::setCircumRadius); \ - connect(_tick_animation_1, &QVariantAnimation::valueChanged, this, &CLASS::setTickP1); \ - connect(_tick_animation_2, &QVariantAnimation::valueChanged, this, &CLASS::setTickP2); \ - connect(_partial_animation, &QVariantAnimation::valueChanged, this, [this](const QVariant &value) { \ - _partial_progress = value.toReal(); \ - _device->update(); \ - }); \ - auto updateCheckState = [this](int checkState) { \ - if (!_animation_enabled) \ - { \ - syncCheckState(); \ - return; \ - } \ - _tick_animation_group->stop(); \ - _partial_animation->stop(); \ - _tick_animation_1->setCurrentTime(0); \ - _tick_animation_2->setCurrentTime(0); \ - _partial_animation->setCurrentTime(0); \ - _tick_p1 = _p1; \ - _tick_p2 = _p2; \ - _partial_progress = 0.0; \ - _radius_animation->stop(); \ - _radius_animation->setStartValue(_circum_radius); \ - _radius_animation->setEndValue(checkState == Qt::Unchecked ? 0.0 : qreal(aniRadiusEnd)); \ - _radius_animation->start(); \ - if (checkState == Qt::Checked) \ - _tick_animation_group->start(); \ - else if (checkState == Qt::PartiallyChecked) \ - _partial_animation->start(); \ - _device->update(); \ - }; \ - connect(parent, CHECKBOX_STATE_CHANGED, this, updateCheckState); \ - syncCheckState(); \ - connect(&Palette::palette(), &Palette::appThemeChange, parent, QOverload<>::of(&QCheckBox::update)); \ - connect(&Palette::palette(), &Palette::appColorChange, parent, QOverload<>::of(&QCheckBox::update)); \ -} -#define CHECKBOX_STYLE_ANIMATION_METHODS(CLASS) \ - void CLASS::setAnimationEnabled(bool enabled) \ - { \ - if (_animation_enabled == enabled) \ - return; \ - _animation_enabled = enabled; \ - if (!enabled) \ - syncCheckState(); \ - } \ - void CLASS::syncCheckState() \ - { \ - _radius_animation->stop(); \ - _tick_animation_group->stop(); \ - _partial_animation->stop(); \ - const auto state = _device->checkState(); \ - _circum_radius = state == Qt::Unchecked ? 0.0 : _fill_radius; \ - _tick_p1 = state == Qt::Checked ? _p2 : _p1; \ - _tick_p2 = state == Qt::Checked ? _tick_animation_2->endValue().toPointF() : _p2; \ - _partial_progress = state == Qt::PartiallyChecked ? 1.0 : 0.0; \ - _device->update(); \ - } - -#define CHECKBOX_STYLE_drawControl \ - if (element == CE_CheckBox) \ - { \ - const auto bopt = qstyleoption_cast(option); \ - if (!bopt) \ - return; \ - constexpr int RADIUS = 2; \ - const QRect &rect = bopt->rect; \ - const int adjust_Y = (rect.height() - _checkbox_side) / 2; \ - QRectF indicatorRect(rect.x(), rect.y() + adjust_Y, _checkbox_side, _checkbox_side); \ - indicatorRect.adjust(0.5, 0.5, -0.5, -0.5); \ - const QPointF center = indicatorRect.center() ;\ - const ControlState state(bopt->state, ControlType::IndicatorCheckable); \ - Palette &p = Palette::palette(); \ - QPen pen; \ - painter->save(); \ - painter->setRenderHint(QPainter::Antialiasing, true); \ - switch (state) \ - { \ - case VisualState::Normal : \ - pen.setColor(p[ColorRole::Border]); \ - break; \ - case VisualState::Pressed : \ - pen.setColor(p[ColorRole::AppAccent]); \ - pen.setWidth(2); \ - indicatorRect.adjust(0.5, 0.5, -0.5, -0.5); \ - break; \ - case VisualState::Hover : [[fallthrough]]; \ - case VisualState::Selected : [[fallthrough]]; \ - case VisualState::SelectedHover : [[fallthrough]]; \ - case VisualState::SelectedPressed : \ - pen.setColor(p[ColorRole::AppAccent]); \ - break; \ - case VisualState::Disabled : [[fallthrough]]; \ - case VisualState::SelectedDisabled : \ - pen.setColor(p[ColorRole::DisEnabled]); \ - break; \ - default : \ - painter->restore(); \ - return; \ - } \ - painter->setPen(pen); \ - painter->drawRoundedRect(indicatorRect, RADIUS, RADIUS); \ - QPainterPath clipPath; \ - clipPath.addRoundedRect(indicatorRect, RADIUS, RADIUS); \ - painter->setClipPath(clipPath); \ - painter->setBrush(p[ColorRole::AppAccent]); \ - if (!state._enable) \ - painter->setBrush(p[ColorRole::DisEnabled]); \ - painter->setPen(Qt::NoPen); \ - painter->drawEllipse(center, _circum_radius, _circum_radius); \ - if (state._on || state._partial) \ - { \ - state._enable ? pen.setColor(p[ColorRole::AppAccentText]) : pen.setColor(Qt::GlobalColor::white); \ - pen.setWidthF(1.5); \ - if (state._over) \ - pen.setWidth(2); \ - if (state == VisualState::SelectedPressed) \ - pen.setWidth(1); \ - painter->setPen(pen); \ - const QPointF offset(rect.x(), rect.y() + adjust_Y); \ - if (state._partial && _partial_progress > 0.0) \ - { \ - const QPointF start(_checkbox_side * 0.25, _checkbox_side * 0.5); \ - const QPointF end(start.x() + _checkbox_side * 0.5 * _partial_progress, start.y()); \ - painter->drawLine(start + offset, end + offset); \ - } \ - else if (state._on && !state._partial && _p1 != _tick_p1) \ - { \ - painter->drawLine(_p1 + offset, _tick_p1 + offset); \ - if (_p2 != _tick_p2) \ - painter->drawLine(_p2 + offset, _tick_p2 + offset); \ - } \ - } \ - pen.setColor(p[state._enable ? ColorRole::Text : ColorRole::DisEnabledText]); \ - pen.setWidth(0); \ - painter->setPen(pen); \ - painter->setClipping(false); \ - const QRect textRect(rect.x() + _checkbox_side + 8, rect.y(), rect.width(), rect.height()); \ - painter->drawText(textRect, Qt::AlignmentFlag::AlignLeft | Qt::AlignmentFlag::AlignVCenter, bopt->text); \ - painter->restore(); \ - return; \ - } \ - QProxyStyle::drawControl(element, option, painter, widget); -#define CHECKBOX_STYLE_pixelMetric(indicatorSide) \ -switch (metric) \ -{ \ - case PM_CheckBoxLabelSpacing : \ - return 10; /* 实际使用8px,预留2px防止文字绘制超出边界*/ \ - case PM_IndicatorWidth : [[fallthrough]]; \ - case PM_IndicatorHeight : \ - return indicatorSide; \ - default : \ - break; \ -} \ -return QProxyStyle::pixelMetric(metric, option, widget); - -namespace fancy -{ - namespace - { - // 指示器几何参数,勾选坐标相对于指示器左上角,单位:px - constexpr int checkbox_indicator_side = 21; - constexpr QPointF checkbox_tick_start(5.01375, 12.20625); - constexpr QPointF checkbox_tick_corner(8.95125, 16.8); - constexpr QPointF checkbox_tick_end(18.13875, 5.64375); - constexpr int checkbox_fill_radius = 15; - - constexpr int small_checkbox_indicator_side = 16; - constexpr QPointF small_checkbox_tick_start(3.82, 9.3); - constexpr QPointF small_checkbox_tick_corner(6.82, 12.8); - constexpr QPointF small_checkbox_tick_end(13.82, 4.3); - constexpr int small_checkbox_fill_radius = 12; - - // 动画时长,单位:ms - constexpr int checkbox_radius_animation_duration = 150; - constexpr int checkbox_tick_animation_1_duration = 115; - constexpr int checkbox_tick_animation_2_duration = 230; - constexpr int checkbox_partial_animation_duration = 345; - - constexpr int small_checkbox_radius_animation_duration = 115; - constexpr int small_checkbox_tick_animation_1_duration = 100; - constexpr int small_checkbox_tick_animation_2_duration = 200; - constexpr int small_checkbox_partial_animation_duration = 115; - } - - CHECKBOX_STYLE_ANIMATION_METHODS(CheckBoxStyle) - CHECKBOX_STYLE_ANIMATION_METHODS(CheckBoxStyle_SM) - - CheckBoxStyle::CheckBoxStyle(QCheckBox *parent) : - CHECKBOX_STYLE_INIT( - CheckBoxStyle, - checkbox_indicator_side, - checkbox_tick_start.x(), - checkbox_tick_start.y(), - checkbox_tick_corner.x(), - checkbox_tick_corner.y(), - checkbox_tick_end.x(), - checkbox_tick_end.y(), - checkbox_radius_animation_duration, - checkbox_tick_animation_1_duration, - checkbox_tick_animation_2_duration, - checkbox_partial_animation_duration, - checkbox_fill_radius) - - void CheckBoxStyle::drawControl( - ControlElement element, - const QStyleOption *option, - QPainter *painter, - const QWidget *widget) const - { - CHECKBOX_STYLE_drawControl - } - - int CheckBoxStyle::pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const - { - CHECKBOX_STYLE_pixelMetric(checkbox_indicator_side) - } - - CheckBoxStyle_SM::CheckBoxStyle_SM(QCheckBox *parent) : - CHECKBOX_STYLE_INIT( - CheckBoxStyle_SM, - small_checkbox_indicator_side, - small_checkbox_tick_start.x(), - small_checkbox_tick_start.y(), - small_checkbox_tick_corner.x(), - small_checkbox_tick_corner.y(), - small_checkbox_tick_end.x(), - small_checkbox_tick_end.y(), - small_checkbox_radius_animation_duration, - small_checkbox_tick_animation_1_duration, - small_checkbox_tick_animation_2_duration, - small_checkbox_partial_animation_duration, - small_checkbox_fill_radius) - - void CheckBoxStyle_SM::drawControl( - ControlElement element, - const QStyleOption *option, - QPainter *painter, - const QWidget *widget) const - { - CHECKBOX_STYLE_drawControl - } - - int CheckBoxStyle_SM::pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const - { - CHECKBOX_STYLE_pixelMetric(small_checkbox_indicator_side) - } -} // fancy diff --git a/src/Fancy/Widgets/ScrollBar/ScrollBarStyle.cpp b/src/Fancy/Widgets/ScrollBar/ScrollBarStyle.cpp deleted file mode 100644 index 539f9023..00000000 --- a/src/Fancy/Widgets/ScrollBar/ScrollBarStyle.cpp +++ /dev/null @@ -1,202 +0,0 @@ -// -// Created by TX on 2025/10/16. -// - -#include "ScrollBarStyle.h" - -#include -#include -#include -#include -#include "Core/Defs.h" -#include "Core/ControlState/ControlState.h" -#include "Core/Palette/Palette.h" - -namespace fancy -{ - namespace - { - constexpr int scrollBarExtent = 12; - constexpr qreal normalThumbWidth = 2; - constexpr qreal activeThumbWidth = 6; - constexpr qreal arrowWidth = 8; - constexpr int arrowLength = 6; - constexpr int arrowEndMargin = 5; - constexpr int arrowThumbGap = 4; - constexpr int endExtent = arrowEndMargin + arrowLength + arrowThumbGap; - } - - ScrollBarStyle::ScrollBarStyle(QScrollBar *parent) : - QProxyStyle(nullptr) - { - setParent(parent); - parent->setAttribute(Qt::WidgetAttribute::WA_OpaquePaintEvent, false); - } - - void ScrollBarStyle::drawComplexControl( - ComplexControl control, - const QStyleOptionComplex *option, - QPainter *painter, - const QWidget *widget) const - { - if (const auto *opt = qstyleoption_cast(option); control == CC_ScrollBar && opt) - { - auto &pal = Palette::palette(); - const ControlState state(opt->state, ControlType::Widget); - const bool isHorizontal = opt->orientation == Qt::Horizontal; - const bool isActive = state == VisualState::Hover || state == VisualState::Pressed || (state == VisualState::Disabled && state._over); - const QRectF sliderRect = subControlRect(control, option, SC_ScrollBarSlider, widget); - const QRectF rect = opt->rect; - const qreal center = isHorizontal ? rect.center().y() : rect.center().x(); // 中心坐标 - const qreal bgRadius = (isHorizontal ? opt->rect.height() : opt->rect.width()) / 2.0; // 背景圆角 - const QRectF addArrowRect = isHorizontal - ? QRectF(rect.right() - arrowEndMargin - arrowLength, center - arrowWidth / 2, arrowLength, arrowWidth) - : QRectF(center - arrowWidth / 2, rect.bottom() - arrowEndMargin - arrowLength, arrowWidth, arrowLength); - const QRectF subArrowRect = isHorizontal - ? QRectF(rect.left() + arrowEndMargin, center - arrowWidth / 2, arrowLength, arrowWidth) - : QRectF(center - arrowWidth / 2, rect.top() + arrowEndMargin, arrowWidth, arrowLength); - const qreal thumbWidth = isActive ? activeThumbWidth : normalThumbWidth; - // 收起时细条靠右(水平滚动条靠下),距外侧边缘 2 px。 - const qreal thumbCenter = isActive - ? center - : (isHorizontal ? rect.bottom() : rect.right()) - 2 - thumbWidth / 2; - const QRectF slider = isHorizontal - ? QRectF(sliderRect.left(), thumbCenter - thumbWidth / 2, sliderRect.width(), thumbWidth) - : QRectF(thumbCenter - thumbWidth / 2, sliderRect.top(), thumbWidth, sliderRect.height()); - const qreal sdRadius = (isHorizontal ? slider.height() : slider.width()) / 2.0; // 滑块圆角 - - const auto drawArrow = [painter, &addArrowRect, &subArrowRect](const bool horizontal) { - if (horizontal) - { - ScrollBarStyle::drawArrow(painter, addArrowRect, Direction::Right); - ScrollBarStyle::drawArrow(painter, subArrowRect, Direction::Left); - } - else - { - ScrollBarStyle::drawArrow(painter, addArrowRect, Direction::Down); - ScrollBarStyle::drawArrow(painter, subArrowRect, Direction::Up); - } - }; - - painter->save(); - painter->setRenderHint(QPainter::Antialiasing, true); - painter->setPen(Qt::NoPen); - - // 绘制背景 - painter->setBrush(pal[isActive ? ColorRole::ScrollBarFocus : ColorRole::ScrollBarNormal]); - painter->drawRoundedRect(opt->rect, bgRadius, bgRadius); - - // 绘制滑块 - painter->setBrush(pal[ColorRole::ScrollBarSlider]); - painter->drawRoundedRect(slider, sdRadius, sdRadius); - - // 绘制箭头 - if (isActive && (isHorizontal ? rect.width() : rect.height()) >= 2 * endExtent) - drawArrow(isHorizontal); - painter->restore(); - return; - } - - QProxyStyle::drawComplexControl(control, option, painter, widget); - } - - - int ScrollBarStyle::pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const - { - switch (metric) - { - case PM_ScrollBarExtent: // 12 垂直滚动条的宽度和水平滚动条的高度 - return scrollBarExtent; - case PM_ScrollBarSliderMin: // 18 垂直滚动条滑块的最小高度和水平滚动条滑块的最小宽度 - return 18; - default: - break; - } - return QProxyStyle::pixelMetric(metric, option, widget); - } - - QRect ScrollBarStyle::subControlRect(ComplexControl control, const QStyleOptionComplex *option, SubControl subControl, const QWidget *widget) const - { - const auto *opt = qstyleoption_cast(option); - if (control != CC_ScrollBar || !opt) - return QProxyStyle::subControlRect(control, option, subControl, widget); - - const bool horizontal = opt->orientation == Qt::Horizontal; - const QRect rect = opt->rect; - const int length = qMax(0, horizontal ? rect.width() : rect.height()); - const int buttonLength = qMin(endExtent, length / 2); - const int grooveLength = length - 2 * buttonLength; - const qint64 range = qMax(qint64(0), qint64(opt->maximum) - qint64(opt->minimum)); - const qint64 pageStep = qMax(0, opt->pageStep); - const int thumbLength = - range == 0 - ? grooveLength - : qMin(grooveLength, qMax(pixelMetric(PM_ScrollBarSliderMin, option, widget), static_cast(pageStep * grooveLength / (range + pageStep)))); - const int thumbStart = - buttonLength + - sliderPositionFromValue(opt->minimum, opt->maximum, opt->sliderPosition, grooveLength - thumbLength, opt->upsideDown); - - // 绘制、点击和拖拽共用同一套端部留白,滑块到端点时仍留 4 px 空隙。 - const auto section = [&](int start, int span) { - const QRect result = horizontal - ? QRect(rect.x() + start, rect.y(), span, rect.height()) - : QRect(rect.x(), rect.y() + start, rect.width(), span); - return horizontal ? visualRect(opt->direction, rect, result) : result; - }; - switch (subControl) - { - case SC_ScrollBarSubLine: - return section(0, buttonLength); - case SC_ScrollBarAddLine: - return section(length - buttonLength, buttonLength); - case SC_ScrollBarGroove: - return section(buttonLength, grooveLength); - case SC_ScrollBarSlider: - return section(thumbStart, thumbLength); - case SC_ScrollBarSubPage: - return section(buttonLength, thumbStart - buttonLength); - case SC_ScrollBarAddPage: - return section(thumbStart + thumbLength, - length - buttonLength - thumbStart - thumbLength); - default: - return QProxyStyle::subControlRect(control, option, subControl, widget); - } - } - - void ScrollBarStyle::drawArrow(QPainter *painter, const QRectF &arrowRect, const Direction dir) - { - // 8 × 6 的圆角实心三角形;只填充,避免描边向外扩张尺寸。 - QPainterPath path; - path.moveTo(1, 0); - path.lineTo(7, 0); - path.quadTo(8, 0, 8, 1); - path.quadTo(8, 1.3, 7.8, 1.6); - path.lineTo(4.8, 5.6); - path.quadTo(4, 6.4, 3.2, 5.6); - path.lineTo(0.2, 1.6); - path.quadTo(0, 1.3, 0, 1); - path.quadTo(0, 0, 1, 0); - path.closeSubpath(); - - QTransform transform; - transform.translate(arrowRect.center().x(), arrowRect.center().y()); - switch (dir) - { - case Direction::Right: - transform.rotate(-90); - break; - case Direction::Left: - transform.rotate(90); - break; - case Direction::Up: - transform.rotate(180); - break; - case Direction::Down: - break; - default: - return; - } - transform.translate(-arrowWidth / 2, -arrowLength / 2.0); - painter->fillPath(transform.map(path), Palette::palette()[ColorRole::ScrollBarSlider]); - } -} // fancy diff --git a/src/Fancy/Widgets/SelectorBar/SelectorBar.cpp b/src/Fancy/Widgets/SelectorBar/SelectorBar.cpp deleted file mode 100644 index 370c97fe..00000000 --- a/src/Fancy/Widgets/SelectorBar/SelectorBar.cpp +++ /dev/null @@ -1,197 +0,0 @@ -#include "SelectorBar.h" - -#include -#include -#include - -#include "SelectorBarItem.h" -#include "private/SelectorBarScrollArea.h" -#include "private/SelectorBarIndicatorLayer.h" - -namespace fancy -{ - SelectorBar::SelectorBar(QWidget *parent) : - QWidget(parent), - _scrollArea(new detail::SelectorBarScrollArea(this)), - _indicatorLayer(new detail::SelectorBarIndicatorLayer(_scrollArea->widget())), - _buttonGroup(new QButtonGroup(this)) - { - _buttonGroup->setExclusive(true); - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - auto *outerLayout = new QVBoxLayout(this); - outerLayout->setContentsMargins(0, 0, 0, 0); - outerLayout->addWidget(_scrollArea); - - connect(_buttonGroup, &QButtonGroup::buttonToggled, this, &SelectorBar::syncSelection); - connect(_scrollArea, &detail::SelectorBarScrollArea::sizeHintChanged, this, &QWidget::updateGeometry); - } - - SelectorBar::~SelectorBar() - { - // 先销毁覆盖层,停止动画和项目事件监听。 - delete _indicatorLayer; - for (const auto *item: _items) - disconnect(item, nullptr, this, nullptr); - disconnect(_buttonGroup, nullptr, this, nullptr); - disconnect(_scrollArea, nullptr, this, nullptr); - } - - void SelectorBar::addItem(SelectorBarItem *item) - { - insertItem(_items.size(), item); - } - - SelectorBarItem *SelectorBar::addItem(const QString &text) - { - auto *item = new SelectorBarItem(text); - addItem(item); - return item; - } - - SelectorBarItem *SelectorBar::addItem(const QString &text, const IconId &icon) - { - auto *item = new SelectorBarItem(text); - item->setIcon(icon); - addItem(item); - return item; - } - - SelectorBarItem *SelectorBar::addItem(const QString &text, const QIcon &icon) - { - auto *item = new SelectorBarItem(text); - item->setIcon(icon); - addItem(item); - return item; - } - - void SelectorBar::insertItem(int index, SelectorBarItem *item) - { - if (!item || _items.contains(item)) - return; - - // 转移按钮组归属,旧栏的布局和项目列表同步移除 - if (auto *group = item->group()) - { - if (auto *owner = qobject_cast(group->parent())) - owner->removeItem(item); - else - group->removeButton(item); - } - - index = qBound(0, index, _items.size()); - _scrollArea->insertWidget(index, item); - item->setChecked(false); - _items.insert(index, item); - _buttonGroup->addButton(item); - - connect(item, &QObject::destroyed, this, [this, item] { - const int index = _items.indexOf(item); - _items.removeOne(item); - // QAbstractButton 析构时已从按钮组移除 - if (!selectedItem() && !_items.isEmpty()) - setSelectedItem(_items.at(qBound(0, index, _items.size() - 1))); - syncSelection(); - }); - connect(item, &QAbstractButton::clicked, this, [this, item] { - // 手动滚动后再次点击已选项目,也应能看到选中指示条 - if (selectedItem() == item) - _scrollArea->ensureItemVisible(item); - }); - syncSelection(); - } - - void SelectorBar::removeItem(SelectorBarItem *item, bool selectFirst) - { - if (!_items.contains(item)) - return; - const int index = _items.indexOf(item); - _buttonGroup->removeButton(item); - _items.removeOne(item); - disconnect(item, nullptr, this, nullptr); - _scrollArea->removeWidget(item); - item->setChecked(false); - // 按参数选择第一项或相邻项;保留回调已指定的选择。 - if (!selectedItem() && !_items.isEmpty()) - setSelectedItem(_items.at(selectFirst ? 0 : qMin(index, _items.size() - 1))); - syncSelection(); - } - - int SelectorBar::itemCount() const - { - return _items.size(); - } - - SelectorBarItem *SelectorBar::itemAt(int index) const - { - return _items.value(index, nullptr); - } - - SelectorBarItem *SelectorBar::selectedItem() const - { - return qobject_cast(_buttonGroup->checkedButton()); - } - - void SelectorBar::setSelectedItem(SelectorBarItem *item) - { - if (!item) - return; // 非空栏不允许清空;空栏本身已无选中项 - if (item && !_items.contains(item)) - { - qWarning() << "SelectorBar::setSelectedItem: item is not in this bar"; - return; - } - if (selectedItem() == item) - return; - - item->setChecked(true); - syncSelection(); - } - - void SelectorBar::syncSelection() - { - // 回调可能再次改选;以组的当前状态为准,而非 buttonToggled 的参数 - auto *item = selectedItem(); - if (!item && !_items.isEmpty()) - { - _items.first()->setChecked(true); - return; // buttonToggled 会同步选择并发出通知,避免重复通知 - } - if (_lastNotifiedItem == item) - return; - _lastNotifiedItem = item; - if (item) - _scrollArea->ensureItemVisible(item); - _indicatorLayer->select(item); - emit selectionChanged(item); - } - - void SelectorBar::setIndicatorSlideEnabled(bool enabled) - { - _indicatorLayer->setSlideEnabled(enabled); - } - - bool SelectorBar::isIndicatorSlideEnabled() const - { - return _indicatorLayer->isSlideEnabled(); - } - - void SelectorBar::setHorizontalScrollBarVisible(bool visible) - { - _scrollArea->setHorizontalScrollBarVisible(visible); - } - - bool SelectorBar::isHorizontalScrollBarVisible() const - { - return _scrollArea->isHorizontalScrollBarVisible(); - } - - QSize SelectorBar::sizeHint() const - { - return _scrollArea->sizeHint(); - } - - QSize SelectorBar::minimumSizeHint() const - { - return {0, sizeHint().height()}; - } -} // fancy 命名空间 diff --git a/src/Fancy/Widgets/SelectorBar/SelectorBar.h b/src/Fancy/Widgets/SelectorBar/SelectorBar.h deleted file mode 100644 index 00f035b4..00000000 --- a/src/Fancy/Widgets/SelectorBar/SelectorBar.h +++ /dev/null @@ -1,158 +0,0 @@ -#ifndef QWIDGET_FANCYUI_SELECTORBAR_H -#define QWIDGET_FANCYUI_SELECTORBAR_H - -#include -#include - -#include "Core/Defs.h" - -class QButtonGroup; - -namespace fancy -{ - class SelectorBarItem; - - namespace detail - { - class SelectorBarScrollArea; - class SelectorBarIndicatorLayer; - } - - class SelectorBar : public QWidget - { - Q_OBJECT - Q_PROPERTY(bool horizontalScrollBarVisible READ isHorizontalScrollBarVisible WRITE setHorizontalScrollBarVisible) - Q_PROPERTY(bool indicatorSlideEnabled READ isIndicatorSlideEnabled WRITE setIndicatorSlideEnabled) - - public: - explicit SelectorBar(QWidget *parent = nullptr); - - ~SelectorBar() override; - - /** - * @brief 在末尾添加项目 - * @param item 要添加的项目;为空或已属于当前栏时不执行任何操作 - * @note 项目属于其他 SelectorBar 时,会自动从旧栏移除,再转移至当前栏 - * - 旧栏会同步清理按钮组、布局、项目列表以及项目到旧栏的信号连接 - * - 若 item 原先在旧栏选中,旧栏优先选中下一项,无下一项时选中前一项,并发出 selectionChanged - * - 若旧栏已无项目,则清空选择并发出 selectionChanged(nullptr);移除未选中项不改变旧栏选择 - * - 添加后 item 的父对象设为当前栏的内容容器,包括从其他栏转入的 item;其生命周期由当前栏管理,随当前栏销毁 - * - 首次添加时自动选中;后续添加保持当前选择。移除或转移后按新的归属管理生命周期 - * - 项目属于普通 QButtonGroup 时,先退出原按钮组;其外部布局和业务记录由使用者管理 - * @note 首项自动选中时,selectionChanged 会在本函数返回前同步发出。 - * 需要接收首次通知时,应在添加项目之前连接信号。 - * 若需先创建项目、页面并建立对应关系,可在连接信号后读取 selectedItem(),主动初始化当前页面。 - */ - void addItem(SelectorBarItem *item); - - /** - * @brief 创建纯文本项目并添加到末尾 - * @param text 项目文本 - * @return 新建的项目,可用于继续设置属性或指定选择 - * @note 生命周期由当前栏管理;首次添加时自动选中,后续添加保持当前选择 - * 首次选择通知在返回项目指针之前发出,连接时机与初始化方式参见 addItem(SelectorBarItem *)。 - * @see addItem(SelectorBarItem *) - */ - SelectorBarItem *addItem(const QString &text); - - /** - * @brief 创建带图标的文本项目并添加到末尾 - * @param text 项目文本 - * @param icon 项目图标,使用 SelectorBarItem 的主题颜色适配逻辑 - * @return 新建的项目,生命周期及选择规则与纯文本重载相同 - */ - SelectorBarItem *addItem(const QString &text, const IconId &icon); - - /** - * @brief 使用调用者提供的 QIcon 创建项目并添加到末尾 - * @param text 项目文本 - * @param icon 项目图标,各状态的图像及配色由调用者提供,不自动生成或随主题重建 - * @return 新建的项目,生命周期及选择规则与纯文本重载相同 - * @note 未提供的状态由 Qt 按默认规则回退或生成效果 - */ - SelectorBarItem *addItem(const QString &text, const QIcon &icon); - - /** @brief 使用枚举图标创建项目,状态配色和主题适配规则与 IconId 重载相同 */ - SelectorBarItem *addItem(const QString &text, AntDesignIcons icon) { return addItem(text, iconId(icon)); } - SelectorBarItem *addItem(const QString &text, BootstrapIcons icon) { return addItem(text, iconId(icon)); } - SelectorBarItem *addItem(const QString &text, IconPark icon) { return addItem(text, iconId(icon)); } - - /** - * @brief 在指定位置插入项目 - * @param index 插入位置,超出范围时限制在 [0, itemCount()] 内 - * @param item 要插入的项目;为空或已属于当前栏时不执行任何操作,不调整已有 item 顺序 - * @note 项目归属、跨栏转移和选中状态的处理与 addItem() 相同 - * 向空栏插入首项时会自动选中,并在本函数返回前同步发出 selectionChanged。 - * @see addItem - */ - void insertItem(int index, SelectorBarItem *item); - - /** - * @brief 移除 item,但不销毁 - * @param item 要移除的项目;不属于当前栏时不执行任何操作 - * @param selectFirst 移除选中项后是否选择剩余第一项;默认 false,优先下一项,否则前一项 - * @note 移除后 item 退出按钮组和布局,断开 item 到当前栏的信号连接,父对象设为空并取消选中 - * - 若移除的是选中项,按 selectFirst 选择替代项;无剩余项目时清空选择 - * - 直接销毁选中项时仍优先选中下一项,否则前一项;选择变化时发出 selectionChanged - * - 移除未选中项不改变当前选择;若移除过程中的回调已指定新选择,则保留该选择 - * - 使用者负责后续销毁项目,或将其加入其他容器 - */ - void removeItem(SelectorBarItem *item, bool selectFirst = false); - - [[nodiscard]] int itemCount() const; - - [[nodiscard]] SelectorBarItem *itemAt(int index) const; - - [[nodiscard]] SelectorBarItem *selectedItem() const; - - /** - * @brief 选中栏内项目;传入 nullptr 时忽略请求,不清空当前选择 - */ - void setSelectedItem(SelectorBarItem *item); - - /** - * @brief 设置是否允许按需显示横向滚动条 - * @param visible true 在内容宽度超出可用空间时显示,false 始终隐藏;默认 false - * @note 隐藏滚动条不禁用滚动,仍可使用滚轮和选中项定位 - */ - void setHorizontalScrollBarVisible(bool visible); - - /** - * @brief 返回是否允许按需显示横向滚动条,不表示滚动条此刻实际可见 - */ - [[nodiscard]] bool isHorizontalScrollBarVisible() const; - - /** - * @brief 设置跨项拉伸滑动,默认开启;关闭后使用入场动画,首次选中仍使用入场动画。 - * @note 开关不改变选择,不发出 selectionChanged;中途关闭会立即定位到当前选中项 - */ - void setIndicatorSlideEnabled(bool enabled); - - [[nodiscard]] bool isIndicatorSlideEnabled() const; - - [[nodiscard]] QSize sizeHint() const override; - - [[nodiscard]] QSize minimumSizeHint() const override; - - signals: - /** - * @brief 当前选中项变化时发出 - * @param item 当前选中项;栏内没有 item 时为 nullptr - * @note 信号在选择变化时同步发出,不会向之后建立的连接补发历史通知 - * 首项自动选中的通知发生在 addItem()/insertItem() 返回之前 - * 调用者应提前连接;若在项目初始化后连接,应使用 selectedItem() 主动同步一次当前状态 - */ - void selectionChanged(SelectorBarItem *item); - - private: - void syncSelection(); - - detail::SelectorBarScrollArea *_scrollArea; // 管理内容布局与滚动,不参与选择决策 - detail::SelectorBarIndicatorLayer *_indicatorLayer; - QButtonGroup *_buttonGroup; - QList _items; - SelectorBarItem *_lastNotifiedItem = nullptr; // 仅用于去重通知,选择状态由按钮组维护 - }; -} // fancy 命名空间 - -#endif // QWIDGET_FANCYUI_SELECTORBAR_H diff --git a/src/Fancy/Widgets/SelectorBar/SelectorBarItem.cpp b/src/Fancy/Widgets/SelectorBar/SelectorBarItem.cpp deleted file mode 100644 index 37ccc56f..00000000 --- a/src/Fancy/Widgets/SelectorBar/SelectorBarItem.cpp +++ /dev/null @@ -1,102 +0,0 @@ -#include "SelectorBarItem.h" - -#include -#include -#include -#include - -#include "SelectorBarItemStyle.h" -#include "Core/Palette/Palette.h" -#include "Icon/Icon.h" - -namespace fancy -{ - SelectorBarItem::SelectorBarItem(QWidget *parent) : - QAbstractButton(parent) - { - setStyle(new SelectorBarItemStyle(this)); - setCheckable(true); - setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - setAttribute(Qt::WA_Hover); - setMouseTracking(true); - setFocusPolicy(Qt::StrongFocus); - auto &palette = Palette::palette(); - connect(&palette, &Palette::appColorChange, this, &SelectorBarItem::refreshIconColors); - connect(&palette, &Palette::appThemeChange, this, &SelectorBarItem::refreshIconColors); - connect(&palette, &Palette::appColorChange, this, QOverload<>::of(&SelectorBarItem::update)); - connect(&palette, &Palette::appThemeChange, this, QOverload<>::of(&SelectorBarItem::update)); - } - - SelectorBarItem::SelectorBarItem(const QString &text, QWidget *parent) : - SelectorBarItem(parent) - { - setText(text); - } - - void SelectorBarItem::setIcon(const QIcon &icon) - { - _iconId.reset(); - QAbstractButton::setIcon(icon); - } - - void SelectorBarItem::setIcon(const IconId &id) - { - _iconId = id; - refreshIconColors(); - } - - void SelectorBarItem::refreshIconColors() - { - if (!_iconId) - return; - - Icon icon; - auto &palette = Palette::palette(); - icon.addIconEnum(*_iconId, QIcon::Normal, QIcon::Off, palette[ColorRole::Text]); - icon.addIconEnum(*_iconId, QIcon::Active, QIcon::Off, palette[ColorRole::SelectorBarItemTextHover]); - icon.addIconEnum(*_iconId, QIcon::Selected, QIcon::Off, palette[ColorRole::SelectorBarItemTextPressed]); - icon.addIconEnum(*_iconId, QIcon::Disabled, QIcon::Off, palette[ColorRole::DisEnabledText]); - icon.addIconEnum(*_iconId, QIcon::Normal, QIcon::On, palette[ColorRole::SelectorBarItemTextSelected]); - icon.addIconEnum(*_iconId, QIcon::Active, QIcon::On, palette[ColorRole::SelectorBarItemTextSelectedHover]); - icon.addIconEnum(*_iconId, QIcon::Selected, QIcon::On, palette[ColorRole::SelectorBarItemTextSelectedPressed]); - icon.addIconEnum(*_iconId, QIcon::Disabled, QIcon::On, palette[ColorRole::DisEnabledText]); - QAbstractButton::setIcon(icon); - } - - void SelectorBarItem::setTextElideEnabled(bool enabled) - { - if (_textElideEnabled == enabled) - return; - _textElideEnabled = enabled; - update(); - } - - bool SelectorBarItem::isTextElideEnabled() const - { - return _textElideEnabled; - } - - QSize SelectorBarItem::sizeHint() const - { - if (const auto s = dynamic_cast(style())) - return s->sizeHint(); - return QAbstractButton::sizeHint(); - } - - void SelectorBarItem::paintEvent(QPaintEvent *event) - { - Q_UNUSED(event) - QStyleOptionButton option; - option.initFrom(this); - option.text = text(); - option.icon = icon(); - option.iconSize = iconSize(); - option.state |= isChecked() ? QStyle::State_On : QStyle::State_Off; - if (isDown()) - option.state |= QStyle::State_Sunken; - - QPainter painter(this); - style()->drawControl(QStyle::CE_PushButton, &option, &painter, this); - } - -} // fancy 命名空间 diff --git a/src/Fancy/Widgets/SelectorBar/SelectorBarItem.h b/src/Fancy/Widgets/SelectorBar/SelectorBarItem.h deleted file mode 100644 index 84b2b57e..00000000 --- a/src/Fancy/Widgets/SelectorBar/SelectorBarItem.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef QWIDGET_FANCYUI_SELECTORBARITEM_H -#define QWIDGET_FANCYUI_SELECTORBARITEM_H - -#include -#include - -#include "Core/Defs.h" - -namespace fancy -{ - /** @brief 透明选择按钮;指示器仅由所属 SelectorBar 的共享覆层绘制。 */ - class SelectorBarItem : public QAbstractButton - { - Q_OBJECT - Q_PROPERTY(QIcon icon READ icon WRITE setIcon) - Q_PROPERTY(bool textElideEnabled READ isTextElideEnabled WRITE setTextElideEnabled) - - public: - explicit SelectorBarItem(QWidget *parent = nullptr); - - explicit SelectorBarItem(const QString &text, QWidget *parent = nullptr); - - /** - * @brief 使用调用者提供的图标,不补充状态配色,并停止此前的自动主题适配 - */ - void setIcon(const QIcon &icon); - - /** - * @brief 使用库图标,自动生成各状态配色并随主题更新 - */ - void setIcon(const IconId &id); - - void setIcon(AntDesignIcons icon) { setIcon(iconId(icon)); } - void setIcon(BootstrapIcons icon) { setIcon(iconId(icon)); } - void setIcon(IconPark icon) { setIcon(iconId(icon)); } - - void setTextElideEnabled(bool enabled); // 宽度不足时逐行右侧省略,默认关闭;配合 QWidget 的最大宽度使用 - - [[nodiscard]] bool isTextElideEnabled() const; - - [[nodiscard]] QSize sizeHint() const override; - - protected: - void paintEvent(QPaintEvent *event) override; - - private: - void refreshIconColors(); - - std::optional _iconId; - bool _textElideEnabled = false; - }; -} // fancy - -#endif //QWIDGET_FANCYUI_SELECTORBARITEM_H diff --git a/src/Fancy/Widgets/SelectorBar/SelectorBarItemStyle.cpp b/src/Fancy/Widgets/SelectorBar/SelectorBarItemStyle.cpp deleted file mode 100644 index 0446d297..00000000 --- a/src/Fancy/Widgets/SelectorBar/SelectorBarItemStyle.cpp +++ /dev/null @@ -1,226 +0,0 @@ -// -// A1840 于 2026/10/9 创建。 -// - -#include "SelectorBarItemStyle.h" - -#include -#include -#include - -#include "Core/ControlState/ControlState.h" -#include "Core/Palette/Palette.h" -#include "utils/General.h" - -namespace fancy -{ - namespace - { - constexpr int HorizontalPadding = 12; // 内容两侧的留白 - constexpr int TopPadding = 10; // 内容上方的留白 - constexpr int BottomPadding = 7; // 内容与指示条之间的留白 - constexpr int IconTextSpacing = 8; // 图标与文字的间距 - constexpr int DefaultIconSize = 17; // 默认图标可见笔画的最大宽高 - constexpr int DefaultIconLayoutSize = 20; // 默认图标参与布局的宽高 - constexpr int IconHorizontalMargin = -2; // 图标左右两侧的布局边距 - constexpr int IconMeasureSize = 128; // 测量图标透明留白时使用的画布大小 - constexpr int IndicatorHeight = 3; // 选中指示条的高度 - constexpr qreal IconLayoutScale = static_cast(DefaultIconLayoutSize) / DefaultIconSize; // 图标布局尺寸与可见尺寸的比例 - } - - SelectorBarItemStyle::SelectorBarItemStyle(SelectorBarItem *parent) : - QProxyStyle(nullptr), - _parent(parent) - { - setParent(parent); - parent->setIconSize(QSize(DefaultIconSize, DefaultIconSize)); - } - - void SelectorBarItemStyle::drawItemText( - QPainter *painter, - const QRect &rect, - int flags, - const QPalette &pal, - bool enabled, - const QString &text, - QPalette::ColorRole textRole) const - { - Q_UNUSED(enabled) - Q_UNUSED(flags) - const QFontMetrics metrics(painter->font()); - const auto lines = textLines(text); - painter->save(); - painter->setPen(pal.color(textRole)); - painter->setClipRect(rect, Qt::IntersectClip); - const int height = text.isEmpty() ? QSize().height() : metrics.height() + (static_cast(lines.size()) - 1) * metrics.lineSpacing(); - int baseline = rect.y() + (rect.height() - height) / 2 + metrics.ascent(); - for (const auto &line: lines) - { - const QString visibleLine = _parent->isTextElideEnabled() - ? metrics.elidedText(line, Qt::ElideRight, qMax(0, rect.width())) - : line; - painter->drawText(QPoint(rect.x(), baseline), visibleLine); - baseline += metrics.lineSpacing(); - } - painter->restore(); - } - - void SelectorBarItemStyle::drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const - { - const auto *button = qstyleoption_cast(option); - if (!button) - { - QProxyStyle::drawControl(element, option, painter, widget); - return; - } - - const ControlState state(button->state, ControlType::WidgetCheckable); - const bool selected = state._on; - - switch (element) - { - case CE_PushButton: - { - drawControl(CE_PushButtonBevel, button, painter, widget); - drawControl(CE_PushButtonLabel, button, painter, widget); - return; - } - case CE_PushButtonBevel: - { - painter->fillRect(button->rect, Palette::palette()[ColorRole::SelectorBarItemBackground]); - return; - } - case CE_PushButtonLabel: - { - // 图标与文字共用内容区域,分别保留上下内边距和指示条空间 - painter->save(); - painter->setFont(_parent->font()); - const bool hasIcon = !button->icon.isNull(); - const bool hasText = !button->text.isEmpty(); - const QFontMetrics metrics(painter->font()); - const QSize iconSize = iconLayoutSize(button->iconSize, IconLayoutScale); - const int iconWidth = hasIcon ? qMax(0, iconSize.width() + 2 * IconHorizontalMargin) : 0; - const int spacing = hasIcon && hasText ? IconTextSpacing : 0; - const QString &text = button->text; - const int availableTextWidth = qMax(0, button->rect.width() - 2 * HorizontalPadding - iconWidth - spacing); - const int textWidth = qMin(textSize(metrics, text).width(), availableTextWidth); - const int groupWidth = iconWidth + spacing + textWidth; - const int x = button->rect.x() + (button->rect.width() - groupWidth) / 2; - const int contentTop = button->rect.y() + TopPadding; - const int contentHeight = qMax(0, button->rect.height() - TopPadding - BottomPadding - IndicatorHeight); - - if (hasIcon) - { - const QRectF iconRect(x + IconHorizontalMargin, contentTop + (contentHeight - iconSize.height()) / 2.0, - iconSize.width(), iconSize.height()); - QIcon::Mode mode = QIcon::Normal; - switch (state) - { - case VisualState::Disabled: - case VisualState::SelectedDisabled: - mode = QIcon::Disabled; - break; - case VisualState::Hover: - case VisualState::SelectedHover: - mode = QIcon::Active; - break; - case VisualState::Pressed: - case VisualState::SelectedPressed: - mode = QIcon::Selected; - break; - default: - break; - } - // 补偿 SVG 自带的透明留白,保持宽高比,使可见笔画符合 iconSize - if (const QRectF ink = iconInkBounds(button->icon); !ink.isEmpty()) - { - const qreal scale = qMin(button->iconSize.width() / ink.width(), button->iconSize.height() / ink.height()); - painter->save(); - painter->translate(iconRect.center()); - painter->scale(scale, scale); - painter->translate(-ink.center()); - button->icon.paint(painter, {0, 0, IconMeasureSize, IconMeasureSize}, - Qt::AlignCenter, mode, selected ? QIcon::On : QIcon::Off); - painter->restore(); - } - } - if (hasText) - { - auto textRole = ColorRole::Text; - switch (state) - { - case VisualState::Normal: - textRole = ColorRole::Text; - break; - case VisualState::Hover: - textRole = ColorRole::SelectorBarItemTextHover; - break; - case VisualState::Pressed: - textRole = ColorRole::SelectorBarItemTextPressed; - break; - case VisualState::Disabled: - case VisualState::SelectedDisabled: - textRole = ColorRole::DisEnabledText; - break; - case VisualState::Selected: - textRole = ColorRole::SelectorBarItemTextSelected; - break; - case VisualState::SelectedHover: - textRole = ColorRole::SelectorBarItemTextSelectedHover; - break; - case VisualState::SelectedPressed: - textRole = ColorRole::SelectorBarItemTextSelectedPressed; - break; - default: - break; - } - QPalette textPalette = button->palette; - textPalette.setColor(QPalette::ButtonText, Palette::palette()[textRole]); - const QRect textRect(x + iconWidth + spacing, contentTop, textWidth, contentHeight); - drawItemText(painter, textRect, Qt::AlignLeft | Qt::AlignVCenter, textPalette, state._enable, text, QPalette::ButtonText); - } - painter->restore(); - return; - } - default: - QProxyStyle::drawControl(element, option, painter, widget); - } - } - - QSize SelectorBarItemStyle::sizeHint() const - { - const QFontMetrics metrics(_parent->font()); - const bool hasIcon = !_parent->icon().isNull(); - const bool hasText = !_parent->text().isEmpty(); - const QSize labelSize = textSize(metrics, _parent->text()); - const int contentWidth = labelSize.width() - + (hasIcon ? qMax(0, iconLayoutSize(_parent->iconSize(), IconLayoutScale).width() + 2 * IconHorizontalMargin) : 0) - + (hasIcon && hasText ? IconTextSpacing : 0); - const int contentHeight = qMax(labelSize.height(), hasIcon ? iconLayoutSize(_parent->iconSize(), IconLayoutScale).height() : 0); - return {contentWidth + 2 * HorizontalPadding, contentHeight + TopPadding + BottomPadding + IndicatorHeight}; - } - - QRectF SelectorBarItemStyle::iconInkBounds(const QIcon &icon) const - { - if (_measuredIconKey == icon.cacheKey() && !_iconInkBounds.isEmpty()) - return _iconInkBounds; - - // 仅在图标变化时测量,交互状态切换时复用边界,避免图标跳动 - QImage image(IconMeasureSize, IconMeasureSize, QImage::Format_ARGB32_Premultiplied); - image.fill(Qt::transparent); - QPainter painter(&image); - icon.paint(&painter, image.rect(), Qt::AlignCenter, QIcon::Normal, QIcon::Off); - painter.end(); - QRect bounds; - for (int y = 0; y < image.height(); ++y) - { - const auto *pixels = reinterpret_cast(image.constScanLine(y)); - for (int x = 0; x < image.width(); ++x) - if (qAlpha(pixels[x]) != 0) - bounds |= QRect(x, y, 1, 1); - } - _measuredIconKey = icon.cacheKey(); - _iconInkBounds = bounds.isEmpty() ? QRectF(image.rect()) : QRectF(bounds); - return _iconInkBounds; - } -} // fancy 命名空间 diff --git a/src/Fancy/Widgets/SelectorBar/SelectorBarItemStyle.h b/src/Fancy/Widgets/SelectorBar/SelectorBarItemStyle.h deleted file mode 100644 index 71f13f2a..00000000 --- a/src/Fancy/Widgets/SelectorBar/SelectorBarItemStyle.h +++ /dev/null @@ -1,35 +0,0 @@ -// -// A1840 于 2026/10/9 创建。 -// - -#ifndef QWIDGET_FANCYUI_SELECTORBARITEMSTYLE_H -#define QWIDGET_FANCYUI_SELECTORBARITEMSTYLE_H -#include - -#include "SelectorBarItem.h" - -namespace fancy -{ - class SelectorBarItemStyle : public QProxyStyle - { - Q_OBJECT - - public: - explicit SelectorBarItemStyle(SelectorBarItem *parent); - - void drawItemText(QPainter *painter, const QRect &rect, int flags, const QPalette &pal, bool enabled, const QString &text, QPalette::ColorRole textRole) const override; - - void drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const override; - - [[nodiscard]] QSize sizeHint() const; - - private: - QRectF iconInkBounds(const QIcon &icon) const; - - SelectorBarItem *_parent; - mutable qint64 _measuredIconKey = 0; // 已测量图标的缓存标识 - mutable QRectF _iconInkBounds; // 图标笔画在测量画布中的边界 - }; -} // fancy 命名空间 - -#endif // 头文件保护结束 diff --git a/src/Fancy/Widgets/SelectorBar/private/SelectorBarIndicatorLayer.cpp b/src/Fancy/Widgets/SelectorBar/private/SelectorBarIndicatorLayer.cpp deleted file mode 100644 index 0366ff45..00000000 --- a/src/Fancy/Widgets/SelectorBar/private/SelectorBarIndicatorLayer.cpp +++ /dev/null @@ -1,155 +0,0 @@ -#include "SelectorBarIndicatorLayer.h" - -#include -#include -#include "../SelectorBarItem.h" -#include "Core/Palette/Palette.h" - -namespace fancy::detail -{ - namespace - { - constexpr int RevealDuration = 167; // 入场动画时长,单位毫秒 - constexpr int SlideDuration = 600; // 跨项滑动时长,单位毫秒 - constexpr qreal IndicatorThickness = 3.0; //指示条厚度,逻辑像素 - } - - SelectorBarIndicatorLayer::SelectorBarIndicatorLayer(QWidget *contents) : - QWidget(contents), - _animation(this) - { - setAttribute(Qt::WA_TransparentForMouseEvents); - setAttribute(Qt::WA_NoSystemBackground); - setFocusPolicy(Qt::NoFocus); - setGeometry(contents->rect()); - contents->installEventFilter(this); - hide(); - connect(&_animation, &QVariantAnimation::valueChanged, this, QOverload<>::of(&SelectorBarIndicatorLayer::update)); - connect(&_animation, &QVariantAnimation::finished, this, &SelectorBarIndicatorLayer::finish); - connect(&Palette::palette(), &Palette::appThemeChange, this, QOverload<>::of(&SelectorBarIndicatorLayer::update)); - connect(&Palette::palette(), &Palette::appColorChange, this, QOverload<>::of(&SelectorBarIndicatorLayer::update)); - } - - QLineF SelectorBarIndicatorLayer::indicatorLine(const QRectF &itemRect) - { - const qreal centerY = itemRect.bottom() - IndicatorThickness / 2; - return {itemRect.center().x() - 8, centerY, itemRect.center().x() + 8, centerY}; - } - - QLineF SelectorBarIndicatorLayer::itemLine(const SelectorBarItem *item) const - { - return indicatorLine(QRectF(item->geometry())); - } - - QLineF SelectorBarIndicatorLayer::currentLine() const - { - if (!_selected) - return {}; - return _animation.geometry(itemLine(_selected)); - } - - void SelectorBarIndicatorLayer::refresh() - { - setGeometry(parentWidget()->rect()); - setVisible(_selected && _selected->isChecked() && !_selected->isHidden()); - raise(); - update(); - } - - void SelectorBarIndicatorLayer::finish() - { - _animation.reveal(false, 4.0, RevealDuration); - refresh(); - } - - void SelectorBarIndicatorLayer::setSlideEnabled(bool enabled) - { - if (_enabled == enabled) - return; - _enabled = enabled; - if (_animation.isSliding()) - finish(); - } - - void SelectorBarIndicatorLayer::select(SelectorBarItem *item) - { - if (_selected == item) - { - if (!item) - finish(); - return; - } - const bool canSlide = _enabled && _selected && item && _selected->isVisible() && item->isVisible() - && _selected->parentWidget() == parentWidget() && item->parentWidget() == parentWidget(); - /** @note 从旧选中项的完整线段重新开始,避免反向切换继承拉长的中间线段。 */ - const QLineF from = canSlide ? itemLine(_selected) : QLineF(); - _animation.stop(); - if (_selected) - _selected->removeEventFilter(this); - _selected = item; - if (item) - item->installEventFilter(this); - if (canSlide) - _animation.slide(from, itemLine(item), Qt::Horizontal, SlideDuration); - else - _animation.reveal(item && item->isVisible(), 4.0, RevealDuration); - refresh(); - } - - bool SelectorBarIndicatorLayer::eventFilter(QObject *watched, QEvent *event) - { - if (watched == _selected && event->type() == QEvent::Destroy) - { - _selected = nullptr; - finish(); - return false; - } - if (watched == _selected && event->type() == QEvent::ParentChange && - _selected->parentWidget() != parentWidget()) - { - select(nullptr); - return false; - } - switch (event->type()) - { - case QEvent::Hide: - finish(); - break; - case QEvent::Show: - case QEvent::Move: - case QEvent::Resize: - case QEvent::LayoutRequest: - /** @note 滚动只移动 contents;目标在内容坐标中重排时才停止滑动。 */ - if (_selected && _animation.isSliding() && itemLine(_selected) != _animation.target()) - finish(); - else - refresh(); - break; - case QEvent::EnabledChange: - update(); - break; - default: - break; - } - return QWidget::eventFilter(watched, event); - } - - void SelectorBarIndicatorLayer::paintEvent(QPaintEvent *) - { - if (!_selected || !_selected->isChecked()) - return; - QPainter painter(this); - painter.setRenderHint(QPainter::Antialiasing); - const QColor color = Palette::palette()[_selected->isEnabled() ? ColorRole::AppAccent : ColorRole::DisEnabledText]; - painter.setPen(QPen(color, IndicatorThickness, Qt::SolidLine, Qt::RoundCap)); - painter.setOpacity(_animation.opacity()); - // 动画线段的两端表示最终可见边界,而非画笔中心线端点 - // RoundCap 在端点外侧和垂直方向各延伸半个线宽,因此左右端点各内缩 1.5: - // 可见长度 16 对应中心线长度 13,入场可见长度 4 对应中心线长度 1 - // Y 始终位于厚度中心,使用非 cosmetic 的 3px 画笔及浮点坐标,DPR 由 Qt 处理 - // 最短可见长度为 4,大于线宽 3;若以后允许更短,应避免内缩后端点倒置 - const QLineF visibleLine = currentLine(); - constexpr qreal halfPen = IndicatorThickness / 2; - painter.drawLine(QLineF(visibleLine.p1() + QPointF(halfPen, 0), visibleLine.p2() - QPointF(halfPen, 0))); - } -} diff --git a/src/Fancy/Widgets/SelectorBar/private/SelectorBarIndicatorLayer.h b/src/Fancy/Widgets/SelectorBar/private/SelectorBarIndicatorLayer.h deleted file mode 100644 index bf409ea9..00000000 --- a/src/Fancy/Widgets/SelectorBar/private/SelectorBarIndicatorLayer.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef FANCY_SELECTORBARINDICATORLAYER_H -#define FANCY_SELECTORBARINDICATORLAYER_H - -#include -#include -#include "Animation/IndicatorAnimation.h" - -namespace fancy -{ - class SelectorBarItem; - - namespace detail - { - /** - * @brief 绘制指示器的入场、跨项运动及静态状态 - * @note 不参与布局和鼠标命中,统一使用 SelectorBar 滚动内容坐标。 - */ - class SelectorBarIndicatorLayer : public QWidget - { - public: - explicit SelectorBarIndicatorLayer(QWidget *contents); - - void select(SelectorBarItem *item); - - void setSlideEnabled(bool enabled); - - [[nodiscard]] bool isSlideEnabled() const { return _enabled; } - - - bool eventFilter(QObject *watched, QEvent *event) override; - - protected: - void paintEvent(QPaintEvent *event) override; - - private: - /** - * @brief 计算最终指示条的可见边界;动画只接收该水平线段 - */ - static QLineF indicatorLine(const QRectF &itemRect); - - QLineF itemLine(const SelectorBarItem *item) const; - - [[nodiscard]] QLineF currentLine() const; - - void refresh(); - - void finish(); - - IndicatorAnimation _animation; - QPointer _selected; - bool _enabled = true; - }; - } -} -#endif diff --git a/src/Fancy/Widgets/SelectorBar/private/SelectorBarScrollArea.cpp b/src/Fancy/Widgets/SelectorBar/private/SelectorBarScrollArea.cpp deleted file mode 100644 index 2440c563..00000000 --- a/src/Fancy/Widgets/SelectorBar/private/SelectorBarScrollArea.cpp +++ /dev/null @@ -1,118 +0,0 @@ -#include "SelectorBarScrollArea.h" - -#include -#include -#include -#include -#include - -namespace fancy::detail -{ - SelectorBarScrollArea::SelectorBarScrollArea(QWidget *parent) : - ScrollArea(parent), - _contents(new QWidget), - _layout(new QHBoxLayout(_contents)) - { - _layout->setContentsMargins(0, 4, 0, 4); - _layout->setSpacing(0); - _layout->setAlignment(Qt::AlignLeft); - _layout->setSizeConstraint(QLayout::SetFixedSize); // 内容保持自然尺寸,空间不足时滚动,不压缩项目 - setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - setWidgetResizable(false); - setAlignment(Qt::AlignLeft | Qt::AlignTop); - setWidget(_contents); // setWidget() 会安装 QScrollArea 的事件过滤器,由本类重载继续处理内容变化 - } - - void SelectorBarScrollArea::insertWidget(int index, QWidget *widget) - { - widget->setParent(_contents); - _layout->insertWidget(index, widget); - invalidateSizeHint(); - } - - void SelectorBarScrollArea::removeWidget(QWidget *widget) - { - _layout->removeWidget(widget); - widget->setParent(nullptr); - invalidateSizeHint(); - } - - void SelectorBarScrollArea::ensureItemVisible(const QWidget *item) const - { - _layout->activate(); - const auto *view = viewport(); - auto *scrollBar = horizontalScrollBar(); - const int viewportWidth = view->width(); - const int left = item->mapTo(view, QPoint(0, 0)).x(); - const int right = left + item->width(); - int delta = 0; - if (item->width() > viewportWidth) - delta = qRound(left + (item->width() - viewportWidth) / 2.0); // 超宽项目居中显示选中指示条 - else if (left < 0) - delta = left; // 只补足左侧被遮挡的部分 - else if (right > viewportWidth) - delta = right - viewportWidth; // 完整可见时不滚动 - if (delta != 0 && viewportWidth > 0) - scrollBar->setValue(scrollBar->value() + (isRightToLeft() ? -delta : delta)); - } - - void SelectorBarScrollArea::setHorizontalScrollBarVisible(bool visible) - { - if (isHorizontalScrollBarVisible() == visible) - return; - setHorizontalScrollBarPolicy(visible ? Qt::ScrollBarAsNeeded : Qt::ScrollBarAlwaysOff); - invalidateSizeHint(); - } - - bool SelectorBarScrollArea::isHorizontalScrollBarVisible() const - { - return horizontalScrollBarPolicy() == Qt::ScrollBarAsNeeded; - } - - QSize SelectorBarScrollArea::sizeHint() const - { - QSize size = _layout->sizeHint(); - // 布局包含项目及上下边距;框架也需要预留空间,避免产生额外的纵向滚动范围 - const int frameExtent = 2 * frameWidth(); - if (isHorizontalScrollBarVisible() && size.width() > qMax(0, width() - frameExtent)) - size.rheight() += horizontalScrollBar()->sizeHint().height(); - return size + QSize(frameExtent, frameExtent); - } - - QSize SelectorBarScrollArea::minimumSizeHint() const - { - return {0, sizeHint().height()}; - } - - void SelectorBarScrollArea::invalidateSizeHint() - { - const QSize hint = sizeHint(); - if (hint == _lastSizeHint) - return; - // 先记录再通知,避免布局调整引发的后续 Resize 对相同尺寸重复通知 - _lastSizeHint = hint; - updateGeometry(); - emit sizeHintChanged(); - } - - void SelectorBarScrollArea::resizeEvent(QResizeEvent *event) - { - ScrollArea::resizeEvent(event); - invalidateSizeHint(); - } - - bool SelectorBarScrollArea::eventFilter(QObject *watched, QEvent *event) - { - // 保留 QScrollArea 更新滚动范围等默认行为,再通知外层更新尺寸提示 - const bool filtered = ScrollArea::eventFilter(watched, event); - if (watched == _contents && (event->type() == QEvent::LayoutRequest || event->type() == QEvent::Resize)) - invalidateSizeHint(); - return filtered; - } - - void SelectorBarScrollArea::wheelEvent(QWheelEvent *event) - { - QCoreApplication::sendEvent(horizontalScrollBar(), event); - } -} diff --git a/src/Fancy/Widgets/SelectorBar/private/SelectorBarScrollArea.h b/src/Fancy/Widgets/SelectorBar/private/SelectorBarScrollArea.h deleted file mode 100644 index 53bd512e..00000000 --- a/src/Fancy/Widgets/SelectorBar/private/SelectorBarScrollArea.h +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef QWIDGET_FANCYUI_SELECTORBARSCROLLAREA_H -#define QWIDGET_FANCYUI_SELECTORBARSCROLLAREA_H - -#include "Widgets/ScrollArea/ScrollArea.h" - -class QHBoxLayout; - -namespace fancy::detail -{ - /** - * @brief SelectorBar 内部使用的横向滚动区域,不作为公共控件使用 - * @note 负责内容容器、布局、尺寸提示及滚动;项目归属校验、按钮组和选择状态由 SelectorBar 管理 - */ - class SelectorBarScrollArea : public ScrollArea - { - Q_OBJECT - - public: - explicit SelectorBarScrollArea(QWidget *parent = nullptr); - - /** - * @brief 将控件加入内容布局,并将其父对象设置为内容容器 - * @note 调用方应先完成旧栏的移除;此处不操作按钮组、选择状态或业务信号 - */ - void insertWidget(int index, QWidget *widget); - - /** - * @brief 将控件移出内容布局并清空父对象,不销毁控件 - * @note 调用方负责更新项目归属及选择状态,并决定控件后续生命周期 - */ - void removeWidget(QWidget *widget); - - /** - * @brief 滚动至指定项目可见;超宽项目居中,完整可见的项目不移动 - */ - void ensureItemVisible(const QWidget *item) const; - - /** - * @brief 设置是否允许在内容溢出时显示横向滚动条,隐藏时仍可滚动 - */ - void setHorizontalScrollBarVisible(bool visible); - - /** - * @brief 返回是否允许按需显示横向滚动条,不表示此刻实际可见 - */ - [[nodiscard]] bool isHorizontalScrollBarVisible() const; - - [[nodiscard]] QSize sizeHint() const override; - - [[nodiscard]] QSize minimumSizeHint() const override; - - signals: - /** - * @brief 尺寸提示的计算结果变化时,通知外层同步更新 - */ - void sizeHintChanged(); - - protected: - bool eventFilter(QObject *watched, QEvent *event) override; - - void resizeEvent(QResizeEvent *event) override; - - /** - * @brief 将内容区域的滚轮事件交给横向滚动条处理 - * @note Qt 默认按 angleDelta 的方向选择滚动条,隐藏纵向滚动条并不会自动改为横向滚动 - * 此处仅调整事件接收者,滚动步长、修饰键及小幅角度增量的累计仍由 Qt 处理 - */ - void wheelEvent(QWheelEvent *event) override; - - private: - /** - * @brief 仅在尺寸提示变化时更新布局协商,避免重复通知内外层布局 - */ - void invalidateSizeHint(); - - QWidget *_contents; - QHBoxLayout *_layout; - QSize _lastSizeHint; // 上次参与布局通知的尺寸提示,不在 sizeHint() 查询时更新 - }; -} - -#endif // QWIDGET_FANCYUI_SELECTORBARSCROLLAREA_H diff --git a/src/Fancy/Widgets/Slider/SliderStyle.cpp b/src/Fancy/Widgets/Slider/SliderStyle.cpp deleted file mode 100644 index 44ef7435..00000000 --- a/src/Fancy/Widgets/Slider/SliderStyle.cpp +++ /dev/null @@ -1,152 +0,0 @@ -// -// Created by TX on 2025/9/26. -// - -#include "SliderStyle.h" -#include -#include -#include -#include "SliderIndicator.h" -#include "Core/Defs.h" -#include "Core/Palette/Palette.h" - -namespace fancy -{ - namespace - { - // 逻辑像素,由 Qt 在绘制时应用 DPI 缩放。 - constexpr int indicatorExtent = 21; - constexpr qreal borderSpace = 1.0; - constexpr qreal trackRatio = 4.0 / 20.0; - } - - SliderStyle::SliderStyle(QSlider *parent, Qt::Orientation orientation) : - QProxyStyle(nullptr), - _indicator(new SliderIndicator(parent)) - { - parent->installEventFilter(this); - setParent(parent); - _indicator->_orientation = orientation; - - connect(&Palette::palette(), &Palette::appThemeChange, parent, QOverload<>::of(&QSlider::update)); - connect(&Palette::palette(), &Palette::appColorChange, parent, QOverload<>::of(&QSlider::update)); - } - - void SliderStyle::setOrientation(Qt::Orientation orientation) - { - _indicator->_orientation = orientation; - } - - bool SliderStyle::eventFilter(QObject *obj, QEvent *event) - { - if (obj == parent() && (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease)) - { - if (auto *mouseEvent = dynamic_cast(event)) - { - if (mouseEvent->button() == Qt::LeftButton && event->type() == QEvent::MouseButtonPress) - _indicator->mousePressEvent(mouseEvent); - else if (event->type() == QEvent::MouseButtonRelease) - { - _indicator->mouseReleaseEvent(mouseEvent); - if (!_indicator->geometry().contains(mouseEvent->pos())) - _indicator->leaveEvent(event); - } - } - } - return QProxyStyle::eventFilter(obj, event); - } - - int SliderStyle::pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const - { - if (metric == PM_SliderLength || metric == PM_SliderThickness || metric == PM_SliderControlThickness) - return indicatorExtent; - if (metric == PM_SliderSpaceAvailable) - { - if (const auto *slider = qstyleoption_cast(option)) - { - const int side = qMin(indicatorExtent, qMin(slider->rect.width(), slider->rect.height())); - return qMax(0, (slider->orientation == Qt::Horizontal ? slider->rect.width() : slider->rect.height()) - side); - } - } - return QProxyStyle::pixelMetric(metric, option, widget); - } - - QRect SliderStyle::subControlRect(ComplexControl control, const QStyleOptionComplex *option, - SubControl subControl, const QWidget *widget) const - { - const auto *slider = qstyleoption_cast(option); - if (control != CC_Slider || !slider || (subControl != SC_SliderHandle && subControl != SC_SliderGroove)) - return QProxyStyle::subControlRect(control, option, subControl, widget); - - const QRect rect = slider->rect; - const int side = qMax(0, qMin(indicatorExtent, qMin(rect.width(), rect.height()))); - const bool horizontal = slider->orientation == Qt::Horizontal; - const int length = horizontal ? rect.width() : rect.height(); - const int offset = sliderPositionFromValue(slider->minimum, slider->maximum, slider->sliderPosition, - qMax(0, length - side), slider->upsideDown); - const QRect groove = horizontal - ? QRect(rect.x(), rect.y() + (rect.height() - side) / 2, rect.width(), side) - : QRect(rect.x() + (rect.width() - side) / 2, rect.y(), side, rect.height()); - if (subControl == SC_SliderGroove) - return groove; - return horizontal ? QRect(groove.x() + offset, groove.y(), side, side) - : QRect(groove.x(), groove.y() + offset, side, side); - } - - void SliderStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget) const - { - if (control == CC_Slider) - { - // 滑槽与指示器区域 - const QRect grooveRect = subControlRect(CC_Slider, option, SC_SliderGroove, widget); - const QRect handleRect = subControlRect(CC_Slider, option, SC_SliderHandle, widget); - - auto &palette = Palette::palette(); - painter->save(); - painter->setRenderHints(QPainter::Antialiasing); - - const auto *slider = qstyleoption_cast(option); - if (!slider || handleRect.isEmpty()) - { - painter->restore(); - return; - } - const bool isHorizontal = slider->orientation == Qt::Horizontal; - const qreal diameter = qMax(0.0, handleRect.width() - borderSpace); - if (diameter <= 0) - { - _indicator->setGeometry(handleRect); - painter->restore(); - return; - } - const qreal penWidth = diameter * trackRatio; - const qreal capRadius = penWidth / 2; - const QRectF groove(grooveRect); - const QPointF handleCenter = QRectF(handleRect).center(); - const QPointF start = isHorizontal ? QPointF(groove.left() + capRadius, handleCenter.y()) - : QPointF(handleCenter.x(), groove.top() + capRadius); - const QPointF end = isHorizontal ? QPointF(groove.right() - capRadius, handleCenter.y()) - : QPointF(handleCenter.x(), groove.bottom() - capRadius); - const QLineF primaryLine(slider->upsideDown ? end : start, handleCenter); - const QLineF secondaryLine(handleCenter, slider->upsideDown ? start : end); - - // 画线 - QPen pen(palette[ColorRole::AppAccent], penWidth, Qt::PenStyle::SolidLine, Qt::PenCapStyle::RoundCap); - painter->setPen(pen); - painter->drawLine(primaryLine); - pen.setColor(palette[ColorRole::DisEnabled].darker(120)); - painter->setPen(pen); - painter->drawLine(secondaryLine); - - - // 绘制和鼠标命中使用同一个矩形,横竖方向共用尺寸基准。 - if (_indicator->geometry() != handleRect) - _indicator->setGeometry(handleRect); - - painter->restore(); - return; - } - - return QProxyStyle::drawComplexControl(control, option, painter, widget); - } -} // fancy diff --git a/src/Fancy/Widgets/Window/Window.cpp b/src/Fancy/Window.cpp similarity index 82% rename from src/Fancy/Widgets/Window/Window.cpp rename to src/Fancy/Window.cpp index 6b21c9e9..f5b8c020 100644 --- a/src/Fancy/Widgets/Window/Window.cpp +++ b/src/Fancy/Window.cpp @@ -5,23 +5,19 @@ #include "Window.h" #include #include +#include #include #include #include -#include -#include -#include -#include -#include -#include "Core/Defs.h" -#include "Core/SystemAccessor.h" -#include "Core/WindowController.h" -#include "Core/Palette/Palette.h" -#include "Core/Theme/ThemeModeController.h" -#include "Widgets/TitleBar/TitleBar.h" -#include "Widgets/TitleBar/Button/MaximizeButton.h" -#include "Widgets/TitleBar/Button/MinimizeButton.h" +#include "Defs.h" +#include "MaximizeButton.h" +#include "MinimizeButton.h" +#include "Palette.h" +#include "SystemAccessor.h" +#include "ThemeModeController.h" +#include "TitleBar.h" +#include "WindowController.h" #include "windowsx.h" @@ -46,26 +42,23 @@ namespace fancy _verticalLayout->setContentsMargins(0, 0, 0, 0); _verticalLayout->addWidget(_titleBar); _verticalLayout->addWidget(_clientArea); + setBorderWidth(); + setWindowFlags(windowFlags() | Qt::WindowType::FramelessWindowHint); #ifdef Q_OS_WIN - const HWND hwnd = reinterpret_cast(winId()); MARGINS margins = {1, 1, 0, 1}; - SystemAccessor::DwmExtendFrameIntoClientArea(hwnd, &margins); - SetWindowLongPtr(hwnd, + SystemAccessor::DwmExtendFrameIntoClientArea(reinterpret_cast(winId()), &margins); + SetWindowLongPtr(reinterpret_cast(winId()), GWL_STYLE, GetWindowLongPtr( - hwnd, + reinterpret_cast(winId()), GWL_STYLE ) | WS_BORDER | WS_CAPTION | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SIZEBOX | WS_SYSMENU ); #endif - setBorderWidth(); connect(windowHandle(), &QWindow::screenChanged, this, &Window::setDwmMargins); - connect(windowHandle(), &QWindow::screenChanged, this, [this] { - QTimer::singleShot(0, this, &Window::setBorderWidth); - }); connect(_animation, &QVariantAnimation::valueChanged, this, &Window::updateBkgColor); connect(&Palette::palette(), &Palette::appThemeChange, this, &Window::onThemeChanged); @@ -208,11 +201,7 @@ namespace fancy painter.drawRect(rect()); } -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) bool Window::nativeEvent(const QByteArray &eventType, void *message, qintptr *result) -#else - bool Window::nativeEvent(const QByteArray &eventType, void *message, long *result) -#endif { switch (const MSG *msg = static_cast(message); msg->message) { @@ -226,59 +215,55 @@ namespace fancy if (!isMaximized()) { RECT widget_rect{0, 0, 0, 0}; - if (!GetWindowRect(msg->hwnd, &widget_rect)) - break; - if (p.x < widget_rect.left || p.x >= widget_rect.right || - p.y < widget_rect.top || p.y >= widget_rect.bottom) - break; + GetWindowRect(msg->hwnd, &widget_rect); // 获取窗口的绝对区域 // 综合考虑使用习惯以及其他一些写法(8个if直接判断、九宫格位视图法) // 下面的代码可能有些冗余,但能有效减少if判断的次平均次数以及减少临时变量的创建 - if (p.x < widget_rect.right && p.x >= widget_rect.right - _xBorderWidth) // 右 + if (p.x <= widget_rect.right && p.x >= widget_rect.right - _xBorderWidth) // 右 { - if (p.y < widget_rect.bottom && p.y >= widget_rect.bottom - _yBorderWidth) // 下 + if (p.y <= widget_rect.bottom && p.y >= widget_rect.bottom - _yBorderWidth) // 下 *result = HTBOTTOMRIGHT; - else if (p.y < widget_rect.top + _yBorderWidth) // 上 + else if (p.y >= widget_rect.top && p.y <= widget_rect.top + _yBorderWidth) // 上 *result = HTTOPRIGHT; else *result = HTRIGHT; return true; } - if (p.y < widget_rect.bottom && p.y >= widget_rect.bottom - _yBorderWidth) // 下 + if (p.y <= widget_rect.bottom && p.y >= widget_rect.bottom - _yBorderWidth) // 下 { - if (p.x < widget_rect.left + _xBorderWidth) // 左 + if (p.x >= widget_rect.left && p.x <= widget_rect.left + _xBorderWidth) // 左 *result = HTBOTTOMLEFT; else *result = HTBOTTOM; return true; } - if (p.y < widget_rect.top + _yBorderWidth) // 上 + if (p.y >= widget_rect.top && p.y <= widget_rect.top + _yBorderWidth) // 上 { - if (p.x < widget_rect.left + _xBorderWidth) // 左 + if (p.x >= widget_rect.left && p.x <= widget_rect.left + _xBorderWidth) // 左 *result = HTTOPLEFT; else *result = HTTOP; return true; } - if (p.x < widget_rect.left + _xBorderWidth) // 左 + if (p.x >= widget_rect.left && p.x <= widget_rect.left + _xBorderWidth) // 左 { *result = HTLEFT; return true; } } - if (_titleBar->_maximizeButton && !_titleBar->_maximizeButton->isHidden()) + if (_titleBar->_maximizeButton && !_titleBar->_minimizeButton->isHidden()) { - // 返回零也可能表示成功且坐标位移为零,需结合错误码判断。 - POINT clientPoint = p; - SetLastError(ERROR_SUCCESS); - if (MapWindowPoints(HWND_DESKTOP, msg->hwnd, &clientPoint, 1) == 0 && GetLastError() != ERROR_SUCCESS) + if (!MapWindowPoints(HWND_DESKTOP, msg->hwnd, &p, 1)) // 将p转换成相对坐标 { - clientPoint = p; - if (!ScreenToClient(msg->hwnd, &clientPoint)) - break; + if (!ScreenToClient(reinterpret_cast(winId()), &p)) + { + QPoint t(mapFromGlobal(QPoint(p.x, p.y))); + p.x = t.x(); + p.y = t.y(); + } } - QPoint widget_point(clientPoint.x, clientPoint.y); // Win32 客户区物理像素 + QPoint widget_point(p.x, p.y); // Qt相对坐标 widget_point /= devicePixelRatioF(); // 除以缩放dpi QRect max_btn_rect(_titleBar->_maximizeButton->mapTo(this, QPoint{0, 0}), _titleBar->_maximizeButton->size()); // 最大化按钮区域 @@ -339,13 +324,9 @@ namespace fancy } } break; - case WM_DPICHANGED : // Qt 处理完新的窗口 DPI 后再刷新边框及布局单位 - case WM_DISPLAYCHANGE : - QTimer::singleShot(0, this, [this] { - setBorderWidth(); - setDwmMargins(); - update(); - }); + case WM_DISPLAYCHANGE : // 分辨率或dpi改变 + setBorderWidth(); + update(); break; // case WM_WININICHANGE : // *result = 0; @@ -371,7 +352,7 @@ namespace fancy { if (_titleBar->_maximizeButton) emit _titleBar->_maximizeButton->stateChange(MaximizeBtnState::Maximize); - updateMaximizedMargins(); + _verticalLayout->setContentsMargins(_xBorderWidth, _yBorderWidth, _xBorderWidth, _yBorderWidth); update(); } // 最大化后还原 @@ -439,22 +420,18 @@ namespace fancy void Window::setBorderWidth() { #ifdef Q_OS_WIN - const QSize borderSize = SystemAccessor::windowResizeBorderSize(); - _xBorderWidth = borderSize.width(); - _yBorderWidth = borderSize.height(); +#ifdef _MSC_VER + _xBorderWidth = GetSystemMetrics(SM_CXSIZEFRAME); + _yBorderWidth = GetSystemMetrics(SM_CYSIZEFRAME); + if (!_xBorderWidth) + _xBorderWidth = 5; + if (!_yBorderWidth) + _yBorderWidth = 5; +#elif defined(__GNUC__) + _xBorderWidth = 5; + _yBorderWidth = 5; +#endif #endif - updateMaximizedMargins(); - } - - void Window::updateMaximizedMargins() - { - if (!isMaximized()) - return; - // Win32 命中测试使用原始像素值;Qt 布局边距转换为窗口的逻辑像素 - const qreal dpr = devicePixelRatioF(); - _verticalLayout->setContentsMargins( - qRound(_xBorderWidth / dpr), qRound(_yBorderWidth / dpr), - qRound(_xBorderWidth / dpr), qRound(_yBorderWidth / dpr)); } void Window::setDwmWindowEffect(DwmWindowEffect effect) @@ -477,7 +454,7 @@ namespace fancy _dwmWindowEffect = effect; _titleBar->setTransparency(); auto hWnd = reinterpret_cast(winId()); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +#if (QT_VERSION <= QT_VERSION_CHECK(6, 0, 0)) setWindowFlags((this->windowFlags()) & (~Qt::WindowType::FramelessWindowHint)); #endif setAttribute(Qt::WidgetAttribute::WA_TranslucentBackground); diff --git a/src/Fancy/Widgets/Window/Window.h b/src/Fancy/Window.h similarity index 92% rename from src/Fancy/Widgets/Window/Window.h rename to src/Fancy/Window.h index 82b9917d..a4375c81 100644 --- a/src/Fancy/Widgets/Window/Window.h +++ b/src/Fancy/Window.h @@ -69,12 +69,7 @@ namespace fancy protected: void paintEvent(QPaintEvent *event) override; -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result) override; -#else - bool nativeEvent(const QByteArray &eventType, void *message, long *result) override; -#endif - void changeEvent(QEvent *event) override; @@ -94,8 +89,6 @@ namespace fancy void setBorderWidth(); - void updateMaximizedMargins(); - void setDwmWindowEffect(DwmWindowEffect effect); void setCompositionWindowEffect(CompositionWindowEffect effect, const QColor &color = {}); diff --git a/src/Fancy/Core/WindowController.cpp b/src/Fancy/WindowController.cpp similarity index 100% rename from src/Fancy/Core/WindowController.cpp rename to src/Fancy/WindowController.cpp diff --git a/src/Fancy/Core/WindowController.h b/src/Fancy/WindowController.h similarity index 100% rename from src/Fancy/Core/WindowController.h rename to src/Fancy/WindowController.h diff --git a/src/Fancy/utils/General.h b/src/Fancy/utils/General.h deleted file mode 100644 index b080a5e1..00000000 --- a/src/Fancy/utils/General.h +++ /dev/null @@ -1,86 +0,0 @@ -// -// Created by A1840 on 2026/8/31. -// - -#ifndef QWIDGET_FANCYUI_UTILS_H -#define QWIDGET_FANCYUI_UTILS_H -#include -#include -#include -#include -#include -#include -#include - -namespace fancy -{ - // 按显式换行拆分文本,统一换行格式并保留空行和末尾换行 - inline QStringList textLines(QString text) - { - text.replace(QStringLiteral("\r\n"), QStringLiteral("\n")); - text.replace(QChar('\r'), QChar('\n')); - return text.split(QChar('\n'), Qt::KeepEmptyParts); - } - - // 测量显式多行文本,不自动折行;空文本返回空尺寸 - inline QSize textSize(const QFontMetrics &metrics, const QString &text) - { - if (text.isEmpty()) - return {}; - const auto lines = textLines(text); - int width = 0; - for (const auto &line: lines) - width = qMax(width, metrics.horizontalAdvance(line)); - return {width, metrics.height() + (static_cast(lines.size()) - 1) * metrics.lineSpacing()}; - } - - // 将图标可见尺寸按指定比例换算为布局尺寸,比例由调用方的样式决定 - inline QSize iconLayoutSize(const QSize &visibleSize, qreal layoutScale) - { - return {qRound(visibleSize.width() * layoutScale), qRound(visibleSize.height() * layoutScale)}; - } - - inline QLine bottomLine(const QRect &rect, const int filletRadius) - { - if (int x = rect.center().x(); rect.width() - 2 * filletRadius <= 0) - return {x, rect.y() + rect.height(), x, rect.y() + rect.height()}; - return { - rect.x() + filletRadius, - rect.y() + rect.height(), - rect.x() + rect.width() - filletRadius, - rect.y() + rect.height() - }; - } - - // region 使用逻辑坐标;resolutionScale 为原生分辨率的采样比例,1 为原生,0.5 为宽高各减半 - // 无效区域或非正、非有限的比例返回空图像;返回图像的 DPR 包含采样比例 - inline QImage renderWidgetRegion(QWidget *widget, const QRect ®ion, const qreal resolutionScale = 1.0) - { - if (!widget || region.isEmpty() || !qIsFinite(resolutionScale) || resolutionScale <= 0) - return {}; - const qreal dpr = widget->devicePixelRatioF() * resolutionScale; - // 向下取整可避免图像的逻辑范围超过 region,防止多出的透明边缘被模糊到图像内部 - const QSize pixelSize(qMax(1, qFloor(region.width() * dpr)), qMax(1, qFloor(region.height() * dpr))); - QImage image(pixelSize, QImage::Format_ARGB32_Premultiplied); - if (image.isNull()) - return {}; - image.setDevicePixelRatio(dpr); - image.fill(Qt::GlobalColor::transparent); - if (const QRect intersected = region.intersected({{0, 0}, widget->size()}); !intersected.isEmpty()) - widget->render( - &image, - intersected.topLeft() - region.topLeft(), - intersected, - QWidget::RenderFlag::DrawWindowBackground); - return image; - } - - inline QRect getWidgetRectInAncestor(const QWidget *child, const QWidget *ancestor) - { - if (!child || !ancestor) - return {}; - return {child->mapTo(ancestor, QPoint{0, 0}), child->size()}; - } -} - -#endif //QWIDGET_FANCYUI_UTILS_H diff --git a/src/Fancy/utils/hash.h b/src/Fancy/utils/hash.h deleted file mode 100644 index 1632ca3c..00000000 --- a/src/Fancy/utils/hash.h +++ /dev/null @@ -1,68 +0,0 @@ -// -// Created by A1840 on 2026/8/31. -// - -#ifndef QWIDGET_FANCYUI_HASH_H -#define QWIDGET_FANCYUI_HASH_H -#include -#include - -#include "Core/Defs.h" - -namespace fancy -{ -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - inline size_t qHash(const IconId &id, size_t seed = 0) { return ::qHash(id.style, seed) ^ ::qHash(id.value); } -#else - - // 若升级到 C++20,可使用约束模板统一下面的枚举 qHash 重载: - // template requires std::is_enum_v - // inline uint qHash(const Enum &e, uint seed = 0) noexcept - // { - // return ::qHash(static_cast>(e), seed); - // } - - inline uint qHash(Theme e, uint seed = 0) noexcept - { - return ::qHash(static_cast>(e), seed); - } - - inline uint qHash(ColorGroups e, uint seed = 0) noexcept - { - return ::qHash(static_cast>(e), seed); - } - - inline uint qHash(ColorRole e, uint seed = 0) noexcept - { - return ::qHash(static_cast>(e), seed); - } - - inline uint qHash(AntDesignIcons e, uint seed = 0) noexcept - { - return ::qHash(static_cast>(e), seed); - } - - inline uint qHash(BootstrapIcons e, uint seed = 0) noexcept - { - return ::qHash(static_cast>(e), seed); - } - - inline uint qHash(IconPark e, uint seed = 0) noexcept - { - return ::qHash(static_cast>(e), seed); - } - - inline uint qHash(const IconId &id, uint seed = 0) noexcept - { - return ::qHash(id.value, seed) ^ ::qHash(static_cast>(id.style), seed); - } -#endif -} - -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) -inline size_t qHash(const QColor &color, size_t seed = 0) { return qHash(color.rgba(), seed); } -#else -inline uint qHash(const QColor &color, uint seed = 0) noexcept { return qHash(color.rgba(), seed); } -#endif - -#endif //QWIDGET_FANCYUI_HASH_H diff --git a/src/include/mini_blur/mini_blur.cpp b/src/include/mini_blur/mini_blur.cpp deleted file mode 100644 index a362a1dd..00000000 --- a/src/include/mini_blur/mini_blur.cpp +++ /dev/null @@ -1,340 +0,0 @@ -// Gaussian tables, 8-bit error diffusion and border semantics adapted from -// OpenCV 5.0d. SIMD and scheduling are standalone implementations. -// See THIRD_PARTY_NOTICES.md and THIRD_PARTY_SOURCE_HEADERS.txt. -#include "mini_blur.h" -#include "mini_blur_avx2.h" -#include "mini_blur_simd.h" -#include "mini_blur_workers.h" -#include -#include -#include -#include - -namespace mini_blur -{ - namespace - { - unsigned threadNumber(unsigned n) - { - if (n > 64) - throw std::invalid_argument("mini_blur: thread count must be <=64"); - return n ? n : std::max(1u, std::min(4u, std::thread::hardware_concurrency())); - } - - void validate(ImageView s, MutableImageView d, int kw, int kh, Border b) - { - if (!s.data || !d.data || s.width <= 0 || s.height <= 0 || s.channels < 1 || s.channels > 4 || - d.width != s.width || d.height != s.height || d.channels != s.channels) - throw std::invalid_argument("mini_blur: invalid image geometry"); - if (kw < 1 || kh < 1 || kw > 4095 || kh > 4095) - throw std::invalid_argument("mini_blur: kernel dimensions must be 1..4095"); - if (b != Border::Reflect101 && b != Border::Reflect && b != Border::Replicate && - b != Border::ConstantZero) - throw std::invalid_argument("mini_blur: unsupported border"); - const auto row = std::size_t(s.width) * s.channels, - max = std::numeric_limits::max(); - if (row / s.channels != std::size_t(s.width) || s.stride < row || d.stride < row || - std::size_t(s.height) > max / row / sizeof(std::uint32_t) || - (s.height > 1 && - (s.stride > (max - row) / (s.height - 1) || d.stride > (max - row) / (s.height - 1)))) - throw std::invalid_argument("mini_blur: invalid stride or image too large"); - } - - int borderIndex(long long p, int n, Border b) - { - if (p >= 0 && p < n) - return int(p); - if (b == Border::ConstantZero) - return -1; - if (b == Border::Replicate || n == 1) - return p < 0 ? 0 : n - 1; - const long long period = b == Border::Reflect101 ? 2LL * (n - 1) : 2LL * n; - p %= period; - if (p < 0) - p += period; - return int(p < n ? p : period - p - (b == Border::Reflect)); - } - - int roundEven(double x) - { - const int lo = int(std::floor(x)); - const double f = x - lo; - return lo + (f > 0.5 || (f == 0.5 && (lo & 1))); - } - - std::vector kernel(int n, double sigma) - { - std::vector k(n); - if (n == 1) - k = {1}; - else if (sigma == 0 && n == 3) - k = {0.25, 0.5, 0.25}; - else if (sigma == 0 && n == 5) - k = {1. / 16, 4. / 16, 6. / 16, 4. / 16, 1. / 16}; - else if (sigma == 0 && n == 7) - k = {4. / 128, 14. / 128, 28. / 128, 36. / 128, 28. / 128, 14. / 128, 4. / 128}; - else if (sigma == 0 && n == 9) - k = { - 4. / 256, - 13. / 256, - 30. / 256, - 51. / 256, - 60. / 256, - 51. / 256, - 30. / 256, - 13. / 256, - 4. / 256 - }; - else - { - if (sigma == 0) - sigma = n * 0.15 + 0.35; - double sum = 0; - for (int i = 0; i < n; ++i) - { - const double x = double(i - n / 2) / sigma; - k[i] = std::exp(-0.5 * x * x); - sum += k[i]; - } - for (auto &v: k) - v /= sum; - } - // Adapted from getGaussianKernelFixedPoint_ED, fractionBits=8. - // Preserve symmetry and sum exactly 256, including quantization error. - std::vector fixed(n); - double error = 0; - int sum = 0; - for (int i = 0; i < n / 2; ++i) - { - const double adjusted = k[i] * 256 + error; - const int value = roundEven(adjusted); - error = adjusted - value; - fixed[i] = fixed[n - 1 - i] = std::uint16_t(value); - sum += value; - } - fixed[n / 2] = std::uint16_t(256 - 2 * sum); - return fixed; - } - } // namespace - struct Context::Impl - { - bool avx2 = detail::avx2Available(); - detail::Workers pool; - std::vector gauss, zero, kx, ky; - std::vector box; - std::vector > padded; - std::vector > sums; - std::vector > rows; - std::vector pairs, taps; - std::vector mapX, mapY; - int cachedKx = 0, cachedKy = 0; - double cachedSx = -1, cachedSy = -1; - explicit Impl(unsigned n) : pool(n), padded(n), sums(n), rows(n) {} - }; - - Context::Context(unsigned n) : impl_(new Impl(threadNumber(n))) {} - - Context::~Context() = default; - - unsigned Context::threadCount() const { return impl_->pool.count(); } - - const char *Context::backend() - { - return detail::avx2Available() ? "AVX2" : MINI_BLUR_SSE2 ? "SSE2" : "scalar"; - } - - void Context::gaussianBlur( - ImageView src, - MutableImageView dst, - int kw, - int kh, - double sx, - double sy, - Border border) - { - validate(src, dst, kw, kh, border); - if (!(kw & 1) || !(kh & 1) || !std::isfinite(sx) || !std::isfinite(sy) || sx < 0 || sy < 0) - throw std::invalid_argument( - "mini_blur: Gaussian sizes must be odd and sigmas finite/nonnegative"); - if (sy == 0) - sy = sx; - auto &p = *impl_; - if (kw != p.cachedKx || sx != p.cachedSx) - { - p.kx = kernel(kw, sx); - p.cachedKx = kw; - p.cachedSx = sx; - } - if (kh != p.cachedKy || sy != p.cachedSy) - { - p.ky = kernel(kh, sy); - p.cachedKy = kh; - p.cachedSy = sy; - } - const std::size_t row = std::size_t(src.width) * src.channels; - const int rx = kw / 2, ry = kh / 2; - const std::size_t padding = std::size_t(rx) * src.channels; - p.gauss.resize(row * src.height); - p.zero.assign(row, 0); - p.pairs.clear(); - p.taps.clear(); - for (int i = 1; i <= rx; ++i) - if (p.kx[rx + i]) - p.pairs.push_back({i * src.channels, p.kx[rx + i]}); - for (int i = 0; i < kh; ++i) - if (p.ky[i]) - p.taps.push_back({i - ry, p.ky[i]}); - p.mapX.resize(std::size_t(src.width) + 2 * rx); - for (std::size_t i = 0; i < p.mapX.size(); ++i) - p.mapX[i] = borderIndex(static_cast(i) - rx, src.width, border); - p.mapY.resize(std::size_t(src.height) + 2 * ry); - for (std::size_t i = 0; i < p.mapY.size(); ++i) - p.mapY[i] = borderIndex(static_cast(i) - ry, src.height, border); - for (unsigned t = 0; t < threadCount(); ++t) - { - p.padded[t].resize(row + 2 * padding); - p.rows[t].resize(p.taps.size()); - } - const unsigned nt = threadCount(); - p.pool.run([&](unsigned t) { - const int first = int(std::int64_t(src.height) * t / nt), - last = int(std::int64_t(src.height) * (t + 1) / nt); - auto *pad = p.padded[t].data(); - for (int y = first; y < last; ++y) - { - const auto *in = src.data + std::size_t(y) * src.stride; - detail::expand(in, pad + padding, row); - for (int x = 0; x < rx; ++x) - { - const int left = p.mapX[x], right = p.mapX[std::size_t(rx) + src.width + x]; - for (int c = 0; c < src.channels; ++c) - { - pad[std::size_t(x) * src.channels + c] = - left < 0 ? 0 : in[std::size_t(left) * src.channels + c]; - pad[padding + row + std::size_t(x) * src.channels + c] = - right < 0 ? 0 : in[std::size_t(right) * src.channels + c]; - } - } - if (p.avx2) - detail::horizontalAvx2(pad + padding, p.gauss.data() + std::size_t(y) * row, row, - p.kx[rx], p.pairs); - else - detail::horizontal(pad + padding, p.gauss.data() + std::size_t(y) * row, row, - p.kx[rx], p.pairs); - } - }); - // Completing horizontal reads before any output write permits in-place use. - p.pool.run([&](unsigned t) { - const int first = int(std::int64_t(src.height) * t / nt), - last = int(std::int64_t(src.height) * (t + 1) / nt); - for (int y = first; y < last; ++y) - { - for (std::size_t j = 0; j < p.taps.size(); ++j) - { - const int iy = p.mapY[std::size_t(y) + std::size_t(ry + p.taps[j].offset)]; - p.rows[t][j] = iy < 0 ? p.zero.data() : p.gauss.data() + std::size_t(iy) * row; - } - if (p.avx2) - detail::verticalAvx2(p.rows[t].data(), p.taps, - dst.data + std::size_t(y) * dst.stride, row); - else - detail::vertical(p.rows[t].data(), p.taps, dst.data + std::size_t(y) * dst.stride, - row); - } - }); - } - - void Context::boxBlur(ImageView src, MutableImageView dst, int kw, int kh, Border border) - { - validate(src, dst, kw, kh, border); - auto &p = *impl_; - const std::size_t row = std::size_t(src.width) * src.channels; - p.box.resize(row * src.height); - const int ax = kw / 2, ay = kh / 2; - p.mapX.resize(std::size_t(src.width) + kw); - for (std::size_t i = 0; i < p.mapX.size(); ++i) - p.mapX[i] = borderIndex(static_cast(i) - ax, src.width, border); - p.mapY.resize(std::size_t(src.height) + kh); - for (std::size_t i = 0; i < p.mapY.size(); ++i) - p.mapY[i] = borderIndex(static_cast(i) - ay, src.height, border); - const unsigned nt = threadCount(); - for (unsigned t = 0; t < nt; ++t) - p.sums[t].resize(row * (t + 1) / nt - row * t / nt); - p.pool.run([&](unsigned t) { - const int first = int(std::int64_t(src.height) * t / nt), - last = int(std::int64_t(src.height) * (t + 1) / nt); - for (int y = first; y < last; ++y) - { - const auto *in = src.data + std::size_t(y) * src.stride; - auto *out = p.box.data() + std::size_t(y) * row; -#if MINI_BLUR_SSE2 - if (src.channels == 4) - { - detail::boxHorizontal4(in, out, src.width, kw, p.mapX.data()); - continue; - } -#endif - std::uint32_t sum[4] = {}; - for (int k = 0; k < kw; ++k) - { - const int ix = p.mapX[k]; - if (ix >= 0) - for (int c = 0; c < src.channels; ++c) - sum[c] += in[std::size_t(ix) * src.channels + c]; - } - for (int x = 0; x < src.width; ++x) - { - const int leave = p.mapX[x], enter = p.mapX[std::size_t(x) + kw]; - for (int c = 0; c < src.channels; ++c) - { - out[std::size_t(x) * src.channels + c] = sum[c]; - if (leave >= 0) - sum[c] -= in[std::size_t(leave) * src.channels + c]; - if (enter >= 0) - sum[c] += in[std::size_t(enter) * src.channels + c]; - } - } - } - }); - p.pool.run([&](unsigned t) { - const auto begin = row * t / nt, count = row * (t + 1) / nt - begin; - auto *sum = p.sums[t].data(); - std::fill(p.sums[t].begin(), p.sums[t].end(), 0); - for (int k = 0; k < kh; ++k) - { - const int iy = p.mapY[k]; - if (iy >= 0) - detail::updateSum(sum, p.box.data() + std::size_t(iy) * row + begin, nullptr, - count); - } - for (int y = 0; y < src.height; ++y) - { - detail::average(sum, dst.data + std::size_t(y) * dst.stride + begin, count, - std::uint32_t(kw) * kh); - const int leave = p.mapY[y], enter = p.mapY[std::size_t(y) + kh]; - detail::updateSum( - sum, enter < 0 ? nullptr : p.box.data() + std::size_t(enter) * row + begin, - leave < 0 ? nullptr : p.box.data() + std::size_t(leave) * row + begin, count); - } - }); - } - - void gaussianBlur( - ImageView src, - MutableImageView dst, - int kw, - int kh, - double sx, - double sy, - Border b) - { - thread_local Context context(1); - context.gaussianBlur(src, dst, kw, kh, sx, sy, b); - } - - void boxBlur(ImageView src, MutableImageView dst, int kw, int kh, Border b) - { - thread_local Context context(1); - context.boxBlur(src, dst, kw, kh, b); - } -} // namespace mini_blur diff --git a/src/include/mini_blur/mini_blur.h b/src/include/mini_blur/mini_blur.h deleted file mode 100644 index 1d26af58..00000000 --- a/src/include/mini_blur/mini_blur.h +++ /dev/null @@ -1,103 +0,0 @@ -#ifndef MINI_BLUR_H -#define MINI_BLUR_H - -#include -#include -#include - -namespace mini_blur -{ - // Pixels are interleaved 8-bit channels. stride is the byte distance between rows. - struct ImageView - { - const std::uint8_t *data; - int width; - int height; - int channels; // 1, 2, 3, or 4 - std::size_t stride; - }; - - struct MutableImageView - { - std::uint8_t *data; - int width; - int height; - int channels; - std::size_t stride; - }; - - enum class Border { Reflect101, Reflect, Replicate, ConstantZero }; - - // Reuse one context per independent stream. A context is NOT reentrant. - // threads=0 selects up to four threads; threads=1 disables worker threads. - // Workers and scratch buffers persist until destruction. Input/output may alias - // exactly. Kernels are limited to 4095 per axis. All views are isolated ROIs. - class Context - { - public: - explicit Context(unsigned threads = 0); - - ~Context(); - - Context(const Context &) = delete; - - Context &operator=(const Context &) = delete; - - void gaussianBlur( - ImageView src, - MutableImageView dst, - int kernelWidth, - int kernelHeight, - double sigmaX = 0, - double sigmaY = 0, - Border border = Border::Reflect101); - - void boxBlur( - ImageView src, - MutableImageView dst, - int kernelWidth, - int kernelHeight, - Border border = Border::Reflect101); - - [[nodiscard]] unsigned threadCount() const; - - static const char *backend(); - - private: - struct Impl; - std::unique_ptr impl_; - }; - - // Throws std::invalid_argument for invalid geometry or parameters. - // Uses a reusable, single-thread context local to the calling thread. - // For high-frequency parallel processing, explicitly reuse Context. - // src and dst may be the same image. Partial overlap is unsupported. - void gaussianBlur( - ImageView src, - MutableImageView dst, - int kernelWidth, - int kernelHeight, - double sigmaX = 0, - double sigmaY = 0, - Border border = Border::Reflect101); - - // Uniform normalized box filter, equivalent in meaning to OpenCV blur(). - // Kernel sizes can be even; the anchor is floor(size / 2), as in OpenCV. - void boxBlur( - ImageView src, - MutableImageView dst, - int kernelWidth, - int kernelHeight, - Border border = Border::Reflect101); - - inline void squareBlur( - ImageView src, - MutableImageView dst, - int kernelSize, - Border border = Border::Reflect101) - { - boxBlur(src, dst, kernelSize, kernelSize, border); - } -} // namespace mini_blur - -#endif // MINI_BLUR_H diff --git a/src/include/mini_blur/mini_blur_avx2.cpp b/src/include/mini_blur/mini_blur_avx2.cpp deleted file mode 100644 index 8749d7db..00000000 --- a/src/include/mini_blur/mini_blur_avx2.cpp +++ /dev/null @@ -1,105 +0,0 @@ -// Isolated AVX2 translation unit. Do not enable /arch:AVX2 or -mavx2 globally: -// the portable entry points must remain executable on older processors. -#include "mini_blur_avx2.h" -#if MINI_BLUR_SSE2 && (defined(__GNUC__) || defined(_MSC_VER)) -#define MINI_BLUR_HAVE_AVX2 1 -#include -#ifdef _MSC_VER -#include -#endif -#else -#define MINI_BLUR_HAVE_AVX2 0 -#endif - -namespace mini_blur::detail -{ - bool avx2Available() - { - static const bool available = [] { -#if MINI_BLUR_HAVE_AVX2 && defined(__GNUC__) - __builtin_cpu_init(); - return bool(__builtin_cpu_supports("avx2")); -#elif MINI_BLUR_HAVE_AVX2 && defined(_MSC_VER) - int info[4]; - __cpuid(info, 0); - if (info[0] < 7) - return false; - __cpuidex(info, 1, 0); - if ((info[2] & (1 << 27)) == 0 || (info[2] & (1 << 28)) == 0) - return false; - if ((_xgetbv(0) & 6) != 6) - return false; - __cpuidex(info, 7, 0); - return (info[1] & (1 << 5)) != 0; -#else - return false; -#endif - }(); - return available; - } -#if MINI_BLUR_HAVE_AVX2 && defined(__GNUC__) -#define MINI_BLUR_AVX2_TARGET __attribute__((target("avx2"))) -#else -#define MINI_BLUR_AVX2_TARGET -#endif - MINI_BLUR_AVX2_TARGET void horizontalAvx2( - const std::uint16_t *center, - std::uint16_t *dst, - std::size_t n, - std::uint16_t middle, - const std::vector &pairs) - { - std::size_t i = 0; -#if MINI_BLUR_HAVE_AVX2 - for (; i + 16 <= n; i += 16) - { - auto sum = _mm256_mullo_epi16(_mm256_loadu_si256(reinterpret_cast(center + i)), - _mm256_set1_epi16(middle)); - for (const auto &t: pairs) - { - const auto l = _mm256_loadu_si256(reinterpret_cast(center + i - t.offset)); - const auto r = _mm256_loadu_si256(reinterpret_cast(center + i + t.offset)); - sum = _mm256_add_epi16(sum, _mm256_mullo_epi16(_mm256_add_epi16(l, r), _mm256_set1_epi16(t.weight))); - } - _mm256_storeu_si256(reinterpret_cast<__m256i *>(dst + i), sum); - } - _mm256_zeroupper(); -#endif - horizontal(center + i, dst + i, n - i, middle, pairs); - } - - MINI_BLUR_AVX2_TARGET void verticalAvx2( - const std::uint16_t *const *rows, - const std::vector &taps, - std::uint8_t *dst, - std::size_t n) - { - std::size_t i = 0; -#if MINI_BLUR_HAVE_AVX2 - for (; i + 16 <= n; i += 16) - { - auto lo = _mm256_set1_epi32(32768), hi = lo; - for (std::size_t j = 0; j < taps.size(); ++j) - { - const auto a = _mm256_loadu_si256(reinterpret_cast(rows[j] + i)); - const auto w = _mm256_set1_epi16(taps[j].weight); - const auto l = _mm256_mullo_epi16(a, w), h = _mm256_mulhi_epu16(a, w); - lo = _mm256_add_epi32(lo, _mm256_unpacklo_epi16(l, h)); - hi = _mm256_add_epi32(hi, _mm256_unpackhi_epi16(l, h)); - } - const auto words = _mm256_packs_epi32(_mm256_srli_epi32(lo, 16), _mm256_srli_epi32(hi, 16)); - const auto bytes = _mm_packus_epi16(_mm256_castsi256_si128(words), _mm256_extracti128_si256(words, 1)); - _mm_storeu_si128(reinterpret_cast<__m128i *>(dst + i), bytes); - } - _mm256_zeroupper(); -#endif - for (; i < n; ++i) - { - std::uint32_t sum = 32768; - for (std::size_t j = 0; j < taps.size(); ++j) - sum += rows[j][i] * taps[j].weight; - dst[i] = static_cast(sum >> 16); - } - } -#undef MINI_BLUR_AVX2_TARGET -} // namespace mini_blur::detail diff --git a/src/include/mini_blur/mini_blur_avx2.h b/src/include/mini_blur/mini_blur_avx2.h deleted file mode 100644 index 087c128e..00000000 --- a/src/include/mini_blur/mini_blur_avx2.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef MINI_BLUR_AVX2_H -#define MINI_BLUR_AVX2_H -#include "mini_blur_simd.h" - - -namespace mini_blur::detail -{ - bool avx2Available(); - - void horizontalAvx2(const std::uint16_t *, std::uint16_t *, std::size_t, std::uint16_t, const std::vector &); - - void verticalAvx2(const std::uint16_t *const *, const std::vector &, std::uint8_t *, std::size_t); -} // namespace mini_blur::detail - -#endif // MINI_BLUR_AVX2_H diff --git a/src/include/mini_blur/mini_blur_qimage.h b/src/include/mini_blur/mini_blur_qimage.h deleted file mode 100644 index 656b87a7..00000000 --- a/src/include/mini_blur/mini_blur_qimage.h +++ /dev/null @@ -1,152 +0,0 @@ -#ifndef MINI_BLUR_QIMAGE_H -#define MINI_BLUR_QIMAGE_H - -#include "mini_blur.h" -#include -#include -#include - -namespace mini_blur -{ - namespace detail - { - inline int channelsFor(QImage::Format format) - { - switch (format) - { - case QImage::Format_Grayscale8: return 1; - case QImage::Format_RGB888: return 3; - case QImage::Format_RGB32: - case QImage::Format_ARGB32: - case QImage::Format_ARGB32_Premultiplied: - case QImage::Format_RGBA8888: - case QImage::Format_RGBA8888_Premultiplied: return 4; - default: return 0; - } - } - - inline QImage supportedImage(const QImage &image) - { - if (image.isNull()) - throw std::invalid_argument("mini_blur: empty QImage"); - if (channelsFor(image.format())) - return image; - // Converts indexed and 16-bit formats to straight RGBA. - return image.convertToFormat(QImage::Format_RGBA8888); - } - - inline ImageView view(const QImage &image) - { - return { - image.constBits(), - image.width(), - image.height(), - channelsFor(image.format()), - static_cast(image.bytesPerLine()) - }; - } - - inline MutableImageView mutableView(QImage &image) - { - return { - image.bits(), - image.width(), - image.height(), - channelsFor(image.format()), - static_cast(image.bytesPerLine()) - }; - } - - inline void finishImage(const QImage &source, QImage &output, Border border) - { - // RGB32 requires an opaque alpha byte, even with zero border extension. - if (output.format() == QImage::Format_RGB32 && border == Border::ConstantZero) - for (int y = 0; y < output.height(); ++y) - { - auto *row = reinterpret_cast(output.scanLine(y)); - for (int x = 0; x < output.width(); ++x) - row[x] |= 0xff000000u; - } - output.setDevicePixelRatio(source.devicePixelRatio()); - output.setDotsPerMeterX(source.dotsPerMeterX()); - output.setDotsPerMeterY(source.dotsPerMeterY()); - output.setColorSpace(source.colorSpace()); - } - } // namespace detail - - // High-frequency API: reuses workers, scratch storage and a detached output - // image of matching size/format. Input/output may be the same QImage. - inline void gaussianBlurTo( - Context &context, - const QImage &input, - QImage &output, - int kw, - int kh, - double sx = 0, - double sy = 0, - Border border = Border::Reflect101) - { - const QImage source = detail::supportedImage(input); - if (output.size() != source.size() || output.format() != source.format()) - output = QImage(source.size(), source.format()); - if (output.isNull()) - throw std::bad_alloc(); - context.gaussianBlur(detail::view(source), detail::mutableView(output), kw, kh, sx, sy, border); - detail::finishImage(source, output, border); - } - - inline void boxBlurTo( - Context &context, - const QImage &input, - QImage &output, - int kw, - int kh, - Border border = Border::Reflect101) - { - const QImage source = detail::supportedImage(input); - if (output.size() != source.size() || output.format() != source.format()) - output = QImage(source.size(), source.format()); - if (output.isNull()) - throw std::bad_alloc(); - context.boxBlur(detail::view(source), detail::mutableView(output), kw, kh, border); - detail::finishImage(source, output, border); - } - - // Returns a new QImage. A supported input format is retained; other formats - // are converted to RGBA8888. Alpha is filtered just like the color channels. - inline QImage gaussianBlur( - const QImage &input, - int kernelWidth, - int kernelHeight, - double sigmaX = 0, - double sigmaY = 0, - Border border = Border::Reflect101) - { - const QImage source = detail::supportedImage(input); - QImage output(source.size(), source.format()); - if (output.isNull()) - throw std::bad_alloc(); - gaussianBlur(detail::view(source), detail::mutableView(output), kernelWidth, kernelHeight, - sigmaX, sigmaY, border); - detail::finishImage(source, output, border); - return output; - } - - inline QImage boxBlur(const QImage &input, int kernelWidth, int kernelHeight, Border border = Border::Reflect101) - { - const QImage source = detail::supportedImage(input); - QImage output(source.size(), source.format()); - if (output.isNull()) - throw std::bad_alloc(); - boxBlur(detail::view(source), detail::mutableView(output), kernelWidth, kernelHeight, border); - detail::finishImage(source, output, border); - return output; - } - - inline QImage squareBlur(const QImage &input, int kernelSize, Border border = Border::Reflect101) - { - return boxBlur(input, kernelSize, kernelSize, border); - } -} // namespace mini_blur - -#endif // MINI_BLUR_QIMAGE_H diff --git a/src/include/mini_blur/mini_blur_simd.h b/src/include/mini_blur/mini_blur_simd.h deleted file mode 100644 index 6687e1a8..00000000 --- a/src/include/mini_blur/mini_blur_simd.h +++ /dev/null @@ -1,176 +0,0 @@ -// Internal SSE2 kernels; scalar fallback on other targets. -#ifndef MINI_BLUR_SIMD_H -#define MINI_BLUR_SIMD_H -#include -#include -#include -#if !defined(MINI_BLUR_DISABLE_SIMD) && \ - (defined(__SSE2__) || defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2)) -#define MINI_BLUR_SSE2 1 -#include -#else -#define MINI_BLUR_SSE2 0 -#endif - -namespace mini_blur::detail -{ - struct Tap - { - int offset; - std::uint16_t weight; - }; - - inline void expand(const std::uint8_t *src, std::uint16_t *dst, std::size_t n) - { - std::size_t i = 0; -#if MINI_BLUR_SSE2 - for (; i + 16 <= n; i += 16) - { - const auto a = _mm_loadu_si128(reinterpret_cast(src + i)); - _mm_storeu_si128(reinterpret_cast<__m128i *>(dst + i), - _mm_unpacklo_epi8(a, _mm_setzero_si128())); - _mm_storeu_si128(reinterpret_cast<__m128i *>(dst + i + 8), - _mm_unpackhi_epi8(a, _mm_setzero_si128())); - } -#endif - for (; i < n; ++i) - dst[i] = src[i]; - } - - inline void horizontal( - const std::uint16_t *center, - std::uint16_t *dst, - std::size_t n, - std::uint16_t middle, - const std::vector &pairs) - { - std::size_t i = 0; -#if MINI_BLUR_SSE2 - for (; i + 8 <= n; i += 8) - { - auto sum = _mm_mullo_epi16(_mm_loadu_si128(reinterpret_cast(center + i)), - _mm_set1_epi16(middle)); - for (const auto &t: pairs) - { - const auto l = - _mm_loadu_si128(reinterpret_cast(center + i - t.offset)); - const auto r = - _mm_loadu_si128(reinterpret_cast(center + i + t.offset)); - sum = - _mm_add_epi16(sum, _mm_mullo_epi16(_mm_add_epi16(l, r), _mm_set1_epi16(t.weight))); - } - _mm_storeu_si128(reinterpret_cast<__m128i *>(dst + i), sum); - } -#endif - for (; i < n; ++i) - { - std::uint32_t sum = center[i] * middle; - for (const auto &t: pairs) - sum += ((center + i)[-t.offset] + (center + i)[t.offset]) * t.weight; - dst[i] = static_cast(sum); - } - } - - inline void vertical(const std::uint16_t *const *rows, const std::vector &taps, std::uint8_t *dst, std::size_t n) - { - std::size_t i = 0; -#if MINI_BLUR_SSE2 - for (; i + 8 <= n; i += 8) - { - auto lo = _mm_set1_epi32(32768), hi = lo; - for (std::size_t j = 0; j < taps.size(); ++j) - { - const auto a = _mm_loadu_si128(reinterpret_cast(rows[j] + i)); - const auto w = _mm_set1_epi16(taps[j].weight); - const auto l = _mm_mullo_epi16(a, w), h = _mm_mulhi_epu16(a, w); - lo = _mm_add_epi32(lo, _mm_unpacklo_epi16(l, h)); - hi = _mm_add_epi32(hi, _mm_unpackhi_epi16(l, h)); - } - const auto words = _mm_packs_epi32(_mm_srli_epi32(lo, 16), _mm_srli_epi32(hi, 16)); - _mm_storel_epi64(reinterpret_cast<__m128i *>(dst + i), _mm_packus_epi16(words, words)); - } -#endif - for (; i < n; ++i) - { - std::uint32_t sum = 32768; - for (std::size_t j = 0; j < taps.size(); ++j) - sum += rows[j][i] * taps[j].weight; - dst[i] = static_cast(sum >> 16); - } - } - - inline void updateSum(std::uint32_t *sums, const std::uint32_t *entering, const std::uint32_t *leaving, std::size_t n) - { - std::size_t i = 0; -#if MINI_BLUR_SSE2 - for (; i + 4 <= n; i += 4) - { - auto s = _mm_loadu_si128(reinterpret_cast(sums + i)); - if (leaving) - s = _mm_sub_epi32(s, _mm_loadu_si128(reinterpret_cast(leaving + i))); - if (entering) - s = _mm_add_epi32(s, _mm_loadu_si128(reinterpret_cast(entering + i))); - _mm_storeu_si128(reinterpret_cast<__m128i *>(sums + i), s); - } -#endif - for (; i < n; ++i) - { - if (leaving) - sums[i] -= leaving[i]; - if (entering) - sums[i] += entering[i]; - } - } -#if MINI_BLUR_SSE2 - inline __m128i loadPixel4(const std::uint8_t *in, int x) - { - if (x < 0) - return _mm_setzero_si128(); - std::uint32_t bytes; - std::memcpy(&bytes, in + static_cast(x) * 4, 4); - const auto words = - _mm_unpacklo_epi8(_mm_cvtsi32_si128(static_cast(bytes)), _mm_setzero_si128()); - return _mm_unpacklo_epi16(words, _mm_setzero_si128()); - } - - inline void boxHorizontal4(const std::uint8_t *in, std::uint32_t *out, int width, int kw, const int *map) - { - auto sum = _mm_setzero_si128(); - for (int i = 0; i < kw; ++i) - sum = _mm_add_epi32(sum, loadPixel4(in, map[i])); - for (int x = 0; x < width; ++x) - { - _mm_storeu_si128(reinterpret_cast<__m128i *>(out + std::size_t(x) * 4), sum); - sum = _mm_sub_epi32(sum, loadPixel4(in, map[x])); - sum = _mm_add_epi32(sum, loadPixel4(in, map[std::size_t(x) + kw])); - } - } -#endif - inline void average(const std::uint32_t *sums, std::uint8_t *dst, std::size_t n, std::uint32_t area) - { - std::size_t i = 0; -#if MINI_BLUR_SSE2 - const auto scale = _mm_set1_pd(1.0 / area); - // Correct reciprocal error at exact half ties. For kernels <=4095, - // the next non-tie is more than 2.9e-8 away. - const auto round = _mm_set1_pd(0.5 + 1e-10), highScale = _mm_set1_pd(2147483648.0); - for (; i + 4 <= n; i += 4) - { - const auto s = _mm_loadu_si128(reinterpret_cast(sums + i)); - const auto low = _mm_and_si128(s, _mm_set1_epi32(0x7fffffff)), high = _mm_srli_epi32(s, 31); - const auto a = _mm_add_pd(_mm_cvtepi32_pd(low), _mm_mul_pd(_mm_cvtepi32_pd(high), highScale)); - const auto b = _mm_add_pd(_mm_cvtepi32_pd(_mm_srli_si128(low, 8)), - _mm_mul_pd(_mm_cvtepi32_pd(_mm_srli_si128(high, 8)), highScale)); - const auto q0 = _mm_cvttpd_epi32(_mm_add_pd(_mm_mul_pd(a, scale), round)); - const auto q1 = _mm_cvttpd_epi32(_mm_add_pd(_mm_mul_pd(b, scale), round)); - const auto q = _mm_unpacklo_epi64(q0, q1), w = _mm_packs_epi32(q, q); - const auto bytes = static_cast(_mm_cvtsi128_si32(_mm_packus_epi16(w, w))); - std::memcpy(dst + i, &bytes, 4); - } -#endif - for (; i < n; ++i) - dst[i] = static_cast((std::uint64_t(sums[i]) + area / 2) / area); - } -} // namespace mini_blur::detail - -#endif // MINI_BLUR_SIMD_H diff --git a/src/include/mini_blur/mini_blur_workers.h b/src/include/mini_blur/mini_blur_workers.h deleted file mode 100644 index 128652df..00000000 --- a/src/include/mini_blur/mini_blur_workers.h +++ /dev/null @@ -1,114 +0,0 @@ -#ifndef MINI_BLUR_WORKERS_H -#define MINI_BLUR_WORKERS_H -#include -#include -#include -#include -#include - - -namespace mini_blur::detail -{ - // Context-owned pool. Jobs finish before run returns; caller is worker 0. - class Workers - { - std::vector workers_; - std::mutex mutex_; - std::condition_variable ready_, done_; - void *jobData_ = nullptr; - - void (*jobFunction_)(void *, unsigned) = nullptr; - - std::exception_ptr error_; - unsigned generation_ = 0, pending_ = 0; - bool stop_ = false; - - void execute(unsigned id) - { - try - { - jobFunction_(jobData_, id); - } - catch (...) - { - std::lock_guard lock(mutex_); - if (!error_) - error_ = std::current_exception(); - } - } - - public: - explicit Workers(unsigned count) - { - try - { - for (unsigned id = 1; id < count; ++id) - workers_.emplace_back([this, id] { - unsigned seen = 0; - std::unique_lock lock(mutex_); - for (;;) - { - ready_.wait(lock, [&] { return stop_ || generation_ != seen; }); - if (stop_) - return; - seen = generation_; - lock.unlock(); - execute(id); - lock.lock(); - if (--pending_ == 0) - done_.notify_one(); - } - }); - } - catch (...) - { - shutdown(); - throw; - } - } - - ~Workers() { shutdown(); } - - void shutdown() - { - { - std::lock_guard lock(mutex_); - stop_ = true; - } - ready_.notify_all(); - for (auto &t: workers_) - if (t.joinable()) - t.join(); - } - - [[nodiscard]] unsigned count() const { return static_cast(workers_.size()) + 1; } - - template - void run(F job) - { - if (workers_.empty()) - { - job(0); - return; - } - { - std::lock_guard lock(mutex_); - jobData_ = &job; - jobFunction_ = [](void *data, unsigned id) { (*static_cast(data))(id); }; - error_ = nullptr; - pending_ = static_cast(workers_.size()); - ++generation_; - } - ready_.notify_all(); - execute(0); - std::unique_lock lock(mutex_); - done_.wait(lock, [&] { return pending_ == 0; }); - jobData_ = nullptr; - jobFunction_ = nullptr; - if (error_) - std::rethrow_exception(error_); - } - }; -} // namespace mini_blur::detail - -#endif // MINI_BLUR_WORKERS_H diff --git a/src/resource/ColorProfile.json b/src/resource/ColorProfile.json deleted file mode 100644 index 0346ef87..00000000 --- a/src/resource/ColorProfile.json +++ /dev/null @@ -1,272 +0,0 @@ -{ - "System": { - "Light": { - "Window": "#F3F3F3", - "WindowText": "#000000", - "WindowBright": "#C42B1C", - "Border": "#838383", - "Text": "#000000", - "SelectorBarItemTextHover": "#5D5D5D", - "SelectorBarItemTextPressed": "#868686", - "SelectorBarItemTextSelected": "#000000", - "SelectorBarItemTextSelectedHover": "#5D5D5D", - "SelectorBarItemTextSelectedPressed": "#5D5D5D", - "SelectorBarItemBackground": "#00000000", - "Button": "#FCFCFC", - "ButtonBorder": "#D9D9D9", - "ButtonBottomLine": "#D3D3D3", - "RadioButtonInnerCircle": "#F3F3F3", - "Link": "$sys:AccentLight2", - "ImportantLink": "$sys:AccentDark2", - "LinkVisited": "#954F72", - "DisEnabled": "#6D6D6D", - "DisEnabledText": "#00000040", - "AppAccent": "$sys:AccentDark1", - "SysAccent": "$sys:Accent", - "AppAccentText": "$calc:apcaTextColor($role:AppAccent)", - "MaskNormal": "#00000000", - "MaskHover": "#0000000A", - "MaskPressed": "#00000006", - "MaskBrightPressed": "#E6DCFF18", - "MaskSelected": "#5A5A5A0F", - "MaskSelectedHover": "#49494909", - "MaskSelectedPressed": "#5A5A5A0F", - "MSWindow": "#FCFCFCAA", - "MSWindowSplitLine": "#E5E5E5", - "ScrollBarNormal": "#00000000", - "ScrollBarFocus": "#F9F9F9", - "ScrollBarEndArrow": "#8A8A8A", - "ScrollBarSlider": "#8A8A8A", - "TextEditPanelNormal": "#FBFBFB", - "TextEditPanelFocus": "#FFFFFF", - "TextEditPanelHover": "#F6F6F6", - "TextEditBorder": "#E5E5E5", - "TextEditIndicatorLine": "#868686", - "PlaceholderText": "#5F5F5F" - }, - "Dark": { - "Window": "#202020", - "WindowText": "#FFFFFF", - "WindowBright": "#C42B1C", - "Border": "#838383", - "Text": "#FFFFFF", - "SelectorBarItemTextHover": "#CCCCCC", - "SelectorBarItemTextPressed": "#646464", - "SelectorBarItemTextSelected": "#FFFFFF", - "SelectorBarItemTextSelectedHover": "#CCCCCC", - "SelectorBarItemTextSelectedPressed": "#CCCCCC", - "SelectorBarItemBackground": "#00000000", - "Button": "#373737", - "ButtonBorder": "#424242", - "ButtonBottomLine": "#2C2C2C", - "RadioButtonInnerCircle": "#202020", - "Link": "$sys:AccentLight3", - "ImportantLink": "$sys:AccentLight3", - "LinkVisited": "#E09EBC", - "DisEnabled": "#B4B4B4", - "DisEnabledText": "#FFFFFF40", - "AppAccent": "$sys:AccentLight2", - "SysAccent": "$sys:Accent", - "AppAccentText": "$calc:apcaTextColor($role:AppAccent)", - "MaskNormal": "#00000000", - "MaskHover": "#FFFFFF0F", - "MaskPressed": "#FFFFFF0A", - "MaskBrightPressed": "#00001216", - "MaskSelected": "#FDFDFD0F", - "MaskSelectedHover": "#B9B9B90F", - "MaskSelectedPressed": "#FDFDFD0F", - "MSWindow": "#9797970F", - "MSWindowSplitLine": "#0F0F0F2D", - "ScrollBarNormal": "#00000000", - "ScrollBarFocus": "#2C2C2C", - "ScrollBarEndArrow": "#9F9F9F", - "ScrollBarSlider": "#9F9F9F", - "TextEditPanelNormal": "#2D2D2D", - "TextEditPanelFocus": "#1F1F1F", - "TextEditPanelHover": "#323232", - "TextEditBorder": "#303030", - "TextEditIndicatorLine": "#9A9A9A", - "PlaceholderText": "#CFCFCF" - } - }, - "Default": { - "Light": { - "Window": "#F0F3F9", - "WindowText": "#000000", - "WindowBright": "#C42B1C", - "Border": "#A0A0A0", - "Text": "#000000", - "SelectorBarItemTextHover": "#5D5D5D", - "SelectorBarItemTextPressed": "#868686", - "SelectorBarItemTextSelected": "#000000", - "SelectorBarItemTextSelectedHover": "#5D5D5D", - "SelectorBarItemTextSelectedPressed": "#5D5D5D", - "SelectorBarItemBackground": "#00000000", - "Button": "#FCFCFC", - "ButtonBorder": "#D9D9D9", - "ButtonBottomLine": "#D3D3D3", - "RadioButtonInnerCircle": "#F0F3F9", - "Link": "$cus:AccentLight2", - "ImportantLink": "$cus:AccentDark2", - "LinkVisited": "#D4624E", - "DisEnabled": "#6D6D6D", - "DisEnabledText": "#00000040", - "AppAccent": "$cus:AccentDark1", - "SysAccent": "$sys:Accent", - "AppAccentText": "$calc:apcaTextColor($role:AppAccent)", - "MaskNormal": "#00000000", - "MaskHover": "#0000000A", - "MaskPressed": "#00000006", - "MaskBrightPressed": "#E6DCFF18", - "MaskSelected": "#5A5A5A0F", - "MaskSelectedHover": "#49494909", - "MaskSelectedPressed": "#5A5A5A0F", - "MSWindow": "#FCFCFCAA", - "MSWindowSplitLine": "#E5E5E5", - "ScrollBarNormal": "#00000000", - "ScrollBarFocus": "#F9F9F9", - "ScrollBarEndArrow": "#8A8A8A", - "ScrollBarSlider": "#8A8A8A", - "TextEditPanelNormal": "#FBFBFB", - "TextEditPanelFocus": "#FFFFFF", - "TextEditPanelHover": "#F6F6F6", - "TextEditBorder": "#E5E5E5", - "TextEditIndicatorLine": "#868686", - "PlaceholderText": "#5F5F5F" - }, - "Dark": { - "Window": "#1A2034", - "WindowText": "#FFFFFF", - "WindowBright": "#C42B1C", - "Border": "#838383", - "Text": "#FFFFFF", - "SelectorBarItemTextHover": "#CCCCCC", - "SelectorBarItemTextPressed": "#646464", - "SelectorBarItemTextSelected": "#FFFFFF", - "SelectorBarItemTextSelectedHover": "#CCCCCC", - "SelectorBarItemTextSelectedPressed": "#CCCCCC", - "SelectorBarItemBackground": "#00000000", - "Button": "#373737", - "ButtonBorder": "#424242", - "ButtonBottomLine": "#2C2C2C", - "RadioButtonInnerCircle": "#1A2034", - "Link": "$cus:AccentLight3", - "ImportantLink": "$cus:AccentLight3", - "LinkVisited": "#D4624E", - "DisEnabled": "#B4B4B4", - "DisEnabledText": "#FFFFFF40", - "AppAccent": "$cus:AccentLight2", - "SysAccent": "$sys:Accent", - "AppAccentText": "$calc:apcaTextColor($role:AppAccent)", - "MaskNormal": "#00000000", - "MaskHover": "#FFFFFF0F", - "MaskPressed": "#FFFFFF0A", - "MaskBrightPressed": "#00001216", - "MaskSelected": "#FDFDFD0F", - "MaskSelectedHover": "#B9B9B90F", - "MaskSelectedPressed": "#FDFDFD0F", - "MSWindow": "#9797970F", - "MSWindowSplitLine": "#0F0F0F2D", - "ScrollBarNormal": "#00000000", - "ScrollBarFocus": "#2C2C2C", - "ScrollBarEndArrow": "#9F9F9F", - "ScrollBarSlider": "#9F9F9F", - "TextEditPanelNormal": "#2D2D2D", - "TextEditPanelFocus": "#1F1F1F", - "TextEditPanelHover": "#323232", - "TextEditBorder": "#303030", - "TextEditIndicatorLine": "#9A9A9A", - "PlaceholderText": "#CFCFCF" - } - }, - "Custom": { - "Light": { - "Window": "#F3F3F3", - "WindowText": "#000000", - "WindowBright": "#C42B1C", - "Border": "#838383", - "Text": "#000000", - "SelectorBarItemTextHover": "#5D5D5D", - "SelectorBarItemTextPressed": "#868686", - "SelectorBarItemTextSelected": "#000000", - "SelectorBarItemTextSelectedHover": "#5D5D5D", - "SelectorBarItemTextSelectedPressed": "#5D5D5D", - "SelectorBarItemBackground": "#00000000", - "Button": "#FCFCFC", - "ButtonBorder": "#D9D9D9", - "ButtonBottomLine": "#D3D3D3", - "RadioButtonInnerCircle": "#F3F3F3", - "Link": "$cus:AccentLight2", - "ImportantLink": "$cus:AccentDark2", - "LinkVisited": "#954F72", - "DisEnabled": "#6D6D6D", - "DisEnabledText": "#00000040", - "AppAccent": "$cus:AccentDark1", - "SysAccent": "$sys:Accent", - "AppAccentText": "$calc:apcaTextColor($role:AppAccent)", - "MaskNormal": "#00000000", - "MaskHover": "#0000000A", - "MaskPressed": "#00000006", - "MaskBrightPressed": "#E6DCFF18", - "MaskSelected": "#5A5A5A0F", - "MaskSelectedHover": "#49494909", - "MaskSelectedPressed": "#5A5A5A0F", - "MSWindow": "#FCFCFCAA", - "MSWindowSplitLine": "#E5E5E5", - "ScrollBarNormal": "#00000000", - "ScrollBarFocus": "#F9F9F9", - "ScrollBarEndArrow": "#8A8A8A", - "ScrollBarSlider": "#8A8A8A", - "TextEditPanelNormal": "#FBFBFB", - "TextEditPanelFocus": "#FFFFFF", - "TextEditPanelHover": "#F6F6F6", - "TextEditBorder": "#E5E5E5", - "TextEditIndicatorLine": "#868686", - "PlaceholderText": "#5F5F5F" - }, - "Dark": { - "Window": "#202020", - "WindowText": "#FFFFFF", - "WindowBright": "#C42B1C", - "Border": "#838383", - "Text": "#FFFFFF", - "SelectorBarItemTextHover": "#CCCCCC", - "SelectorBarItemTextPressed": "#646464", - "SelectorBarItemTextSelected": "#FFFFFF", - "SelectorBarItemTextSelectedHover": "#CCCCCC", - "SelectorBarItemTextSelectedPressed": "#CCCCCC", - "SelectorBarItemBackground": "#00000000", - "Button": "#373737", - "ButtonBorder": "#424242", - "ButtonBottomLine": "#2C2C2C", - "RadioButtonInnerCircle": "#202020", - "Link": "$cus:AccentLight3", - "ImportantLink": "$cus:AccentLight3", - "LinkVisited": "#E09EBC", - "DisEnabled": "#B4B4B4", - "DisEnabledText": "#FFFFFF40", - "AppAccent": "$cus:AccentLight2", - "SysAccent": "$sys:Accent", - "AppAccentText": "$calc:apcaTextColor($role:AppAccent)", - "MaskNormal": "#00000000", - "MaskHover": "#FFFFFF0F", - "MaskPressed": "#FFFFFF0A", - "MaskBrightPressed": "#00001216", - "MaskSelected": "#FDFDFD0F", - "MaskSelectedHover": "#B9B9B90F", - "MaskSelectedPressed": "#FDFDFD0F", - "MSWindow": "#9797970F", - "MSWindowSplitLine": "#0F0F0F2D", - "ScrollBarNormal": "#00000000", - "ScrollBarFocus": "#2C2C2C", - "ScrollBarEndArrow": "#9F9F9F", - "ScrollBarSlider": "#9F9F9F", - "TextEditPanelNormal": "#2D2D2D", - "TextEditPanelFocus": "#1F1F1F", - "TextEditPanelHover": "#323232", - "TextEditBorder": "#303030", - "TextEditIndicatorLine": "#9A9A9A", - "PlaceholderText": "#CFCFCF" - } - } -} diff --git a/src/resource/FancyResource.qrc b/src/resource/FancyResource.qrc index fce4cd15..67b0fa1d 100644 --- a/src/resource/FancyResource.qrc +++ b/src/resource/FancyResource.qrc @@ -5418,7 +5418,4 @@ icons/IconPark/ZoomInternal.svg icons/IconPark/ZoomOut.svg
- - ColorProfile.json -