commit 08a070d874c49ff4523ebbee5d82964bd5db8d09 Author: Misakiotoha <1841738040@qq.com> Date: Thu Jun 18 17:29:28 2026 +0800 frist diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..28f9fce --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +# Python-generated files +__pycache__/ +*.py[oc] +build/ +dist/ +wheels/ +*.egg-info + +# from ide +.vscode +.idea + +# Virtual environments +.venv +venv + +# config +settings.json + +# logs +log/* + +cmake-build-debug + + diff --git a/3rdparty/ElaWidgetTools/.gitignore b/3rdparty/ElaWidgetTools/.gitignore new file mode 100644 index 0000000..dac5832 --- /dev/null +++ b/3rdparty/ElaWidgetTools/.gitignore @@ -0,0 +1,59 @@ +# C++ objects and libs +*.slo +*.lo +*.o +*.a +*.la +*.lai +*.so +*.so.* +*.dll +*.dylib + +# Qt-es +object_script.*.Release +object_script.*.Debug +*_plugin_import.cpp +/.qmake.cache +/.qmake.stash +*.pro.user +*.pro.user.* +*.qbs.user +*.qbs.user.* +*.moc +moc_*.cpp +moc_*.h +qrc_*.cpp +ui_*.h +*.qmlc +*.jsc +Makefile* +*build-* +*.qm +*.prl + +# Qt unit tests +target_wrapper.* + +# QtCreator +*.autosave + +# QtCreator Qml +*.qmlproject.user +*.qmlproject.user.* + +# QtCreator CMake +CMakeLists.txt.user* + +# QtCreator 4.8< compilation database +compile_commands.json + +# QtCreator local machine specific files for imported projects +*creator.user* + +*_qmlcache.qrc + +# CLion +/.idea +*.clang-format +/install diff --git a/3rdparty/ElaWidgetTools/CMakeLists.txt b/3rdparty/ElaWidgetTools/CMakeLists.txt new file mode 100644 index 0000000..2e66cd6 --- /dev/null +++ b/3rdparty/ElaWidgetTools/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.5) + +project(ElaWidgetTools VERSION 0.1 LANGUAGES CXX) + +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(BUILD_ELAWIDGETTOOLS_EXAMPLE FALSE CACHE BOOL "Build Example") +add_compile_options("$<$:/utf-8>") + +find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets) + +add_subdirectory(src) +if(BUILD_ELAWIDGETTOOLS_EXAMPLE) + add_subdirectory(example) +endif () + +# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1. +# If you are developing for iOS or macOS you should consider setting an +# explicit, fixed bundle identifier manually though. +if(${QT_VERSION} VERSION_LESS 6.1.0) + set(BUNDLE_ID_OPTION MACOSX_BUNDLE_GUI_IDENTIFIER com.example.ElaWidgetTools) +endif() diff --git a/3rdparty/ElaWidgetTools/LICENSE b/3rdparty/ElaWidgetTools/LICENSE new file mode 100644 index 0000000..b80cad3 --- /dev/null +++ b/3rdparty/ElaWidgetTools/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Liniyous + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/3rdparty/ElaWidgetTools/README.md b/3rdparty/ElaWidgetTools/README.md new file mode 100644 index 0000000..c7281f5 --- /dev/null +++ b/3rdparty/ElaWidgetTools/README.md @@ -0,0 +1,100 @@ +# ElaWidgetTools + +## 简介 + +本项目是基于QT-Widget开发的FluentUI风格的组件库,同时提供不限于组件的常用集成功能;目前Main分支支持QT5.12以上所有版本( +Linux为QT5.15以上),推荐版本为QT6.4.3和QT6.6.2; + +## 重要提示 + +该项目项目性质原因,暂不接受任何PR!!!使用问题或BUG请直接进群反馈,都会及时处理,感谢您的支持! 企鹅群:850243692 + +## 支持平台 + +| [Windows][win-link] | [Ubuntu][ubuntu-link] +|---------------------|-----------------------| +| ![win-badge] | ![ubuntu-badge] | + +[win-link]: https://github.com/Liniyous/ElaWidgetTools/actions?query=workflow%3AWindows "WindowsAction" + +[win-badge]: https://img.shields.io/badge/Windows-Passing-61C263 + +[ubuntu-link]: https://github.com/Liniyous/ElaWidgetTools/actions?query=workflow%3AUbuntu "UbuntuAction" + +[ubuntu-badge]: https://img.shields.io/badge/Ubuntu-Passing-61C263 + +## 主界面预览 + +
+ +
+
+ +
+ +## 支持的组件 + +| 目录 | 说明 | 备注 | +|------------------------|--------------|----------------------------| +| ElaApplication | 程序初始化 | | +| ElaAppBar | 窗口顶部标题栏 | 支持拖动窗口,最小化、最大化、关闭窗口、调整窗口尺寸 | +| ElaAcrylicUrlCard | 带图片的交互式亚克力卡片 | 支持URL跳转 | +| ElaBreadcrumbBar | 面包屑组件 | 自动处理点击事件 | +| ElaCheckBox | 勾选框 | | +| ElaComboBox | 下拉框 | | +| ElaColorDialog | 颜色选择器 | | +| ElaCalendar | 日历视图 | | +| ElaCalendarPicker | 日期选择器 | | +| ElaMultiSelectComboBox | 多选下拉框 | | +| ElaContentDialog | 带遮罩的对话框 | | +| ElaDockWidget | 停靠窗口 | | +| ElaSpinBox | 微调框 | | +| ElaDoubleSpinBox | 微调框 | 浮点数 | +| ElaDxgiManager | DXGI采集器 | 支持自动选择采集设备 效率远高于原生采集 | +| ElaEventBus | 事件总线 | | +| ElaFlowLayout | 流式布局 | 支持动画 | +| ElaIcon | 图标 | | +| ElaIconButton | 图标按钮 | | +| ElaImageCard | 图片卡片 | | +| ElaInteractiveCard | 带图片的交互式透明卡片 | | +| ElaKeyBinder | 单按键绑定器 | | +| ElaLineEdit | 输入框 | | +| ElaListView | 列表视图 | | +| ElaLog | 消息日志 | | +| ElaMenu | 菜单 | | +| ElaMenuBar | 菜单栏 | | +| ElaMessageBar | 弹出式信息栏 | 支持八方向,锚定位置 | +| ElaMessageButton | 弹出信息按钮 | | +| ElaPivot | 轴转导航 | | +| ElaPlainTextEdit | 文本编辑框 | | +| ElaPushButton | 按钮 | | +| ElaPopularCard | 热门卡片 | | +| ElaProgressBar | 进度条 | | +| ElaPromotionCard | 促销卡片 | | +| ElaPromotionView | 促销卡片视窗 | | +| ElaSwitchButton | 切换按钮 | | +| ElaSuggestBox | 建议搜索框 | | +| ElaRadioButton | 单选按钮 | | +| ElaNavigationRouter | 路由跳转 | | +| ElaToggleSwitch | 开关按钮 | | +| ElaTabBar | 选项卡 | 谷歌浏览器风格 | +| ElaTabWidget | 选项卡页面 | 谷歌浏览器风格 | +| ElaTableView | 表格视图 | | +| ElaTreeView | 树型视图 | | +| ElaToolButton | 带下拉菜单的工具按钮 | | +| ElaToolBar | 工具栏 | | +| ElaTheme | 主题管理器 | | +| ElaReminderCard | 带图片的提醒卡片 | | +| ElaScrollbar | 滚动条 | | +| ElaScrollArea | 滚动区域 | 可设置鼠标拖动 | +| ElaScrollPage | 滚动页面 | 自带堆栈页面和面包屑导航 | +| ElaScrollPageArea | 滚动页面区域组件 | | +| ElaSlider | 拖动条 | | +| ElaStatusBar | 状态栏 | | +| ElaExponentialBlur | 指数模糊 | | +| ElaText | Text文本 | | +| ElaGraphicsScene | 高级场景 | 大量实用API | +| ElaGraphicsView | 高级视图 | 按键缩放、拖动 | +| ElaGraphicsItem | 高级图元 | 大量实用API | +| ElaWindow | 带导航栏的无边框窗口 | | +| ElaWidget | 无边框模态窗口 | | \ No newline at end of file diff --git a/3rdparty/ElaWidgetTools/example/CMakeLists.txt b/3rdparty/ElaWidgetTools/example/CMakeLists.txt new file mode 100644 index 0000000..ff9c7c7 --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/CMakeLists.txt @@ -0,0 +1,108 @@ +cmake_minimum_required(VERSION 3.5) + +project(ElaWidgetToolsExample VERSION 0.1 LANGUAGES CXX) + +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +add_compile_options("$<$:/utf-8>") + +find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets) + +FILE(GLOB ORIGIN *.cpp *.h) +FILE(GLOB MODELVIEW ModelView/*.h ModelView/*.cpp) +FILE(GLOB EAXMPLEPAGE ExamplePage/*.h ExamplePage/*.cpp) + +source_group(ModelView FILES ${MODELVIEW}) +source_group(ExamplePage FILES ${EAXMPLEPAGE}) + + +set(PROJECT_SOURCES + ${ORIGIN} + ${MODELVIEW} + ${EAXMPLEPAGE} +) + +if (${QT_VERSION_MAJOR} GREATER_EQUAL 6) + qt_add_executable(ElaWidgetToolsExample + MANUAL_FINALIZATION + ${PROJECT_SOURCES} + ) + + #遍历所有资源文件 + file(GLOB_RECURSE RES_PATHS *.png *.jpg *.svg *.ico *.ttf *.webp *.js) + foreach (filepath ${RES_PATHS}) + string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" filename ${filepath}) + list(APPEND resource_files ${filename}) + endforeach (filepath) + + qt_add_resources(${PROJECT_NAME} "ElaWidgetToolsExample" + RESOURCES PREFIX "/" + FILES + ${resource_files} + ) + # Define target properties for Android with Qt 6 as: + # set_property(TARGET ElaWidgetToolsExample APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR + # ${CMAKE_CURRENT_SOURCE_DIR}/android) + # For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation +else () + if (ANDROID) + add_library(ElaWidgetToolsExample SHARED + ${PROJECT_SOURCES} + ) + # Define properties for Android with Qt 5 after find_package() calls as: + # set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android") + else () + qt5_add_big_resources(PROJECT_SOURCES + ../src/include/ElaWidgetTools.qrc + ElaWidgetToolsExample.qrc + ) + add_executable(ElaWidgetToolsExample + ${PROJECT_SOURCES} + ) + endif () +endif () + +list(APPEND CMAKE_PREFIX_PATH ${CMAKE_SOURCE_DIR}/install/lib/cmake/) +find_package(ElaWidgetTools REQUIRED) +if (ELAWIDGETTOOLS_FOUND) + message(STATUS "ELAWIDGETTOOLS_INCLUDE_DIR = ${ELAWIDGETTOOLS_INCLUDE_DIRS}") + message(STATUS "ELAWIDGETTOOLS_LIBRARIES = ${ELAWIDGETTOOLS_LIBRARIES}") + message(STATUS "ELAWIDGETTOOLS_LIB_DIR = ${ELAWIDGETTOOLS_LIBRARY_DIRS}") +endif () +set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}) +target_include_directories(ElaWidgetToolsExample PUBLIC + ${ELAWIDGETTOOLS_INCLUDE_DIRS} + ExamplePage + ModelView +) +target_link_libraries(ElaWidgetToolsExample PRIVATE + Qt${QT_VERSION_MAJOR}::Widgets + ElaWidgetTools +) +target_link_directories(ElaWidgetToolsExample PUBLIC + ${ELAWIDGETTOOLS_LIBRARY_DIRS} +) + +# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1. +# If you are developing for iOS or macOS you should consider setting an +# explicit, fixed bundle identifier manually though. +if (${QT_VERSION} VERSION_LESS 6.1.0) + set(BUNDLE_ID_OPTION MACOSX_BUNDLE_GUI_IDENTIFIER com.example.ElaWidgetToolsExample) +endif () +set_target_properties(ElaWidgetToolsExample PROPERTIES + ${BUNDLE_ID_OPTION} + MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} + MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} + MACOSX_BUNDLE TRUE + WIN32_EXECUTABLE TRUE +) + +if (QT_VERSION_MAJOR EQUAL 6) + qt_finalize_executable(ElaWidgetToolsExample) +endif () diff --git a/3rdparty/ElaWidgetTools/example/ElaWidgetToolsExample.qrc b/3rdparty/ElaWidgetTools/example/ElaWidgetToolsExample.qrc new file mode 100644 index 0000000..1f22083 --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ElaWidgetToolsExample.qrc @@ -0,0 +1,124 @@ + + + Resource/Image/control/Acrylic.png + Resource/Image/control/AnimatedIcon.png + Resource/Image/control/AnimatedVisualPlayer.png + Resource/Image/control/AnimationInterop.png + Resource/Image/control/AppBarButton.png + Resource/Image/control/AppBarSeparator.png + Resource/Image/control/AppBarToggleButton.png + Resource/Image/control/AutomationProperties.png + Resource/Image/control/AutoSuggestBox.png + Resource/Image/control/Border.png + Resource/Image/control/BreadcrumbBar.png + Resource/Image/control/Button.png + Resource/Image/control/CalendarDatePicker.png + Resource/Image/control/CalendarView.png + Resource/Image/control/Canvas.png + Resource/Image/control/Checkbox.png + Resource/Image/control/Clipboard.png + Resource/Image/control/ColorPaletteResources.png + Resource/Image/control/ColorPicker.png + Resource/Image/control/ComboBox.png + Resource/Image/control/CommandBar.png + Resource/Image/control/CommandBarFlyout.png + Resource/Image/control/CompactSizing.png + Resource/Image/control/ConnectedAnimation.png + Resource/Image/control/ContentDialog.png + Resource/Image/control/CreateMultipleWindows.png + Resource/Image/control/DataGrid.png + Resource/Image/control/DatePicker.png + Resource/Image/control/DropDownButton.png + Resource/Image/control/EasingFunction.png + Resource/Image/control/Expander.png + Resource/Image/control/FilePicker.png + Resource/Image/control/FlipView.png + Resource/Image/control/Flyout.png + Resource/Image/control/Grid.png + Resource/Image/control/GridView.png + Resource/Image/control/HyperlinkButton.png + Resource/Image/control/IconElement.png + Resource/Image/control/Image.png + Resource/Image/control/ImplicitTransition.png + Resource/Image/control/InfoBadge.png + Resource/Image/control/InfoBar.png + Resource/Image/control/InkCanvas.png + Resource/Image/control/InkToolbar.png + Resource/Image/control/InputValidation.png + Resource/Image/control/ItemsRepeater.png + Resource/Image/control/Line.png + Resource/Image/control/ListBox.png + Resource/Image/control/ListView.png + Resource/Image/control/MediaPlayerElement.png + Resource/Image/control/MenuBar.png + Resource/Image/control/MenuFlyout.png + Resource/Image/control/NavigationView.png + Resource/Image/control/NumberBox.png + Resource/Image/control/PageTransition.png + Resource/Image/control/ParallaxView.png + Resource/Image/control/PasswordBox.png + Resource/Image/control/PersonPicture.png + Resource/Image/control/PipsPager.png + Resource/Image/control/Pivot.png + Resource/Image/control/ProgressBar.png + Resource/Image/control/ProgressRing.png + Resource/Image/control/PullToRefresh.png + Resource/Image/control/RadialGradientBrush.png + Resource/Image/control/RadioButton.png + Resource/Image/control/RadioButtons.png + Resource/Image/control/RatingControl.png + Resource/Image/control/RelativePanel.png + Resource/Image/control/RepeatButton.png + Resource/Image/control/RevealFocus.png + Resource/Image/control/RichEditBox.png + Resource/Image/control/RichTextBlock.png + Resource/Image/control/ScrollViewer.png + Resource/Image/control/SemanticZoom.png + Resource/Image/control/Shape.png + Resource/Image/control/Slider.png + Resource/Image/control/Sound.png + Resource/Image/control/SplitButton.png + Resource/Image/control/SplitView.png + Resource/Image/control/StackPanel.png + Resource/Image/control/StandardUICommand.png + Resource/Image/control/SwipeControl.png + Resource/Image/control/TabView.png + Resource/Image/control/TeachingTip.png + Resource/Image/control/TextBlock.png + Resource/Image/control/TextBox.png + Resource/Image/control/ThemeTransition.png + Resource/Image/control/TimePicker.png + Resource/Image/control/TitleBar.png + Resource/Image/control/ToggleButton.png + Resource/Image/control/ToggleSplitButton.png + Resource/Image/control/ToggleSwitch.png + Resource/Image/control/ToolTip.png + Resource/Image/control/TreeView.png + Resource/Image/control/VariableSizedWrapGrid.png + Resource/Image/control/Viewbox.png + Resource/Image/control/WebView.png + Resource/Image/control/XamlUICommand.png + Resource/Image/Cirno.jpg + Resource/Image/github.png + Resource/Image/Home_Background.png + Resource/Image/Moon.jpg + Resource/Image/Card/beach.png + Resource/Image/Card/classroom.png + Resource/Image/Card/dream.png + Resource/Image/Card/miku.png + Resource/Image/IARC/IARC_12+.svg.png + Resource/Image/IARC/IARC_16+.svg.png + Resource/Image/IARC/IARC_18+.svg.png + Resource/Image/IARC/IARC_3+.svg.png + Resource/Image/IARC/IARC_7+.svg.png + Resource/Image/Model/CollapsingWorld.jpg + Resource/Image/Model/DarkForest.jpg + Resource/Image/Model/LASTSTARDUST.jpg + Resource/Image/Model/Light.jpg + Resource/Image/Model/MaVieEnRose.jpg + Resource/Image/Model/NaightNavigationStar.jpg + Resource/Image/Model/RainMan.jpg + Resource/Image/Model/RunningInTheDark.jpg + Resource/Image/Model/STYXHELIX.jpg + + diff --git a/3rdparty/ElaWidgetTools/example/ExamplePage/T_About.cpp b/3rdparty/ElaWidgetTools/example/ExamplePage/T_About.cpp new file mode 100644 index 0000000..70abde7 --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ExamplePage/T_About.cpp @@ -0,0 +1,73 @@ +#include "T_About.h" + +#include +#include +#include + +#include "ElaImageCard.h" +#include "ElaText.h" +T_About::T_About(QWidget* parent) + : ElaWidget(parent) +{ + setWindowTitle("关于.."); + setWindowIcon(QIcon(":/include/Image/Moon.jpg")); + this->setIsFixedSize(true); + setWindowModality(Qt::ApplicationModal); + setWindowButtonFlags(ElaAppBarType::CloseButtonHint); + ElaImageCard* pixCard = new ElaImageCard(this); + pixCard->setFixedSize(60, 60); + pixCard->setIsPreserveAspectCrop(false); + pixCard->setCardImage(QImage(":/include/Image/Moon.jpg")); + + QVBoxLayout* pixCardLayout = new QVBoxLayout(); + pixCardLayout->addWidget(pixCard); + pixCardLayout->addStretch(); + + ElaText* versionText = new ElaText("ElaWidgetTools-LK-2024", this); + QFont versionTextFont = versionText->font(); + versionTextFont.setWeight(QFont::Bold); + versionText->setFont(versionTextFont); + versionText->setWordWrap(false); + versionText->setTextPixelSize(18); + + ElaText* licenseText = new ElaText("MIT授权协议", this); + licenseText->setWordWrap(false); + licenseText->setTextPixelSize(14); + ElaText* supportText = new ElaText("Windows支持版本: QT5.12以上\nLinux支持版本: Qt5.14以上", this); + supportText->setWordWrap(false); + supportText->setTextPixelSize(14); + ElaText* contactText = new ElaText("作者: 8009963@qq.com\n交流群: 850243692(QQ)", this); + contactText->setWordWrap(false); + contactText->setTextInteractionFlags(Qt::TextSelectableByMouse); + contactText->setTextPixelSize(14); + ElaText* helperText = new ElaText("用户手册及API文档付费提供\n提供额外的专业技术支持", this); + helperText->setWordWrap(false); + helperText->setTextPixelSize(14); + ElaText* copyrightText = new ElaText("版权所有 © 2024 Liniyous", this); + copyrightText->setWordWrap(false); + copyrightText->setTextPixelSize(14); + + QVBoxLayout* textLayout = new QVBoxLayout(); + textLayout->setSpacing(15); + textLayout->addWidget(versionText); + textLayout->addWidget(licenseText); + textLayout->addWidget(supportText); + textLayout->addWidget(contactText); + textLayout->addWidget(helperText); + textLayout->addWidget(copyrightText); + textLayout->addStretch(); + + QHBoxLayout* contentLayout = new QHBoxLayout(); + contentLayout->addSpacing(30); + contentLayout->addLayout(pixCardLayout); + contentLayout->addSpacing(30); + contentLayout->addLayout(textLayout); + + QVBoxLayout* mainLayout = new QVBoxLayout(this); + mainLayout->setContentsMargins(0, 25, 0, 0); + mainLayout->addLayout(contentLayout); +} + +T_About::~T_About() +{ +} diff --git a/3rdparty/ElaWidgetTools/example/ExamplePage/T_About.h b/3rdparty/ElaWidgetTools/example/ExamplePage/T_About.h new file mode 100644 index 0000000..b28c109 --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ExamplePage/T_About.h @@ -0,0 +1,14 @@ +#ifndef T_ABOUT_H +#define T_ABOUT_H + +#include + +class T_About : public ElaWidget +{ + Q_OBJECT +public: + explicit T_About(QWidget* parent = nullptr); + ~T_About(); +}; + +#endif // T_ABOUT_H diff --git a/3rdparty/ElaWidgetTools/example/ExamplePage/T_BaseComponents.cpp b/3rdparty/ElaWidgetTools/example/ExamplePage/T_BaseComponents.cpp new file mode 100644 index 0000000..51f8ebe --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ExamplePage/T_BaseComponents.cpp @@ -0,0 +1,284 @@ +#include "T_BaseComponents.h" + +#include +#include + +#include "ElaCheckBox.h" +#include "ElaComboBox.h" +#include "ElaMessageButton.h" +#include "ElaMultiSelectComboBox.h" +#include "ElaPlainTextEdit.h" +#include "ElaProgressBar.h" +#include "ElaRadioButton.h" +#include "ElaScrollPageArea.h" +#include "ElaSlider.h" +#include "ElaSpinBox.h" +#include "ElaText.h" +#include "ElaToggleButton.h" +#include "ElaToggleSwitch.h" +T_BaseComponents::T_BaseComponents(QWidget* parent) + : T_BasePage(parent) +{ + // 预览窗口标题 + setWindowTitle("ElaBaseComponents"); + + // 顶部元素 + createCustomWidget("一些常用的基础组件被放置于此,可在此界面体验其效果并按需添加进项目中"); + + _toggleSwitch = new ElaToggleSwitch(this); + ElaScrollPageArea* toggleSwitchArea = new ElaScrollPageArea(this); + QHBoxLayout* toggleSwitchLayout = new QHBoxLayout(toggleSwitchArea); + ElaText* toggleSwitchText = new ElaText("ElaToggleSwitch", this); + toggleSwitchText->setTextPixelSize(15); + toggleSwitchLayout->addWidget(toggleSwitchText); + toggleSwitchLayout->addWidget(_toggleSwitch); + toggleSwitchLayout->addStretch(); + ElaToggleSwitch* toggleSwitchDisableSwitch = new ElaToggleSwitch(this); + ElaText* toggleSwitchDisableText = new ElaText("禁用", this); + toggleSwitchDisableText->setTextPixelSize(15); + connect(toggleSwitchDisableSwitch, &ElaToggleSwitch::toggled, this, [=](bool checked) { + _toggleSwitch->setDisabled(checked); + }); + toggleSwitchLayout->addWidget(toggleSwitchDisableSwitch); + toggleSwitchLayout->addWidget(toggleSwitchDisableText); + toggleSwitchLayout->addSpacing(10); + + _toggleButton = new ElaToggleButton("ToggleButton", this); + _toggleButton->setFixedWidth(120); + ElaScrollPageArea* toggleButtonArea = new ElaScrollPageArea(this); + QHBoxLayout* toggleButtonLayout = new QHBoxLayout(toggleButtonArea); + ElaText* toggleButtonText = new ElaText("ElaToggleButton", this); + toggleButtonText->setTextPixelSize(15); + toggleButtonLayout->addWidget(toggleButtonText); + toggleButtonLayout->addWidget(_toggleButton); + toggleButtonLayout->addStretch(); + ElaToggleSwitch* toggleButtonDisableSwitch = new ElaToggleSwitch(this); + ElaText* toggleButtonDisableText = new ElaText("禁用", this); + toggleButtonDisableText->setTextPixelSize(15); + connect(toggleButtonDisableSwitch, &ElaToggleSwitch::toggled, this, [=](bool checked) { + _toggleButton->setDisabled(checked); + }); + toggleButtonLayout->addWidget(toggleButtonDisableSwitch); + toggleButtonLayout->addWidget(toggleButtonDisableText); + toggleButtonLayout->addSpacing(10); + + _comboBox = new ElaComboBox(this); + QStringList comboList{ + "我愿投身前途未卜的群星", + "潜行 步伐小心翼翼", + "不留游走痕迹", + "如同一簇幽灵", + "所谓 道德加上伦理", + "抱歉只能律己"}; + _comboBox->addItems(comboList); + ElaScrollPageArea* comboBoxArea = new ElaScrollPageArea(this); + QHBoxLayout* comboBoxLayout = new QHBoxLayout(comboBoxArea); + ElaText* comboBoxText = new ElaText("ElaComboBox", this); + comboBoxText->setTextPixelSize(15); + comboBoxLayout->addWidget(comboBoxText); + comboBoxLayout->addWidget(_comboBox); + comboBoxLayout->addStretch(); + ElaToggleSwitch* comboBoxDisableSwitch = new ElaToggleSwitch(this); + ElaText* comboBoxDisableText = new ElaText("禁用", this); + comboBoxDisableText->setTextPixelSize(15); + connect(comboBoxDisableSwitch, &ElaToggleSwitch::toggled, this, [=](bool checked) { + _comboBox->setDisabled(checked); + }); + comboBoxLayout->addWidget(comboBoxDisableSwitch); + comboBoxLayout->addWidget(comboBoxDisableText); + comboBoxLayout->addSpacing(10); + + _multiSelectComboBox = new ElaMultiSelectComboBox(this); + QStringList multiComboList{"执念的鱼", "提着灯闯过远洋的甄选", "继续下潜", "无需誓言", "我的心像自沉的旧母舰", "没入深渊", "我曾凝望曾是航向的日出"}; + QStringList multiSelectComboList{"执念的鱼", "提着灯闯过远洋的甄选", "无需誓言", "我的心像自沉的旧母舰"}; + _multiSelectComboBox->addItems(multiComboList); + _multiSelectComboBox->setCurrentSelection(multiSelectComboList); + ElaScrollPageArea* multiSelectComboBoxArea = new ElaScrollPageArea(this); + QHBoxLayout* multiSelectComboBoxLayout = new QHBoxLayout(multiSelectComboBoxArea); + ElaText* multiSelectComboBoxText = new ElaText("ElaMutilSelectComboBox", this); + multiSelectComboBoxText->setTextPixelSize(15); + multiSelectComboBoxLayout->addWidget(multiSelectComboBoxText); + multiSelectComboBoxLayout->addWidget(_multiSelectComboBox); + multiSelectComboBoxLayout->addStretch(); + ElaToggleSwitch* multiSelectComboBoxDisableSwitch = new ElaToggleSwitch(this); + ElaText* multiSelectComboBoxDisableText = new ElaText("禁用", this); + multiSelectComboBoxDisableText->setTextPixelSize(15); + connect(multiSelectComboBoxDisableSwitch, &ElaToggleSwitch::toggled, this, [=](bool checked) { + _multiSelectComboBox->setDisabled(checked); + }); + multiSelectComboBoxLayout->addWidget(multiSelectComboBoxDisableSwitch); + multiSelectComboBoxLayout->addWidget(multiSelectComboBoxDisableText); + multiSelectComboBoxLayout->addSpacing(10); + + _messageButton = new ElaMessageButton("Success", this); + _messageButton->setBarTitle("Success"); + _messageButton->setBarText("点燃星 亲手点燃黑暗森林的火星 蒙昧初醒 而我却 轻声告别这新生的黎明"); + + _infoMessageButton = new ElaMessageButton("Info", this); + _infoMessageButton->setBarTitle("Information"); + _infoMessageButton->setBarText("点燃星 亲手点燃黑暗森林的火星 蒙昧初醒 而我却 轻声告别这新生的黎明"); + _infoMessageButton->setMessageMode(ElaMessageBarType::Information); + _infoMessageButton->setPositionPolicy(ElaMessageBarType::TopLeft); + + _warningMessageButton = new ElaMessageButton("Warning", this); + _warningMessageButton->setBarTitle("Warning"); + _warningMessageButton->setBarText("点燃星 亲手点燃黑暗森林的火星 蒙昧初醒 而我却 轻声告别这新生的黎明"); + _warningMessageButton->setMessageMode(ElaMessageBarType::Warning); + _warningMessageButton->setPositionPolicy(ElaMessageBarType::BottomLeft); + + _errorMessageButton = new ElaMessageButton("Error", this); + _errorMessageButton->setBarTitle("Error"); + _errorMessageButton->setBarText("点燃星 亲手点燃黑暗森林的火星 蒙昧初醒 而我却 轻声告别这新生的黎明"); + _errorMessageButton->setMessageMode(ElaMessageBarType::Error); + _errorMessageButton->setPositionPolicy(ElaMessageBarType::BottomRight); + + ElaScrollPageArea* messageButtonArea = new ElaScrollPageArea(this); + QHBoxLayout* messageButtonLayout = new QHBoxLayout(messageButtonArea); + ElaText* messageButtonText = new ElaText("ElaMessageButton", this); + messageButtonText->setTextPixelSize(15); + messageButtonLayout->addWidget(messageButtonText); + messageButtonLayout->addWidget(_messageButton); + messageButtonLayout->addWidget(_infoMessageButton); + messageButtonLayout->addWidget(_warningMessageButton); + messageButtonLayout->addWidget(_errorMessageButton); + messageButtonLayout->addStretch(); + ElaToggleSwitch* messageButtonDisableSwitch = new ElaToggleSwitch(this); + ElaText* messageButtonDisableText = new ElaText("禁用", this); + messageButtonDisableText->setTextPixelSize(15); + connect(messageButtonDisableSwitch, &ElaToggleSwitch::toggled, this, [=](bool checked) { + _messageButton->setDisabled(checked); + _infoMessageButton->setDisabled(checked); + _warningMessageButton->setDisabled(checked); + _errorMessageButton->setDisabled(checked); + }); + messageButtonLayout->addWidget(messageButtonDisableSwitch); + messageButtonLayout->addWidget(messageButtonDisableText); + messageButtonLayout->addSpacing(10); + + _checkBox = new ElaCheckBox("CheckBox", this); + ElaScrollPageArea* checkBoxArea = new ElaScrollPageArea(this); + QHBoxLayout* checkBoxLayout = new QHBoxLayout(checkBoxArea); + ElaText* checkBoxText = new ElaText("ElacheckBox", this); + checkBoxText->setTextPixelSize(15); + checkBoxLayout->addWidget(checkBoxText); + checkBoxLayout->addWidget(_checkBox); + checkBoxLayout->addStretch(); + ElaToggleSwitch* checkBoxDisableSwitch = new ElaToggleSwitch(this); + ElaText* checkBoxDisableText = new ElaText("禁用", this); + checkBoxDisableText->setTextPixelSize(15); + connect(checkBoxDisableSwitch, &ElaToggleSwitch::toggled, this, [=](bool checked) { + _checkBox->setDisabled(checked); + }); + checkBoxLayout->addWidget(checkBoxDisableSwitch); + checkBoxLayout->addWidget(checkBoxDisableText); + checkBoxLayout->addSpacing(10); + + _spinBox = new ElaSpinBox(this); + ElaScrollPageArea* spinBoxArea = new ElaScrollPageArea(this); + QHBoxLayout* spinBoxLayout = new QHBoxLayout(spinBoxArea); + ElaText* spinBoxText = new ElaText("ElaSpinBox", this); + spinBoxText->setTextPixelSize(15); + spinBoxLayout->addWidget(spinBoxText); + spinBoxLayout->addWidget(_spinBox); + spinBoxLayout->addStretch(); + + _slider = new ElaSlider(this); + ElaScrollPageArea* sliderArea = new ElaScrollPageArea(this); + QHBoxLayout* sliderLayout = new QHBoxLayout(sliderArea); + ElaText* sliderText = new ElaText("ElaSlider", this); + sliderText->setTextPixelSize(15); + sliderLayout->addWidget(sliderText); + sliderLayout->addWidget(_slider); + sliderLayout->addStretch(); + + _radioButton = new ElaRadioButton("RadioButton", this); + ElaScrollPageArea* radioButtonArea = new ElaScrollPageArea(this); + QHBoxLayout* radioButtonLayout = new QHBoxLayout(radioButtonArea); + ElaText* radioButtonText = new ElaText("ElaRadioButton", this); + radioButtonText->setTextPixelSize(15); + radioButtonLayout->addWidget(radioButtonText); + radioButtonLayout->addWidget(_radioButton); + radioButtonLayout->addStretch(); + + _progressBar = new ElaProgressBar(this); + _progressBar->setMinimum(0); + _progressBar->setMaximum(0); + ElaScrollPageArea* progressBarArea = new ElaScrollPageArea(this); + QHBoxLayout* progressBarLayout = new QHBoxLayout(progressBarArea); + ElaText* progressBarText = new ElaText("ElaProgressBar", this); + progressBarText->setTextPixelSize(15); + progressBarLayout->addWidget(progressBarText); + progressBarLayout->addWidget(_progressBar); + progressBarLayout->addStretch(); + + ElaPlainTextEdit* edit = new ElaPlainTextEdit(this); + edit->setPlainText("这是一个ElaPlainTextEdit 暂时放在这里"); + + QWidget* centralWidget = new QWidget(this); + centralWidget->setWindowTitle("ElaBaseComponents"); + QVBoxLayout* centerLayout = new QVBoxLayout(centralWidget); + centerLayout->addWidget(toggleSwitchArea); + centerLayout->addWidget(toggleButtonArea); + centerLayout->addWidget(comboBoxArea); + centerLayout->addWidget(multiSelectComboBoxArea); + centerLayout->addWidget(messageButtonArea); + centerLayout->addWidget(checkBoxArea); + centerLayout->addWidget(spinBoxArea); + centerLayout->addWidget(sliderArea); + centerLayout->addWidget(radioButtonArea); + centerLayout->addWidget(progressBarArea); + centerLayout->addWidget(edit); + centerLayout->addStretch(); + centerLayout->setContentsMargins(0, 0, 0, 0); + addCentralWidget(centralWidget, true, true, 0); + + ElaText* homeStack1 = new ElaText("HomeStack1", this); + QFont font = homeStack1->font(); + font.setPixelSize(32); + homeStack1->setFont(font); + homeStack1->setAlignment(Qt::AlignCenter); + homeStack1->setWindowTitle("HomeStack1"); + addCentralWidget(homeStack1); + ElaText* homeStack2 = new ElaText("HomeStack2", this); + homeStack2->setFont(font); + homeStack2->setAlignment(Qt::AlignCenter); + homeStack2->setWindowTitle("HomeStack2"); + addCentralWidget(homeStack2); +} + +T_BaseComponents::~T_BaseComponents() +{ +} + +void T_BaseComponents::mouseReleaseEvent(QMouseEvent* event) +{ + switch (event->button()) + { + case Qt::LeftButton: + { + //ElaMessageBar::success(ElaMessageBarType::TopRight, "Success", "Never Close Your Eyes", 2500); + //ElaMessageBar::success(ElaMessageBarType::TopRight, "Success", "Never Close Your Eyes", 1500); + break; + } + case Qt::BackButton: + { + this->navigation(0); + break; + } + case Qt::ForwardButton: + { + this->navigation(1); + break; + } + case Qt::MiddleButton: + { + this->navigation(2); + break; + } + default: + { + break; + } + } + ElaScrollPage::mouseReleaseEvent(event); +} diff --git a/3rdparty/ElaWidgetTools/example/ExamplePage/T_BaseComponents.h b/3rdparty/ElaWidgetTools/example/ExamplePage/T_BaseComponents.h new file mode 100644 index 0000000..83c0dd9 --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ExamplePage/T_BaseComponents.h @@ -0,0 +1,42 @@ +#ifndef T_BASECOMPONENTS_H +#define T_BASECOMPONENTS_H + +#include "T_BasePage.h" + +class ElaToggleSwitch; +class ElaToggleButton; +class ElaComboBox; +class ElaMultiSelectComboBox; +class ElaMessageButton; +class ElaCheckBox; +class ElaSpinBox; +class ElaSlider; +class ElaRadioButton; +class ElaProgressBar; +class T_BaseComponents : public T_BasePage +{ + Q_OBJECT +public: + Q_INVOKABLE explicit T_BaseComponents(QWidget* parent = nullptr); + ~T_BaseComponents(); + +protected: + virtual void mouseReleaseEvent(QMouseEvent* event); + +private: + ElaToggleSwitch* _toggleSwitch{nullptr}; + ElaToggleButton* _toggleButton{nullptr}; + ElaComboBox* _comboBox{nullptr}; + ElaMultiSelectComboBox* _multiSelectComboBox{nullptr}; + ElaMessageButton* _messageButton{nullptr}; + ElaMessageButton* _infoMessageButton{nullptr}; + ElaMessageButton* _warningMessageButton{nullptr}; + ElaMessageButton* _errorMessageButton{nullptr}; + ElaCheckBox* _checkBox{nullptr}; + ElaSpinBox* _spinBox{nullptr}; + ElaSlider* _slider{nullptr}; + ElaRadioButton* _radioButton{nullptr}; + ElaProgressBar* _progressBar{nullptr}; +}; + +#endif // T_BASECOMPONENTS_H diff --git a/3rdparty/ElaWidgetTools/example/ExamplePage/T_BasePage.cpp b/3rdparty/ElaWidgetTools/example/ExamplePage/T_BasePage.cpp new file mode 100644 index 0000000..00705ed --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ExamplePage/T_BasePage.cpp @@ -0,0 +1,85 @@ +#include "T_BasePage.h" + +#include +#include + +#include "ElaMenu.h" +#include "ElaText.h" +#include "ElaTheme.h" +#include "ElaToolButton.h" +T_BasePage::T_BasePage(QWidget* parent) + : ElaScrollPage(parent) +{ + connect(eTheme, &ElaTheme::themeModeChanged, this, [=]() { + if (!parent) + { + update(); + } + }); +} + +T_BasePage::~T_BasePage() +{ +} + +void T_BasePage::createCustomWidget(QString desText) +{ + // 顶部元素 + QWidget* customWidget = new QWidget(this); + ElaText* subTitleText = new ElaText(this); + subTitleText->setText("https://github.com/Liniyous/ElaWidgetTools"); + subTitleText->setTextInteractionFlags(Qt::TextSelectableByMouse); + subTitleText->setTextPixelSize(11); + + ElaToolButton* documentationButton = new ElaToolButton(this); + documentationButton->setFixedHeight(35); + documentationButton->setIsTransparent(false); + documentationButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + //_toolButton->setPopupMode(QToolButton::MenuButtonPopup); + documentationButton->setText("Documentation"); + documentationButton->setElaIcon(ElaIconType::FileDoc); + ElaMenu* documentationMenu = new ElaMenu(this); + documentationMenu->addElaIconAction(ElaIconType::CardsBlank, "CardsBlank"); + documentationMenu->addElaIconAction(ElaIconType::EarthAmericas, "EarthAmericas"); + documentationButton->setMenu(documentationMenu); + + ElaToolButton* sourceButton = new ElaToolButton(this); + sourceButton->setFixedHeight(35); + sourceButton->setIsTransparent(false); + sourceButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + sourceButton->setText("Source"); + sourceButton->setElaIcon(ElaIconType::NfcSymbol); + ElaMenu* sourceMenu = new ElaMenu(this); + sourceMenu->addElaIconAction(ElaIconType::FireBurner, "FireBurner"); + sourceMenu->addElaIconAction(ElaIconType::Galaxy, "Galaxy~~~~"); + sourceButton->setMenu(sourceMenu); + + ElaToolButton* themeButton = new ElaToolButton(this); + themeButton->setFixedSize(35, 35); + themeButton->setIsTransparent(false); + themeButton->setElaIcon(ElaIconType::MoonStars); + connect(themeButton, &ElaToolButton::clicked, this, [=]() { + eTheme->setThemeMode(eTheme->getThemeMode() == ElaThemeType::Light ? ElaThemeType::Dark : ElaThemeType::Light); + }); + + QHBoxLayout* buttonLayout = new QHBoxLayout(); + buttonLayout->addWidget(documentationButton); + buttonLayout->addSpacing(5); + buttonLayout->addWidget(sourceButton); + buttonLayout->addStretch(); + buttonLayout->addWidget(themeButton); + buttonLayout->addSpacing(15); + + ElaText* descText = new ElaText(this); + descText->setText(desText); + descText->setTextPixelSize(13); + + QVBoxLayout* topLayout = new QVBoxLayout(customWidget); + topLayout->setContentsMargins(0, 0, 0, 0); + topLayout->addWidget(subTitleText); + topLayout->addSpacing(5); + topLayout->addLayout(buttonLayout); + topLayout->addSpacing(5); + topLayout->addWidget(descText); + setCustomWidget(customWidget); +} diff --git a/3rdparty/ElaWidgetTools/example/ExamplePage/T_BasePage.h b/3rdparty/ElaWidgetTools/example/ExamplePage/T_BasePage.h new file mode 100644 index 0000000..52b47ee --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ExamplePage/T_BasePage.h @@ -0,0 +1,18 @@ +#ifndef T_BASEPAGE_H +#define T_BASEPAGE_H + +#include + +class QVBoxLayout; +class T_BasePage : public ElaScrollPage +{ + Q_OBJECT +public: + explicit T_BasePage(QWidget* parent = nullptr); + ~T_BasePage(); + +protected: + void createCustomWidget(QString desText); +}; + +#endif // T_BASEPAGE_H diff --git a/3rdparty/ElaWidgetTools/example/ExamplePage/T_Card.cpp b/3rdparty/ElaWidgetTools/example/ExamplePage/T_Card.cpp new file mode 100644 index 0000000..2a0a871 --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ExamplePage/T_Card.cpp @@ -0,0 +1,83 @@ +#include "T_Card.h" + +#include + +#include "ElaLCDNumber.h" +#include "ElaPromotionCard.h" +#include "ElaPromotionView.h" +T_Card::T_Card(QWidget* parent) + : T_BasePage(parent) +{ + // 预览窗口标题 + setWindowTitle("ElaCard"); + + // 顶部元素 + createCustomWidget("一些常用的卡片组件被放置于此,可在此界面体验其效果并按需添加进项目中"); + + _lcdNumber = new ElaLCDNumber(this); + _lcdNumber->setIsUseAutoClock(true); + _lcdNumber->setIsTransparent(false); + // _lcdNumber->setAutoClockFormat("hh:mm:ss"); + _lcdNumber->setFixedHeight(100); + + _promotionCard = new ElaPromotionCard(this); + _promotionCard->setFixedSize(600, 300); + _promotionCard->setCardPixmap(QPixmap(":/Resource/Image/Card/miku.png")); + _promotionCard->setCardTitle("MiKu"); + _promotionCard->setPromotionTitle("SONG~"); + _promotionCard->setTitle("STYX HELIX"); + _promotionCard->setSubTitle("Never close your eyes, Searching for a true fate"); + + _promotionView = new ElaPromotionView(this); + + ElaPromotionCard* exampleCard1 = new ElaPromotionCard(this); + exampleCard1->setCardPixmap(QPixmap(":/Resource/Image/Card/miku.png")); + exampleCard1->setCardTitle("MiKu"); + exampleCard1->setPromotionTitle("SONG~"); + exampleCard1->setTitle("STYX HELIX"); + exampleCard1->setSubTitle("Never close your eyes, Searching for a true fate"); + + ElaPromotionCard* exampleCard2 = new ElaPromotionCard(this); + exampleCard2->setCardPixmap(QPixmap(":/Resource/Image/Card/beach.png")); + exampleCard2->setCardTitle("Beach"); + exampleCard2->setPromotionTitle("SONG~"); + exampleCard2->setTitle("STYX HELIX"); + exampleCard2->setSubTitle("Never close your eyes, Searching for a true fate"); + + ElaPromotionCard* exampleCard3 = new ElaPromotionCard(this); + exampleCard3->setCardPixmap(QPixmap(":/Resource/Image/Card/dream.png")); + exampleCard3->setCardTitle("Dream"); + exampleCard3->setPromotionTitle("SONG~"); + exampleCard3->setTitle("STYX HELIX"); + exampleCard3->setSubTitle("Never close your eyes, Searching for a true fate"); + + ElaPromotionCard* exampleCard4 = new ElaPromotionCard(this); + exampleCard4->setCardPixmap(QPixmap(":/Resource/Image/Card/classroom.png")); + exampleCard4->setCardTitle("Classroom"); + exampleCard4->setPromotionTitle("SONG~"); + exampleCard4->setTitle("STYX HELIX"); + exampleCard4->setSubTitle("Never close your eyes, Searching for a true fate"); + + _promotionView->appendPromotionCard(exampleCard1); + _promotionView->appendPromotionCard(exampleCard2); + _promotionView->appendPromotionCard(exampleCard3); + _promotionView->appendPromotionCard(exampleCard4); + _promotionView->setIsAutoScroll(true); + + QWidget* centralWidget = new QWidget(this); + centralWidget->setWindowTitle("ElaCard"); + QVBoxLayout* centerLayout = new QVBoxLayout(centralWidget); + centerLayout->setContentsMargins(0, 0, 0, 0); + centerLayout->addWidget(_lcdNumber); + centerLayout->addSpacing(20); + centerLayout->addWidget(_promotionCard); + centerLayout->addSpacing(20); + centerLayout->addWidget(_promotionView); + centerLayout->addSpacing(100); + centerLayout->addStretch(); + addCentralWidget(centralWidget, true, true, 0); +} + +T_Card::~T_Card() +{ +} diff --git a/3rdparty/ElaWidgetTools/example/ExamplePage/T_Card.h b/3rdparty/ElaWidgetTools/example/ExamplePage/T_Card.h new file mode 100644 index 0000000..c3f0466 --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ExamplePage/T_Card.h @@ -0,0 +1,22 @@ +#ifndef T_CARD_H +#define T_CARD_H + +#include "T_BasePage.h" + +class ElaLCDNumber; +class ElaPromotionCard; +class ElaPromotionView; +class T_Card : public T_BasePage +{ + Q_OBJECT +public: + Q_INVOKABLE explicit T_Card(QWidget* parent = nullptr); + ~T_Card(); + +private: + ElaLCDNumber* _lcdNumber{nullptr}; + ElaPromotionCard* _promotionCard{nullptr}; + ElaPromotionView* _promotionView{nullptr}; +}; + +#endif // T_CARD_H diff --git a/3rdparty/ElaWidgetTools/example/ExamplePage/T_ElaScreen.cpp b/3rdparty/ElaWidgetTools/example/ExamplePage/T_ElaScreen.cpp new file mode 100644 index 0000000..bfe6065 --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ExamplePage/T_ElaScreen.cpp @@ -0,0 +1,86 @@ +#include "T_ElaScreen.h" + +#include "ElaToggleButton.h" +#ifdef Q_OS_WIN +#include + +#include "ElaComboBox.h" +#include "ElaDxgiManager.h" +#include "ElaText.h" +T_ElaScreen::T_ElaScreen(QWidget* parent) + : T_BasePage(parent) +{ + // 预览窗口标题 + setWindowTitle("ElaScreen"); + + // 顶部元素 + createCustomWidget("DXGI录制组件被放置于此,可在此界面预览录制效果"); + + ElaDxgiManager* dxgiManager = ElaDxgiManager::getInstance(); + dxgiManager->setGrabArea(1920, 1080); + + _dxgiScreen = new ElaDxgiScreen(this); + _dxgiScreen->setIsSyncGrabSize(true); + + ElaText* dxText = new ElaText("显卡选择", this); + dxText->setTextPixelSize(15); + _dxComboBox = new ElaComboBox(this); + _dxComboBox->addItems(dxgiManager->getDxDeviceList()); + _dxComboBox->setCurrentIndex(dxgiManager->getDxDeviceID()); + + ElaText* outputText = new ElaText("屏幕选择", this); + outputText->setTextPixelSize(15); + _outputComboBox = new ElaComboBox(this); + _outputComboBox->addItems(dxgiManager->getOutputDeviceList()); + _outputComboBox->setCurrentIndex(dxgiManager->getOutputDeviceID()); + + connect(_dxComboBox, QOverload::of(&ElaComboBox::currentIndexChanged), this, [=](int index) { + dxgiManager->setDxDeviceID(index); + _outputComboBox->blockSignals(true); + _outputComboBox->clear(); + _outputComboBox->addItems(dxgiManager->getOutputDeviceList()); + _outputComboBox->setCurrentIndex(dxgiManager->getOutputDeviceID()); + _outputComboBox->blockSignals(false); + _dxgiScreen->update(); + }); + connect(_outputComboBox, QOverload::of(&ElaComboBox::currentIndexChanged), this, [=](int index) { + dxgiManager->setOutputDeviceID(index); + _dxgiScreen->update(); + }); + + ElaToggleButton* startButton = new ElaToggleButton("捕获", this); + connect(startButton, &ElaToggleButton::toggled, this, [=](bool isToggled) { + if (isToggled) + { + dxgiManager->startGrabScreen(); + } + else + { + dxgiManager->stopGrabScreen(); + _dxgiScreen->update(); + } + }); + + QHBoxLayout* comboBoxLayout = new QHBoxLayout(); + comboBoxLayout->addWidget(dxText); + comboBoxLayout->addWidget(_dxComboBox); + comboBoxLayout->addSpacing(10); + comboBoxLayout->addWidget(outputText); + comboBoxLayout->addWidget(_outputComboBox); + comboBoxLayout->addWidget(startButton); + comboBoxLayout->addStretch(); + + QWidget* centralWidget = new QWidget(this); + centralWidget->setWindowTitle("ElaScreen"); + QVBoxLayout* centerLayout = new QVBoxLayout(centralWidget); + centerLayout->setContentsMargins(0, 0, 0, 0); + centerLayout->addLayout(comboBoxLayout); + centerLayout->addWidget(_dxgiScreen); + + addCentralWidget(centralWidget, false, true); +} + +T_ElaScreen::~T_ElaScreen() +{ +} +#endif diff --git a/3rdparty/ElaWidgetTools/example/ExamplePage/T_ElaScreen.h b/3rdparty/ElaWidgetTools/example/ExamplePage/T_ElaScreen.h new file mode 100644 index 0000000..6efbb75 --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ExamplePage/T_ElaScreen.h @@ -0,0 +1,22 @@ +#ifndef T_ELASCREEN_H +#define T_ELASCREEN_H +#include + +#include "T_BasePage.h" +#ifdef Q_OS_WIN +class ElaDxgiScreen; +class ElaComboBox; +class T_ElaScreen : public T_BasePage +{ + Q_OBJECT +public: + Q_INVOKABLE explicit T_ElaScreen(QWidget* parent = nullptr); + ~T_ElaScreen(); + +private: + ElaDxgiScreen* _dxgiScreen{nullptr}; + ElaComboBox* _dxComboBox{nullptr}; + ElaComboBox* _outputComboBox{nullptr}; +}; +#endif +#endif // T_ELASCREEN_H diff --git a/3rdparty/ElaWidgetTools/example/ExamplePage/T_Graphics.cpp b/3rdparty/ElaWidgetTools/example/ExamplePage/T_Graphics.cpp new file mode 100644 index 0000000..0b35ef7 --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ExamplePage/T_Graphics.cpp @@ -0,0 +1,49 @@ +#include "T_Graphics.h" + +#include +#include + +#include "ElaGraphicsItem.h" +#include "ElaGraphicsScene.h" +#include "ElaGraphicsView.h" +T_Graphics::T_Graphics(QWidget* parent) + : T_BasePage(parent) +{ + // 预览窗口标题 + setWindowTitle("ElaGraphics"); + // 顶部元素 + createCustomWidget("图形视图框架被放置于此,可在此界面体验其效果,按住Ctrl进行缩放,按住Shitf进行连接"); + + QWidget* centralWidget = new QWidget(this); + centralWidget->setWindowTitle("ElaGraphics"); + + // GraphicsView + ElaGraphicsScene* scene = new ElaGraphicsScene(this); + scene->setSceneRect(0, 0, 1500, 1500); + ElaGraphicsItem* item1 = new ElaGraphicsItem(); + item1->setWidth(100); + item1->setHeight(100); + item1->setMaxLinkPortCount(100); + item1->setMaxLinkPortCount(1); + ElaGraphicsItem* item2 = new ElaGraphicsItem(); + item2->setWidth(100); + item2->setHeight(100); + scene->addItem(item1); + scene->addItem(item2); + ElaGraphicsView* view = new ElaGraphicsView(scene); + view->setScene(scene); + view->setFixedHeight(600); + QHBoxLayout* viewLayout = new QHBoxLayout(); + viewLayout->setContentsMargins(0, 0, 12, 0); + viewLayout->addWidget(view); + + QVBoxLayout* centerVLayout = new QVBoxLayout(centralWidget); + centerVLayout->setContentsMargins(0, 0, 0, 0); + centerVLayout->addLayout(viewLayout); + centerVLayout->addStretch(); + addCentralWidget(centralWidget, true, false, 0); +} + +T_Graphics::~T_Graphics() +{ +} diff --git a/3rdparty/ElaWidgetTools/example/ExamplePage/T_Graphics.h b/3rdparty/ElaWidgetTools/example/ExamplePage/T_Graphics.h new file mode 100644 index 0000000..33574c2 --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ExamplePage/T_Graphics.h @@ -0,0 +1,14 @@ +#ifndef T_GRAPHICS_H +#define T_GRAPHICS_H + +#include "T_BasePage.h" + +class T_Graphics : public T_BasePage +{ + Q_OBJECT +public: + Q_INVOKABLE explicit T_Graphics(QWidget* parent = nullptr); + ~T_Graphics(); +}; + +#endif // T_GRAPHICS_H diff --git a/3rdparty/ElaWidgetTools/example/ExamplePage/T_Home.cpp b/3rdparty/ElaWidgetTools/example/ExamplePage/T_Home.cpp new file mode 100644 index 0000000..3773502 --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ExamplePage/T_Home.cpp @@ -0,0 +1,240 @@ +#include "T_Home.h" + +#include +#include +#include +#include +#include +#include + +#include "ElaAcrylicUrlCard.h" +#include "ElaFlowLayout.h" +#include "ElaImageCard.h" +#include "ElaMenu.h" +#include "ElaMessageBar.h" +#include "ElaNavigationRouter.h" +#include "ElaPopularCard.h" +#include "ElaScrollArea.h" +#include "ElaText.h" +#include "ElaToolTip.h" +T_Home::T_Home(QWidget* parent) + : T_BasePage(parent) +{ + // 预览窗口标题 + setWindowTitle("Home"); + + setTitleVisible(false); + setContentsMargins(2, 2, 0, 0); + // 标题卡片区域 + ElaText* desText = new ElaText("FluentUI For QWidget", this); + desText->setTextPixelSize(18); + ElaText* titleText = new ElaText("ElaWidgetTools", this); + titleText->setTextPixelSize(35); + + QVBoxLayout* titleLayout = new QVBoxLayout(); + titleLayout->setContentsMargins(30, 60, 0, 0); + titleLayout->addWidget(desText); + titleLayout->addWidget(titleText); + + ElaImageCard* backgroundCard = new ElaImageCard(this); + backgroundCard->setBorderRadius(10); + backgroundCard->setFixedHeight(400); + backgroundCard->setMaximumAspectRatio(1.7); + backgroundCard->setCardImage(QImage(":/Resource/Image/Home_Background.png")); + + ElaAcrylicUrlCard* urlCard1 = new ElaAcrylicUrlCard(this); + urlCard1->setCardPixmapSize(QSize(62, 62)); + urlCard1->setFixedSize(195, 225); + urlCard1->setTitlePixelSize(17); + urlCard1->setTitleSpacing(25); + urlCard1->setSubTitleSpacing(13); + urlCard1->setUrl("https://github.com/Liniyous/ElaWidgetTools"); + urlCard1->setCardPixmap(QPixmap(":/Resource/Image/github.png")); + urlCard1->setTitle("ElaTool Github"); + urlCard1->setSubTitle("Use ElaWidgetTools To Create A Cool Project"); + ElaToolTip* urlCard1ToolTip = new ElaToolTip(urlCard1); + urlCard1ToolTip->setToolTip("https://github.com/Liniyous/ElaWidgetTools"); + ElaAcrylicUrlCard* urlCard2 = new ElaAcrylicUrlCard(this); + urlCard2->setCardPixmapSize(QSize(62, 62)); + urlCard2->setFixedSize(195, 225); + urlCard2->setTitlePixelSize(17); + urlCard2->setTitleSpacing(25); + urlCard2->setSubTitleSpacing(13); + urlCard2->setUrl("https://space.bilibili.com/21256707"); + urlCard2->setCardPixmap(QPixmap(":/Resource/Image/Moon.jpg")); + urlCard2->setTitle("ElaWidgetTool"); + urlCard2->setSubTitle("8009963@qq.com"); + + ElaScrollArea* cardScrollArea = new ElaScrollArea(this); + cardScrollArea->setWidgetResizable(true); + cardScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + cardScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + cardScrollArea->setIsGrabGesture(true, 0); + cardScrollArea->setIsOverShoot(Qt::Horizontal, true); + QWidget* cardScrollAreaWidget = new QWidget(this); + cardScrollAreaWidget->setStyleSheet("background-color:transparent;"); + cardScrollArea->setWidget(cardScrollAreaWidget); + QHBoxLayout* urlCardLayout = new QHBoxLayout(); + urlCardLayout->setSpacing(15); + urlCardLayout->setContentsMargins(30, 0, 0, 6); + urlCardLayout->addWidget(urlCard1); + urlCardLayout->addWidget(urlCard2); + urlCardLayout->addStretch(); + QVBoxLayout* cardScrollAreaWidgetLayout = new QVBoxLayout(cardScrollAreaWidget); + cardScrollAreaWidgetLayout->setContentsMargins(0, 0, 0, 0); + cardScrollAreaWidgetLayout->addStretch(); + cardScrollAreaWidgetLayout->addLayout(urlCardLayout); + + QVBoxLayout* backgroundLayout = new QVBoxLayout(backgroundCard); + backgroundLayout->setContentsMargins(0, 0, 0, 0); + backgroundLayout->addLayout(titleLayout); + backgroundLayout->addWidget(cardScrollArea); + + // 推荐卡片 + ElaText* flowText = new ElaText("热门免费应用", this); + flowText->setTextPixelSize(20); + QHBoxLayout* flowTextLayout = new QHBoxLayout(); + flowTextLayout->setContentsMargins(33, 0, 0, 0); + flowTextLayout->addWidget(flowText); + // ElaFlowLayout + ElaPopularCard* homeCard = new ElaPopularCard(this); + connect(homeCard, &ElaPopularCard::popularCardButtonClicked, this, [=]() { + QDesktopServices::openUrl(QUrl("https://github.com/Liniyous/ElaWidgetTools")); + }); + homeCard->setCardPixmap(QPixmap(":/Resource/Image/Cirno.jpg")); + homeCard->setTitle("ElaWidgetTool"); + homeCard->setSubTitle("5.0⭐ 实用程序与工具"); + homeCard->setInteractiveTips("免费下载"); + homeCard->setDetailedText("ElaWidgetTools致力于为QWidget用户提供一站式的外观和实用功能解决方案,只需数十MB内存和极少CPU占用以支持高效而美观的界面开发"); + homeCard->setCardFloatPixmap(QPixmap(":/Resource/Image/IARC/IARC_7+.svg.png")); + + ElaPopularCard* homeCard1 = new ElaPopularCard(this); + connect(homeCard1, &ElaPopularCard::popularCardButtonClicked, this, [=]() { Q_EMIT elaScreenNavigation(); }); + homeCard1->setTitle("ElaScreen"); + homeCard1->setSubTitle("5.0⭐ 实用程序与工具"); + homeCard1->setCardPixmap(QPixmap(":/Resource/Image/control/AutomationProperties.png")); + homeCard1->setInteractiveTips("免费使用"); + homeCard1->setDetailedText("使用ElaDxgiManager获取屏幕的实时数据,以QImage的形式处理数据,支持切换采集设备和输出设备。"); + homeCard1->setCardFloatPixmap(QPixmap(":/Resource/Image/IARC/IARC_7+.svg.png")); + + ElaPopularCard* homeCard2 = new ElaPopularCard(this); + connect(homeCard2, &ElaPopularCard::popularCardButtonClicked, this, [=]() { Q_EMIT elaSceneNavigation(); }); + homeCard2->setTitle("ElaScene"); + homeCard2->setSubTitle("5.0⭐ 实用程序与工具"); + homeCard2->setCardPixmap(QPixmap(":/Resource/Image/control/Canvas.png")); + homeCard2->setInteractiveTips("免费使用"); + homeCard2->setDetailedText("使用ElaScene封装的高集成度API进行快速拓扑绘图开发,对基于连接的网络拓扑特化处理。"); + homeCard2->setCardFloatPixmap(QPixmap(":/Resource/Image/IARC/IARC_7+.svg.png")); + + ElaPopularCard* homeCard3 = new ElaPopularCard(this); + connect(homeCard3, &ElaPopularCard::popularCardButtonClicked, this, [=]() { Q_EMIT elaBaseComponentNavigation(); }); + homeCard3->setTitle("ElaBaseComponent"); + homeCard3->setSubTitle("5.0⭐ 实用程序与工具"); + homeCard3->setCardPixmap(QPixmap(":/Resource/Image/control/StandardUICommand.png")); + homeCard3->setInteractiveTips("免费使用"); + homeCard3->setDetailedText("添加ElaBaseComponent页面中的基础组件到你的项目中以进行快捷开发,使用方便,结构整洁,API规范"); + homeCard3->setCardFloatPixmap(QPixmap(":/Resource/Image/IARC/IARC_7+.svg.png")); + + ElaPopularCard* homeCard4 = new ElaPopularCard(this); + connect(homeCard4, &ElaPopularCard::popularCardButtonClicked, this, [=]() { Q_EMIT elaCardNavigation(); }); + homeCard4->setTitle("ElaCard"); + homeCard4->setSubTitle("5.0⭐ 实用程序与工具"); + homeCard4->setCardPixmap(QPixmap(":/Resource/Image/control/FlipView.png")); + homeCard4->setInteractiveTips("免费使用"); + homeCard4->setDetailedText("使用ElaCard系列组件,包括促销卡片和促销卡片视窗来快速建立循环动画。"); + homeCard4->setCardFloatPixmap(QPixmap(":/Resource/Image/IARC/IARC_7+.svg.png")); + + ElaPopularCard* homeCard5 = new ElaPopularCard(this); + connect(homeCard5, &ElaPopularCard::popularCardButtonClicked, this, [=]() { Q_EMIT elaIconNavigation(); }); + homeCard5->setTitle("ElaIcon"); + homeCard5->setSubTitle("5.0⭐ 实用程序与工具"); + homeCard5->setCardPixmap(QPixmap(":/Resource/Image/control/CommandBarFlyout.png")); + homeCard5->setInteractiveTips("免费使用"); + homeCard5->setDetailedText("在该界面快速挑选你喜欢的图标应用到项目中,以枚举的形式使用它"); + homeCard5->setCardFloatPixmap(QPixmap(":/Resource/Image/IARC/IARC_7+.svg.png")); + + ElaFlowLayout* flowLayout = new ElaFlowLayout(0, 5, 5); + flowLayout->setContentsMargins(30, 0, 0, 0); + flowLayout->setIsAnimation(true); + flowLayout->addWidget(homeCard); + flowLayout->addWidget(homeCard1); + flowLayout->addWidget(homeCard2); + flowLayout->addWidget(homeCard3); + flowLayout->addWidget(homeCard4); + flowLayout->addWidget(homeCard5); + + // 菜单 + _homeMenu = new ElaMenu(this); + ElaMenu* checkMenu = _homeMenu->addMenu(ElaIconType::Cubes, "查看"); + checkMenu->addAction("查看1"); + checkMenu->addAction("查看2"); + checkMenu->addAction("查看3"); + checkMenu->addAction("查看4"); + + ElaMenu* checkMenu1 = _homeMenu->addMenu(ElaIconType::Cubes, "查看"); + checkMenu1->addAction("查看1"); + checkMenu1->addAction("查看2"); + checkMenu1->addAction("查看3"); + checkMenu1->addAction("查看4"); + + ElaMenu* checkMenu2 = checkMenu->addMenu(ElaIconType::Cubes, "查看"); + checkMenu2->addAction("查看1"); + checkMenu2->addAction("查看2"); + checkMenu2->addAction("查看3"); + checkMenu2->addAction("查看4"); + + // QKeySequence key = QKeySequence(Qt::CTRL | Qt::Key_S); + + _homeMenu->addSeparator(); + _homeMenu->addElaIconAction(ElaIconType::BoxCheck, "排序方式", QKeySequence::Save); + _homeMenu->addElaIconAction(ElaIconType::ArrowRotateRight, "刷新"); + QAction* action = _homeMenu->addElaIconAction(ElaIconType::ArrowRotateLeft, "撤销"); + connect(action, &QAction::triggered, this, [=]() { + ElaNavigationRouter::getInstance()->navigationRouteBack(); + }); + + _homeMenu->addElaIconAction(ElaIconType::Copy, "复制"); + _homeMenu->addElaIconAction(ElaIconType::MagnifyingGlassPlus, "显示设置"); + + QWidget* centralWidget = new QWidget(this); + centralWidget->setWindowTitle("Home"); + QVBoxLayout* centerVLayout = new QVBoxLayout(centralWidget); + centerVLayout->setSpacing(0); + centerVLayout->setContentsMargins(0, 0, 0, 0); + centerVLayout->addWidget(backgroundCard); + centerVLayout->addSpacing(20); + centerVLayout->addLayout(flowTextLayout); + centerVLayout->addSpacing(10); + centerVLayout->addLayout(flowLayout); + centerVLayout->addStretch(); + addCentralWidget(centralWidget); + + // 初始化提示 + ElaMessageBar::success(ElaMessageBarType::BottomRight, "Success", "初始化成功!", 2000); + qDebug() << "初始化成功"; +} + +T_Home::~T_Home() +{ +} + +void T_Home::mouseReleaseEvent(QMouseEvent* event) +{ + switch (event->button()) + { + case Qt::RightButton: + { +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + _homeMenu->popup(event->globalPosition().toPoint()); +#else + _homeMenu->popup(event->globalPos()); +#endif + break; + } + default: + { + break; + } + } + ElaScrollPage::mouseReleaseEvent(event); +} diff --git a/3rdparty/ElaWidgetTools/example/ExamplePage/T_Home.h b/3rdparty/ElaWidgetTools/example/ExamplePage/T_Home.h new file mode 100644 index 0000000..071e10f --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ExamplePage/T_Home.h @@ -0,0 +1,26 @@ +#ifndef T_HOME_H +#define T_HOME_H + +#include "T_BasePage.h" +class ElaMenu; +class T_Home : public T_BasePage +{ + Q_OBJECT +public: + Q_INVOKABLE explicit T_Home(QWidget* parent = nullptr); + ~T_Home(); +Q_SIGNALS: + Q_SIGNAL void elaScreenNavigation(); + Q_SIGNAL void elaBaseComponentNavigation(); + Q_SIGNAL void elaSceneNavigation(); + Q_SIGNAL void elaCardNavigation(); + Q_SIGNAL void elaIconNavigation(); + +protected: + virtual void mouseReleaseEvent(QMouseEvent* event); + +private: + ElaMenu* _homeMenu{nullptr}; +}; + +#endif // T_HOME_H diff --git a/3rdparty/ElaWidgetTools/example/ExamplePage/T_Icon.cpp b/3rdparty/ElaWidgetTools/example/ExamplePage/T_Icon.cpp new file mode 100644 index 0000000..8798cb8 --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ExamplePage/T_Icon.cpp @@ -0,0 +1,88 @@ +#include "T_Icon.h" + +#include +#include +#include +#include +#include + +#include "ElaLineEdit.h" +#include "ElaListView.h" +#include "ElaMessageBar.h" +#include "T_IconDelegate.h" +#include "T_IconModel.h" +T_Icon::T_Icon(QWidget* parent) + : T_BasePage(parent) +{ + // 预览窗口标题 + setWindowTitle("ElaIcon"); + // 顶部元素 + createCustomWidget("一堆常用图标被放置于此,左键单击以复制其枚举"); + + _metaEnum = QMetaEnum::fromType(); + QWidget* centralWidget = new QWidget(this); + QVBoxLayout* centerVLayout = new QVBoxLayout(centralWidget); + centerVLayout->setContentsMargins(0, 0, 5, 0); + centralWidget->setWindowTitle("ElaIcon"); + // ListView + _iconView = new ElaListView(this); + _iconView->setIsTransparent(true); + _iconView->setFlow(QListView::LeftToRight); + _iconView->setViewMode(QListView::IconMode); + _iconView->setResizeMode(QListView::Adjust); + connect(_iconView, &ElaListView::clicked, this, [=](const QModelIndex& index) { + QString iconName = _iconModel->getIconNameFromModelIndex(index); + if (iconName.isEmpty()) + { + return; + } + qApp->clipboard()->setText(iconName); + ElaMessageBar::success(ElaMessageBarType::Top, "复制完成", iconName + "已被复制到剪贴板", 1000, this); + }); + _iconModel = new T_IconModel(this); + _iconDelegate = new T_IconDelegate(this); + _iconView->setModel(_iconModel); + _iconView->setItemDelegate(_iconDelegate); + _iconView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + _searchEdit = new ElaLineEdit(this); + _searchEdit->setPlaceholderText("搜索图标"); + _searchEdit->setFixedSize(300, 35); + connect(_searchEdit, &ElaLineEdit::textEdited, this, &T_Icon::onSearchEditTextEdit); + connect(_searchEdit, &ElaLineEdit::focusIn, this, &T_Icon::onSearchEditTextEdit); + + centerVLayout->addSpacing(13); + centerVLayout->addWidget(_searchEdit); + centerVLayout->addWidget(_iconView); + this->addCentralWidget(centralWidget, true, true, 0); +} + +T_Icon::~T_Icon() +{ +} + +void T_Icon::onSearchEditTextEdit(const QString& searchText) +{ + if (searchText.isEmpty()) + { + _iconModel->setIsSearchMode(false); + _iconModel->setSearchKeyList(QStringList()); + _iconView->clearSelection(); + _iconView->viewport()->update(); + return; + } + QStringList searchKeyList; + for (int i = 1; i < _metaEnum.keyCount(); i++) + { + QString key = QString(_metaEnum.key(i)); + if (key.contains(searchText, Qt::CaseInsensitive)) + { + searchKeyList.append(key); + } + } + _iconModel->setSearchKeyList(searchKeyList); + _iconModel->setIsSearchMode(true); + _iconView->clearSelection(); + _iconView->scrollTo(_iconModel->index(0, 0)); + _iconView->viewport()->update(); +} diff --git a/3rdparty/ElaWidgetTools/example/ExamplePage/T_Icon.h b/3rdparty/ElaWidgetTools/example/ExamplePage/T_Icon.h new file mode 100644 index 0000000..b319959 --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ExamplePage/T_Icon.h @@ -0,0 +1,27 @@ +#ifndef T_ICON_H +#define T_ICON_H + +#include + +#include "T_BasePage.h" +class ElaLineEdit; +class ElaListView; +class T_IconModel; +class T_IconDelegate; +class T_Icon : public T_BasePage +{ + Q_OBJECT +public: + Q_INVOKABLE explicit T_Icon(QWidget* parent = nullptr); + ~T_Icon(); + Q_SLOT void onSearchEditTextEdit(const QString& searchText); + +private: + QMetaEnum _metaEnum; + ElaLineEdit* _searchEdit{nullptr}; + ElaListView* _iconView; + T_IconModel* _iconModel; + T_IconDelegate* _iconDelegate; +}; + +#endif // T_ICON_H diff --git a/3rdparty/ElaWidgetTools/example/ExamplePage/T_ListView.cpp b/3rdparty/ElaWidgetTools/example/ExamplePage/T_ListView.cpp new file mode 100644 index 0000000..323ce8d --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ExamplePage/T_ListView.cpp @@ -0,0 +1,45 @@ +#include "T_ListView.h" + +#include +#include + +#include "ElaListView.h" +#include "ElaScrollBar.h" +#include "ElaText.h" +#include "T_ListViewModel.h" +T_ListView::T_ListView(QWidget* parent) + : T_BasePage(parent) +{ + // 预览窗口标题 + setWindowTitle("ElaListView"); + + // 顶部元素 + createCustomWidget("列表视图被放置于此,可在此界面体验其效果并按需添加进项目中"); + + //ElaListView + ElaText* listText = new ElaText("ElaListView", this); + listText->setTextPixelSize(18); + _listView = new ElaListView(this); + _listView->setFixedHeight(450); + // _listView->setAlternatingRowColors(true); + _listView->setModel(new T_ListViewModel(this)); + ElaScrollBar* listViewFloatScrollBar = new ElaScrollBar(_listView->verticalScrollBar(), _listView); + listViewFloatScrollBar->setIsAnimation(true); + QHBoxLayout* listViewLayout = new QHBoxLayout(); + listViewLayout->setContentsMargins(0, 0, 10, 0); + listViewLayout->addWidget(_listView); + + QWidget* centralWidget = new QWidget(this); + centralWidget->setWindowTitle("ElaView"); + QVBoxLayout* centerVLayout = new QVBoxLayout(centralWidget); + centerVLayout->setContentsMargins(0, 0, 0, 0); + centerVLayout->addWidget(listText); + centerVLayout->addSpacing(10); + centerVLayout->addLayout(listViewLayout); + centerVLayout->addStretch(); + addCentralWidget(centralWidget, true, false, 0); +} + +T_ListView::~T_ListView() +{ +} diff --git a/3rdparty/ElaWidgetTools/example/ExamplePage/T_ListView.h b/3rdparty/ElaWidgetTools/example/ExamplePage/T_ListView.h new file mode 100644 index 0000000..90d3ba7 --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ExamplePage/T_ListView.h @@ -0,0 +1,18 @@ +#ifndef T_LISTVIEW_H +#define T_LISTVIEW_H + +#include "T_BasePage.h" + +class ElaListView; +class T_ListView : public T_BasePage +{ + Q_OBJECT +public: + Q_INVOKABLE explicit T_ListView(QWidget* parent = nullptr); + ~T_ListView(); + +private: + ElaListView* _listView{nullptr}; +}; + +#endif // T_LISTVIEW_H diff --git a/3rdparty/ElaWidgetTools/example/ExamplePage/T_LogWidget.cpp b/3rdparty/ElaWidgetTools/example/ExamplePage/T_LogWidget.cpp new file mode 100644 index 0000000..0a69487 --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ExamplePage/T_LogWidget.cpp @@ -0,0 +1,30 @@ +#include "T_LogWidget.h" + +#include + +#include + +#include "ElaLog.h" +#include "T_LogModel.h" +T_LogWidget::T_LogWidget(QWidget* parent) + : QWidget{parent} +{ + QVBoxLayout* mainLayout = new QVBoxLayout(this); + mainLayout->setContentsMargins(0, 5, 5, 0); + ElaListView* logView = new ElaListView(this); + logView->setIsTransparent(true); + _logModel = new T_LogModel(this); + logView->setModel(_logModel); + mainLayout->addWidget(logView); + connect(ElaLog::getInstance(), &ElaLog::logMessage, this, [=](QString log) { + _logModel->appendLogList(log); + }); + _logModel->appendLogList("测试条例11223344556677889900"); + _logModel->appendLogList("测试条例11223344556677889900"); + _logModel->appendLogList("测试条例11223344556677889900"); + _logModel->appendLogList("测试条例11223344556677889900"); +} + +T_LogWidget::~T_LogWidget() +{ +} diff --git a/3rdparty/ElaWidgetTools/example/ExamplePage/T_LogWidget.h b/3rdparty/ElaWidgetTools/example/ExamplePage/T_LogWidget.h new file mode 100644 index 0000000..331b4db --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ExamplePage/T_LogWidget.h @@ -0,0 +1,18 @@ +#ifndef T_LOGWIDGET_H +#define T_LOGWIDGET_H + +#include + +class T_LogModel; +class T_LogWidget : public QWidget +{ + Q_OBJECT +public: + explicit T_LogWidget(QWidget* parent = nullptr); + ~T_LogWidget(); +signals: +private: + T_LogModel* _logModel{nullptr}; +}; + +#endif // T_LOGWIDGET_H diff --git a/3rdparty/ElaWidgetTools/example/ExamplePage/T_Navigation.cpp b/3rdparty/ElaWidgetTools/example/ExamplePage/T_Navigation.cpp new file mode 100644 index 0000000..4b95d97 --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ExamplePage/T_Navigation.cpp @@ -0,0 +1,108 @@ +#include "T_Navigation.h" + +#include +#include +#include + +#include "ElaBreadcrumbBar.h" +#include "ElaPivot.h" +#include "ElaPushButton.h" +#include "ElaScrollPageArea.h" +#include "ElaTabWidget.h" +#include "ElaText.h" +T_Navigation::T_Navigation(QWidget* parent) + : T_BasePage(parent) +{ + // 预览窗口标题 + setWindowTitle("ElaNavigation"); + + // 顶部元素 + createCustomWidget("一些导航组件被放置于此,可在此界面体验其效果并按需添加进项目中"); + + //ElaBreadcrumbBar + ElaText* breadcrumbBarText = new ElaText("ElaBreadcrumbBar", this); + breadcrumbBarText->setTextPixelSize(18); + _breadcrumbBar = new ElaBreadcrumbBar(this); + QStringList breadcrumbBarList; + for (int i = 0; i < 20; i++) + { + breadcrumbBarList << QString("Item%1").arg(i + 1); + } + _breadcrumbBar->setBreadcrumbList(breadcrumbBarList); + + ElaPushButton* resetButton = new ElaPushButton("还原", this); + resetButton->setFixedSize(60, 32); + connect(resetButton, &ElaPushButton::clicked, this, [=]() { + _breadcrumbBar->setBreadcrumbList(breadcrumbBarList); + }); + + QHBoxLayout* breadcrumbBarTextLayout = new QHBoxLayout(); + breadcrumbBarTextLayout->addWidget(breadcrumbBarText); + breadcrumbBarTextLayout->addSpacing(15); + breadcrumbBarTextLayout->addWidget(resetButton); + breadcrumbBarTextLayout->addStretch(); + + ElaScrollPageArea* breadcrumbBarArea = new ElaScrollPageArea(this); + QVBoxLayout* breadcrumbBarLayout = new QVBoxLayout(breadcrumbBarArea); + breadcrumbBarLayout->addWidget(_breadcrumbBar); + + // ElaPivot + ElaText* pivotText = new ElaText("ElaPivot", this); + pivotText->setTextPixelSize(18); + _pivot = new ElaPivot(this); + _pivot->setPivotSpacing(8); + _pivot->setMarkWidth(75); + _pivot->appendPivot("本地歌曲"); + _pivot->appendPivot("下载歌曲"); + _pivot->appendPivot("下载视频"); + _pivot->appendPivot("正在下载"); + _pivot->setCurrentIndex(0); + + ElaScrollPageArea* pivotArea = new ElaScrollPageArea(this); + QVBoxLayout* pivotLayout = new QVBoxLayout(pivotArea); + pivotLayout->addWidget(_pivot); + + // ElaTabWidget + ElaText* tabWidgetText = new ElaText("ElaTabWidget", this); + tabWidgetText->setTextPixelSize(18); + _tabWidget = new ElaTabWidget(this); + _tabWidget->setFixedHeight(500); + QLabel* page1 = new QLabel("新标签页1", this); + page1->setAlignment(Qt::AlignCenter); + QFont font = page1->font(); + font.setPixelSize(32); + page1->setFont(font); + QLabel* page2 = new QLabel("新标签页2", this); + page2->setFont(font); + page2->setAlignment(Qt::AlignCenter); + QLabel* page3 = new QLabel("新标签页3", this); + page3->setFont(font); + page3->setAlignment(Qt::AlignCenter); + QLabel* page4 = new QLabel("新标签页4", this); + page4->setFont(font); + page4->setAlignment(Qt::AlignCenter); + _tabWidget->addTab(page1, QIcon(":/Resource/Image/Cirno.jpg"), "新标签页1"); + _tabWidget->addTab(page2, "新标签页2"); + _tabWidget->addTab(page3, "新标签页3"); + _tabWidget->addTab(page4, "新标签页4"); + QWidget* centralWidget = new QWidget(this); + centralWidget->setWindowTitle("ElaNavigation"); + QVBoxLayout* centerVLayout = new QVBoxLayout(centralWidget); + centerVLayout->setContentsMargins(0, 0, 0, 0); + centerVLayout->addLayout(breadcrumbBarTextLayout); + centerVLayout->addSpacing(10); + centerVLayout->addWidget(breadcrumbBarArea); + centerVLayout->addSpacing(15); + centerVLayout->addWidget(pivotText); + centerVLayout->addSpacing(10); + centerVLayout->addWidget(pivotArea); + centerVLayout->addSpacing(15); + centerVLayout->addWidget(tabWidgetText); + centerVLayout->addSpacing(10); + centerVLayout->addWidget(_tabWidget); + addCentralWidget(centralWidget, true, false, 0); +} + +T_Navigation::~T_Navigation() +{ +} diff --git a/3rdparty/ElaWidgetTools/example/ExamplePage/T_Navigation.h b/3rdparty/ElaWidgetTools/example/ExamplePage/T_Navigation.h new file mode 100644 index 0000000..9a2fcd3 --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ExamplePage/T_Navigation.h @@ -0,0 +1,22 @@ +#ifndef T_NAVIGATION_H +#define T_NAVIGATION_H + +#include "T_BasePage.h" + +class ElaPivot; +class ElaTabWidget; +class ElaBreadcrumbBar; +class T_Navigation : public T_BasePage +{ + Q_OBJECT +public: + Q_INVOKABLE explicit T_Navigation(QWidget* parent = nullptr); + ~T_Navigation(); + +private: + ElaBreadcrumbBar* _breadcrumbBar{nullptr}; + ElaPivot* _pivot{nullptr}; + ElaTabWidget* _tabWidget{nullptr}; +}; + +#endif // T_NAVIGATION_H diff --git a/3rdparty/ElaWidgetTools/example/ExamplePage/T_Popup.cpp b/3rdparty/ElaWidgetTools/example/ExamplePage/T_Popup.cpp new file mode 100644 index 0000000..4a8baa0 --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ExamplePage/T_Popup.cpp @@ -0,0 +1,114 @@ +#include "T_Popup.h" + +#include +#include + +#include "ElaCalendar.h" +#include "ElaCalendarPicker.h" +#include "ElaColorDialog.h" +#include "ElaKeyBinder.h" +#include "ElaMenu.h" +#include "ElaPushButton.h" +#include "ElaScrollPageArea.h" +#include "ElaText.h" +#include "ElaToolButton.h" + +T_Popup::T_Popup(QWidget* parent) + : T_BasePage(parent) +{ + // 预览窗口标题 + setWindowTitle("ElaPopup"); + + // 顶部元素 + createCustomWidget("一些常用的弹出组件被放置于此,可在此界面体验其效果并按需添加进项目中"); + + QWidget* centralWidget = new QWidget(this); + centralWidget->setWindowTitle("ElaPopup"); + + _toolButton = new ElaToolButton(this); + _toolButton->setIsTransparent(false); + _toolButton->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); + //_toolButton->setPopupMode(QToolButton::MenuButtonPopup); + _toolButton->setText("ElaToolButton"); + + ElaMenu* menu = new ElaMenu(this); + menu->addElaIconAction(ElaIconType::JackOLantern, "JackOLantern"); + menu->addElaIconAction(ElaIconType::LacrosseStick, "LacrosseStick"); + _toolButton->setMenu(menu); + _toolButton->setElaIcon(ElaIconType::Broom); + + ElaScrollPageArea* toolButtonArea = new ElaScrollPageArea(this); + QHBoxLayout* toolButtonLayout = new QHBoxLayout(toolButtonArea); + ElaText* toolButtonText = new ElaText("ElaToolButton", this); + toolButtonText->setTextPixelSize(15); + toolButtonLayout->addWidget(toolButtonText); + toolButtonLayout->addWidget(_toolButton); + toolButtonLayout->addStretch(); + + _colorDialog = new ElaColorDialog(this); + ElaText* colorText = new ElaText(_colorDialog->getCurrentColorRGB(), this); + colorText->setTextPixelSize(15); + ElaPushButton* colorDialogButton = new ElaPushButton(this); + colorDialogButton->setFixedSize(35, 35); + colorDialogButton->setLightDefaultColor(_colorDialog->getCurrentColor()); + colorDialogButton->setLightHoverColor(_colorDialog->getCurrentColor()); + colorDialogButton->setLightPressColor(_colorDialog->getCurrentColor()); + colorDialogButton->setDarkDefaultColor(_colorDialog->getCurrentColor()); + colorDialogButton->setDarkHoverColor(_colorDialog->getCurrentColor()); + colorDialogButton->setDarkPressColor(_colorDialog->getCurrentColor()); + connect(colorDialogButton, &ElaPushButton::clicked, this, [=]() { + _colorDialog->exec(); + }); + connect(_colorDialog, &ElaColorDialog::colorSelected, this, [=](const QColor& color) { + colorDialogButton->setLightDefaultColor(color); + colorDialogButton->setLightHoverColor(color); + colorDialogButton->setLightPressColor(color); + colorDialogButton->setDarkDefaultColor(color); + colorDialogButton->setDarkHoverColor(color); + colorDialogButton->setDarkPressColor(color); + colorText->setText(_colorDialog->getCurrentColorRGB()); + }); + + ElaScrollPageArea* colorDialogArea = new ElaScrollPageArea(this); + QHBoxLayout* colorDialogLayout = new QHBoxLayout(colorDialogArea); + ElaText* colorDialogText = new ElaText("ElaColorDialog", this); + colorDialogText->setTextPixelSize(15); + colorDialogLayout->addWidget(colorDialogText); + colorDialogLayout->addWidget(colorDialogButton); + colorDialogLayout->addWidget(colorText); + colorDialogLayout->addStretch(); + + _calendar = new ElaCalendar(this); + + _calendarPicker = new ElaCalendarPicker(this); + ElaScrollPageArea* calendarPickerArea = new ElaScrollPageArea(this); + QHBoxLayout* calendarPickerLayout = new QHBoxLayout(calendarPickerArea); + ElaText* calendarPickerText = new ElaText("ElaCalendarPicker", this); + calendarPickerText->setTextPixelSize(15); + calendarPickerLayout->addWidget(calendarPickerText); + calendarPickerLayout->addWidget(_calendarPicker); + calendarPickerLayout->addStretch(); + + _keyBinder = new ElaKeyBinder(this); + ElaScrollPageArea* keyBinderArea = new ElaScrollPageArea(this); + QHBoxLayout* keyBinderLayout = new QHBoxLayout(keyBinderArea); + ElaText* keyBinderText = new ElaText("ElaKeyBinder", this); + keyBinderText->setTextPixelSize(15); + keyBinderLayout->addWidget(keyBinderText); + keyBinderLayout->addWidget(_keyBinder); + keyBinderLayout->addStretch(); + + QVBoxLayout* centerVLayout = new QVBoxLayout(centralWidget); + centerVLayout->setContentsMargins(0, 0, 0, 0); + centerVLayout->addWidget(toolButtonArea); + centerVLayout->addWidget(colorDialogArea); + centerVLayout->addWidget(calendarPickerArea); + centerVLayout->addWidget(_calendar); + centerVLayout->addWidget(keyBinderArea); + centerVLayout->addStretch(); + addCentralWidget(centralWidget, true, false, 0); +} + +T_Popup::~T_Popup() +{ +} diff --git a/3rdparty/ElaWidgetTools/example/ExamplePage/T_Popup.h b/3rdparty/ElaWidgetTools/example/ExamplePage/T_Popup.h new file mode 100644 index 0000000..df5358b --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ExamplePage/T_Popup.h @@ -0,0 +1,26 @@ +#ifndef T_POPUP_H +#define T_POPUP_H + +#include "T_BasePage.h" + +class ElaToolButton; +class ElaColorDialog; +class ElaCalendar; +class ElaCalendarPicker; +class ElaKeyBinder; +class T_Popup : public T_BasePage +{ + Q_OBJECT +public: + Q_INVOKABLE explicit T_Popup(QWidget* parent = nullptr); + ~T_Popup(); + +private: + ElaToolButton* _toolButton{nullptr}; + ElaColorDialog* _colorDialog{nullptr}; + ElaCalendar* _calendar{nullptr}; + ElaCalendarPicker* _calendarPicker{nullptr}; + ElaKeyBinder* _keyBinder{nullptr}; +}; + +#endif// T_POPUP_H diff --git a/3rdparty/ElaWidgetTools/example/ExamplePage/T_Setting.cpp b/3rdparty/ElaWidgetTools/example/ExamplePage/T_Setting.cpp new file mode 100644 index 0000000..4178e2c --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ExamplePage/T_Setting.cpp @@ -0,0 +1,160 @@ +#include "T_Setting.h" + +#include +#include +#include + +#include "ElaApplication.h" +#include "ElaComboBox.h" +#include "ElaLog.h" +#include "ElaRadioButton.h" +#include "ElaScrollPageArea.h" +#include "ElaText.h" +#include "ElaTheme.h" +#include "ElaToggleSwitch.h" +#include "ElaWindow.h" +T_Setting::T_Setting(QWidget* parent) + : T_BasePage(parent) +{ + // 预览窗口标题 + ElaWindow* window = dynamic_cast(parent); + setWindowTitle("Setting"); + + ElaText* themeText = new ElaText("主题设置", this); + themeText->setWordWrap(false); + themeText->setTextPixelSize(18); + + _themeComboBox = new ElaComboBox(this); + _themeComboBox->addItem("日间模式"); + _themeComboBox->addItem("夜间模式"); + ElaScrollPageArea* themeSwitchArea = new ElaScrollPageArea(this); + QHBoxLayout* themeSwitchLayout = new QHBoxLayout(themeSwitchArea); + ElaText* themeSwitchText = new ElaText("主题切换", this); + themeSwitchText->setWordWrap(false); + themeSwitchText->setTextPixelSize(15); + themeSwitchLayout->addWidget(themeSwitchText); + themeSwitchLayout->addStretch(); + themeSwitchLayout->addWidget(_themeComboBox); + connect(_themeComboBox, QOverload::of(&ElaComboBox::currentIndexChanged), this, [=](int index) { + if (index == 0) + { + eTheme->setThemeMode(ElaThemeType::Light); + } + else + { + eTheme->setThemeMode(ElaThemeType::Dark); + } + }); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { + _themeComboBox->blockSignals(true); + if (themeMode == ElaThemeType::Light) + { + _themeComboBox->setCurrentIndex(0); + } + else + { + _themeComboBox->setCurrentIndex(1); + } + _themeComboBox->blockSignals(false); + }); + + ElaText* helperText = new ElaText("应用程序设置", this); + helperText->setWordWrap(false); + helperText->setTextPixelSize(18); + + _micaSwitchButton = new ElaToggleSwitch(this); + ElaScrollPageArea* micaSwitchArea = new ElaScrollPageArea(this); + QHBoxLayout* micaSwitchLayout = new QHBoxLayout(micaSwitchArea); + ElaText* micaSwitchText = new ElaText("启用云母效果(跨平台)", this); + micaSwitchText->setWordWrap(false); + micaSwitchText->setTextPixelSize(15); + micaSwitchLayout->addWidget(micaSwitchText); + micaSwitchLayout->addStretch(); + micaSwitchLayout->addWidget(_micaSwitchButton); + connect(_micaSwitchButton, &ElaToggleSwitch::toggled, this, [=](bool checked) { + eApp->setIsEnableMica(checked); + }); + + _logSwitchButton = new ElaToggleSwitch(this); + ElaScrollPageArea* logSwitchArea = new ElaScrollPageArea(this); + QHBoxLayout* logSwitchLayout = new QHBoxLayout(logSwitchArea); + ElaText* logSwitchText = new ElaText("启用日志功能", this); + logSwitchText->setWordWrap(false); + logSwitchText->setTextPixelSize(15); + logSwitchLayout->addWidget(logSwitchText); + logSwitchLayout->addStretch(); + logSwitchLayout->addWidget(_logSwitchButton); + connect(_logSwitchButton, &ElaToggleSwitch::toggled, this, [=](bool checked) { + ElaLog::getInstance()->initMessageLog(checked); + if (checked) + { + qDebug() << "日志已启用!"; + } + else + { + qDebug() << "日志已关闭!"; + } + }); + + _minimumButton = new ElaRadioButton("Minimum", this); + _compactButton = new ElaRadioButton("Compact", this); + _maximumButton = new ElaRadioButton("Maximum", this); + _autoButton = new ElaRadioButton("Auto", this); + _autoButton->setChecked(true); + ElaScrollPageArea* displayModeArea = new ElaScrollPageArea(this); + QHBoxLayout* displayModeLayout = new QHBoxLayout(displayModeArea); + ElaText* displayModeText = new ElaText("导航栏模式选择", this); + displayModeText->setWordWrap(false); + displayModeText->setTextPixelSize(15); + displayModeLayout->addWidget(displayModeText); + displayModeLayout->addStretch(); + displayModeLayout->addWidget(_minimumButton); + displayModeLayout->addWidget(_compactButton); + displayModeLayout->addWidget(_maximumButton); + displayModeLayout->addWidget(_autoButton); + connect(_minimumButton, &ElaRadioButton::toggled, this, [=](bool checked) { + if (checked) + { + window->setNavigationBarDisplayMode(ElaNavigationType::Minimal); + } + }); + connect(_compactButton, &ElaRadioButton::toggled, this, [=](bool checked) { + if (checked) + { + window->setNavigationBarDisplayMode(ElaNavigationType::Compact); + } + }); + connect(_maximumButton, &ElaRadioButton::toggled, this, [=](bool checked) { + if (checked) + { + window->setNavigationBarDisplayMode(ElaNavigationType::Maximal); + } + }); + connect(_autoButton, &ElaRadioButton::toggled, this, [=](bool checked) { + if (checked) + { + window->setNavigationBarDisplayMode(ElaNavigationType::Auto); + } + }); + + QWidget* centralWidget = new QWidget(this); + centralWidget->setWindowTitle("Setting"); + QVBoxLayout* centerLayout = new QVBoxLayout(centralWidget); + centerLayout->addSpacing(30); + centerLayout->addWidget(themeText); + centerLayout->addSpacing(10); + centerLayout->addWidget(themeSwitchArea); + centerLayout->addSpacing(15); + centerLayout->addWidget(helperText); + centerLayout->addSpacing(10); + centerLayout->addWidget(logSwitchArea); + centerLayout->addWidget(micaSwitchArea); + centerLayout->addWidget(displayModeArea); + centerLayout->addStretch(); + centerLayout->setContentsMargins(0, 0, 0, 0); + addCentralWidget(centralWidget, true, true, 0); +} + +T_Setting::~T_Setting() +{ +} diff --git a/3rdparty/ElaWidgetTools/example/ExamplePage/T_Setting.h b/3rdparty/ElaWidgetTools/example/ExamplePage/T_Setting.h new file mode 100644 index 0000000..fe3df1d --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ExamplePage/T_Setting.h @@ -0,0 +1,25 @@ +#ifndef T_SETTING_H +#define T_SETTING_H + +#include "T_BasePage.h" +class ElaRadioButton; +class ElaToggleSwitch; +class ElaComboBox; +class T_Setting : public T_BasePage +{ + Q_OBJECT +public: + Q_INVOKABLE explicit T_Setting(QWidget* parent = nullptr); + ~T_Setting(); + +private: + ElaComboBox* _themeComboBox{nullptr}; + ElaToggleSwitch* _micaSwitchButton{nullptr}; + ElaToggleSwitch* _logSwitchButton{nullptr}; + ElaRadioButton* _minimumButton{nullptr}; + ElaRadioButton* _compactButton{nullptr}; + ElaRadioButton* _maximumButton{nullptr}; + ElaRadioButton* _autoButton{nullptr}; +}; + +#endif // T_SETTING_H diff --git a/3rdparty/ElaWidgetTools/example/ExamplePage/T_TableView.cpp b/3rdparty/ElaWidgetTools/example/ExamplePage/T_TableView.cpp new file mode 100644 index 0000000..23491d2 --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ExamplePage/T_TableView.cpp @@ -0,0 +1,60 @@ +#include "T_TableView.h" + +#include +#include +#include + +#include "ElaTableView.h" +#include "ElaText.h" +#include "T_TableViewModel.h" +T_TableView::T_TableView(QWidget* parent) + : T_BasePage(parent) +{ + // 预览窗口标题 + setWindowTitle("ElaTableView"); + + // 顶部元素 + createCustomWidget("表格视图被放置于此,可在此界面体验其效果并按需添加进项目中"); + + //ElaTableView + ElaText* tableText = new ElaText("ElaTableView", this); + tableText->setTextPixelSize(18); + _tableView = new ElaTableView(this); + // ElaScrollBar* tableViewFloatScrollBar = new ElaScrollBar(_tableView->verticalScrollBar(), _tableView); + // tableViewFloatScrollBar->setIsAnimation(true); + QFont tableHeaderFont = _tableView->horizontalHeader()->font(); + tableHeaderFont.setPixelSize(16); + _tableView->horizontalHeader()->setFont(tableHeaderFont); + _tableView->setModel(new T_TableViewModel(this)); + _tableView->setAlternatingRowColors(true); + _tableView->setIconSize(QSize(38, 38)); + _tableView->verticalHeader()->setHidden(true); + _tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive); + _tableView->setSelectionBehavior(QAbstractItemView::SelectRows); + _tableView->horizontalHeader()->setMinimumSectionSize(60); + _tableView->verticalHeader()->setMinimumSectionSize(46); + _tableView->setFixedHeight(450); + connect(_tableView, &ElaTableView::tableViewShow, this, [=]() { + _tableView->setColumnWidth(0, 60); + _tableView->setColumnWidth(1, 205); + _tableView->setColumnWidth(2, 170); + _tableView->setColumnWidth(3, 150); + _tableView->setColumnWidth(4, 60); + }); + QHBoxLayout* tableViewLayout = new QHBoxLayout(); + tableViewLayout->setContentsMargins(0, 0, 10, 0); + tableViewLayout->addWidget(_tableView); + + QWidget* centralWidget = new QWidget(this); + centralWidget->setWindowTitle("ElaView"); + QVBoxLayout* centerVLayout = new QVBoxLayout(centralWidget); + centerVLayout->setContentsMargins(0, 0, 0, 0); + centerVLayout->addWidget(tableText); + centerVLayout->addSpacing(10); + centerVLayout->addLayout(tableViewLayout); + addCentralWidget(centralWidget, true, false, 0); +} + +T_TableView::~T_TableView() +{ +} diff --git a/3rdparty/ElaWidgetTools/example/ExamplePage/T_TableView.h b/3rdparty/ElaWidgetTools/example/ExamplePage/T_TableView.h new file mode 100644 index 0000000..ac25829 --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ExamplePage/T_TableView.h @@ -0,0 +1,18 @@ +#ifndef T_TABLEVIEW_H +#define T_TABLEVIEW_H + +#include "T_BasePage.h" + +class ElaTableView; +class T_TableView : public T_BasePage +{ + Q_OBJECT +public: + Q_INVOKABLE explicit T_TableView(QWidget* parent = nullptr); + ~T_TableView(); + +private: + ElaTableView* _tableView{nullptr}; +}; + +#endif // T_TABLEVIEW_H diff --git a/3rdparty/ElaWidgetTools/example/ExamplePage/T_TreeView.cpp b/3rdparty/ElaWidgetTools/example/ExamplePage/T_TreeView.cpp new file mode 100644 index 0000000..f7d824c --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ExamplePage/T_TreeView.cpp @@ -0,0 +1,141 @@ +#include "T_TreeView.h" + +#include +#include +#include + +#include "ElaPushButton.h" +#include "ElaScrollBar.h" +#include "ElaSlider.h" +#include "ElaText.h" +#include "ElaTreeView.h" +#include "T_TreeViewModel.h" +T_TreeView::T_TreeView(QWidget* parent) + : T_BasePage(parent) +{ + // 预览窗口标题 + setWindowTitle("ElaTreeView"); + + // 顶部元素 + createCustomWidget("树型视图被放置于此,可在此界面体验其效果并按需添加进项目中"); + + // ElaTreeView + T_TreeViewModel* treeModel = new T_TreeViewModel(this); + QHBoxLayout* treeLayout = new QHBoxLayout(); + treeLayout->setContentsMargins(0, 0, 10, 0); + QWidget* treeSettingWidget = new QWidget(this); + QVBoxLayout* treeSettingWidgetLayout = new QVBoxLayout(treeSettingWidget); + treeSettingWidgetLayout->setContentsMargins(0, 0, 0, 0); + treeSettingWidgetLayout->setSpacing(15); + + // 数据统计 + ElaText* dataText = new ElaText(QString("树模型总数据条数:%1").arg(treeModel->getItemCount()), this); + dataText->setTextPixelSize(15); + + // ItemHeight + ElaText* itemHeightText = new ElaText("ItemHeight", this); + itemHeightText->setTextPixelSize(15); + ElaSlider* itemHeightSlider = new ElaSlider(this); + itemHeightSlider->setRange(200, 600); + itemHeightSlider->setValue(350); + ElaText* itemHeightValueText = new ElaText("35", this); + itemHeightValueText->setTextPixelSize(15); + connect(itemHeightSlider, &ElaSlider::valueChanged, this, [=](int value) { + itemHeightValueText->setText(QString::number(value / 10)); + _treeView->setItemHeight(value / 10); + }); + QHBoxLayout* itemHeightLayout = new QHBoxLayout(); + itemHeightLayout->setContentsMargins(0, 0, 0, 0); + itemHeightLayout->addWidget(itemHeightText); + itemHeightLayout->addWidget(itemHeightSlider); + itemHeightLayout->addWidget(itemHeightValueText); + + // HeaderMargin + ElaText* headerMarginText = new ElaText("HeaderMargin", this); + headerMarginText->setTextPixelSize(15); + ElaSlider* headerMarginSlider = new ElaSlider(this); + headerMarginSlider->setRange(0, 200); + headerMarginSlider->setValue(50); + ElaText* headerMarginValueText = new ElaText("5", this); + headerMarginValueText->setTextPixelSize(15); + connect(headerMarginSlider, &ElaSlider::valueChanged, this, [=](int value) { + headerMarginValueText->setText(QString::number(value / 10)); + _treeView->setHeaderMargin(value / 10); + }); + QHBoxLayout* headerMarginLayout = new QHBoxLayout(); + headerMarginLayout->setContentsMargins(0, 0, 0, 0); + headerMarginLayout->addWidget(headerMarginText); + headerMarginLayout->addWidget(headerMarginSlider); + headerMarginLayout->addWidget(headerMarginValueText); + + // Indentation + ElaText* indentationText = new ElaText("Indentation", this); + indentationText->setTextPixelSize(15); + ElaSlider* indentationSlider = new ElaSlider(this); + indentationSlider->setRange(200, 1000); + indentationSlider->setValue(200); + ElaText* indentationValueText = new ElaText("20", this); + indentationValueText->setTextPixelSize(15); + connect(indentationSlider, &ElaSlider::valueChanged, this, [=](int value) { + indentationValueText->setText(QString::number(value / 10)); + _treeView->setIndentation(value / 10); + }); + QHBoxLayout* indentationLayout = new QHBoxLayout(); + indentationLayout->setContentsMargins(0, 0, 0, 0); + indentationLayout->addWidget(indentationText); + indentationLayout->addWidget(indentationSlider); + indentationLayout->addWidget(indentationValueText); + + //展开全部 + QHBoxLayout* expandCollapseLayout = new QHBoxLayout(); + expandCollapseLayout->setContentsMargins(0, 0, 0, 0); + ElaPushButton* expandButton = new ElaPushButton("展开全部", this); + expandButton->setFixedWidth(80); + connect(expandButton, &ElaPushButton::clicked, this, [=]() { + _treeView->expandAll(); + }); + + //收起全部 + ElaPushButton* collapseButton = new ElaPushButton("收起全部", this); + collapseButton->setFixedWidth(80); + connect(collapseButton, &ElaPushButton::clicked, this, [=]() { + _treeView->collapseAll(); + }); + expandCollapseLayout->addWidget(expandButton); + expandCollapseLayout->addWidget(collapseButton); + expandCollapseLayout->addStretch(); + + treeSettingWidgetLayout->addWidget(dataText); + treeSettingWidgetLayout->addLayout(itemHeightLayout); + treeSettingWidgetLayout->addLayout(headerMarginLayout); + treeSettingWidgetLayout->addLayout(indentationLayout); + treeSettingWidgetLayout->addLayout(expandCollapseLayout); + treeSettingWidgetLayout->addStretch(); + + // TreeView + ElaText* treeText = new ElaText("ElaTreeView", this); + treeText->setTextPixelSize(18); + _treeView = new ElaTreeView(this); + ElaScrollBar* treeViewFloatScrollBar = new ElaScrollBar(_treeView->verticalScrollBar(), _treeView); + treeViewFloatScrollBar->setIsAnimation(true); + QFont headerFont = _treeView->header()->font(); + headerFont.setPixelSize(16); + _treeView->header()->setFont(headerFont); + _treeView->setFixedHeight(450); + _treeView->setModel(treeModel); + treeLayout->addWidget(treeSettingWidget); + treeLayout->addWidget(_treeView); + + QWidget* centralWidget = new QWidget(this); + centralWidget->setWindowTitle("ElaView"); + QVBoxLayout* centerVLayout = new QVBoxLayout(centralWidget); + centerVLayout->setContentsMargins(0, 0, 0, 0); + centerVLayout->addWidget(treeText); + centerVLayout->addSpacing(10); + centerVLayout->addLayout(treeLayout); + addCentralWidget(centralWidget, true, false, 0); +} + +T_TreeView::~T_TreeView() +{ +} diff --git a/3rdparty/ElaWidgetTools/example/ExamplePage/T_TreeView.h b/3rdparty/ElaWidgetTools/example/ExamplePage/T_TreeView.h new file mode 100644 index 0000000..6e13e59 --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ExamplePage/T_TreeView.h @@ -0,0 +1,18 @@ +#ifndef T_TREEVIEW_H +#define T_TREEVIEW_H + +#include "T_BasePage.h" + +class ElaTreeView; +class T_TreeView : public T_BasePage +{ + Q_OBJECT +public: + Q_INVOKABLE explicit T_TreeView(QWidget* parent = nullptr); + ~T_TreeView(); + +private: + ElaTreeView* _treeView{nullptr}; +}; + +#endif // T_TREEVIEW_H diff --git a/3rdparty/ElaWidgetTools/example/ExamplePage/T_UpdateWidget.cpp b/3rdparty/ElaWidgetTools/example/ExamplePage/T_UpdateWidget.cpp new file mode 100644 index 0000000..0c286b5 --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ExamplePage/T_UpdateWidget.cpp @@ -0,0 +1,34 @@ +#include "T_UpdateWidget.h" + +#include + +#include "ElaText.h" +T_UpdateWidget::T_UpdateWidget(QWidget* parent) + : QWidget{parent} +{ + setMinimumSize(200, 260); + QVBoxLayout* mainLayout = new QVBoxLayout(this); + mainLayout->setSizeConstraint(QLayout::SetMaximumSize); + mainLayout->setContentsMargins(5, 10, 5, 5); + mainLayout->setSpacing(4); + ElaText* updateTitle = new ElaText("2024-11-17更新", 15, this); + ElaText* update1 = new ElaText("1、新增ElaKeyBinder单键位绑定组件,支持鼠标按键", 13, this); + ElaText* update2 = new ElaText("2、ElaContentDialog中间按钮按下行为优化", 13, this); + ElaText* update3 = new ElaText("3、ElaWindow导航模式切换相关问题修正", 13, this); + ElaText* update4 = new ElaText("4、CMAKE编译配置和体验优化", 13, this); + update1->setIsWrapAnywhere(true); + update2->setIsWrapAnywhere(true); + update3->setIsWrapAnywhere(true); + update4->setIsWrapAnywhere(true); + + mainLayout->addWidget(updateTitle); + mainLayout->addWidget(update1); + mainLayout->addWidget(update2); + mainLayout->addWidget(update3); + mainLayout->addWidget(update4); + mainLayout->addStretch(); +} + +T_UpdateWidget::~T_UpdateWidget() +{ +} diff --git a/3rdparty/ElaWidgetTools/example/ExamplePage/T_UpdateWidget.h b/3rdparty/ElaWidgetTools/example/ExamplePage/T_UpdateWidget.h new file mode 100644 index 0000000..8652c52 --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ExamplePage/T_UpdateWidget.h @@ -0,0 +1,14 @@ +#ifndef T_UPDATEWIDGET_H +#define T_UPDATEWIDGET_H + +#include + +class T_UpdateWidget : public QWidget +{ + Q_OBJECT +public: + explicit T_UpdateWidget(QWidget* parent = nullptr); + ~T_UpdateWidget(); +}; + +#endif // T_UPDATEWIDGET_H diff --git a/3rdparty/ElaWidgetTools/example/ModelView/T_IconDelegate.cpp b/3rdparty/ElaWidgetTools/example/ModelView/T_IconDelegate.cpp new file mode 100644 index 0000000..9f74c1c --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ModelView/T_IconDelegate.cpp @@ -0,0 +1,74 @@ +#include "T_IconDelegate.h" + +#include + +#include "Def.h" +#include "ElaTheme.h" +T_IconDelegate::T_IconDelegate(QObject* parent) + : QStyledItemDelegate{parent} +{ + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { _themeMode = themeMode; }); +} + +T_IconDelegate::~T_IconDelegate() +{ +} + +void T_IconDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + QStyleOptionViewItem viewOption(option); + initStyleOption(&viewOption, index); + + if (option.state.testFlag(QStyle::State_HasFocus)) + { + viewOption.state &= ~QStyle::State_HasFocus; + } + QStyledItemDelegate::paint(painter, viewOption, index); + QStringList iconList = index.data(Qt::UserRole).toStringList(); + if (iconList.count() != 2) + { + return; + } + QString iconName = iconList.at(0); + QString iconValue = iconList.at(1); + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + painter->save(); + QFont iconFont = QFont("ElaAwesome"); + iconFont.setPixelSize(22); + painter->setFont(iconFont); + painter->setPen(ElaThemeColor(_themeMode, BasicText)); + painter->drawText(option.rect.x() + option.rect.width() / 2 - 11, option.rect.y() + option.rect.height() / 2 - 11, iconValue); + painter->restore(); + // 文字绘制 + painter->setPen(ElaThemeColor(_themeMode, BasicText)); + QFont titlefont = painter->font(); + titlefont.setPixelSize(13); + painter->setFont(titlefont); + qreal rowTextWidth = option.rect.width() * 0.8; + int subTitleRow = painter->fontMetrics().horizontalAdvance(iconName) / rowTextWidth; + if (subTitleRow > 0) + { + QString subTitleText = iconName; + for (int i = 0; i < subTitleRow + 1; i++) + { + QString text = painter->fontMetrics().elidedText(subTitleText, Qt::ElideRight, rowTextWidth); + if (text.right(3).contains("…")) + { + text = text.replace("…", subTitleText.mid(text.length() - 1, 1)); + } + subTitleText.remove(0, text.length()); + painter->drawText(option.rect.x() + option.rect.width() / 2 - painter->fontMetrics().horizontalAdvance(text) / 2, option.rect.y() + option.rect.height() - 10 * (subTitleRow + 1 - i), text); + } + } + else + { + painter->drawText(option.rect.x() + option.rect.width() / 2 - painter->fontMetrics().horizontalAdvance(iconName) / 2, option.rect.y() + option.rect.height() - 20, iconName); + } + painter->restore(); +} + +QSize T_IconDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + return QSize(100, 100); +} diff --git a/3rdparty/ElaWidgetTools/example/ModelView/T_IconDelegate.h b/3rdparty/ElaWidgetTools/example/ModelView/T_IconDelegate.h new file mode 100644 index 0000000..5696435 --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ModelView/T_IconDelegate.h @@ -0,0 +1,22 @@ +#ifndef T_ICONDELEGATE_H +#define T_ICONDELEGATE_H + +#include + +#include "Def.h" +class T_IconDelegate : public QStyledItemDelegate +{ + Q_OBJECT +public: + explicit T_IconDelegate(QObject* parent = nullptr); + ~T_IconDelegate(); + +protected: + void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; + QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override; + +private: + ElaThemeType::ThemeMode _themeMode{ElaThemeType::Light}; +}; + +#endif // T_ICONDELEGATE_H diff --git a/3rdparty/ElaWidgetTools/example/ModelView/T_IconModel.cpp b/3rdparty/ElaWidgetTools/example/ModelView/T_IconModel.cpp new file mode 100644 index 0000000..aa0ff1a --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ModelView/T_IconModel.cpp @@ -0,0 +1,86 @@ +#include "T_IconModel.h" + +#include "Def.h" +T_IconModel::T_IconModel(QObject* parent) + : QAbstractListModel{parent} +{ + _metaEnum = QMetaEnum::fromType(); + _rowCount = _metaEnum.keyCount() - 1; + _pIsSearchMode = false; +} + +T_IconModel::~T_IconModel() +{ +} + +int T_IconModel::rowCount(const QModelIndex& parent) const +{ + return _rowCount; +} + +void T_IconModel::setSearchKeyList(QStringList list) +{ + beginResetModel(); + this->_searchKeyList = list; + if (_pIsSearchMode) + { + _rowCount = this->getSearchKeyList().count(); + } + else + { + _rowCount = _metaEnum.keyCount() - 1; + } + endResetModel(); +} + +QStringList T_IconModel::getSearchKeyList() +{ + return this->_searchKeyList; +} + +QVariant T_IconModel::data(const QModelIndex& index, int role) const +{ + if (role == Qt::UserRole) + { + if (!_pIsSearchMode) + { + if (index.row() >= _metaEnum.keyCount() - 1) + { + return QVariant(); + } + return QStringList{_metaEnum.key(index.row() + 1), QChar(_metaEnum.value(index.row() + 1))}; + } + else + { + QStringList iconList; + if (index.row() >= _searchKeyList.count()) + { + return QVariant(); + } + iconList.append(_searchKeyList.at(index.row())); + iconList.append(QChar(_metaEnum.keyToValue(_searchKeyList.at(index.row()).toUtf8().constData()))); + return iconList; + } + } + return QVariant(); +} + +QString T_IconModel::getIconNameFromModelIndex(const QModelIndex& index) const +{ + QString iconName; + if (_pIsSearchMode) + { + if (index.row() < _searchKeyList.count()) + { + iconName = QString("ElaIconType::") + _searchKeyList.at(index.row()); + } + } + else + { + if (index.row() < _metaEnum.keyCount() - 1) + { + iconName = QString("ElaIconType::") + _metaEnum.key(index.row() + 1); + } + } + return iconName; +} diff --git a/3rdparty/ElaWidgetTools/example/ModelView/T_IconModel.h b/3rdparty/ElaWidgetTools/example/ModelView/T_IconModel.h new file mode 100644 index 0000000..83ba8bc --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ModelView/T_IconModel.h @@ -0,0 +1,28 @@ +#ifndef T_ICONMODEL_H +#define T_ICONMODEL_H + +#include +#include + +#include "stdafx.h" +class T_IconModel : public QAbstractListModel +{ + Q_OBJECT + Q_PROPERTY_CREATE(bool, IsSearchMode) +public: + explicit T_IconModel(QObject* parent = nullptr); + ~T_IconModel(); + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + void setSearchKeyList(QStringList list); + QStringList getSearchKeyList(); + + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + QString getIconNameFromModelIndex(const QModelIndex& index) const; + +private: + QMetaEnum _metaEnum; + QStringList _searchKeyList; + int _rowCount; +}; + +#endif // T_ICONMODEL_H diff --git a/3rdparty/ElaWidgetTools/example/ModelView/T_ListViewModel.cpp b/3rdparty/ElaWidgetTools/example/ModelView/T_ListViewModel.cpp new file mode 100644 index 0000000..71056d1 --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ModelView/T_ListViewModel.cpp @@ -0,0 +1,63 @@ +#include "T_ListViewModel.h" + +T_ListViewModel::T_ListViewModel(QObject* parent) + : QAbstractListModel{parent} +{ + _dataList.append("最轻一个灵魂 送出玫瑰星光"); + _dataList.append("最轻一句提问 答案是生命的存亡"); + _dataList.append("最轻一颗麦种 由云帆托起"); + _dataList.append("流浪至黑暗的心脏"); + _dataList.append("轻轻的一支航船"); + _dataList.append("归来却被囚困旧港湾"); + _dataList.append("轻轻的一些落叶"); + _dataList.append("飘零在梦萦故乡彼岸"); + _dataList.append("轻轻的虫 森林鸣唱"); + _dataList.append("轻轻的鱼 渡向汪洋"); + _dataList.append("我们是 轻轻轻轻摇曳的篝火"); + _dataList.append("轻轻对天空哼一支歌"); + _dataList.append("轻轻被谁听见了"); + _dataList.append("轻轻的被随意熄灭了"); + _dataList.append("可我要 重重将夜灼伤"); + _dataList.append("可我要 重重将夜点燃"); + _dataList.append("更明亮的 也曾尘埃一样"); + _dataList.append("最轻一段目光 注视年岁漫长"); + _dataList.append("最轻一根琴弦 沉默着致命的声浪"); + _dataList.append("最轻一线悬丝 紧握住锋芒"); + _dataList.append("牵系着存亡的衡量"); + _dataList.append("轻轻的一枚棋子 阻挡在骑士前进路上"); + _dataList.append("轻轻的一双手掌 爱抚的婴孩不再成长"); + _dataList.append("轻轻的虫 童话里唱"); + _dataList.append("轻轻的鱼 向宇宙望"); + _dataList.append("我们是 轻轻轻轻摇曳的篝火"); + _dataList.append("轻轻对天空哼一支歌"); + _dataList.append("轻轻被谁听见了"); + _dataList.append("轻轻的被随意熄灭了"); + _dataList.append("可我要 重重将夜灼伤"); + _dataList.append("可我要 重重将夜点燃"); + _dataList.append("更明亮的 也曾尘埃一样"); + _dataList.append("轻轻时间 重重浩荡纪年"); + _dataList.append("轻轻的文明 重重书写"); + _dataList.append("轻轻身躯 重重对峙黑夜"); + _dataList.append("轻轻的人类 重重思想"); + _dataList.append("向宇宙望 向宇宙望"); + _dataList.append("向远航"); + _dataList.append("最重一粒微光"); +} + +T_ListViewModel::~T_ListViewModel() +{ +} + +int T_ListViewModel::rowCount(const QModelIndex& parent) const +{ + return this->_dataList.count(); +} + +QVariant T_ListViewModel::data(const QModelIndex& index, int role) const +{ + if (role == Qt::DisplayRole) + { + return _dataList[index.row()]; + } + return QVariant(); +} diff --git a/3rdparty/ElaWidgetTools/example/ModelView/T_ListViewModel.h b/3rdparty/ElaWidgetTools/example/ModelView/T_ListViewModel.h new file mode 100644 index 0000000..0cbe586 --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ModelView/T_ListViewModel.h @@ -0,0 +1,21 @@ +#ifndef T_LISTVIEWMODEL_H +#define T_LISTVIEWMODEL_H + +#include + +class T_ListViewModel : public QAbstractListModel +{ + Q_OBJECT +public: + explicit T_ListViewModel(QObject* parent = nullptr); + ~T_ListViewModel(); + +protected: + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + QVariant data(const QModelIndex& index, int role) const override; + +private: + QStringList _dataList; +}; + +#endif // T_LISTVIEWMODEL_H diff --git a/3rdparty/ElaWidgetTools/example/ModelView/T_LogModel.cpp b/3rdparty/ElaWidgetTools/example/ModelView/T_LogModel.cpp new file mode 100644 index 0000000..d39d3b3 --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ModelView/T_LogModel.cpp @@ -0,0 +1,43 @@ +#include "T_LogModel.h" + +T_LogModel::T_LogModel(QObject* parent) + : QAbstractListModel{parent} +{ +} + +T_LogModel::~T_LogModel() +{ +} + +int T_LogModel::rowCount(const QModelIndex& parent) const +{ + return this->_logList.count(); +} + +QVariant T_LogModel::data(const QModelIndex& index, int role) const +{ + if (role == Qt::DisplayRole) + { + return _logList[index.row()]; + } + return QVariant(); +} + +void T_LogModel::setLogList(QStringList list) +{ + beginResetModel(); + this->_logList = list; + endResetModel(); +} + +void T_LogModel::appendLogList(QString log) +{ + beginResetModel(); + this->_logList.append(log); + endResetModel(); +} + +QStringList T_LogModel::getLogList() const +{ + return this->_logList; +} diff --git a/3rdparty/ElaWidgetTools/example/ModelView/T_LogModel.h b/3rdparty/ElaWidgetTools/example/ModelView/T_LogModel.h new file mode 100644 index 0000000..df2f281 --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ModelView/T_LogModel.h @@ -0,0 +1,24 @@ +#ifndef T_LOGMODEL_H +#define T_LOGMODEL_H + +#include + +class T_LogModel : public QAbstractListModel +{ + Q_OBJECT +public: + explicit T_LogModel(QObject* parent = nullptr); + ~T_LogModel(); + void setLogList(QStringList list); + void appendLogList(QString log); + QStringList getLogList() const; + +protected: + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + QVariant data(const QModelIndex& index, int role) const override; + +private: + QStringList _logList; +}; + +#endif // T_LOGMODEL_H diff --git a/3rdparty/ElaWidgetTools/example/ModelView/T_TableViewModel.cpp b/3rdparty/ElaWidgetTools/example/ModelView/T_TableViewModel.cpp new file mode 100644 index 0000000..4ff8a3a --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ModelView/T_TableViewModel.cpp @@ -0,0 +1,120 @@ +#include "T_TableViewModel.h" + +#include +T_TableViewModel::T_TableViewModel(QObject* parent) + : QAbstractTableModel{parent} +{ + _header << "预览" + << "歌名" + << "歌手" + << "专辑" + << "时长"; + QStringList data0; + QStringList data1; + QStringList data2; + QStringList data3; + QStringList data4; + QStringList data5; + QStringList data6; + QStringList data7; + QStringList data8; + data0 << "夜航星(Night Voyager)" + << "不才/三体宇宙" + << "我的三体之章北海传" + << "05:03"; + data1 << "玫瑰少年" + << "五月天" + << "玫瑰少年" + << "03:55"; + data2 << "Collapsing World(Original Mix)" + << "Lightscape" + << "Collapsing World" + << "03:10"; + data3 << "RAIN MAN (雨人)" + << "AKIHIDE (佐藤彰秀)" + << "RAIN STORY" + << "05:37"; + data4 << "黑暗森林" + << "雲翼星辰" + << "黑暗森林" + << "05:47"; + data5 << "轻(我的三体第四季主题曲)" + << "刘雪茗" + << "我的三体第四季" + << "01:59"; + data6 << "STYX HELIX" + << "MYTH & ROID" + << "STYX HELIX" + << "04:51"; + data7 << "LAST STARDUST" + << "Aimer" + << "DAWN" + << "05:18"; + data8 << "Running In The Dark" + << "MONKEY MAJIK/塞壬唱片" + << "Running In The Dark" + << "03:40"; + _dataList.append(data0); + _dataList.append(data1); + _dataList.append(data2); + _dataList.append(data3); + _dataList.append(data4); + _dataList.append(data5); + _dataList.append(data6); + _dataList.append(data7); + _dataList.append(data8); + + _iconList.append(QIcon(QPixmap(":/Resource/Image/Model/NaightNavigationStar.jpg").scaled(38, 38, Qt::KeepAspectRatio, Qt::SmoothTransformation))); + _iconList.append(QIcon(QPixmap(":/Resource/Image/Model/MaVieEnRose.jpg").scaled(38, 38, Qt::KeepAspectRatio, Qt::SmoothTransformation))); + _iconList.append(QIcon(QPixmap(":/Resource/Image/Model/CollapsingWorld.jpg").scaled(38, 38, Qt::KeepAspectRatio, Qt::SmoothTransformation))); + _iconList.append(QIcon(QPixmap(":/Resource/Image/Model/RainMan.jpg").scaled(38, 38, Qt::KeepAspectRatio, Qt::SmoothTransformation))); + _iconList.append(QIcon(QPixmap(":/Resource/Image/Model/DarkForest.jpg").scaled(38, 38, Qt::KeepAspectRatio, Qt::SmoothTransformation))); + _iconList.append(QIcon(QPixmap(":/Resource/Image/Model/Light.jpg").scaled(38, 38, Qt::KeepAspectRatio, Qt::SmoothTransformation))); + _iconList.append(QIcon(QPixmap(":/Resource/Image/Model/STYXHELIX.jpg").scaled(38, 38, Qt::KeepAspectRatio, Qt::SmoothTransformation))); + _iconList.append(QIcon(QPixmap(":/Resource/Image/Model/LASTSTARDUST.jpg").scaled(38, 38, Qt::KeepAspectRatio, Qt::SmoothTransformation))); + _iconList.append(QIcon(QPixmap(":/Resource/Image/Model/RunningInTheDark.jpg").scaled(38, 38, Qt::KeepAspectRatio, Qt::SmoothTransformation))); +} + +T_TableViewModel::~T_TableViewModel() +{ +} + +int T_TableViewModel::rowCount(const QModelIndex& parent) const +{ + return 100; +} + +int T_TableViewModel::columnCount(const QModelIndex& parent) const +{ + return _header.count(); +} + +QVariant T_TableViewModel::data(const QModelIndex& index, int role) const +{ + if (role == Qt::DisplayRole && index.column() != 0) + { + return _dataList[index.row() % 9][index.column() - 1]; + } + else if (role == Qt::DecorationRole && index.column() == 0) + { + return _iconList[index.row() % 9]; + } + else if (role == Qt::DecorationPropertyRole) + { + return Qt::AlignCenter; + } + else if (role == Qt::TextAlignmentRole && index.column() == 4) + { + return Qt::AlignCenter; + } + return QVariant(); +} + +QVariant T_TableViewModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) + { + return _header[section]; + } + return QAbstractTableModel::headerData(section, orientation, role); +} diff --git a/3rdparty/ElaWidgetTools/example/ModelView/T_TableViewModel.h b/3rdparty/ElaWidgetTools/example/ModelView/T_TableViewModel.h new file mode 100644 index 0000000..c901278 --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ModelView/T_TableViewModel.h @@ -0,0 +1,24 @@ +#ifndef T_TABLEVIEWMODEL_H +#define T_TABLEVIEWMODEL_H + +#include + +class T_TableViewModel : public QAbstractTableModel +{ + Q_OBJECT +public: + explicit T_TableViewModel(QObject* parent = nullptr); + ~T_TableViewModel(); + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + int columnCount(const QModelIndex& parent = QModelIndex()) const override; + + QVariant data(const QModelIndex& index, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + +private: + QStringList _header; + QList _dataList; + QList _iconList; +}; + +#endif // T_TABLEVIEWMODEL_H diff --git a/3rdparty/ElaWidgetTools/example/ModelView/T_TreeItem.cpp b/3rdparty/ElaWidgetTools/example/ModelView/T_TreeItem.cpp new file mode 100644 index 0000000..7a56a09 --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ModelView/T_TreeItem.cpp @@ -0,0 +1,110 @@ +#include "T_TreeItem.h" + +#include +T_TreeItem::T_TreeItem(QString itemTitle, T_TreeItem* parent) + : QObject{parent} +{ + _itemKey = QUuid::createUuid().toString().remove("{").remove("}").remove("-"); + _itemTitle = itemTitle; + _pParentItem = parent; + _pIsChecked = false; +} + +T_TreeItem::~T_TreeItem() +{ + qDeleteAll(_pChildrenItems); +} + +QString T_TreeItem::getItemKey() const +{ + return _itemKey; +} + +QString T_TreeItem::getItemTitle() const +{ + return _itemTitle; +} + +void T_TreeItem::setChildChecked(bool isChecked) +{ + if (isChecked) + { + for (auto node : _pChildrenItems) + { + node->setIsChecked(isChecked); + node->setChildChecked(isChecked); + } + } + else + { + for (auto node : _pChildrenItems) + { + node->setChildChecked(isChecked); + node->setIsChecked(isChecked); + } + } +} + +Qt::CheckState T_TreeItem::getChildCheckState() +{ + bool isAllChecked = true; + bool isAnyChecked = false; + for (auto node : _pChildrenItems) + { + if (node->getIsChecked()) + { + isAnyChecked = true; + } + else + { + isAllChecked = false; + } + Qt::CheckState childState = node->getChildCheckState(); + if (childState == Qt::PartiallyChecked) + { + isAllChecked = false; + isAnyChecked = true; + break; + } + else if (childState == Qt::Unchecked) + { + isAllChecked = false; + } + } + if (_pChildrenItems.count() > 0) + { + if (isAllChecked) + { + return Qt::Checked; + } + if (isAnyChecked) + { + return Qt::PartiallyChecked; + } + return Qt::Unchecked; + } + return Qt::Checked; +} + +void T_TreeItem::appendChildItem(T_TreeItem* childItem) +{ + _pChildrenItems.append(childItem); +} + +bool T_TreeItem::getIsHasChild() const +{ + if (_pChildrenItems.count() > 0) + { + return true; + } + return false; +} + +int T_TreeItem::getRow() const +{ + if (_pParentItem) + { + return _pParentItem->getChildrenItems().indexOf(const_cast(this)); + } + return 0; +} diff --git a/3rdparty/ElaWidgetTools/example/ModelView/T_TreeItem.h b/3rdparty/ElaWidgetTools/example/ModelView/T_TreeItem.h new file mode 100644 index 0000000..3cd40cc --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ModelView/T_TreeItem.h @@ -0,0 +1,33 @@ +#ifndef T_TREEITEM_H +#define T_TREEITEM_H + +#include +#include + +#include "stdafx.h" +class T_TreeItem : public QObject +{ + Q_OBJECT + Q_PROPERTY_CREATE(QList, ChildrenItems) + Q_PROPERTY_CREATE(bool, IsChecked) + Q_PRIVATE_CREATE(T_TreeItem*, ParentItem) +public: + explicit T_TreeItem(QString itemTitle, T_TreeItem* parent = nullptr); + ~T_TreeItem(); + QString getItemKey() const; + QString getItemTitle() const; + + void setChildChecked(bool isChecked); + Qt::CheckState getChildCheckState(); + + void appendChildItem(T_TreeItem* childItem); + bool getIsHasChild() const; + int getRow() const; + +private: + QString _itemKey = ""; + QString _itemTitle = ""; + bool _isExpanded{false}; +}; + +#endif // T_TREEITEM_H diff --git a/3rdparty/ElaWidgetTools/example/ModelView/T_TreeViewModel.cpp b/3rdparty/ElaWidgetTools/example/ModelView/T_TreeViewModel.cpp new file mode 100644 index 0000000..332b350 --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ModelView/T_TreeViewModel.cpp @@ -0,0 +1,169 @@ +#include "T_TreeViewModel.h" + +#include + +#include "T_TreeItem.h" +T_TreeViewModel::T_TreeViewModel(QObject* parent) + : QAbstractItemModel{parent} +{ + _rootItem = new T_TreeItem("root"); + for (int i = 0; i < 20; i++) + { + T_TreeItem* level1Item = new T_TreeItem(QString("Lv1--TreeItem%1").arg(i + 1), _rootItem); + for (int j = 0; j < 6; j++) + { + T_TreeItem* level2Item = new T_TreeItem(QString("Lv2--TreeItem%1").arg(j + 1), level1Item); + for (int k = 0; k < 6; k++) + { + T_TreeItem* level3Item = new T_TreeItem(QString("Lv3--TreeItem%1").arg(k + 1), level2Item); + for (int l = 0; l < 6; l++) + { + T_TreeItem* level4Item = new T_TreeItem(QString("Lv4--TreeItem%1").arg(l + 1), level3Item); + level3Item->appendChildItem(level4Item); + _itemsMap.insert(level4Item->getItemKey(), level4Item); + } + level2Item->appendChildItem(level3Item); + _itemsMap.insert(level3Item->getItemKey(), level3Item); + } + level1Item->appendChildItem(level2Item); + _itemsMap.insert(level2Item->getItemKey(), level2Item); + } + _rootItem->appendChildItem(level1Item); + _itemsMap.insert(level1Item->getItemKey(), level1Item); + } +} + +T_TreeViewModel::~T_TreeViewModel() +{ + delete _rootItem; +} + +QModelIndex T_TreeViewModel::parent(const QModelIndex& child) const +{ + if (!child.isValid()) + { + return QModelIndex(); + } + T_TreeItem* childItem = static_cast(child.internalPointer()); + T_TreeItem* parentItem = childItem->getParentItem(); + if (parentItem == _rootItem) + { + return QModelIndex(); + } + else if (parentItem == nullptr) + { + return QModelIndex(); + } + return createIndex(parentItem->getRow(), 0, parentItem); +} + +QModelIndex T_TreeViewModel::index(int row, int column, const QModelIndex& parent) const +{ + if (!hasIndex(row, column, parent)) + { + return QModelIndex(); + } + T_TreeItem* parentItem; + if (!parent.isValid()) + { + parentItem = _rootItem; + } + else + { + parentItem = static_cast(parent.internalPointer()); + } + T_TreeItem* childItem = nullptr; + if (parentItem->getChildrenItems().count() > row) + { + childItem = parentItem->getChildrenItems().at(row); + } + if (childItem) + { + return createIndex(row, column, childItem); + } + return QModelIndex(); +} + +int T_TreeViewModel::rowCount(const QModelIndex& parent) const +{ + T_TreeItem* parentItem; + if (parent.column() > 0) + { + return 0; + } + if (!parent.isValid()) + { + parentItem = _rootItem; + } + else + { + parentItem = static_cast(parent.internalPointer()); + } + return parentItem->getChildrenItems().count(); +} + +int T_TreeViewModel::columnCount(const QModelIndex& parent) const +{ + Q_UNUSED(parent) + return 1; +} + +QVariant T_TreeViewModel::data(const QModelIndex& index, int role) const +{ + if (role == Qt::DisplayRole) + { + return static_cast(index.internalPointer())->getItemTitle(); + } + else if (role == Qt::DecorationRole) + { + return QIcon(":/Resource/Image/Cirno.jpg"); + } + else if (role == Qt::CheckStateRole) + { + T_TreeItem* item = static_cast(index.internalPointer()); + if (item->getIsHasChild()) + { + return item->getChildCheckState(); + } + else + { + return item->getIsChecked() ? Qt::Checked : Qt::Unchecked; + } + return Qt::Unchecked; + } + return QVariant(); +} + +bool T_TreeViewModel::setData(const QModelIndex& index, const QVariant& value, int role) +{ + if (role == Qt::CheckStateRole) + { + T_TreeItem* item = static_cast(index.internalPointer()); + item->setIsChecked(!item->getIsChecked()); + item->setChildChecked(item->getIsChecked()); + Q_EMIT dataChanged(QModelIndex(), QModelIndex(), {role}); + return true; + } + return QAbstractItemModel::setData(index, value, role); +} + +Qt::ItemFlags T_TreeViewModel::flags(const QModelIndex& index) const +{ + Qt::ItemFlags flags = QAbstractItemModel::flags(index); + flags |= Qt::ItemIsUserCheckable; + return flags; +} + +QVariant T_TreeViewModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) + { + return QString("ElaTreeView-Example-4Level"); + } + return QAbstractItemModel::headerData(section, orientation, role); +} + +int T_TreeViewModel::getItemCount() const +{ + return this->_itemsMap.count(); +} diff --git a/3rdparty/ElaWidgetTools/example/ModelView/T_TreeViewModel.h b/3rdparty/ElaWidgetTools/example/ModelView/T_TreeViewModel.h new file mode 100644 index 0000000..4a30b06 --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/ModelView/T_TreeViewModel.h @@ -0,0 +1,30 @@ +#ifndef T_TREEVIEWMODEL_H +#define T_TREEVIEWMODEL_H + +#include +#include + +class T_TreeItem; +class T_TreeViewModel : public QAbstractItemModel +{ + Q_OBJECT +public: + explicit T_TreeViewModel(QObject* parent = nullptr); + ~T_TreeViewModel(); + QModelIndex parent(const QModelIndex& child) const override; + QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + int columnCount(const QModelIndex& parent = QModelIndex()) const override; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; + Qt::ItemFlags flags(const QModelIndex& index) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + + int getItemCount() const; + +private: + QMap _itemsMap; + T_TreeItem* _rootItem{nullptr}; +}; + +#endif // T_TREEVIEWMODEL_H diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/Card/beach.png b/3rdparty/ElaWidgetTools/example/Resource/Image/Card/beach.png new file mode 100644 index 0000000..648fe5b Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/Card/beach.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/Card/classroom.png b/3rdparty/ElaWidgetTools/example/Resource/Image/Card/classroom.png new file mode 100644 index 0000000..9419b2c Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/Card/classroom.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/Card/dream.png b/3rdparty/ElaWidgetTools/example/Resource/Image/Card/dream.png new file mode 100644 index 0000000..ac49414 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/Card/dream.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/Card/miku.png b/3rdparty/ElaWidgetTools/example/Resource/Image/Card/miku.png new file mode 100644 index 0000000..77d9050 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/Card/miku.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/Cirno.jpg b/3rdparty/ElaWidgetTools/example/Resource/Image/Cirno.jpg new file mode 100644 index 0000000..0677457 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/Cirno.jpg differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/Home_Background.png b/3rdparty/ElaWidgetTools/example/Resource/Image/Home_Background.png new file mode 100644 index 0000000..5b7a9bd Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/Home_Background.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/IARC/IARC_12+.svg.png b/3rdparty/ElaWidgetTools/example/Resource/Image/IARC/IARC_12+.svg.png new file mode 100644 index 0000000..5dccb5f Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/IARC/IARC_12+.svg.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/IARC/IARC_16+.svg.png b/3rdparty/ElaWidgetTools/example/Resource/Image/IARC/IARC_16+.svg.png new file mode 100644 index 0000000..ed6d0cd Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/IARC/IARC_16+.svg.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/IARC/IARC_18+.svg.png b/3rdparty/ElaWidgetTools/example/Resource/Image/IARC/IARC_18+.svg.png new file mode 100644 index 0000000..5db33ea Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/IARC/IARC_18+.svg.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/IARC/IARC_3+.svg.png b/3rdparty/ElaWidgetTools/example/Resource/Image/IARC/IARC_3+.svg.png new file mode 100644 index 0000000..acc547d Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/IARC/IARC_3+.svg.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/IARC/IARC_7+.svg.png b/3rdparty/ElaWidgetTools/example/Resource/Image/IARC/IARC_7+.svg.png new file mode 100644 index 0000000..67430ae Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/IARC/IARC_7+.svg.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/Model/CollapsingWorld.jpg b/3rdparty/ElaWidgetTools/example/Resource/Image/Model/CollapsingWorld.jpg new file mode 100644 index 0000000..cbeb470 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/Model/CollapsingWorld.jpg differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/Model/DarkForest.jpg b/3rdparty/ElaWidgetTools/example/Resource/Image/Model/DarkForest.jpg new file mode 100644 index 0000000..ce65982 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/Model/DarkForest.jpg differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/Model/LASTSTARDUST.jpg b/3rdparty/ElaWidgetTools/example/Resource/Image/Model/LASTSTARDUST.jpg new file mode 100644 index 0000000..12462c6 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/Model/LASTSTARDUST.jpg differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/Model/Light.jpg b/3rdparty/ElaWidgetTools/example/Resource/Image/Model/Light.jpg new file mode 100644 index 0000000..d91ec71 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/Model/Light.jpg differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/Model/MaVieEnRose.jpg b/3rdparty/ElaWidgetTools/example/Resource/Image/Model/MaVieEnRose.jpg new file mode 100644 index 0000000..e0cb59e Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/Model/MaVieEnRose.jpg differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/Model/NaightNavigationStar.jpg b/3rdparty/ElaWidgetTools/example/Resource/Image/Model/NaightNavigationStar.jpg new file mode 100644 index 0000000..0f98251 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/Model/NaightNavigationStar.jpg differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/Model/RainMan.jpg b/3rdparty/ElaWidgetTools/example/Resource/Image/Model/RainMan.jpg new file mode 100644 index 0000000..b60f0a8 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/Model/RainMan.jpg differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/Model/RunningInTheDark.jpg b/3rdparty/ElaWidgetTools/example/Resource/Image/Model/RunningInTheDark.jpg new file mode 100644 index 0000000..aa52513 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/Model/RunningInTheDark.jpg differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/Model/STYXHELIX.jpg b/3rdparty/ElaWidgetTools/example/Resource/Image/Model/STYXHELIX.jpg new file mode 100644 index 0000000..7c79d74 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/Model/STYXHELIX.jpg differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/Moon.jpg b/3rdparty/ElaWidgetTools/example/Resource/Image/Moon.jpg new file mode 100644 index 0000000..7c6ebbe Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/Moon.jpg differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/Acrylic.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/Acrylic.png new file mode 100644 index 0000000..73c1328 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/Acrylic.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/AnimatedIcon.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/AnimatedIcon.png new file mode 100644 index 0000000..9311db4 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/AnimatedIcon.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/AnimatedVisualPlayer.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/AnimatedVisualPlayer.png new file mode 100644 index 0000000..8035660 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/AnimatedVisualPlayer.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/AnimationInterop.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/AnimationInterop.png new file mode 100644 index 0000000..6b385c5 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/AnimationInterop.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/AppBarButton.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/AppBarButton.png new file mode 100644 index 0000000..1bd23eb Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/AppBarButton.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/AppBarSeparator.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/AppBarSeparator.png new file mode 100644 index 0000000..b14b397 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/AppBarSeparator.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/AppBarToggleButton.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/AppBarToggleButton.png new file mode 100644 index 0000000..8eaaf8d Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/AppBarToggleButton.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/AutoSuggestBox.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/AutoSuggestBox.png new file mode 100644 index 0000000..5403ced Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/AutoSuggestBox.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/AutomationProperties.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/AutomationProperties.png new file mode 100644 index 0000000..7a44af3 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/AutomationProperties.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/Border.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/Border.png new file mode 100644 index 0000000..02717a9 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/Border.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/BreadcrumbBar.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/BreadcrumbBar.png new file mode 100644 index 0000000..38629c3 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/BreadcrumbBar.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/Button.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/Button.png new file mode 100644 index 0000000..7408813 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/Button.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/CalendarDatePicker.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/CalendarDatePicker.png new file mode 100644 index 0000000..b3f5f9a Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/CalendarDatePicker.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/CalendarView.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/CalendarView.png new file mode 100644 index 0000000..fe34b82 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/CalendarView.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/Canvas.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/Canvas.png new file mode 100644 index 0000000..9c857db Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/Canvas.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/Checkbox.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/Checkbox.png new file mode 100644 index 0000000..6616d87 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/Checkbox.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/Clipboard.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/Clipboard.png new file mode 100644 index 0000000..0e24c27 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/Clipboard.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/ColorPaletteResources.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ColorPaletteResources.png new file mode 100644 index 0000000..8f4e31c Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ColorPaletteResources.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/ColorPicker.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ColorPicker.png new file mode 100644 index 0000000..b4e7a96 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ColorPicker.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/ComboBox.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ComboBox.png new file mode 100644 index 0000000..dee1fd9 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ComboBox.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/CommandBar.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/CommandBar.png new file mode 100644 index 0000000..e265666 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/CommandBar.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/CommandBarFlyout.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/CommandBarFlyout.png new file mode 100644 index 0000000..415c429 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/CommandBarFlyout.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/CompactSizing.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/CompactSizing.png new file mode 100644 index 0000000..4ae95db Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/CompactSizing.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/ConnectedAnimation.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ConnectedAnimation.png new file mode 100644 index 0000000..ff4e159 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ConnectedAnimation.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/ContentDialog.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ContentDialog.png new file mode 100644 index 0000000..0ade66e Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ContentDialog.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/CreateMultipleWindows.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/CreateMultipleWindows.png new file mode 100644 index 0000000..d70a55c Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/CreateMultipleWindows.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/DataGrid.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/DataGrid.png new file mode 100644 index 0000000..3b105a3 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/DataGrid.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/DatePicker.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/DatePicker.png new file mode 100644 index 0000000..23d35b3 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/DatePicker.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/DropDownButton.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/DropDownButton.png new file mode 100644 index 0000000..dc5ff6d Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/DropDownButton.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/EasingFunction.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/EasingFunction.png new file mode 100644 index 0000000..caeee14 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/EasingFunction.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/Expander.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/Expander.png new file mode 100644 index 0000000..531a7f4 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/Expander.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/FilePicker.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/FilePicker.png new file mode 100644 index 0000000..7bcbbc1 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/FilePicker.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/FlipView.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/FlipView.png new file mode 100644 index 0000000..ee466c0 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/FlipView.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/Flyout.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/Flyout.png new file mode 100644 index 0000000..28fd032 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/Flyout.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/Grid.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/Grid.png new file mode 100644 index 0000000..6019cee Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/Grid.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/GridView.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/GridView.png new file mode 100644 index 0000000..3829acb Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/GridView.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/HyperlinkButton.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/HyperlinkButton.png new file mode 100644 index 0000000..51549ec Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/HyperlinkButton.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/IconElement.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/IconElement.png new file mode 100644 index 0000000..9164bff Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/IconElement.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/Image.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/Image.png new file mode 100644 index 0000000..96df4be Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/Image.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/ImplicitTransition.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ImplicitTransition.png new file mode 100644 index 0000000..fe595e1 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ImplicitTransition.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/InfoBadge.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/InfoBadge.png new file mode 100644 index 0000000..21263ac Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/InfoBadge.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/InfoBar.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/InfoBar.png new file mode 100644 index 0000000..3d9f9d7 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/InfoBar.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/InkCanvas.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/InkCanvas.png new file mode 100644 index 0000000..897b82c Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/InkCanvas.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/InkToolbar.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/InkToolbar.png new file mode 100644 index 0000000..2134e8c Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/InkToolbar.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/InputValidation.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/InputValidation.png new file mode 100644 index 0000000..d090df4 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/InputValidation.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/ItemsRepeater.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ItemsRepeater.png new file mode 100644 index 0000000..18dc1cd Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ItemsRepeater.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/Line.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/Line.png new file mode 100644 index 0000000..ed3e408 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/Line.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/ListBox.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ListBox.png new file mode 100644 index 0000000..2d8d1a7 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ListBox.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/ListView.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ListView.png new file mode 100644 index 0000000..8fc7cec Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ListView.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/MediaPlayerElement.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/MediaPlayerElement.png new file mode 100644 index 0000000..b517c7e Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/MediaPlayerElement.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/MenuBar.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/MenuBar.png new file mode 100644 index 0000000..35553ad Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/MenuBar.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/MenuFlyout.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/MenuFlyout.png new file mode 100644 index 0000000..a501970 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/MenuFlyout.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/NavigationView.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/NavigationView.png new file mode 100644 index 0000000..a92e069 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/NavigationView.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/NumberBox.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/NumberBox.png new file mode 100644 index 0000000..674791c Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/NumberBox.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/PageTransition.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/PageTransition.png new file mode 100644 index 0000000..25b18ab Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/PageTransition.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/ParallaxView.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ParallaxView.png new file mode 100644 index 0000000..07160cf Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ParallaxView.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/PasswordBox.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/PasswordBox.png new file mode 100644 index 0000000..9b613c7 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/PasswordBox.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/PersonPicture.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/PersonPicture.png new file mode 100644 index 0000000..23d2e64 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/PersonPicture.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/PipsPager.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/PipsPager.png new file mode 100644 index 0000000..c07faf1 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/PipsPager.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/Pivot.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/Pivot.png new file mode 100644 index 0000000..675ee0e Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/Pivot.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/ProgressBar.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ProgressBar.png new file mode 100644 index 0000000..9e0e003 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ProgressBar.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/ProgressRing.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ProgressRing.png new file mode 100644 index 0000000..fc365f6 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ProgressRing.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/PullToRefresh.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/PullToRefresh.png new file mode 100644 index 0000000..0ea571a Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/PullToRefresh.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/RadialGradientBrush.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/RadialGradientBrush.png new file mode 100644 index 0000000..047ef76 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/RadialGradientBrush.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/RadioButton.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/RadioButton.png new file mode 100644 index 0000000..9fdaafa Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/RadioButton.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/RadioButtons.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/RadioButtons.png new file mode 100644 index 0000000..cab12c1 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/RadioButtons.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/RatingControl.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/RatingControl.png new file mode 100644 index 0000000..30efd43 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/RatingControl.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/RelativePanel.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/RelativePanel.png new file mode 100644 index 0000000..df1ae73 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/RelativePanel.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/RepeatButton.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/RepeatButton.png new file mode 100644 index 0000000..eb1ad5b Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/RepeatButton.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/RevealFocus.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/RevealFocus.png new file mode 100644 index 0000000..d912778 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/RevealFocus.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/RichEditBox.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/RichEditBox.png new file mode 100644 index 0000000..3ffe648 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/RichEditBox.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/RichTextBlock.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/RichTextBlock.png new file mode 100644 index 0000000..34cd05e Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/RichTextBlock.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/ScrollViewer.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ScrollViewer.png new file mode 100644 index 0000000..d54541a Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ScrollViewer.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/SemanticZoom.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/SemanticZoom.png new file mode 100644 index 0000000..bbdbf45 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/SemanticZoom.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/Shape.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/Shape.png new file mode 100644 index 0000000..c37f49e Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/Shape.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/Slider.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/Slider.png new file mode 100644 index 0000000..cdc6175 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/Slider.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/Sound.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/Sound.png new file mode 100644 index 0000000..bcec53a Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/Sound.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/SplitButton.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/SplitButton.png new file mode 100644 index 0000000..1f0e141 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/SplitButton.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/SplitView.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/SplitView.png new file mode 100644 index 0000000..73d8119 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/SplitView.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/StackPanel.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/StackPanel.png new file mode 100644 index 0000000..f7e46a0 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/StackPanel.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/StandardUICommand.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/StandardUICommand.png new file mode 100644 index 0000000..a22ee81 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/StandardUICommand.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/SwipeControl.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/SwipeControl.png new file mode 100644 index 0000000..796eb65 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/SwipeControl.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/TabView.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/TabView.png new file mode 100644 index 0000000..0364e3a Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/TabView.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/TeachingTip.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/TeachingTip.png new file mode 100644 index 0000000..2428e2a Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/TeachingTip.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/TextBlock.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/TextBlock.png new file mode 100644 index 0000000..a97c489 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/TextBlock.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/TextBox.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/TextBox.png new file mode 100644 index 0000000..281645b Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/TextBox.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/ThemeTransition.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ThemeTransition.png new file mode 100644 index 0000000..91a74c0 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ThemeTransition.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/TimePicker.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/TimePicker.png new file mode 100644 index 0000000..c63aca3 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/TimePicker.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/TitleBar.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/TitleBar.png new file mode 100644 index 0000000..2c76819 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/TitleBar.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/ToggleButton.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ToggleButton.png new file mode 100644 index 0000000..52682aa Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ToggleButton.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/ToggleSplitButton.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ToggleSplitButton.png new file mode 100644 index 0000000..3582c10 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ToggleSplitButton.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/ToggleSwitch.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ToggleSwitch.png new file mode 100644 index 0000000..a8537a2 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ToggleSwitch.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/ToolTip.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ToolTip.png new file mode 100644 index 0000000..83eee5a Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/ToolTip.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/TreeView.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/TreeView.png new file mode 100644 index 0000000..0a652ba Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/TreeView.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/VariableSizedWrapGrid.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/VariableSizedWrapGrid.png new file mode 100644 index 0000000..f2f0c78 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/VariableSizedWrapGrid.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/Viewbox.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/Viewbox.png new file mode 100644 index 0000000..8940cdf Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/Viewbox.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/WebView.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/WebView.png new file mode 100644 index 0000000..c6742a1 Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/WebView.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/control/XamlUICommand.png b/3rdparty/ElaWidgetTools/example/Resource/Image/control/XamlUICommand.png new file mode 100644 index 0000000..9c03d5b Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/control/XamlUICommand.png differ diff --git a/3rdparty/ElaWidgetTools/example/Resource/Image/github.png b/3rdparty/ElaWidgetTools/example/Resource/Image/github.png new file mode 100644 index 0000000..059c42c Binary files /dev/null and b/3rdparty/ElaWidgetTools/example/Resource/Image/github.png differ diff --git a/3rdparty/ElaWidgetTools/example/main.cpp b/3rdparty/ElaWidgetTools/example/main.cpp new file mode 100644 index 0000000..54040ce --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/main.cpp @@ -0,0 +1,29 @@ +#include +#include + +#include "ElaApplication.h" +#include "mainwindow.h" + +int main(int argc, char* argv[]) +{ +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); +#else + //根据实际屏幕缩放比例更改 + qputenv("QT_SCALE_FACTOR", "1.5"); +#endif +#endif + QApplication a(argc, argv); + eApp->init(); + MainWindow w; + w.show(); + // HWND handle = FindWindowA(NULL, "ElaWidgetTools"); + // if (handle != NULL) + // { + // SetWindowDisplayAffinity(handle, WDA_EXCLUDEFROMCAPTURE); + // } + return a.exec(); +} diff --git a/3rdparty/ElaWidgetTools/example/mainwindow.cpp b/3rdparty/ElaWidgetTools/example/mainwindow.cpp new file mode 100644 index 0000000..4d6da58 --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/mainwindow.cpp @@ -0,0 +1,294 @@ +#include "mainwindow.h" + +#include +#include +#include +#include +#include +#include + +#include "ElaContentDialog.h" +#include "ElaDockWidget.h" +#include "ElaEventBus.h" +#include "ElaLog.h" +#include "ElaMenu.h" +#include "ElaMenuBar.h" +#include "ElaProgressBar.h" +#include "ElaStatusBar.h" +#include "ElaText.h" +#include "ElaTheme.h" +#include "ElaToolBar.h" +#include "ElaToolButton.h" +#include "T_About.h" +#include "T_BaseComponents.h" +#include "T_Card.h" +#include "T_Graphics.h" +#include "T_ListView.h" +#include "T_Setting.h" +#include "T_TableView.h" +#include "T_TreeView.h" +#ifdef Q_OS_WIN +#include "ExamplePage/T_ElaScreen.h" +#endif +#include "ExamplePage/T_Home.h" +#include "ExamplePage/T_Icon.h" +#include "ExamplePage/T_LogWidget.h" +#include "ExamplePage/T_Navigation.h" +#include "ExamplePage/T_Popup.h" +#include "ExamplePage/T_UpdateWidget.h" +MainWindow::MainWindow(QWidget* parent) + : ElaWindow(parent) +{ + initWindow(); + + //额外布局 + initEdgeLayout(); + + //中心窗口 + initContent(); + + // 拦截默认关闭事件 + _closeDialog = new ElaContentDialog(this); + connect(_closeDialog, &ElaContentDialog::rightButtonClicked, this, &MainWindow::closeWindow); + connect(_closeDialog, &ElaContentDialog::middleButtonClicked, this, [=]() { + _closeDialog->close(); + showMinimized(); + }); + this->setIsDefaultClosed(false); + connect(this, &MainWindow::closeButtonClicked, this, [=]() { + _closeDialog->exec(); + }); + + //移动到中心 + moveToCenter(); +} + +MainWindow::~MainWindow() +{ + delete this->_aboutPage; +} + +void MainWindow::initWindow() +{ + // setIsEnableMica(true); + // setIsCentralStackedWidgetTransparent(true); + setWindowIcon(QIcon(":/include/Image/Cirno.jpg")); + resize(1200, 740); + // ElaLog::getInstance()->initMessageLog(true); + // eTheme->setThemeMode(ElaThemeType::Dark); + // setIsNavigationBarEnable(false); + // setNavigationBarDisplayMode(ElaNavigationType::Compact); + // setWindowButtonFlag(ElaAppBarType::MinimizeButtonHint, false); + setUserInfoCardPixmap(QPixmap(":/Resource/Image/Cirno.jpg")); + setUserInfoCardTitle("Ela Tool"); + setUserInfoCardSubTitle("Liniyous@gmail.com"); + setWindowTitle("ElaWidgetTool"); + // setIsStayTop(true); + // setUserInfoCardVisible(false); +} + +void MainWindow::initEdgeLayout() +{ + //菜单栏 + ElaMenuBar* menuBar = new ElaMenuBar(this); + menuBar->setFixedHeight(30); + QWidget* customWidget = new QWidget(this); + QVBoxLayout* customLayout = new QVBoxLayout(customWidget); + customLayout->setContentsMargins(0, 0, 0, 0); + customLayout->addWidget(menuBar); + customLayout->addStretch(); + // this->setMenuBar(menuBar); + this->setCustomWidget(ElaAppBarType::MiddleArea, customWidget); + this->setCustomWidgetMaximumWidth(500); + + menuBar->addElaIconAction(ElaIconType::AtomSimple, "动作菜单"); + ElaMenu* iconMenu = menuBar->addMenu(ElaIconType::Aperture, "图标菜单"); + iconMenu->setMenuItemHeight(27); + iconMenu->addElaIconAction(ElaIconType::BoxCheck, "排序方式", QKeySequence::SelectAll); + iconMenu->addElaIconAction(ElaIconType::Copy, "复制"); + iconMenu->addElaIconAction(ElaIconType::MagnifyingGlassPlus, "显示设置"); + iconMenu->addSeparator(); + iconMenu->addElaIconAction(ElaIconType::ArrowRotateRight, "刷新"); + iconMenu->addElaIconAction(ElaIconType::ArrowRotateLeft, "撤销"); + menuBar->addSeparator(); + ElaMenu* shortCutMenu = new ElaMenu("快捷菜单(&A)", this); + shortCutMenu->setMenuItemHeight(27); + shortCutMenu->addElaIconAction(ElaIconType::BoxCheck, "排序方式", QKeySequence::Find); + shortCutMenu->addElaIconAction(ElaIconType::Copy, "复制"); + shortCutMenu->addElaIconAction(ElaIconType::MagnifyingGlassPlus, "显示设置"); + shortCutMenu->addSeparator(); + shortCutMenu->addElaIconAction(ElaIconType::ArrowRotateRight, "刷新"); + shortCutMenu->addElaIconAction(ElaIconType::ArrowRotateLeft, "撤销"); + menuBar->addMenu(shortCutMenu); + + menuBar->addMenu("样例菜单(&B)")->addElaIconAction(ElaIconType::ArrowRotateRight, "样例选项"); + menuBar->addMenu("样例菜单(&C)")->addElaIconAction(ElaIconType::ArrowRotateRight, "样例选项"); + menuBar->addMenu("样例菜单(&E)")->addElaIconAction(ElaIconType::ArrowRotateRight, "样例选项"); + menuBar->addMenu("样例菜单(&F)")->addElaIconAction(ElaIconType::ArrowRotateRight, "样例选项"); + menuBar->addMenu("样例菜单(&G)")->addElaIconAction(ElaIconType::ArrowRotateRight, "样例选项"); + + //工具栏 + ElaToolBar* toolBar = new ElaToolBar("工具栏", this); + toolBar->setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea); + toolBar->setToolBarSpacing(3); + toolBar->setToolButtonStyle(Qt::ToolButtonIconOnly); + toolBar->setIconSize(QSize(25, 25)); + // toolBar->setFloatable(false); + // toolBar->setMovable(false); + ElaToolButton* toolButton1 = new ElaToolButton(this); + toolButton1->setElaIcon(ElaIconType::BadgeCheck); + toolBar->addWidget(toolButton1); + ElaToolButton* toolButton2 = new ElaToolButton(this); + toolButton2->setElaIcon(ElaIconType::ChartUser); + toolBar->addWidget(toolButton2); + toolBar->addSeparator(); + ElaToolButton* toolButton3 = new ElaToolButton(this); + toolButton3->setElaIcon(ElaIconType::Bluetooth); + toolButton3->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + toolButton3->setText("Bluetooth"); + toolBar->addWidget(toolButton3); + ElaToolButton* toolButton4 = new ElaToolButton(this); + toolButton4->setElaIcon(ElaIconType::BringFront); + toolBar->addWidget(toolButton4); + toolBar->addSeparator(); + ElaToolButton* toolButton5 = new ElaToolButton(this); + toolButton5->setElaIcon(ElaIconType::ChartSimple); + toolBar->addWidget(toolButton5); + ElaToolButton* toolButton6 = new ElaToolButton(this); + toolButton6->setElaIcon(ElaIconType::FaceClouds); + toolBar->addWidget(toolButton6); + ElaToolButton* toolButton8 = new ElaToolButton(this); + toolButton8->setElaIcon(ElaIconType::Aperture); + toolBar->addWidget(toolButton8); + ElaToolButton* toolButton9 = new ElaToolButton(this); + toolButton9->setElaIcon(ElaIconType::ChartMixed); + toolBar->addWidget(toolButton9); + ElaToolButton* toolButton10 = new ElaToolButton(this); + toolButton10->setElaIcon(ElaIconType::Coins); + toolBar->addWidget(toolButton10); + ElaToolButton* toolButton11 = new ElaToolButton(this); + toolButton11->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + toolButton11->setElaIcon(ElaIconType::AlarmPlus); + toolButton11->setText("AlarmPlus"); + toolBar->addWidget(toolButton11); + ElaToolButton* toolButton12 = new ElaToolButton(this); + toolButton12->setElaIcon(ElaIconType::Crown); + toolBar->addWidget(toolButton12); + QAction* test = new QAction(this); + test->setMenu(new QMenu(this)); + + ElaProgressBar* progressBar = new ElaProgressBar(this); + progressBar->setMinimum(0); + progressBar->setMaximum(0); + progressBar->setFixedWidth(350); + toolBar->addWidget(progressBar); + + this->addToolBar(Qt::TopToolBarArea, toolBar); + + //停靠窗口 + ElaDockWidget* logDockWidget = new ElaDockWidget("日志信息", this); + logDockWidget->setWidget(new T_LogWidget(this)); + this->addDockWidget(Qt::RightDockWidgetArea, logDockWidget); + resizeDocks({logDockWidget}, {200}, Qt::Horizontal); + + ElaDockWidget* updateDockWidget = new ElaDockWidget("更新内容", this); + updateDockWidget->setWidget(new T_UpdateWidget(this)); + this->addDockWidget(Qt::RightDockWidgetArea, updateDockWidget); + resizeDocks({updateDockWidget}, {200}, Qt::Horizontal); + + //状态栏 + ElaStatusBar* statusBar = new ElaStatusBar(this); + ElaText* statusText = new ElaText("初始化成功!", this); + statusText->setTextPixelSize(14); + statusBar->addWidget(statusText); + this->setStatusBar(statusBar); +} + +void MainWindow::initContent() +{ + _homePage = new T_Home(this); +#ifdef Q_OS_WIN + _elaScreenPage = new T_ElaScreen(this); +#endif + _iconPage = new T_Icon(this); + _baseComponentsPage = new T_BaseComponents(this); + _graphicsPage = new T_Graphics(this); + _navigationPage = new T_Navigation(this); + _popupPage = new T_Popup(this); + _cardPage = new T_Card(this); + _listViewPage = new T_ListView(this); + _tableViewPage = new T_TableView(this); + _treeViewPage = new T_TreeView(this); + _settingPage = new T_Setting(this); + + QString testKey_1; + QString testKey_2; + addPageNode("HOME", _homePage, ElaIconType::House); +#ifdef Q_OS_WIN + addExpanderNode("ElaDxgi", _elaDxgiKey, ElaIconType::TvMusic); + addPageNode("ElaScreen", _elaScreenPage, _elaDxgiKey, 3, ElaIconType::ObjectGroup); +#endif + // navigation(elaScreenWidget->property("ElaPageKey").toString()); + addPageNode("ElaBaseComponents", _baseComponentsPage, ElaIconType::CabinetFiling); + + addExpanderNode("ElaView", _viewKey, ElaIconType::CameraViewfinder); + addPageNode("ElaListView", _listViewPage, _viewKey, 9, ElaIconType::List); + addPageNode("ElaTableView", _tableViewPage, _viewKey, ElaIconType::Table); + addPageNode("ElaTreeView", _treeViewPage, _viewKey, ElaIconType::ListTree); + + addPageNode("ElaGraphics", _graphicsPage, 9, ElaIconType::Paintbrush); + addPageNode("ElaCard", _cardPage, ElaIconType::Cards); + addPageNode("ElaNavigation", _navigationPage, ElaIconType::LocationArrow); + addPageNode("ElaPopup", _popupPage, ElaIconType::Envelope); + addPageNode("ElaIcon", _iconPage, 99, ElaIconType::FontCase); + addExpanderNode("TEST4", testKey_2, ElaIconType::Acorn); + addExpanderNode("TEST5", testKey_1, testKey_2, ElaIconType::Acorn); + addPageNode("Third Level", new QWidget(this), testKey_1, ElaIconType::Acorn); + addExpanderNode("TEST6", testKey_1, testKey_2, ElaIconType::Acorn); + addExpanderNode("TEST7", testKey_1, testKey_2, ElaIconType::Acorn); + addExpanderNode("TEST8", testKey_1, testKey_2, ElaIconType::Acorn); + addExpanderNode("TEST9", testKey_1, testKey_2, ElaIconType::Acorn); + addExpanderNode("TEST10", testKey_1, testKey_2, ElaIconType::Acorn); + addExpanderNode("TEST11", testKey_1, testKey_2, ElaIconType::Acorn); + addExpanderNode("TEST12", testKey_1, testKey_2, ElaIconType::Acorn); + addExpanderNode("TEST13", testKey_1, testKey_2, ElaIconType::Acorn); + addExpanderNode("TEST14", testKey_1, testKey_2, ElaIconType::Acorn); + addExpanderNode("TEST15", testKey_1, ElaIconType::Acorn); + addExpanderNode("TEST16", testKey_1, ElaIconType::Acorn); + addExpanderNode("TEST17", testKey_1, ElaIconType::Acorn); + + addFooterNode("About", nullptr, _aboutKey, 0, ElaIconType::User); + _aboutPage = new T_About(); + + _aboutPage->hide(); + connect(this, &ElaWindow::navigationNodeClicked, this, [=](ElaNavigationType::NavigationNodeType nodeType, QString nodeKey) { + if (_aboutKey == nodeKey) + { + _aboutPage->setFixedSize(400, 400); + _aboutPage->moveToCenter(); + _aboutPage->show(); + } + }); + addFooterNode("Setting", _settingPage, _settingKey, 0, ElaIconType::GearComplex); + connect(this, &MainWindow::userInfoCardClicked, this, [=]() { + this->navigation(_homePage->property("ElaPageKey").toString()); + }); +#ifdef Q_OS_WIN + connect(_homePage, &T_Home::elaScreenNavigation, this, [=]() { + this->navigation(_elaScreenPage->property("ElaPageKey").toString()); + }); +#endif + connect(_homePage, &T_Home::elaBaseComponentNavigation, this, [=]() { + this->navigation(_baseComponentsPage->property("ElaPageKey").toString()); + }); + connect(_homePage, &T_Home::elaSceneNavigation, this, [=]() { + this->navigation(_graphicsPage->property("ElaPageKey").toString()); + }); + connect(_homePage, &T_Home::elaIconNavigation, this, [=]() { + this->navigation(_iconPage->property("ElaPageKey").toString()); + }); + connect(_homePage, &T_Home::elaCardNavigation, this, [=]() { + this->navigation(_cardPage->property("ElaPageKey").toString()); + }); + qDebug() << "已注册的事件列表" << ElaEventBus::getInstance()->getRegisteredEventsName(); +} diff --git a/3rdparty/ElaWidgetTools/example/mainwindow.h b/3rdparty/ElaWidgetTools/example/mainwindow.h new file mode 100644 index 0000000..e89ebe7 --- /dev/null +++ b/3rdparty/ElaWidgetTools/example/mainwindow.h @@ -0,0 +1,55 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include + +#include "ElaWindow.h" +class T_Home; +class T_Icon; +class T_ElaScreen; +class T_BaseComponents; +class T_Graphics; +class T_Navigation; +class T_Popup; +class T_Card; +class T_ListView; +class T_TableView; +class T_TreeView; +class T_About; +class T_Setting; +class ElaContentDialog; +class MainWindow : public ElaWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget* parent = nullptr); + ~MainWindow(); + + void initWindow(); + void initEdgeLayout(); + void initContent(); + +private: + ElaContentDialog* _closeDialog{nullptr}; + T_Home* _homePage{nullptr}; +#ifdef Q_OS_WIN + T_ElaScreen* _elaScreenPage{nullptr}; +#endif + T_Icon* _iconPage{nullptr}; + T_BaseComponents* _baseComponentsPage{nullptr}; + T_Graphics* _graphicsPage{nullptr}; + T_Navigation* _navigationPage{nullptr}; + T_Popup* _popupPage{nullptr}; + T_Card* _cardPage{nullptr}; + T_ListView* _listViewPage{nullptr}; + T_TableView* _tableViewPage{nullptr}; + T_TreeView* _treeViewPage{nullptr}; + T_About* _aboutPage{nullptr}; + T_Setting* _settingPage{nullptr}; + QString _elaDxgiKey{""}; + QString _viewKey{""}; + QString _aboutKey{""}; + QString _settingKey{""}; +}; +#endif // MAINWINDOW_H diff --git a/3rdparty/ElaWidgetTools/qt6.natvis b/3rdparty/ElaWidgetTools/qt6.natvis new file mode 100644 index 0000000..d8bd150 --- /dev/null +++ b/3rdparty/ElaWidgetTools/qt6.natvis @@ -0,0 +1,735 @@ + + + + + + + {val} + + val + + + + + + + + {{ x = {x,g}, y = {y,g}, z = {_extraData().z,g}, width = {width,g}, height = {height,g} }} + {{ x = {x,g}, y = {y,g}, width = {width,g}, height = {height,g} }} + + x + y + _extraData().z + _extraData().scale + _extraData().rotation + _extraData().opacity + width + height + implicitWidth + implicitHeight + effectiveVisible + explicitEnable + _objectName(),na + parentItem + childItems, nr + + + + + {d_ptr.d,na} + + d_ptr.d + + + + + {{{data1,Xb}-{data2,Xb}-{data3,Xb}-{(data4[0]),nvoXb}{(data4[1]),nvoXb}-{(data4[2]),nvoXb}{(data4[3]),nvoXb}{(data4[4]),nvoXb}{(data4[5]),nvoXb}{(data4[6]),nvoXb}{(data4[7]),nvoXb}}} + + + + {val} + + val + + + + + {_q_value} + + _q_value + + + + + + + empty + {_q_value} + + *value() + + + + + + {{ x = {xp}, y = {yp} }} + + xp + yp + + + + + {{ x = {x1}, y = {y1}, width = {x2 - x1 + 1}, height = {y2 - y1 + 1} }} + + x1 + y1 + x2 - x1 + 1 + y2 - y1 + 1 + + + + + {{ x = {xp}, y = {yp}, width = {w}, height = {h} }} + + xp + yp + w + h + + + + + + {{ width = {wd}, height = {ht} }} + + wd + ht + + + + + + {{ start point = {pt1}, end point = {pt2} }} + + + {pt1} + + pt1 + + + + {pt2} + + pt2 + + + + + + + + {{ size={d->size} }} + + d->ref.atomic._q_value + + d->size + (QPoint*)((reinterpret_cast<char*>(d)) + d->offset) + + + + + + {{ size={d->size} }} + + + d->size > 0 + && ((((QPointF*)((reinterpret_cast<char*>(d)) + d->offset)[0]).xp + == (((QPointF*)((reinterpret_cast<char*>(d)) + d->offset)[d->size - 1]).xp) + && ((((QPointF*)((reinterpret_cast<char*>(d)) + d->offset)[0]).yp + == (((QPointF*)((reinterpret_cast<char*>(d)) + d->offset)[d->size - 1]).yp) + + d->ref.atomic._q_value + + d->size + (QPointF*)((reinterpret_cast<char*>(d)) + d->offset) + + + + + + {{ x = {xp}, y = {yp} }} + + xp + yp + + + + + {{ x = {xp}, y = {yp}, z = {zp} }} + + xp + yp + zp + + + + + {{ x = {xp}, y = {yp}, z = {zp}, w = {wp} }} + + xp + yp + zp + wp + + + + + + {{ m11 = {_m11}, m12 = {_m12}, m21 = {_m21}, m22 = {_m22}, ... }} + + + _m11 + _m12 + _m21 + _m22 + _dx + _dy + + + + + + {{ m11 = {m[0][0]}, m12 = {m[1][0]}, m13 = {m[2][0]}, m14 = {m[3][0]}, ... }} + + + m[0][0] + m[1][0] + m[2][0] + m[3][0] + m[0][1] + m[1][1] + m[2][1] + m[3][1] + m[0][2] + m[1][2] + m[2][2] + m[3][2] + m[0][3] + m[1][3] + m[2][3] + m[3][3] + + + + + + {{ horizontal = {static_cast<Policy>(bits.horPolicy)}, vertical = {static_cast<Policy>(bits.verPolicy)}, type = {ControlType(1 << bits.ctype)} }} + + + + QSizePolicy::Policy::{static_cast<Policy>(bits.verPolicy)} + + + QSizePolicy::Policy::{static_cast<Policy>(bits.horPolicy)} + + + QSizePolicy::ControlType::{ControlType(1 << bits.ctype)} + + + + Qt::Vertical (2) + + + Qt::Horizontal (1) + + + static_cast<int>(bits.verStretch) + static_cast<int>(bits.horStretch) + bits.hfw == 1 + bits.wfh == 1 + + + + + {ucs,c} + ucs,c + + ucs > 0xff ? '\0' : char(ucs),c + ucs,c + + + + + "{(reinterpret_cast<unsigned short*>(d.ptr)),sub}" + (reinterpret_cast<unsigned short*>(d.ptr)),sub + + d.size + + d.size + d.ptr + + + + + + {m_string,[m_size]} u"" + {m_string->d.ptr+m_position,[m_size]} + "" + m_string,[m_position+m_size] + + m_position + m_size + + m_size + m_string->d.ptr+m_position + + + + + + {m_data,[m_size]} + m_data,[m_size] + + m_size + + m_size + m_data + + + + + + "{((reinterpret_cast<char*>(d.ptr))),sb}" + ((reinterpret_cast<char*>(d.ptr))),sb + + d.size + + d.size + d.ptr + + + + + + + + + + + + + + + + + + + + {scheme()}://{host()}{path()} + {path()} + + scheme() + username() + password() + host() + path() + query() + fragment() + + + + + {{ julian day = {jd} }} + + + + + + + + {{ millisecond = {mds} }} + {{ milliseconds = {mds} }} + + hour(), d + hour(), d + minute(), d + minute(), d + second(), d + second(), d + millisecond(), d + millisecond(), d + + + + + ({first}, {second}) + + first + second + + + + + + {{ size={d.size} }} + + + d.size + reinterpret_cast<$T1*>(d.ptr) + + + + + + {{ size={s} }} + + a + + s + ptr + + + + + + + {{ size={d.d->m._Mypair._Myval2._Myval2._Mysize} }} + + + d.d->m._Mypair._Myval2._Myval2._Mysize + d.d->m._Mypair._Myval2._Myval2._Myhead->_Parent + _Left + _Right + _Myval,view(MapHelper) + + + + + + {second} + + + + {value} + + key + value + + + + + {value->value} + ({value->value}, {value->next->value}) + ({value->value}, {value->next->value}, ...) + + + value + next + value + + + + + + + + + + + + + + + + + + + + {{ size={d->size} }} + + + + + d->size + + + getNode(iSpan, 0) + getNode(iSpan, 1) + getNode(iSpan, 2) + getNode(iSpan, 3) + getNode(iSpan, 4) + getNode(iSpan, 5) + getNode(iSpan, 6) + getNode(iSpan, 7) + getNode(iSpan, 8) + getNode(iSpan, 9) + getNode(iSpan, 10) + getNode(iSpan, 11) + getNode(iSpan, 12) + getNode(iSpan, 13) + getNode(iSpan, 14) + getNode(iSpan, 15) + getNode(iSpan, 16) + getNode(iSpan, 17) + getNode(iSpan, 18) + getNode(iSpan, 19) + getNode(iSpan, 20) + getNode(iSpan, 21) + getNode(iSpan, 22) + getNode(iSpan, 23) + getNode(iSpan, 24) + getNode(iSpan, 25) + getNode(iSpan, 26) + getNode(iSpan, 27) + getNode(iSpan, 28) + getNode(iSpan, 29) + getNode(iSpan, 30) + getNode(iSpan, 31) + getNode(iSpan, 32) + getNode(iSpan, 33) + getNode(iSpan, 34) + getNode(iSpan, 35) + getNode(iSpan, 36) + getNode(iSpan, 37) + getNode(iSpan, 38) + getNode(iSpan, 39) + getNode(iSpan, 40) + getNode(iSpan, 41) + getNode(iSpan, 42) + getNode(iSpan, 43) + getNode(iSpan, 44) + getNode(iSpan, 45) + getNode(iSpan, 46) + getNode(iSpan, 47) + getNode(iSpan, 48) + getNode(iSpan, 49) + getNode(iSpan, 50) + getNode(iSpan, 51) + getNode(iSpan, 52) + getNode(iSpan, 53) + getNode(iSpan, 54) + getNode(iSpan, 55) + getNode(iSpan, 56) + getNode(iSpan, 57) + getNode(iSpan, 58) + getNode(iSpan, 59) + getNode(iSpan, 60) + getNode(iSpan, 61) + getNode(iSpan, 62) + getNode(iSpan, 63) + getNode(iSpan, 64) + getNode(iSpan, 65) + getNode(iSpan, 66) + getNode(iSpan, 67) + getNode(iSpan, 68) + getNode(iSpan, 69) + getNode(iSpan, 70) + getNode(iSpan, 71) + getNode(iSpan, 72) + getNode(iSpan, 73) + getNode(iSpan, 74) + getNode(iSpan, 75) + getNode(iSpan, 76) + getNode(iSpan, 77) + getNode(iSpan, 78) + getNode(iSpan, 79) + getNode(iSpan, 80) + getNode(iSpan, 81) + getNode(iSpan, 82) + getNode(iSpan, 83) + getNode(iSpan, 84) + getNode(iSpan, 85) + getNode(iSpan, 86) + getNode(iSpan, 87) + getNode(iSpan, 88) + getNode(iSpan, 89) + getNode(iSpan, 90) + getNode(iSpan, 91) + getNode(iSpan, 92) + getNode(iSpan, 93) + getNode(iSpan, 94) + getNode(iSpan, 95) + getNode(iSpan, 96) + getNode(iSpan, 97) + getNode(iSpan, 98) + getNode(iSpan, 99) + getNode(iSpan, 100) + getNode(iSpan, 101) + getNode(iSpan, 102) + getNode(iSpan, 103) + getNode(iSpan, 104) + getNode(iSpan, 105) + getNode(iSpan, 106) + getNode(iSpan, 107) + getNode(iSpan, 108) + getNode(iSpan, 109) + getNode(iSpan, 110) + getNode(iSpan, 111) + getNode(iSpan, 112) + getNode(iSpan, 113) + getNode(iSpan, 114) + getNode(iSpan, 115) + getNode(iSpan, 116) + getNode(iSpan, 117) + getNode(iSpan, 118) + getNode(iSpan, 119) + getNode(iSpan, 120) + getNode(iSpan, 121) + getNode(iSpan, 122) + getNode(iSpan, 123) + getNode(iSpan, 124) + getNode(iSpan, 125) + getNode(iSpan, 126) + getNode(iSpan, 127) + iSpan++ + + + + + + + + {{ size={q_hash.d->size} }} + + q_hash + + + + + + + + + + + + (null) + + + QObject* + + + {*(QMap<QString,QVariant>*) sharedDataStar()} + {*(QList<QVariant>*) sharedDataStar()} + {*(QHash<QString,QVariant>*) sharedDataStar()} + QVariantPair + {*(QList<QByteArray>*) sharedDataStar()} + {*(QList<QString>*) sharedDataStar()} + + + {*(bool*) dataStar()} + {*(int*) dataStar()} + {*(unsigned int*) dataStar()} + {*(long long*) dataStar()} + {*(unsigned long long*) dataStar()} + {*(double*) dataStar()} + {*(void**) dataStar()} + {*(long*) dataStar()} + {*(short*) dataStar()} + {*(char*) dataStar()} + {*(char16_t*) dataStar()} + {*(char32_t*) dataStar()} + {*(unsigned long*) dataStar()} + {*(unsigned short*) dataStar()} + {*(unsigned char*) dataStar()} + {*(float*) dataStar()} + {*(signed char*) dataStar()} + + + {*(QChar*) sharedDataStar()} + {*(QString*) sharedDataStar()} + {*(QByteArray*) sharedDataStar()} + {*(QDate*) sharedDataStar()} + {*(QTime*) sharedDataStar()} + QDateTime + QUrl + QLocale + {*(QRect*) sharedDataStar()} + {*(QRectF*) sharedDataStar()} + {*(QSize*) sharedDataStar()} + {*(QSizeF*) sharedDataStar()} + {*(QLine*) sharedDataStar()} + {*(QLineF*) sharedDataStar()} + {*(QPoint*) sharedDataStar()} + {*(QPointF*) sharedDataStar()} + EasingCurve + Uuid + RegularExpression + QJsonValue + QJsonObject + QJsonArray + QJsonDocument + QCborValue + QCborArray + QCborMap + ModelIndex + QPersistentModelIndex + + + QFont + QPixmap + QBrush + QColor + QPalette + QIcon + QImage + QPolygon + QRegion + QBitmap + QCursor + QKeySequence + QPen + QTextLength + QTextFormat + QTransform + QMatrix4x4 + QVector2D + QVector3D + QVector4D + QQuaternion + QPolygonF + QColorSpace + + + QSizePolicy + + + QMetaType::Type ({typeId()}) + + + *(QString*) sharedDataStar() + *(QByteArray*) sharedDataStar() + *(QDate*) sharedDataStar() + *(QTime*) sharedDataStar() + *(QRect*) sharedDataStar() + *(QRectF*) sharedDataStar() + *(QSize*) sharedDataStar() + *(QSizeF*) sharedDataStar() + *(QLine*) sharedDataStar() + *(QLineF*) sharedDataStar() + *(QPoint*) sharedDataStar() + *(QPointF*) sharedDataStar() + + *(QMap<QString,QVariant>*) sharedDataStar() + *(QList<QVariant>*) sharedDataStar() + *(QHash<QString,QVariant>*) sharedDataStar() + *(QList<QByteArray>*) sharedDataStar() + *(QList<QString>*) sharedDataStar() + + + + diff --git a/3rdparty/ElaWidgetTools/src/CMakeLists.txt b/3rdparty/ElaWidgetTools/src/CMakeLists.txt new file mode 100644 index 0000000..f820282 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/CMakeLists.txt @@ -0,0 +1,134 @@ +cmake_minimum_required(VERSION 3.5) + +project(ELAWIDGETTOOLS VERSION 2.0.0 LANGUAGES CXX) + +set(EXPORT_NAME "ElaWidgetTools") +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +add_compile_options("$<$:/utf-8>") + +SET(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/install CACHE PATH "Installation path" FORCE) +add_definitions(-DELAWIDGETTOOLS_LIBRARY) +option(ELAWIDGETTOOLS_BUILD_STATIC_LIB "Build static library." OFF) + +find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets) + +FILE(GLOB ORIGIN *.h *.cpp) +FILE(GLOB INCLUDE include/*.h) +FILE(GLOB PRIVATE private/*.h private/*.cpp) +FILE(GLOB DEVELOPER DeveloperComponents/*.h DeveloperComponents/*.cpp) + +source_group(include FILES ${INCLUDE}) +source_group(private FILES ${PRIVATE}) +source_group(DeveloperComponents FILES ${DEVELOPER}) + +set(PROJECT_SOURCES + ${ORIGIN} + ${INCLUDE} + ${PRIVATE} + ${DEVELOPER} +) + +if (ELAWIDGETTOOLS_BUILD_STATIC_LIB) + set(LIB_TYPE "STATIC") +else () + set(LIB_TYPE "SHARED" + DeveloperComponents/ElaKeyBinderContainer.cpp + DeveloperComponents/ElaKeyBinderContainer.h) +endif () + +if (${QT_VERSION_MAJOR} GREATER_EQUAL 6) + qt_add_library(${EXPORT_NAME} ${LIB_TYPE} + ${PROJECT_SOURCES} + ) +else () + add_library(${EXPORT_NAME} ${LIB_TYPE} + ${PROJECT_SOURCES} + ) +endif () + +FILE(GLOB EXPORT_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/*.qrc +) +target_include_directories(${EXPORT_NAME} PUBLIC + $ + $ + $ + $ +) + +if (${QT_VERSION_MAJOR} GREATER_EQUAL 6) + #遍历所有资源文件 + file(GLOB_RECURSE RES_PATHS *.png *.jpg *.svg *.ico *.ttf *.webp *.js) + foreach (filepath ${RES_PATHS}) + string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" filename ${filepath}) + list(APPEND resource_files ${filename}) + endforeach (filepath) + qt_add_resources(${EXPORT_NAME} "ElaWidgetTools" + RESOURCES PREFIX "/" + FILES + ${resource_files} + ) +endif () + +if (MINGW) + set_target_properties(${EXPORT_NAME} PROPERTIES PREFIX "") +endif () +if (MSVC) + set_target_properties(${EXPORT_NAME} PROPERTIES DEBUG_POSTFIX "d") +endif () + +if (WIN32) + target_link_libraries(${EXPORT_NAME} PUBLIC + Qt${QT_VERSION_MAJOR}::Widgets + D3D11 + DXGI + ) +else () + target_link_libraries(${EXPORT_NAME} PUBLIC + Qt${QT_VERSION_MAJOR}::Widgets + ) +endif () + + +install( + TARGETS ${EXPORT_NAME} + EXPORT ${EXPORT_NAME} + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin +) + +install(FILES ${EXPORT_HEADERS} DESTINATION include) +install(TARGETS ${EXPORT_NAME} + RUNTIME DESTINATION ${CMAKE_BINARY_DIR} +) + +set(INCLUDE_DIRS include) +set(LIBRARIES ${EXPORT_NAME}) +set(LIB_DIR lib) + +include(CMakePackageConfigHelpers) +write_basic_package_version_file( + ${PROJECT_BINARY_DIR}/${EXPORT_NAME}ConfigVersion.cmake + VERSION 2.0.0 + COMPATIBILITY SameMajorVersion +) + +configure_package_config_file( + ${PROJECT_SOURCE_DIR}/${EXPORT_NAME}Config.cmake.in + ${PROJECT_BINARY_DIR}/${EXPORT_NAME}Config.cmake + INSTALL_DESTINATION lib/cmake + PATH_VARS INCLUDE_DIRS LIBRARIES LIB_DIR + INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX} +) + +install( + FILES ${PROJECT_BINARY_DIR}/${EXPORT_NAME}Config.cmake ${PROJECT_BINARY_DIR}/${EXPORT_NAME}ConfigVersion.cmake DESTINATION lib/cmake +) diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaBaseListView.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaBaseListView.cpp new file mode 100644 index 0000000..035eb2e --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaBaseListView.cpp @@ -0,0 +1,45 @@ +#include "ElaBaseListView.h" + +#include + +#include "ElaScrollBar.h" +ElaBaseListView::ElaBaseListView(QWidget* parent) + : QListView(parent) +{ + setObjectName("ElaBaseListView"); + setStyleSheet( + "ElaBaseListView{background-color: transparent;border:0px;}" + "ElaBaseListView::item{border:none;}"); + setAutoScroll(false); + setFocusPolicy(Qt::NoFocus); + setVerticalScrollBar(new ElaScrollBar(this)); + setHorizontalScrollBar(new ElaScrollBar(this)); + setSelectionMode(QAbstractItemView::NoSelection); + setMouseTracking(true); + this->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + this->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); + setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); +} + +ElaBaseListView::~ElaBaseListView() +{ +} + +void ElaBaseListView::mousePressEvent(QMouseEvent* event) +{ + Q_EMIT mousePress(indexAt(event->pos())); + QListView::mousePressEvent(event); +} + +void ElaBaseListView::mouseReleaseEvent(QMouseEvent* event) +{ + Q_EMIT mouseRelease(indexAt(event->pos())); + QListView::mouseReleaseEvent(event); +} + +void ElaBaseListView::mouseDoubleClickEvent(QMouseEvent* event) +{ + Q_EMIT mouseDoubleClick(indexAt(event->pos())); + QListView::mouseDoubleClickEvent(event); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaBaseListView.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaBaseListView.h new file mode 100644 index 0000000..839b917 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaBaseListView.h @@ -0,0 +1,25 @@ +#ifndef ELABASELISTVIEW_H +#define ELABASELISTVIEW_H + +#include +#include + +class ElaScrollBar; +class ElaBaseListView : public QListView +{ + Q_OBJECT +public: + explicit ElaBaseListView(QWidget* parent = nullptr); + ~ElaBaseListView(); +Q_SIGNALS: + Q_SIGNAL void mousePress(const QModelIndex& index); + Q_SIGNAL void mouseRelease(const QModelIndex& index); + Q_SIGNAL void mouseDoubleClick(const QModelIndex& index); + +protected: + virtual void mousePressEvent(QMouseEvent* event) override; + virtual void mouseReleaseEvent(QMouseEvent* event) override; + virtual void mouseDoubleClickEvent(QMouseEvent* event) override; +}; + +#endif // ELABASELISTVIEW_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaBreadcrumbBarDelegate.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaBreadcrumbBarDelegate.cpp new file mode 100644 index 0000000..c4f35db --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaBreadcrumbBarDelegate.cpp @@ -0,0 +1,57 @@ +#include "ElaBreadcrumbBarDelegate.h" + +#include + +#include "ElaTheme.h" +ElaBreadcrumbBarDelegate::ElaBreadcrumbBarDelegate(QObject* parent) + : QStyledItemDelegate{parent} +{ + _themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { + _themeMode = themeMode; + }); +} + +ElaBreadcrumbBarDelegate::~ElaBreadcrumbBarDelegate() +{ +} + +void ElaBreadcrumbBarDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + painter->setPen(ElaThemeColor(_themeMode, BasicText)); + QRect itemRect = option.rect; + QString breadcrumbDisplayData = index.data(Qt::DisplayRole).toString(); + QString breadcrumbUserData = index.data(Qt::UserRole).toString(); + if (breadcrumbUserData != "LastBreadcrumb") + { + if (_pPressIndex == index) + { + //鼠标按下 + painter->setPen(ElaThemeColor(_themeMode, BasicTextPress)); + } + else + { + //不为最后一个 且没有被鼠标覆盖 + if (!(option.state & QStyle::State_MouseOver)) + { + painter->setPen(ElaThemeColor(_themeMode, BasicTextNoFocus)); + } + } + } + if (breadcrumbDisplayData != ">") + { + painter->drawText(itemRect, Qt::AlignVCenter | Qt::AlignLeft, breadcrumbDisplayData); + } + else + { + //分隔符 + QFont iconFont = QFont("ElaAwesome"); + iconFont.setPixelSize(painter->font().pixelSize() * 0.785); + painter->setFont(iconFont); + itemRect.setX(itemRect.x() - itemRect.width() * 0.36); + painter->drawText(itemRect, Qt::AlignCenter, QChar((unsigned short)ElaIconType::AngleRight)); + } + painter->restore(); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaBreadcrumbBarDelegate.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaBreadcrumbBarDelegate.h new file mode 100644 index 0000000..c077e07 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaBreadcrumbBarDelegate.h @@ -0,0 +1,23 @@ +#ifndef ELABREADCRUMBBARDELEGATE_H +#define ELABREADCRUMBBARDELEGATE_H + +#include +#include + +#include "Def.h" +class ElaBreadcrumbBarDelegate : public QStyledItemDelegate +{ + Q_OBJECT + Q_PRIVATE_CREATE(QModelIndex, PressIndex) +public: + explicit ElaBreadcrumbBarDelegate(QObject* parent = nullptr); + ~ElaBreadcrumbBarDelegate(); + +protected: + void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; + +private: + ElaThemeType::ThemeMode _themeMode; +}; + +#endif // ELABREADCRUMBBARDELEGATE_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaBreadcrumbBarModel.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaBreadcrumbBarModel.cpp new file mode 100644 index 0000000..8a4a69a --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaBreadcrumbBarModel.cpp @@ -0,0 +1,108 @@ +#include "ElaBreadcrumbBarModel.h" + +ElaBreadcrumbBarModel::ElaBreadcrumbBarModel(QObject* parent) + : QAbstractListModel{parent} +{ +} + +ElaBreadcrumbBarModel::~ElaBreadcrumbBarModel() +{ +} + +int ElaBreadcrumbBarModel::rowCount(const QModelIndex& parent) const +{ + return _breadcrumbList.count() * 2 - 1; +} + +QVariant ElaBreadcrumbBarModel::data(const QModelIndex& index, int role) const +{ + if (role == Qt::DisplayRole) + { + if (index.row() % 2 == 0) + { + return _breadcrumbList[index.row() / 2]; + } + else + { + return ">"; + } + } + else if (role == Qt::UserRole) + { + if (index.row() == _breadcrumbList.count() * 2 - 2 || index.row() == _breadcrumbList.count() * 2 - 3) + { + return QString("LastBreadcrumb"); + } + } + return QVariant(); +} + +void ElaBreadcrumbBarModel::appendBreadcrumb(QString breadcrumb) +{ + if (!breadcrumb.isEmpty()) + { + if (_breadcrumbList.count() && _breadcrumbList.last() == breadcrumb) + { + return; + } + if (_breadcrumbList.count() >= 2 && _breadcrumbList[_breadcrumbList.count() - 2] == breadcrumb) + { + beginResetModel(); + _breadcrumbList.removeLast(); + endResetModel(); + return; + } + beginResetModel(); + _breadcrumbList.append(breadcrumb); + endResetModel(); + } +} + +void ElaBreadcrumbBarModel::removeBreadcrumb(QString breadcrumb) +{ + if (_breadcrumbList.contains(breadcrumb)) + { + beginResetModel(); + _breadcrumbList.removeAt(_breadcrumbList.lastIndexOf(breadcrumb)); + endResetModel(); + } +} + +void ElaBreadcrumbBarModel::removeBreadcrumb(int index) +{ + if (index >= _breadcrumbList.count()) + { + return; + } + beginResetModel(); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + _breadcrumbList.remove(index, _breadcrumbList.count() - index); +#else + for (int i = _breadcrumbList.count() - 1; i >= 0; i--) + { + if (i < index) + { + break; + } + _breadcrumbList.removeAt(i); + } +#endif + endResetModel(); +} + +void ElaBreadcrumbBarModel::setBreadcrumbList(QStringList breadcrumbList) +{ + beginResetModel(); + this->_breadcrumbList = breadcrumbList; + endResetModel(); +} + +int ElaBreadcrumbBarModel::getBreadcrumbListCount() +{ + return _breadcrumbList.count(); +} + +QStringList ElaBreadcrumbBarModel::getBreadcrumbList() +{ + return _breadcrumbList; +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaBreadcrumbBarModel.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaBreadcrumbBarModel.h new file mode 100644 index 0000000..1a35d5e --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaBreadcrumbBarModel.h @@ -0,0 +1,30 @@ +#ifndef ELABREADCRUMBBARMODEL_H +#define ELABREADCRUMBBARMODEL_H + +#include + +class ElaBreadcrumbBarModel : public QAbstractListModel +{ + Q_OBJECT +public: + explicit ElaBreadcrumbBarModel(QObject* parent = nullptr); + ~ElaBreadcrumbBarModel(); + + void appendBreadcrumb(QString breadcrumb); + void removeBreadcrumb(QString breadcrumb); + void removeBreadcrumb(int index); + + void setBreadcrumbList(QStringList breadcrumbList); + + int getBreadcrumbListCount(); + QStringList getBreadcrumbList(); + +protected: + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + QVariant data(const QModelIndex& index, int role) const override; + +private: + QStringList _breadcrumbList; +}; + +#endif // ELABREADCRUMBBARMODEL_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCalendarDelegate.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCalendarDelegate.cpp new file mode 100644 index 0000000..ffa8b4c --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCalendarDelegate.cpp @@ -0,0 +1,187 @@ +#include "ElaCalendarDelegate.h" + +#include +#include +#include + +#include "ElaTheme.h" +ElaCalendarDelegate::ElaCalendarDelegate(ElaCalendarModel* calendarModel, QObject* parent) + : QStyledItemDelegate{parent} +{ + _pIsTransparent = false; + _calendarModel = calendarModel; + _pItemWidth = 42; + _pItemHeight = 42; + _nowDate = QDate::currentDate(); + _themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { + _themeMode = themeMode; + }); + connect(_calendarModel, &ElaCalendarModel::displayModeChanged, this, &ElaCalendarDelegate::onCalendarModelDisplayModeChanged); +} + +ElaCalendarDelegate::~ElaCalendarDelegate() +{ +} + +void ElaCalendarDelegate::onCalendarModelDisplayModeChanged() +{ + switch (_calendarModel->getDisplayMode()) + { + case YearMode: + case MonthMode: + { + _pItemWidth = 53; + _pItemHeight = 60; + break; + } + case DayMode: + { + _pItemWidth = 42; + _pItemHeight = 42; + break; + } + } +} + +void ElaCalendarDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + if (_pIsTransparent) + { + return; + } + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + ElaCalendarType displayModel = _calendarModel->getDisplayMode(); + switch (displayModel) + { + case YearMode: + case MonthMode: + { + _drawYearOrMonth(painter, option, index); + break; + } + case DayMode: + { + _drawDays(painter, option, index); + break; + } + } + painter->restore(); + QStyledItemDelegate::paint(painter, option, index); +} + +QSize ElaCalendarDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + return QSize(_pItemWidth, _pItemHeight); +} + +void ElaCalendarDelegate::_drawYearOrMonth(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + QRectF itemRect = option.rect; + bool isNow = false; + ElaCalendarData data = index.data(Qt::UserRole).value(); + qreal baseRadius = _pItemWidth * 0.5; + // 当前日期绘制 + ElaCalendarType displayMode = _calendarModel->getDisplayMode(); + if ((displayMode == ElaCalendarType::YearMode && data.year == _nowDate.year()) || (displayMode == ElaCalendarType::MonthMode && data.month == _nowDate.month() && data.year == _nowDate.year())) + { + isNow = true; + QColor drawColor = option.state.testFlag(QStyle::State_MouseOver) ? ElaThemeColor(_themeMode, PrimaryHover) : ElaThemeColor(_themeMode, PrimaryNormal); + painter->setPen(Qt::NoPen); + painter->setBrush(drawColor); + painter->drawEllipse(itemRect.center(), baseRadius, baseRadius); + } + else + { + // 覆盖和选中效果绘制 + if (option.state.testFlag(QStyle::State_MouseOver)) + { + painter->setPen(Qt::NoPen); + painter->setBrush(ElaThemeColor(_themeMode, BasicHover)); + painter->drawEllipse(itemRect.center(), baseRadius, baseRadius); + } + } + + // 文字绘制 + painter->setPen(isNow ? ElaThemeColor(_themeMode, BasicTextInvert) : ElaThemeColor(_themeMode, BasicText)); + painter->drawText(itemRect, Qt::AlignCenter, displayMode == ElaCalendarType::YearMode ? QString::number(data.year) : QString::number(data.month)); + QString desText = data.desText; + if (!desText.isEmpty()) + { + itemRect.adjust(0, 10, 0, 0); + QFont font = painter->font(); + font.setPixelSize(9); + painter->setFont(font); + painter->drawText(itemRect, Qt::AlignTop | Qt::AlignHCenter, desText); + } +} + +void ElaCalendarDelegate::_drawDays(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + qreal penWidth = 1.5; + qreal baseRadius = _pItemWidth * 0.5 - penWidth; + QVariant variant = index.data(Qt::UserRole); + if (variant.isValid()) + { + QRectF itemRect = option.rect; + bool isNow = false; + ElaCalendarData data = variant.value(); + // 当前日期绘制 + if (data.year == _nowDate.year() && data.month == _nowDate.month() && data.day == _nowDate.day()) + { + isNow = true; + QColor drawColor = option.state.testFlag(QStyle::State_MouseOver) ? ElaThemeColor(_themeMode, PrimaryHover) : ElaThemeColor(_themeMode, PrimaryNormal); + if (option.state.testFlag(QStyle::State_Selected)) + { + painter->setPen(QPen(drawColor, penWidth)); + painter->drawEllipse(itemRect.center(), baseRadius, baseRadius); + painter->setBrush(drawColor); + painter->setPen(Qt::NoPen); + painter->drawEllipse(itemRect.center(), baseRadius - 2 * penWidth, baseRadius - 2 * penWidth); + } + else + { + painter->setPen(Qt::NoPen); + painter->setBrush(drawColor); + painter->drawEllipse(itemRect.center(), baseRadius, baseRadius); + } + } + else + { + // 覆盖和选中效果绘制 + if (option.state.testFlag(QStyle::State_Selected)) + { + painter->setPen(QPen(option.state.testFlag(QStyle::State_MouseOver) ? ElaThemeColor(_themeMode, PrimaryHover) : ElaThemeColor(_themeMode, PrimaryNormal), penWidth)); + painter->setBrush(Qt::NoBrush); + painter->drawEllipse(itemRect.center(), baseRadius, baseRadius); + } + if (option.state.testFlag(QStyle::State_MouseOver)) + { + painter->setPen(Qt::NoPen); + painter->setBrush(ElaThemeColor(_themeMode, BasicHover)); + if (option.state.testFlag(QStyle::State_Selected)) + { + painter->drawEllipse(itemRect.center(), baseRadius - penWidth, baseRadius - penWidth); + } + else + { + painter->drawEllipse(itemRect.center(), baseRadius, baseRadius); + } + } + } + + // 文字绘制 + painter->setPen(isNow ? ElaThemeColor(_themeMode, BasicTextInvert) : ElaThemeColor(_themeMode, BasicText)); + painter->drawText(itemRect, Qt::AlignCenter, QString::number(data.day)); + QString desText = data.desText; + if (!desText.isEmpty()) + { + itemRect.adjust(0, 2 * penWidth, 0, 0); + QFont font = painter->font(); + font.setPixelSize(9); + painter->setFont(font); + painter->drawText(itemRect, Qt::AlignTop | Qt::AlignHCenter, desText); + } + } +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCalendarDelegate.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCalendarDelegate.h new file mode 100644 index 0000000..be5e20e --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCalendarDelegate.h @@ -0,0 +1,32 @@ +#ifndef ELACALENDARDELEGATE_H +#define ELACALENDARDELEGATE_H + +#include +#include + +#include "Def.h" +#include "ElaCalendarModel.h" +class ElaCalendarDelegate : public QStyledItemDelegate +{ + Q_OBJECT + Q_PRIVATE_CREATE(int, ItemWidth) + Q_PRIVATE_CREATE(int, ItemHeight) + Q_PRIVATE_CREATE(bool, IsTransparent) +public: + explicit ElaCalendarDelegate(ElaCalendarModel* calendarModel, QObject* parent = nullptr); + ~ElaCalendarDelegate(); + + Q_SLOT void onCalendarModelDisplayModeChanged(); + + void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; + QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override; + +private: + ElaCalendarModel* _calendarModel{nullptr}; + ElaThemeType::ThemeMode _themeMode; + QDate _nowDate; + void _drawYearOrMonth(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; + void _drawDays(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; +}; + +#endif // ELACALENDARDELEGATE_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCalendarModel.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCalendarModel.cpp new file mode 100644 index 0000000..161f158 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCalendarModel.cpp @@ -0,0 +1,152 @@ +#include "ElaCalendarModel.h" + +ElaCalendarModel::ElaCalendarModel(QObject* parent) + : QAbstractListModel{parent} +{ + _pMinimumDate.setDate(1924, 1, 1); + _pMaximumDate.setDate(2124, 12, 31); + _initRowCount(); +} + +ElaCalendarModel::~ElaCalendarModel() +{ +} + +void ElaCalendarModel::setMinimumDate(QDate minimudate) +{ + _pMinimumDate = minimudate; + _initRowCount(); +} + +QDate ElaCalendarModel::getMinimumDate() const +{ + return _pMinimumDate; +} + +void ElaCalendarModel::setMaximumDate(QDate maximumDate) +{ + _pMaximumDate = maximumDate; + _initRowCount(); +} + +QDate ElaCalendarModel::getMaximumDate() const +{ + return _pMaximumDate; +} + +void ElaCalendarModel::setDisplayMode(ElaCalendarType displayMode) +{ + beginResetModel(); + _displayMode = displayMode; + endResetModel(); + Q_EMIT displayModeChanged(); +} + +ElaCalendarType ElaCalendarModel::getDisplayMode() const +{ + return _displayMode; +} + +QModelIndex ElaCalendarModel::getIndexFromDate(QDate date) +{ + switch (_displayMode) + { + case YearMode: + { + return this->index(date.year() - _pMinimumDate.year()); + } + case MonthMode: + { + return this->index((date.year() - _pMinimumDate.year()) * 12 + date.month() - 1); + } + case DayMode: + { + return this->index(_pMinimumDate.daysTo(date) + _offset); + } + } + return QModelIndex(); +} + +QDate ElaCalendarModel::getDateFromIndex(const QModelIndex& index) const +{ + if (!index.isValid() || index.row() < _offset) + { + return QDate(); + } + return _pMinimumDate.addDays(index.row() - _offset); +} + +QVariant ElaCalendarModel::data(const QModelIndex& index, int role) const +{ + if (role == Qt::UserRole) + { + switch (_displayMode) + { + case YearMode: + { + return QVariant::fromValue(ElaCalendarData(_pMinimumDate.year() + index.row(), 1, 1)); + } + case MonthMode: + { + int year = _pMinimumDate.year() + index.row() / 12; + int month = index.row() % 12 + 1; + if (month == 1) + { + return QVariant::fromValue(ElaCalendarData(year, month, 1, QString::number(year))); + } + else + { + return QVariant::fromValue(ElaCalendarData(_pMinimumDate.year() + index.row() / 12, month, 1)); + } + } + case DayMode: + { + if (index.row() >= _offset) + { + QDate date = getDateFromIndex(index); + if (date.day() == 1) + { + return QVariant::fromValue(ElaCalendarData(date.year(), date.month(), date.day(), QString("%1月").arg(date.month()))); + } + else + { + return QVariant::fromValue(ElaCalendarData(date.year(), date.month(), date.day())); + } + } + } + } + } + return QVariant(); +} + +int ElaCalendarModel::rowCount(const QModelIndex& parent) const +{ + switch (_displayMode) + { + case YearMode: + { + return _pMaximumDate.year() - _pMinimumDate.year() + 1; + } + case MonthMode: + { + return (_pMaximumDate.year() - _pMinimumDate.year() + 1) * 12; + } + case DayMode: + { + return _dayRowCount; + } + } + return 0; +} + +void ElaCalendarModel::_initRowCount() +{ + _dayRowCount = _pMinimumDate.daysTo(_pMaximumDate); + _offset = _pMinimumDate.dayOfWeek(); + _dayRowCount += _offset + 1; +} + +int ElaCalendarModel::_getCurrentDay(int row) const +{ + return _pMinimumDate.addDays(row - _offset).day(); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCalendarModel.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCalendarModel.h new file mode 100644 index 0000000..a479a02 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCalendarModel.h @@ -0,0 +1,70 @@ +#ifndef ELACALENDARMODEL_H +#define ELACALENDARMODEL_H + +#include +#include +#include + +#include "stdafx.h" +enum ElaCalendarType +{ + YearMode = 0x0001, + MonthMode = 0x0002, + DayMode = 0x0003, +}; + +struct ElaCalendarData : public QObjectData +{ +public: + ElaCalendarData(){}; + ~ElaCalendarData(){}; + ElaCalendarData(int year, int month, int day, QString desText = "") + : year(year), month(month), day(day), desText(desText){}; + ElaCalendarData(const ElaCalendarData& other) + { + year = other.year; + month = other.month; + day = other.day; + desText = other.desText; + } + int year = 1924; + int month = 1; + int day = 1; + QString desText{""}; +}; +Q_DECLARE_METATYPE(ElaCalendarData); + +class ElaCalendarModel : public QAbstractListModel +{ + Q_OBJECT + Q_PRIVATE_CREATE_Q_H(QDate, MinimumDate) + Q_PRIVATE_CREATE_Q_H(QDate, MaximumDate) +public: + explicit ElaCalendarModel(QObject* parent = nullptr); + ~ElaCalendarModel(); + + void setDisplayMode(ElaCalendarType displayType); + ElaCalendarType getDisplayMode() const; + + QModelIndex getIndexFromDate(QDate date); + QDate getDateFromIndex(const QModelIndex& index) const; + virtual QVariant data(const QModelIndex& index, int role) const override; + +Q_SIGNALS: + Q_SIGNAL void currentYearMonthChanged(QString date); + Q_SIGNAL void displayModeChanged(); + +protected: + virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; + +private: + int _offset{0}; + QDate _pMinimumDate; + QDate _pMaximumDate; + ElaCalendarType _displayMode{ElaCalendarType::DayMode}; + int _dayRowCount{0}; + void _initRowCount(); + int _getCurrentDay(int row) const; +}; + +#endif // ELACALENDARMODEL_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCalendarPickerContainer.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCalendarPickerContainer.cpp new file mode 100644 index 0000000..d6ee19c --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCalendarPickerContainer.cpp @@ -0,0 +1,29 @@ +#include "ElaCalendarPickerContainer.h" + +#include + +#include "ElaTheme.h" +ElaCalendarPickerContainer::ElaCalendarPickerContainer(QWidget* parent) + : QWidget{parent} +{ + setWindowFlags(Qt::FramelessWindowHint | Qt::Popup | Qt::NoDropShadowWindowHint); + setAttribute(Qt::WA_TranslucentBackground); + setContentsMargins(0, 0, 0, 0); + setObjectName("ElaCalendarPickerContainer"); + setStyleSheet("#ElaCalendarPickerContainer{background-color:transparent}"); + _themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { _themeMode = themeMode; }); +} + +ElaCalendarPickerContainer::~ElaCalendarPickerContainer() +{ +} + +void ElaCalendarPickerContainer::paintEvent(QPaintEvent* event) +{ + QPainter painter(this); + painter.save(); + painter.setRenderHints(QPainter::Antialiasing); + eTheme->drawEffectShadow(&painter, rect(), 6, 5); + painter.restore(); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCalendarPickerContainer.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCalendarPickerContainer.h new file mode 100644 index 0000000..3d52a11 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCalendarPickerContainer.h @@ -0,0 +1,21 @@ +#ifndef ELACALENDARPICKERCONTAINER_H +#define ELACALENDARPICKERCONTAINER_H + +#include + +#include "Def.h" +class ElaCalendarPickerContainer : public QWidget +{ + Q_OBJECT +public: + explicit ElaCalendarPickerContainer(QWidget* parent = nullptr); + ~ElaCalendarPickerContainer(); + +protected: + virtual void paintEvent(QPaintEvent* event) override; + +private: + ElaThemeType::ThemeMode _themeMode; +}; + +#endif // ELACALENDARPICKERCONTAINER_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCalendarTitleDelegate.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCalendarTitleDelegate.cpp new file mode 100644 index 0000000..f13bae1 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCalendarTitleDelegate.cpp @@ -0,0 +1,43 @@ +#include "ElaCalendarTitleDelegate.h" + +#include +#include +#include + +#include "ElaTheme.h" +ElaCalendarTitleDelegate::ElaCalendarTitleDelegate(QObject* parent) + : QStyledItemDelegate{parent} +{ + _themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { + _themeMode = themeMode; + }); +} + +ElaCalendarTitleDelegate::~ElaCalendarTitleDelegate() +{ +} + +void ElaCalendarTitleDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + QRectF itemRect = option.rect; + // 文字绘制 + QString title = index.data(Qt::UserRole).toString(); + if (!title.isEmpty()) + { + painter->setPen(ElaThemeColor(_themeMode, BasicText)); + QFont font = painter->font(); + font.setWeight(QFont::Bold); + painter->setFont(font); + painter->drawText(itemRect, Qt::AlignCenter, title); + } + painter->restore(); + QStyledItemDelegate::paint(painter, option, index); +} + +QSize ElaCalendarTitleDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + return QSize(42, 30); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCalendarTitleDelegate.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCalendarTitleDelegate.h new file mode 100644 index 0000000..f89e53a --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCalendarTitleDelegate.h @@ -0,0 +1,21 @@ +#ifndef ELACALENDARTITLEDELEGATE_H +#define ELACALENDARTITLEDELEGATE_H + +#include + +#include "Def.h" +class ElaCalendarTitleDelegate : public QStyledItemDelegate +{ + Q_OBJECT +public: + explicit ElaCalendarTitleDelegate(QObject* parent = nullptr); + ~ElaCalendarTitleDelegate(); + + void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; + QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override; + +private: + ElaThemeType::ThemeMode _themeMode; +}; + +#endif // ELACALENDARTITLEDELEGATE_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCalendarTitleModel.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCalendarTitleModel.cpp new file mode 100644 index 0000000..fd0fb5a --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCalendarTitleModel.cpp @@ -0,0 +1,54 @@ +#include "ElaCalendarTitleModel.h" + +ElaCalendarTitleModel::ElaCalendarTitleModel(QObject* parent) + : QAbstractListModel{parent} +{ +} + +ElaCalendarTitleModel::~ElaCalendarTitleModel() +{ +} + +int ElaCalendarTitleModel::rowCount(const QModelIndex& parent) const +{ + return 7; +} + +QVariant ElaCalendarTitleModel::data(const QModelIndex& index, int role) const +{ + if (role == Qt::UserRole) + { + switch (index.row()) + { + case 0: + { + return tr("日"); + } + case 1: + { + return tr("一"); + } + case 2: + { + return tr("二"); + } + case 3: + { + return tr("三"); + } + case 4: + { + return tr("四"); + } + case 5: + { + return tr("五"); + } + case 6: + { + return tr("六"); + } + } + } + return QVariant(); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCalendarTitleModel.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCalendarTitleModel.h new file mode 100644 index 0000000..778f2af --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCalendarTitleModel.h @@ -0,0 +1,18 @@ +#ifndef ELACALENDARTITLEMODEL_H +#define ELACALENDARTITLEMODEL_H + +#include + +class ElaCalendarTitleModel : public QAbstractListModel +{ + Q_OBJECT +public: + explicit ElaCalendarTitleModel(QObject* parent = nullptr); + ~ElaCalendarTitleModel(); + +protected: + virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; + virtual QVariant data(const QModelIndex& index, int role) const override; +}; + +#endif // ELACALENDARTITLEMODEL_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCentralStackedWidget.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCentralStackedWidget.cpp new file mode 100644 index 0000000..a86b951 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCentralStackedWidget.cpp @@ -0,0 +1,63 @@ +#include "ElaCentralStackedWidget.h" + +#include +#include + +#include "ElaTheme.h" +ElaCentralStackedWidget::ElaCentralStackedWidget(QWidget* parent) + : QStackedWidget(parent) +{ + setObjectName("ElaCentralStackedWidget"); + _themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, &ElaCentralStackedWidget::onThemeModeChanged); +} + +ElaCentralStackedWidget::~ElaCentralStackedWidget() +{ +} + +void ElaCentralStackedWidget::onThemeModeChanged(ElaThemeType::ThemeMode themeMode) +{ + _themeMode = themeMode; +} + +void ElaCentralStackedWidget::setIsTransparent(bool isTransparent) +{ + this->_isTransparent = isTransparent; + update(); +} + +bool ElaCentralStackedWidget::getIsTransparent() const +{ + return _isTransparent; +} + +void ElaCentralStackedWidget::setIsHasRadius(bool isHasRadius) +{ + this->_isHasRadius = isHasRadius; + update(); +} + +void ElaCentralStackedWidget::paintEvent(QPaintEvent* event) +{ + if (!_isTransparent) + { + QPainter painter(this); + QRect targetRect = this->rect(); + targetRect.adjust(1, 1, 10, 10); + painter.save(); + painter.setRenderHint(QPainter::Antialiasing); + painter.setPen(QPen(ElaThemeColor(_themeMode, BasicBaseLine), 1.5)); + painter.setBrush(ElaThemeColor(_themeMode, WindowCentralStackBase)); + if (_isHasRadius) + { + painter.drawRoundedRect(targetRect, 10, 10); + } + else + { + painter.drawRect(targetRect); + } + painter.restore(); + } + QStackedWidget::paintEvent(event); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCentralStackedWidget.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCentralStackedWidget.h new file mode 100644 index 0000000..9b89af8 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCentralStackedWidget.h @@ -0,0 +1,29 @@ +#ifndef ELACENTRALSTACKEDWIDGET_H +#define ELACENTRALSTACKEDWIDGET_H + +#include + +#include "Def.h" +class ElaCentralStackedWidget : public QStackedWidget +{ + Q_OBJECT +public: + explicit ElaCentralStackedWidget(QWidget* parent = nullptr); + ~ElaCentralStackedWidget(); + Q_SLOT void onThemeModeChanged(ElaThemeType::ThemeMode themeMode); + + void setIsTransparent(bool isTransparent); + bool getIsTransparent() const; + + void setIsHasRadius(bool isHasRadius); + +protected: + virtual void paintEvent(QPaintEvent* event) override; + +private: + ElaThemeType::ThemeMode _themeMode; + bool _isTransparent{false}; + bool _isHasRadius{true}; +}; + +#endif // ELACENTRALSTACKEDWIDGET_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCheckBoxStyle.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCheckBoxStyle.cpp new file mode 100644 index 0000000..4076c19 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCheckBoxStyle.cpp @@ -0,0 +1,131 @@ +#include "ElaCheckBoxStyle.h" + +#include +#include +#include + +#include "ElaTheme.h" +ElaCheckBoxStyle::ElaCheckBoxStyle(QStyle* style) +{ + _pCheckIndicatorWidth = 21; + _themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { _themeMode = themeMode; }); +} + +ElaCheckBoxStyle::~ElaCheckBoxStyle() +{ +} + +void ElaCheckBoxStyle::drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const +{ + // qDebug() << element << option->rect; + switch (element) + { + case QStyle::CE_CheckBox: + { + if (const QStyleOptionButton* bopt = qstyleoption_cast(option)) + { + bool isEnabled = bopt->state.testFlag(QStyle::State_Enabled); + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + QRect checkBoxRect = bopt->rect; + QRect checkRect(checkBoxRect.x(), checkBoxRect.y(), _pCheckIndicatorWidth, _pCheckIndicatorWidth); + checkRect.adjust(1, 1, -1, -1); + //复选框绘制 + painter->setPen(Qt::NoPen); + if (bopt->state.testFlag(QStyle::State_On) || bopt->state.testFlag(QStyle::State_NoChange)) + { + painter->setPen(Qt::NoPen); + if (bopt->state.testFlag(QStyle::State_Sunken)) + { + painter->setBrush(ElaThemeColor(_themeMode, PrimaryPress)); + } + else + { + if (bopt->state.testFlag(QStyle::State_MouseOver)) + { + painter->setBrush(ElaThemeColor(_themeMode, PrimaryHover)); + } + else + { + painter->setBrush(ElaThemeColor(_themeMode, PrimaryNormal)); + } + } + } + else + { + if (bopt->state.testFlag(QStyle::State_Sunken)) + { + painter->setPen(ElaThemeColor(_themeMode, BasicBorderDeep)); + } + else + { + painter->setPen(ElaThemeColor(_themeMode, BasicBorderDeep)); + if (bopt->state.testFlag(QStyle::State_MouseOver)) + { + painter->setBrush(ElaThemeColor(_themeMode, BasicHover)); + } + else + { + painter->setBrush(ElaThemeColor(_themeMode, BasicBase)); + } + } + } + painter->drawRoundedRect(checkRect, 2, 2); + //图标绘制 + painter->setPen(ElaThemeColor(ElaThemeType::Dark, BasicText)); + if (bopt->state.testFlag(QStyle::State_On)) + { + painter->save(); + QFont iconFont = QFont("ElaAwesome"); + iconFont.setPixelSize(_pCheckIndicatorWidth * 0.75); + painter->setFont(iconFont); + painter->drawText(checkRect, Qt::AlignCenter, QChar((unsigned short)ElaIconType::Check)); + painter->restore(); + } + else if (bopt->state.testFlag(QStyle::State_NoChange)) + { + QLine checkLine(checkRect.x() + 3, checkRect.center().y(), checkRect.right() - 3, checkRect.center().y()); + painter->drawLine(checkLine); + } + //文字绘制 + painter->setPen(isEnabled ? ElaThemeColor(_themeMode, BasicText) : ElaThemeColor(_themeMode, BasicTextDisable)); + QRect textRect(checkRect.right() + 10, checkBoxRect.y(), checkBoxRect.width(), checkBoxRect.height()); + painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, bopt->text); + painter->restore(); + } + return; + } + default: + { + break; + } + } + + QProxyStyle::drawControl(element, option, painter, widget); +} + +int ElaCheckBoxStyle::pixelMetric(PixelMetric metric, const QStyleOption* option, const QWidget* widget) const +{ + // qDebug() << metric << QProxyStyle::pixelMetric(metric, option, widget); + switch (metric) + { + case QStyle::PM_IndicatorWidth: + { + return _pCheckIndicatorWidth; + } + case QStyle::PM_IndicatorHeight: + { + return _pCheckIndicatorWidth; + } + case QStyle::PM_CheckBoxLabelSpacing: + { + return 10; + } + default: + { + break; + } + } + return QProxyStyle::pixelMetric(metric, option, widget); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCheckBoxStyle.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCheckBoxStyle.h new file mode 100644 index 0000000..9a82043 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCheckBoxStyle.h @@ -0,0 +1,21 @@ +#ifndef ELACHECKBOXSTYLE_H +#define ELACHECKBOXSTYLE_H + +#include + +#include "Def.h" +class ElaCheckBoxStyle : public QProxyStyle +{ + Q_OBJECT + Q_PRIVATE_CREATE(int, CheckIndicatorWidth) +public: + explicit ElaCheckBoxStyle(QStyle* style = nullptr); + ~ElaCheckBoxStyle(); + void drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget = nullptr) const override; + int pixelMetric(PixelMetric metric, const QStyleOption* option = nullptr, const QWidget* widget = nullptr) const override; + +private: + ElaThemeType::ThemeMode _themeMode; +}; + +#endif // ELACHECKBOXSTYLE_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaColorDisplayDelegate.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaColorDisplayDelegate.cpp new file mode 100644 index 0000000..6c30c7b --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaColorDisplayDelegate.cpp @@ -0,0 +1,53 @@ +#include "ElaColorDisplayDelegate.h" + +#include +#include +#include +#include + +#include "ElaTheme.h" +ElaColorDisplayDelegate::ElaColorDisplayDelegate(QObject* parent) + : QStyledItemDelegate{parent} +{ + _pThemeMode = eTheme->getThemeMode(); +} + +ElaColorDisplayDelegate::~ElaColorDisplayDelegate() +{ +} + +void ElaColorDisplayDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + painter->save(); + painter->setRenderHints(QPainter::Antialiasing); + QRectF itemRect = option.rect; + // 颜色球绘制 + QColor displayColor = index.data(Qt::UserRole).value(); + if (displayColor.isValid()) + { + painter->setPen(ElaThemeColor(_pThemeMode, PopupBorderHover)); + painter->setBrush(index.data(Qt::UserRole).value()); + } + else + { + QPen pen(ElaThemeColor(_pThemeMode, PopupBorderHover)); + pen.setStyle(Qt::DashLine); + painter->setPen(pen); + } + painter->drawEllipse(itemRect.center(), 10, 10); + + // 覆盖效果绘制 + if (option.state.testFlag(QStyle::State_MouseOver) || option.state.testFlag(QStyle::State_Selected)) + { + painter->setBrush(Qt::NoBrush); + painter->drawEllipse(itemRect.center(), 13, 13); + } + + painter->restore(); + QStyledItemDelegate::paint(painter, option, index); +} + +QSize ElaColorDisplayDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + return QSize(30, 30); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaColorDisplayDelegate.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaColorDisplayDelegate.h new file mode 100644 index 0000000..88cc1a3 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaColorDisplayDelegate.h @@ -0,0 +1,19 @@ +#ifndef ELACOLORDISPLAYDELEGATE_H +#define ELACOLORDISPLAYDELEGATE_H + +#include + +#include "Def.h" +class ElaColorDisplayDelegate : public QStyledItemDelegate +{ + Q_OBJECT + Q_PRIVATE_CREATE(ElaThemeType::ThemeMode, ThemeMode) +public: + explicit ElaColorDisplayDelegate(QObject* parent = nullptr); + ~ElaColorDisplayDelegate(); + + void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; + QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override; +}; + +#endif // ELACOLORDISPLAYDELEGATE_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaColorDisplayModel.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaColorDisplayModel.cpp new file mode 100644 index 0000000..465d1bb --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaColorDisplayModel.cpp @@ -0,0 +1,74 @@ +#include "ElaColorDisplayModel.h" + +ElaColorDisplayModel::ElaColorDisplayModel(QObject* parent) + : QAbstractListModel{parent} +{ +} + +ElaColorDisplayModel::~ElaColorDisplayModel() +{ +} + +void ElaColorDisplayModel::appendDisplayColor(QList colorList) +{ + beginResetModel(); + _displayColorList.append(colorList); + endResetModel(); +} + +void ElaColorDisplayModel::appendDisplayColor(QColor color) +{ + beginResetModel(); + _displayColorList.append(color); + endResetModel(); +} + +void ElaColorDisplayModel::removeDisplayColor(int index) +{ + if (index < 0 || index >= _displayColorList.count()) + { + return; + } + beginResetModel(); + _displayColorList.removeAt(index); + endResetModel(); +} + +void ElaColorDisplayModel::replaceDisplayColor(QColor color, int index) +{ + if (index < 0 || index >= _displayColorList.count()) + { + return; + } + beginResetModel(); + _displayColorList[index] = color; + endResetModel(); +} + +QList ElaColorDisplayModel::getDisplayColorList() const +{ + return _displayColorList; +} + +QColor ElaColorDisplayModel::getDisplayColor(int index) const +{ + if (index < 0 || index >= _displayColorList.count()) + { + return QColor(); + } + return _displayColorList[index]; +} + +int ElaColorDisplayModel::rowCount(const QModelIndex& parent) const +{ + return _displayColorList.count(); +} + +QVariant ElaColorDisplayModel::data(const QModelIndex& index, int role) const +{ + if (role == Qt::UserRole) + { + return QVariant::fromValue(_displayColorList[index.row()]); + } + return QVariant(); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaColorDisplayModel.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaColorDisplayModel.h new file mode 100644 index 0000000..528f433 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaColorDisplayModel.h @@ -0,0 +1,28 @@ +#ifndef ELACOLORDISPLAYMODEL_H +#define ELACOLORDISPLAYMODEL_H + +#include +#include +class ElaColorDisplayModel : public QAbstractListModel +{ + Q_OBJECT +public: + explicit ElaColorDisplayModel(QObject* parent = nullptr); + ~ElaColorDisplayModel(); + void appendDisplayColor(QList colorList); + void appendDisplayColor(QColor color); + void removeDisplayColor(int index); + void replaceDisplayColor(QColor color, int index); + + QList getDisplayColorList() const; + QColor getDisplayColor(int index) const; + +protected: + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + QVariant data(const QModelIndex& index, int role) const override; + +private: + QList _displayColorList; +}; + +#endif // ELACOLORDISPLAYMODEL_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaColorPicker.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaColorPicker.cpp new file mode 100644 index 0000000..8493f6d --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaColorPicker.cpp @@ -0,0 +1,120 @@ +#include "ElaColorPicker.h" + +#include +#include +#include + +#include "ElaTheme.h" +ElaColorPicker::ElaColorPicker(QWidget* parent) + : QWidget{parent} +{ + setFixedSize(260, 260); + _pThemeMode = eTheme->getThemeMode(); + // 初始化色相图 + QPixmap ColorPickerPix(QSize(257, 257)); + QImage ColorPickerImage = ColorPickerPix.toImage(); + for (int y = 0; y < ColorPickerImage.height(); y++) + { + for (int x = 0; x < ColorPickerImage.width(); x++) + { + QColor pixColor; + pixColor.setHsvF((qreal)1 / ColorPickerPix.width() * x, 1 - (qreal)1 / ColorPickerPix.height() * y, 1); + pixColor = pixColor.toRgb(); + ColorPickerImage.setPixelColor(x, y, pixColor); + } + } + _colorPickerImage = ColorPickerImage; +} + +ElaColorPicker::~ElaColorPicker() +{ +} + +void ElaColorPicker::setSelectedColor(QColor color) +{ + _selectedColor = color; + QColor hsvColor = color.toHsv(); + _selectedPoint = QPoint(257 * hsvColor.hue() / (qreal)360, 255 - 257 * hsvColor.saturation() / (qreal)255); + update(); +} + +QColor ElaColorPicker::getSelectedColor() const +{ + return _selectedColor; +} + +void ElaColorPicker::mousePressEvent(QMouseEvent* event) +{ + _selectedPoint = _adjustPointLimit(event->pos()); + _selectedColor = _colorPickerImage.pixelColor(_selectedPoint); + Q_EMIT selectedColorChanged(_selectedColor); + QWidget::mousePressEvent(event); + update(); +} + +void ElaColorPicker::mouseReleaseEvent(QMouseEvent* event) +{ + _selectedPoint = _adjustPointLimit(event->pos()); + _selectedColor = _colorPickerImage.pixelColor(_selectedPoint); + Q_EMIT selectedColorChanged(_selectedColor); + QWidget::mouseReleaseEvent(event); + update(); +} + +void ElaColorPicker::mouseMoveEvent(QMouseEvent* event) +{ + _selectedPoint = _adjustPointLimit(event->pos()); + _selectedColor = _colorPickerImage.pixelColor(_selectedPoint); + Q_EMIT selectedColorChanged(_selectedColor); + QWidget::mouseMoveEvent(event); + update(); +} + +void ElaColorPicker::paintEvent(QPaintEvent* event) +{ + int penWidth = 3; + int borderRadius = 5; + QPainter painter(this); + painter.save(); + painter.setRenderHint(QPainter::Antialiasing); + // 色相图绘制 + QRect pickerRect = rect(); + pickerRect.adjust(penWidth, penWidth, -penWidth, -penWidth); + painter.setPen(QPen(ElaThemeColor(_pThemeMode, BasicBorder), penWidth)); + painter.drawRoundedRect(pickerRect, borderRadius, borderRadius); + QPainterPath path; + path.addRoundedRect(pickerRect, borderRadius, borderRadius); + painter.setClipPath(path); + painter.drawImage(pickerRect, _colorPickerImage); + + // 取色点绘制 + if (!_selectedPoint.isNull()) + { + painter.setPen(QPen(Qt::black, 2)); + painter.setBrush(Qt::NoBrush); + painter.drawEllipse(_selectedPoint, 8, 8); + } + painter.restore(); +} + +QPoint ElaColorPicker::_adjustPointLimit(QPoint point) +{ + QRect pickerRect = rect(); + if (point.x() < 3) + { + point.setX(3); + } + else if (point.x() > pickerRect.right() - 3) + { + point.setX(pickerRect.right() - 3); + } + if (point.y() < 3) + { + point.setY(3); + } + else if (point.y() > pickerRect.bottom() - 3) + { + point.setY(pickerRect.bottom() - 3); + } + return point; +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaColorPicker.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaColorPicker.h new file mode 100644 index 0000000..3b9ea76 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaColorPicker.h @@ -0,0 +1,36 @@ +#ifndef ELACOLORPICKER_H +#define ELACOLORPICKER_H + +#include +#include + +#include "Def.h" +class ElaColorPicker : public QWidget +{ + Q_OBJECT + Q_PRIVATE_CREATE(ElaThemeType::ThemeMode, ThemeMode) + +public: + explicit ElaColorPicker(QWidget* parent = nullptr); + ~ElaColorPicker(); + + void setSelectedColor(QColor color); + QColor getSelectedColor() const; + +Q_SIGNALS: + Q_SIGNAL void selectedColorChanged(QColor selectedColor); + +protected: + virtual void mousePressEvent(QMouseEvent* event) override; + virtual void mouseReleaseEvent(QMouseEvent* event) override; + virtual void mouseMoveEvent(QMouseEvent* event) override; + virtual void paintEvent(QPaintEvent* event) override; + +private: + QImage _colorPickerImage; + QPoint _selectedPoint; + QColor _selectedColor; + QPoint _adjustPointLimit(QPoint point); +}; + +#endif // ELACOLORPICKER_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaColorPreview.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaColorPreview.cpp new file mode 100644 index 0000000..067e2f8 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaColorPreview.cpp @@ -0,0 +1,31 @@ +#include "ElaColorPreview.h" + +#include +#include + +#include "ElaTheme.h" +ElaColorPreview::ElaColorPreview(QWidget* parent) + : QWidget{parent} +{ + setFixedSize(50, 260); + _pThemeMode = eTheme->getThemeMode(); +} + +ElaColorPreview::~ElaColorPreview() +{ +} + +void ElaColorPreview::paintEvent(QPaintEvent* event) +{ + int penWidth = 3; + int borderRadius = 5; + QPainter painter(this); + painter.save(); + painter.setRenderHint(QPainter::Antialiasing); + QRect viewRect = rect(); + viewRect.adjust(penWidth, penWidth, -penWidth, -penWidth); + painter.setPen(QPen(ElaThemeColor(_pThemeMode, BasicBorder), penWidth)); + painter.setBrush(_pBaseColor); + painter.drawRoundedRect(viewRect, borderRadius, borderRadius); + painter.restore(); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaColorPreview.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaColorPreview.h new file mode 100644 index 0000000..89c98b9 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaColorPreview.h @@ -0,0 +1,20 @@ +#ifndef ELACOLORPREVIEW_H +#define ELACOLORPREVIEW_H + +#include + +#include "Def.h" +class ElaColorPreview : public QWidget +{ + Q_OBJECT + Q_PRIVATE_CREATE(QColor, BaseColor) + Q_PRIVATE_CREATE(ElaThemeType::ThemeMode, ThemeMode) +public: + explicit ElaColorPreview(QWidget* parent = nullptr); + ~ElaColorPreview(); + +protected: + virtual void paintEvent(QPaintEvent* event) override; +}; + +#endif // ELACOLORPREVIEW_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaColorValueSliderStyle.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaColorValueSliderStyle.cpp new file mode 100644 index 0000000..440ff97 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaColorValueSliderStyle.cpp @@ -0,0 +1,159 @@ +#include "ElaColorValueSliderStyle.h" + +#include +#include +#include + +#include "ElaTheme.h" +ElaColorValueSliderStyle::ElaColorValueSliderStyle(QStyle* style) +{ + _baseGradient = new QLinearGradient(0, 0, 100, 100); + setProperty("circleRadius", 0.01); + _themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { _themeMode = themeMode; }); +} + +ElaColorValueSliderStyle::~ElaColorValueSliderStyle() +{ +} + +void ElaColorValueSliderStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex* option, QPainter* painter, const QWidget* widget) const +{ + switch (control) + { + case QStyle::CC_Slider: + { + const QStyleOptionSlider* sopt = qstyleoption_cast(option); + if (!sopt) + { + break; + } + _baseGradient->setFinalStop(widget->rect().bottomRight()); + QColor highValueColor = _pBaseColor.toHsv(); + highValueColor.setHsv(highValueColor.hue(), highValueColor.saturation(), 255); + QColor lowValueColor = _pBaseColor.toHsv(); + lowValueColor.setHsv(lowValueColor.hue(), lowValueColor.saturation(), 0); + _baseGradient->setColorAt(0, highValueColor); + _baseGradient->setColorAt(1, lowValueColor); + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + QRectF sliderRect = sopt->rect; + sliderRect.adjust(1.5, 0, -1.5, 0); + QRect sliderHandleRect = subControlRect(control, sopt, SC_SliderHandle, widget); + sliderHandleRect.adjust(1, 1, -1, -1); + // 滑槽 + painter->setPen(Qt::NoPen); + painter->setBrush(*_baseGradient); + // 渐变背景 + painter->drawRoundedRect(QRectF(sliderRect.x(), sliderRect.y() + sliderRect.width() / 8, sliderRect.width(), sliderRect.height() - sliderRect.width() / 4), sliderRect.width() / 2, sliderRect.width() / 2); + + // 滑块 + // 外圆形 + painter->setPen(ElaThemeColor(_themeMode, BasicBorder)); + painter->setBrush(ElaThemeColor(_themeMode, BasicBase)); + painter->drawEllipse(QPointF(sliderHandleRect.center().x() + 1, sliderHandleRect.center().y() + 1), sliderHandleRect.width() / 2, sliderHandleRect.width() / 2); + // 内圆形 + painter->setPen(Qt::NoPen); + painter->setBrush(ElaThemeColor(_themeMode, PrimaryNormal)); + if (_lastState == 0) + { + _lastState = sopt->state; + } + if (_circleRadius == 0) + { + _circleRadius = sliderHandleRect.width() / 3.8; + } + if (sopt->activeSubControls == SC_SliderHandle) + { + if (sopt->state & QStyle::State_Sunken) + { + if (sopt->state & QStyle::State_MouseOver) + { + if (!_lastState.testFlag(QStyle::State_Sunken)) + { + _startRadiusAnimation(sliderHandleRect.width() / 2.8, sliderHandleRect.width() / 4.5, const_cast(widget)); + _lastState = sopt->state; + } + painter->drawEllipse(QPointF(sliderHandleRect.center().x() + 1, sliderHandleRect.center().y() + 1), _circleRadius, _circleRadius); + } + } + else + { + if (sopt->state & QStyle::State_MouseOver) + { + if (!_lastState.testFlag(QStyle::State_MouseOver)) + { + _startRadiusAnimation(_circleRadius, sliderHandleRect.width() / 2.8, const_cast(widget)); + _lastState = sopt->state; + } + if (_lastState.testFlag(QStyle::State_Sunken)) + { + _startRadiusAnimation(_circleRadius, sliderHandleRect.width() / 2.8, const_cast(widget)); + _lastState = sopt->state; + } + painter->drawEllipse(QPointF(sliderHandleRect.center().x() + 1, sliderHandleRect.center().y() + 1), _circleRadius, _circleRadius); + } + } + } + else + { + if (_lastState.testFlag(QStyle::State_MouseOver) || _lastState.testFlag(QStyle::State_Sunken)) + { + _startRadiusAnimation(_circleRadius, sliderHandleRect.width() / 3.8, const_cast(widget)); + _lastState &= ~QStyle::State_MouseOver; + _lastState &= ~QStyle::State_Sunken; + } + painter->drawEllipse(QPointF(sliderHandleRect.center().x() + 1, sliderHandleRect.center().y() + 1), _circleRadius, _circleRadius); + } + painter->restore(); + return; + } + default: + { + break; + } + } + QProxyStyle::drawComplexControl(control, option, painter, widget); +} + +int ElaColorValueSliderStyle::pixelMetric(PixelMetric metric, const QStyleOption* option, const QWidget* widget) const +{ + switch (metric) + { + case QStyle::PM_SliderLength: + { + return 20; + } + case QStyle::PM_SliderThickness: + { + return 16; + } + default: + { + break; + } + } + return QProxyStyle::pixelMetric(metric, option, widget); +} + +int ElaColorValueSliderStyle::styleHint(StyleHint hint, const QStyleOption* option, const QWidget* widget, QStyleHintReturn* returnData) const +{ + if (hint == QStyle::SH_Slider_AbsoluteSetButtons) + { + return Qt::LeftButton; + } + return QProxyStyle::styleHint(hint, option, widget, returnData); +} + +void ElaColorValueSliderStyle::_startRadiusAnimation(qreal startRadius, qreal endRadius, QWidget* widget) const +{ + ElaColorValueSliderStyle* style = const_cast(this); + QPropertyAnimation* circleRadiusAnimation = new QPropertyAnimation(style, "circleRadius"); + connect(circleRadiusAnimation, &QPropertyAnimation::valueChanged, style, [=](const QVariant& value) { + this->_circleRadius = value.toReal(); + widget->update(); }); + circleRadiusAnimation->setEasingCurve(QEasingCurve::InOutSine); + circleRadiusAnimation->setStartValue(startRadius); + circleRadiusAnimation->setEndValue(endRadius); + circleRadiusAnimation->start(QAbstractAnimation::DeleteWhenStopped); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaColorValueSliderStyle.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaColorValueSliderStyle.h new file mode 100644 index 0000000..302b5f0 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaColorValueSliderStyle.h @@ -0,0 +1,27 @@ +#ifndef ELACOLORVALUESLIDERSTYLE_H +#define ELACOLORVALUESLIDERSTYLE_H + +#include +#include + +#include "Def.h" +class ElaColorValueSliderStyle : public QProxyStyle +{ + Q_OBJECT + Q_PRIVATE_CREATE(QColor, BaseColor) +public: + explicit ElaColorValueSliderStyle(QStyle* style = nullptr); + ~ElaColorValueSliderStyle(); + void drawComplexControl(ComplexControl control, const QStyleOptionComplex* option, QPainter* painter, const QWidget* widget = nullptr) const override; + int pixelMetric(PixelMetric metric, const QStyleOption* option = nullptr, const QWidget* widget = nullptr) const override; + int styleHint(StyleHint hint, const QStyleOption* option = nullptr, const QWidget* widget = nullptr, QStyleHintReturn* returnData = nullptr) const override; + +private: + ElaThemeType::ThemeMode _themeMode; + QLinearGradient* _baseGradient{nullptr}; + mutable QStyle::State _lastState{0}; + mutable qreal _circleRadius{0}; + void _startRadiusAnimation(qreal startRadius, qreal endRadius, QWidget* widget) const; +}; + +#endif // ELACOLORVALUESLIDERSTYLE_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaComboBoxStyle.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaComboBoxStyle.cpp new file mode 100644 index 0000000..227e816 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaComboBoxStyle.cpp @@ -0,0 +1,247 @@ +#include "ElaComboBoxStyle.h" + +#include +#include +#include +#include + +#include "ElaTheme.h" +ElaComboBoxStyle::ElaComboBoxStyle(QStyle* style) +{ + _pExpandIconRotate = 0; + _pExpandMarkWidth = 0; + _themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { _themeMode = themeMode; }); +} + +ElaComboBoxStyle::~ElaComboBoxStyle() +{ +} + +void ElaComboBoxStyle::drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const +{ + switch (element) + { + case QStyle::PE_Widget: + { + return; + } +#ifndef Q_OS_WIN + case PE_PanelMenu: + { + return; + } + case PE_IndicatorArrowDown: + { + return; + } +#endif + default: + { + break; + } + } + QProxyStyle::drawPrimitive(element, option, painter, widget); +} + +void ElaComboBoxStyle::drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const +{ + switch (element) + { + case QStyle::CE_ComboBoxLabel: + { + return; + } + case QStyle::CE_ShapedFrame: + { + //container区域 + if (widget->objectName() == "ElaComboBoxContainer") + { + QRect viewRect = option->rect; + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + eTheme->drawEffectShadow(painter, viewRect, _shadowBorderWidth, 6); + QRect foregroundRect(viewRect.x() + _shadowBorderWidth, viewRect.y(), viewRect.width() - 2 * _shadowBorderWidth, viewRect.height() - _shadowBorderWidth); + painter->setPen(ElaThemeColor(_themeMode, PopupBorder)); + painter->setBrush(ElaThemeColor(_themeMode, PopupBase)); + painter->drawRoundedRect(foregroundRect, 3, 3); + painter->restore(); + } + return; + } + case QStyle::CE_ItemViewItem: + { + //覆盖高亮 + if (const QStyleOptionViewItem* vopt = qstyleoption_cast(option)) + { + int margin = 2; + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + painter->setPen(Qt::NoPen); + QPainterPath path; + QRect optionRect = option->rect; + optionRect.adjust(margin, margin, -margin, -margin); +#ifndef Q_OS_WIN + optionRect.adjust(6, 0, -6, 0); +#endif + path.addRoundedRect(optionRect, 5, 5); + if (option->state & QStyle::State_Selected) + { + if (option->state & QStyle::State_MouseOver) + { + // 选中时覆盖 + painter->setBrush(ElaThemeColor(_themeMode, BasicSelectedHoverAlpha)); + painter->drawPath(path); + } + else + { + // 选中 + painter->setBrush(ElaThemeColor(_themeMode, BasicSelectedAlpha)); + painter->drawPath(path); + } + //选中Mark + painter->setPen(Qt::NoPen); + painter->setBrush(ElaThemeColor(_themeMode, PrimaryNormal)); + painter->drawRoundedRect(QRectF(optionRect.x() + 3, optionRect.y() + optionRect.height() * 0.2, 3, optionRect.height() - +optionRect.height() * 0.4), 2, 2); + } + else + { + if (option->state & QStyle::State_MouseOver) + { + // 覆盖时颜色 + painter->setBrush(ElaThemeColor(_themeMode, BasicHoverAlpha)); + painter->drawPath(path); + } + } + // 文字绘制 + painter->setPen(ElaThemeColor(_themeMode, BasicText)); + painter->drawText(QRect(option->rect.x() + 15, option->rect.y(), option->rect.width() - 15, option->rect.height()), Qt::AlignVCenter, vopt->text); + painter->restore(); + } + return; + } + default: + { + break; + } + } + QProxyStyle::drawControl(element, option, painter, widget); +} + +void ElaComboBoxStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex* option, QPainter* painter, const QWidget* widget) const +{ + switch (control) + { + case QStyle::CC_ComboBox: + { + //主体显示绘制 + if (const QStyleOptionComboBox* copt = qstyleoption_cast(option)) + { + painter->save(); + painter->setRenderHints(QPainter::SmoothPixmapTransform | QPainter::Antialiasing | QPainter::TextAntialiasing); + //背景绘制 + bool isEnabled = copt->state.testFlag(QStyle::State_Enabled); + painter->setPen(ElaThemeColor(_themeMode, BasicBorder)); + painter->setBrush(isEnabled ? copt->state.testFlag(QStyle::State_MouseOver) ? ElaThemeColor(_themeMode, BasicHover) : ElaThemeColor(_themeMode, BasicBase) : ElaThemeColor(_themeMode, BasicDisable)); + QRect comboBoxRect = copt->rect; + comboBoxRect.adjust(_shadowBorderWidth, 1, -_shadowBorderWidth, -1); + painter->drawRoundedRect(comboBoxRect, 3, 3); + // 底边线绘制 + painter->setPen(ElaThemeColor(_themeMode, BasicBaseLine)); + painter->drawLine(comboBoxRect.x() + 3, comboBoxRect.y() + comboBoxRect.height(), comboBoxRect.x() + comboBoxRect.width() - 3, comboBoxRect.y() + comboBoxRect.height()); + + //文字绘制 + QRect textRect = subControlRect(QStyle::CC_ComboBox, copt, QStyle::SC_ScrollBarSubLine, widget); + painter->setPen(isEnabled ? ElaThemeColor(_themeMode, BasicText) : ElaThemeColor(_themeMode, BasicTextDisable)); + painter->drawText(textRect, Qt::AlignVCenter | Qt::AlignLeft, copt->currentText); + //展开指示器绘制 + painter->setPen(Qt::NoPen); + painter->setBrush(ElaThemeColor(_themeMode, PrimaryNormal)); + painter->drawRoundedRect(QRectF(comboBoxRect.center().x() - _pExpandMarkWidth, comboBoxRect.y() + comboBoxRect.height() - 3, _pExpandMarkWidth * 2, 3), 2, 2); + // 展开图标绘制 + QRect expandIconRect = subControlRect(QStyle::CC_ComboBox, copt, QStyle::SC_ScrollBarAddPage, widget); + if (expandIconRect.isValid()) + { + QFont iconFont = QFont("ElaAwesome"); + iconFont.setPixelSize(17); + painter->setFont(iconFont); + painter->setPen(isEnabled ? ElaThemeColor(_themeMode, BasicText) : ElaThemeColor(_themeMode, BasicTextDisable)); + painter->translate(expandIconRect.x() + (qreal)expandIconRect.width() / 2, expandIconRect.y() + (qreal)expandIconRect.height() / 2); + painter->rotate(_pExpandIconRotate); + painter->translate(-expandIconRect.x() - (qreal)expandIconRect.width() / 2, -expandIconRect.y() - (qreal)expandIconRect.height() / 2); + painter->drawText(expandIconRect, Qt::AlignCenter, QChar((unsigned short)ElaIconType::AngleDown)); + painter->restore(); + } + } + return; + } + default: + { + break; + } + } + QProxyStyle::drawComplexControl(control, option, painter, widget); +} + +QRect ElaComboBoxStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex* opt, SubControl sc, const QWidget* widget) const +{ + switch (cc) + { + case QStyle::CC_ComboBox: + { + switch (sc) + { + case QStyle::SC_ScrollBarSubLine: + { + //文字区域 + QRect textRect = QProxyStyle::subControlRect(cc, opt, sc, widget); + textRect.setLeft(16); + textRect.setRight(textRect.right() - 15); + return textRect; + } + case QStyle::SC_ScrollBarAddPage: + { + //展开图标区域 + QRect expandIconRect = QProxyStyle::subControlRect(cc, opt, sc, widget); + expandIconRect.setLeft(expandIconRect.left() - 25); + return expandIconRect; + } + default: + { + break; + } + } + break; + } + default: + { + break; + } + } + + return QProxyStyle::subControlRect(cc, opt, sc, widget); +} + +QSize ElaComboBoxStyle::sizeFromContents(ContentsType type, const QStyleOption* option, const QSize& size, const QWidget* widget) const +{ + switch (type) + { + case QStyle::CT_ItemViewItem: + { + QSize itemSize = QProxyStyle::sizeFromContents(type, option, size, widget); + itemSize.setHeight(35); + return itemSize; + } + case QStyle::CT_ComboBox: + { + QSize comboBoxSize = QProxyStyle::sizeFromContents(type, option, size, widget); + comboBoxSize.setWidth(comboBoxSize.width() + 26); + return comboBoxSize; + } + default: + { + break; + } + } + return QProxyStyle::sizeFromContents(type, option, size, widget); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaComboBoxStyle.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaComboBoxStyle.h new file mode 100644 index 0000000..842b899 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaComboBoxStyle.h @@ -0,0 +1,27 @@ +#ifndef ELACOMBOBOXSTYLE_H +#define ELACOMBOBOXSTYLE_H + +#include + +#include "Def.h" +class ElaComboBoxStyle : public QProxyStyle +{ + Q_OBJECT + Q_PROPERTY_CREATE(qreal, ExpandIconRotate) + Q_PROPERTY_CREATE(qreal, ExpandMarkWidth) +public: + explicit ElaComboBoxStyle(QStyle* style = nullptr); + ~ElaComboBoxStyle(); + void drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget = nullptr) const override; + void drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget = nullptr) const override; + void drawComplexControl(ComplexControl control, const QStyleOptionComplex* option, QPainter* painter, const QWidget* widget = nullptr) const override; + + QRect subControlRect(ComplexControl cc, const QStyleOptionComplex* opt, SubControl sc, const QWidget* widget) const override; + QSize sizeFromContents(ContentsType type, const QStyleOption* option, const QSize& size, const QWidget* widget) const override; + +private: + ElaThemeType::ThemeMode _themeMode; + int _shadowBorderWidth{6}; +}; + +#endif // ELACOMBOBOXSTYLE_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaComboBoxView.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaComboBoxView.cpp new file mode 100644 index 0000000..3fdba6d --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaComboBoxView.cpp @@ -0,0 +1,22 @@ +#include "ElaComboBoxView.h" + +#include + +ElaComboBoxView::ElaComboBoxView(QWidget* parent) + : QListView(parent) +{ +} + +ElaComboBoxView::~ElaComboBoxView() +{ +} + +void ElaComboBoxView::mousePressEvent(QMouseEvent* event) +{ + QModelIndex index = indexAt(event->pos()); + if (index.isValid()) + { + Q_EMIT itemPressed(index); + } + event->ignore(); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaComboBoxView.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaComboBoxView.h new file mode 100644 index 0000000..1fb9fe1 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaComboBoxView.h @@ -0,0 +1,19 @@ +#ifndef ELACOMBOBOXVIEW_H +#define ELACOMBOBOXVIEW_H + +#include + +class ElaComboBoxView : public QListView +{ + Q_OBJECT +public: + explicit ElaComboBoxView(QWidget* parent = nullptr); + ~ElaComboBoxView(); +Q_SIGNALS: + Q_SIGNAL void itemPressed(const QModelIndex& index); + +protected: + void mousePressEvent(QMouseEvent* event) override; +}; + +#endif // ELACOMBOBOXVIEW_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCustomTabWidget.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCustomTabWidget.cpp new file mode 100644 index 0000000..f3455dd --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCustomTabWidget.cpp @@ -0,0 +1,77 @@ +#include "ElaCustomTabWidget.h" + +#include +#include + +#include "ElaAppBar.h" +#include "ElaTabBar.h" +#include "ElaTabWidget.h" +#include "ElaTabWidgetPrivate.h" +ElaCustomTabWidget::ElaCustomTabWidget(QWidget* parent) + : ElaCustomWidget(parent) +{ + resize(700, 500); + setWindowTitle(""); + setWindowIcon(QIcon()); + _customTabWidget = new ElaTabWidget(this); + QTabBar* originTabBar = _customTabWidget->tabBar(); + originTabBar->hide(); + _customTabBar = new ElaTabBar(this); + _customTabBar->setObjectName("ElaCustomTabBar"); + connect(_customTabBar, &ElaTabBar::tabMoved, this, [=](int from, int to) { + _customTabWidget->tabBar()->moveTab(from, to); + }); + connect(_customTabBar, &ElaTabBar::currentChanged, this, [=](int index) { + _customTabWidget->setCurrentIndex(index); + }); + connect(_customTabWidget, &ElaTabWidget::currentChanged, this, [=](int index) { + if (index == -1) + { + close(); + } + }); + connect(_customTabBar, &ElaTabBar::tabCloseRequested, originTabBar, &QTabBar::tabCloseRequested); + + QWidget* customWidget = new QWidget(this); + QVBoxLayout* customLayout = new QVBoxLayout(customWidget); + customLayout->setContentsMargins(10, 0, 10, 0); + customLayout->addStretch(); + customLayout->addWidget(_customTabBar); + _appBar->setCustomWidget(ElaAppBarType::LeftArea, customWidget); + setCentralWidget(_customTabWidget); +} + +ElaCustomTabWidget::~ElaCustomTabWidget() +{ + while (_customTabWidget->count() > 0) + { + QWidget* closeWidget = _customTabWidget->widget(0); + ElaTabWidget* originTabWidget = closeWidget->property("ElaOriginTabWidget").value(); + + if (originTabWidget) + { + closeWidget->setProperty("CurrentCustomBar", QVariant::fromValue(nullptr)); + originTabWidget->addTab(closeWidget, _customTabWidget->tabIcon(0), _customTabWidget->tabText(0)); + } + else + { + _customTabWidget->removeTab(0); + } + } +} + +void ElaCustomTabWidget::addTab(QWidget* widget, QIcon& tabIcon, const QString& tabTitle) +{ + _customTabBar->addTab(tabIcon, tabTitle); + _customTabWidget->addTab(widget, tabIcon, tabTitle); +} + +ElaTabBar* ElaCustomTabWidget::getCustomTabBar() const +{ + return _customTabBar; +} + +ElaTabWidget* ElaCustomTabWidget::getCustomTabWidget() const +{ + return _customTabWidget; +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCustomTabWidget.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCustomTabWidget.h new file mode 100644 index 0000000..d85e996 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCustomTabWidget.h @@ -0,0 +1,23 @@ +#ifndef ELACUSTOMTABWIDGET_H +#define ELACUSTOMTABWIDGET_H + +#include "ElaCustomWidget.h" + +class ElaTabBar; +class ElaTabWidget; +class ElaCustomTabWidget : public ElaCustomWidget +{ + Q_OBJECT +public: + explicit ElaCustomTabWidget(QWidget* parent = nullptr); + ~ElaCustomTabWidget(); + void addTab(QWidget* widget, QIcon& tabIcon, const QString& tabTitle); + ElaTabBar* getCustomTabBar() const; + ElaTabWidget* getCustomTabWidget() const; + +private: + ElaTabBar* _customTabBar{nullptr}; + ElaTabWidget* _customTabWidget{nullptr}; +}; + +#endif // ELACUSTOMTABWIDGET_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCustomWidget.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCustomWidget.cpp new file mode 100644 index 0000000..4c6c53b --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCustomWidget.cpp @@ -0,0 +1,62 @@ +#include "ElaCustomWidget.h" + +#include +#include + +#include "ElaApplication.h" +#include "ElaTheme.h" +Q_TAKEOVER_NATIVEEVENT_CPP(ElaCustomWidget, _appBar); +ElaCustomWidget::ElaCustomWidget(QWidget* parent) + : QDialog{parent} +{ + resize(500, 500); // 默认宽高 + setObjectName("ElaCustomWidget"); + // 自定义AppBar + _appBar = new ElaAppBar(this); + _appBar->setWindowButtonFlags(ElaAppBarType::MinimizeButtonHint | ElaAppBarType::MaximizeButtonHint | ElaAppBarType::CloseButtonHint); + _mainLayout = new QVBoxLayout(this); + _mainLayout->setContentsMargins(0, 0, 0, 0); + + _themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { + _themeMode = themeMode; + update(); + }); + + _isEnableMica = eApp->getIsEnableMica(); + connect(eApp, &ElaApplication::pIsEnableMicaChanged, this, [=]() { + _isEnableMica = eApp->getIsEnableMica(); + update(); + }); + eApp->syncMica(this); + setAttribute(Qt::WA_DeleteOnClose); +} + +ElaCustomWidget::~ElaCustomWidget() +{ +} + +void ElaCustomWidget::setCentralWidget(QWidget* widget) +{ + if (!widget) + { + return; + } + _centralWidget = widget; + _mainLayout->addWidget(widget); + widget->setVisible(true); +} + +void ElaCustomWidget::paintEvent(QPaintEvent* event) +{ + if (!_isEnableMica) + { + QPainter painter(this); + painter.save(); + painter.setRenderHint(QPainter::Antialiasing); + painter.setPen(Qt::NoPen); + painter.setBrush(ElaThemeColor(_themeMode, WindowBase)); + painter.drawRect(rect()); + painter.restore(); + } +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCustomWidget.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCustomWidget.h new file mode 100644 index 0000000..23066c9 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaCustomWidget.h @@ -0,0 +1,31 @@ +#ifndef ELACUSTOMWIDGET_H +#define ELACUSTOMWIDGET_H + +#include + +#include "Def.h" +#include "ElaAppBar.h" + +class QVBoxLayout; +class ElaCustomWidget : public QDialog +{ + Q_OBJECT + Q_TAKEOVER_NATIVEEVENT_H +public: + explicit ElaCustomWidget(QWidget* parent = nullptr); + ~ElaCustomWidget(); + + void setCentralWidget(QWidget* widget); + +protected: + virtual void paintEvent(QPaintEvent* event) override; + QVBoxLayout* _mainLayout{nullptr}; + ElaAppBar* _appBar{nullptr}; + QWidget* _centralWidget{nullptr}; + +private: + ElaThemeType::ThemeMode _themeMode; + bool _isEnableMica; +}; + +#endif // ELACUSTOMWIDGET_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaDockWidgetTitleBar.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaDockWidgetTitleBar.cpp new file mode 100644 index 0000000..6d9d4be --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaDockWidgetTitleBar.cpp @@ -0,0 +1,88 @@ +#include "ElaDockWidgetTitleBar.h" + +#include +#include + +#include "ElaDockWidget.h" +#include "ElaIconButton.h" +#include "ElaText.h" +#include "ElaTheme.h" +ElaDockWidgetTitleBar::ElaDockWidgetTitleBar(QWidget* parent) + : QWidget{parent} +{ + _dockWidget = dynamic_cast(parent); + _iconLabel = new QLabel(this); + _iconLabel->setPixmap(_dockWidget->windowIcon().pixmap(QSize(18, 18))); + _titleLabel = new ElaText(_dockWidget->windowTitle(), this); + _titleLabel->setWordWrap(false); + _titleLabel->setTextPixelSize(13); + + _floatButton = new ElaIconButton(ElaIconType::WindowRestore, 13, 32, 26, this); + _floatButton->setLightHoverColor(ElaThemeColor(ElaThemeType::Light, BasicHoverAlpha)); + _floatButton->setDarkHoverColor(ElaThemeColor(ElaThemeType::Dark, BasicHoverAlpha)); + connect(_floatButton, &ElaIconButton::clicked, this, &ElaDockWidgetTitleBar::onFloatButtonClicked); + _closeButton = new ElaIconButton(ElaIconType::Xmark, 17, 32, 26, this); + _closeButton->setLightHoverColor(ElaThemeColor(ElaThemeType::Light, StatusDanger)); + _closeButton->setDarkHoverColor(ElaThemeColor(ElaThemeType::Dark, StatusDanger)); + connect(_closeButton, &ElaIconButton::clicked, this, &ElaDockWidgetTitleBar::onCloseButtonClicked); + + _setVisibleFromFeatures(_dockWidget->features()); + connect(_dockWidget, &QDockWidget::featuresChanged, this, [=](QDockWidget::DockWidgetFeatures features) { + _setVisibleFromFeatures(features); + }); + connect(_dockWidget, &QDockWidget::windowTitleChanged, this, [=](const QString& title) { + _titleLabel->setText(title); + }); + connect(_dockWidget, &QDockWidget::windowIconChanged, this, [=](const QIcon& icon) { + _iconLabel->setPixmap(icon.pixmap(QSize(18, 18))); + }); + + QHBoxLayout* mainLayout = new QHBoxLayout(this); + mainLayout->setSpacing(0); + mainLayout->setContentsMargins(0, 0, 0, 0); + mainLayout->addSpacing(5); + mainLayout->addWidget(_iconLabel); + mainLayout->addSpacing(10); + mainLayout->addWidget(_titleLabel); + mainLayout->addSpacing(10); + mainLayout->addStretch(); + mainLayout->addWidget(_floatButton); + mainLayout->addWidget(_closeButton); + + //主题变更 + _themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { + _themeMode = themeMode; + }); +} + +ElaDockWidgetTitleBar::~ElaDockWidgetTitleBar() +{ +} + +void ElaDockWidgetTitleBar::onFloatButtonClicked() +{ + _dockWidget->setFloating(_dockWidget->isFloating() ? false : true); +} + +void ElaDockWidgetTitleBar::onCloseButtonClicked() +{ + _dockWidget->close(); +} + +void ElaDockWidgetTitleBar::paintEvent(QPaintEvent* event) +{ + QPainter painter(this); + painter.save(); + painter.setRenderHint(QPainter::Antialiasing); + painter.setPen(Qt::NoPen); + painter.setBrush(_dockWidget->isFloating() ? Qt::transparent : ElaThemeColor(_themeMode, BasicBaseAlpha)); + painter.drawRect(rect()); + painter.restore(); +} + +void ElaDockWidgetTitleBar::_setVisibleFromFeatures(QDockWidget::DockWidgetFeatures features) +{ + _floatButton->setVisible(features.testFlag(QDockWidget::DockWidgetFloatable)); + _closeButton->setVisible(features.testFlag(QDockWidget::DockWidgetClosable)); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaDockWidgetTitleBar.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaDockWidgetTitleBar.h new file mode 100644 index 0000000..a0dcd7c --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaDockWidgetTitleBar.h @@ -0,0 +1,36 @@ +#ifndef ELADOCKWIDGETTITLEBAR_H +#define ELADOCKWIDGETTITLEBAR_H + +#include +#include +#include + +#include "Def.h" +class ElaText; +class ElaIconButton; +class ElaDockWidget; +class ElaDockWidgetTitleBar : public QWidget +{ + Q_OBJECT +public: + explicit ElaDockWidgetTitleBar(QWidget* parent = nullptr); + ~ElaDockWidgetTitleBar(); + + Q_SLOT void onFloatButtonClicked(); + Q_SLOT void onCloseButtonClicked(); + +protected: + virtual void paintEvent(QPaintEvent* event) override; + +private: + friend class ElaDockWidgetPrivate; + ElaThemeType::ThemeMode _themeMode; + ElaDockWidget* _dockWidget{nullptr}; + QLabel* _iconLabel{nullptr}; + ElaText* _titleLabel{nullptr}; + ElaIconButton* _floatButton{nullptr}; + ElaIconButton* _closeButton{nullptr}; + void _setVisibleFromFeatures(QDockWidget::DockWidgetFeatures features); +}; + +#endif // ELADOCKWIDGETTITLEBAR_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaDxgi.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaDxgi.cpp new file mode 100644 index 0000000..b061461 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaDxgi.cpp @@ -0,0 +1,322 @@ +#include "ElaDxgi.h" +#ifdef Q_OS_WIN +#include +#include + +ElaDxgi::ElaDxgi(QObject* parent) + : QObject(parent) +{ + _pIsInitSuccess = false; + _pIsGrabActive = false; + _pGrabFrameRate = 120; + _pTimeoutMsValue = 50; + _pIsGrabStoped = true; + _pIsGrabCenter = false; +} + +ElaDxgi::~ElaDxgi() +{ + releaseInterface(); +} + +bool ElaDxgi::initialize(int dxID, int outputID) +{ + _pIsInitSuccess = false; + _pDxDeviceID = dxID; + _pOutputDeviceID = outputID; + releaseInterface(); + ID3D11Device* d3dDevice = nullptr; + ID3D11DeviceContext* d3dContext = nullptr; + D3D_FEATURE_LEVEL feat = D3D_FEATURE_LEVEL_11_0; + HRESULT hr = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, nullptr, 0, + D3D11_SDK_VERSION, &d3dDevice, &feat, &d3dContext); + if (FAILED(hr)) + { + _pLastError = "Failed to D3D11CreateDevice ErrorCode = " + QString::number(uint(hr), 16); + qDebug() << _pLastError; + if (d3dDevice) + { + d3dDevice->Release(); + } + if (d3dContext) + { + d3dContext->Release(); + } + return false; + } + + IDXGIFactory* dxgiFactory = nullptr; + CreateDXGIFactory(__uuidof(dxgiFactory), reinterpret_cast(&dxgiFactory)); + IDXGIAdapter* dxgiAdapter = nullptr; + QVector dxgiAdapterVector; + for (uint i = 0; dxgiFactory->EnumAdapters(i, &dxgiAdapter) != DXGI_ERROR_NOT_FOUND; ++i) + { + dxgiAdapterVector.append(dxgiAdapter); + } + dxgiFactory->Release(); + dxgiAdapter = nullptr; + _pDxDeviceList.clear(); + for (int i = 0; i < dxgiAdapterVector.count(); i++) + { + IDXGIAdapter* adapt = dxgiAdapterVector.at(i); + DXGI_ADAPTER_DESC desc; + adapt->GetDesc(&desc); + _pDxDeviceList.append(QString::fromWCharArray(desc.Description)); + } + if (dxID >= 0 && dxID < dxgiAdapterVector.count()) + { + dxgiAdapter = dxgiAdapterVector.at(dxID); + } + + if (!dxgiAdapter) + { + _pLastError = "Failed to found gpu"; + qDebug() << _pLastError; + d3dDevice->Release(); + d3dContext->Release(); + for (auto adapter: dxgiAdapterVector) + { + adapter->Release(); + } + return false; + } + + IDXGIOutput* dxgiOutput = nullptr; + QVector dxgiOutputVector; + for (uint i = 0; dxgiAdapter->EnumOutputs(i, &dxgiOutput) != DXGI_ERROR_NOT_FOUND; ++i) + { + dxgiOutputVector.append(dxgiOutput); + } + for (auto adapter: dxgiAdapterVector) + { + adapter->Release(); + } + dxgiOutput = nullptr; + _pOutputDeviceList.clear(); + for (int i = 0; i < dxgiOutputVector.count(); i++) + { + const auto& output = dxgiOutputVector.at(i); + DXGI_OUTPUT_DESC desc; + output->GetDesc(&desc); + _pOutputDeviceList.append(QString::fromWCharArray(desc.DeviceName)); + } + if (outputID >= 0 && outputID < dxgiOutputVector.count()) + { + dxgiOutput = dxgiOutputVector.at(outputID); + } + + if (!dxgiOutput) + { + _pLastError = "Failed to found screen!"; + qDebug() << _pLastError; + d3dDevice->Release(); + d3dContext->Release(); + for (auto output: dxgiOutputVector) + { + output->Release(); + } + return false; + } + IDXGIOutput6* dxgiOutput6 = nullptr; + hr = dxgiOutput->QueryInterface(__uuidof(IDXGIOutput6), reinterpret_cast(&dxgiOutput6)); + for (auto output: dxgiOutputVector) + { + output->Release(); + } + if (FAILED(hr)) + { + _pLastError = "Failed to QueryInterface IDXGIOutput6 ErrorCode = " + QString::number(uint(hr), 16); + qDebug() << _pLastError; + d3dDevice->Release(); + d3dContext->Release(); + dxgiOutput6->Release(); + return false; + } + UINT supportedFormatsCount = 1; // 支持的格式数 + DXGI_FORMAT supportedFormats[1] = {DXGI_FORMAT_B8G8R8A8_UNORM}; + hr = dxgiOutput6->DuplicateOutput1(d3dDevice, 0, supportedFormatsCount, supportedFormats, &_duplication); + dxgiOutput6->Release(); + if (FAILED(hr)) + { + _pLastError = "Failed to DuplicateOutput ErrorCode = " + QString::number(uint(hr), 16); + qDebug() << _pLastError; + d3dDevice->Release(); + d3dContext->Release(); + if (_duplication) + { + _duplication->Release(); + _duplication = nullptr; + } + return false; + } + _device = d3dDevice; + _context = d3dContext; + _pIsInitSuccess = true; + return true; +} + +QImage ElaDxgi::getGrabImage() const +{ + QImage grabImage(_imageBits, _descWidth, _descHeight, QImage::Format_ARGB32); + if (_pIsGrabCenter) + { + return grabImage.copy(QRect((_descWidth - _pGrabArea.width()) / 2, (_descHeight - _pGrabArea.height()) / 2, _pGrabArea.width(), _pGrabArea.height())); + } + else + { + return grabImage.copy(_pGrabArea); + } +} + +void ElaDxgi::onGrabScreen() +{ + qreal startTime = 0; + qreal endTime = 0; + if (!_duplication || !_device || !_context) + { + setIsGrabStoped(true); + return; + } + _pIsGrabStoped = false; + while (_pIsGrabActive) + { + IDXGIResource* desktopRes = nullptr; + DXGI_OUTDUPL_FRAME_INFO frameInfo; + while (true) + { + if (!_pIsGrabActive) + { + setIsGrabStoped(true); + return; + } + startTime = QDateTime::currentMSecsSinceEpoch(); + _duplication->ReleaseFrame(); + HRESULT hr = _duplication->AcquireNextFrame(_pTimeoutMsValue, &frameInfo, &desktopRes); + if (FAILED(hr)) + { + if (hr != DXGI_ERROR_WAIT_TIMEOUT) + { + if (desktopRes) + { + desktopRes->Release(); + desktopRes = nullptr; + } + while (true) + { + if (!_duplication || !_device || !_context) + { + setIsGrabStoped(true); + return; + } + if (initialize(_pDxDeviceID, _pOutputDeviceID)) + { + break; + } + } + } + continue; + } + if (frameInfo.LastPresentTime.QuadPart) + { + break; + } + } + D3D11_TEXTURE2D_DESC desc; + ID3D11Texture2D* textrueRes = nullptr; + HRESULT hr = desktopRes->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast(&textrueRes)); + desktopRes->Release(); + if (FAILED(hr)) + { + qDebug() << "Failed to ID3D11Texture2D result =" << QString::number(uint(hr), 16); + continue; + } + textrueRes->GetDesc(&desc); + D3D11_TEXTURE2D_DESC texDesc; + ZeroMemory(&texDesc, sizeof(texDesc)); + texDesc.Width = desc.Width; + texDesc.Height = desc.Height; + texDesc.MipLevels = 1; + texDesc.ArraySize = 1; + texDesc.SampleDesc.Count = 1; + texDesc.SampleDesc.Quality = 0; + texDesc.Usage = D3D11_USAGE_STAGING; + texDesc.Format = desc.Format; + texDesc.BindFlags = 0; + texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + texDesc.MiscFlags = 0; + _device->CreateTexture2D(&texDesc, nullptr, &_texture); + _context->CopyResource(_texture, textrueRes); + IDXGISurface1* surface = nullptr; + hr = _texture->QueryInterface(__uuidof(IDXGISurface1), reinterpret_cast(&surface)); + if (FAILED(hr)) + { + qDebug() << "Failed to QueryInterface IDXGISurface1 ErrorCode =" + << QString::number(uint(hr), 16); + continue; + } + + DXGI_MAPPED_RECT map; + surface->Map(&map, DXGI_MAP_READ); + _imageBits = static_cast(map.pBits); + _descWidth = desc.Width; + _descHeight = desc.Height; + surface->Unmap(); + surface->Release(); + _texture->Release(); + Q_EMIT grabScreenOver(); + endTime = QDateTime::currentMSecsSinceEpoch(); + if (_lastGrabTime == 0) + { + _lastGrabTime = endTime - startTime; // 毫秒 + } + else + { + _lastGrabTime = ((endTime - startTime) + _lastGrabTime) / 2.0; + } + _cpuSleepTime = (1000 - _lastGrabTime * _pGrabFrameRate) / _pGrabFrameRate; + cpuSleep(_cpuSleepTime * 1000); + // qDebug() << _cpuSleepTime << _lastGrabTime; + } + setIsGrabStoped(true); +} + +void ElaDxgi::releaseInterface() +{ + if (_duplication) + { + _duplication->Release(); + _duplication = nullptr; + } + if (_device) + { + _device->Release(); + _device = nullptr; + } + if (_context) + { + _context->Release(); + _context = nullptr; + } +} + +void ElaDxgi::cpuSleep(int usec) +{ + if (usec <= 0) + { + return; + } + LARGE_INTEGER cpuFerq; + LARGE_INTEGER startTime; + LARGE_INTEGER endTime; + QueryPerformanceFrequency(&cpuFerq); + QueryPerformanceCounter(&startTime); + while (1) + { + QueryPerformanceCounter(&endTime); + if (((endTime.QuadPart - startTime.QuadPart) * 1000000) / cpuFerq.QuadPart > usec) + { + break; + } + } +} +#endif diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaDxgi.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaDxgi.h new file mode 100644 index 0000000..ab2dfd9 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaDxgi.h @@ -0,0 +1,51 @@ +#ifndef ELADXGI_H +#define ELADXGI_H + +#include +#ifdef Q_OS_WIN +#include +#include + +#include + +#include "stdafx.h" + +class ElaDxgi : public QObject +{ + Q_OBJECT + Q_PRIVATE_CREATE(QStringList, DxDeviceList) + Q_PRIVATE_CREATE(QStringList, OutputDeviceList) + Q_PRIVATE_CREATE(int, DxDeviceID); + Q_PRIVATE_CREATE(int, OutputDeviceID); + Q_PRIVATE_CREATE(QString, LastError) + Q_PRIVATE_CREATE(bool, IsGrabActive) + Q_PRIVATE_CREATE(QRect, GrabArea); + Q_PRIVATE_CREATE(int, GrabFrameRate); // 截图帧数 + Q_PRIVATE_CREATE(int, TimeoutMsValue); // 超时等待 + Q_PRIVATE_CREATE(bool, IsInitSuccess); + Q_PRIVATE_CREATE(bool, IsGrabStoped); + Q_PRIVATE_CREATE(bool, IsGrabCenter); + +public: + explicit ElaDxgi(QObject* parent = nullptr); + ~ElaDxgi(); + bool initialize(int dxID, int outputID); + QImage getGrabImage() const; + Q_SLOT void onGrabScreen(); + Q_SIGNAL void grabScreenOver(); + +private: + IDXGIOutputDuplication* _duplication{nullptr}; + ID3D11Device* _device{nullptr}; + ID3D11DeviceContext* _context{nullptr}; + ID3D11Texture2D* _texture{nullptr}; + qreal _lastGrabTime{0}; + qreal _cpuSleepTime{0}; + uchar* _imageBits{nullptr}; + int _descWidth{0}; + int _descHeight{0}; + void releaseInterface(); + void cpuSleep(int usec); +}; +#endif +#endif // ELADXGI_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaFooterDelegate.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaFooterDelegate.cpp new file mode 100644 index 0000000..e33808d --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaFooterDelegate.cpp @@ -0,0 +1,266 @@ +#include "ElaFooterDelegate.h" + +#include +#include +#include + +#include "ElaBaseListView.h" +#include "ElaFooterModel.h" +#include "ElaNavigationNode.h" +#include "ElaTheme.h" +ElaFooterDelegate::ElaFooterDelegate(QObject* parent) + : QStyledItemDelegate{parent} +{ + _pElaListView = nullptr; + _themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { + _themeMode = themeMode; + }); + setProperty("lastSelectMarkTop", 10.0); + setProperty("lastSelectMarkBottom", 10.0); + setProperty("selectMarkTop", 10.0); + setProperty("selectMarkBottom", 10.0); + // Mark向上 + _lastSelectMarkTopAnimation = new QPropertyAnimation(this, "lastSelectMarkTop"); + connect(_lastSelectMarkTopAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + _lastSelectMarkTop = value.toReal(); + _pElaListView->viewport()->update(); }); + _lastSelectMarkTopAnimation->setDuration(300); + _lastSelectMarkTopAnimation->setEasingCurve(QEasingCurve::InOutSine); + + _selectMarkBottomAnimation = new QPropertyAnimation(this, "selectMarkBottom"); + connect(_selectMarkBottomAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + _selectMarkBottom = value.toReal(); + _pElaListView->viewport()->update(); }); + _selectMarkBottomAnimation->setDuration(300); + _selectMarkBottomAnimation->setEasingCurve(QEasingCurve::InOutSine); + connect(_lastSelectMarkTopAnimation, &QPropertyAnimation::finished, this, [=]() { + _isSelectMarkDisplay = true; + _lastSelectedNode = nullptr; + _selectMarkBottomAnimation->setStartValue(0); + _selectMarkBottomAnimation->setEndValue(10); + _selectMarkBottomAnimation->start(); }); + + // Mark向下 + _lastSelectMarkBottomAnimation = new QPropertyAnimation(this, "lastSelectMarkBottom"); + connect(_lastSelectMarkBottomAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + _lastSelectMarkBottom = value.toReal(); + _pElaListView->viewport()->update(); }); + _lastSelectMarkBottomAnimation->setDuration(300); + _lastSelectMarkBottomAnimation->setEasingCurve(QEasingCurve::InOutSine); + + _selectMarkTopAnimation = new QPropertyAnimation(this, "selectMarkTop"); + connect(_selectMarkTopAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + _selectMarkTop = value.toReal(); + _pElaListView->viewport()->update(); }); + _selectMarkTopAnimation->setDuration(300); + _selectMarkTopAnimation->setEasingCurve(QEasingCurve::InOutSine); + connect(_lastSelectMarkBottomAnimation, &QPropertyAnimation::finished, this, [=]() { + _isSelectMarkDisplay = true; + _lastSelectedNode = nullptr; + _selectMarkTopAnimation->setStartValue(0); + _selectMarkTopAnimation->setEndValue(10); + _selectMarkTopAnimation->start(); }); +} + +ElaFooterDelegate::~ElaFooterDelegate() +{ +} + +void ElaFooterDelegate::navigationNodeStateChange(QVariantMap data) +{ + if (data.contains("SelectMarkChanged")) + { + _lastSelectedNode = data.value("LastSelectedNode").value(); + ElaNavigationNode* selectedNode = data.value("SelectedNode").value(); + bool direction = _compareItemY(selectedNode, _lastSelectedNode); + _lastSelectMarkTop = 10; + _lastSelectMarkBottom = 10; + _selectMarkTop = 10; + _selectMarkBottom = 10; + if (direction) + { + _lastSelectMarkTopAnimation->setStartValue(10); + _lastSelectMarkTopAnimation->setEndValue(0); + _lastSelectMarkTopAnimation->start(); + _lastSelectMarkBottomAnimation->stop(); + _selectMarkTopAnimation->stop(); + _isSelectMarkDisplay = false; + } + else + { + _lastSelectMarkBottomAnimation->setStartValue(10); + _lastSelectMarkBottomAnimation->setEndValue(0); + _lastSelectMarkBottomAnimation->start(); + _lastSelectMarkTopAnimation->stop(); + _selectMarkBottomAnimation->stop(); + _isSelectMarkDisplay = false; + } + } +} + +void ElaFooterDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + QStyleOptionViewItem viewOption(option); + initStyleOption(&viewOption, index); + ElaFooterModel* model = dynamic_cast(const_cast(index.model())); + ElaNavigationNode* node = index.data(Qt::UserRole).value(); + if (option.state.testFlag(QStyle::State_HasFocus)) + { + viewOption.state &= ~QStyle::State_HasFocus; + } + QStyledItemDelegate::paint(painter, viewOption, index); + // 背景绘制 + QRect itemRect = option.rect; + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing); + itemRect.setTop(itemRect.top() + 2); + itemRect.setBottom(itemRect.bottom() - 2); + QPainterPath path; + path.addRoundedRect(itemRect, 8, 8); + if (option.state & QStyle::State_Selected) + { + if (index == _pPressIndex) + { + // 选中时点击 + painter->fillPath(path, ElaThemeColor(_themeMode, BasicHoverAlpha)); + } + else + { + if (option.state & QStyle::State_MouseOver) + { + // 选中时覆盖 + painter->fillPath(path, ElaThemeColor(_themeMode, BasicSelectedHoverAlpha)); + } + else + { + // 选中 + painter->fillPath(path, ElaThemeColor(_themeMode, BasicSelectedAlpha)); + } + } + } + else + { + if (index == _pPressIndex) + { + // 点击时颜色 + painter->fillPath(path, ElaThemeColor(_themeMode, BasicSelectedHoverAlpha)); + } + else + { + if (option.state & QStyle::State_MouseOver) + { + // 覆盖时颜色 + painter->fillPath(path, ElaThemeColor(_themeMode, BasicHoverAlpha)); + } + } + } + painter->restore(); + + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing); + itemRect = option.rect; + + //顶边线绘制 + if (index.row() == 0) + { + painter->setPen(ElaThemeColor(_themeMode, BasicBaseLine)); + painter->drawLine(option.rect.x(), itemRect.y() + 1, option.rect.x() + option.rect.width(), itemRect.y() + 1); + } + // 图标绘制 + painter->setPen(index == _pPressIndex ? ElaThemeColor(_themeMode, BasicTextPress) : ElaThemeColor(_themeMode, BasicText)); + if (node->getAwesome() != ElaIconType::None) + { + painter->save(); + QFont iconFont = QFont("ElaAwesome"); + iconFont.setPixelSize(17); + painter->setFont(iconFont); + painter->drawText(QRect(itemRect.x(), itemRect.y(), _iconAreaWidth, itemRect.height()), Qt::AlignCenter, QChar((unsigned short)node->getAwesome())); + painter->restore(); + } + + int keyPoints = node->getKeyPoints(); + if (keyPoints) + { + // KeyPoints + painter->save(); + painter->setPen(Qt::NoPen); + painter->setBrush(Qt::white); + painter->drawEllipse(QPoint(255, itemRect.y() + itemRect.height() / 2), 10, 10); + painter->setBrush(ElaThemeColor(_themeMode, StatusDanger)); + painter->drawEllipse(QPoint(255, itemRect.y() + itemRect.height() / 2), 9, 9); + painter->setPen(QPen(Qt::white, 2)); + QFont font = painter->font(); + font.setBold(true); + if (keyPoints > 99) + { + keyPoints = 99; + } + if (keyPoints > 9) + { + font.setPixelSize(11); + } + else + { + font.setPixelSize(12); + } + painter->setFont(font); + painter->drawText(keyPoints > 9 ? 248 : 251, itemRect.y() + itemRect.height() / 2 + 4, QString::number(keyPoints)); + painter->restore(); + } + + // 文字绘制 + painter->setPen(index == _pPressIndex ? ElaThemeColor(_themeMode, BasicTextPress) : ElaThemeColor(_themeMode, BasicText)); + QRect textRect; + if (node->getAwesome() != ElaIconType::None) + { + textRect = QRect(itemRect.x() + _iconAreaWidth, itemRect.y(), itemRect.width() - _textRightSpacing - _indicatorIconAreaWidth - _iconAreaWidth, itemRect.height()); + } + else + { + textRect = QRect(itemRect.x() + _leftPadding, itemRect.y(), itemRect.width() - _textRightSpacing - _indicatorIconAreaWidth - _leftPadding, itemRect.height()); + } + QString text = painter->fontMetrics().elidedText(node->getNodeTitle(), Qt::ElideRight, textRect.width()); + painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, text); + // 选中特效 + if (_isSelectMarkDisplay && (node == model->getSelectedNode())) + { + painter->setPen(Qt::NoPen); + painter->setBrush(ElaThemeColor(_themeMode, PrimaryNormal)); + painter->drawRoundedRect(QRectF(itemRect.x() + 3, itemRect.y() + _selectMarkTop, 3, itemRect.height() - _selectMarkTop - _selectMarkBottom), 3, 3); + } + if (node == _lastSelectedNode) + { + painter->setPen(Qt::NoPen); + painter->setBrush(ElaThemeColor(_themeMode, PrimaryNormal)); + painter->drawRoundedRect(QRectF(itemRect.x() + 3, itemRect.y() + _lastSelectMarkTop, 3, itemRect.height() - _lastSelectMarkTop - _lastSelectMarkBottom), 3, 3); + } + painter->restore(); +} + +QSize ElaFooterDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + QSize size = QStyledItemDelegate::sizeHint(option, index); + size.setHeight(40); + return size; +} + +bool ElaFooterDelegate::_compareItemY(ElaNavigationNode* node1, ElaNavigationNode* node2) +{ + if (!node1) + { + return true; + } + if (!node2) + { + return false; + } + if (node1->getModelIndex().row() < node2->getModelIndex().row()) + { + return true; + } + else + { + return false; + } +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaFooterDelegate.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaFooterDelegate.h new file mode 100644 index 0000000..b6a8892 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaFooterDelegate.h @@ -0,0 +1,45 @@ +#ifndef ELAFOOTERDELEGATE_H +#define ELAFOOTERDELEGATE_H + +#include +#include + +#include "Def.h" +class ElaNavigationNode; +class QPropertyAnimation; +class ElaBaseListView; +class ElaFooterDelegate : public QStyledItemDelegate +{ + Q_OBJECT + Q_PRIVATE_CREATE(ElaBaseListView*, ElaListView); + Q_PRIVATE_CREATE(QModelIndex, PressIndex); + +public: + explicit ElaFooterDelegate(QObject* parent = nullptr); + ~ElaFooterDelegate(); + void navigationNodeStateChange(QVariantMap data); + +protected: + void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; + QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override; + +private: + int _leftPadding{11}; + int _iconAreaWidth{40}; + int _textRightSpacing{3}; + int _indicatorIconAreaWidth{24}; + qreal _lastSelectMarkTop{10}; + qreal _lastSelectMarkBottom{10}; + qreal _selectMarkTop{10}; + qreal _selectMarkBottom{10}; + bool _isSelectMarkDisplay{true}; + ElaNavigationNode* _lastSelectedNode{nullptr}; + ElaThemeType::ThemeMode _themeMode; + QPropertyAnimation* _lastSelectMarkTopAnimation{nullptr}; + QPropertyAnimation* _lastSelectMarkBottomAnimation{nullptr}; + QPropertyAnimation* _selectMarkTopAnimation{nullptr}; + QPropertyAnimation* _selectMarkBottomAnimation{nullptr}; + bool _compareItemY(ElaNavigationNode* node1, ElaNavigationNode* node2); +}; + +#endif // ELAFOOTERDELEGATE_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaFooterModel.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaFooterModel.cpp new file mode 100644 index 0000000..3896d52 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaFooterModel.cpp @@ -0,0 +1,68 @@ +#include "ElaFooterModel.h" + +#include + +#include "ElaNavigationNode.h" +ElaFooterModel::ElaFooterModel(QObject* parent) + : QAbstractListModel{parent} +{ + _pSelectedNode = nullptr; +} + +ElaFooterModel::~ElaFooterModel() +{ + qDeleteAll(_footerNodeList); +} + +int ElaFooterModel::rowCount(const QModelIndex& parent) const +{ + return _footerNodeList.count(); +} + +QVariant ElaFooterModel::data(const QModelIndex& index, int role) const +{ + if (role == Qt::UserRole) + { + if (index.row() < _footerNodeList.count()) + { + return QVariant::fromValue(_footerNodeList[index.row()]); + } + } + return QVariant(); +} + +ElaNavigationType::NodeOperateReturnType ElaFooterModel::addFooterNode(QString footerTitle, QString& footerKey, bool isHasFooterPage, int keyPoints, ElaIconType::IconName awesome) +{ + if (_footerNodeList.count() >= 3) + { + return ElaNavigationType::FooterUpperLimit; + } + ElaNavigationNode* node = new ElaNavigationNode(footerTitle); + node->setKeyPoints(keyPoints); + node->setIsFooterNode(true); + node->setIsHasFooterPage(isHasFooterPage); + node->setAwesome(awesome); + footerKey = node->getNodeKey(); + beginResetModel(); + _footerNodeList.append(node); + endResetModel(); + node->setModelIndex(this->index(_footerNodeList.count() - 1)); + return ElaNavigationType::Success; +} + +int ElaFooterModel::getFooterNodeCount() const +{ + return _footerNodeList.count(); +} + +ElaNavigationNode* ElaFooterModel::getNavigationNode(QString footerKey) +{ + for (auto node : _footerNodeList) + { + if (node->getNodeKey() == footerKey) + { + return node; + } + } + return nullptr; +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaFooterModel.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaFooterModel.h new file mode 100644 index 0000000..a164772 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaFooterModel.h @@ -0,0 +1,28 @@ +#ifndef ELAFOOTERMODEL_H +#define ELAFOOTERMODEL_H + +#include + +#include "Def.h" +#include "stdafx.h" +class ElaNavigationNode; +class ElaFooterModel : public QAbstractListModel +{ + Q_OBJECT + Q_PRIVATE_CREATE(ElaNavigationNode*, SelectedNode) +public: + explicit ElaFooterModel(QObject* parent = nullptr); + ~ElaFooterModel(); + ElaNavigationType::NodeOperateReturnType addFooterNode(QString footerTitle, QString& footerKey, bool isHasFooterPage, int keyPoints = 0, ElaIconType::IconName awesome = ElaIconType::None); + int getFooterNodeCount() const; + ElaNavigationNode* getNavigationNode(QString footerKey); + +protected: + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + QVariant data(const QModelIndex& index, int role) const override; + +private: + QList _footerNodeList; +}; + +#endif // ELAFOOTERMODEL_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaIntValidator.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaIntValidator.cpp new file mode 100644 index 0000000..5e5d795 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaIntValidator.cpp @@ -0,0 +1,86 @@ +#include "ElaIntValidator.h" + +ElaIntValidator::ElaIntValidator(QObject* parent) + : QIntValidator{parent} +{ + _pIsHexMode = false; +} + +ElaIntValidator::ElaIntValidator(int bottom, int top, QObject* parent) + : QIntValidator{bottom, top, parent} +{ + _pIsHexMode = false; +} + +ElaIntValidator::~ElaIntValidator() +{ +} + +QValidator::State ElaIntValidator::validate(QString& input, int& pos) const +{ + QString inputCopy = input; + if (_pIsHexMode) + { + inputCopy.remove("#"); + if (!inputCopy.isEmpty()) + { + bool isInt = false; + int value = inputCopy.toInt(&isInt, 16); + if (!isInt) + { + return QValidator::Invalid; + } + if (value < bottom() || value > top()) + { + return QValidator::Invalid; + } + int topLength = QString::number(top(), 16).length(); + if (inputCopy.length() > topLength) + { + return QValidator::Invalid; + } + } + inputCopy.prepend("#"); + } + else + { + if (input.isEmpty()) + { + return QValidator::Intermediate; + } + bool isInt = false; + int value = inputCopy.toInt(&isInt); + if (!isInt) + { + return QValidator::Invalid; + } + if (value < bottom() || value > top()) + { + return QValidator::Invalid; + } + } + input = inputCopy; + return QValidator::Acceptable; +} + +void ElaIntValidator::fixup(QString& input) const +{ + if (_pIsHexMode) + { + QString inputComplete = _completeInput(input, QString::number(top(), 16).length()); + input = QString("#") + inputComplete; + } + else + { + input = QString::number(bottom()); + } +} + +QString ElaIntValidator::_completeInput(QString input, int length) const +{ + while (input.length() < length) + { + input.prepend("0"); + } + return input; +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaIntValidator.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaIntValidator.h new file mode 100644 index 0000000..e1af777 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaIntValidator.h @@ -0,0 +1,23 @@ +#ifndef ELAINTVALIDATOR_H +#define ELAINTVALIDATOR_H + +#include + +#include "stdafx.h" +class ElaIntValidator : public QIntValidator +{ + Q_OBJECT + Q_PRIVATE_CREATE(bool, IsHexMode) +public: + explicit ElaIntValidator(QObject* parent = nullptr); + explicit ElaIntValidator(int bottom, int top, QObject* parent = nullptr); + ~ElaIntValidator(); + + QValidator::State validate(QString& input, int& pos) const override; + void fixup(QString& input) const override; + +private: + QString _completeInput(QString input, int length) const; +}; + +#endif // ELAINTVALIDATOR_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaKeyBinderContainer.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaKeyBinderContainer.cpp new file mode 100644 index 0000000..b2dc6ab --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaKeyBinderContainer.cpp @@ -0,0 +1,144 @@ +#include "ElaKeyBinderContainer.h" +#include "ElaKeyBinder.h" +#include "ElaTheme.h" +#include +#include +#include +#include +ElaKeyBinderContainer::ElaKeyBinderContainer(QWidget* parent) + : QWidget(parent) +{ + _keyBinder = dynamic_cast(parent); + setStyleSheet("#ElaKeyBinderContainer{background-color:transparent;}"); + setFocusPolicy(Qt::StrongFocus); + setMouseTracking(true); + setFixedHeight(140); + QFont textFont = font(); + textFont.setLetterSpacing(QFont::AbsoluteSpacing, 0.5); + textFont.setPixelSize(16); + setFont(textFont); + _themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { + _themeMode = themeMode; + }); +} + +ElaKeyBinderContainer::~ElaKeyBinderContainer() +{ +} + +void ElaKeyBinderContainer::logOrResetHistoryData(bool isLog) +{ + if (isLog) + { + _historyBinderKeyText = _pBinderKeyText; + _historyNativeVirtualBinderKey = _pNativeVirtualBinderKey; + } + else + { + _pBinderKeyText = _historyBinderKeyText; + _pNativeVirtualBinderKey = _historyNativeVirtualBinderKey; + update(); + } +} + +void ElaKeyBinderContainer::saveBinderChanged() +{ + Q_EMIT _keyBinder->binderKeyTextChanged(_pBinderKeyText); + Q_EMIT _keyBinder->nativeVirtualBinderKeyChanged(_pNativeVirtualBinderKey); + if (_pBinderKeyText.isEmpty()) + { + _keyBinder->setText(u8" 按键: " + QString(u8"未绑定") + " "); + } + else + { + _keyBinder->setText(u8" 按键: " + _pBinderKeyText + " "); + } +} + +void ElaKeyBinderContainer::mousePressEvent(QMouseEvent* event) +{ + switch (event->button()) + { + case Qt::LeftButton: + { + _pBinderKeyText = "Mouse1"; + _pNativeVirtualBinderKey = 0x01; + break; + } + case Qt::RightButton: + { + _pBinderKeyText = "Mouse2"; + _pNativeVirtualBinderKey = 0x02; + break; + } + case Qt::MiddleButton: + { + _pBinderKeyText = "Middle"; + _pNativeVirtualBinderKey = 0x04; + break; + } + case Qt::BackButton: + { + _pBinderKeyText = "Back"; + _pNativeVirtualBinderKey = 0x05; + break; + } + case Qt::ForwardButton: + { + _pBinderKeyText = "Forward"; + _pNativeVirtualBinderKey = 0x06; + break; + } + default: + { + break; + } + } + QWidget::mousePressEvent(event); + update(); +} + +void ElaKeyBinderContainer::keyPressEvent(QKeyEvent* event) +{ + if (!event->isAutoRepeat()) + { + _pBinderKeyText = QKeySequence(event->key()).toString(); + _pNativeVirtualBinderKey = event->nativeVirtualKey(); + update(); + } + QWidget::keyPressEvent(event); +} + +void ElaKeyBinderContainer::focusOutEvent(QFocusEvent* event) +{ + QWidget::focusOutEvent(event); + setFocus(); +} + +void ElaKeyBinderContainer::paintEvent(QPaintEvent* event) +{ + QPainter painter(this); + painter.save(); + painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + // 顶部提示绘制 + painter.setPen(ElaThemeColor(_themeMode, BasicText)); + painter.drawText(QRect(20, 20, width(), 50), Qt::AlignLeft | Qt::AlignTop, u8"按下任意按键以进行绑定"); + if (_pBinderKeyText.isEmpty()) + { + painter.restore(); + return; + } + // 中心文字背景绘制 + painter.setPen(ElaThemeColor(_themeMode, BasicBorder)); + painter.setBrush(ElaThemeColor(_themeMode, PrimaryNormal)); + qreal textWidth = fontMetrics().horizontalAdvance(_pBinderKeyText) * 1.3; + qreal textHeight = fontMetrics().height() * 1.2; + textWidth = qMax(textWidth, 35.0); + textHeight = qMax(textHeight, 35.0); + painter.drawRoundedRect(QRectF((width() - textWidth) / 2.0, rect().center().y() + 10, textWidth, textHeight), 5, 5); + // 文字绘制 + painter.setPen(ElaThemeColor(_themeMode, BasicTextInvert)); + painter.drawText(QRectF((width() - textWidth) / 2.0, rect().center().y() + 10, textWidth, textHeight), Qt::AlignCenter, _pBinderKeyText); + painter.restore(); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaKeyBinderContainer.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaKeyBinderContainer.h new file mode 100644 index 0000000..ced6cca --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaKeyBinderContainer.h @@ -0,0 +1,31 @@ +#ifndef ELAWIDGETTOOLS_SRC_DEVELOPERCOMPONENTS_ELAKEYBINDERCONTAINER_H_ +#define ELAWIDGETTOOLS_SRC_DEVELOPERCOMPONENTS_ELAKEYBINDERCONTAINER_H_ + +#include "Def.h" +#include +class ElaKeyBinder; +class ElaKeyBinderContainer : public QWidget +{ + Q_OBJECT + Q_PRIVATE_CREATE(QString, BinderKeyText) + Q_PRIVATE_CREATE(quint32, NativeVirtualBinderKey) +public: + explicit ElaKeyBinderContainer(QWidget* parent = nullptr); + ~ElaKeyBinderContainer(); + void logOrResetHistoryData(bool isLog); + void saveBinderChanged(); + +protected: + virtual void mousePressEvent(QMouseEvent* event) override; + virtual void keyPressEvent(QKeyEvent* event) override; + virtual void focusOutEvent(QFocusEvent* event) override; + virtual void paintEvent(QPaintEvent* event) override; + +private: + QString _historyBinderKeyText; + quint32 _historyNativeVirtualBinderKey{0}; + ElaThemeType::ThemeMode _themeMode; + ElaKeyBinder* _keyBinder{nullptr}; +}; + +#endif//ELAWIDGETTOOLS_SRC_DEVELOPERCOMPONENTS_ELAKEYBINDERCONTAINER_H_ diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaLCDNumberStyle.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaLCDNumberStyle.cpp new file mode 100644 index 0000000..ad1f5cc --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaLCDNumberStyle.cpp @@ -0,0 +1,52 @@ +#include "ElaLCDNumberStyle.h" +#include "ElaTheme.h" + +#include +#include +#include +ElaLCDNumberStyle::ElaLCDNumberStyle(QStyle* style) +{ + _pIsTransparent = false; + _themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { + _themeMode = themeMode; + }); +} + +ElaLCDNumberStyle::~ElaLCDNumberStyle() +{ +} + +void ElaLCDNumberStyle::drawControl(QStyle::ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const +{ + switch (element) + { + case QStyle::CE_ShapedFrame: + { + if (const QStyleOptionFrame* fopt = qstyleoption_cast(option)) + { + if (!_pIsTransparent) + { + // 背景绘制 + QRect numberRect = option->rect; + painter->save(); + painter->setRenderHints(QPainter::Antialiasing); + painter->setPen(Qt::NoPen); + // 边框绘制 + painter->setBrush(ElaThemeColor(_themeMode, BasicBorder)); + painter->drawRoundedRect(numberRect, 6, 6); + + painter->setBrush(ElaThemeColor(_themeMode, BasicBase)); + painter->drawRoundedRect(QRectF(numberRect.x() + 1.5, numberRect.y() + 1.5, numberRect.width() - 3, numberRect.height() - 3), 6, 6); + painter->restore(); + } + } + return; + } + default: + { + break; + } + } + QProxyStyle::drawControl(element, option, painter, widget); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaLCDNumberStyle.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaLCDNumberStyle.h new file mode 100644 index 0000000..8e4dae6 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaLCDNumberStyle.h @@ -0,0 +1,19 @@ +#ifndef ELAWIDGETTOOLS_ELALCDNUMBERSTYLE_H +#define ELAWIDGETTOOLS_ELALCDNUMBERSTYLE_H +#include + +#include "Def.h" +class ElaLCDNumberStyle : public QProxyStyle +{ + Q_OBJECT + Q_PRIVATE_CREATE(bool, IsTransparent) +public: + explicit ElaLCDNumberStyle(QStyle* style = nullptr); + ~ElaLCDNumberStyle(); + void drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget = nullptr) const override; + +private: + ElaThemeType::ThemeMode _themeMode; +}; + +#endif //ELAWIDGETTOOLS_ELALCDNUMBERSTYLE_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaLineEditStyle.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaLineEditStyle.cpp new file mode 100644 index 0000000..1adea60 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaLineEditStyle.cpp @@ -0,0 +1,69 @@ +#include "ElaLineEditStyle.h" + +#include +#include +#include +#include + +#include "ElaTheme.h" +ElaLineEditStyle::ElaLineEditStyle(QStyle* style) +{ + _themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { _themeMode = themeMode; }); +} + +ElaLineEditStyle::~ElaLineEditStyle() +{ +} + +void ElaLineEditStyle::drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const +{ + switch (element) + { + case PE_PanelLineEdit: + { + if (const QStyleOptionFrame* fopt = qstyleoption_cast(option)) + { + QRect lineEditRect = fopt->rect; + painter->save(); + painter->setRenderHints(QPainter::Antialiasing); + painter->setPen(Qt::NoPen); + // 边框绘制 + painter->setBrush(ElaThemeColor(_themeMode, BasicBorder)); + painter->drawRoundedRect(lineEditRect, 6, 6); + // 背景绘制 + if (fopt->state & QStyle::State_HasFocus) + { + painter->setBrush(ElaThemeColor(_themeMode, DialogBase)); + } + else if (fopt->state & QStyle::State_MouseOver) + { + painter->setBrush(ElaThemeColor(_themeMode, BasicHover)); + } + else + { + painter->setBrush(ElaThemeColor(_themeMode, BasicBase)); + } + painter->drawRoundedRect(QRectF(lineEditRect.x() + 1.5, lineEditRect.y() + 1.5, lineEditRect.width() - 3, lineEditRect.height() - 3), 6, 6); + + // 底边线绘制 + painter->setBrush(ElaThemeColor(_themeMode, BasicHemline)); + QPainterPath path; + path.moveTo(6, lineEditRect.height()); + path.lineTo(lineEditRect.width() - 6, lineEditRect.height()); + path.arcTo(QRectF(lineEditRect.width() - 12, lineEditRect.height() - 12, 12, 12), -90, 45); + path.lineTo(6 - 3 * std::sqrt(2), lineEditRect.height() - (6 - 3 * std::sqrt(2))); + path.arcTo(QRectF(0, lineEditRect.height() - 12, 12, 12), 270, 45); + path.closeSubpath(); + painter->drawPath(path); + painter->restore(); + } + return; + } + default: + { + break; + } + } + QProxyStyle::drawPrimitive(element, option, painter, widget); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaLineEditStyle.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaLineEditStyle.h new file mode 100644 index 0000000..c38e37b --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaLineEditStyle.h @@ -0,0 +1,19 @@ +#ifndef ELALINEEDITSTYLE_H +#define ELALINEEDITSTYLE_H + +#include + +#include "Def.h" +class ElaLineEditStyle : public QProxyStyle +{ + Q_OBJECT +public: + explicit ElaLineEditStyle(QStyle* style = nullptr); + ~ElaLineEditStyle(); + void drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget = nullptr) const override; + +private: + ElaThemeType::ThemeMode _themeMode; +}; + +#endif // ELALINEEDITSTYLE_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaListViewStyle.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaListViewStyle.cpp new file mode 100644 index 0000000..f4c446c --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaListViewStyle.cpp @@ -0,0 +1,194 @@ +#include "ElaListViewStyle.h" + +#include +#include +#include + +#include "ElaListView.h" +#include "ElaTheme.h" +ElaListViewStyle::ElaListViewStyle(QStyle* style) +{ + _pItemHeight = 35; + _pIsTransparent = false; + _themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { + _themeMode = themeMode; + }); +} + +ElaListViewStyle::~ElaListViewStyle() +{ +} + +void ElaListViewStyle::drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const +{ + switch (element) + { + case QStyle::PE_PanelItemViewItem: + { + // Item背景 + if (const QStyleOptionViewItem* vopt = qstyleoption_cast(option)) + { + painter->save(); + painter->setRenderHint(QPainter::Antialiasing); + QRect itemRect = vopt->rect; + itemRect.adjust(0, 2, 0, -2); + QPainterPath path; + path.addRoundedRect(itemRect, 4, 4); + if (vopt->state & QStyle::State_Selected) + { + if (vopt->state & QStyle::State_MouseOver) + { + // 选中时覆盖 + painter->fillPath(path, ElaThemeColor(_themeMode, BasicSelectedHoverAlpha)); + } + else + { + // 选中 + painter->fillPath(path, ElaThemeColor(_themeMode, BasicSelectedAlpha)); + } + } + else + { + if (vopt->state & QStyle::State_MouseOver) + { + // 覆盖时颜色 + painter->fillPath(path, ElaThemeColor(_themeMode, BasicHoverAlpha)); + } + } + painter->restore(); + } + return; + } + case QStyle::PE_PanelItemViewRow: + { + // Item背景隔行变色 + if (const QStyleOptionViewItem* vopt = qstyleoption_cast(option)) + { + if (vopt->features == QStyleOptionViewItem::Alternate) + { + painter->save(); + painter->setRenderHint(QPainter::Antialiasing); + painter->setPen(Qt::NoPen); + painter->setBrush(ElaThemeColor(_themeMode, BasicAlternating)); + painter->drawRect(vopt->rect); + painter->restore(); + } + } + return; + } + case QStyle::PE_Widget: + { + return; + } + default: + { + break; + } + } + QProxyStyle::drawPrimitive(element, option, painter, widget); +} + +void ElaListViewStyle::drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const +{ + switch (element) + { + case QStyle::CE_ShapedFrame: + { + // viewport视口外的其他区域背景 + if (!_pIsTransparent) + { + QRect frameRect = option->rect; + frameRect.adjust(1, 1, -1, -1); + painter->save(); + painter->setRenderHints(QPainter::Antialiasing); + painter->setPen(ElaThemeColor(_themeMode, PopupBorder)); + painter->setBrush(ElaThemeColor(_themeMode, BasicBase)); + painter->drawRoundedRect(frameRect, 3, 3); + painter->restore(); + } + return; + } + case QStyle::CE_ItemViewItem: + { + if (const QStyleOptionViewItem* vopt = qstyleoption_cast(option)) + { + // 背景绘制 + this->drawPrimitive(QStyle::PE_PanelItemViewItem, option, painter, widget); + + // 内容绘制 + QRect itemRect = option->rect; + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing); + const ElaListView* listView = dynamic_cast(widget); + QListView::ViewMode viewMode = listView->viewMode(); + // QRect checkRect = proxy()->subElementRect(SE_ItemViewItemCheckIndicator, vopt, widget); + QRect iconRect = proxy()->subElementRect(SE_ItemViewItemDecoration, vopt, widget); + QRect textRect = proxy()->subElementRect(SE_ItemViewItemText, vopt, widget); + iconRect.adjust(_leftPadding, 0, 0, 0); + textRect.adjust(_leftPadding, 0, 0, 0); + // 图标绘制 + if (!vopt->icon.isNull()) + { + QIcon::Mode mode = QIcon::Normal; + if (!(vopt->state.testFlag(QStyle::State_Enabled))) + { + mode = QIcon::Disabled; + } + else if (vopt->state.testFlag(QStyle::State_Selected)) + { + mode = QIcon::Selected; + } + QIcon::State state = vopt->state & QStyle::State_Open ? QIcon::On : QIcon::Off; + vopt->icon.paint(painter, iconRect, vopt->decorationAlignment, mode, state); + } + // 文字绘制 + if (!vopt->text.isEmpty()) + { + painter->setPen(ElaThemeColor(_themeMode, BasicText)); + painter->drawText(textRect, vopt->displayAlignment, vopt->text); + } + // 选中特效 + if (vopt->state.testFlag(QStyle::State_Selected) && viewMode == QListView::ListMode) + { + int heightOffset = itemRect.height() / 4; + painter->setPen(Qt::NoPen); + painter->setBrush(ElaThemeColor(_themeMode, PrimaryNormal)); + painter->drawRoundedRect(QRectF(itemRect.x() + 3, itemRect.y() + heightOffset, 3, itemRect.height() - 2 * heightOffset), 3, 3); + } + painter->restore(); + } + + return; + } + default: + { + break; + } + } + QProxyStyle::drawControl(element, option, painter, widget); +} + +QSize ElaListViewStyle::sizeFromContents(ContentsType type, const QStyleOption* option, const QSize& size, const QWidget* widget) const +{ + switch (type) + { + case QStyle::CT_ItemViewItem: + { + QSize itemSize = QProxyStyle::sizeFromContents(type, option, size, widget); + const ElaListView* listView = dynamic_cast(widget); + QListView::ViewMode viewMode = listView->viewMode(); + if (viewMode == QListView::ListMode) + { + itemSize.setWidth(itemSize.width() + _leftPadding); + } + itemSize.setHeight(_pItemHeight); + return itemSize; + } + default: + { + break; + } + } + return QProxyStyle::sizeFromContents(type, option, size, widget); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaListViewStyle.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaListViewStyle.h new file mode 100644 index 0000000..1ed10c1 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaListViewStyle.h @@ -0,0 +1,24 @@ +#ifndef ELALISTVIEWSTYLE_H +#define ELALISTVIEWSTYLE_H + +#include + +#include "Def.h" +class ElaListViewStyle : public QProxyStyle +{ + Q_OBJECT + Q_PROPERTY_CREATE(int, ItemHeight) + Q_PROPERTY_CREATE(bool, IsTransparent) +public: + explicit ElaListViewStyle(QStyle* style = nullptr); + ~ElaListViewStyle(); + void drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget = nullptr) const override; + void drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget = nullptr) const override; + QSize sizeFromContents(ContentsType type, const QStyleOption* option, const QSize& size, const QWidget* widget) const override; + +private: + ElaThemeType::ThemeMode _themeMode; + int _leftPadding{11}; +}; + +#endif // ELALISTVIEWSTYLE_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaMaskWidget.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaMaskWidget.cpp new file mode 100644 index 0000000..81f62be --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaMaskWidget.cpp @@ -0,0 +1,45 @@ +#include "ElaMaskWidget.h" + +#include +#include +ElaMaskWidget::ElaMaskWidget(QWidget* parent) + : QWidget{parent} +{ + setObjectName("ElaMaskWidget"); + setStyleSheet("#ElaMaskWidget{background-color:transparent;}"); + _pMaskAlpha = 0; +} + +ElaMaskWidget::~ElaMaskWidget() +{ +} + +void ElaMaskWidget::doMaskAnimation(int endValue) +{ + QPropertyAnimation* opacityAnimation = new QPropertyAnimation(this, "pMaskAlpha"); + connect(opacityAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + update(); + }); + connect(opacityAnimation, &QPropertyAnimation::finished, this, [=]() { + if (endValue == 0) + { + setVisible(false); + } + }); + opacityAnimation->setEasingCurve(QEasingCurve::InOutSine); + opacityAnimation->setDuration(250); + opacityAnimation->setStartValue(_pMaskAlpha); + opacityAnimation->setEndValue(endValue); + opacityAnimation->start(QAbstractAnimation::DeleteWhenStopped); +} + +void ElaMaskWidget::paintEvent(QPaintEvent* event) +{ + QPainter painter(this); + painter.save(); + painter.setPen(Qt::NoPen); + painter.setRenderHint(QPainter::Antialiasing); + painter.setBrush(QColor(0x00, 0x00, 0x00, _pMaskAlpha)); + painter.drawRect(rect()); + painter.restore(); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaMaskWidget.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaMaskWidget.h new file mode 100644 index 0000000..8a8b30e --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaMaskWidget.h @@ -0,0 +1,20 @@ +#ifndef ELAMASKWIDGET_H +#define ELAMASKWIDGET_H + +#include + +#include "stdafx.h" +class ElaMaskWidget : public QWidget +{ + Q_OBJECT + Q_PROPERTY_CREATE(int, MaskAlpha) +public: + explicit ElaMaskWidget(QWidget* parent = nullptr); + ~ElaMaskWidget(); + void doMaskAnimation(int endValue); + +protected: + virtual void paintEvent(QPaintEvent* event) override; +}; + +#endif // ELAMASKWIDGET_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaMenuBarStyle.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaMenuBarStyle.cpp new file mode 100644 index 0000000..bcf5c4b --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaMenuBarStyle.cpp @@ -0,0 +1,225 @@ +#include "ElaMenuBarStyle.h" + +#include +#include +#include + +#include "ElaMenuBar.h" +#include "ElaTheme.h" +ElaMenuBarStyle::ElaMenuBarStyle(QStyle* style) +{ + _themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { + _themeMode = themeMode; + }); +} + +ElaMenuBarStyle::~ElaMenuBarStyle() +{ +} + +void ElaMenuBarStyle::drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const +{ + switch (element) + { + case QStyle::PE_PanelButtonTool: + { + return; + } + default: + { + break; + } + } + QProxyStyle::drawPrimitive(element, option, painter, widget); +} + +void ElaMenuBarStyle::drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const +{ + switch (element) + { + case QStyle::CE_ToolButtonLabel: + { + if (const QStyleOptionToolButton* topt = qstyleoption_cast(option)) + { + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + painter->setPen(Qt::NoPen); + if (topt->state.testFlag(QStyle::State_Enabled) && topt->state.testFlag(QStyle::State_MouseOver)) + { + painter->setBrush(ElaThemeColor(_themeMode, BasicHoverAlpha)); + painter->drawRect(topt->rect); + } + //展开图标 + painter->setPen(!topt->state.testFlag(QStyle::State_Enabled) ? Qt::gray : ElaThemeColor(_themeMode, BasicText)); + QFont iconFont = QFont("ElaAwesome"); + iconFont.setPixelSize(18); + painter->setFont(iconFont); + painter->drawText(topt->rect, Qt::AlignCenter, QChar((unsigned short)ElaIconType::AngleRight)); + painter->restore(); + } + return; + } + case QStyle::CE_MenuBarEmptyArea: + { + return; + } + case QStyle::CE_MenuBarItem: + { + if (const QStyleOptionMenuItem* mopt = qstyleoption_cast(option)) + { + if (mopt->menuItemType == QStyleOptionMenuItem::Separator) + { + return; + } + int menuBarHeight = widget->height(); + QRect menuItemRect = mopt->rect; + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + //背景绘制 + painter->setPen(Qt::NoPen); + if (mopt->state.testFlag(QStyle::State_Enabled)) + { + if (mopt->state.testFlag(QStyle::State_Sunken)) + { + painter->setBrush(ElaThemeColor(_themeMode, BasicPressAlpha)); + } + else if (mopt->state.testFlag(QStyle::State_Selected)) + { + painter->setBrush(ElaThemeColor(_themeMode, BasicSelectedAlpha)); + } + painter->drawRoundedRect(menuItemRect, 3, 3); + } + //图标和文字绘制 + QIcon icon = mopt->icon; + QString menuText = mopt->text; + QString iconText; + menuText = menuText.replace("&", ""); + const ElaMenuBar* menuBar = dynamic_cast(widget); + if (menuBar) + { + QAction* action = menuBar->actionAt(menuItemRect.center()); + if (action) + { + iconText = action->property("ElaIconType").toString(); + } + } + if (menuText.isEmpty()) + { + //图标 + if (!iconText.isEmpty()) + { + painter->save(); + painter->setPen(!mopt->state.testFlag(QStyle::State_Enabled) ? ElaThemeColor(_themeMode, BasicTextDisable) : ElaThemeColor(_themeMode, BasicText)); + QFont iconFont = QFont("ElaAwesome"); + iconFont.setPixelSize(menuBarHeight * 0.7); + painter->setFont(iconFont); + painter->drawText(menuItemRect, Qt::AlignCenter, iconText); + painter->restore(); + } + else if (!icon.isNull()) + { + QRect menuIconRect(menuItemRect.center().x() - menuBarHeight * 0.4, menuItemRect.center().y() - menuBarHeight * 0.4, menuBarHeight * 0.8, menuBarHeight * 0.8); + painter->drawPixmap(menuIconRect, icon.pixmap(menuBarHeight * 0.8, menuBarHeight * 0.8)); + } + } + else + { + //图标 + 文字 + painter->setPen(!mopt->state.testFlag(QStyle::State_Enabled) ? ElaThemeColor(_themeMode, BasicTextDisable) : ElaThemeColor(_themeMode, BasicText)); + if (icon.isNull() && iconText.isEmpty()) + { + painter->drawText(menuItemRect, Qt::AlignCenter, menuText); + } + else + { + if (!iconText.isEmpty()) + { + QRectF menuIconRect(menuItemRect.x() + menuBarHeight * 0.1 + _menuBarItemMargin, menuBarHeight * 0.1, menuBarHeight * 0.8, menuBarHeight * 0.8); + painter->save(); + QFont iconFont = QFont("ElaAwesome"); + iconFont.setPixelSize(menuBarHeight * 0.7); + painter->setFont(iconFont); + painter->drawText(menuIconRect, Qt::AlignCenter, iconText); + painter->restore(); + painter->drawText(QRect(menuIconRect.right(), menuItemRect.y(), menuItemRect.width() - menuIconRect.width(), menuBarHeight), Qt::AlignCenter, menuText); + } + else + { + QRect menuIconRect(menuItemRect.x() + menuBarHeight * 0.1 + _menuBarItemMargin, menuBarHeight * 0.1, menuBarHeight * 0.8, menuBarHeight * 0.8); + painter->drawPixmap(menuIconRect, icon.pixmap(menuBarHeight * 0.8, menuBarHeight * 0.8)); + painter->drawText(QRect(menuIconRect.right(), menuItemRect.y(), menuItemRect.width() - menuIconRect.width(), menuBarHeight), Qt::AlignCenter, menuText); + } + } + } + painter->restore(); + } + return; + } + default: + { + break; + } + } + + QProxyStyle::drawControl(element, option, painter, widget); +} + +QSize ElaMenuBarStyle::sizeFromContents(ContentsType type, const QStyleOption* option, const QSize& size, const QWidget* widget) const +{ + switch (type) + { + case QStyle::CT_MenuBar: + { + QSize menuBarHeight = QProxyStyle::sizeFromContents(type, option, size, widget); + menuBarHeight.setHeight(widget->height()); + return menuBarHeight; + } + case QStyle::CT_MenuBarItem: + { + if (const QStyleOptionMenuItem* mopt = qstyleoption_cast(option)) + { + QSize menuBarItemSize = QProxyStyle::sizeFromContents(type, option, size, widget); + menuBarItemSize.setHeight(widget->height()); + if ((!mopt->icon.isNull()) && !mopt->text.isEmpty()) + { + menuBarItemSize.setWidth(menuBarItemSize.width() + mopt->fontMetrics.horizontalAdvance(mopt->text) + 2 * _menuBarItemMargin); + } + if (!mopt->text.contains("&")) + { + menuBarItemSize.setWidth(menuBarItemSize.width() + 5); + } + return menuBarItemSize; + } + } + default: + { + break; + } + } + return QProxyStyle::sizeFromContents(type, option, size, widget); +} + +int ElaMenuBarStyle::pixelMetric(PixelMetric metric, const QStyleOption* option, const QWidget* widget) const +{ + switch (metric) + { + case QStyle::PM_MenuBarHMargin: + { + return 0; + } + case QStyle::PM_MenuBarItemSpacing: + { + return 6; + } + case QStyle::PM_ToolBarExtensionExtent: + { + return 26; + } + default: + { + break; + } + } + return QProxyStyle::pixelMetric(metric, option, widget); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaMenuBarStyle.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaMenuBarStyle.h new file mode 100644 index 0000000..8af06b9 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaMenuBarStyle.h @@ -0,0 +1,23 @@ +#ifndef ELAMENUBARSTYLE_H +#define ELAMENUBARSTYLE_H + +#include + +#include "Def.h" +class ElaMenuBarStyle : public QProxyStyle +{ + Q_OBJECT +public: + explicit ElaMenuBarStyle(QStyle* style = nullptr); + ~ElaMenuBarStyle(); + void drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget = nullptr) const override; + void drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget = nullptr) const override; + QSize sizeFromContents(ContentsType type, const QStyleOption* option, const QSize& size, const QWidget* widget) const override; + int pixelMetric(PixelMetric metric, const QStyleOption* option = nullptr, const QWidget* widget = nullptr) const override; + +private: + ElaThemeType::ThemeMode _themeMode; + int _menuBarItemMargin{0}; +}; + +#endif // ELAMENUBARSTYLE_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaMenuStyle.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaMenuStyle.cpp new file mode 100644 index 0000000..43071b9 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaMenuStyle.cpp @@ -0,0 +1,233 @@ +#include "ElaMenuStyle.h" + +#include +#include +#include +#include + +#include "ElaMenu.h" +#include "ElaTheme.h" +ElaMenuStyle::ElaMenuStyle(QStyle* style) +{ + _pMenuItemHeight = 32; + _themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { + _themeMode = themeMode; + }); +} + +ElaMenuStyle::~ElaMenuStyle() +{ +} + +void ElaMenuStyle::drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const +{ + switch (element) + { + case QStyle::PE_PanelMenu: + { + // 高性能阴影 + painter->save(); + painter->setRenderHint(QPainter::Antialiasing); + eTheme->drawEffectShadow(painter, option->rect, _shadowBorderWidth, 6); + // 背景绘制 + QRect foregroundRect(_shadowBorderWidth, _shadowBorderWidth, option->rect.width() - 2 * _shadowBorderWidth, option->rect.height() - 2 * _shadowBorderWidth); + painter->setPen(ElaThemeColor(_themeMode, PopupBorder)); + painter->setBrush(ElaThemeColor(_themeMode, PopupBase)); + painter->drawRoundedRect(foregroundRect, 6, 6); + painter->restore(); + return; + } + case QStyle::PE_FrameMenu: + { + return; + } + default: + { + break; + } + } + QProxyStyle::drawPrimitive(element, option, painter, widget); +} + +void ElaMenuStyle::drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const +{ + switch (element) + { + case QStyle::CE_MenuItem: + { + //内容绘制 区分类型 + if (const QStyleOptionMenuItem* mopt = qstyleoption_cast(option)) + { + if (mopt->menuItemType == QStyleOptionMenuItem::Separator) + { + QRect separatorRect = mopt->rect; + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + painter->setPen(Qt::NoPen); + painter->setBrush(ElaThemeColor(_themeMode, BasicBaseLine)); + painter->drawRoundedRect(QRectF(separatorRect.x() + separatorRect.width() * 0.055, separatorRect.center().y(), separatorRect.width() - separatorRect.width() * 0.11, 1.5), 1, 1); + painter->restore(); + return; + } + else + { + QRect menuRect = mopt->rect; + qreal contentPadding = menuRect.width() * 0.055; + qreal textLeftSpacing = menuRect.width() * 0.082; + painter->save(); + painter->setRenderHints(QPainter::SmoothPixmapTransform | QPainter::Antialiasing | QPainter::TextAntialiasing); + //覆盖效果 + if (mopt->state.testFlag(QStyle::State_Enabled) && (mopt->state.testFlag(QStyle::State_MouseOver) || mopt->state.testFlag(QStyle::State_Selected))) + { + QRect hoverRect = menuRect; + hoverRect.adjust(0, 2, 0, -2); + painter->setPen(Qt::NoPen); + painter->setBrush(ElaThemeColor(_themeMode, PopupHover)); + painter->drawRoundedRect(hoverRect, 5, 5); + } + //Icon绘制 + QIcon menuIcon = mopt->icon; + //check绘制 + if (mopt->menuHasCheckableItems) + { + painter->save(); + painter->setPen(!mopt->state.testFlag(QStyle::State_Enabled) ? Qt::gray : _themeMode == ElaThemeType::Light ? Qt::black + : Qt::white); + QFont iconFont = QFont("ElaAwesome"); + iconFont.setPixelSize(_pMenuItemHeight * 0.57); + painter->setFont(iconFont); + painter->drawText(QRectF(menuRect.x() + contentPadding, menuRect.y(), _iconWidth, menuRect.height()), Qt::AlignCenter, mopt->checked ? QChar((unsigned short)ElaIconType::Check) : QChar((unsigned short)ElaIconType::None)); + painter->restore(); + } + else + { + QString iconText; + const ElaMenu* menu = dynamic_cast(widget); + if (menu) + { + QAction* action = menu->actionAt(menuRect.center()); + if (action) + { + iconText = action->property("ElaIconType").toString(); + } + } + if (!iconText.isEmpty()) + { + painter->save(); + painter->setPen(!mopt->state.testFlag(QStyle::State_Enabled) ? Qt::gray : _themeMode == ElaThemeType::Light ? Qt::black + : Qt::white); + QFont iconFont = QFont("ElaAwesome"); + iconFont.setPixelSize(_pMenuItemHeight * 0.57); + painter->setFont(iconFont); + painter->drawText(QRectF(menuRect.x() + contentPadding, menuRect.y(), _iconWidth, menuRect.height()), Qt::AlignCenter, iconText); + painter->restore(); + } + else + { + if (!menuIcon.isNull()) + { + painter->drawPixmap(QRect(menuRect.x() + contentPadding, menuRect.center().y() - _iconWidth / 2, _iconWidth, _iconWidth), menuIcon.pixmap(_iconWidth, _iconWidth)); + } + } + } + //文字和快捷键绘制 + if (!mopt->text.isEmpty()) + { + QStringList textList = mopt->text.split("\t"); + painter->setPen(!mopt->state.testFlag(QStyle::State_Enabled) ? Qt::gray : _themeMode == ElaThemeType::Light ? Qt::black + : Qt::white); + + painter->drawText(QRectF(menuRect.x() + (_isAnyoneItemHasIcon ? contentPadding + textLeftSpacing : 0) + _iconWidth, menuRect.y(), menuRect.width(), menuRect.height()), Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, textList[0]); + if (textList.count() > 1) + { + painter->drawText(QRectF(menuRect.x() + contentPadding + _iconWidth + textLeftSpacing, menuRect.y(), menuRect.width() - (contentPadding * 2 + _iconWidth + textLeftSpacing), menuRect.height()), Qt::AlignRight | Qt::AlignVCenter | Qt::TextSingleLine, textList[1]); + } + } + //展开图标 + if (mopt->menuItemType == QStyleOptionMenuItem::SubMenu) + { + painter->save(); + painter->setPen(!mopt->state.testFlag(QStyle::State_Enabled) ? Qt::gray : _themeMode == ElaThemeType::Light ? Qt::black + : Qt::white); + QFont iconFont = QFont("ElaAwesome"); + iconFont.setPixelSize(18); + painter->setFont(iconFont); + painter->drawText(QRect(menuRect.right() - 25, menuRect.y(), 25, menuRect.height()), Qt::AlignVCenter, QChar((unsigned short)ElaIconType::AngleRight)); + painter->restore(); + } + painter->restore(); + } + } + + return; + } + case QStyle::CE_MenuEmptyArea: + { + return; + } + default: + { + break; + } + } + QProxyStyle::drawControl(element, option, painter, widget); +} + +int ElaMenuStyle::pixelMetric(PixelMetric metric, const QStyleOption* option, const QWidget* widget) const +{ + switch (metric) + { + case QStyle::PM_SmallIconSize: + { + //图标宽度 + return _iconWidth; + } + case QStyle::PM_MenuPanelWidth: + { + //外围容器宽度 + return _shadowBorderWidth * 1.5; + } + default: + { + break; + } + } + return QProxyStyle::pixelMetric(metric, option, widget); +} + +QSize ElaMenuStyle::sizeFromContents(ContentsType type, const QStyleOption* option, const QSize& size, const QWidget* widget) const +{ + switch (type) + { + case QStyle::CT_MenuItem: + { + if (const QStyleOptionMenuItem* mopt = qstyleoption_cast(option)) + { + if (mopt->menuItemType == QStyleOptionMenuItem::Separator) + { + break; + } + QSize menuItemSize = QProxyStyle::sizeFromContents(type, option, size, widget); + const ElaMenu* menu = dynamic_cast(widget); + if (menu->isHasIcon() || mopt->menuHasCheckableItems) + { + _isAnyoneItemHasIcon = true; + } + if (menu->isHasChildMenu()) + { + return QSize(menuItemSize.width() + 20, _pMenuItemHeight); + } + else + { + return QSize(menuItemSize.width(), _pMenuItemHeight); + } + } + } + default: + { + break; + } + } + return QProxyStyle::sizeFromContents(type, option, size, widget); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaMenuStyle.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaMenuStyle.h new file mode 100644 index 0000000..8755360 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaMenuStyle.h @@ -0,0 +1,28 @@ +#ifndef ELAMENUSTYLE_H +#define ELAMENUSTYLE_H + +#include + +#include "Def.h" + +class ElaMenuStyle : public QProxyStyle +{ + Q_OBJECT + Q_PROPERTY_CREATE(int, MenuItemHeight); + +public: + explicit ElaMenuStyle(QStyle* style = nullptr); + ~ElaMenuStyle(); + void drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget = nullptr) const override; + void drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget = nullptr) const override; + int pixelMetric(PixelMetric metric, const QStyleOption* option = nullptr, const QWidget* widget = nullptr) const override; + QSize sizeFromContents(ContentsType type, const QStyleOption* option, const QSize& size, const QWidget* widget) const override; + +private: + mutable bool _isAnyoneItemHasIcon{false}; + int _shadowBorderWidth{6}; + ElaThemeType::ThemeMode _themeMode; + int _iconWidth{22}; +}; + +#endif // ELAMENUSTYLE_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaMicaBaseInitObject.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaMicaBaseInitObject.cpp new file mode 100644 index 0000000..0697411 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaMicaBaseInitObject.cpp @@ -0,0 +1,71 @@ +#include "ElaMicaBaseInitObject.h" + +#include + +#include "ElaApplicationPrivate.h" +#include "ElaExponentialBlur.h" +ElaMicaBaseInitObject::ElaMicaBaseInitObject(ElaApplicationPrivate* appPrivate, QObject* parent) + : QObject{parent} +{ + _appPrivate = appPrivate; +} + +ElaMicaBaseInitObject::~ElaMicaBaseInitObject() +{ +} + +void ElaMicaBaseInitObject::onInitMicaBase(QImage img) +{ + // QColorDialog + // 统一处理为1920*1080以节省空间 + img = img.scaled(QSize(1920, 1080), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + + QImage blurImage = ElaExponentialBlur::doExponentialBlur(img, 500).toImage(); + QImage lightImage = blurImage; + QImage darkImage = blurImage; + QColor lightMixColor = QColor(0xF3, 0xF3, 0xF3); + lightMixColor = lightMixColor.toHsv(); + QColor darkMixColor = QColor(0x20, 0x20, 0x20); + darkMixColor = darkMixColor.toHsv(); + QColor originColor; + QColor lightColor; + QColor darkColor; + int h, s, v; + for (int y = 0; y < blurImage.height(); y++) + { + QRgb* line = (QRgb*)blurImage.scanLine(y); + for (int x = 0; x < blurImage.width(); x++) + { + originColor = QColor(line[x]); + originColor = originColor.toHsv(); + h = originColor.hsvHue(); + s = originColor.hsvSaturation(); + v = originColor.value(); + if (s / 20 > 11) + { + lightColor.setHsv(h, (s / 20 + 11) / 2, 250); + } + else + { + lightColor.setHsv(h, 11, 250); + } + lightColor = lightColor.toRgb(); + if (v / 1.1 > 40) + { + darkColor.setHsv(h, s / 2, (v / 1.1 + 40) / 2); + } + else + { + darkColor.setHsv(h, s / 2, 40); + } + darkColor = darkColor.toRgb(); + lightImage.setPixel(x, y, qRgb(lightColor.red(), lightColor.green(), lightColor.blue())); + darkImage.setPixel(x, y, qRgb(darkColor.red(), darkColor.green(), darkColor.blue())); + } + } + _appPrivate->_lightBaseImage = lightImage.copy(); + _appPrivate->_darkBaseImage = darkImage.copy(); + // _appPrivate->_lightBaseImage.save("light.png", "PNG"); + // _appPrivate->_darkBaseImage.save("dark.png", "PNG"); + Q_EMIT initFinished(); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaMicaBaseInitObject.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaMicaBaseInitObject.h new file mode 100644 index 0000000..b033507 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaMicaBaseInitObject.h @@ -0,0 +1,20 @@ +#ifndef ELAMICABASEINITOBJECT_H +#define ELAMICABASEINITOBJECT_H + +#include +class ElaApplicationPrivate; +class ElaMicaBaseInitObject : public QObject +{ + Q_OBJECT +public: + explicit ElaMicaBaseInitObject(ElaApplicationPrivate* appPrivate, QObject* parent = nullptr); + ~ElaMicaBaseInitObject(); + Q_SLOT void onInitMicaBase(QImage img); +Q_SIGNALS: + Q_SIGNAL void initFinished(); + +private: + ElaApplicationPrivate* _appPrivate{nullptr}; +}; + +#endif // ELAMICABASEINITOBJECT_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaNavigationModel.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaNavigationModel.cpp new file mode 100644 index 0000000..6321f71 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaNavigationModel.cpp @@ -0,0 +1,284 @@ +#include "ElaNavigationModel.h" + +#include +#include + +#include "ElaNavigationNode.h" +ElaNavigationModel::ElaNavigationModel(QObject* parent) + : QAbstractItemModel{parent} +{ + _rootNode = new ElaNavigationNode("root"); + _rootNode->setIsRootNode(true); + _rootNode->setIsExpanderNode(true); + _pSelectedNode = nullptr; + _pSelectedExpandedNode = nullptr; +} + +ElaNavigationModel::~ElaNavigationModel() +{ + delete _rootNode; +} + +QModelIndex ElaNavigationModel::parent(const QModelIndex& child) const +{ + if (!child.isValid()) + { + return QModelIndex(); + } + ElaNavigationNode* childNode = static_cast(child.internalPointer()); + ElaNavigationNode* parentNode = childNode->getParentNode(); + if (parentNode == _rootNode) + { + return QModelIndex(); + } + else if (parentNode == nullptr) + { + return QModelIndex(); + } + return createIndex(parentNode->getRow(), 0, parentNode); +} + +QModelIndex ElaNavigationModel::index(int row, int column, const QModelIndex& parent) const +{ + if (!hasIndex(row, column, parent)) + { + return QModelIndex(); + } + ElaNavigationNode* parentNode; + if (!parent.isValid()) + { + parentNode = _rootNode; + } + else + { + parentNode = static_cast(parent.internalPointer()); + } + ElaNavigationNode* childNode = nullptr; + if (parentNode->getChildrenNodes().count() > row) + { + childNode = parentNode->getChildrenNodes().at(row); + } + if (childNode) + { + return createIndex(row, column, childNode); + } + return QModelIndex(); +} + +int ElaNavigationModel::rowCount(const QModelIndex& parent) const +{ + ElaNavigationNode* parentNode; + if (parent.column() > 0) + { + return 0; + } + if (!parent.isValid()) + { + parentNode = _rootNode; + } + else + { + parentNode = static_cast(parent.internalPointer()); + } + return parentNode->getChildrenNodes().count(); +}; + +int ElaNavigationModel::columnCount(const QModelIndex& parent) const +{ + Q_UNUSED(parent) + return 1; +} + +QVariant ElaNavigationModel::data(const QModelIndex& index, int role) const +{ + Q_UNUSED(index) + Q_UNUSED(role) + return QVariant(); +} + +ElaNavigationType::NodeOperateReturnType ElaNavigationModel::addExpanderNode(QString expanderTitle, QString& expanderKey, ElaIconType::IconName awesome) +{ + ElaNavigationNode* node = new ElaNavigationNode(expanderTitle, _rootNode); + node->setDepth(1); + node->setIsVisible(true); + node->setIsExpanderNode(true); + node->setAwesome(awesome); + beginInsertRows(QModelIndex(), _rootNode->getChildrenNodes().count(), _rootNode->getChildrenNodes().count()); + _rootNode->appendChildNode(node); + _nodesMap.insert(node->getNodeKey(), node); + endInsertRows(); + expanderKey = node->getNodeKey(); + return ElaNavigationType::NodeOperateReturnType::Success; +} + +ElaNavigationType::NodeOperateReturnType ElaNavigationModel::addExpanderNode(QString expanderTitle, QString& expanderKey, QString targetExpanderKey, ElaIconType::IconName awesome) +{ + if (!_nodesMap.contains(targetExpanderKey)) + { + return ElaNavigationType::NodeOperateReturnType::TargetNodeInvalid; + } + ElaNavigationNode* parentNode = _nodesMap.value(targetExpanderKey); + if (!parentNode->getIsExpanderNode()) + { + return ElaNavigationType::NodeOperateReturnType::TargetNodeTypeError; + } + if (parentNode->getDepth() > 10) + { + return ElaNavigationType::NodeOperateReturnType::TargetNodeDepthLimit; + } + ElaNavigationNode* node = new ElaNavigationNode(expanderTitle, parentNode); + node->setDepth(parentNode->getDepth() + 1); + node->setIsExpanderNode(true); + node->setAwesome(awesome); + if (parentNode->getIsVisible() && parentNode->getIsExpanded()) + { + node->setIsVisible(true); + } + beginInsertRows(parentNode->getModelIndex(), parentNode->getChildrenNodes().count(), parentNode->getChildrenNodes().count()); + parentNode->appendChildNode(node); + _nodesMap.insert(node->getNodeKey(), node); + endInsertRows(); + expanderKey = node->getNodeKey(); + return ElaNavigationType::NodeOperateReturnType::Success; +} + +ElaNavigationType::NodeOperateReturnType ElaNavigationModel::addPageNode(QString pageTitle, QString& pageKey, ElaIconType::IconName awesome) +{ + ElaNavigationNode* node = new ElaNavigationNode(pageTitle, _rootNode); + node->setAwesome(awesome); + node->setDepth(1); + node->setIsVisible(true); + beginInsertRows(QModelIndex(), _rootNode->getChildrenNodes().count(), _rootNode->getChildrenNodes().count()); + _rootNode->appendChildNode(node); + _nodesMap.insert(node->getNodeKey(), node); + endInsertRows(); + pageKey = node->getNodeKey(); + if (!_pSelectedNode) + { + _pSelectedNode = node; + } + return ElaNavigationType::NodeOperateReturnType::Success; +} + +ElaNavigationType::NodeOperateReturnType ElaNavigationModel::addPageNode(QString pageTitle, QString& pageKey, QString targetExpanderKey, ElaIconType::IconName awesome) +{ + if (!_nodesMap.contains(targetExpanderKey)) + { + return ElaNavigationType::NodeOperateReturnType::TargetNodeInvalid; + } + ElaNavigationNode* parentNode = _nodesMap.value(targetExpanderKey); + if (!parentNode->getIsExpanderNode()) + { + return ElaNavigationType::NodeOperateReturnType::TargetNodeTypeError; + } + if (parentNode->getDepth() > 10) + { + return ElaNavigationType::NodeOperateReturnType::TargetNodeDepthLimit; + } + ElaNavigationNode* node = new ElaNavigationNode(pageTitle, parentNode); + node->setDepth(parentNode->getDepth() + 1); + node->setAwesome(awesome); + if (parentNode->getIsVisible() && parentNode->getIsExpanded()) + { + node->setIsVisible(true); + } + beginInsertRows(parentNode->getModelIndex(), parentNode->getChildrenNodes().count(), parentNode->getChildrenNodes().count()); + parentNode->appendChildNode(node); + _nodesMap.insert(node->getNodeKey(), node); + endInsertRows(); + pageKey = node->getNodeKey(); + if (!_pSelectedNode) + { + _pSelectedNode = node; + } + return ElaNavigationType::NodeOperateReturnType::Success; +} + +ElaNavigationType::NodeOperateReturnType ElaNavigationModel::addPageNode(QString pageTitle, QString& pageKey, int keyPoints, ElaIconType::IconName awesome) +{ + ElaNavigationNode* node = new ElaNavigationNode(pageTitle, _rootNode); + node->setAwesome(awesome); + node->setDepth(1); + node->setIsVisible(true); + node->setKeyPoints(keyPoints); + beginInsertRows(QModelIndex(), _rootNode->getChildrenNodes().count(), _rootNode->getChildrenNodes().count()); + _rootNode->appendChildNode(node); + _nodesMap.insert(node->getNodeKey(), node); + endInsertRows(); + pageKey = node->getNodeKey(); + if (!_pSelectedNode) + { + _pSelectedNode = node; + } + return ElaNavigationType::NodeOperateReturnType::Success; +} + +ElaNavigationType::NodeOperateReturnType ElaNavigationModel::addPageNode(QString pageTitle, QString& pageKey, QString targetExpanderKey, int keyPoints, ElaIconType::IconName awesome) +{ + if (!_nodesMap.contains(targetExpanderKey)) + { + return ElaNavigationType::NodeOperateReturnType::TargetNodeInvalid; + } + ElaNavigationNode* parentNode = _nodesMap.value(targetExpanderKey); + if (!parentNode->getIsExpanderNode()) + { + return ElaNavigationType::NodeOperateReturnType::TargetNodeTypeError; + } + if (parentNode->getDepth() > 10) + { + return ElaNavigationType::NodeOperateReturnType::TargetNodeDepthLimit; + } + ElaNavigationNode* node = new ElaNavigationNode(pageTitle, parentNode); + node->setDepth(parentNode->getDepth() + 1); + node->setAwesome(awesome); + node->setKeyPoints(keyPoints); + if (parentNode->getIsVisible() && parentNode->getIsExpanded()) + { + node->setIsVisible(true); + } + beginInsertRows(parentNode->getModelIndex(), parentNode->getChildrenNodes().count(), parentNode->getChildrenNodes().count()); + parentNode->appendChildNode(node); + _nodesMap.insert(node->getNodeKey(), node); + endInsertRows(); + pageKey = node->getNodeKey(); + if (!_pSelectedNode) + { + _pSelectedNode = node; + } + return ElaNavigationType::NodeOperateReturnType::Success; +} + +ElaNavigationNode* ElaNavigationModel::getNavigationNode(QString nodeKey) const +{ + if (_nodesMap.contains(nodeKey)) + { + return _nodesMap.value(nodeKey); + } + return nullptr; +} + +QList ElaNavigationModel::getRootExpanderNodes() const +{ + QList expandedNodeList; + for (auto node : _rootNode->getChildrenNodes()) + { + if (node->getIsExpanderNode()) + { + expandedNodeList.append(node); + } + } + return expandedNodeList; +} + +QList ElaNavigationModel::getRootExpandedNodes() const +{ + QList expandedNodeList; + for (auto node : _rootNode->getChildrenNodes()) + { + if (node->getIsExpanderNode() && node->getIsExpanded()) + { + expandedNodeList.append(node); + } + } + return expandedNodeList; +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaNavigationModel.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaNavigationModel.h new file mode 100644 index 0000000..9017105 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaNavigationModel.h @@ -0,0 +1,40 @@ +#ifndef ELANAVIGATIONMODEL_H +#define ELANAVIGATIONMODEL_H + +#include +#include + +#include "Def.h" +#include "stdafx.h" +class ElaNavigationNode; +class ElaNavigationModel : public QAbstractItemModel +{ + Q_OBJECT + Q_PRIVATE_CREATE(ElaNavigationNode*, SelectedNode) + Q_PRIVATE_CREATE(ElaNavigationNode*, SelectedExpandedNode) + +public: + explicit ElaNavigationModel(QObject* parent = nullptr); + ~ElaNavigationModel(); + QModelIndex parent(const QModelIndex& child) const override; + QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + int columnCount(const QModelIndex& parent = QModelIndex()) const override; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + ElaNavigationType::NodeOperateReturnType addExpanderNode(QString expanderTitle, QString& expanderKey, ElaIconType::IconName awesome); + ElaNavigationType::NodeOperateReturnType addExpanderNode(QString expanderTitle, QString& expanderKey, QString targetExpanderKey, ElaIconType::IconName awesome); + ElaNavigationType::NodeOperateReturnType addPageNode(QString pageTitle, QString& pageKey, ElaIconType::IconName awesome); + ElaNavigationType::NodeOperateReturnType addPageNode(QString pageTitle, QString& pageKey, QString targetExpanderKey, ElaIconType::IconName awesome); + ElaNavigationType::NodeOperateReturnType addPageNode(QString pageTitle, QString& pageKey, int keyPoints, ElaIconType::IconName awesome); + ElaNavigationType::NodeOperateReturnType addPageNode(QString pageTitle, QString& pageKey, QString targetExpanderKey, int keyPoints, ElaIconType::IconName awesome); + + ElaNavigationNode* getNavigationNode(QString nodeKey) const; + QList getRootExpanderNodes() const; + QList getRootExpandedNodes() const; + +private: + mutable QMap _nodesMap; + ElaNavigationNode* _rootNode{nullptr}; +}; + +#endif // ELANAVIGATIONMODEL_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaNavigationNode.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaNavigationNode.cpp new file mode 100644 index 0000000..949ed95 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaNavigationNode.cpp @@ -0,0 +1,166 @@ +#include "ElaNavigationNode.h" + +#include + +ElaNavigationNode::ElaNavigationNode(QString nodeTitle, ElaNavigationNode* parent) + : QObject(parent) +{ + _pDepth = 0; + _pKeyPoints = 0; + _nodeKey = QUuid::createUuid().toString().remove("{").remove("}").remove("-"); + _nodeTitle = nodeTitle; + _pIsRootNode = false; + _pIsFooterNode = false; + _pIsHasFooterPage = false; + _pParentNode = parent; + _pIsExpanderNode = false; + _pIsVisible = false; +} + +ElaNavigationNode::~ElaNavigationNode() +{ + qDeleteAll(_pChildrenNodes); +} + +QString ElaNavigationNode::getNodeKey() const +{ + return _nodeKey; +} + +QString ElaNavigationNode::getNodeTitle() const +{ + return _nodeTitle; +} + +void ElaNavigationNode::setIsExpanded(bool isExpanded) +{ + _isExpanded = isExpanded; + setChildVisible(isExpanded); +} + +bool ElaNavigationNode::getIsExpanded() const +{ + return _isExpanded; +} + +void ElaNavigationNode::setChildVisible(bool isVisible) +{ + if (isVisible) + { + for (auto node : _pChildrenNodes) + { + node->setIsVisible(isVisible); + if (node->getIsExpanderNode() && !node->getIsExpanded()) + { + continue; + } + node->setChildVisible(isVisible); + } + } + else + { + for (auto node : _pChildrenNodes) + { + node->setChildVisible(isVisible); + node->setIsVisible(isVisible); + } + } +} + +bool ElaNavigationNode::getIsHasChild() const +{ + if (_pChildrenNodes.count() > 0) + { + return true; + } + return false; +} + +bool ElaNavigationNode::getIsHasPageChild() const +{ + if (_pChildrenNodes.count() == 0) + { + return false; + } + for (auto childNode : _pChildrenNodes) + { + if (!childNode->getIsExpanderNode()) + { + return true; + } + if (childNode->getIsHasPageChild()) + { + return true; + } + } + return false; +} + +void ElaNavigationNode::appendChildNode(ElaNavigationNode* childNode) +{ + if (_pIsExpanderNode) + { + _pChildrenNodes.append(childNode); + } +} + +bool ElaNavigationNode::getIsChildHasKeyPoints() const +{ + for (auto childNnode : _pChildrenNodes) + { + if (childNnode->getKeyPoints()) + { + return true; + } + if (childNnode->getIsChildHasKeyPoints()) + { + return true; + } + } + return false; +} + +ElaNavigationNode* ElaNavigationNode::getOriginalNode() +{ + if (this->getParentNode()->getIsRootNode()) + { + return this; + } + else + { + ElaNavigationNode* originalNode = this->getParentNode(); + while (!originalNode->getIsRootNode() && !originalNode->getParentNode()->getIsRootNode()) + { + originalNode = originalNode->getParentNode(); + } + return originalNode; + } +} + +bool ElaNavigationNode::getIsChildNode(ElaNavigationNode* node) +{ + if (_pChildrenNodes.count() > 0) + { + if (_pChildrenNodes.contains(node)) + { + return true; + } + for (auto childNnode : _pChildrenNodes) + { + if (childNnode->getIsChildNode(node)) + { + return true; + } + } + } + return false; +} + +int ElaNavigationNode::getRow() const +{ + if (_pParentNode) + { + return _pParentNode->getChildrenNodes().indexOf(const_cast(this)); + } + return 0; +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaNavigationNode.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaNavigationNode.h new file mode 100644 index 0000000..9854297 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaNavigationNode.h @@ -0,0 +1,52 @@ +#ifndef ELANAVIGATIONNODE_H +#define ELANAVIGATIONNODE_H + +#include +#include + +#include "Def.h" + +class ElaNavigationNode : public QObject +{ + Q_OBJECT + Q_PROPERTY_CREATE(QList, ChildrenNodes) + Q_PRIVATE_CREATE(ElaNavigationNode*, ParentNode) + Q_PROPERTY_CREATE(ElaIconType::IconName, Awesome) + Q_PROPERTY_CREATE(QModelIndex, ModelIndex) + Q_PROPERTY_CREATE(int, KeyPoints) + Q_PROPERTY_CREATE(int, Depth) + Q_PROPERTY_CREATE(bool, IsRootNode) + Q_PROPERTY_CREATE(bool, IsFooterNode) + Q_PROPERTY_CREATE(bool, IsHasFooterPage) + Q_PROPERTY_CREATE(bool, IsExpanderNode) + Q_PROPERTY_CREATE(bool, IsVisible) +public: + explicit ElaNavigationNode(QString nodeTitle, ElaNavigationNode* parent = nullptr); + ~ElaNavigationNode(); + + QString getNodeKey() const; + QString getNodeTitle() const; + + void setIsExpanded(bool isExpanded); + bool getIsExpanded() const; + + void setChildVisible(bool isVisible); + bool getIsHasChild() const; + bool getIsHasPageChild() const; + + void appendChildNode(ElaNavigationNode* childNode); + + bool getIsChildHasKeyPoints() const; + + ElaNavigationNode* getOriginalNode(); + bool getIsChildNode(ElaNavigationNode* node); + + int getRow() const; + +private: + QString _nodeKey = ""; + QString _nodeTitle = ""; + bool _isExpanded{false}; +}; + +#endif // ELANAVIGATIONNODE_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaNavigationStyle.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaNavigationStyle.cpp new file mode 100644 index 0000000..8123c15 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaNavigationStyle.cpp @@ -0,0 +1,519 @@ +#include "ElaNavigationStyle.h" + +#include +#include +#include +#include + +#include "ElaNavigationModel.h" +#include "ElaNavigationNode.h" +#include "ElaNavigationView.h" +#include "ElaTheme.h" +ElaNavigationStyle::ElaNavigationStyle(QStyle* style) +{ + _pOpacity = 1; + _pItemHeight = 40; + _pLastSelectMarkTop = 10.0; + _pLastSelectMarkBottom = 10.0; + _pSelectMarkTop = 10.0; + _pSelectMarkBottom = 10.0; + + // Mark向上 + _lastSelectMarkTopAnimation = new QPropertyAnimation(this, "pLastSelectMarkTop"); + connect(_lastSelectMarkTopAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { _pNavigationView->viewport()->update(); }); + _lastSelectMarkTopAnimation->setDuration(300); + _lastSelectMarkTopAnimation->setEasingCurve(QEasingCurve::InOutSine); + + _selectMarkBottomAnimation = new QPropertyAnimation(this, "pSelectMarkBottom"); + connect(_selectMarkBottomAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { _pNavigationView->viewport()->update(); }); + _selectMarkBottomAnimation->setDuration(300); + _selectMarkBottomAnimation->setEasingCurve(QEasingCurve::InOutSine); + connect(_lastSelectMarkTopAnimation, &QPropertyAnimation::finished, this, [=]() { + _isSelectMarkDisplay = true; + _lastSelectedNode = nullptr; + _selectMarkBottomAnimation->setStartValue(0); + _selectMarkBottomAnimation->setEndValue(10); + _selectMarkBottomAnimation->start(); }); + + // Mark向下 + _lastSelectMarkBottomAnimation = new QPropertyAnimation(this, "pLastSelectMarkBottom"); + connect(_lastSelectMarkBottomAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { _pNavigationView->viewport()->update(); }); + _lastSelectMarkBottomAnimation->setDuration(300); + _lastSelectMarkBottomAnimation->setEasingCurve(QEasingCurve::InOutSine); + + _selectMarkTopAnimation = new QPropertyAnimation(this, "pSelectMarkTop"); + connect(_selectMarkTopAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { _pNavigationView->viewport()->update(); }); + _selectMarkTopAnimation->setDuration(300); + _selectMarkTopAnimation->setEasingCurve(QEasingCurve::InOutSine); + connect(_lastSelectMarkBottomAnimation, &QPropertyAnimation::finished, this, [=]() { + _isSelectMarkDisplay = true; + _lastSelectedNode = nullptr; + _selectMarkTopAnimation->setStartValue(0); + _selectMarkTopAnimation->setEndValue(10); + _selectMarkTopAnimation->start(); }); + _themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { + _themeMode = themeMode; + }); +} + +ElaNavigationStyle::~ElaNavigationStyle() +{ +} + +void ElaNavigationStyle::drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const +{ + switch (element) + { + case QStyle::PE_PanelItemViewItem: + { + // Item背景 + if (const QStyleOptionViewItem* vopt = qstyleoption_cast(option)) + { + painter->save(); + QModelIndex index = vopt->index; + ElaNavigationNode* node = static_cast(index.internalPointer()); + if (this->_opacityAnimationTargetNode && node->getParentNode() == this->_opacityAnimationTargetNode) + { + painter->setOpacity(_pOpacity); + } + painter->setRenderHint(QPainter::Antialiasing); + QRect itemRect = vopt->rect; + itemRect.setTop(itemRect.top() + 2); + itemRect.setBottom(itemRect.bottom() - 2); + QPainterPath path; + path.addRoundedRect(itemRect, 8, 8); + if (vopt->state & QStyle::State_Selected) + { + if (index == _pPressIndex) + { + // 选中时点击 + painter->fillPath(path, ElaThemeColor(_themeMode, BasicHoverAlpha)); + } + else + { + if (vopt->state & QStyle::State_MouseOver) + { + // 选中时覆盖 + painter->fillPath(path, ElaThemeColor(_themeMode, BasicSelectedHoverAlpha)); + } + else + { + // 选中 + painter->fillPath(path, ElaThemeColor(_themeMode, BasicSelectedAlpha)); + } + } + } + else + { + if (index == _pPressIndex) + { + // 点击时颜色 + painter->fillPath(path, ElaThemeColor(_themeMode, BasicSelectedHoverAlpha)); + } + else + { + if (vopt->state & QStyle::State_MouseOver) + { + // 覆盖时颜色 + painter->fillPath(path, ElaThemeColor(_themeMode, BasicHoverAlpha)); + } + } + } + painter->restore(); + } + return; + } + case QStyle::PE_PanelItemViewRow: + { + // 行背景 + return; + } + case QStyle::PE_IndicatorBranch: + { + // Branch指示器 + return; + } + default: + { + break; + } + } + QProxyStyle::drawPrimitive(element, option, painter, widget); +} + +void ElaNavigationStyle::drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const +{ + switch (element) + { + case QStyle::CE_ShapedFrame: + { + // viewport视口外的其他区域背景 + return; + } + case QStyle::CE_ItemViewItem: + { + if (const QStyleOptionViewItem* vopt = qstyleoption_cast(option)) + { + // 背景绘制 + this->drawPrimitive(QStyle::PE_PanelItemViewItem, option, painter, widget); + + // 内容绘制 + QRect itemRect = option->rect; + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing); + ElaNavigationNode* node = static_cast(vopt->index.internalPointer()); + ElaNavigationModel* model = dynamic_cast(const_cast(vopt->index.model())); + + if (this->_opacityAnimationTargetNode && node->getParentNode() == this->_opacityAnimationTargetNode) + { + painter->setOpacity(_pOpacity); + } + + // 选中特效 + if (_isSelectMarkDisplay && (node == model->getSelectedNode() || node == model->getSelectedExpandedNode())) + { + painter->setPen(Qt::NoPen); + painter->setBrush(ElaThemeColor(_themeMode, PrimaryNormal)); + painter->drawRoundedRect(QRectF(itemRect.x() + 3, itemRect.y() + _pSelectMarkTop, 3, itemRect.height() - _pSelectMarkTop - _pSelectMarkBottom), 3, 3); + } + if (node == _lastSelectedNode) + { + painter->setPen(Qt::NoPen); + painter->setBrush(ElaThemeColor(_themeMode, PrimaryNormal)); + painter->drawRoundedRect(QRectF(itemRect.x() + 3, itemRect.y() + _pLastSelectMarkTop, 3, itemRect.height() - _pLastSelectMarkTop - _pLastSelectMarkBottom), 3, 3); + } + + // 图标绘制 + painter->setPen(vopt->index == _pPressIndex ? ElaThemeColor(_themeMode, BasicTextPress) : ElaThemeColor(_themeMode, BasicText)); + if (node->getAwesome() != ElaIconType::None) + { + painter->save(); + QFont iconFont = QFont("ElaAwesome"); + iconFont.setPixelSize(17); + painter->setFont(iconFont); + painter->drawText(QRect(itemRect.x(), itemRect.y(), _iconAreaWidth, itemRect.height()), Qt::AlignCenter, QChar((unsigned short)node->getAwesome())); + painter->restore(); + } + + int viewWidth = widget->width(); + // 文字绘制 + painter->setPen(vopt->index == _pPressIndex ? ElaThemeColor(_themeMode, BasicTextPress) : ElaThemeColor(_themeMode, BasicText)); + QRect textRect; + if (node->getAwesome() != ElaIconType::None) + { + textRect = QRect(itemRect.x() + _iconAreaWidth, itemRect.y(), itemRect.width() - _textRightSpacing - _indicatorIconAreaWidth - _iconAreaWidth, itemRect.height()); + } + else + { + textRect = QRect(itemRect.x() + _leftPadding, itemRect.y(), itemRect.width() - _textRightSpacing - _indicatorIconAreaWidth - _leftPadding, itemRect.height()); + } + QString text = painter->fontMetrics().elidedText(node->getNodeTitle(), Qt::ElideRight, textRect.width()); + painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, text); + + if (viewWidth > 260) + { + // 展开图标 KeyPoints + if (node->getIsExpanderNode()) + { + if (node->getIsHasChild()) + { + QRectF expandIconRect(itemRect.right() - _indicatorIconAreaWidth, itemRect.y(), 17, itemRect.height()); + + painter->save(); + QFont iconFont = QFont("ElaAwesome"); + iconFont.setPixelSize(17); + painter->setFont(iconFont); + painter->translate(expandIconRect.x() + (qreal)expandIconRect.width() / 2, expandIconRect.y() + (qreal)expandIconRect.height() / 2); + if (node == _expandAnimationTargetNode) + { + painter->rotate(_pRotate); + } + else + { + if (node->getIsExpanded()) + { + //展开 + painter->rotate(-180); + } + else + { + // 未展开 + painter->rotate(0); + } + } + painter->translate(-expandIconRect.x() - (qreal)expandIconRect.width() / 2 + 1, -expandIconRect.y() - (qreal)expandIconRect.height() / 2); + painter->drawText(expandIconRect, Qt::AlignVCenter, QChar((unsigned short)ElaIconType::AngleDown)); + painter->restore(); + } + if (node->getIsChildHasKeyPoints()) + { + painter->save(); + painter->setPen(Qt::NoPen); + painter->setBrush(ElaThemeColor(_themeMode, PrimaryNormal)); + painter->drawEllipse(QPoint(itemRect.right() - 17, itemRect.y() + 12), 3, 3); + painter->restore(); + } + } + else + { + int keyPoints = node->getKeyPoints(); + if (keyPoints) + { + // KeyPoints + painter->save(); + painter->setPen(Qt::NoPen); + painter->setBrush(Qt::white); + painter->drawEllipse(QPoint(itemRect.right() - 26, itemRect.y() + itemRect.height() / 2), 10, 10); + painter->setBrush(ElaThemeColor(_themeMode, StatusDanger)); + painter->drawEllipse(QPoint(itemRect.right() - 26, itemRect.y() + itemRect.height() / 2), 9, 9); + painter->setPen(QPen(Qt::white, 2)); + QFont font = painter->font(); + font.setBold(true); + if (keyPoints > 99) + { + keyPoints = 99; + } + if (keyPoints > 9) + { + font.setPixelSize(11); + } + else + { + font.setPixelSize(12); + } + painter->setFont(font); + painter->drawText(keyPoints > 9 ? itemRect.right() - 33 : itemRect.right() - 30, itemRect.y() + itemRect.height() / 2 + 4, QString::number(keyPoints)); + painter->restore(); + } + } + } + painter->restore(); + } + + return; + } + default: + { + break; + } + } + QProxyStyle::drawControl(element, option, painter, widget); +} + +QSize ElaNavigationStyle::sizeFromContents(ContentsType type, const QStyleOption* option, const QSize& size, const QWidget* widget) const +{ + switch (type) + { + case QStyle::CT_ItemViewItem: + { + QSize itemSize = QProxyStyle::sizeFromContents(type, option, size, widget); + itemSize.setHeight(_pItemHeight); + return itemSize; + } + default: + { + break; + } + } + return QProxyStyle::sizeFromContents(type, option, size, widget); +} + +void ElaNavigationStyle::navigationNodeStateChange(QVariantMap data) +{ + if (data.contains("Expand")) + { + ElaNavigationNode* lastExpandNode = _expandAnimationTargetNode; + _opacityAnimationTargetNode = data.value("Expand").value(); + _expandAnimationTargetNode = _opacityAnimationTargetNode; + QPropertyAnimation* nodeOpacityAnimation = new QPropertyAnimation(this, "pOpacity"); + connect(nodeOpacityAnimation, &QPropertyAnimation::finished, this, [=]() { _opacityAnimationTargetNode = nullptr; }); + connect(nodeOpacityAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + _pNavigationView->viewport()->update(); + }); + nodeOpacityAnimation->setDuration(600); + nodeOpacityAnimation->setEasingCurve(QEasingCurve::InOutSine); + nodeOpacityAnimation->setKeyValueAt(0.4, 0); + nodeOpacityAnimation->setStartValue(0); + nodeOpacityAnimation->setEndValue(1); + nodeOpacityAnimation->start(QAbstractAnimation::DeleteWhenStopped); + + QPropertyAnimation* rotateAnimation = new QPropertyAnimation(this, "pRotate"); + connect(rotateAnimation, &QPropertyAnimation::finished, this, [=]() { _expandAnimationTargetNode = nullptr; }); + connect(rotateAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + _pNavigationView->viewport()->update(); + }); + rotateAnimation->setDuration(300); + rotateAnimation->setEasingCurve(QEasingCurve::InOutSine); + if (lastExpandNode == _expandAnimationTargetNode) + { + rotateAnimation->setStartValue(_pRotate); + } + else + { + rotateAnimation->setStartValue(0); + } + rotateAnimation->setEndValue(-180); + rotateAnimation->start(QAbstractAnimation::DeleteWhenStopped); + } + else if (data.contains("Collapse")) + { + ElaNavigationNode* lastExpandNode = _expandAnimationTargetNode; + _opacityAnimationTargetNode = data.value("Collapse").value(); + _expandAnimationTargetNode = _opacityAnimationTargetNode; + _pOpacity = 0; + + QPropertyAnimation* rotateAnimation = new QPropertyAnimation(this, "pRotate"); + connect(rotateAnimation, &QPropertyAnimation::finished, this, [=]() { + _pOpacity = 1; + _expandAnimationTargetNode = nullptr; }); + connect(rotateAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + _pNavigationView->viewport()->update(); + }); + rotateAnimation->setDuration(300); + rotateAnimation->setEasingCurve(QEasingCurve::InOutSine); + if (lastExpandNode == _expandAnimationTargetNode) + { + rotateAnimation->setStartValue(_pRotate); + } + else + { + rotateAnimation->setStartValue(-180); + } + rotateAnimation->setEndValue(0); + rotateAnimation->start(QAbstractAnimation::DeleteWhenStopped); + } + else if (data.contains("SelectMarkChanged")) + { + _lastSelectedNode = data.value("LastSelectedNode").value(); + ElaNavigationNode* selectedNode = data.value("SelectedNode").value(); + bool direction = _compareItemY(selectedNode, _lastSelectedNode); + _pLastSelectMarkTop = 10; + _pLastSelectMarkBottom = 10; + _pSelectMarkTop = 10; + _pSelectMarkBottom = 10; + if (direction) + { + _lastSelectMarkTopAnimation->setStartValue(10); + _lastSelectMarkTopAnimation->setEndValue(0); + _lastSelectMarkTopAnimation->start(); + _lastSelectMarkBottomAnimation->stop(); + _selectMarkTopAnimation->stop(); + _isSelectMarkDisplay = false; + } + else + { + _lastSelectMarkBottomAnimation->setStartValue(10); + _lastSelectMarkBottomAnimation->setEndValue(0); + _lastSelectMarkBottomAnimation->start(); + _lastSelectMarkTopAnimation->stop(); + _selectMarkBottomAnimation->stop(); + _isSelectMarkDisplay = false; + } + } +} + +bool ElaNavigationStyle::_compareItemY(ElaNavigationNode* node1, ElaNavigationNode* node2) +{ + // 返回true 即node1 高于 node2 + if (!node1) + { + return false; + } + if (!node2) + { + return true; + } + // 同一父节点 + if (node1->getParentNode() == node2->getParentNode()) + { + if (node1->getModelIndex().row() < node2->getModelIndex().row()) + { + return true; + } + else + { + return false; + } + } + else + { + ElaNavigationNode* node1OriginalNode = node1->getOriginalNode(); + ElaNavigationNode* node2OriginalNode = node2->getOriginalNode(); + // 不同父节点 相同起源节点 + if (node1OriginalNode == node2OriginalNode) + { + int node1Depth = node1->getDepth(); + int node2Depth = node2->getDepth(); + // 相同深度 + if (node1Depth == node2Depth) + { + ElaNavigationNode* node1ParentNode = node1->getParentNode(); + ElaNavigationNode* node2ParentNode = node2->getParentNode(); + if (node1ParentNode->getModelIndex().row() < node2ParentNode->getModelIndex().row()) + { + return true; + } + else + { + return false; + } + } + else + { + if (node1Depth < node2Depth) + { + ElaNavigationNode* node2ParentNode = node2->getParentNode(); + while (node2ParentNode->getDepth() != node1Depth) + { + node2ParentNode = node2ParentNode->getParentNode(); + } + // 父子节点关系 + if (node1 == node2ParentNode) + { + return true; + } + if (node1->getModelIndex().row() < node2ParentNode->getModelIndex().row()) + { + return true; + } + else + { + return false; + } + } + else + { + ElaNavigationNode* node1ParentNode = node1->getParentNode(); + while (node1ParentNode->getDepth() != node2Depth) + { + node1ParentNode = node1ParentNode->getParentNode(); + } + if (node2 == node1ParentNode) + { + return false; + } + if (node1ParentNode->getModelIndex().row() < node2->getModelIndex().row()) + { + return true; + } + else + { + return false; + } + } + } + } + else + { + if (node1OriginalNode->getModelIndex().row() < node2OriginalNode->getModelIndex().row()) + { + return true; + } + else + { + return false; + } + } + } +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaNavigationStyle.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaNavigationStyle.h new file mode 100644 index 0000000..6a17376 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaNavigationStyle.h @@ -0,0 +1,48 @@ +#ifndef ELANAVIGATIONSTYLE_H +#define ELANAVIGATIONSTYLE_H +#include +#include + +#include "Def.h" +class ElaNavigationNode; +class ElaNavigationView; +class QPropertyAnimation; +class ElaNavigationStyle : public QProxyStyle +{ + Q_OBJECT + Q_PROPERTY_CREATE(qreal, Opacity) + Q_PROPERTY_CREATE(qreal, Rotate) + Q_PROPERTY_CREATE(int, ItemHeight) + Q_PRIVATE_CREATE(ElaNavigationView*, NavigationView) + Q_PROPERTY_CREATE(qreal, LastSelectMarkTop) + Q_PROPERTY_CREATE(qreal, LastSelectMarkBottom) + Q_PROPERTY_CREATE(qreal, SelectMarkTop) + Q_PROPERTY_CREATE(qreal, SelectMarkBottom) + Q_PRIVATE_CREATE(QModelIndex, PressIndex) +public: + explicit ElaNavigationStyle(QStyle* style = nullptr); + ~ElaNavigationStyle(); + void drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget = nullptr) const override; + void drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget = nullptr) const override; + QSize sizeFromContents(ContentsType type, const QStyleOption* option, const QSize& size, const QWidget* widget) const override; + + void navigationNodeStateChange(QVariantMap data); + +private: + ElaThemeType::ThemeMode _themeMode; + bool _isSelectMarkDisplay{true}; + int _leftPadding{11}; + int _iconAreaWidth{40}; + int _textRightSpacing{3}; + int _indicatorIconAreaWidth{34}; + ElaNavigationNode* _opacityAnimationTargetNode{nullptr}; + ElaNavigationNode* _expandAnimationTargetNode{nullptr}; + ElaNavigationNode* _lastSelectedNode{nullptr}; + QPropertyAnimation* _lastSelectMarkTopAnimation{nullptr}; + QPropertyAnimation* _lastSelectMarkBottomAnimation{nullptr}; + QPropertyAnimation* _selectMarkTopAnimation{nullptr}; + QPropertyAnimation* _selectMarkBottomAnimation{nullptr}; + bool _compareItemY(ElaNavigationNode* node1, ElaNavigationNode* node2); +}; + +#endif // ELANAVIGATIONSTYLE_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaNavigationView.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaNavigationView.cpp new file mode 100644 index 0000000..76a3bac --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaNavigationView.cpp @@ -0,0 +1,122 @@ +#include "ElaNavigationView.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "ElaMenu.h" +#include "ElaNavigationModel.h" +#include "ElaNavigationNode.h" +#include "ElaNavigationStyle.h" +#include "ElaScrollBar.h" + +ElaNavigationView::ElaNavigationView(QWidget* parent) + : QTreeView(parent) +{ + setObjectName("ElaNavigationView"); + setStyleSheet("#ElaNavigationView{background-color:transparent;}"); + setAnimated(true); + setHeaderHidden(true); + setRootIsDecorated(false); + setExpandsOnDoubleClick(false); + setAutoScroll(false); + setMouseTracking(true); + setSelectionMode(QAbstractItemView::NoSelection); + + // 滚动条设置 + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + ElaScrollBar* vScrollBar = new ElaScrollBar(this); + connect(vScrollBar, &ElaScrollBar::rangeAnimationFinished, this, [=]() { + doItemsLayout(); + }); + setVerticalScrollBar(vScrollBar); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + ElaScrollBar* floatVScrollBar = new ElaScrollBar(vScrollBar, this); + floatVScrollBar->setIsAnimation(true); + + _navigationStyle = new ElaNavigationStyle(this->style()); + _navigationStyle->setNavigationView(this); + setStyle(_navigationStyle); + + QScroller::grabGesture(this->viewport(), QScroller::LeftMouseButtonGesture); + QScroller* scroller = QScroller::scroller(this->viewport()); + QScrollerProperties properties = scroller->scrollerProperties(); + properties.setScrollMetric(QScrollerProperties::MousePressEventDelay, 0); + properties.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy, QScrollerProperties::OvershootAlwaysOff); + properties.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy, QScrollerProperties::OvershootAlwaysOn); + properties.setScrollMetric(QScrollerProperties::OvershootDragResistanceFactor, 0.35); + properties.setScrollMetric(QScrollerProperties::OvershootScrollTime, 0.5); + properties.setScrollMetric(QScrollerProperties::FrameRate, QScrollerProperties::Fps60); + scroller->setScrollerProperties(properties); + + connect(scroller, &QScroller::stateChanged, this, [=](QScroller::State newstate) { + if (newstate == QScroller::Pressed) + { + _navigationStyle->setPressIndex(indexAt(mapFromGlobal(QCursor::pos()))); + viewport()->update(); + } + else if (newstate == QScroller::Scrolling || newstate == QScroller::Inactive) + { + _navigationStyle->setPressIndex(QModelIndex()); + } + }); + + setContextMenuPolicy(Qt::CustomContextMenu); + connect(this, &ElaNavigationView::customContextMenuRequested, this, &ElaNavigationView::onCustomContextMenuRequested); +} + +ElaNavigationView::~ElaNavigationView() +{ +} + +void ElaNavigationView::navigationNodeStateChange(QVariantMap data) +{ + this->_navigationStyle->navigationNodeStateChange(data); +} + +void ElaNavigationView::onCustomContextMenuRequested(const QPoint& pos) +{ + QModelIndex posIndex = indexAt(pos); + if (!posIndex.isValid()) + { + return; + } + ElaNavigationNode* posNode = static_cast(posIndex.internalPointer()); + if (!posNode->getIsExpanderNode()) + { + ElaMenu menu; + menu.setMenuItemHeight(27); + QAction* openAction = menu.addElaIconAction(ElaIconType::ObjectGroup, "在新窗口中打开"); + connect(openAction, &QAction::triggered, this, [=]() { + Q_EMIT navigationOpenNewWindow(posNode->getNodeKey()); + }); + menu.exec(mapToGlobal(pos)); + } +} + +void ElaNavigationView::mouseDoubleClickEvent(QMouseEvent* event) +{ + _navigationStyle->setPressIndex(indexAt(event->pos())); + viewport()->update(); + QTreeView::mouseDoubleClickEvent(event); +} + +void ElaNavigationView::mouseReleaseEvent(QMouseEvent* event) +{ + QTreeView::mouseReleaseEvent(event); + if (event->button() == Qt::LeftButton) + { + QModelIndex index = indexAt(event->pos()); + if (index.isValid()) + { + Q_EMIT navigationClicked(index); + } + _navigationStyle->setPressIndex(QModelIndex()); + } +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaNavigationView.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaNavigationView.h new file mode 100644 index 0000000..b31cd9a --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaNavigationView.h @@ -0,0 +1,30 @@ +#ifndef ELANAVIGATIONVIEW_H +#define ELANAVIGATIONVIEW_H + +#include +#include + +class ElaScrollBar; +class ElaNavigationStyle; +class ElaNavigationView : public QTreeView +{ + Q_OBJECT +public: + explicit ElaNavigationView(QWidget* parent = nullptr); + ~ElaNavigationView(); + void navigationNodeStateChange(QVariantMap data); + + Q_SLOT void onCustomContextMenuRequested(const QPoint& pos); +Q_SIGNALS: + Q_SIGNAL void navigationClicked(const QModelIndex& index); + Q_SIGNAL void navigationOpenNewWindow(QString nodeKey); + +protected: + virtual void mouseDoubleClickEvent(QMouseEvent* event) override; + virtual void mouseReleaseEvent(QMouseEvent* event) override; + +private: + ElaNavigationStyle* _navigationStyle{nullptr}; +}; + +#endif // ELANAVIGATIONVIEW_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaPivotModel.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaPivotModel.cpp new file mode 100644 index 0000000..3a055ad --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaPivotModel.cpp @@ -0,0 +1,51 @@ +#include "ElaPivotModel.h" + +ElaPivotModel::ElaPivotModel(QObject* parent) + : QAbstractListModel{parent} +{ +} + +ElaPivotModel::~ElaPivotModel() +{ +} + +void ElaPivotModel::appendPivot(QString pivot) +{ + if (!pivot.isEmpty()) + { + beginInsertRows(QModelIndex(), _pivotList.count(), _pivotList.count()); + _pivotList.append(pivot); + endInsertRows(); + return; + } +} + +void ElaPivotModel::removePivot(QString pivot) +{ + if (_pivotList.contains(pivot)) + { + int index = _pivotList.lastIndexOf(pivot); + beginRemoveRows(QModelIndex(), index, index); + _pivotList.removeAt(index); + endRemoveRows(); + } +} + +int ElaPivotModel::getPivotListCount() const +{ + return _pivotList.count(); +} + +int ElaPivotModel::rowCount(const QModelIndex& parent) const +{ + return _pivotList.count(); +} + +QVariant ElaPivotModel::data(const QModelIndex& index, int role) const +{ + if (role == Qt::DisplayRole) + { + return _pivotList[index.row()]; + } + return QVariant(); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaPivotModel.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaPivotModel.h new file mode 100644 index 0000000..cc55633 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaPivotModel.h @@ -0,0 +1,26 @@ +#ifndef ELAPIVOTMODEL_H +#define ELAPIVOTMODEL_H + +#include + +class ElaPivotModel : public QAbstractListModel +{ + Q_OBJECT +public: + explicit ElaPivotModel(QObject* parent = nullptr); + ~ElaPivotModel(); + + void appendPivot(QString pivot); + void removePivot(QString pivot); + + int getPivotListCount() const; + +protected: + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + QVariant data(const QModelIndex& index, int role) const override; + +private: + QStringList _pivotList; +}; + +#endif // ELAPIVOTMODEL_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaPivotStyle.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaPivotStyle.cpp new file mode 100644 index 0000000..95c5c58 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaPivotStyle.cpp @@ -0,0 +1,112 @@ +#include "ElaPivotStyle.h" + +#include +#include +#include + +#include "ElaTheme.h" +ElaPivotStyle::ElaPivotStyle(QStyle* style) +{ + _pCurrentIndex = -1; + _pPivotSpacing = 5; + _themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { + _themeMode = themeMode; + }); +} + +ElaPivotStyle::~ElaPivotStyle() +{ +} + +void ElaPivotStyle::drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const +{ + switch (element) + { + case QStyle::PE_PanelItemViewRow: + { + return; + } + case QStyle::PE_Widget: + { + return; + } + default: + { + break; + } + } + QProxyStyle::drawPrimitive(element, option, painter, widget); +} + +void ElaPivotStyle::drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const +{ + switch (element) + { + case QStyle::CE_ShapedFrame: + { + // viewport视口外的其他区域背景 + return; + } + case QStyle::CE_ItemViewItem: + { + if (const QStyleOptionViewItem* vopt = qstyleoption_cast(option)) + { + // 内容绘制 + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing); + QRect textRect = proxy()->subElementRect(SE_ItemViewItemText, vopt, widget); + textRect.adjust(0, 0, 0, -5); + // 文字绘制 + if (!vopt->text.isEmpty()) + { + if (_pPressIndex == vopt->index) + { + painter->setPen(ElaThemeColor(_themeMode, BasicTextPress)); + } + else + { + if (_pCurrentIndex == vopt->index.row() || vopt->state.testFlag(QStyle::State_MouseOver)) + { + painter->setPen(ElaThemeColor(_themeMode, BasicText)); + } + else + { + painter->setPen(ElaThemeColor(_themeMode, BasicTextNoFocus)); + } + } + painter->drawText(textRect, Qt::AlignCenter, vopt->text); + } + painter->restore(); + } + return; + } + default: + { + break; + } + } + QProxyStyle::drawControl(element, option, painter, widget); +} + +int ElaPivotStyle::pixelMetric(PixelMetric metric, const QStyleOption* option, const QWidget* widget) const +{ + switch (metric) + { + case QStyle::PM_FocusFrameHMargin: + { + return _pPivotSpacing; + } + default: + { + break; + } + } + + return QProxyStyle::pixelMetric(metric, option, widget); +} + +const QColor& ElaPivotStyle::getMarkColor() +{ + return ElaThemeColor(_themeMode, PrimaryNormal); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaPivotStyle.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaPivotStyle.h new file mode 100644 index 0000000..2804cc4 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaPivotStyle.h @@ -0,0 +1,27 @@ +#ifndef ELAPIVOTSTYLE_H +#define ELAPIVOTSTYLE_H + +#include +#include + +#include "Def.h" +class ElaPivotStyle : public QProxyStyle +{ + Q_OBJECT + Q_PRIVATE_CREATE(QModelIndex, PressIndex) + Q_PRIVATE_CREATE(int, CurrentIndex) + Q_PRIVATE_CREATE(int, PivotSpacing) +public: + explicit ElaPivotStyle(QStyle* style = nullptr); + ~ElaPivotStyle(); + void drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget = nullptr) const override; + void drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget = nullptr) const override; + int pixelMetric(PixelMetric metric, const QStyleOption* option = nullptr, const QWidget* widget = nullptr) const override; + + const QColor& getMarkColor(); + +private: + ElaThemeType::ThemeMode _themeMode; +}; + +#endif // ELAPIVOTSTYLE_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaPivotView.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaPivotView.cpp new file mode 100644 index 0000000..3ec0e3d --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaPivotView.cpp @@ -0,0 +1,111 @@ +#include "ElaPivotView.h" + +#include +#include +#include + +#include "ElaPivotStyle.h" +#include "ElaScrollBar.h" +ElaPivotView::ElaPivotView(QWidget* parent) + : QListView(parent) +{ + _pMarkX = 0; + _pMarkWidth = 40; + _pMarkAnimationWidth = 0; + setObjectName("ElaPivotView"); + setStyleSheet("#ElaPivotView{background-color:transparent;}"); + setMouseTracking(true); + setVerticalScrollBar(new ElaScrollBar(this)); + setHorizontalScrollBar(new ElaScrollBar(this)); + this->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + this->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); + setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); +} + +ElaPivotView::~ElaPivotView() +{ +} + +void ElaPivotView::doCurrentIndexChangedAnimation(const QModelIndex& index) +{ + if (index.row() >= 0) + { + QRect newIndexRect = visualRect(index); + QPropertyAnimation* markAnimation = new QPropertyAnimation(this, "pMarkX"); + connect(markAnimation, &QPropertyAnimation::valueChanged, this, [=]() { + update(); + }); + markAnimation->setDuration(300); + markAnimation->setEasingCurve(QEasingCurve::InOutSine); + if (_pPivotStyle->getCurrentIndex() >= 0) + { + markAnimation->setStartValue(_pMarkX); + markAnimation->setEndValue(newIndexRect.center().x() - _pMarkWidth / 2); + } + else + { + markAnimation->setStartValue(newIndexRect.center().x()); + markAnimation->setEndValue(newIndexRect.center().x() - _pMarkWidth / 2); + + QPropertyAnimation* markWidthAnimation = new QPropertyAnimation(this, "pMarkAnimationWidth"); + markWidthAnimation->setDuration(300); + markWidthAnimation->setEasingCurve(QEasingCurve::InOutSine); + markWidthAnimation->setStartValue(0); + markWidthAnimation->setEndValue(_pMarkWidth); + markWidthAnimation->start(QAbstractAnimation::DeleteWhenStopped); + } + markAnimation->start(QAbstractAnimation::DeleteWhenStopped); + } + else + { + QRect newIndexRect = visualRect(model()->index(_pPivotStyle->getCurrentIndex(), 0)); + QPropertyAnimation* markAnimation = new QPropertyAnimation(this, "pMarkX"); + connect(markAnimation, &QPropertyAnimation::valueChanged, this, [=]() { + update(); + }); + markAnimation->setDuration(300); + markAnimation->setEasingCurve(QEasingCurve::InOutSine); + markAnimation->setStartValue(_pMarkX); + markAnimation->setEndValue(newIndexRect.center().x()); + markAnimation->start(QAbstractAnimation::DeleteWhenStopped); + + QPropertyAnimation* markWidthAnimation = new QPropertyAnimation(this, "pMarkAnimationWidth"); + markWidthAnimation->setDuration(300); + markWidthAnimation->setEasingCurve(QEasingCurve::InOutSine); + markWidthAnimation->setStartValue(_pMarkAnimationWidth); + markWidthAnimation->setEndValue(0); + markWidthAnimation->start(QAbstractAnimation::DeleteWhenStopped); + } + setCurrentIndex(index); +} + +void ElaPivotView::mouseDoubleClickEvent(QMouseEvent* event) +{ + _pPivotStyle->setPressIndex(indexAt(event->pos())); + viewport()->update(); + QListView::mouseDoubleClickEvent(event); +} + +void ElaPivotView::mouseReleaseEvent(QMouseEvent* event) +{ + if (event->button() == Qt::LeftButton) + { + _pPivotStyle->setPressIndex(QModelIndex()); + viewport()->update(); + } + QListView::mouseReleaseEvent(event); +} + +void ElaPivotView::paintEvent(QPaintEvent* event) +{ + QPainter painter(viewport()); + QRect viewPortRect = viewport()->rect(); + painter.save(); + painter.setRenderHint(QPainter::Antialiasing); + painter.setPen(Qt::NoPen); + painter.setBrush(_pPivotStyle->getMarkColor()); + painter.drawRoundedRect(QRect(_pMarkX, viewPortRect.bottom() - 4, _pMarkAnimationWidth, 3), 3, 3); + painter.restore(); + QListView::paintEvent(event); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaPivotView.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaPivotView.h new file mode 100644 index 0000000..c324919 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaPivotView.h @@ -0,0 +1,26 @@ +#ifndef ELAPIVOTVIEW_H +#define ELAPIVOTVIEW_H + +#include + +#include "stdafx.h" +class ElaPivotStyle; +class ElaPivotView : public QListView +{ + Q_OBJECT + Q_PROPERTY_CREATE(int, MarkX) + Q_PRIVATE_CREATE(int, MarkWidth) + Q_PROPERTY_CREATE(int, MarkAnimationWidth) + Q_PRIVATE_CREATE(ElaPivotStyle*, PivotStyle) +public: + explicit ElaPivotView(QWidget* parent = nullptr); + ~ElaPivotView(); + void doCurrentIndexChangedAnimation(const QModelIndex& index); + +protected: + void mouseDoubleClickEvent(QMouseEvent* event) override; + void mouseReleaseEvent(QMouseEvent* event) override; + virtual void paintEvent(QPaintEvent* event) override; +}; + +#endif // ELAPIVOTVIEW_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaPlainTextEditStyle.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaPlainTextEditStyle.cpp new file mode 100644 index 0000000..2d835c1 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaPlainTextEditStyle.cpp @@ -0,0 +1,66 @@ +#include "ElaPlainTextEditStyle.h" + +#include +#include +#include +#include + +#include "ElaTheme.h" +ElaPlainTextEditStyle::ElaPlainTextEditStyle(QStyle* style) +{ + _pExpandMarkWidth = 0; + _themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { _themeMode = themeMode; }); +} + +ElaPlainTextEditStyle::~ElaPlainTextEditStyle() +{ +} + +void ElaPlainTextEditStyle::drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const +{ + switch (element) + { + case QStyle::CE_ShapedFrame: + { + if (const QStyleOptionFrame* fopt = qstyleoption_cast(option)) + { + //背景绘制 + QRect editRect = option->rect; + painter->save(); + painter->setRenderHints(QPainter::Antialiasing); + painter->setPen(Qt::NoPen); + // 边框绘制 + painter->setBrush(ElaThemeColor(_themeMode, BasicBorder)); + painter->drawRoundedRect(editRect, 6, 6); + + painter->setBrush(ElaThemeColor(_themeMode, BasicBase)); + painter->drawRoundedRect(QRectF(editRect.x() + 1.5, editRect.y() + 1.5, editRect.width() - 3, editRect.height() - 3), 6, 6); + + // 底边线绘制 + painter->setBrush(ElaThemeColor(_themeMode, BasicHemline)); + QPainterPath path; + path.moveTo(6, editRect.height()); + path.lineTo(editRect.width() - 6, editRect.height()); + path.arcTo(QRectF(editRect.width() - 12, editRect.height() - 12, 12, 12), -90, 45); + path.lineTo(6 - 3 * std::sqrt(2), editRect.height() - (6 - 3 * std::sqrt(2))); + path.arcTo(QRectF(0, editRect.height() - 12, 12, 12), 270, 45); + path.closeSubpath(); + painter->drawPath(path); + + //焦点指示器 + painter->setPen(Qt::NoPen); + painter->setBrush(ElaThemeColor(_themeMode, PrimaryNormal)); + painter->drawRoundedRect(QRectF(editRect.width() / 2 - _pExpandMarkWidth, editRect.height() - 2.5, _pExpandMarkWidth * 2, 2.5), 2, 2); + + painter->restore(); + } + return; + } + default: + { + break; + } + } + QProxyStyle::drawControl(element, option, painter, widget); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaPlainTextEditStyle.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaPlainTextEditStyle.h new file mode 100644 index 0000000..38eb663 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaPlainTextEditStyle.h @@ -0,0 +1,20 @@ +#ifndef ELAPLAINTEXTEDITSTYLE_H +#define ELAPLAINTEXTEDITSTYLE_H + +#include + +#include "Def.h" +class ElaPlainTextEditStyle : public QProxyStyle +{ + Q_OBJECT + Q_PROPERTY_CREATE(qreal, ExpandMarkWidth) +public: + explicit ElaPlainTextEditStyle(QStyle* style = nullptr); + ~ElaPlainTextEditStyle(); + void drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget = nullptr) const override; + +private: + ElaThemeType::ThemeMode _themeMode; +}; + +#endif // ELAPLAINTEXTEDITSTYLE_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaPopularCardFloater.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaPopularCardFloater.cpp new file mode 100644 index 0000000..ebebd76 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaPopularCardFloater.cpp @@ -0,0 +1,242 @@ +#include "ElaPopularCardFloater.h" + +#include +#include +#include +#include +#include + +#include "ElaPopularCard.h" +#include "ElaPopularCardPrivate.h" +#include "ElaPushButton.h" +#include "ElaTheme.h" +ElaPopularCardFloater::ElaPopularCardFloater(ElaPopularCard* card, ElaPopularCardPrivate* cardPrivate, QWidget* parent) + : QWidget{parent} +{ + _card = card; + _cardPrivate = cardPrivate; + _pHoverOpacity = 0; + _pHoverYOffset = 0; + setObjectName("ElaPopularCardFloater"); + setStyleSheet("#ElaPopularCardFloater{background-color:transparent}"); + setMouseTracking(true); + + _overButton = new ElaPushButton("获取", this); + _opacityEffect = new QGraphicsOpacityEffect(_overButton); + _opacityEffect->setOpacity(1); + _overButton->setGraphicsEffect(_opacityEffect); + _overButton->move(0, 0); + _overButton->setLightDefaultColor(ElaThemeColor(ElaThemeType::Light, PrimaryNormal)); + _overButton->setLightHoverColor(ElaThemeColor(ElaThemeType::Light, PrimaryHover)); + _overButton->setLightPressColor(ElaThemeColor(ElaThemeType::Light, PrimaryPress)); + _overButton->setLightTextColor(Qt::white); + _overButton->setDarkDefaultColor(ElaThemeColor(ElaThemeType::Dark, PrimaryNormal)); + _overButton->setDarkHoverColor(ElaThemeColor(ElaThemeType::Dark, PrimaryHover)); + _overButton->setDarkPressColor(ElaThemeColor(ElaThemeType::Dark, PrimaryPress)); + _overButton->setDarkTextColor(Qt::white); + _overButton->setMinimumSize(0, 0); + _overButton->setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)); + connect(_overButton, &ElaPushButton::clicked, _card, &ElaPopularCard::popularCardButtonClicked); + + _themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { _themeMode = themeMode; }); + setVisible(false); +} + +ElaPopularCardFloater::~ElaPopularCardFloater() +{ +} + +void ElaPopularCardFloater::showFloater() +{ + _isHideAnimationFinished = true; + _pHoverYOffset = 6; + _pHoverOpacity = 1; + _opacityEffect->setOpacity(1); + + QPropertyAnimation* geometryAnimation = new QPropertyAnimation(this, "geometry"); + connect(geometryAnimation, &QPropertyAnimation::valueChanged, this, [=]() { + update(); + }); + geometryAnimation->setEasingCurve(QEasingCurve::OutQuad); + geometryAnimation->setDuration(300); + QRect cardGeometry = QRect(_card->mapTo(_cardPrivate->_pCardFloatArea, QPoint(0, 0)), _card->size()); + QRect endGeometry = _calculateTargetGeometry(cardGeometry); + QRect startGeometry = QRect(endGeometry.x() + _floatGeometryOffset, endGeometry.y() + _floatGeometryOffset, _card->width(), _card->height()); + geometryAnimation->setStartValue(startGeometry); + geometryAnimation->setEndValue(endGeometry); + geometryAnimation->start(QAbstractAnimation::DeleteWhenStopped); + + //Button动画 + QPropertyAnimation* buttonAnimation = new QPropertyAnimation(_overButton, "geometry"); + buttonAnimation->setEasingCurve(QEasingCurve::OutQuad); + buttonAnimation->setDuration(300); + buttonAnimation->setStartValue(_cardPrivate->_interactiveTipsBaseRect); + buttonAnimation->setEndValue(_cardPrivate->_buttonTargetRect); + buttonAnimation->start(QAbstractAnimation::DeleteWhenStopped); + setVisible(true); +} + +void ElaPopularCardFloater::hideFloater() +{ + if (!_isHideAnimationFinished) + { + return; + } + _isHideAnimationFinished = false; + QPropertyAnimation* geometryAnimation = new QPropertyAnimation(this, "geometry"); + connect(geometryAnimation, &QPropertyAnimation::valueChanged, this, [=]() { + QRect endGeometry = QRect(_card->mapTo(_cardPrivate->_pCardFloatArea, QPoint(0, 0)), _card->size()); + geometryAnimation->setEndValue(endGeometry); + }); + connect(geometryAnimation, &QPropertyAnimation::finished, this, [=]() { + _cardPrivate->_isFloating = false; + setVisible(false); + _card->update(); + }); + geometryAnimation->setEasingCurve(QEasingCurve::InOutSine); + geometryAnimation->setDuration(300); + QRect endGeometry = QRect(_card->mapTo(_cardPrivate->_pCardFloatArea, QPoint(0, 0)), _card->size()); + geometryAnimation->setStartValue(geometry()); + geometryAnimation->setEndValue(endGeometry); + geometryAnimation->start(QAbstractAnimation::DeleteWhenStopped); + + QPropertyAnimation* opacityAnimation = new QPropertyAnimation(this, "pHoverOpacity"); + opacityAnimation->setDuration(300); + opacityAnimation->setStartValue(_pHoverOpacity); + opacityAnimation->setEndValue(0); + opacityAnimation->start(QAbstractAnimation::DeleteWhenStopped); + + //内容调整动画 + QPropertyAnimation* hoverAnimation = new QPropertyAnimation(this, "pHoverYOffset"); + connect(hoverAnimation, &QPropertyAnimation::valueChanged, this, [=]() { + update(); + }); + hoverAnimation->setDuration(300); + hoverAnimation->setStartValue(_pHoverYOffset); + hoverAnimation->setEndValue(0); + hoverAnimation->start(QAbstractAnimation::DeleteWhenStopped); + + //Button动画 + QPropertyAnimation* buttonAnimation = new QPropertyAnimation(_overButton, "geometry"); + buttonAnimation->setEasingCurve(QEasingCurve::InOutSine); + buttonAnimation->setDuration(300); + buttonAnimation->setStartValue(_overButton->geometry()); + QRectF endRect = _cardPrivate->_interactiveTipsBaseRect; + endRect.adjust(0, 6, 0, 6); + buttonAnimation->setEndValue(endRect); + buttonAnimation->start(QAbstractAnimation::DeleteWhenStopped); + + QPropertyAnimation* buttonOpacityAnimation = new QPropertyAnimation(_overButton->graphicsEffect(), "opacity"); + buttonOpacityAnimation->setEasingCurve(QEasingCurve::InOutSine); + buttonOpacityAnimation->setDuration(350); + buttonOpacityAnimation->setStartValue(1); + buttonOpacityAnimation->setEndValue(0); + buttonOpacityAnimation->start(QAbstractAnimation::DeleteWhenStopped); +} + +bool ElaPopularCardFloater::event(QEvent* event) +{ + switch (event->type()) + { + case QEvent::Leave: + { + hideFloater(); + break; + } + case QEvent::MouseButtonRelease: + { + Q_EMIT _card->popularCardClicked(); + break; + } + default: + { + break; + } + } + return QWidget::event(event); +} + +void ElaPopularCardFloater::paintEvent(QPaintEvent* event) +{ + QPainter painter(this); + painter.save(); + painter.setRenderHints(QPainter::SmoothPixmapTransform | QPainter::Antialiasing | QPainter::TextAntialiasing); + // 阴影绘制 + painter.setOpacity(_pHoverOpacity); + QRect shadowRect = rect(); + shadowRect.adjust(0, 0, 0, -_pHoverYOffset); + eTheme->drawEffectShadow(&painter, shadowRect, _cardPrivate->_shadowBorderWidth, _cardPrivate->_pBorderRadius); + QRectF foregroundRect(_cardPrivate->_shadowBorderWidth, _cardPrivate->_shadowBorderWidth - _pHoverYOffset + 1, width() - 2 * _cardPrivate->_shadowBorderWidth, height() - 2 * _cardPrivate->_shadowBorderWidth); + QRectF cardForegroundRect(_cardPrivate->_shadowBorderWidth, _cardPrivate->_shadowBorderWidth - _cardPrivate->_pHoverYOffset + 1, _card->width() - 2 * _cardPrivate->_shadowBorderWidth, _card->height() - 2 * _cardPrivate->_shadowBorderWidth); + + //背景绘制 + painter.setOpacity(1); + painter.setPen(ElaThemeColor(_themeMode, PopupBorderHover)); + painter.setBrush(ElaThemeColor(_themeMode, DialogBase)); + painter.drawRoundedRect(foregroundRect, _cardPrivate->_pBorderRadius, _cardPrivate->_pBorderRadius); + painter.setClipRect(foregroundRect); + //图片绘制 + painter.save(); + QRectF pixRect(foregroundRect.x() + cardForegroundRect.height() * 0.15, foregroundRect.y() + cardForegroundRect.height() * 0.15, cardForegroundRect.height() * 0.7, cardForegroundRect.height() * 0.7); + + QPainterPath pixPath; + pixPath.addRoundedRect(pixRect, 4, 4); + painter.setClipPath(pixPath); + painter.drawPixmap(pixRect, _cardPrivate->_pCardPixmap, _cardPrivate->_pCardPixmap.rect()); + painter.restore(); + + //文字绘制 + //Title + painter.setPen(ElaThemeColor(_themeMode, BasicText)); + QFont font = painter.font(); + font.setWeight(QFont::Bold); + font.setPixelSize(15); + painter.setFont(font); + int titleHeight = painter.fontMetrics().height(); + QRectF titleRect(pixRect.right() + _cardPrivate->_textHSpacing, pixRect.y(), _floatGeometryOffset * 2 + cardForegroundRect.width() - pixRect.width() - _cardPrivate->_textHSpacing * 2 - cardForegroundRect.height() * 0.15 - _cardPrivate->_buttonTargetRect.width(), titleHeight); + QString titleText = painter.fontMetrics().elidedText(_cardPrivate->_pTitle, Qt::ElideRight, titleRect.width()); + painter.drawText(titleRect, Qt::AlignLeft | Qt::AlignTop | Qt::TextSingleLine, titleText); + + //SubTitle + font.setWeight(QFont::DemiBold); + font.setPixelSize(13); + painter.setFont(font); + int subTitleHeight = painter.fontMetrics().height(); + QRectF subTitleRect(pixRect.right() + _cardPrivate->_textHSpacing, titleRect.bottom() + _cardPrivate->_textVSpacing, _floatGeometryOffset * 2 + cardForegroundRect.width() - pixRect.width() - _cardPrivate->_textHSpacing * 2 - cardForegroundRect.height() * 0.15 - _cardPrivate->_buttonTargetRect.width(), subTitleHeight); + QString subTitleText = painter.fontMetrics().elidedText(_cardPrivate->_pSubTitle, Qt::ElideRight, subTitleRect.width()); + painter.drawText(subTitleRect, Qt::AlignLeft | Qt::AlignTop | Qt::TextSingleLine, subTitleText); + + //DetailedText + painter.setPen(ElaThemeColor(_themeMode, BasicDetailsText)); + int detailedTextHeight = painter.fontMetrics().height() * 2 + 2; + QRectF detailedTextRect(pixRect.x(), pixRect.bottom() + cardForegroundRect.height() * 0.15, cardForegroundRect.width() + 2 * _floatGeometryOffset - _cardPrivate->_textHSpacing - cardForegroundRect.height() * 0.15, detailedTextHeight); + QString detailedText = painter.fontMetrics().elidedText(_cardPrivate->_pDetailedText, Qt::ElideRight, detailedTextRect.width() * 1.9); + painter.drawText(detailedTextRect, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, detailedText); + + //分割线绘制 + painter.setPen(ElaThemeColor(_themeMode, BasicBaseLine)); + painter.drawLine(foregroundRect.x(), detailedTextRect.bottom() + 5, foregroundRect.right(), detailedTextRect.bottom() + 5); + + //CardFloatPixmap + painter.drawPixmap(QRect(pixRect.x(), detailedTextRect.bottom() + 15, cardForegroundRect.bottom() + _floatGeometryOffset + 90 - detailedTextRect.bottom() - 15 - _cardPrivate->_shadowBorderWidth - 10, cardForegroundRect.bottom() + _floatGeometryOffset + 90 - detailedTextRect.bottom() - 15 - _cardPrivate->_shadowBorderWidth - 10), _cardPrivate->_pCardFloatPixmap); + painter.restore(); +} + +QRect ElaPopularCardFloater::_calculateTargetGeometry(QRect cardGeometry) +{ + QRect targetGeometry = cardGeometry; + targetGeometry.adjust(-_floatGeometryOffset, -_floatGeometryOffset, _floatGeometryOffset, 90); + QRect windowRect = _cardPrivate->_pCardFloatArea->rect(); + if (targetGeometry.bottom() > windowRect.bottom()) + { + int offset = targetGeometry.bottom() - windowRect.bottom(); + targetGeometry.adjust(0, -offset, 0, -offset); + } + if (targetGeometry.top() < windowRect.top()) + { + int offset = targetGeometry.top() - windowRect.top(); + targetGeometry.adjust(0, -offset, 0, -offset); + } + return targetGeometry; +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaPopularCardFloater.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaPopularCardFloater.h new file mode 100644 index 0000000..35e0d3f --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaPopularCardFloater.h @@ -0,0 +1,39 @@ +#ifndef ELAPOPULARCARDFLOATER_H +#define ELAPOPULARCARDFLOATER_H + +#include + +#include "Def.h" +class ElaPushButton; +class ElaPopularCard; +class ElaPopularCardPrivate; +class QGraphicsOpacityEffect; +class ElaPopularCardFloater : public QWidget +{ + Q_OBJECT + Q_PROPERTY_CREATE(qreal, HoverYOffset); + Q_PROPERTY_CREATE(qreal, HoverOpacity); + +public: + explicit ElaPopularCardFloater(ElaPopularCard* card, ElaPopularCardPrivate* cardPrivate, QWidget* parent = nullptr); + ~ElaPopularCardFloater(); + void showFloater(); + void hideFloater(); + +protected: + virtual bool event(QEvent* event) override; + virtual void paintEvent(QPaintEvent* event) override; + +private: + friend class ElaPopularCard; + ElaThemeType::ThemeMode _themeMode; + ElaPushButton* _overButton{nullptr}; + QGraphicsOpacityEffect* _opacityEffect{nullptr}; + bool _isHideAnimationFinished{true}; + ElaPopularCard* _card{nullptr}; + ElaPopularCardPrivate* _cardPrivate{nullptr}; + QRect _calculateTargetGeometry(QRect cardGeometry); + int _floatGeometryOffset{25}; +}; + +#endif // ELAPOPULARCARDFLOATER_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaProgressBarStyle.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaProgressBarStyle.cpp new file mode 100644 index 0000000..6cf3c73 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaProgressBarStyle.cpp @@ -0,0 +1,155 @@ +#include "ElaProgressBarStyle.h" + +#include +#include +#include + +#include "ElaTheme.h" +ElaProgressBarStyle::ElaProgressBarStyle(QStyle* style) +{ + setProperty("busyStartValue", 0); + setProperty("busyEndValue", 0); + _themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { _themeMode = themeMode; }); +} + +ElaProgressBarStyle::~ElaProgressBarStyle() +{ +} + +void ElaProgressBarStyle::drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const +{ + switch (element) + { + case QStyle::CE_ProgressBarGroove: + { + //背景轨道 + painter->save(); + painter->setRenderHints(QPainter::Antialiasing); + painter->setPen(Qt::NoPen); + painter->setBrush(ElaThemeColor(_themeMode, BasicChute)); + painter->drawRoundedRect(option->rect, 3, 3); + painter->restore(); + return; + } + case QStyle::CE_ProgressBarContents: + { + //滑块 + const QStyleOptionProgressBar* popt = qstyleoption_cast(option); + if (!popt) + { + break; + } + QRect contentRect = popt->rect; + painter->save(); + painter->setRenderHints(QPainter::Antialiasing); + painter->setPen(Qt::NoPen); + painter->setBrush(ElaThemeColor(_themeMode, PrimaryNormal)); + const bool inverted = popt->invertedAppearance; + bool reverse = popt->direction == Qt::RightToLeft; + if (inverted) + { + reverse = !reverse; + } + if (popt->minimum == 0 && popt->maximum == 0) + { + //忙碌动画 + int startValue = this->property("busyStartValue").toInt(); + if (startValue < 0) + { + startValue = 0; + } + int endValue = this->property("busyEndValue").toInt(); + if (popt->state & QStyle::State_Horizontal) + { + if (endValue > contentRect.width()) + { + endValue = contentRect.width(); + } + int busyWidth = endValue - startValue; + if (reverse) + { + painter->translate((contentRect.x() + contentRect.width()) / 2, contentRect.y()); + painter->rotate(180); + painter->translate(-(contentRect.x() + contentRect.width()) / 2, -contentRect.y() - contentRect.height() * 0.90); + } + painter->drawRoundedRect(QRectF(startValue, contentRect.y(), busyWidth, contentRect.height()), 3, 3); + } + else + { + if (endValue > contentRect.height()) + { + endValue = contentRect.height(); + } + int busyHeight = endValue - startValue; + painter->drawRoundedRect(QRectF(contentRect.x(), contentRect.height() - endValue, contentRect.width(), busyHeight), 3, 3); + } + } + else + { + qreal ratio = popt->progress / (qreal)(popt->maximum - popt->minimum); + if (popt->state & QStyle::State_Horizontal) + { + if (reverse) + { + painter->translate((contentRect.x() + contentRect.width()) / 2, contentRect.y()); + painter->rotate(180); + painter->translate(-(contentRect.x() + contentRect.width()) / 2, -contentRect.y() - contentRect.height() * 0.90); + } + painter->drawRoundedRect(QRectF(contentRect.x(), contentRect.y(), contentRect.width() * ratio, contentRect.height()), 3, 3); + } + else + { + painter->drawRoundedRect(QRectF(contentRect.x(), contentRect.y() + contentRect.height() * (1 - ratio), contentRect.width(), contentRect.height() * ratio), 3, 3); + } + } + painter->restore(); + return; + } + default: + { + break; + } + } + QProxyStyle::drawControl(element, option, painter, widget); +} + +QRect ElaProgressBarStyle::subElementRect(SubElement element, const QStyleOption* option, const QWidget* widget) const +{ + switch (element) + { + case QStyle::SE_ProgressBarGroove: + case QStyle::SE_ProgressBarContents: + { + QRect textRect = subElementRect(SE_ProgressBarLabel, option, widget); + const QStyleOptionProgressBar* popt = qstyleoption_cast(option); + if (!popt) + { + break; + } + QRect contentRect = popt->rect; + int width = contentRect.width(); + int height = contentRect.height(); + if (popt->state & QStyle::State_Horizontal) + { + contentRect.setTop(contentRect.top() + height * 0.45); + contentRect.setBottom(contentRect.bottom() - height * 0.30); + if (!(popt->minimum == 0 && popt->maximum == 0)) + { + contentRect.setWidth(contentRect.width() - textRect.width()); + } + } + else + { + contentRect.setLeft(contentRect.left() + width * 0.375); + contentRect.setRight(contentRect.right() - width * 0.375); + } + return contentRect; + } + default: + { + break; + } + } + return QProxyStyle::subElementRect(element, option, widget); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaProgressBarStyle.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaProgressBarStyle.h new file mode 100644 index 0000000..6b7f524 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaProgressBarStyle.h @@ -0,0 +1,20 @@ +#ifndef ELAPROGRESSBARSTYLE_H +#define ELAPROGRESSBARSTYLE_H + +#include + +#include "Def.h" +class ElaProgressBarStyle : public QProxyStyle +{ + Q_OBJECT +public: + explicit ElaProgressBarStyle(QStyle* style = nullptr); + ~ElaProgressBarStyle(); + void drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget = nullptr) const override; + QRect subElementRect(SubElement element, const QStyleOption* option, const QWidget* widget) const override; + +private: + ElaThemeType::ThemeMode _themeMode; +}; + +#endif // ELAPROGRESSBARSTYLE_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaRadioButtonStyle.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaRadioButtonStyle.cpp new file mode 100644 index 0000000..ec938ab --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaRadioButtonStyle.cpp @@ -0,0 +1,104 @@ +#include "ElaRadioButtonStyle.h" + +#include +#include +#include + +#include "ElaTheme.h" +ElaRadioButtonStyle::ElaRadioButtonStyle(QStyle* style) +{ + _themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { _themeMode = themeMode; }); +} + +ElaRadioButtonStyle::~ElaRadioButtonStyle() +{ +} + +void ElaRadioButtonStyle::drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const +{ + switch (element) + { + case PE_IndicatorRadioButton: + { + const QStyleOptionButton* bopt = qstyleoption_cast(option); + if (!bopt) + { + break; + } + QRect buttonRect = bopt->rect; + buttonRect.adjust(1, 1, -1, -1); + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + + if (bopt->state & QStyle::State_Off) + { + painter->setPen(QPen(ElaThemeColor(_themeMode, BasicBorder), 1.5)); + if (bopt->state & QStyle::State_MouseOver) + { + painter->setBrush(ElaThemeColor(_themeMode, BasicHover)); + } + else + { + painter->setBrush(ElaThemeColor(_themeMode, BasicBase)); + } + painter->drawEllipse(QPointF(buttonRect.center().x() + 1, buttonRect.center().y() + 1), 8.5, 8.5); + } + else + { + painter->setPen(Qt::NoPen); + // 外圆形 + painter->setBrush(ElaThemeColor(_themeMode, PrimaryNormal)); + painter->drawEllipse(QPointF(buttonRect.center().x() + 1, buttonRect.center().y() + 1), buttonRect.width() / 2, buttonRect.width() / 2); + // 内圆形 + painter->setBrush(ElaThemeColor(_themeMode, BasicTextInvert)); + if (bopt->state & QStyle::State_Sunken) + { + if (bopt->state & QStyle::State_MouseOver) + { + painter->drawEllipse(QPointF(buttonRect.center().x() + 1, buttonRect.center().y() + 1), buttonRect.width() / 4.5, buttonRect.width() / 4.5); + } + } + else + { + if (bopt->state & QStyle::State_MouseOver) + { + painter->drawEllipse(QPointF(buttonRect.center().x() + 1, buttonRect.center().y() + 1), buttonRect.width() / 3.5, buttonRect.width() / 3.5); + } + else + { + painter->drawEllipse(QPointF(buttonRect.center().x() + 1, buttonRect.center().y() + 1), buttonRect.width() / 4, buttonRect.width() / 4); + } + } + } + painter->restore(); + return; + } + default: + { + break; + } + } + + QProxyStyle::drawPrimitive(element, option, painter, widget); +} + +int ElaRadioButtonStyle::pixelMetric(PixelMetric metric, const QStyleOption* option, const QWidget* widget) const +{ + switch (metric) + { + case QStyle::PM_ExclusiveIndicatorWidth: + { + return 20; + } + case QStyle::PM_ExclusiveIndicatorHeight: + { + return 20; + } + default: + { + break; + } + } + return QProxyStyle::pixelMetric(metric, option, widget); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaRadioButtonStyle.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaRadioButtonStyle.h new file mode 100644 index 0000000..81767d6 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaRadioButtonStyle.h @@ -0,0 +1,20 @@ +#ifndef ELARADIOBUTTONSTYLE_H +#define ELARADIOBUTTONSTYLE_H + +#include + +#include "Def.h" +class ElaRadioButtonStyle : public QProxyStyle +{ + Q_OBJECT +public: + explicit ElaRadioButtonStyle(QStyle* style = nullptr); + ~ElaRadioButtonStyle(); + void drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget = nullptr) const override; + int pixelMetric(PixelMetric metric, const QStyleOption* option = nullptr, const QWidget* widget = nullptr) const override; + +private: + ElaThemeType::ThemeMode _themeMode; +}; + +#endif // ELARADIOBUTTONSTYLE_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaScrollBarStyle.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaScrollBarStyle.cpp new file mode 100644 index 0000000..4ac8a8d --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaScrollBarStyle.cpp @@ -0,0 +1,192 @@ +#include "ElaScrollBarStyle.h" + +#include +#include +#include +#include +#include + +#include "ElaScrollBar.h" +#include "ElaTheme.h" +ElaScrollBarStyle::ElaScrollBarStyle(QStyle* style) +{ + _pIsExpand = false; + _pOpacity = 0; + _pScrollBar = nullptr; + _pSliderExtent = 2.4; + _themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { _themeMode = themeMode; }); +} + +ElaScrollBarStyle::~ElaScrollBarStyle() +{ +} + +void ElaScrollBarStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex* option, QPainter* painter, const QWidget* widget) const +{ + //QStyle::SC_ScrollBarGroove QStyle::SC_ScrollBarAddLine QStyle::SC_ScrollBarSubLine上指示器 + switch (control) + { + case QStyle::CC_ScrollBar: + { + if (const QStyleOptionSlider* sopt = qstyleoption_cast(option)) + { + painter->save(); + painter->setRenderHint(QPainter::Antialiasing); + painter->setPen(Qt::NoPen); + QRect scrollBarRect = sopt->rect; + if (_pIsExpand) + { + // 背景绘制 + painter->setOpacity(_pOpacity); + painter->setPen(Qt::NoPen); + painter->setBrush(ElaThemeColor(_themeMode, BasicBase)); + painter->drawRoundedRect(scrollBarRect, 6, 6); + //指示器绘制 center()在此处不适用 存在外围边距宽度 需手动计算 + int sideLength = 8; + painter->setBrush(ElaThemeColor(_themeMode, ScrollBarHandle)); + if (sopt->orientation == Qt::Horizontal) + { + QRect leftIndicatorRect = subControlRect(control, sopt, QStyle::SC_ScrollBarSubLine, widget); + QRect rightIndicatorRect = subControlRect(control, sopt, QStyle::SC_ScrollBarAddLine, widget); + // 左三角 + qreal centerLeftX = leftIndicatorRect.x() + leftIndicatorRect.width() / 2; + qreal centerRightX = rightIndicatorRect.x() + rightIndicatorRect.width() / 2; + qreal centerY = leftIndicatorRect.height() / 2; + QPainterPath leftPath; + leftPath.moveTo(centerLeftX - qCos(30 * M_PI / 180.0) * sideLength / 2, centerY); + leftPath.lineTo(centerLeftX + qCos(30 * M_PI / 180.0) * sideLength / 2, centerY + sideLength / 2); + leftPath.lineTo(centerLeftX + qCos(30 * M_PI / 180.0) * sideLength / 2, centerY - sideLength / 2); + leftPath.closeSubpath(); + painter->drawPath(leftPath); + + // 右三角 + QPainterPath rightPath; + rightPath.moveTo(centerRightX + qCos(30 * M_PI / 180.0) * sideLength / 2, centerY); + rightPath.lineTo(centerRightX - qCos(30 * M_PI / 180.0) * sideLength / 2, centerY + sideLength / 2); + rightPath.lineTo(centerRightX - qCos(30 * M_PI / 180.0) * sideLength / 2, centerY - sideLength / 2); + rightPath.closeSubpath(); + painter->drawPath(rightPath); + } + else + { + QRect upIndicatorRect = subControlRect(control, sopt, QStyle::SC_ScrollBarSubLine, widget); + QRect downIndicatorRect = subControlRect(control, sopt, QStyle::SC_ScrollBarAddLine, widget); + qreal centerToTop = (sideLength / 2) / qCos(30 * M_PI / 180.0); + qreal centerToBottom = (sideLength / 2) * qTan(30 * M_PI / 180.0); + // 上三角 + qreal centerX = upIndicatorRect.width() / 2.0; + qreal centerUpY = upIndicatorRect.center().y() + 2; + qreal centerDownY = downIndicatorRect.center().y() + 2; + QPainterPath upPath; + upPath.moveTo(centerX, centerUpY - centerToTop); + upPath.lineTo(centerX + sideLength / 2, centerUpY + centerToBottom); + upPath.lineTo(centerX - sideLength / 2, centerUpY + centerToBottom); + upPath.closeSubpath(); + painter->drawPath(upPath); + + // 下三角 + QPainterPath downPath; + downPath.moveTo(centerX, centerDownY + centerToBottom); + downPath.lineTo(centerX + sideLength / 2, centerDownY - centerToTop); + downPath.lineTo(centerX - sideLength / 2, centerDownY - centerToTop); + downPath.closeSubpath(); + painter->drawPath(downPath); + } + } + painter->setOpacity(1); + //滑块绘制 + QRectF sliderRect = subControlRect(control, sopt, QStyle::SC_ScrollBarSlider, widget); + painter->setBrush(ElaThemeColor(_themeMode, ScrollBarHandle)); + if (sopt->orientation == Qt::Horizontal) + { + sliderRect.setRect(sliderRect.x(), sliderRect.bottom() - _sliderMargin - _pSliderExtent, sliderRect.width(), _pSliderExtent); + } + else + { + sliderRect.setRect(sliderRect.right() - _sliderMargin - _pSliderExtent, sliderRect.y(), _pSliderExtent, sliderRect.height()); + } + painter->drawRoundedRect(sliderRect, _pSliderExtent / 2.0, _pSliderExtent / 2.0); + painter->restore(); + } + return; + } + default: + { + break; + } + } + QProxyStyle::drawComplexControl(control, option, painter, widget); +} + +int ElaScrollBarStyle::pixelMetric(PixelMetric metric, const QStyleOption* option, const QWidget* widget) const +{ + // qDebug() << metric << QProxyStyle::pixelMetric(metric, option, widget); + switch (metric) + { + case QStyle::PM_ScrollBarExtent: + { + return _scrollBarExtent; + } + default: + { + break; + } + } + return QProxyStyle::pixelMetric(metric, option, widget); +} + +int ElaScrollBarStyle::styleHint(StyleHint hint, const QStyleOption* option, const QWidget* widget, QStyleHintReturn* returnData) const +{ + if (hint == QStyle::SH_ScrollBar_LeftClickAbsolutePosition) + { + return true; + } + return QProxyStyle::styleHint(hint, option, widget, returnData); +} + +void ElaScrollBarStyle::startExpandAnimation(bool isExpand) +{ + if (isExpand) + { + _pIsExpand = true; + QPropertyAnimation* opacityAnimation = new QPropertyAnimation(this, "pOpacity"); + connect(opacityAnimation, &QPropertyAnimation::valueChanged, this, [=]() { + _pScrollBar->update(); + }); + opacityAnimation->setDuration(250); + opacityAnimation->setEasingCurve(QEasingCurve::InOutSine); + opacityAnimation->setStartValue(_pOpacity); + opacityAnimation->setEndValue(1); + opacityAnimation->start(QAbstractAnimation::DeleteWhenStopped); + + QPropertyAnimation* extentAnimation = new QPropertyAnimation(this, "pSliderExtent"); + extentAnimation->setDuration(250); + extentAnimation->setEasingCurve(QEasingCurve::InOutSine); + extentAnimation->setStartValue(_pSliderExtent); + extentAnimation->setEndValue(_scrollBarExtent - 2 * _sliderMargin); + extentAnimation->start(QAbstractAnimation::DeleteWhenStopped); + } + else + { + QPropertyAnimation* opacityAnimation = new QPropertyAnimation(this, "pOpacity"); + connect(opacityAnimation, &QPropertyAnimation::finished, this, [=]() { + _pIsExpand = false; + }); + connect(opacityAnimation, &QPropertyAnimation::valueChanged, this, [=]() { + _pScrollBar->update(); + }); + opacityAnimation->setDuration(250); + opacityAnimation->setEasingCurve(QEasingCurve::InOutSine); + opacityAnimation->setStartValue(_pOpacity); + opacityAnimation->setEndValue(0); + opacityAnimation->start(QAbstractAnimation::DeleteWhenStopped); + + QPropertyAnimation* extentAnimation = new QPropertyAnimation(this, "pSliderExtent"); + extentAnimation->setDuration(250); + extentAnimation->setEasingCurve(QEasingCurve::InOutSine); + extentAnimation->setStartValue(_pSliderExtent); + extentAnimation->setEndValue(2.4); + extentAnimation->start(QAbstractAnimation::DeleteWhenStopped); + } +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaScrollBarStyle.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaScrollBarStyle.h new file mode 100644 index 0000000..ccb31b4 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaScrollBarStyle.h @@ -0,0 +1,28 @@ +#ifndef ELASCROLLBARSTYLE_H +#define ELASCROLLBARSTYLE_H +#include + +#include "Def.h" +class ElaScrollBar; +class ElaScrollBarStyle : public QProxyStyle +{ + Q_OBJECT + Q_PRIVATE_CREATE(bool, IsExpand) + Q_PROPERTY_CREATE(qreal, Opacity) + Q_PROPERTY_CREATE(qreal, SliderExtent) + Q_PRIVATE_CREATE(ElaScrollBar*, ScrollBar) +public: + explicit ElaScrollBarStyle(QStyle* style = nullptr); + ~ElaScrollBarStyle(); + void drawComplexControl(ComplexControl control, const QStyleOptionComplex* option, QPainter* painter, const QWidget* widget = nullptr) const override; + int pixelMetric(PixelMetric metric, const QStyleOption* option = nullptr, const QWidget* widget = nullptr) const override; + int styleHint(StyleHint hint, const QStyleOption* option = nullptr, const QWidget* widget = nullptr, QStyleHintReturn* returnData = nullptr) const override; + void startExpandAnimation(bool isExpand); + +private: + ElaThemeType::ThemeMode _themeMode; + qreal _sliderMargin{2.5}; + int _scrollBarExtent{10}; +}; + +#endif // ELASCROLLBARSTYLE_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaSliderStyle.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaSliderStyle.cpp new file mode 100644 index 0000000..4008ed0 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaSliderStyle.cpp @@ -0,0 +1,164 @@ +#include "ElaSliderStyle.h" + +#include +#include +#include + +#include "ElaTheme.h" +ElaSliderStyle::ElaSliderStyle(QStyle* style) +{ + setProperty("circleRadius", 0.01); + _themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { _themeMode = themeMode; }); +} + +ElaSliderStyle::~ElaSliderStyle() +{ +} + +void ElaSliderStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex* option, QPainter* painter, const QWidget* widget) const +{ + switch (control) + { + case QStyle::CC_Slider: + { + const QStyleOptionSlider* sopt = qstyleoption_cast(option); + if (!sopt) + { + break; + } + + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + QRect sliderRect = sopt->rect; + QRect sliderHandleRect = subControlRect(control, sopt, SC_SliderHandle, widget); + sliderHandleRect.adjust(1, 1, -1, -1); + // 滑槽 + painter->setPen(Qt::NoPen); + painter->setBrush(ElaThemeColor(_themeMode, BasicChute)); + if (sopt->orientation == Qt::Horizontal) + { + // 未滑过 + painter->drawRoundedRect(QRect(sliderRect.x() + sliderRect.height() / 8, sliderRect.y() + sliderRect.height() * 0.375, sliderRect.width() - sliderRect.height() / 4, sliderRect.height() / 4), sliderRect.height() / 8, sliderRect.height() / 8); + // 已滑过 + painter->setBrush(ElaThemeColor(_themeMode, PrimaryNormal)); + painter->drawRoundedRect(QRect(sliderRect.x() + sliderRect.height() / 8, sliderRect.y() + sliderRect.height() * 0.375, sliderHandleRect.x(), sliderRect.height() / 4), sliderRect.height() / 8, sliderRect.height() / 8); + } + else + { + // 未滑过 + painter->drawRoundedRect(QRect(sliderRect.x() + sliderRect.width() * 0.375, sliderRect.y() + sliderRect.width() / 8, sliderRect.width() / 4, sliderRect.height() - sliderRect.width() / 4), sliderRect.width() / 8, sliderRect.width() / 8); + // 已滑过 + painter->setBrush(ElaThemeColor(_themeMode, PrimaryNormal)); + painter->drawRoundedRect(QRect(sliderRect.x() + sliderRect.width() * 0.375, sliderHandleRect.y(), sliderRect.width() / 4, sliderRect.height() - sliderRect.width() / 8 - sliderHandleRect.y()), sliderRect.width() / 8, sliderRect.width() / 8); + } + // 滑块 + // 外圆形 + painter->setPen(ElaThemeColor(_themeMode, BasicBorder)); + painter->setBrush(ElaThemeColor(_themeMode, BasicBase)); + painter->drawEllipse(QPointF(sliderHandleRect.center().x() + 1, sliderHandleRect.center().y() + 1), sliderHandleRect.width() / 2, sliderHandleRect.width() / 2); + // 内圆形 + painter->setPen(Qt::NoPen); + painter->setBrush(ElaThemeColor(_themeMode, PrimaryNormal)); + if (_lastState == 0) + { + _lastState = sopt->state; + } + if (_circleRadius == 0) + { + _circleRadius = sliderHandleRect.width() / 3.8; + } + if (sopt->activeSubControls == SC_SliderHandle) + { + if (sopt->state & QStyle::State_Sunken) + { + if (sopt->state & QStyle::State_MouseOver) + { + if (!_lastState.testFlag(QStyle::State_Sunken)) + { + _startRadiusAnimation(sliderHandleRect.width() / 2.8, sliderHandleRect.width() / 4.5, const_cast(widget)); + _lastState = sopt->state; + } + painter->drawEllipse(QPointF(sliderHandleRect.center().x() + 1, sliderHandleRect.center().y() + 1), _circleRadius, _circleRadius); + } + } + else + { + if (sopt->state & QStyle::State_MouseOver) + { + if (!_lastState.testFlag(QStyle::State_MouseOver)) + { + _startRadiusAnimation(_circleRadius, sliderHandleRect.width() / 2.8, const_cast(widget)); + _lastState = sopt->state; + } + if (_lastState.testFlag(QStyle::State_Sunken)) + { + _startRadiusAnimation(_circleRadius, sliderHandleRect.width() / 2.8, const_cast(widget)); + _lastState = sopt->state; + } + painter->drawEllipse(QPointF(sliderHandleRect.center().x() + 1, sliderHandleRect.center().y() + 1), _circleRadius, _circleRadius); + } + } + } + else + { + if (_lastState.testFlag(QStyle::State_MouseOver) || _lastState.testFlag(QStyle::State_Sunken)) + { + _startRadiusAnimation(_circleRadius, sliderHandleRect.width() / 3.8, const_cast(widget)); + _lastState &= ~QStyle::State_MouseOver; + _lastState &= ~QStyle::State_Sunken; + } + painter->drawEllipse(QPointF(sliderHandleRect.center().x() + 1, sliderHandleRect.center().y() + 1), _circleRadius, _circleRadius); + } + painter->restore(); + return; + } + default: + { + break; + } + } + QProxyStyle::drawComplexControl(control, option, painter, widget); +} + +int ElaSliderStyle::pixelMetric(PixelMetric metric, const QStyleOption* option, const QWidget* widget) const +{ + switch (metric) + { + case QStyle::PM_SliderLength: + { + return 20; + } + case QStyle::PM_SliderThickness: + { + return 20; + } + default: + { + break; + } + } + return QProxyStyle::pixelMetric(metric, option, widget); +} + +int ElaSliderStyle::styleHint(StyleHint hint, const QStyleOption* option, const QWidget* widget, QStyleHintReturn* returnData) const +{ + if (hint == QStyle::SH_Slider_AbsoluteSetButtons) + { + return Qt::LeftButton; + } + return QProxyStyle::styleHint(hint, option, widget, returnData); +} + +void ElaSliderStyle::_startRadiusAnimation(qreal startRadius, qreal endRadius, QWidget* widget) const +{ + ElaSliderStyle* style = const_cast(this); + QPropertyAnimation* circleRadiusAnimation = new QPropertyAnimation(style, "circleRadius"); + connect(circleRadiusAnimation, &QPropertyAnimation::valueChanged, style, [=](const QVariant& value) { + this->_circleRadius = value.toReal(); + widget->update(); }); + circleRadiusAnimation->setEasingCurve(QEasingCurve::InOutSine); + circleRadiusAnimation->setStartValue(startRadius); + circleRadiusAnimation->setEndValue(endRadius); + circleRadiusAnimation->start(QAbstractAnimation::DeleteWhenStopped); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaSliderStyle.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaSliderStyle.h new file mode 100644 index 0000000..0b24acd --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaSliderStyle.h @@ -0,0 +1,24 @@ +#ifndef ELASLIDERSTYLE_H +#define ELASLIDERSTYLE_H + +#include + +#include "Def.h" +class ElaSliderStyle : public QProxyStyle +{ + Q_OBJECT +public: + explicit ElaSliderStyle(QStyle* style = nullptr); + ~ElaSliderStyle(); + void drawComplexControl(ComplexControl control, const QStyleOptionComplex* option, QPainter* painter, const QWidget* widget = nullptr) const override; + int pixelMetric(PixelMetric metric, const QStyleOption* option = nullptr, const QWidget* widget = nullptr) const override; + int styleHint(StyleHint hint, const QStyleOption* option = nullptr, const QWidget* widget = nullptr, QStyleHintReturn* returnData = nullptr) const override; + +private: + ElaThemeType::ThemeMode _themeMode; + mutable QStyle::State _lastState{0}; + mutable qreal _circleRadius{0}; + void _startRadiusAnimation(qreal startRadius, qreal endRadius, QWidget* widget) const; +}; + +#endif // ELASLIDERSTYLE_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaSpinBoxStyle.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaSpinBoxStyle.cpp new file mode 100644 index 0000000..324294b --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaSpinBoxStyle.cpp @@ -0,0 +1,173 @@ +#include "ElaSpinBoxStyle.h" + +#include +#include +#include +#include + +#include "ElaTheme.h" +ElaSpinBoxStyle::ElaSpinBoxStyle(QStyle* style) +{ + _themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { _themeMode = themeMode; }); +} + +ElaSpinBoxStyle::~ElaSpinBoxStyle() +{ +} + +void ElaSpinBoxStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex* option, QPainter* painter, const QWidget* widget) const +{ + switch (control) + { + case QStyle::CC_SpinBox: + { + const QStyleOptionSpinBox* sopt = qstyleoption_cast(option); + if (!sopt) + { + break; + } + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + //背景 + painter->setPen(ElaThemeColor(_themeMode, BasicBorder)); + painter->setBrush(ElaThemeColor(_themeMode, BasicBase)); + painter->drawRoundedRect(sopt->rect, 4, 4); + //添加按钮 + QRect addLineRect = subControlRect(control, sopt, SC_ScrollBarAddLine, widget); + if (sopt->activeSubControls == SC_ScrollBarAddLine) + { + if (sopt->state & QStyle::State_Sunken && sopt->state & QStyle::State_MouseOver) + { + painter->setBrush(ElaThemeColor(_themeMode, BasicPressAlpha)); + } + else + { + if (sopt->state & QStyle::State_MouseOver) + { + painter->setBrush(ElaThemeColor(_themeMode, BasicHoverAlpha)); + } + else + { + painter->setBrush(ElaThemeColor(_themeMode, BasicBaseDeep)); + } + } + } + else + { + painter->setBrush(ElaThemeColor(_themeMode, BasicBaseDeep)); + } + QPainterPath addLinePath; + addLinePath.moveTo(addLineRect.topLeft()); + addLinePath.lineTo(addLineRect.bottomLeft()); + addLinePath.lineTo(addLineRect.right() - 4, addLineRect.bottom()); + addLinePath.arcTo(QRectF(addLineRect.right() - 8, addLineRect.bottom() - 8, 8, 8), -90, 90); + addLinePath.lineTo(addLineRect.right(), addLineRect.y() + 4); + addLinePath.arcTo(QRectF(addLineRect.right() - 8, addLineRect.y(), 8, 8), 0, 90); + addLinePath.closeSubpath(); + painter->drawPath(addLinePath); + + //减少按钮 + QRect subLineRect = subControlRect(control, sopt, SC_ScrollBarSubLine, widget); + if (sopt->activeSubControls == SC_ScrollBarSubLine) + { + if (sopt->state & QStyle::State_Sunken && sopt->state & QStyle::State_MouseOver) + { + painter->setBrush(ElaThemeColor(_themeMode, BasicPressAlpha)); + } + else + { + if (sopt->state & QStyle::State_MouseOver) + { + painter->setBrush(ElaThemeColor(_themeMode, BasicHoverAlpha)); + } + else + { + painter->setBrush(ElaThemeColor(_themeMode, BasicBaseDeep)); + } + } + } + else + { + painter->setBrush(ElaThemeColor(_themeMode, BasicBaseDeep)); + } + QPainterPath subLinePath; + subLinePath.moveTo(subLineRect.topRight()); + subLinePath.lineTo(subLineRect.x() + 4, subLineRect.y()); + subLinePath.arcTo(QRectF(subLineRect.x(), subLineRect.y(), 8, 8), 90, 90); + subLinePath.lineTo(subLineRect.x(), subLineRect.bottom() - 4); + subLinePath.arcTo(QRectF(subLineRect.x(), subLineRect.bottom() - 8, 8, 8), 180, 90); + subLinePath.lineTo(subLineRect.bottomRight()); + subLinePath.closeSubpath(); + painter->drawPath(subLinePath); + //底边线 + if (sopt->state & QStyle::State_HasFocus) + { + painter->setPen(QPen(ElaThemeColor(_themeMode, PrimaryNormal), 2)); + painter->drawLine(subLineRect.right() + 1, subLineRect.y() + subLineRect.height() - 2, addLineRect.left() - 1, subLineRect.y() + subLineRect.height() - 2); + } + else + { + painter->setPen(ElaThemeColor(_themeMode, BasicHemline)); + painter->drawLine(subLineRect.right() + 1, subLineRect.y() + subLineRect.height() - 1, addLineRect.left() - 1, subLineRect.y() + subLineRect.height() - 1); + } + + //添加图标 + QFont iconFont = QFont("ElaAwesome"); + iconFont.setPixelSize(17); + painter->setFont(iconFont); + painter->setPen(ElaThemeColor(_themeMode, BasicText)); + painter->drawText(addLineRect, Qt::AlignCenter, QChar((unsigned short)ElaIconType::Plus)); + //减小图标 + painter->drawText(subLineRect, Qt::AlignCenter, QChar((unsigned short)ElaIconType::Minus)); + painter->restore(); + return; + } + default: + { + break; + } + } + QProxyStyle::drawComplexControl(control, option, painter, widget); +} + +QRect ElaSpinBoxStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex* opt, SubControl sc, const QWidget* widget) const +{ + QRect rect = QProxyStyle::subControlRect(cc, opt, sc, widget); + switch (cc) + { + case CC_SpinBox: + { + switch (sc) + { + case SC_ScrollBarAddLine: + { + //增加按钮 + QRect spinBoxRect = QProxyStyle::subControlRect(cc, opt, SC_SpinBoxFrame, widget); + return QRect(spinBoxRect.width() - spinBoxRect.height(), 0, spinBoxRect.height(), spinBoxRect.height()); + } + case SC_ScrollBarSubLine: + { + //减少按钮 + QRect spinBoxRect = QProxyStyle::subControlRect(cc, opt, SC_SpinBoxFrame, widget); + return QRect(0, 0, spinBoxRect.height(), spinBoxRect.height()); + } + case SC_SpinBoxEditField: + { + QRect spinBoxRect = QProxyStyle::subControlRect(cc, opt, SC_SpinBoxFrame, widget); + return QRect(spinBoxRect.height(), 0, spinBoxRect.width() - 2 * spinBoxRect.height(), spinBoxRect.height()); + } + default: + { + break; + } + } + break; + } + default: + { + break; + } + } + return rect; +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaSpinBoxStyle.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaSpinBoxStyle.h new file mode 100644 index 0000000..0888acb --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaSpinBoxStyle.h @@ -0,0 +1,20 @@ +#ifndef ELASPINBOXSTYLE_H +#define ELASPINBOXSTYLE_H + +#include + +#include "Def.h" +class ElaSpinBoxStyle : public QProxyStyle +{ + Q_OBJECT +public: + explicit ElaSpinBoxStyle(QStyle* style = nullptr); + ~ElaSpinBoxStyle(); + void drawComplexControl(ComplexControl control, const QStyleOptionComplex* option, QPainter* painter, const QWidget* widget = nullptr) const override; + QRect subControlRect(ComplexControl cc, const QStyleOptionComplex* opt, SubControl sc, const QWidget* widget) const override; + +private: + ElaThemeType::ThemeMode _themeMode; +}; + +#endif // ELASPINBOXSTYLE_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaStatusBarStyle.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaStatusBarStyle.cpp new file mode 100644 index 0000000..dc87f11 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaStatusBarStyle.cpp @@ -0,0 +1,80 @@ +#include "ElaStatusBarStyle.h" + +#include +#include +#include + +#include "ElaTheme.h" +ElaStatusBarStyle::ElaStatusBarStyle(QStyle* style) +{ + _themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { _themeMode = themeMode; }); +} + +ElaStatusBarStyle::~ElaStatusBarStyle() +{ +} + +void ElaStatusBarStyle::drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const +{ + switch (element) + { + case QStyle::PE_PanelStatusBar: + { + //背景绘制 + QRect statusBarRect = option->rect; + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + painter->setPen(ElaThemeColor(_themeMode, BasicBorder)); + painter->setBrush(ElaThemeColor(_themeMode, BasicBaseAlpha)); + painter->drawRect(statusBarRect); + painter->restore(); + return; + } + case QStyle::PE_FrameStatusBarItem: + { + //间隔符绘制 + QRect statusBarItemRect = option->rect; + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + painter->setPen(Qt::NoPen); + painter->setBrush(ElaThemeColor(_themeMode, PrimaryNormal)); + painter->drawRoundedRect(QRectF(statusBarItemRect.right() - 3, statusBarItemRect.y() + statusBarItemRect.height() * 0.1, 3, statusBarItemRect.height() - statusBarItemRect.height() * 0.2), 2, 2); + painter->restore(); + return; + } + default: + { + break; + } + } + QProxyStyle::drawPrimitive(element, option, painter, widget); +} + +void ElaStatusBarStyle::drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const +{ + switch (element) + { + case QStyle::CE_SizeGrip: + { + return; + } + default: + { + break; + } + } + + QProxyStyle::drawControl(element, option, painter, widget); +} + +QSize ElaStatusBarStyle::sizeFromContents(ContentsType type, const QStyleOption* option, const QSize& size, const QWidget* widget) const +{ + //qDebug() << type << QProxyStyle::sizeFromContents(type, option, size, widget); + return QProxyStyle::sizeFromContents(type, option, size, widget); +} + +int ElaStatusBarStyle::pixelMetric(PixelMetric metric, const QStyleOption* option, const QWidget* widget) const +{ + return QProxyStyle::pixelMetric(metric, option, widget); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaStatusBarStyle.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaStatusBarStyle.h new file mode 100644 index 0000000..587f4c5 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaStatusBarStyle.h @@ -0,0 +1,22 @@ +#ifndef ELASTATUSBARSTYLE_H +#define ELASTATUSBARSTYLE_H + +#include + +#include "Def.h" +class ElaStatusBarStyle : public QProxyStyle +{ + Q_OBJECT +public: + explicit ElaStatusBarStyle(QStyle* style = nullptr); + ~ElaStatusBarStyle(); + void drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget = nullptr) const override; + void drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget = nullptr) const override; + QSize sizeFromContents(ContentsType type, const QStyleOption* option, const QSize& size, const QWidget* widget) const override; + int pixelMetric(PixelMetric metric, const QStyleOption* option = nullptr, const QWidget* widget = nullptr) const override; + +private: + ElaThemeType::ThemeMode _themeMode; +}; + +#endif // ELASTATUSBARSTYLE_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaSuggestBoxSearchViewContainer.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaSuggestBoxSearchViewContainer.cpp new file mode 100644 index 0000000..59ad7ff --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaSuggestBoxSearchViewContainer.cpp @@ -0,0 +1,31 @@ +#include "ElaSuggestBoxSearchViewContainer.h" + +#include + +#include "ElaTheme.h" +ElaSuggestBoxSearchViewContainer::ElaSuggestBoxSearchViewContainer(QWidget* parent) + : QWidget{parent} +{ + setContentsMargins(8, 8, 8, 8); + setObjectName("ElaSuggestBoxSearchViewBaseWidget"); + setStyleSheet("#ElaSuggestBoxSearchViewBaseWidget{background-color:transparent}"); + _themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { _themeMode = themeMode; }); +} + +ElaSuggestBoxSearchViewContainer::~ElaSuggestBoxSearchViewContainer() +{ +} + +void ElaSuggestBoxSearchViewContainer::paintEvent(QPaintEvent* event) +{ + QPainter painter(this); + painter.save(); + painter.setRenderHints(QPainter::Antialiasing); + eTheme->drawEffectShadow(&painter, rect(), 6, 8); + painter.setPen(ElaThemeColor(_themeMode, PopupBorder)); + painter.setBrush(ElaThemeColor(_themeMode, PopupBase)); + QRect foregroundRect(6, 0, rect().width() - 2 * 6, rect().height() - 6); + painter.drawRoundedRect(foregroundRect, 8, 8); + painter.restore(); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaSuggestBoxSearchViewContainer.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaSuggestBoxSearchViewContainer.h new file mode 100644 index 0000000..b975db7 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaSuggestBoxSearchViewContainer.h @@ -0,0 +1,21 @@ +#ifndef ELASUGGESTBOXSEARCHVIEWCONTAINER_H +#define ELASUGGESTBOXSEARCHVIEWCONTAINER_H + +#include + +#include "Def.h" +class ElaSuggestBoxSearchViewContainer : public QWidget +{ + Q_OBJECT +public: + explicit ElaSuggestBoxSearchViewContainer(QWidget* parent = nullptr); + ~ElaSuggestBoxSearchViewContainer(); + +protected: + virtual void paintEvent(QPaintEvent* event) override; + +private: + ElaThemeType::ThemeMode _themeMode; +}; + +#endif // ELASUGGESTBOXSEARCHVIEWCONTAINER_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaSuggestDelegate.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaSuggestDelegate.cpp new file mode 100644 index 0000000..7c1c6da --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaSuggestDelegate.cpp @@ -0,0 +1,79 @@ +#include "ElaSuggestDelegate.h" + +#include +#include + +#include "ElaSuggestBoxPrivate.h" +#include "ElaSuggestModel.h" +#include "ElaTheme.h" +ElaSuggestDelegate::ElaSuggestDelegate(QObject* parent) + : QStyledItemDelegate{parent} +{ + _themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { _themeMode = themeMode; }); +} + +ElaSuggestDelegate::~ElaSuggestDelegate() +{ +} + +void ElaSuggestDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + int margin = 2; + QStyleOptionViewItem viewOption(option); + initStyleOption(&viewOption, index); + + ElaSuggestModel* model = dynamic_cast(const_cast(index.model())); + ElaSuggestion* suggest = model->getSearchSuggestion(index.row()); + if (option.state.testFlag(QStyle::State_HasFocus)) + { + viewOption.state &= ~QStyle::State_HasFocus; + } + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + QPainterPath path; + QRect optionRect = option.rect; + optionRect.adjust(margin * 2, margin, -margin * 2, -margin); + path.addRoundedRect(optionRect, 8, 8); + if (option.state & QStyle::State_Selected) + { + if (option.state & QStyle::State_MouseOver) + { + //选中时覆盖 + painter->fillPath(path, ElaThemeColor(_themeMode, BasicSelectedHoverAlpha)); + } + else + { + //选中 + painter->fillPath(path, ElaThemeColor(_themeMode, BasicSelectedAlpha)); + } + } + else + { + if (option.state & QStyle::State_MouseOver) + { + //覆盖时颜色 + painter->fillPath(path, ElaThemeColor(_themeMode, BasicHoverAlpha)); + } + } + //文字绘制 + painter->setPen(ElaThemeColor(_themeMode, BasicText)); + painter->drawText(option.rect.x() + 37, option.rect.y() + 25, suggest->getSuggestText()); + + //图标绘制 + if (suggest->getElaIcon() != ElaIconType::None) + { + QFont iconFont = QFont("ElaAwesome"); + iconFont.setPixelSize(17); + painter->setFont(iconFont); + painter->drawText(option.rect.x() + 11, option.rect.y() + 26, QChar((unsigned short)suggest->getElaIcon())); + } + painter->restore(); +} + +QSize ElaSuggestDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + QSize size = QStyledItemDelegate::sizeHint(option, index); + size.setHeight(40); + return size; +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaSuggestDelegate.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaSuggestDelegate.h new file mode 100644 index 0000000..b5f222a --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaSuggestDelegate.h @@ -0,0 +1,22 @@ +#ifndef ELASUGGESTDELEGATE_H +#define ELASUGGESTDELEGATE_H + +#include + +#include "Def.h" +class ElaSuggestDelegate : public QStyledItemDelegate +{ + Q_OBJECT +public: + explicit ElaSuggestDelegate(QObject* parent = nullptr); + ~ElaSuggestDelegate(); + +protected: + void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; + QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override; + +private: + ElaThemeType::ThemeMode _themeMode; +}; + +#endif // ELASUGGESTDELEGATE_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaSuggestModel.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaSuggestModel.cpp new file mode 100644 index 0000000..8a22adc --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaSuggestModel.cpp @@ -0,0 +1,46 @@ +#include "ElaSuggestModel.h" + +ElaSuggestModel::ElaSuggestModel(QObject* parent) + : QAbstractListModel{parent} +{ +} + +ElaSuggestModel::~ElaSuggestModel() +{ +} + +int ElaSuggestModel::rowCount(const QModelIndex& parent) const +{ + Q_UNUSED(parent) + return _suggestionVector.count(); +} + +QVariant ElaSuggestModel::data(const QModelIndex& index, int role) const +{ + return QVariant(); +} + +void ElaSuggestModel::setSearchSuggestion(QVector suggestionVector) +{ + if (suggestionVector.count() == 0) + { + return; + } + beginResetModel(); + _suggestionVector = suggestionVector; + endResetModel(); +} + +void ElaSuggestModel::clearSearchNode() +{ + this->_suggestionVector.clear(); +} + +ElaSuggestion* ElaSuggestModel::getSearchSuggestion(int row) +{ + if (row >= _suggestionVector.count()) + { + return nullptr; + } + return _suggestionVector[row]; +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaSuggestModel.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaSuggestModel.h new file mode 100644 index 0000000..6f30915 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaSuggestModel.h @@ -0,0 +1,24 @@ +#ifndef ELASUGGESTMODEL_H +#define ELASUGGESTMODEL_H + +#include + +#include "Def.h" +class ElaSuggestion; +class ElaSuggestModel : public QAbstractListModel +{ + Q_OBJECT +public: + explicit ElaSuggestModel(QObject* parent = nullptr); + ~ElaSuggestModel(); + int rowCount(const QModelIndex& parent) const; + QVariant data(const QModelIndex& index, int role) const; + void setSearchSuggestion(QVector suggestionVector); + void clearSearchNode(); + ElaSuggestion* getSearchSuggestion(int row); + +private: + QVector _suggestionVector; //符合搜索的节点 +}; + +#endif // ELASUGGESTMODEL_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaTabBarStyle.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaTabBarStyle.cpp new file mode 100644 index 0000000..29a23a7 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaTabBarStyle.cpp @@ -0,0 +1,213 @@ +#include "ElaTabBarStyle.h" + +#include +#include +#include +#include + +#include "ElaTheme.h" +ElaTabBarStyle::ElaTabBarStyle(QStyle* style) +{ + _themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { _themeMode = themeMode; }); +} + +ElaTabBarStyle::~ElaTabBarStyle() +{ +} + +void ElaTabBarStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption* opt, QPainter* p, const QWidget* w) const +{ + // qDebug() << pe << opt->rect; + switch (pe) + { + case QStyle::PE_FrameTabBarBase: + { + //底边线 + return; + } + case QStyle::PE_IndicatorArrowLeft: + { + return; + } + case QStyle::PE_IndicatorArrowRight: + { + return; + } + case QStyle::PE_PanelButtonTool: + { + //左侧隐藏图标 + return; + } + case QStyle::PE_IndicatorTabTear: + { + return; + } + case QStyle::PE_IndicatorTabClose: + { + p->save(); + p->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + if (opt->state.testFlag(QStyle::State_MouseOver)) + { + p->setPen(Qt::NoPen); + p->setBrush(ElaThemeColor(_themeMode, BasicHoverAlpha)); + p->drawRoundedRect(opt->rect, 2, 2); + } + QFont iconFont = QFont("ElaAwesome"); + iconFont.setPixelSize(16); + p->setFont(iconFont); + p->setPen(ElaThemeColor(_themeMode, BasicText)); + p->drawText(opt->rect, Qt::AlignCenter, QChar((unsigned short)ElaIconType::Xmark)); + p->restore(); + return; + } + default: + { + break; + } + } + QProxyStyle::drawPrimitive(pe, opt, p, w); +} + +void ElaTabBarStyle::drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const +{ + int margin = 9; + int topRadius = 7; + switch (element) + { + case QStyle::CE_TabBarTabShape: + { + //背景绘制 + if (const QStyleOptionTab* topt = qstyleoption_cast(option)) + { + QRect tabRect = topt->rect; + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + painter->setPen(Qt::NoPen); + if (topt->state.testFlag(QStyle::State_Selected)) + { + //选中背景绘制 + tabRect.setLeft(tabRect.left() - margin); + if (topt->position != QStyleOptionTab::End) + { + tabRect.setRight(tabRect.right() + margin + 1); + } + painter->setBrush(ElaThemeColor(_themeMode, BasicSelectedAlpha)); + QPainterPath path; + path.moveTo(tabRect.x(), tabRect.bottom() + 1); + path.arcTo(QRectF(tabRect.x() - margin, tabRect.bottom() - margin * 2 + 1, margin * 2, margin * 2), -90, 90); + path.lineTo(tabRect.x() + margin, tabRect.y() + topRadius); + path.arcTo(QRectF(tabRect.x() + margin, tabRect.y(), topRadius * 2, topRadius * 2), 180, -90); + path.lineTo(tabRect.right() - margin - topRadius, tabRect.y()); + path.arcTo(QRectF(tabRect.right() - margin - 2 * topRadius, tabRect.y(), topRadius * 2, topRadius * 2), 90, -90); + path.lineTo(tabRect.right() - margin, tabRect.bottom() - margin); + path.arcTo(QRectF(tabRect.right() - margin, tabRect.bottom() - 2 * margin + 1, margin * 2, margin * 2), -180, 90); + path.lineTo(tabRect.right(), tabRect.bottom() + 10); + path.lineTo(tabRect.x(), tabRect.bottom() + 10); + path.closeSubpath(); + painter->drawPath(path); + } + else + { + if (topt->state.testFlag(QStyle::State_MouseOver)) + { + painter->setBrush(ElaThemeColor(_themeMode, BasicHoverAlpha)); + } + else + { + painter->setBrush(Qt::transparent); + } + tabRect.setHeight(tabRect.height() + 10); + painter->drawRoundedRect(tabRect, 0, 0); + tabRect.setHeight(tabRect.height() - 10); + } + + //间隔符绘制 + if (!topt->state.testFlag(QStyle::State_Selected) && topt->position != QStyleOptionTab::End && topt->selectedPosition != QStyleOptionTab::NextIsSelected) + { + painter->setPen(Qt::NoPen); + painter->setBrush(ElaThemeColor(_themeMode, PrimaryNormal)); + painter->drawRoundedRect(QRectF(tabRect.right() - 3, tabRect.y() + 7, 3, tabRect.height() - 14), 2, 2); + } + painter->restore(); + return; + } + } + case QStyle::CE_TabBarTabLabel: + { + //文字和图标绘制 + if (const QStyleOptionTab* topt = qstyleoption_cast(option)) + { + QRect textRect = subElementRect(QStyle::SE_TabBarTabText, topt, widget); + textRect.setLeft(textRect.left() + 10); + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform); + //图标绘制 + QIcon icon = topt->icon; + if (!icon.isNull()) + { + QRectF iconRect(topt->rect.x() + 15, textRect.center().y() - (qreal)topt->iconSize.height() / 2 + 1, topt->iconSize.width(), topt->iconSize.height()); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QPixmap iconPix = icon.pixmap(topt->iconSize, painter->device()->devicePixelRatio(), + (topt->state & State_Enabled) ? QIcon::Normal + : QIcon::Disabled, + (topt->state & State_Selected) ? QIcon::On + : QIcon::Off); +#else + QPixmap iconPix = icon.pixmap(topt->iconSize, + (topt->state & State_Enabled) ? QIcon::Normal + : QIcon::Disabled, + (topt->state & State_Selected) ? QIcon::On + : QIcon::Off); +#endif + painter->drawPixmap(iconRect.x(), iconRect.y(), iconPix); + } + //文字绘制 + painter->setPen(ElaThemeColor(_themeMode, BasicText)); + QString text = painter->fontMetrics().elidedText(topt->text, Qt::ElideRight, textRect.width()); + painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextDontClip, text); + painter->restore(); + return; + } + } + default: + { + break; + } + } + + QProxyStyle::drawControl(element, option, painter, widget); +} + +QSize ElaTabBarStyle::sizeFromContents(ContentsType type, const QStyleOption* option, const QSize& size, const QWidget* widget) const +{ + switch (type) + { + case CT_TabBarTab: + { + return QSize(220, 35); + } + default: + { + break; + } + } + return QProxyStyle::sizeFromContents(type, option, size, widget); +} + +QRect ElaTabBarStyle::subElementRect(SubElement element, const QStyleOption* option, const QWidget* widget) const +{ + switch (element) + { + case QStyle::SE_TabBarScrollLeftButton: + case QStyle::SE_TabBarScrollRightButton: + { + return QRect(); + } + default: + { + break; + } + } + return QProxyStyle::subElementRect(element, option, widget); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaTabBarStyle.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaTabBarStyle.h new file mode 100644 index 0000000..d1e69f8 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaTabBarStyle.h @@ -0,0 +1,22 @@ +#ifndef ELATABBARSTYLE_H +#define ELATABBARSTYLE_H + +#include + +#include "Def.h" +class ElaTabBarStyle : public QProxyStyle +{ + Q_OBJECT +public: + explicit ElaTabBarStyle(QStyle* style = nullptr); + ~ElaTabBarStyle(); + void drawPrimitive(QStyle::PrimitiveElement pe, const QStyleOption* opt, QPainter* p, const QWidget* w) const override; + void drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget = nullptr) const override; + QSize sizeFromContents(ContentsType type, const QStyleOption* option, const QSize& size, const QWidget* widget) const override; + QRect subElementRect(SubElement element, const QStyleOption* option, const QWidget* widget) const override; + +private: + ElaThemeType::ThemeMode _themeMode; +}; + +#endif // ELATABBARSTYLE_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaTableViewStyle.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaTableViewStyle.cpp new file mode 100644 index 0000000..28e8a64 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaTableViewStyle.cpp @@ -0,0 +1,256 @@ +#include "ElaTableViewStyle.h" + +#include +#include +#include + +#include "ElaTableView.h" +#include "ElaTheme.h" +ElaTableViewStyle::ElaTableViewStyle(QStyle* style) +{ + _pHeaderMargin = 6; + _pCurrentHoverRow = -1; + _themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { + _themeMode = themeMode; + }); +} + +ElaTableViewStyle::~ElaTableViewStyle() +{ +} + +void ElaTableViewStyle::drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const +{ + //qDebug() << element << option->rect << widget->objectName(); + switch (element) + { + case QStyle::PE_PanelItemViewItem: + { + // 行覆盖绘制 + if (const QStyleOptionViewItem* vopt = qstyleoption_cast(option)) + { + const ElaTableView* tabView = dynamic_cast(widget); + if (!tabView) + { + return; + } + painter->save(); + painter->setRenderHint(QPainter::Antialiasing); + QAbstractItemView::SelectionBehavior selectionBehavior = tabView->selectionBehavior(); + if (selectionBehavior == QAbstractItemView::SelectRows) + { + if (vopt->index.row() == _pCurrentHoverRow) + { + painter->setPen(Qt::NoPen); + painter->setBrush(ElaThemeColor(_themeMode, BasicHoverAlpha)); + painter->drawRect(vopt->rect); + } + } + else + { + if (vopt->state.testFlag(QStyle::State_Selected) || vopt->state.testFlag(QStyle::State_MouseOver)) + { + painter->setPen(Qt::NoPen); + painter->setBrush(ElaThemeColor(_themeMode, BasicHoverAlpha)); + painter->drawRect(vopt->rect); + } + } + painter->restore(); + } + return; + } + case QStyle::PE_PanelItemViewRow: + { + if (const QStyleOptionViewItem* vopt = qstyleoption_cast(option)) + { + painter->save(); + painter->setRenderHint(QPainter::Antialiasing); + QRect itemRect = vopt->rect; + painter->setPen(Qt::NoPen); + if (vopt->state & QStyle::State_Selected) + { + // 选中 + painter->setBrush(ElaThemeColor(_themeMode, BasicSelectedAlpha)); + painter->drawRect(itemRect); + } + else + { + if (vopt->features.testFlag(QStyleOptionViewItem::Alternate)) + { + // Item背景隔行变色 + painter->setPen(Qt::NoPen); + painter->setBrush(ElaThemeColor(_themeMode, BasicAlternating)); + painter->drawRect(vopt->rect); + } + } + painter->restore(); + } + return; + } + case QStyle::PE_Widget: + { + return; + } + default: + { + break; + } + } + QProxyStyle::drawPrimitive(element, option, painter, widget); +} + +void ElaTableViewStyle::drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const +{ + //qDebug() << element << option->rect; + switch (element) + { + case QStyle::CE_ShapedFrame: + { + // viewport视口外的其他区域背景 + QRect frameRect = option->rect; + frameRect.adjust(1, 1, -1, -1); + painter->save(); + painter->setRenderHints(QPainter::Antialiasing); + painter->setPen(ElaThemeColor(_themeMode, PopupBorder)); + painter->setBrush(ElaThemeColor(_themeMode, BasicBaseAlpha)); + painter->drawRoundedRect(frameRect, 3, 3); + painter->restore(); + return; + } + case QStyle::CE_HeaderLabel: + { + // 表头文字绘制 + if (const QStyleOptionHeader* hopt = qstyleoption_cast(option)) + { + QRect headerRect = option->rect; + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing); + if (!hopt->text.isEmpty()) + { + painter->setPen(ElaThemeColor(_themeMode, BasicText)); + painter->drawText(headerRect, hopt->textAlignment, hopt->text); + } + painter->restore(); + } + return; + } + case QStyle::CE_HeaderSection: + { + // 表头背景绘制 + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing); + painter->setPen(Qt::NoPen); + painter->setBrush(ElaThemeColor(_themeMode, BasicBaseDeep)); + painter->drawRect(option->rect); + if (option->state.testFlag(QStyle::State_Sunken)) + { + painter->setBrush(ElaThemeColor(_themeMode, BasicPressAlpha)); + } + else + { + if (option->state.testFlag(QStyle::State_MouseOver)) + { + painter->setBrush(ElaThemeColor(_themeMode, BasicHoverAlpha)); + } + } + painter->drawRect(option->rect); + painter->restore(); + return; + } + case QStyle::CE_HeaderEmptyArea: + { + // 表头未使用区域背景绘制 + QRect frameRect = option->rect; + painter->save(); + painter->setRenderHints(QPainter::Antialiasing); + painter->setPen(Qt::NoPen); + painter->setBrush(ElaThemeColor(_themeMode, BasicBaseDeep)); + painter->drawRect(frameRect); + painter->restore(); + return; + } + case QStyle::CE_ItemViewItem: + { + if (const QStyleOptionViewItem* vopt = qstyleoption_cast(option)) + { + // 背景绘制 + const ElaTableView* tabView = dynamic_cast(widget); + if (!tabView) + { + return; + } + this->drawPrimitive(QStyle::PE_PanelItemViewItem, option, painter, widget); + QAbstractItemView::SelectionBehavior selectionBehavior = tabView->selectionBehavior(); + // 内容绘制 + QRect itemRect = option->rect; + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing); + // QRect checkRect = proxy()->subElementRect(SE_ItemViewItemCheckIndicator, vopt, widget); + QRect iconRect = proxy()->subElementRect(SE_ItemViewItemDecoration, vopt, widget); + QRect textRect = proxy()->subElementRect(SE_ItemViewItemText, vopt, widget); + if (vopt->index.column() == 0) + { + iconRect.adjust(_horizontalPadding, 0, _horizontalPadding, 0); + textRect.adjust(_horizontalPadding, 0, 0, 0); + } + // 图标绘制 + if (!vopt->icon.isNull()) + { + QIcon::Mode mode = QIcon::Normal; + // if (!(vopt->state.testFlag(QStyle::State_Enabled))) + // { + // mode = QIcon::Disabled; + // } + // else if (vopt->state.testFlag(QStyle::State_Selected)) + // { + // mode = QIcon::Selected; + // } + QIcon::State state = vopt->state & QStyle::State_Open ? QIcon::On : QIcon::Off; + vopt->icon.paint(painter, iconRect, vopt->decorationAlignment, mode, state); + } + // 文字绘制 + if (!vopt->text.isEmpty()) + { + painter->setPen(ElaThemeColor(_themeMode, BasicText)); + painter->drawText(textRect, vopt->displayAlignment, vopt->text); + } + // 选中特效 + int heightOffset = itemRect.height() / 4; + painter->setPen(Qt::NoPen); + painter->setBrush(ElaThemeColor(_themeMode, PrimaryNormal)); + if (vopt->state.testFlag(QStyle::State_Selected)) + { + if (selectionBehavior == QAbstractItemView::SelectRows && vopt->index.column() == 0) + { + painter->drawRoundedRect(QRectF(itemRect.x() + 3, itemRect.y() + heightOffset, 3, itemRect.height() - 2 * heightOffset), 3, 3); + } + } + painter->restore(); + } + return; + } + default: + { + break; + } + } + QProxyStyle::drawControl(element, option, painter, widget); +} + +int ElaTableViewStyle::pixelMetric(PixelMetric metric, const QStyleOption* option, const QWidget* widget) const +{ + //qDebug() << metric << QProxyStyle::pixelMetric(metric, option, widget); + switch (metric) + { + case QStyle::PM_HeaderMargin: + { + return _pHeaderMargin; + } + default: + { + break; + } + } + return QProxyStyle::pixelMetric(metric, option, widget); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaTableViewStyle.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaTableViewStyle.h new file mode 100644 index 0000000..8327880 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaTableViewStyle.h @@ -0,0 +1,25 @@ +#ifndef ELATABLEVIEWSTYLE_H +#define ELATABLEVIEWSTYLE_H +#include +#include + +#include "Def.h" +class QStyleOptionViewItem; +class ElaTableViewStyle : public QProxyStyle +{ + Q_OBJECT + Q_PROPERTY_CREATE(int, HeaderMargin) + Q_PROPERTY_CREATE(int, CurrentHoverRow) +public: + explicit ElaTableViewStyle(QStyle* style = nullptr); + ~ElaTableViewStyle(); + void drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget = nullptr) const override; + void drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget = nullptr) const override; + int pixelMetric(PixelMetric metric, const QStyleOption* option = nullptr, const QWidget* widget = nullptr) const override; + +private: + ElaThemeType::ThemeMode _themeMode; + int _horizontalPadding{11}; +}; + +#endif // ELATABLEVIEWSTYLE_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaThemeAnimationWidget.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaThemeAnimationWidget.cpp new file mode 100644 index 0000000..cdf189d --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaThemeAnimationWidget.cpp @@ -0,0 +1,44 @@ +#include "ElaThemeAnimationWidget.h" + +#include +#include +#include +ElaThemeAnimationWidget::ElaThemeAnimationWidget(QWidget* parent) + : QWidget{parent} +{ + _pEndRadius = 0.01; +} + +ElaThemeAnimationWidget::~ElaThemeAnimationWidget() +{ +} + +void ElaThemeAnimationWidget::startAnimation(int msec) +{ + QPropertyAnimation* themeChangeAnimation = new QPropertyAnimation(this, "pRadius"); + themeChangeAnimation->setDuration(msec); + themeChangeAnimation->setEasingCurve(QEasingCurve::InOutSine); + connect(themeChangeAnimation, &QPropertyAnimation::finished, this, [=]() { + Q_EMIT animationFinished(); + this->deleteLater(); + }); + connect(themeChangeAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { update(); }); + themeChangeAnimation->setStartValue(0); + themeChangeAnimation->setEndValue(_pEndRadius); + themeChangeAnimation->start(QAbstractAnimation::DeleteWhenStopped); +} + +void ElaThemeAnimationWidget::paintEvent(QPaintEvent* event) +{ + QPainter painter(this); + painter.save(); + painter.setRenderHints(QPainter::Antialiasing); + painter.setPen(Qt::NoPen); + painter.drawImage(rect(), _pOldWindowBackground); + QPainterPath path; + path.moveTo(_pCenter.x(), _pCenter.y()); + path.addEllipse(QPointF(_pCenter.x(), _pCenter.y()), _pRadius, _pRadius); + painter.setClipPath(path); + painter.drawImage(rect(), _pNewWindowBackground); + painter.restore(); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaThemeAnimationWidget.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaThemeAnimationWidget.h new file mode 100644 index 0000000..3ee9b26 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaThemeAnimationWidget.h @@ -0,0 +1,26 @@ +#ifndef ELATHEMEANIMATIONWIDGET_H +#define ELATHEMEANIMATIONWIDGET_H + +#include + +#include "stdafx.h" +class ElaThemeAnimationWidget : public QWidget +{ + Q_OBJECT + Q_PROPERTY_CREATE(qreal, Radius) + Q_PROPERTY_CREATE(qreal, EndRadius) + Q_PROPERTY_CREATE(QPoint, Center) + Q_PROPERTY_CREATE(QImage, OldWindowBackground) + Q_PROPERTY_CREATE(QImage, NewWindowBackground) +public: + explicit ElaThemeAnimationWidget(QWidget* parent = nullptr); + ~ElaThemeAnimationWidget(); + void startAnimation(int msec); +Q_SIGNALS: + Q_SIGNAL void animationFinished(); + +protected: + virtual void paintEvent(QPaintEvent* event) override; +}; + +#endif // ELATHEMEANIMATIONWIDGET_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaToolBarStyle.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaToolBarStyle.cpp new file mode 100644 index 0000000..54951ca --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaToolBarStyle.cpp @@ -0,0 +1,330 @@ +#include "ElaToolBarStyle.h" + +#include +#include +#include +#include +#include +#include + +#include "ElaTheme.h" +#include "ElaToolBar.h" +ElaToolBarStyle::ElaToolBarStyle(QStyle* style) +{ + _themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { _themeMode = themeMode; }); +} + +ElaToolBarStyle::~ElaToolBarStyle() +{ +} + +void ElaToolBarStyle::drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const +{ + switch (element) + { + case QStyle::PE_PanelButtonTool: + { + return; + } + case QStyle::PE_FrameMenu: + { + painter->save(); + painter->setRenderHint(QPainter::Antialiasing); + painter->setPen(ElaThemeColor(_themeMode, PopupBorder)); + painter->setBrush(ElaThemeColor(_themeMode, DialogBase)); + painter->drawRect(option->rect); + painter->restore(); + return; + } + case QStyle::PE_IndicatorToolBarHandle: + { + //拖动虚线 + QRect handleRect = option->rect; + painter->save(); + painter->setRenderHint(QPainter::Antialiasing); + QPen handlePen(Qt::DotLine); + handlePen.setColor(ElaThemeColor(_themeMode, PrimaryNormal)); + handlePen.setWidthF(1.8); + painter->setPen(handlePen); + const ElaToolBar* toolBar = dynamic_cast(widget); + if (toolBar->orientation() == Qt::Vertical) + { + painter->drawLine(handleRect.x(), handleRect.center().y(), handleRect.right(), handleRect.center().y()); + } + else + { + painter->drawLine(handleRect.center().x(), handleRect.y(), handleRect.center().x(), handleRect.bottom()); + } + painter->restore(); + return; + } + case QStyle::PE_IndicatorToolBarSeparator: + { + //间隔符绘制 + QRect separatorRect = option->rect; + painter->save(); + painter->setRenderHint(QPainter::Antialiasing); + painter->setPen(Qt::NoPen); + painter->setBrush(ElaThemeColor(_themeMode, PrimaryNormal)); + const ElaToolBar* toolBar = dynamic_cast(widget); + if (toolBar->orientation() == Qt::Vertical) + { + painter->drawRect(QRectF(separatorRect.x() + separatorRect.width() * 0.1, separatorRect.center().y() - 0.9, separatorRect.width() * 0.8, 1.8)); + } + else + { + painter->drawRect(QRectF(separatorRect.center().x() - 0.9, separatorRect.y() + separatorRect.height() * 0.1, 1.8, separatorRect.height() * 0.8)); + } + painter->restore(); + return; + } + default: + { + break; + } + } + QProxyStyle::drawPrimitive(element, option, painter, widget); +} + +void ElaToolBarStyle::drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const +{ + switch (element) + { + case QStyle::CE_ToolButtonLabel: + { + //展开按钮 + if (const QStyleOptionToolButton* bopt = qstyleoption_cast(option)) + { + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + painter->setPen(Qt::NoPen); + if (widget->objectName() == "qt_toolbar_ext_button") + { + if (bopt->state.testFlag(QStyle::State_Enabled) && bopt->state.testFlag(QStyle::State_MouseOver)) + { + painter->setBrush(ElaThemeColor(_themeMode, BasicHoverAlpha)); + painter->drawRect(bopt->rect); + } + //展开图标 + painter->setPen(!bopt->state.testFlag(QStyle::State_Enabled) ? ElaThemeColor(_themeMode, BasicTextDisable) : ElaThemeColor(_themeMode, BasicText)); + QFont iconFont = QFont("ElaAwesome"); + iconFont.setPixelSize(18); + painter->setFont(iconFont); + painter->drawText(bopt->rect, Qt::AlignCenter, QChar((unsigned short)ElaIconType::AngleRight)); + } + else + { + if (bopt->arrowType != Qt::NoArrow) + { + break; + } + //背景绘制 + if (bopt->state.testFlag(QStyle::State_Enabled)) + { + if (bopt->state.testFlag(QStyle::State_Sunken)) + { + painter->setBrush(ElaThemeColor(_themeMode, BasicPressAlpha)); + painter->drawRoundedRect(bopt->rect, 4, 4); + } + else + { + if (bopt->state.testFlag(QStyle::State_MouseOver) || bopt->state.testFlag(QStyle::State_On)) + { + painter->setBrush(ElaThemeColor(_themeMode, BasicHoverAlpha)); + painter->drawRoundedRect(bopt->rect, 4, 4); + } + } + } + //指示器绘制 + _drawIndicator(painter, bopt, widget); + + //图标绘制 + QRect contentRect = subControlRect(QStyle::CC_ToolButton, bopt, QStyle::SC_ScrollBarAddLine, widget); + QRect iconRect = contentRect; + int heightOffset = iconRect.height() * 0.1; + iconRect.adjust(3, heightOffset, -3, -heightOffset); + _drawIcon(painter, iconRect, bopt, widget); + + //文字绘制 + contentRect.adjust(0, heightOffset, 0, -heightOffset); + _drawText(painter, contentRect, bopt); + } + painter->restore(); + } + return; + } + case QStyle::CE_ToolBar: + { + return; + } + default: + { + break; + } + } + QProxyStyle::drawControl(element, option, painter, widget); +} + +int ElaToolBarStyle::pixelMetric(PixelMetric metric, const QStyleOption* option, const QWidget* widget) const +{ + switch (metric) + { + case QStyle::PM_ToolBarExtensionExtent: + { + return 16; + } + default: + { + break; + } + } + return QProxyStyle::pixelMetric(metric, option, widget); +} + +void ElaToolBarStyle::_drawIndicator(QPainter* painter, const QStyleOptionToolButton* bopt, const QWidget* widget) const +{ + if (bopt->features.testFlag(QStyleOptionToolButton::MenuButtonPopup)) + { + QRect indicatorRect = subControlRect(QStyle::CC_ToolButton, bopt, QStyle::SC_ScrollBarSubLine, widget); + //指示器区域 + if (bopt->state.testFlag(QStyle::State_Enabled) && bopt->activeSubControls.testFlag(QStyle::SC_ScrollBarSubLine)) + { + painter->setBrush(ElaThemeColor(_themeMode, BasicIndicator)); + QPainterPath path; + path.moveTo(indicatorRect.topLeft()); + path.lineTo(indicatorRect.right() - 4, indicatorRect.y()); + path.arcTo(QRect(indicatorRect.right() - 8, indicatorRect.y(), 8, 8), 90, -90); + path.lineTo(indicatorRect.right(), indicatorRect.bottom() - 4); + path.arcTo(QRect(indicatorRect.right() - 8, indicatorRect.bottom() - 8, 8, 8), 0, -90); + path.lineTo(indicatorRect.bottomLeft()); + path.closeSubpath(); + painter->drawPath(path); + } + //指示器 + painter->setBrush(bopt->state.testFlag(QStyle::State_Enabled) ? ElaThemeColor(_themeMode, BasicText) : ElaThemeColor(_themeMode, BasicTextDisable)); + QPainterPath indicatorPath; + qreal indicatorHeight = qCos(30 * M_PI / 180.0) * indicatorRect.width() * 0.85; + indicatorPath.moveTo(indicatorRect.x() + indicatorRect.width() * 0.15, indicatorRect.center().y()); + indicatorPath.lineTo(indicatorRect.right() - indicatorRect.width() * 0.15, indicatorRect.center().y()); + indicatorPath.lineTo(indicatorRect.center().x(), indicatorRect.center().y() + indicatorHeight / 2); + indicatorPath.closeSubpath(); + painter->drawPath(indicatorPath); + } +} + +void ElaToolBarStyle::_drawIcon(QPainter* painter, QRectF iconRect, const QStyleOptionToolButton* bopt, const QWidget* widget) const +{ + if (bopt->toolButtonStyle != Qt::ToolButtonTextOnly) + { + QSize iconSize = bopt->iconSize; + const QToolButton* toolButton = dynamic_cast(widget); + QAction* action = toolButton->defaultAction(); + if (!action) + { + return; + } + if (action->property("ElaIconType").toString().isEmpty()) + { + //绘制QIcon + QIcon icon = bopt->icon; + if (!icon.isNull()) + { + QPixmap iconPix = icon.pixmap(iconSize, + (bopt->state & State_Enabled) ? QIcon::Normal + : QIcon::Disabled, + (bopt->state & State_Selected) ? QIcon::On + : QIcon::Off); + switch (bopt->toolButtonStyle) + { + case Qt::ToolButtonTextBesideIcon: + { + painter->drawPixmap(QRect(QPoint(iconRect.x(), iconRect.center().y() - iconSize.height() / 2), iconSize), iconPix); + break; + } + case Qt::ToolButtonTextUnderIcon: + { + painter->drawPixmap(QRect(QPoint(iconRect.center().x() - iconSize.width() / 2, iconRect.y()), iconSize), iconPix); + break; + } + case Qt::ToolButtonFollowStyle: + { + painter->drawPixmap(iconRect.x(), iconRect.y() - iconSize.height() / 2, iconPix); + break; + } + default: + { + break; + } + } + } + } + else + { + //绘制ElaIcon + painter->save(); + painter->setPen(ElaThemeColor(_themeMode, BasicText)); + QFont iconFont = QFont("ElaAwesome"); + switch (bopt->toolButtonStyle) + { + case Qt::ToolButtonIconOnly: + case Qt::ToolButtonTextBesideIcon: + case Qt::ToolButtonFollowStyle: + { + QRect adjustIconRect(iconRect.x(), iconRect.y(), iconSize.width(), iconRect.height()); + iconFont.setPixelSize(0.8 * std::min(adjustIconRect.width(), adjustIconRect.height())); + painter->setFont(iconFont); + painter->drawText(adjustIconRect, Qt::AlignCenter, action->property("ElaIconType").toString()); + break; + } + case Qt::ToolButtonTextUnderIcon: + { + QRect adjustIconRect(iconRect.center().x() - iconSize.width() / 2, iconRect.y(), iconSize.width(), iconSize.height()); + iconFont.setPixelSize(0.8 * std::min(iconSize.width(), iconSize.height())); + painter->setFont(iconFont); + painter->drawText(adjustIconRect, Qt::AlignHCenter, action->property("ElaIconType").toString()); + break; + } + default: + { + break; + } + } + painter->restore(); + } + } +} + +void ElaToolBarStyle::_drawText(QPainter* painter, QRect contentRect, const QStyleOptionToolButton* bopt) const +{ + if (!bopt->text.isEmpty()) + { + painter->setPen(ElaThemeColor(_themeMode, BasicText)); + switch (bopt->toolButtonStyle) + { + case Qt::ToolButtonTextOnly: + { + painter->drawText(contentRect, Qt::AlignCenter, bopt->text); + break; + } + case Qt::ToolButtonTextBesideIcon: + { + painter->drawText(QRect(contentRect.x() + bopt->iconSize.width() + 12, contentRect.y(), contentRect.width() - bopt->iconSize.width(), contentRect.height()), Qt::AlignLeft | Qt::AlignVCenter, bopt->text); + break; + } + case Qt::ToolButtonTextUnderIcon: + { + painter->drawText(contentRect, Qt::AlignBottom | Qt::AlignHCenter, bopt->text); + break; + } + case Qt::ToolButtonFollowStyle: + { + break; + } + default: + { + break; + } + } + } +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaToolBarStyle.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaToolBarStyle.h new file mode 100644 index 0000000..12279b6 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaToolBarStyle.h @@ -0,0 +1,25 @@ +#ifndef ELATOOLBARSTYLE_H +#define ELATOOLBARSTYLE_H + +#include + +#include "Def.h" +class QStyleOptionToolButton; +class ElaToolBarStyle : public QProxyStyle +{ + Q_OBJECT +public: + explicit ElaToolBarStyle(QStyle* style = nullptr); + ~ElaToolBarStyle(); + void drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget = nullptr) const override; + void drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget = nullptr) const override; + int pixelMetric(PixelMetric metric, const QStyleOption* option = nullptr, const QWidget* widget = nullptr) const override; + +private: + ElaThemeType::ThemeMode _themeMode; + void _drawIndicator(QPainter* painter, const QStyleOptionToolButton* bopt, const QWidget* widget) const; + void _drawIcon(QPainter* painter, QRectF iconRect, const QStyleOptionToolButton* bopt, const QWidget* widget) const; + void _drawText(QPainter* painter, QRect contentRect, const QStyleOptionToolButton* bopt) const; +}; + +#endif // ELATOOLBARSTYLE_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaToolButtonStyle.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaToolButtonStyle.cpp new file mode 100644 index 0000000..2010310 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaToolButtonStyle.cpp @@ -0,0 +1,332 @@ +#include "ElaToolButtonStyle.h" + +#include +#include +#include +#include +#include + +#include "ElaTheme.h" +ElaToolButtonStyle::ElaToolButtonStyle(QStyle* style) +{ + _pIsSelected = false; + _pIsTransparent = true; + _pExpandIconRotate = 0; + _pBorderRadius = 4; + _themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { + _themeMode = themeMode; + }); +} + +ElaToolButtonStyle::~ElaToolButtonStyle() +{ +} + +void ElaToolButtonStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex* option, QPainter* painter, const QWidget* widget) const +{ + switch (control) + { + case QStyle::CC_ToolButton: + { + // 内容绘制 + if (const QStyleOptionToolButton* bopt = qstyleoption_cast(option)) + { + if (bopt->arrowType != Qt::NoArrow) + { + break; + } + QRect toolButtonRect = bopt->rect; + if (!_pIsTransparent) + { + // 边框距离 + toolButtonRect.adjust(1, 1, -1, -1); + } + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform); + painter->setPen(_pIsTransparent ? Qt::transparent : ElaThemeColor(_themeMode, BasicBorder)); + // 背景绘制 + if (bopt->state.testFlag(QStyle::State_Enabled)) + { + if (bopt->state.testFlag(QStyle::State_Sunken)) + { + painter->setBrush(_pIsTransparent ? ElaThemeColor(_themeMode, BasicPressAlpha) : ElaThemeColor(_themeMode, BasicPress)); + painter->drawRoundedRect(toolButtonRect, _pBorderRadius, _pBorderRadius); + } + else + { + if (_pIsSelected) + { + painter->setBrush(_pIsTransparent ? ElaThemeColor(_themeMode, BasicSelectedAlpha) : ElaThemeColor(_themeMode, BasicHover)); + painter->drawRoundedRect(toolButtonRect, _pBorderRadius, _pBorderRadius); + } + else + { + if (bopt->state.testFlag(QStyle::State_MouseOver) || bopt->state.testFlag(QStyle::State_On)) + { + painter->setBrush(_pIsTransparent ? ElaThemeColor(_themeMode, BasicHoverAlpha) : ElaThemeColor(_themeMode, BasicHover)); + painter->drawRoundedRect(toolButtonRect, _pBorderRadius, _pBorderRadius); + } + else + { + if (!_pIsTransparent) + { + painter->setBrush(ElaThemeColor(_themeMode, BasicBase)); + painter->drawRoundedRect(toolButtonRect, _pBorderRadius, _pBorderRadius); + // 底边线绘制 + painter->setPen(ElaThemeColor(_themeMode, BasicBaseLine)); + painter->drawLine(toolButtonRect.x() + _pBorderRadius, toolButtonRect.y() + toolButtonRect.height(), toolButtonRect.x() + toolButtonRect.width() - _pBorderRadius, toolButtonRect.y() + toolButtonRect.height()); + } + } + } + } + } + // 指示器绘制 + _drawIndicator(painter, bopt, widget); + + // 图标绘制 + QRect contentRect = subControlRect(control, bopt, QStyle::SC_ScrollBarAddLine, widget); + int heightOffset = contentRect.height() * 0.05; + contentRect.adjust(0, heightOffset, 0, -heightOffset); + _drawIcon(painter, contentRect, bopt, widget); + + // 文字绘制 + _drawText(painter, contentRect, bopt); + painter->restore(); + } + return; + } + default: + { + break; + } + } + QProxyStyle::drawComplexControl(control, option, painter, widget); +} + +QSize ElaToolButtonStyle::sizeFromContents(ContentsType type, const QStyleOption* option, const QSize& size, const QWidget* widget) const +{ + if (type == QStyle::CT_ToolButton) + { + if (const QStyleOptionToolButton* bopt = qstyleoption_cast(option)) + { + QSize toolButtonSize = QProxyStyle::sizeFromContents(type, option, size, widget); + if (bopt->features.testFlag(QStyleOptionToolButton::HasMenu) && !bopt->features.testFlag(QStyleOptionToolButton::MenuButtonPopup)) + { + toolButtonSize.setWidth(toolButtonSize.width() + _contentMargin + 0.65 * std::min(bopt->iconSize.width(), bopt->iconSize.height())); + } + return toolButtonSize; + } + } + return QProxyStyle::sizeFromContents(type, option, size, widget); +} + +void ElaToolButtonStyle::_drawIndicator(QPainter* painter, const QStyleOptionToolButton* bopt, const QWidget* widget) const +{ + if (bopt->features.testFlag(QStyleOptionToolButton::MenuButtonPopup)) + { + QRect indicatorRect = subControlRect(QStyle::CC_ToolButton, bopt, QStyle::SC_ScrollBarSubLine, widget); + // 指示器区域 + if (bopt->state.testFlag(QStyle::State_Enabled) && bopt->activeSubControls.testFlag(QStyle::SC_ScrollBarSubLine)) + { + painter->setBrush(ElaThemeColor(_themeMode, BasicIndicator)); + QPainterPath path; + path.moveTo(indicatorRect.topLeft()); + path.lineTo(indicatorRect.right() - 4, indicatorRect.y()); + path.arcTo(QRect(indicatorRect.right() - 8, indicatorRect.y(), 8, 8), 90, -90); + path.lineTo(indicatorRect.right(), indicatorRect.bottom() - 4); + path.arcTo(QRect(indicatorRect.right() - 8, indicatorRect.bottom() - 8, 8, 8), 0, -90); + path.lineTo(indicatorRect.bottomLeft()); + path.closeSubpath(); + painter->drawPath(path); + } + // 指示器 + painter->setBrush(bopt->state.testFlag(QStyle::State_Enabled) ? ElaThemeColor(_themeMode, BasicText) : ElaThemeColor(_themeMode, BasicTextDisable)); + QPainterPath indicatorPath; + qreal indicatorHeight = qCos(30 * M_PI / 180.0) * indicatorRect.width() * 0.85; + indicatorPath.moveTo(indicatorRect.x() + indicatorRect.width() * 0.15, indicatorRect.center().y()); + indicatorPath.lineTo(indicatorRect.right() - indicatorRect.width() * 0.15, indicatorRect.center().y()); + indicatorPath.lineTo(indicatorRect.center().x(), indicatorRect.center().y() + indicatorHeight / 2); + indicatorPath.closeSubpath(); + painter->drawPath(indicatorPath); + } + else if (bopt->features.testFlag(QStyleOptionToolButton::HasMenu)) + { + // 展开指示器 + QSize iconSize = bopt->iconSize; + painter->save(); + QRect toolButtonRect = bopt->rect; + QFont iconFont = QFont("ElaAwesome"); + iconFont.setPixelSize(0.75 * std::min(iconSize.width(), iconSize.height())); + painter->setFont(iconFont); + int indicatorWidth = painter->fontMetrics().horizontalAdvance(QChar((unsigned short)ElaIconType::AngleDown)); + QRect expandIconRect(toolButtonRect.right() - _contentMargin - indicatorWidth, toolButtonRect.y() + 1, indicatorWidth, toolButtonRect.height()); + painter->setPen(ElaThemeColor(_themeMode, BasicText)); + painter->translate(expandIconRect.center().x(), expandIconRect.y() + (qreal)expandIconRect.height() / 2); + painter->rotate(_pExpandIconRotate); + painter->translate(-expandIconRect.center().x() - 1, -expandIconRect.y() - (qreal)expandIconRect.height() / 2); + painter->drawText(expandIconRect, Qt::AlignCenter, QChar((unsigned short)ElaIconType::AngleDown)); + painter->restore(); + } +} + +void ElaToolButtonStyle::_drawIcon(QPainter* painter, QRectF iconRect, const QStyleOptionToolButton* bopt, const QWidget* widget) const +{ + if (bopt->toolButtonStyle != Qt::ToolButtonTextOnly) + { + QSize iconSize = bopt->iconSize; + if (widget->property("ElaIconType").toString().isEmpty()) + { + // 绘制QIcon + QIcon icon = bopt->icon; + if (!icon.isNull()) + { + QPixmap iconPix = icon.pixmap(iconSize, + (bopt->state & State_Enabled) ? QIcon::Normal + : QIcon::Disabled, + (bopt->state & State_Selected) ? QIcon::On + : QIcon::Off); + switch (bopt->toolButtonStyle) + { + case Qt::ToolButtonIconOnly: + { + painter->drawPixmap(QRect(QPoint(iconRect.x(), iconRect.center().y() - iconSize.height() / 2), iconSize), iconPix); + break; + } + case Qt::ToolButtonFollowStyle: + case Qt::ToolButtonTextBesideIcon: + { + painter->drawPixmap(QRect(QPoint(iconRect.x() + _contentMargin, iconRect.center().y() - iconSize.height() / 2), iconSize), iconPix); + break; + } + case Qt::ToolButtonTextUnderIcon: + { + if (bopt->features.testFlag(QStyleOptionToolButton::HasMenu) && !bopt->features.testFlag(QStyleOptionToolButton::MenuButtonPopup)) + { + iconRect.setRight(iconRect.right() - _calculateExpandIndicatorWidth(bopt, painter)); + } + painter->drawPixmap(QRect(QPoint(iconRect.center().x() - iconSize.width() / 2, iconRect.y()), iconSize), iconPix); + break; + } + default: + { + break; + } + } + } + } + else + { + // 绘制ElaIcon + painter->save(); + if (bopt->state.testFlag(QStyle::State_Enabled)) + { + painter->setPen(ElaThemeColor(_themeMode, BasicText)); + } + else + { + painter->setPen(ElaThemeColor(_themeMode, BasicTextDisable)); + } + QFont iconFont = QFont("ElaAwesome"); + switch (bopt->toolButtonStyle) + { + case Qt::ToolButtonIconOnly: + { + iconFont.setPixelSize(0.75 * std::min(iconSize.width(), iconSize.height())); + painter->setFont(iconFont); + painter->drawText(iconRect, Qt::AlignCenter, widget->property("ElaIconType").toString()); + break; + } + case Qt::ToolButtonFollowStyle: + case Qt::ToolButtonTextBesideIcon: + { + QRect adjustIconRect(iconRect.x() + _contentMargin, iconRect.y(), iconSize.width(), iconRect.height()); + iconFont.setPixelSize(0.75 * std::min(iconSize.width(), iconSize.height())); + painter->setFont(iconFont); + painter->drawText(adjustIconRect, Qt::AlignCenter, widget->property("ElaIconType").toString()); + break; + } + case Qt::ToolButtonTextUnderIcon: + { + if (bopt->features.testFlag(QStyleOptionToolButton::HasMenu) && !bopt->features.testFlag(QStyleOptionToolButton::MenuButtonPopup)) + { + iconRect.setRight(iconRect.right() - _calculateExpandIndicatorWidth(bopt, painter)); + } + QRect adjustIconRect(iconRect.center().x() - iconSize.width() / 2, iconRect.y() + 0.2 * std::min(iconSize.width(), iconSize.height()), iconSize.width(), iconSize.height()); + iconFont.setPixelSize(0.8 * std::min(iconSize.width(), iconSize.height())); + painter->setFont(iconFont); + painter->drawText(adjustIconRect, Qt::AlignHCenter, widget->property("ElaIconType").toString()); + break; + } + default: + { + break; + } + } + painter->restore(); + } + } +} + +void ElaToolButtonStyle::_drawText(QPainter* painter, QRect contentRect, const QStyleOptionToolButton* bopt) const +{ + if (!bopt->text.isEmpty()) + { + if (bopt->state.testFlag(QStyle::State_Enabled)) + { + painter->setPen(ElaThemeColor(_themeMode, BasicText)); + } + else + { + painter->setPen(ElaThemeColor(_themeMode, BasicTextDisable)); + } + switch (bopt->toolButtonStyle) + { + case Qt::ToolButtonTextOnly: + { + contentRect.setLeft(contentRect.left() + _contentMargin); + painter->drawText(contentRect, Qt::AlignLeft | Qt::AlignVCenter, bopt->text); + break; + } + case Qt::ToolButtonTextBesideIcon: + { + painter->drawText(QRect(contentRect.x() + _contentMargin * 2 + bopt->iconSize.width(), contentRect.y(), contentRect.width() - bopt->iconSize.width(), contentRect.height()), Qt::AlignLeft | Qt::AlignVCenter, bopt->text); + break; + } + case Qt::ToolButtonTextUnderIcon: + { + if (bopt->features.testFlag(QStyleOptionToolButton::HasMenu) && !bopt->features.testFlag(QStyleOptionToolButton::MenuButtonPopup)) + { + contentRect.setLeft(contentRect.left() + _contentMargin); + painter->drawText(contentRect, Qt::AlignBottom | Qt::AlignLeft, bopt->text); + } + else + { + painter->drawText(contentRect, Qt::AlignBottom | Qt::AlignHCenter, bopt->text); + } + break; + } + case Qt::ToolButtonFollowStyle: + { + break; + } + default: + { + break; + } + } + } +} + +qreal ElaToolButtonStyle::_calculateExpandIndicatorWidth(const QStyleOptionToolButton* bopt, QPainter* painter) const +{ + // 展开指示器 + QSize iconSize = bopt->iconSize; + painter->save(); + QFont iconFont = QFont("ElaAwesome"); + iconFont.setPixelSize(0.75 * std::min(iconSize.width(), iconSize.height())); + painter->setFont(iconFont); + int indicatorWidth = painter->fontMetrics().horizontalAdvance(QChar((unsigned short)ElaIconType::AngleDown)); + painter->restore(); + return indicatorWidth; +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaToolButtonStyle.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaToolButtonStyle.h new file mode 100644 index 0000000..5468fad --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaToolButtonStyle.h @@ -0,0 +1,32 @@ +#ifndef ELATOOLBUTTONSTYLE_H +#define ELATOOLBUTTONSTYLE_H + +#include + +#include "Def.h" +class QStyleOptionToolButton; +class ElaToolButtonStyle : public QProxyStyle +{ + Q_OBJECT + Q_PRIVATE_CREATE(bool, IsTransparent) + Q_PRIVATE_CREATE(int, BorderRadius) + Q_PROPERTY_CREATE(qreal, ExpandIconRotate) + Q_PRIVATE_CREATE(bool, IsSelected); + +public: + explicit ElaToolButtonStyle(QStyle* style = nullptr); + ~ElaToolButtonStyle(); + void drawComplexControl(ComplexControl control, const QStyleOptionComplex* option, QPainter* painter, const QWidget* widget = nullptr) const override; + QSize sizeFromContents(ContentsType type, const QStyleOption* option, const QSize& size, const QWidget* widget) const override; + +private: + ElaThemeType::ThemeMode _themeMode; + int _contentMargin{6}; + int _hasMenuIndicatorWidth{30}; + void _drawIndicator(QPainter* painter, const QStyleOptionToolButton* bopt, const QWidget* widget) const; + void _drawIcon(QPainter* painter, QRectF iconRect, const QStyleOptionToolButton* bopt, const QWidget* widget) const; + void _drawText(QPainter* painter, QRect contentRect, const QStyleOptionToolButton* bopt) const; + qreal _calculateExpandIndicatorWidth(const QStyleOptionToolButton* bopt, QPainter* painter) const; +}; + +#endif // ELATOOLBUTTONSTYLE_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaTreeViewStyle.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaTreeViewStyle.cpp new file mode 100644 index 0000000..0c5a256 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaTreeViewStyle.cpp @@ -0,0 +1,309 @@ +#include "ElaTreeViewStyle.h" + +#include +#include +#include +#include + +#include "ElaTheme.h" +ElaTreeViewStyle::ElaTreeViewStyle(QStyle* style) +{ + _pItemHeight = 35; + _pHeaderMargin = 5; + _themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { _themeMode = themeMode; }); +} + +ElaTreeViewStyle::~ElaTreeViewStyle() +{ +} + +void ElaTreeViewStyle::drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const +{ + switch (element) + { + case QStyle::PE_PanelItemViewItem: + { + // Item背景 + if (const QStyleOptionViewItem* vopt = qstyleoption_cast(option)) + { + painter->save(); + painter->setRenderHint(QPainter::Antialiasing); + QRect itemRect = vopt->rect; + itemRect.adjust(0, 2, 0, -2); + QPainterPath path; + path.addRoundedRect(itemRect, 4, 4); + if (vopt->state & QStyle::State_Selected) + { + if (vopt->state & QStyle::State_MouseOver) + { + // 选中时覆盖 + painter->fillPath(path, ElaThemeColor(_themeMode, BasicSelectedHoverAlpha)); + } + else + { + // 选中 + painter->fillPath(path, ElaThemeColor(_themeMode, BasicSelectedAlpha)); + } + } + else + { + if (vopt->state & QStyle::State_MouseOver) + { + // 覆盖时颜色 + painter->fillPath(path, ElaThemeColor(_themeMode, BasicHoverAlpha)); + } + } + painter->restore(); + } + return; + } + case QStyle::PE_IndicatorBranch: + { + // 展开图标绘制 + if (const QStyleOptionViewItem* vopt = qstyleoption_cast(option)) + { + if (vopt->state.testFlag(QStyle::State_Children)) + { + painter->save(); + QRect indicatorRect = option->rect; + indicatorRect.adjust(0, 0, -2, 0); + QFont iconFont = QFont("ElaAwesome"); + iconFont.setPixelSize(17); + painter->setFont(iconFont); + painter->setPen(ElaThemeColor(_themeMode, BasicText)); + painter->drawText(indicatorRect, Qt::AlignVCenter | Qt::AlignRight, vopt->state.testFlag(QStyle::State_Open) ? QChar((unsigned short)ElaIconType::AngleDown) : QChar((unsigned short)ElaIconType::AngleRight)); + painter->restore(); + } + } + return; + } + case QStyle::PE_PanelItemViewRow: + { + return; + } + case QStyle::PE_Widget: + { + return; + } + case QStyle::PE_Frame: + { + return; + } + default: + { + break; + } + } + QProxyStyle::drawPrimitive(element, option, painter, widget); +} + +void ElaTreeViewStyle::drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const +{ + switch (element) + { + case QStyle::CE_ShapedFrame: + { + // viewport视口外的其他区域背景 + QRect frameRect = option->rect; + frameRect.adjust(1, 1, -1, -1); + painter->save(); + painter->setRenderHints(QPainter::Antialiasing); + painter->setPen(ElaThemeColor(_themeMode, PopupBorder)); + painter->setBrush(ElaThemeColor(_themeMode, BasicBase)); + painter->drawRoundedRect(frameRect, 3, 3); + painter->restore(); + return; + } + case QStyle::CE_HeaderLabel: + { + // 表头文字绘制 + if (const QStyleOptionHeader* hopt = qstyleoption_cast(option)) + { + QRect headerRect = option->rect; + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing); + if (!hopt->text.isEmpty()) + { + painter->setPen(ElaThemeColor(_themeMode, BasicText)); + painter->drawText(headerRect, hopt->textAlignment, hopt->text); + } + painter->restore(); + } + return; + } + case QStyle::CE_HeaderSection: + { + // 表头背景绘制 + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing); + painter->setPen(Qt::NoPen); + painter->setBrush(ElaThemeColor(_themeMode, BasicBaseDeepAlpha)); + painter->drawRect(option->rect); + painter->restore(); + return; + } + case QStyle::CE_ItemViewItem: + { + if (const QStyleOptionViewItem* vopt = qstyleoption_cast(option)) + { + // 背景绘制 + this->drawPrimitive(QStyle::PE_PanelItemViewItem, option, painter, widget); + + // 内容绘制 + QRect itemRect = option->rect; + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing); + QRect checkRect = proxy()->subElementRect(SE_ItemViewItemCheckIndicator, vopt, widget); + QRect iconRect = proxy()->subElementRect(SE_ItemViewItemDecoration, vopt, widget); + QRect textRect = proxy()->subElementRect(SE_ItemViewItemText, vopt, widget); + // 复选框绘制 + if (checkRect.isValid()) + { + painter->save(); + //图标绘制 + if (vopt->checkState == Qt::Checked) + { + painter->setPen(Qt::NoPen); + painter->setBrush(ElaThemeColor(_themeMode, PrimaryNormal)); + painter->drawRoundedRect(checkRect, 2, 2); + QFont iconFont = QFont("ElaAwesome"); + iconFont.setPixelSize(checkRect.width() * 0.85); + painter->setFont(iconFont); + painter->setPen(ElaThemeColor(ElaThemeType::Dark, BasicText)); + painter->drawText(checkRect, Qt::AlignCenter, QChar((unsigned short)ElaIconType::Check)); + } + else if (vopt->checkState == Qt::PartiallyChecked) + { + painter->setPen(Qt::NoPen); + painter->setBrush(ElaThemeColor(_themeMode, PrimaryNormal)); + painter->drawRoundedRect(checkRect, 2, 2); + painter->setPen(ElaThemeColor(ElaThemeType::Dark, BasicText)); + QLine checkLine(checkRect.x() + 3, checkRect.center().y(), checkRect.right() - 3, checkRect.center().y()); + painter->drawLine(checkLine); + } + else + { + painter->setPen(QPen(ElaThemeColor(_themeMode, BasicBorderDeep), 1.2)); + painter->setBrush(Qt::transparent); + painter->drawRoundedRect(checkRect, 2, 2); + } + painter->restore(); + } + // 图标绘制 + if (!vopt->icon.isNull()) + { + QIcon::Mode mode = QIcon::Normal; + QIcon::State state = vopt->state & QStyle::State_Open ? QIcon::On : QIcon::Off; + vopt->icon.paint(painter, iconRect, vopt->decorationAlignment, mode, state); + } + // 文字绘制 + if (!vopt->text.isEmpty()) + { + painter->setPen(ElaThemeColor(_themeMode, BasicText)); + painter->drawText(textRect, vopt->displayAlignment, vopt->text); + } + // 选中特效 + int heightOffset = itemRect.height() / 4; + painter->setPen(Qt::NoPen); + painter->setBrush(ElaThemeColor(_themeMode, PrimaryNormal)); + if (vopt->state.testFlag(QStyle::State_Selected) && (vopt->viewItemPosition == QStyleOptionViewItem::Beginning || vopt->viewItemPosition == QStyleOptionViewItem::OnlyOne)) + { + painter->drawRoundedRect(QRectF(itemRect.x() + 3, itemRect.y() + heightOffset, 3, itemRect.height() - 2 * heightOffset), 3, 3); + } + painter->restore(); + } + return; + } + default: + { + break; + } + } + QProxyStyle::drawControl(element, option, painter, widget); +} + +QSize ElaTreeViewStyle::sizeFromContents(ContentsType type, const QStyleOption* option, const QSize& size, const QWidget* widget) const +{ + switch (type) + { + case QStyle::CT_ItemViewItem: + { + QSize itemSize = QProxyStyle::sizeFromContents(type, option, size, widget); + itemSize.setHeight(_pItemHeight); + return itemSize; + } + default: + { + break; + } + } + return QProxyStyle::sizeFromContents(type, option, size, widget); +} + +int ElaTreeViewStyle::pixelMetric(PixelMetric metric, const QStyleOption* option, const QWidget* widget) const +{ + switch (metric) + { + case QStyle::PM_HeaderMargin: + { + return _pHeaderMargin; + } + default: + { + break; + } + } + return QProxyStyle::pixelMetric(metric, option, widget); +} + +QRect ElaTreeViewStyle::subElementRect(SubElement element, const QStyleOption* option, const QWidget* widget) const +{ + switch (element) + { + case QStyle::SE_ItemViewItemCheckIndicator: + { + if (const QStyleOptionViewItem* vopt = qstyleoption_cast(option)) + { + if (vopt->viewItemPosition != QStyleOptionViewItem::Middle && vopt->viewItemPosition != QStyleOptionViewItem::End) + { + QRect indicatorRect = QProxyStyle::subElementRect(element, option, widget); + indicatorRect.adjust(_leftPadding, 0, _leftPadding, 0); + return indicatorRect; + } + } + break; + } + case QStyle::SE_ItemViewItemDecoration: + { + if (const QStyleOptionViewItem* vopt = qstyleoption_cast(option)) + { + if (vopt->viewItemPosition != QStyleOptionViewItem::Middle && vopt->viewItemPosition != QStyleOptionViewItem::End) + { + QRect iconRect = QProxyStyle::subElementRect(element, option, widget); + iconRect.adjust(_leftPadding + 5, 0, _leftPadding + 5, 0); + return iconRect; + } + } + break; + } + case QStyle::SE_ItemViewItemText: + { + if (const QStyleOptionViewItem* vopt = qstyleoption_cast(option)) + { + if (vopt->viewItemPosition != QStyleOptionViewItem::Middle && vopt->viewItemPosition != QStyleOptionViewItem::End) + { + QRect textRect = QProxyStyle::subElementRect(element, option, widget); + textRect.adjust(_leftPadding + 10, 0, 0, 0); + return textRect; + } + } + break; + } + default: + { + break; + } + } + return QProxyStyle::subElementRect(element, option, widget); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaTreeViewStyle.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaTreeViewStyle.h new file mode 100644 index 0000000..a98872c --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaTreeViewStyle.h @@ -0,0 +1,26 @@ +#ifndef ELATREEVIEWSTYLE_H +#define ELATREEVIEWSTYLE_H + +#include + +#include "Def.h" +class ElaTreeViewStyle : public QProxyStyle +{ + Q_OBJECT + Q_PROPERTY_CREATE(int, ItemHeight) + Q_PROPERTY_CREATE(int, HeaderMargin) +public: + explicit ElaTreeViewStyle(QStyle* style = nullptr); + ~ElaTreeViewStyle(); + void drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget = nullptr) const override; + void drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget = nullptr) const override; + QSize sizeFromContents(ContentsType type, const QStyleOption* option, const QSize& size, const QWidget* widget) const override; + int pixelMetric(PixelMetric metric, const QStyleOption* option = nullptr, const QWidget* widget = nullptr) const override; + QRect subElementRect(SubElement element, const QStyleOption* option, const QWidget* widget) const override; + +private: + ElaThemeType::ThemeMode _themeMode; + int _leftPadding{11}; +}; + +#endif // ELATREEVIEWSTYLE_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaWinShadowHelper.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaWinShadowHelper.h new file mode 100644 index 0000000..16e50c6 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaWinShadowHelper.h @@ -0,0 +1,27 @@ +#ifndef ELAWINSHADOWHELPER_H +#define ELAWINSHADOWHELPER_H +#include + +#ifdef Q_OS_WIN +#include +#include +#endif +#ifdef Q_OS_WIN +[[maybe_unused]] static inline void setShadow(HWND hwnd) +{ + const MARGINS shadow = {1, 0, 0, 0}; + typedef HRESULT(WINAPI * DwmExtendFrameIntoClientAreaPtr)(HWND hWnd, const MARGINS* pMarInset); + HMODULE module = LoadLibraryW(L"dwmapi.dll"); + if (module) + { + DwmExtendFrameIntoClientAreaPtr dwm_extendframe_into_client_area_; + dwm_extendframe_into_client_area_ = reinterpret_cast(GetProcAddress(module, "DwmExtendFrameIntoClientArea")); + if (dwm_extendframe_into_client_area_) + { + dwm_extendframe_into_client_area_(hwnd, &shadow); + } + } +} +#endif + +#endif // ELAWINSHADOWHELPER_H diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaWindowStyle.cpp b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaWindowStyle.cpp new file mode 100644 index 0000000..c2610b8 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaWindowStyle.cpp @@ -0,0 +1,211 @@ +#include "ElaWindowStyle.h" + +#include +#include +#include +#include +#include + +#include "ElaTheme.h" +ElaWindowStyle::ElaWindowStyle(QStyle* style) +{ + _themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { _themeMode = themeMode; }); +} + +ElaWindowStyle::~ElaWindowStyle() +{ +} + +void ElaWindowStyle::drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const +{ + switch (element) + { + case QStyle::PE_FrameTabBarBase: + { + return; + } + case QStyle::PE_PanelButtonTool: + { + if (option->state.testFlag(QStyle::State_Enabled)) + { + painter->save(); + painter->setRenderHint(QPainter::Antialiasing); + painter->setPen(Qt::NoPen); + if (option->state.testFlag(QStyle::State_Sunken)) + { + painter->setBrush(ElaThemeColor(_themeMode, BasicHoverAlpha)); + } + else if (option->state.testFlag(QStyle::State_MouseOver)) + { + painter->setBrush(ElaThemeColor(_themeMode, BasicPressAlpha)); + } + else + { + painter->setBrush(Qt::transparent); + } + painter->drawRect(option->rect); + painter->restore(); + } + return; + } + case QStyle::PE_IndicatorArrowLeft: + { + painter->save(); + painter->setRenderHint(QPainter::Antialiasing); + painter->setPen(Qt::NoPen); + painter->setBrush(option->state.testFlag(QStyle::State_Enabled) ? ElaThemeColor(_themeMode, BasicText) : ElaThemeColor(_themeMode, BasicTextDisable)); + // 左三角 + int sideLength = 10; + QRect indicatorRect = option->rect; + QPainterPath path; + path.moveTo(indicatorRect.center().x() - qCos(30 * M_PI / 180.0) * sideLength / 2, indicatorRect.center().y()); + path.lineTo(indicatorRect.center().x() + qCos(30 * M_PI / 180.0) * sideLength / 2, indicatorRect.center().y() - sideLength / 2); + path.lineTo(indicatorRect.center().x() + qCos(30 * M_PI / 180.0) * sideLength / 2, indicatorRect.center().y() + sideLength / 2); + path.closeSubpath(); + painter->drawPath(path); + painter->restore(); + return; + } + case QStyle::PE_IndicatorArrowRight: + { + painter->save(); + painter->setRenderHint(QPainter::Antialiasing); + painter->setPen(Qt::NoPen); + painter->setBrush(option->state.testFlag(QStyle::State_Enabled) ? ElaThemeColor(_themeMode, BasicText) : ElaThemeColor(_themeMode, BasicTextDisable)); + // 右三角 + int sideLength = 10; + QRect indicatorRect = option->rect; + QPainterPath path; + path.moveTo(indicatorRect.center().x() - qCos(30 * M_PI / 180.0) * sideLength / 2, indicatorRect.center().y() - sideLength / 2); + path.lineTo(indicatorRect.center().x() + qCos(30 * M_PI / 180.0) * sideLength / 2, indicatorRect.center().y()); + path.lineTo(indicatorRect.center().x() - qCos(30 * M_PI / 180.0) * sideLength / 2, indicatorRect.center().y() + sideLength / 2); + path.closeSubpath(); + painter->drawPath(path); + painter->restore(); + return; + } + case QStyle::PE_IndicatorTabTear: + { + return; + } + case QStyle::PE_IndicatorDockWidgetResizeHandle: + { + painter->save(); + painter->setRenderHint(QPainter::Antialiasing); + painter->setPen(QPen(ElaThemeColor(_themeMode, BasicBaseLine), 2)); + QRectF handleRect = option->rect; + if (option->state.testFlag(QStyle::State_Horizontal)) + { + painter->drawLine(handleRect.x(), handleRect.center().y(), handleRect.right(), handleRect.center().y()); + } + else + { + painter->drawLine(handleRect.center().x(), handleRect.y(), handleRect.center().x(), handleRect.bottom()); + } + painter->restore(); + return; + } + default: + { + break; + } + } + QProxyStyle::drawPrimitive(element, option, painter, widget); +} + +void ElaWindowStyle::drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const +{ + // qDebug() << element << option->rect; + switch (element) + { + case QStyle::CE_RubberBand: + { + // 预览颜色 + QRect rubberBandRect = option->rect; + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + painter->setPen(Qt::NoPen); + painter->setBrush(ElaThemeColor(_themeMode, BasicHoverAlpha)); + painter->drawRect(rubberBandRect); + painter->restore(); + return; + } + case QStyle::CE_TabBarTabShape: + { + // 背景绘制 + if (const QStyleOptionTab* topt = qstyleoption_cast(option)) + { + QRect tabRect = topt->rect; + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + painter->setPen(Qt::NoPen); + if (topt->state & QStyle::State_Selected) + { + if (topt->state & QStyle::State_Sunken) + { + // 选中时点击 + painter->setBrush(ElaThemeColor(_themeMode, BasicHoverAlpha)); + } + else + { + if (topt->state & QStyle::State_MouseOver) + { + // 选中时覆盖 + painter->setBrush(ElaThemeColor(_themeMode, BasicSelectedHoverAlpha)); + } + else + { + // 选中 + painter->setBrush(ElaThemeColor(_themeMode, BasicSelectedAlpha)); + } + } + } + else + { + if (topt->state & QStyle::State_Sunken) + { + // 点击时颜色 + painter->setBrush(ElaThemeColor(_themeMode, BasicSelectedHoverAlpha)); + } + else + { + if (topt->state & QStyle::State_MouseOver) + { + // 覆盖时颜色 + painter->setBrush(ElaThemeColor(_themeMode, BasicHoverAlpha)); + } + } + } + painter->drawRect(tabRect); + // 间隔符绘制 + if (topt->position != QStyleOptionTab::End) + { + painter->setPen(Qt::NoPen); + painter->setBrush(ElaThemeColor(_themeMode, PrimaryNormal)); + painter->drawRoundedRect(QRectF(tabRect.right() - 3, tabRect.y() + 7, 3, tabRect.height() - 14), 2, 2); + } + painter->restore(); + } + return; + } + case QStyle::CE_TabBarTabLabel: + { + // 文字绘制 + if (const QStyleOptionTab* topt = qstyleoption_cast(option)) + { + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + painter->setPen(ElaThemeColor(_themeMode, BasicText)); + painter->drawText(topt->rect, Qt::AlignCenter, topt->text); + painter->restore(); + } + return; + } + default: + { + break; + } + } + QProxyStyle::drawControl(element, option, painter, widget); +} diff --git a/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaWindowStyle.h b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaWindowStyle.h new file mode 100644 index 0000000..ed4c118 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/DeveloperComponents/ElaWindowStyle.h @@ -0,0 +1,20 @@ +#ifndef ELAWINDOWSTYLE_H +#define ELAWINDOWSTYLE_H + +#include + +#include "Def.h" +class ElaWindowStyle : public QProxyStyle +{ + Q_OBJECT +public: + explicit ElaWindowStyle(QStyle* style = nullptr); + ~ElaWindowStyle(); + void drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget = nullptr) const override; + void drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget = nullptr) const override; + +private: + ElaThemeType::ThemeMode _themeMode; +}; + +#endif // ELAWINDOWSTYLE_H diff --git a/3rdparty/ElaWidgetTools/src/ElaAcrylicUrlCard.cpp b/3rdparty/ElaWidgetTools/src/ElaAcrylicUrlCard.cpp new file mode 100644 index 0000000..b6cebcb --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaAcrylicUrlCard.cpp @@ -0,0 +1,129 @@ +#include "ElaAcrylicUrlCard.h" + +#include +#include +#include +#include + +#include "ElaTheme.h" +#include "private/ElaAcrylicUrlCardPrivate.h" +Q_PROPERTY_CREATE_Q_CPP(ElaAcrylicUrlCard, int, BorderRadius) +Q_PROPERTY_CREATE_Q_CPP(ElaAcrylicUrlCard, qreal, MainOpacity) +Q_PROPERTY_CREATE_Q_CPP(ElaAcrylicUrlCard, qreal, NoiseOpacity) +Q_PROPERTY_CREATE_Q_CPP(ElaAcrylicUrlCard, int, BrushAlpha) +Q_PROPERTY_CREATE_Q_CPP(ElaAcrylicUrlCard, QString, Title); +Q_PROPERTY_CREATE_Q_CPP(ElaAcrylicUrlCard, QString, SubTitle); +Q_PROPERTY_CREATE_Q_CPP(ElaAcrylicUrlCard, int, TitlePixelSize); +Q_PROPERTY_CREATE_Q_CPP(ElaAcrylicUrlCard, int, SubTitlePixelSize); +Q_PROPERTY_CREATE_Q_CPP(ElaAcrylicUrlCard, int, TitleSpacing); +Q_PROPERTY_CREATE_Q_CPP(ElaAcrylicUrlCard, int, SubTitleSpacing); +Q_PROPERTY_CREATE_Q_CPP(ElaAcrylicUrlCard, QPixmap, CardPixmap); +Q_PROPERTY_CREATE_Q_CPP(ElaAcrylicUrlCard, QSize, CardPixmapSize); +Q_PROPERTY_CREATE_Q_CPP(ElaAcrylicUrlCard, int, CardPixmapBorderRadius) +Q_PROPERTY_CREATE_Q_CPP(ElaAcrylicUrlCard, ElaCardPixType::PixMode, CardPixMode); +Q_PROPERTY_CREATE_Q_CPP(ElaAcrylicUrlCard, QString, Url); +ElaAcrylicUrlCard::ElaAcrylicUrlCard(QWidget* parent) + : QPushButton(parent), d_ptr(new ElaAcrylicUrlCardPrivate()) +{ + Q_D(ElaAcrylicUrlCard); + d->q_ptr = this; + d->_noisePix = QPixmap(":/include/Image/noise.png"); + d->_pBorderRadius = 5; + d->_pMainOpacity = 0.95; + d->_pNoiseOpacity = 0.06; + d->_pBrushAlpha = 245; + d->_pTitlePixelSize = 15; + d->_pSubTitlePixelSize = 12; + setFixedSize(180, 200); + d->_pCardPixmapSize = QSize(54, 54); + d->_pTitleSpacing = 10; + d->_pSubTitleSpacing = 10; + d->_pCardPixmapBorderRadius = 6; + d->_pCardPixMode = ElaCardPixType::PixMode::Ellipse; + d->_themeMode = eTheme->getThemeMode(); + connect(this, &ElaAcrylicUrlCard::clicked, this, [=]() { QDesktopServices::openUrl(QUrl(d->_pUrl)); }); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { d->_themeMode = themeMode; }); +} + +ElaAcrylicUrlCard::~ElaAcrylicUrlCard() +{ +} + +void ElaAcrylicUrlCard::setCardPixmapSize(int width, int height) +{ + Q_D(ElaAcrylicUrlCard); + d->_pCardPixmapSize = QSize(width, height); + Q_EMIT pCardPixmapSizeChanged(); +} + +void ElaAcrylicUrlCard::paintEvent(QPaintEvent* event) +{ + Q_D(ElaAcrylicUrlCard); + QPainter painter(this); + painter.setRenderHints(QPainter::SmoothPixmapTransform | QPainter::Antialiasing | QPainter::TextAntialiasing); + // 亚克力绘制 + painter.save(); + painter.setPen(QPen(underMouse() ? ElaThemeColor(d->_themeMode, BasicBorderHover) : ElaThemeColor(d->_themeMode, BasicBorder), d->_borderWidth)); + QColor brushColor; + brushColor = underMouse() ? ElaThemeColor(d->_themeMode, BasicHover) : ElaThemeColor(d->_themeMode, BasicBase); + brushColor.setAlpha(d->_pBrushAlpha); + painter.setBrush(brushColor); + QRect foregroundRect = rect(); + foregroundRect.adjust(d->_borderWidth, d->_borderWidth, -d->_borderWidth, -d->_borderWidth); + int width = foregroundRect.width(); + painter.setOpacity(d->_pNoiseOpacity); + painter.drawPixmap(foregroundRect, d->_noisePix); + painter.setOpacity(d->_pMainOpacity); + painter.drawRoundedRect(foregroundRect, d->_pBorderRadius, d->_pBorderRadius); + painter.restore(); + + // 图片绘制 + QRectF pixRect(width / 8.5, height() / 4 - d->_pCardPixmapSize.height() / 2, d->_pCardPixmapSize.width(), d->_pCardPixmapSize.height()); + if (!d->_pCardPixmap.isNull()) + { + painter.save(); + QPainterPath path; + if (d->_pCardPixMode == ElaCardPixType::PixMode::Ellipse) + { + path.addEllipse(pixRect.center(), d->_pCardPixmapSize.width() / 2, d->_pCardPixmapSize.height() / 2); + painter.setClipPath(path); + painter.drawPixmap(pixRect, d->_pCardPixmap, d->_pCardPixmap.rect()); + } + else if (d->_pCardPixMode == ElaCardPixType::PixMode::Default) + { + painter.drawPixmap(pixRect, d->_pCardPixmap, d->_pCardPixmap.rect()); + } + else if (d->_pCardPixMode == ElaCardPixType::PixMode::RoundedRect) + { + path.addRoundedRect(pixRect, d->_pCardPixmapBorderRadius, d->_pCardPixmapBorderRadius); + painter.setClipPath(path); + painter.drawPixmap(pixRect, d->_pCardPixmap, d->_pCardPixmap.rect()); + } + painter.restore(); + } + + // 文字绘制 + painter.save(); + QFont font = this->font(); + font.setWeight(QFont::Bold); + font.setPixelSize(d->_pTitlePixelSize); + painter.setFont(font); + painter.setPen(ElaThemeColor(d->_themeMode, BasicText)); + painter.drawText(QRect(pixRect.x(), pixRect.bottom() + d->_pTitleSpacing, width - width / 7, height() / 3), Qt::AlignLeft | Qt::AlignTop | Qt::TextSingleLine, d->_pTitle); + font.setWeight(QFont::Normal); + font.setPixelSize(d->_pSubTitlePixelSize); + painter.setFont(font); + painter.setPen(ElaThemeColor(d->_themeMode, BasicDetailsText)); + int titleHeight = painter.fontMetrics().boundingRect(d->_pTitle).height() * 1.1; + painter.drawText(QRect(pixRect.x(), d->_pSubTitleSpacing + titleHeight + pixRect.bottom() + d->_pTitleSpacing, width - width / 7, height() / 3), Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, d->_pSubTitle); + painter.restore(); + + // 图标绘制 + painter.save(); + QFont iconFont = QFont("ElaAwesome"); + iconFont.setPixelSize(13); + painter.setFont(iconFont); + painter.setPen(ElaThemeColor(d->_themeMode, BasicText)); + painter.drawText(width - 1.5 * iconFont.pixelSize(), height() - iconFont.pixelSize(), QChar((unsigned short)ElaIconType::UpRightFromSquare)); + painter.restore(); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaAppBar.cpp b/3rdparty/ElaWidgetTools/src/ElaAppBar.cpp new file mode 100644 index 0000000..9e6e10e --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaAppBar.cpp @@ -0,0 +1,807 @@ +#include "ElaAppBar.h" + +#include +#include + +#include "ElaText.h" +#include "ElaToolButton.h" +#include "ElaWinShadowHelper.h" +#ifndef Q_OS_WIN +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Def.h" +#include "ElaEventBus.h" +#include "ElaIconButton.h" +#include "ElaTheme.h" +#include "private/ElaAppBarPrivate.h" +Q_PROPERTY_CREATE_Q_CPP(ElaAppBar, bool, IsStayTop) +Q_PROPERTY_CREATE_Q_CPP(ElaAppBar, bool, IsDefaultClosed) +Q_PROPERTY_CREATE_Q_CPP(ElaAppBar, bool, IsOnlyAllowMinAndClose) + +ElaAppBar::ElaAppBar(QWidget* parent) + : QWidget{parent}, d_ptr(new ElaAppBarPrivate()) +{ + Q_D(ElaAppBar); + d->_buttonFlags = ElaAppBarType::RouteBackButtonHint | ElaAppBarType::StayTopButtonHint | ElaAppBarType::ThemeChangeButtonHint | ElaAppBarType::MinimizeButtonHint | ElaAppBarType::MaximizeButtonHint | ElaAppBarType::CloseButtonHint; + window()->setAttribute(Qt::WA_Mapped); + d->_pAppBarHeight = 45; + setFixedHeight(d->_pAppBarHeight); + window()->setContentsMargins(0, this->height(), 0, 0); + d->q_ptr = this; + d->_pIsStayTop = false; + d->_pIsFixedSize = false; + d->_pIsDefaultClosed = true; + d->_pIsOnlyAllowMinAndClose = false; + d->_pCustomWidget = nullptr; + d->_pCustomWidgetMaximumWidth = 550; + + window()->installEventFilter(this); +#ifdef Q_OS_WIN +#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 3) && QT_VERSION <= QT_VERSION_CHECK(6, 6, 1)) + window()->setWindowFlags((window()->windowFlags()) | Qt::WindowMinimizeButtonHint | Qt::FramelessWindowHint); +#endif +#else + window()->setWindowFlags((window()->windowFlags()) | Qt::FramelessWindowHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint); +#endif + setMouseTracking(true); + setObjectName("ElaAppBar"); + setStyleSheet("#ElaAppBar{background-color:transparent;}"); + d->_routeBackButton = new ElaToolButton(this); + d->_routeBackButton->setElaIcon(ElaIconType::ArrowLeft); + d->_routeBackButton->setFixedSize(40, 30); + d->_routeBackButton->setEnabled(false); + // 路由跳转 + connect(d->_routeBackButton, &ElaIconButton::clicked, this, &ElaAppBar::routeBackButtonClicked); + + // 导航栏展开按钮 + d->_navigationButton = new ElaToolButton(this); + d->_navigationButton->setElaIcon(ElaIconType::Bars); + d->_navigationButton->setFixedSize(40, 30); + d->_navigationButton->setObjectName("NavigationButton"); + d->_navigationButton->setVisible(false); + // 展开导航栏 + connect(d->_navigationButton, &ElaIconButton::clicked, this, &ElaAppBar::navigationButtonClicked); + + // 设置置顶 + d->_stayTopButton = new ElaToolButton(this); + d->_stayTopButton->setElaIcon(ElaIconType::ArrowUpToArc); + d->_stayTopButton->setFixedSize(40, 30); + connect(d->_stayTopButton, &ElaIconButton::clicked, this, [=]() { this->setIsStayTop(!this->getIsStayTop()); }); + connect(this, &ElaAppBar::pIsStayTopChanged, d, &ElaAppBarPrivate::onStayTopButtonClicked); + + //图标 + d->_iconLabel = new QLabel(this); + d->_iconLabelLayout = d->_createVLayout(d->_iconLabel); + if (parent->windowIcon().isNull()) + { + d->_iconLabel->setVisible(false); + } + else + { + d->_iconLabel->setPixmap(parent->windowIcon().pixmap(18, 18)); + d->_iconLabelLayout->setContentsMargins(10, 0, 0, 0); + } + connect(parent, &QWidget::windowIconChanged, this, [=](const QIcon& icon) { + d->_iconLabel->setPixmap(icon.pixmap(18, 18)); + d->_iconLabel->setVisible(icon.isNull() ? false : true); + d->_iconLabelLayout->setContentsMargins(icon.isNull() ? 0 : 10, 0, 0, 0); + }); + + //标题 + d->_titleLabel = new ElaText(this); + d->_titleLabel->setIsWrapAnywhere(false); + d->_titleLabel->setTextPixelSize(13); + d->_titleLabelLayout = d->_createVLayout(d->_titleLabel); + if (parent->windowTitle().isEmpty()) + { + d->_titleLabel->setVisible(false); + } + else + { + d->_titleLabel->setText(parent->windowTitle()); + d->_titleLabelLayout->setContentsMargins(10, 0, 0, 0); + } + connect(parent, &QWidget::windowTitleChanged, this, [=](const QString& title) { + d->_titleLabel->setText(title); + d->_titleLabel->setVisible(title.isEmpty() ? false : true); + d->_titleLabelLayout->setContentsMargins(title.isEmpty() ? 0 : 10, 0, 0, 0); + }); + + // 主题变更 + d->_themeChangeButton = new ElaToolButton(this); + d->_themeChangeButton->setElaIcon(ElaIconType::MoonStars); + d->_themeChangeButton->setFixedSize(40, 30); + connect(d->_themeChangeButton, &ElaIconButton::clicked, this, &ElaAppBar::themeChangeButtonClicked); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { d->_onThemeModeChange(themeMode); }); + + d->_minButton = new ElaToolButton(this); + d->_minButton->setElaIcon(ElaIconType::Dash); + d->_minButton->setFixedSize(40, 30); + connect(d->_minButton, &ElaIconButton::clicked, d, &ElaAppBarPrivate::onMinButtonClicked); + d->_maxButton = new ElaToolButton(this); + d->_maxButton->setIconSize(QSize(18, 18)); + d->_maxButton->setElaIcon(ElaIconType::Square); + d->_maxButton->setFixedSize(40, 30); + connect(d->_maxButton, &ElaIconButton::clicked, d, &ElaAppBarPrivate::onMaxButtonClicked); + d->_closeButton = new ElaIconButton(ElaIconType::Xmark, 18, 40, 30, this); + d->_closeButton->setLightHoverColor(QColor(0xE8, 0x11, 0x23)); + d->_closeButton->setDarkHoverColor(QColor(0xE8, 0x11, 0x23)); + d->_closeButton->setLightHoverIconColor(Qt::white); + d->_closeButton->setDarkHoverIconColor(Qt::white); + connect(d->_closeButton, &ElaIconButton::clicked, d, &ElaAppBarPrivate::onCloseButtonClicked); + + d->_mainLayout = new QHBoxLayout(this); + d->_mainLayout->setContentsMargins(0, 0, 0, 0); + d->_mainLayout->setSpacing(0); + d->_mainLayout->addLayout(d->_createVLayout(d->_routeBackButton)); + d->_mainLayout->addLayout(d->_createVLayout(d->_navigationButton)); + d->_mainLayout->addLayout(d->_iconLabelLayout); + d->_mainLayout->addLayout(d->_titleLabelLayout); + d->_mainLayout->addStretch(); + d->_mainLayout->addStretch(); + d->_mainLayout->addLayout(d->_createVLayout(d->_stayTopButton)); + d->_mainLayout->addLayout(d->_createVLayout(d->_themeChangeButton)); + d->_mainLayout->addLayout(d->_createVLayout(d->_minButton)); + d->_mainLayout->addLayout(d->_createVLayout(d->_maxButton)); + d->_mainLayout->addLayout(d->_createVLayout(d->_closeButton)); + +#ifdef Q_OS_WIN + for (int i = 0; i < qApp->screens().count(); i++) + { + connect(qApp->screens().at(i), &QScreen::logicalDotsPerInchChanged, this, [=] { + if (d->_pIsFixedSize) + { + HWND hwnd = (HWND)(d->_currentWinID); + SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_FRAMECHANGED); + } + }); + } + //主屏幕变更处理 + connect(qApp, &QApplication::primaryScreenChanged, this, [=]() { + HWND hwnd = (HWND)(d->_currentWinID); + ::SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); + ::RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW); + }); + d->_lastScreen = qApp->screenAt(window()->geometry().center()); +#endif +} + +ElaAppBar::~ElaAppBar() +{ +} + +void ElaAppBar::setAppBarHeight(int height) +{ + Q_D(ElaAppBar); + d->_pAppBarHeight = height; + setFixedHeight(d->_pAppBarHeight); + window()->setContentsMargins(0, this->height(), 0, 0); + Q_EMIT pAppBarHeightChanged(); +} + +int ElaAppBar::getAppBarHeight() const +{ + Q_D(const ElaAppBar); + return d->_pAppBarHeight; +} + +void ElaAppBar::setCustomWidget(ElaAppBarType::CustomArea customArea, QWidget* widget) +{ + Q_D(ElaAppBar); + if (!widget || widget == this) + { + return; + } + widget->setMinimumHeight(0); + widget->setMaximumHeight(height()); + widget->setMaximumWidth(d->_pCustomWidgetMaximumWidth); + widget->setParent(this); + if (d->_pCustomWidget) + { + d->_mainLayout->removeWidget(d->_pCustomWidget); + } + switch (customArea) + { + case ElaAppBarType::LeftArea: + { + d->_mainLayout->insertWidget(4, widget); + break; + } + case ElaAppBarType::MiddleArea: + { + d->_mainLayout->insertWidget(5, widget); + break; + } + case ElaAppBarType::RightArea: + { + d->_mainLayout->insertWidget(6, widget); + break; + } + } + d->_pCustomWidget = widget; + Q_EMIT customWidgetChanged(); +} + +QWidget* ElaAppBar::getCustomWidget() const +{ + Q_D(const ElaAppBar); + return d->_pCustomWidget; +} + +void ElaAppBar::setCustomWidgetMaximumWidth(int width) +{ + Q_D(ElaAppBar); + d->_pCustomWidgetMaximumWidth = width; + if (d->_pCustomWidget) + { + d->_pCustomWidget->setMaximumWidth(width); + } + Q_EMIT pCustomWidgetMaximumWidthChanged(); +} + +int ElaAppBar::getCustomWidgetMaximumWidth() const +{ + Q_D(const ElaAppBar); + return d->_pCustomWidgetMaximumWidth; +} + +void ElaAppBar::setIsFixedSize(bool isFixedSize) +{ + Q_D(ElaAppBar); + d->_pIsFixedSize = isFixedSize; +#ifdef Q_OS_WIN + HWND hwnd = (HWND)d->_currentWinID; + DWORD style = ::GetWindowLongPtr(hwnd, GWL_STYLE); + if (d->_pIsFixedSize) + { + //切换DPI处理 + style &= ~WS_THICKFRAME; + ::SetWindowLongPtr(hwnd, GWL_STYLE, style); + } + else + { + ::SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_MAXIMIZEBOX | WS_THICKFRAME); + } +#else + bool isVisible = window()->isVisible(); + window()->setWindowFlags((window()->windowFlags()) | Qt::FramelessWindowHint | Qt::WindowMinimizeButtonHint | Qt::WindowCloseButtonHint); + if (!isFixedSize) + { + window()->setWindowFlag(Qt::WindowMaximizeButtonHint); + } + if (isVisible) + { + window()->show(); + } +#endif + Q_EMIT pIsFixedSizeChanged(); +} + +bool ElaAppBar::getIsFixedSize() const +{ + Q_D(const ElaAppBar); + return d->_pIsFixedSize; +} + +void ElaAppBar::setWindowButtonFlag(ElaAppBarType::ButtonType buttonFlag, bool isEnable) +{ + Q_D(ElaAppBar); + if (isEnable) + { + setWindowButtonFlags(d->_buttonFlags | buttonFlag); + } + else + { + setWindowButtonFlags(d->_buttonFlags & ~buttonFlag); + } +} + +void ElaAppBar::setWindowButtonFlags(ElaAppBarType::ButtonFlags buttonFlags) +{ + Q_D(ElaAppBar); + d->_buttonFlags = buttonFlags; + d->_routeBackButton->setVisible(d->_buttonFlags.testFlag(ElaAppBarType::RouteBackButtonHint)); + d->_navigationButton->setVisible(d->_buttonFlags.testFlag(ElaAppBarType::NavigationButtonHint)); + d->_stayTopButton->setVisible(d->_buttonFlags.testFlag(ElaAppBarType::StayTopButtonHint)); + d->_themeChangeButton->setVisible(d->_buttonFlags.testFlag(ElaAppBarType::ThemeChangeButtonHint)); + d->_minButton->setVisible(d->_buttonFlags.testFlag(ElaAppBarType::MinimizeButtonHint)); + d->_maxButton->setVisible(d->_buttonFlags.testFlag(ElaAppBarType::MaximizeButtonHint)); + d->_closeButton->setVisible(d->_buttonFlags.testFlag(ElaAppBarType::CloseButtonHint)); +} + +ElaAppBarType::ButtonFlags ElaAppBar::getWindowButtonFlags() const +{ + return d_ptr->_buttonFlags; +} + +void ElaAppBar::setRouteBackButtonEnable(bool isEnable) +{ + Q_D(ElaAppBar); + d->_routeBackButton->setEnabled(isEnable); +} + +void ElaAppBar::closeWindow() +{ + Q_D(ElaAppBar); + QPropertyAnimation* closeOpacityAnimation = new QPropertyAnimation(window(), "windowOpacity"); + connect(closeOpacityAnimation, &QPropertyAnimation::finished, this, [=]() { + window()->close(); + }); + closeOpacityAnimation->setStartValue(1); + closeOpacityAnimation->setEndValue(0); + closeOpacityAnimation->setEasingCurve(QEasingCurve::InOutSine); + closeOpacityAnimation->start(QAbstractAnimation::DeleteWhenStopped); + if (window()->isMaximized() || window()->isFullScreen() || d->_pIsFixedSize) + { + return; + } + QPropertyAnimation* geometryAnimation = new QPropertyAnimation(window(), "geometry"); + QRect geometry = window()->geometry(); + geometryAnimation->setStartValue(geometry); + qreal targetWidth = (geometry.width() - d->_lastMinTrackWidth) * 0.7 + d->_lastMinTrackWidth; + qreal targetHeight = (geometry.height() - window()->minimumHeight()) * 0.7 + window()->minimumHeight(); + geometryAnimation->setEndValue(QRectF(geometry.center().x() - targetWidth / 2, geometry.center().y() - targetHeight / 2, targetWidth, targetHeight)); + geometryAnimation->setEasingCurve(QEasingCurve::InOutSine); + geometryAnimation->start(QAbstractAnimation::DeleteWhenStopped); +} + +#ifdef Q_OS_WIN +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +int ElaAppBar::takeOverNativeEvent(const QByteArray& eventType, void* message, qintptr* result) +#else +int ElaAppBar::takeOverNativeEvent(const QByteArray& eventType, void* message, long* result) +#endif +{ + Q_D(ElaAppBar); + if ((eventType != "windows_generic_MSG") || !message) + { + return 0; + } + const auto msg = static_cast(message); + const HWND hwnd = msg->hwnd; + if (!hwnd || !msg) + { + return 0; + } + d->_currentWinID = (qint64)hwnd; + const UINT uMsg = msg->message; + const WPARAM wParam = msg->wParam; + const LPARAM lParam = msg->lParam; + switch (uMsg) + { + case WM_WINDOWPOSCHANGING: + { + WINDOWPOS* wp = reinterpret_cast(lParam); + if (wp != nullptr && (wp->flags & SWP_NOSIZE) == 0) + { + wp->flags |= SWP_NOCOPYBITS; + *result = ::DefWindowProcW(hwnd, uMsg, wParam, lParam); + return 1; + } + return 0; + } + case WM_NCACTIVATE: + { + *result = TRUE; + return 1; + } + case WM_SIZE: + { + if (wParam == SIZE_RESTORED) + { + d->_changeMaxButtonAwesome(false); + } + else if (wParam == SIZE_MAXIMIZED) + { + d->_changeMaxButtonAwesome(true); + } + return 0; + } + case WM_NCCALCSIZE: + { +#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 3) && QT_VERSION <= QT_VERSION_CHECK(6, 6, 1)) + if (wParam == FALSE) + { + return 0; + } + if (::IsZoomed(hwnd)) + { + this->move(7, 7); + window()->setContentsMargins(8, 8 + height(), 8, 8); + } + else + { + this->move(0, 0); + window()->setContentsMargins(0, height(), 0, 0); + } + *result = 0; + return 1; +#else + if (wParam == FALSE) + { + return 0; + } + RECT* clientRect = &((NCCALCSIZE_PARAMS*)(lParam))->rgrc[0]; + if (!::IsZoomed(hwnd)) + { + clientRect->top -= 1; + clientRect->bottom -= 1; + } + else + { + const LRESULT hitTestResult = ::DefWindowProcW(hwnd, WM_NCCALCSIZE, wParam, lParam); + if ((hitTestResult != HTERROR) && (hitTestResult != HTNOWHERE)) + { + *result = static_cast(hitTestResult); + return 1; + } +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + auto geometry = window()->screen()->geometry(); +#else + QScreen* screen = qApp->screenAt(window()->geometry().center()); + QRect geometry; + if (!screen) + { + screen = qApp->screenAt(QCursor::pos()); + } + geometry = screen->geometry(); +#endif + clientRect->top = geometry.top(); + } + *result = WVR_REDRAW; + return 1; +#endif + } + case WM_MOVE: + { + QScreen* currentScreen = qApp->screenAt(window()->geometry().center()); + if (currentScreen && currentScreen != d->_lastScreen) + { + if (d->_lastScreen) + { + ::SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); + ::RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW); + } + d->_lastScreen = currentScreen; + } + break; + } + case WM_NCHITTEST: + { + if (d->_containsCursorToItem(d->_maxButton)) + { + if (*result == HTNOWHERE) + { + if (!d->_isHoverMaxButton) + { + d->_isHoverMaxButton = true; + d->_maxButton->setIsSelected(true); + d->_maxButton->update(); + } + *result = HTZOOM; + } + return 1; + } + else + { + if (d->_isHoverMaxButton) + { + d->_isHoverMaxButton = false; + d->_maxButton->setIsSelected(false); + d->_maxButton->update(); + } + } + POINT nativeLocalPos{GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; + ::ScreenToClient(hwnd, &nativeLocalPos); + RECT clientRect{0, 0, 0, 0}; + ::GetClientRect(hwnd, &clientRect); + auto clientWidth = clientRect.right - clientRect.left; + auto clientHeight = clientRect.bottom - clientRect.top; + bool left = nativeLocalPos.x < d->_margins; + bool right = nativeLocalPos.x > clientWidth - d->_margins; + bool top = nativeLocalPos.y < d->_margins; + bool bottom = nativeLocalPos.y > clientHeight - d->_margins; + *result = 0; + if (!d->_pIsOnlyAllowMinAndClose && !d->_pIsFixedSize && !window()->isFullScreen() && !window()->isMaximized()) + { + if (left && top) + { + *result = HTTOPLEFT; + } + else if (left && bottom) + { + *result = HTBOTTOMLEFT; + } + else if (right && top) + { + *result = HTTOPRIGHT; + } + else if (right && bottom) + { + *result = HTBOTTOMRIGHT; + } + else if (left) + { + *result = HTLEFT; + } + else if (right) + { + *result = HTRIGHT; + } + else if (top) + { + *result = HTTOP; + } + else if (bottom) + { + *result = HTBOTTOM; + } + } + if (0 != *result) + { + return 1; + } + if (d->_containsCursorToItem(this)) + { + *result = HTCAPTION; + return 1; + } + *result = HTCLIENT; + return 1; + } + case WM_GETMINMAXINFO: + { + MINMAXINFO* minmaxInfo = reinterpret_cast(lParam); + RECT rect; + SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0); + d->_lastMinTrackWidth = d->_calculateMinimumWidth(); + minmaxInfo->ptMinTrackSize.x = d->_lastMinTrackWidth * qApp->devicePixelRatio(); + minmaxInfo->ptMinTrackSize.y = parentWidget()->minimumHeight() * qApp->devicePixelRatio(); + minmaxInfo->ptMaxPosition.x = rect.left; + minmaxInfo->ptMaxPosition.y = rect.top; + return 1; + } + case WM_LBUTTONDBLCLK: + { + QVariantMap postData; + postData.insert("WMClickType", ElaAppBarType::WMLBUTTONDBLCLK); + ElaEventBus::getInstance()->post("WMWindowClicked", postData); + return 0; + } + case WM_LBUTTONDOWN: + { + QVariantMap postData; + postData.insert("WMClickType", ElaAppBarType::WMLBUTTONDOWN); + ElaEventBus::getInstance()->post("WMWindowClicked", postData); + return 0; + } + case WM_LBUTTONUP: + { + QVariantMap postData; + postData.insert("WMClickType", ElaAppBarType::WMLBUTTONUP); + ElaEventBus::getInstance()->post("WMWindowClicked", postData); + return 0; + } + case WM_NCLBUTTONDOWN: + { + QVariantMap postData; + postData.insert("WMClickType", ElaAppBarType::WMNCLBUTTONDOWN); + ElaEventBus::getInstance()->post("WMWindowClicked", postData); + if (d->_containsCursorToItem(d->_maxButton) || d->_pIsOnlyAllowMinAndClose) + { + return 1; + } + break; + } + case WM_NCLBUTTONUP: + { + QVariantMap postData; + postData.insert("WMClickType", ElaAppBarType::WMNCLBUTTONDOWN); + ElaEventBus::getInstance()->post("WMWindowClicked", postData); + if (d->_containsCursorToItem(d->_maxButton) && !d->_pIsOnlyAllowMinAndClose) + { + d->onMaxButtonClicked(); + return 1; + } + break; + } + case WM_NCLBUTTONDBLCLK: + { + if (!d->_pIsOnlyAllowMinAndClose && !d->_pIsFixedSize) + { + return 0; + } + return 1; + } + case WM_NCRBUTTONDOWN: + { + if (wParam == HTCAPTION && !d->_pIsOnlyAllowMinAndClose) + { + d->_showSystemMenu(QCursor::pos()); + } + break; + } + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + { + if ((GetAsyncKeyState(VK_MENU) & 0x8000) && (GetAsyncKeyState(VK_SPACE) & 0x8000) && !d->_pIsOnlyAllowMinAndClose) + { + auto pos = window()->geometry().topLeft(); + d->_showSystemMenu(QPoint(pos.x(), pos.y() + this->height())); + } + break; + } + } + return -1; +} +#endif + +bool ElaAppBar::eventFilter(QObject* obj, QEvent* event) +{ + Q_D(ElaAppBar); + switch (event->type()) + { + case QEvent::Resize: + { + QSize size = parentWidget()->size(); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 3) && QT_VERSION <= QT_VERSION_CHECK(6, 6, 1)) + if (::IsZoomed((HWND)d->_currentWinID)) + { + this->resize(size.width() - 14, this->height()); + } + else + { + this->resize(size.width(), this->height()); + } +#else + this->resize(size.width(), this->height()); +#endif + break; + } +#ifdef Q_OS_WIN + case QEvent::Show: + { + if (!d->_pIsFixedSize && !d->_pIsOnlyAllowMinAndClose) + { + HWND hwnd = (HWND)d->_currentWinID; + DWORD style = ::GetWindowLongPtr(hwnd, GWL_STYLE); + ::SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_MAXIMIZEBOX | WS_THICKFRAME); + } +#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 3) && QT_VERSION <= QT_VERSION_CHECK(6, 6, 1)) + HWND hwnd = (HWND)d->_currentWinID; + setShadow(hwnd); + DWORD style = ::GetWindowLongPtr(hwnd, GWL_STYLE); + bool hasCaption = (style & WS_CAPTION) == WS_CAPTION; + if (!hasCaption) + { + ::SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_CAPTION); + } +#endif + break; + } +#endif + case QEvent::Close: + { + QCloseEvent* closeEvent = dynamic_cast(event); + if (!d->_pIsDefaultClosed && closeEvent->spontaneous()) + { + event->ignore(); + if (window()->isMinimized()) + { + window()->showNormal(); + } + d->onCloseButtonClicked(); + } + return true; + } +#ifndef Q_OS_WIN + case QEvent::MouseButtonPress: + { + if (d->_edges != 0) + { + QMouseEvent* mouseEvent = static_cast(event); + if (mouseEvent->button() == Qt::LeftButton) + { + d->_updateCursor(d->_edges); + window()->windowHandle()->startSystemResize(Qt::Edges(d->_edges)); + } + } + else + { + if (d->_containsCursorToItem(this)) + { + qint64 clickTimer = QDateTime::currentMSecsSinceEpoch(); + qint64 offset = clickTimer - d->_clickTimer; + d->_clickTimer = clickTimer; + if (offset > 300) + { + window()->windowHandle()->startSystemMove(); + } + } + } + break; + } + case QEvent::MouseButtonDblClick: + { + if (d->_containsCursorToItem(this)) + { + if (window()->isMaximized()) + { + window()->showNormal(); + } + else + { + window()->showMaximized(); + } + } + break; + } + case QEvent::MouseButtonRelease: + { + d->_edges = 0; + break; + } + case QEvent::HoverMove: + { + if (window()->isMaximized() || window()->isFullScreen()) + { + break; + } + if (d->_pIsFixedSize) + { + break; + } + QHoverEvent* mouseEvent = static_cast(event); + QPoint p = +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + mouseEvent->pos(); +#else + mouseEvent->position().toPoint(); +#endif + if (p.x() >= d->_margins && p.x() <= (window()->width() - d->_margins) && p.y() >= d->_margins && p.y() <= (window()->height() - d->_margins)) + { + if (d->_edges != 0) + { + d->_edges = 0; + d->_updateCursor(d->_edges); + } + break; + } + d->_edges = 0; + if (p.x() < d->_margins) + { + d->_edges |= Qt::LeftEdge; + } + if (p.x() > (window()->width() - d->_margins)) + { + d->_edges |= Qt::RightEdge; + } + if (p.y() < d->_margins) + { + d->_edges |= Qt::TopEdge; + } + if (p.y() > (window()->height() - d->_margins)) + { + d->_edges |= Qt::BottomEdge; + } + d->_updateCursor(d->_edges); + break; + } +#endif + default: + { + break; + } + } + return QObject::eventFilter(obj, event); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaApplication.cpp b/3rdparty/ElaWidgetTools/src/ElaApplication.cpp new file mode 100644 index 0000000..09490a1 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaApplication.cpp @@ -0,0 +1,109 @@ +#include "ElaApplication.h" + +#include +#include +#include +#include + +#include "ElaTheme.h" +#include "private/ElaApplicationPrivate.h" +Q_SINGLETON_CREATE_CPP(ElaApplication) +ElaApplication::ElaApplication(QObject* parent) + : QObject{parent}, d_ptr(new ElaApplicationPrivate()) +{ + Q_D(ElaApplication); + d->q_ptr = this; + d->_pIsEnableMica = false; + d->_pMicaImagePath = ":/include/Image/MicaBase.png"; + d->_themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, d, &ElaApplicationPrivate::onThemeModeChanged); +} + +ElaApplication::~ElaApplication() +{ +} + +void ElaApplication::setIsEnableMica(bool isEnable) +{ + Q_D(ElaApplication); + d->_pIsEnableMica = isEnable; + if (isEnable) + { + d->_initMicaBaseImage(QImage(d->_pMicaImagePath)); + } + else + { + d->onThemeModeChanged(d->_themeMode); + Q_EMIT pIsEnableMicaChanged(); + } +} + +bool ElaApplication::getIsEnableMica() const +{ + Q_D(const ElaApplication); + return d->_pIsEnableMica; +} + +void ElaApplication::setMicaImagePath(QString micaImagePath) +{ + Q_D(ElaApplication); + d->_pMicaImagePath = micaImagePath; + d->_initMicaBaseImage(QImage(d->_pMicaImagePath)); + Q_EMIT pMicaImagePathChanged(); +} + +QString ElaApplication::getMicaImagePath() const +{ + Q_D(const ElaApplication); + return d->_pMicaImagePath; +} + +void ElaApplication::init() +{ + QApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); + QFontDatabase::addApplicationFont(":/include/Font/ElaAwesome.ttf"); + //默认字体 + QFont font = qApp->font(); + font.setPixelSize(13); + font.setFamily("Microsoft YaHei"); + font.setHintingPreference(QFont::PreferNoHinting); + qApp->setFont(font); +} + +void ElaApplication::syncMica(QWidget* widget, bool isSync) +{ + Q_D(ElaApplication); + if (!widget) + { + return; + } + if (isSync) + { + widget->installEventFilter(d); + d->_micaWidgetList.append(widget); + if (d->_pIsEnableMica) + { + d->_updateMica(widget, false); + } + } + else + { + widget->removeEventFilter(d); + d->_micaWidgetList.removeOne(widget); + } +} + +bool ElaApplication::containsCursorToItem(QWidget* item) +{ + if (!item || !item->isVisible()) + { + return false; + } + auto point = item->window()->mapFromGlobal(QCursor::pos()); + QRectF rect = QRectF(item->mapTo(item->window(), QPoint(0, 0)), item->size()); + if (rect.contains(point)) + { + return true; + } + return false; +} diff --git a/3rdparty/ElaWidgetTools/src/ElaBreadcrumbBar.cpp b/3rdparty/ElaWidgetTools/src/ElaBreadcrumbBar.cpp new file mode 100644 index 0000000..8a57dd4 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaBreadcrumbBar.cpp @@ -0,0 +1,133 @@ +#include "ElaBreadcrumbBar.h" + +#include +#include +#include + +#include "DeveloperComponents/ElaBreadcrumbBarDelegate.h" +#include "DeveloperComponents/ElaBreadcrumbBarModel.h" +#include "ElaBaseListView.h" +#include "ElaBreadcrumbBarPrivate.h" +Q_PROPERTY_CREATE_Q_CPP(ElaBreadcrumbBar, bool, IsAutoRemove); +ElaBreadcrumbBar::ElaBreadcrumbBar(QWidget* parent) + : QWidget{parent}, d_ptr(new ElaBreadcrumbBarPrivate()) +{ + Q_D(ElaBreadcrumbBar); + d->q_ptr = this; + d->_pTextPixelSize = 18; + d->_pIsAutoRemove = true; + setFixedHeight(37); + setObjectName("ElaBreadcrumbBar"); + setStyleSheet("#ElaBreadcrumbBar{background-color:transparent;}"); + setMouseTracking(true); + + d->_listView = new ElaBaseListView(this); + d->_listView->setMinimumHeight(0); + d->_listView->setFlow(QListView::LeftToRight); + d->_listView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + d->_listView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + d->_listModel = new ElaBreadcrumbBarModel(this); + d->_listView->setModel(d->_listModel); + d->_listDelegate = new ElaBreadcrumbBarDelegate(this); + d->_listView->setItemDelegate(d->_listDelegate); + connect(d->_listView, &QListView::clicked, this, [=](const QModelIndex& index) { + if (d->_pIsAutoRemove) + { + if (d->_listModel->getBreadcrumbListCount() != 1 && index.row() != d->_listModel->getBreadcrumbListCount() * 2 - 2 && index.data(Qt::DisplayRole).toString() != ">") + { + Q_EMIT breadcrumbClicked(index.data(Qt::DisplayRole).toString(), d->_listModel->getBreadcrumbList()); + d->_listModel->removeBreadcrumb(index.row() / 2 + 1); + } + } + else + { + Q_EMIT breadcrumbClicked(index.data(Qt::DisplayRole).toString(), d->_listModel->getBreadcrumbList()); + } + }); + QFont textFont = this->font(); + textFont.setLetterSpacing(QFont::AbsoluteSpacing, 0.5); + textFont.setPixelSize(d->_pTextPixelSize); + d->_listView->setFont(textFont); + + QScroller::grabGesture(d->_listView->viewport(), QScroller::LeftMouseButtonGesture); + QScroller* scroller = QScroller::scroller(d->_listView->viewport()); + QScrollerProperties properties = scroller->scrollerProperties(); + properties.setScrollMetric(QScrollerProperties::MousePressEventDelay, 0); + properties.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy, QScrollerProperties::OvershootAlwaysOn); + properties.setScrollMetric(QScrollerProperties::OvershootDragResistanceFactor, 0.35); + properties.setScrollMetric(QScrollerProperties::OvershootScrollTime, 0.5); + properties.setScrollMetric(QScrollerProperties::FrameRate, QScrollerProperties::Fps60); + scroller->setScrollerProperties(properties); + + connect(scroller, &QScroller::stateChanged, this, [=](QScroller::State newstate) { + if (newstate == QScroller::Pressed) + { + d->_listDelegate->setPressIndex(d->_listView->indexAt(d->_listView->mapFromGlobal(QCursor::pos()))); + d->_listView->viewport()->update(); + } + else if (newstate == QScroller::Scrolling || newstate == QScroller::Inactive) + { + d->_listDelegate->setPressIndex(QModelIndex()); + } + }); + + QVBoxLayout* mainLayout = new QVBoxLayout(this); + mainLayout->setContentsMargins(0, 0, 0, 0); + mainLayout->addWidget(d->_listView); +} + +ElaBreadcrumbBar::~ElaBreadcrumbBar() +{ +} + +void ElaBreadcrumbBar::setTextPixelSize(int textPixelSize) +{ + Q_D(ElaBreadcrumbBar); + if (textPixelSize > 0) + { + d->_pTextPixelSize = textPixelSize; + QFont textFont = this->font(); + textFont.setLetterSpacing(QFont::AbsoluteSpacing, 0.5); + textFont.setPixelSize(d->_pTextPixelSize); + d->_listView->setFont(textFont); + Q_EMIT pTextPixelSizeChanged(); + } +} + +int ElaBreadcrumbBar::getTextPixelSize() const +{ + Q_D(const ElaBreadcrumbBar); + return d->_pTextPixelSize; +} + +void ElaBreadcrumbBar::setBreadcrumbList(QStringList breadcrumbList) +{ + Q_D(ElaBreadcrumbBar); + d->_listModel->setBreadcrumbList(breadcrumbList); +} + +QStringList ElaBreadcrumbBar::appendBreadcrumb(QString breadcrumb) +{ + Q_D(ElaBreadcrumbBar); + d->_listModel->appendBreadcrumb(breadcrumb); + return d->_listModel->getBreadcrumbList(); +} + +QStringList ElaBreadcrumbBar::removeBreadcrumb(QString breadcrumb) +{ + Q_D(ElaBreadcrumbBar); + d->_listModel->removeBreadcrumb(breadcrumb); + return d->_listModel->getBreadcrumbList(); +} + +int ElaBreadcrumbBar::getBreadcrumbListCount() const +{ + Q_D(const ElaBreadcrumbBar); + return d->_listModel->getBreadcrumbListCount(); +} + +QStringList ElaBreadcrumbBar::getBreadcrumbList() const +{ + Q_D(const ElaBreadcrumbBar); + return d->_listModel->getBreadcrumbList(); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaCalendar.cpp b/3rdparty/ElaWidgetTools/src/ElaCalendar.cpp new file mode 100644 index 0000000..d018b5f --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaCalendar.cpp @@ -0,0 +1,188 @@ +#include "ElaCalendar.h" + +#include +#include +#include +#include + +#include "ElaBaseListView.h" +#include "ElaCalendarDelegate.h" +#include "ElaCalendarModel.h" +#include "ElaCalendarPrivate.h" +#include "ElaCalendarTitleDelegate.h" +#include "ElaCalendarTitleModel.h" +#include "ElaScrollBar.h" +#include "ElaTheme.h" +#include "ElaToolButton.h" +Q_PROPERTY_CREATE_Q_CPP(ElaCalendar, int, BorderRaiuds) +ElaCalendar::ElaCalendar(QWidget* parent) + : QWidget{parent}, d_ptr(new ElaCalendarPrivate()) +{ + Q_D(ElaCalendar); + setFixedSize(305, 340); + setObjectName("ElaCalendar"); + d->q_ptr = this; + d->_pBorderRaiuds = 5; + + // 日历标题 + d->_calendarTitleView = new ElaBaseListView(this); + d->_calendarTitleView->setFlow(QListView::LeftToRight); + d->_calendarTitleView->setViewMode(QListView::IconMode); + d->_calendarTitleView->setResizeMode(QListView::Adjust); + d->_calendarTitleView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + d->_calendarTitleView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + d->_calendarTitleView->setModel(new ElaCalendarTitleModel(this)); + d->_calendarTitleView->setItemDelegate(new ElaCalendarTitleDelegate(this)); + d->_calendarTitleView->setFixedHeight(30); + + // 日历内容 + d->_calendarView = new ElaBaseListView(this); + d->_calendarView->setFlow(QListView::LeftToRight); + d->_calendarView->setViewMode(QListView::IconMode); + d->_calendarView->setResizeMode(QListView::Adjust); + ElaScrollBar* vScrollBar = dynamic_cast(d->_calendarView->verticalScrollBar()); + vScrollBar->setSpeedLimit(6); + d->_calendarView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + d->_calendarView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + d->_calendarView->setSelectionMode(QAbstractItemView::NoSelection); + d->_calendarModel = new ElaCalendarModel(this); + d->_calendarView->setModel(d->_calendarModel); + d->_calendarDelegate = new ElaCalendarDelegate(d->_calendarModel, this); + d->_calendarView->setItemDelegate(d->_calendarDelegate); + connect(d->_calendarView, &ElaBaseListView::clicked, d, &ElaCalendarPrivate::onCalendarViewClicked); + + // 模式切换按钮 + d->_modeSwitchButton = new ElaToolButton(this); + d->_modeSwitchButton->setText("1924年1月"); + QFont switchButtonFont = d->_modeSwitchButton->font(); + switchButtonFont.setWeight(QFont::Bold); + d->_modeSwitchButton->setFont(switchButtonFont); + d->_modeSwitchButton->setToolButtonStyle(Qt::ToolButtonTextOnly); + d->_modeSwitchButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + connect(d->_modeSwitchButton, &ElaToolButton::clicked, d, &ElaCalendarPrivate::onSwitchButtonClicked); + connect(d->_calendarView->verticalScrollBar(), &QScrollBar::valueChanged, d, [=]() { + d->_updateSwitchButtonText(); + }); + + // 翻页按钮 + d->_upButton = new ElaToolButton(this); + d->_upButton->setFixedSize(36, 36); + d->_upButton->setElaIcon(ElaIconType::CaretUp); + connect(d->_upButton, &ElaToolButton::clicked, d, &ElaCalendarPrivate::onUpButtonClicked); + + d->_downButton = new ElaToolButton(this); + d->_downButton->setFixedSize(36, 36); + d->_downButton->setElaIcon(ElaIconType::CaretDown); + connect(d->_downButton, &ElaToolButton::clicked, d, &ElaCalendarPrivate::onDownButtonClicked); + + QHBoxLayout* buttonLayout = new QHBoxLayout(); + buttonLayout->setContentsMargins(5, 5, 10, 0); + buttonLayout->addWidget(d->_modeSwitchButton); + buttonLayout->addWidget(d->_upButton); + buttonLayout->addWidget(d->_downButton); + + QVBoxLayout* mainLayout = new QVBoxLayout(this); + mainLayout->setContentsMargins(5, 0, 0, 0); + mainLayout->addLayout(buttonLayout); + mainLayout->addWidget(d->_calendarTitleView); + mainLayout->addWidget(d->_calendarView); + + d->_themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { + d->_themeMode = themeMode; + }); + setVisible(true); + QDate currentDate = QDate::currentDate(); + d->_lastSelectedYear = currentDate.year(); + d->_lastSelectedMonth = currentDate.month(); + d->_scrollToDate(currentDate); +} + +ElaCalendar::~ElaCalendar() +{ +} + +void ElaCalendar::setSelectedDate(QDate selectedDate) +{ + Q_D(ElaCalendar); + if (!selectedDate.isValid() || selectedDate.daysTo(d->_calendarModel->getMaximumDate()) < 0 || selectedDate.daysTo(d->_calendarModel->getMinimumDate()) > 0) + { + return; + } + d->_pSelectedDate = selectedDate; + d->_calendarView->selectionModel()->setCurrentIndex(d->_calendarModel->getIndexFromDate(selectedDate), QItemSelectionModel::Select); + Q_EMIT pSelectedDateChanged(); +} + +QDate ElaCalendar::getSelectedDate() const +{ + Q_D(const ElaCalendar); + return d->_pSelectedDate; + //return d->_calendarModel->getDateFromIndex(d->_calendarView->selectionModel()->currentIndex()); +} + +void ElaCalendar::setMinimumDate(QDate minimudate) +{ + Q_D(ElaCalendar); + if (!minimudate.isValid() || minimudate.daysTo(d->_calendarModel->getMaximumDate()) < 0) + { + return; + } + d->_calendarModel->setMaximumDate(minimudate); + Q_EMIT pMinimumDateChanged(); +} + +QDate ElaCalendar::getMinimumDate() const +{ + Q_D(const ElaCalendar); + return d->_calendarModel->getMinimumDate(); +} + +void ElaCalendar::setMaximumDate(QDate maximumDate) +{ + Q_D(ElaCalendar); + if (!maximumDate.isValid() || maximumDate.daysTo(d->_calendarModel->getMinimumDate()) > 0) + { + return; + } + d->_calendarModel->setMaximumDate(maximumDate); + Q_EMIT pMaximumDateChanged(); +} + +QDate ElaCalendar::getMaximumDate() const +{ + Q_D(const ElaCalendar); + return d->_calendarModel->getMaximumDate(); +} + +void ElaCalendar::paintEvent(QPaintEvent* event) +{ + Q_D(ElaCalendar); + QPainter painter(this); + painter.save(); + painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + + QRect baseRect = rect(); + baseRect.adjust(d->_borderWidth, d->_borderWidth, -d->_borderWidth, -d->_borderWidth); + painter.setPen(Qt::NoPen); + painter.setBrush(ElaThemeColor(d->_themeMode, DialogBase)); + painter.drawRoundedRect(baseRect, d->_pBorderRaiuds, d->_pBorderRaiuds); + // 缩放动画 + if (!d->_isSwitchAnimationFinished) + { + painter.save(); + QRect pixRect = QRect(baseRect.x(), d->_borderWidth + 45, baseRect.width(), baseRect.height() - 45); + painter.setOpacity(d->_pPixOpacity); + painter.translate(pixRect.center()); + painter.scale(d->_pZoomRatio, d->_pZoomRatio); + painter.translate(-pixRect.center()); + painter.drawPixmap(pixRect, d->_isDrawNewPix ? d->_newCalendarViewPix : d->_oldCalendarViewPix); + painter.restore(); + } + // 分割线 + painter.setPen(QPen(ElaThemeColor(d->_themeMode, BasicBorder), d->_borderWidth)); + painter.setBrush(Qt::NoBrush); + painter.drawRoundedRect(baseRect, d->_pBorderRaiuds, d->_pBorderRaiuds); + painter.drawLine(QPointF(baseRect.x(), 45), QPointF(baseRect.right(), 45)); + painter.restore(); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaCalendarPicker.cpp b/3rdparty/ElaWidgetTools/src/ElaCalendarPicker.cpp new file mode 100644 index 0000000..3d5b5ad --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaCalendarPicker.cpp @@ -0,0 +1,82 @@ +#include "ElaCalendarPicker.h" + +#include +#include +#include +#include + +#include "ElaCalendar.h" +#include "ElaCalendarPickerContainer.h" +#include "ElaCalendarPickerPrivate.h" +#include "ElaTheme.h" +Q_PROPERTY_CREATE_Q_CPP(ElaCalendarPicker, int, BorderRadius) +ElaCalendarPicker::ElaCalendarPicker(QWidget* parent) + : QPushButton{parent}, d_ptr(new ElaCalendarPickerPrivate()) +{ + Q_D(ElaCalendarPicker); + d->q_ptr = this; + d->_pBorderRadius = 3; + setFixedSize(120, 30); + setObjectName("ElaCalendarPicker"); + setMouseTracking(true); + d->_calendarPickerContainer = new ElaCalendarPickerContainer(this); + d->_calendarPickerContainer->resize(317, 352); + d->_calendar = new ElaCalendar(d->_calendarPickerContainer); + QVBoxLayout* containerLayout = new QVBoxLayout(d->_calendarPickerContainer); + containerLayout->setContentsMargins(6, 6, 6, 6); + containerLayout->addWidget(d->_calendar); + d->_calendarPickerContainer->hide(); + connect(this, &QPushButton::clicked, d, &ElaCalendarPickerPrivate::onCalendarPickerClicked); + + connect(d->_calendar, &ElaCalendar::pSelectedDateChanged, d, &ElaCalendarPickerPrivate::onCalendarSelectedDateChanged); + setSelectedDate(QDate::currentDate()); + + d->_themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { d->_themeMode = themeMode; }); +} + +ElaCalendarPicker::~ElaCalendarPicker() +{ +} + +void ElaCalendarPicker::setSelectedDate(QDate selectedDate) +{ + Q_D(ElaCalendarPicker); + d->_calendar->setSelectedDate(selectedDate); + Q_EMIT selectedDateChanged(selectedDate); +} + +QDate ElaCalendarPicker::getSelectedDate() const +{ + Q_D(const ElaCalendarPicker); + return d->_calendar->getSelectedDate(); +} + +void ElaCalendarPicker::paintEvent(QPaintEvent* event) +{ + Q_D(ElaCalendarPicker); + QPainter painter(this); + painter.save(); + painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + // 边框和背景绘制 + QRect baseRect = rect(); + baseRect.adjust(1, 1, -1, -1); + painter.setPen(ElaThemeColor(d->_themeMode, BasicBorder)); + painter.setBrush(underMouse() ? ElaThemeColor(d->_themeMode, BasicHover) : ElaThemeColor(d->_themeMode, BasicBase)); + painter.drawRoundedRect(baseRect, d->_pBorderRadius, d->_pBorderRadius); + + // 日期绘制 + QDate selectedDate = getSelectedDate(); + QString date = QString("%1/%2/%3").arg(selectedDate.year()).arg(selectedDate.month()).arg(selectedDate.day()); + painter.setPen(ElaThemeColor(d->_themeMode, BasicText)); + QRect textRect = baseRect; + textRect.adjust(10, 0, 0, 0); + painter.drawText(textRect, Qt::AlignVCenter | Qt::AlignLeft, date); + + // 图标绘制 + QFont iconFont = QFont("ElaAwesome"); + iconFont.setPixelSize(17); + painter.setFont(iconFont); + painter.drawText(QRect(baseRect.right() - 25, 0, 15, height()), Qt::AlignVCenter | Qt::AlignRight, QChar((unsigned short)ElaIconType::CalendarRange)); + painter.restore(); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaCheckBox.cpp b/3rdparty/ElaWidgetTools/src/ElaCheckBox.cpp new file mode 100644 index 0000000..0f34248 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaCheckBox.cpp @@ -0,0 +1,24 @@ +#include "ElaCheckBox.h" + +#include "ElaCheckBoxStyle.h" +ElaCheckBox::ElaCheckBox(QWidget* parent) + : QCheckBox(parent) +{ + _pBorderRadius = 3; + setMouseTracking(true); + setObjectName("ElaCheckBox"); + setStyle(new ElaCheckBoxStyle(style())); + QFont font = this->font(); + font.setPixelSize(15); + setFont(font); +} + +ElaCheckBox::ElaCheckBox(const QString& text, QWidget* parent) + : ElaCheckBox(parent) +{ + setText(text); +} + +ElaCheckBox::~ElaCheckBox() +{ +} diff --git a/3rdparty/ElaWidgetTools/src/ElaColorDialog.cpp b/3rdparty/ElaWidgetTools/src/ElaColorDialog.cpp new file mode 100644 index 0000000..0f4cc7b --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaColorDialog.cpp @@ -0,0 +1,340 @@ +#include "ElaColorDialog.h" + +#include +#include +#include + +#include "ElaBaseListView.h" +#include "ElaColorDialogPrivate.h" +#include "ElaColorDisplayDelegate.h" +#include "ElaColorDisplayModel.h" +#include "ElaColorPicker.h" +#include "ElaColorPreview.h" +#include "ElaColorValueSliderStyle.h" +#include "ElaComboBox.h" +#include "ElaIntValidator.h" +#include "ElaLineEdit.h" +#include "ElaPushButton.h" +#include "ElaText.h" +#include "ElaTheme.h" +#include "ElaToolButton.h" +Q_TAKEOVER_NATIVEEVENT_CPP(ElaColorDialog, d_func()->_appBar); +ElaColorDialog::ElaColorDialog(QWidget* parent) + : QDialog{parent}, d_ptr(new ElaColorDialogPrivate()) +{ + Q_D(ElaColorDialog); + d->q_ptr = this; + setFixedSize(620, 630); // 默认宽高 + setObjectName("ElaColorDialog"); + setWindowTitle("ElaColorDialog"); + setWindowModality(Qt::ApplicationModal); + + // 自定义AppBar + d->_appBar = new ElaAppBar(this); + d->_appBar->setAppBarHeight(30); + d->_appBar->setIsFixedSize(true); + d->_appBar->setIsStayTop(true); + d->_appBar->setWindowButtonFlags(ElaAppBarType::CloseButtonHint); + d->_appBar->setIsDefaultClosed(false); + connect(d->_appBar, &ElaAppBar::closeButtonClicked, this, [=]() { + close(); + }); + + // 颜色选择器 + ElaText* colorPickerText = new ElaText("编辑颜色", this); + colorPickerText->setTextPixelSize(17); + QHBoxLayout* colorPickerTextLayout = new QHBoxLayout(); + colorPickerTextLayout->setContentsMargins(0, 0, 0, 0); + colorPickerTextLayout->addSpacing(3); + colorPickerTextLayout->addWidget(colorPickerText); + + d->_colorPicker = new ElaColorPicker(this); + connect(d->_colorPicker, &ElaColorPicker::selectedColorChanged, d, &ElaColorDialogPrivate::onColorPickerColorChanged); + + QVBoxLayout* colorPickerLayout = new QVBoxLayout(); + colorPickerLayout->setContentsMargins(0, 0, 0, 0); + colorPickerLayout->addWidget(d->_colorPicker); + colorPickerLayout->addStretch(); + + // 颜色预览 + d->_colorPreview = new ElaColorPreview(this); + QVBoxLayout* colorPreviewLayout = new QVBoxLayout(); + colorPreviewLayout->setContentsMargins(0, 0, 0, 0); + colorPreviewLayout->addWidget(d->_colorPreview); + colorPreviewLayout->addStretch(); + + // 颜色value控制 + d->_colorValueSlider = new QSlider(this); + d->_colorValueSlider->setFixedHeight(257); + d->_colorValueSlider->setOrientation(Qt::Vertical); + d->_colorValueSlider->setRange(0, 255); + d->_colorValueSlider->setValue(255); + d->_colorValueSliderStyle = new ElaColorValueSliderStyle(); + d->_colorValueSlider->setStyle(d->_colorValueSliderStyle); + connect(d->_colorValueSlider, &QSlider::valueChanged, d, &ElaColorDialogPrivate::onColorValueSliderChanged); + QVBoxLayout* colorValueSliderLayout = new QVBoxLayout(); + colorValueSliderLayout->setContentsMargins(0, 0, 0, 0); + colorValueSliderLayout->addWidget(d->_colorValueSlider); + colorValueSliderLayout->addStretch(); + + // 颜色控制器 + d->_htmlEdit = new ElaLineEdit(this); + d->_htmlEdit->setText("#000000"); + ElaIntValidator* htmlValidator = new ElaIntValidator(0x000000, 0xFFFFFF, this); + htmlValidator->setIsHexMode(true); + d->_htmlEdit->setValidator(htmlValidator); + d->_htmlEdit->setFixedSize(120, 33); + connect(d->_htmlEdit, &ElaLineEdit::focusOut, d, &ElaColorDialogPrivate::onHtmlEditFocusOut); + connect(d->_htmlEdit, &ElaLineEdit::textEdited, d, &ElaColorDialogPrivate::onHtmlEditChanged); + + // 数值预览 + QHBoxLayout* htmlEditLayout = new QHBoxLayout(); + htmlEditLayout->setContentsMargins(0, 0, 0, 0); + htmlEditLayout->addSpacing(6); + htmlEditLayout->addWidget(d->_htmlEdit); + htmlEditLayout->addStretch(); + d->_modeComboBox = new ElaComboBox(this); + d->_modeComboBox->setFixedSize(132, 33); + d->_modeComboBox->addItem("RGB"); + d->_modeComboBox->addItem("HSV"); + d->_modeComboBox->setCurrentIndex(0); + connect(d->_modeComboBox, QOverload::of(&ElaComboBox::currentIndexChanged), d, &ElaColorDialogPrivate::onColorModeChanged); + + // R或H + d->_firstEdit = new ElaLineEdit(this); + d->_firstEdit->setText("0"); + d->_firstEdit->setFixedSize(120, 33); + d->_firstEdit->setValidator(new ElaIntValidator(0, 255, this)); + d->_firstText = new ElaText("红色", this); + d->_firstText->setTextPixelSize(13); + connect(d->_firstEdit, &ElaLineEdit::textEdited, d, &ElaColorDialogPrivate::onColorEditChanged); + + QHBoxLayout* firstEditLayout = new QHBoxLayout(); + firstEditLayout->setContentsMargins(0, 0, 0, 0); + firstEditLayout->addSpacing(6); + firstEditLayout->addWidget(d->_firstEdit); + firstEditLayout->addWidget(d->_firstText); + firstEditLayout->addStretch(); + + // G或S + d->_secondEdit = new ElaLineEdit(this); + d->_secondEdit->setText("0"); + d->_secondEdit->setFixedSize(120, 33); + d->_secondEdit->setValidator(new ElaIntValidator(0, 255, this)); + d->_secondText = new ElaText("绿色", this); + d->_secondText->setTextPixelSize(13); + connect(d->_secondEdit, &ElaLineEdit::textEdited, d, &ElaColorDialogPrivate::onColorEditChanged); + QHBoxLayout* secondEditLayout = new QHBoxLayout(); + secondEditLayout->setContentsMargins(0, 0, 0, 0); + secondEditLayout->addSpacing(6); + secondEditLayout->addWidget(d->_secondEdit); + secondEditLayout->addWidget(d->_secondText); + secondEditLayout->addStretch(); + + // B或V + d->_thridEdit = new ElaLineEdit(this); + d->_thridEdit->setText("0"); + d->_thridEdit->setFixedSize(120, 33); + d->_thridEdit->setValidator(new ElaIntValidator(0, 255, this)); + d->_thridText = new ElaText("蓝色", this); + d->_thridText->setTextPixelSize(13); + connect(d->_thridEdit, &ElaLineEdit::textEdited, d, &ElaColorDialogPrivate::onColorEditChanged); + QHBoxLayout* thridEditLayout = new QHBoxLayout(); + thridEditLayout->setContentsMargins(0, 0, 0, 0); + thridEditLayout->addSpacing(6); + thridEditLayout->addWidget(d->_thridEdit); + thridEditLayout->addWidget(d->_thridText); + thridEditLayout->addStretch(); + + QVBoxLayout* paramControlLayout = new QVBoxLayout(); + paramControlLayout->setContentsMargins(0, 0, 0, 0); + paramControlLayout->setSpacing(10); + paramControlLayout->addLayout(htmlEditLayout); + paramControlLayout->addWidget(d->_modeComboBox); + paramControlLayout->addLayout(firstEditLayout); + paramControlLayout->addLayout(secondEditLayout); + paramControlLayout->addLayout(thridEditLayout); + paramControlLayout->addStretch(); + + QHBoxLayout* colorControlLayout = new QHBoxLayout(); + colorControlLayout->setSpacing(0); + colorControlLayout->setContentsMargins(0, 0, 0, 0); + colorControlLayout->addLayout(colorPickerLayout); + colorControlLayout->addSpacing(5); + colorControlLayout->addLayout(colorPreviewLayout); + colorControlLayout->addSpacing(20); + colorControlLayout->addLayout(colorValueSliderLayout); + colorControlLayout->addSpacing(10); + colorControlLayout->addLayout(paramControlLayout); + colorControlLayout->addStretch(); + + // 基本颜色 + d->_basicColorView = new ElaBaseListView(this); + d->_basicColorView->setFixedSize(380, 170); + d->_basicColorView->setSelectionMode(QListView::NoSelection); + d->_basicColorView->setFlow(QListView::LeftToRight); + d->_basicColorView->setViewMode(QListView::IconMode); + d->_basicColorView->setResizeMode(QListView::Adjust); + d->_basicColorModel = new ElaColorDisplayModel(this); + d->_basicColorView->setModel(d->_basicColorModel); + d->_basicColorDelegate = new ElaColorDisplayDelegate(this); + d->_basicColorView->setItemDelegate(d->_basicColorDelegate); + d->_initBasicColor(); + connect(d->_basicColorView, &ElaBaseListView::clicked, d, &ElaColorDialogPrivate::onBasicColorViewClicked); + + ElaText* basicColorViewText = new ElaText("基本颜色", this); + basicColorViewText->setTextPixelSize(14); + QVBoxLayout* basicColorLayout = new QVBoxLayout(); + basicColorLayout->setContentsMargins(3, 0, 0, 0); + basicColorLayout->addWidget(basicColorViewText); + basicColorLayout->addSpacing(15); + basicColorLayout->addWidget(d->_basicColorView); + basicColorLayout->addStretch(); + + // 自定义颜色 + d->_customColorView = new ElaBaseListView(this); + d->_customColorView->setFixedSize(200, 170); + d->_customColorView->setSelectionMode(QListView::SingleSelection); + d->_customColorView->setFlow(QListView::LeftToRight); + d->_customColorView->setViewMode(QListView::IconMode); + d->_customColorView->setResizeMode(QListView::Adjust); + d->_customColorModel = new ElaColorDisplayModel(this); + d->_customColorView->setModel(d->_customColorModel); + d->_customColorDelegate = new ElaColorDisplayDelegate(this); + d->_customColorView->setItemDelegate(d->_customColorDelegate); + d->_initCustomColor(); + connect(d->_customColorView, &ElaBaseListView::clicked, d, &ElaColorDialogPrivate::onCustomColorViewClicked); + + // 增加移除按钮 + d->_addCustomColorButton = new ElaToolButton(this); + d->_addCustomColorButton->setIsTransparent(false); + d->_addCustomColorButton->setElaIcon(ElaIconType::Plus); + connect(d->_addCustomColorButton, &ElaToolButton::clicked, d, &ElaColorDialogPrivate::onAddCustomColorButtonClicked); + + d->_removeCustomColorButton = new ElaToolButton(this); + d->_removeCustomColorButton->setIsTransparent(false); + d->_removeCustomColorButton->setElaIcon(ElaIconType::Minus); + connect(d->_removeCustomColorButton, &ElaToolButton::clicked, d, &ElaColorDialogPrivate::onRemoveCustomColorButtonClicked); + + ElaText* customColorViewText = new ElaText("自定义颜色", this); + customColorViewText->setTextPixelSize(14); + QHBoxLayout* customButtonLayout = new QHBoxLayout(); + customButtonLayout->setContentsMargins(0, 0, 0, 0); + customButtonLayout->addWidget(customColorViewText); + customButtonLayout->addSpacing(35); + customButtonLayout->addWidget(d->_addCustomColorButton); + customButtonLayout->addWidget(d->_removeCustomColorButton); + customButtonLayout->addStretch(); + + QVBoxLayout* customColorLayout = new QVBoxLayout(); + customColorLayout->setContentsMargins(3, 0, 0, 0); + customColorLayout->addLayout(customButtonLayout); + customColorLayout->addSpacing(5); + customColorLayout->addWidget(d->_customColorView); + customColorLayout->addStretch(); + + QHBoxLayout* colorDisplayLayout = new QHBoxLayout(); + colorDisplayLayout->setContentsMargins(0, 0, 0, 0); + colorDisplayLayout->addLayout(basicColorLayout); + colorDisplayLayout->addLayout(customColorLayout); + colorDisplayLayout->addStretch(); + + // 确定取消按钮 + d->_overButton = new ElaPushButton("确定", this); + d->_overButton->setBorderRadius(6); + d->_overButton->setLightDefaultColor(ElaThemeColor(ElaThemeType::Light, PrimaryNormal)); + d->_overButton->setLightHoverColor(ElaThemeColor(ElaThemeType::Light, PrimaryHover)); + d->_overButton->setLightPressColor(ElaThemeColor(ElaThemeType::Light, PrimaryPress)); + d->_overButton->setLightTextColor(Qt::white); + d->_overButton->setDarkDefaultColor(ElaThemeColor(ElaThemeType::Dark, PrimaryNormal)); + d->_overButton->setDarkHoverColor(ElaThemeColor(ElaThemeType::Dark, PrimaryHover)); + d->_overButton->setDarkPressColor(ElaThemeColor(ElaThemeType::Dark, PrimaryPress)); + d->_overButton->setDarkTextColor(Qt::white); + connect(d->_overButton, &ElaPushButton::clicked, this, [=]() { + Q_EMIT colorSelected(d->_pCurrentColor); + close(); + }); + d->_cancelButton = new ElaPushButton("取消", this); + d->_cancelButton->setBorderRadius(6); + connect(d->_cancelButton, &ElaPushButton::clicked, this, [=]() { + close(); + }); + QHBoxLayout* buttonLayout = new QHBoxLayout(); + buttonLayout->setContentsMargins(0, 0, 0, 0); + buttonLayout->addWidget(d->_overButton); + buttonLayout->addWidget(d->_cancelButton); + + QVBoxLayout* mainLayout = new QVBoxLayout(this); + mainLayout->setContentsMargins(20, 10, 11, 20); + mainLayout->addLayout(colorPickerTextLayout); + mainLayout->addSpacing(3); + mainLayout->addLayout(colorControlLayout); + mainLayout->addSpacing(120); + mainLayout->addLayout(colorDisplayLayout); + mainLayout->addLayout(buttonLayout); + mainLayout->addStretch(); + + d->_themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { + d->_themeMode = themeMode; + }); + setCurrentColor(QColor(0x80, 0xFF, 0xEF)); +} + +ElaColorDialog::~ElaColorDialog() +{ +} + +void ElaColorDialog::setCurrentColor(QColor currentColor) +{ + Q_D(ElaColorDialog); + d->_pCurrentColor = currentColor; + d->_updateHtmlEditValue(); + d->_updateEditValue(); + d->_updateColorPreview(); + d->_updateColorValueSlider(); + d->_colorPicker->setSelectedColor(d->_pCurrentColor); + Q_EMIT pCurrentColorChanged(); +} + +QColor ElaColorDialog::getCurrentColor() const +{ + Q_D(const ElaColorDialog); + return d->_pCurrentColor; +} + +QList ElaColorDialog::getCustomColorList() const +{ + Q_D(const ElaColorDialog); + return d->_customColorModel->getDisplayColorList(); +} + +QColor ElaColorDialog::getCustomColor(int index) const +{ + Q_D(const ElaColorDialog); + return d->_customColorModel->getDisplayColor(index); +} + +QString ElaColorDialog::getCurrentColorRGB() const +{ + Q_D(const ElaColorDialog); + return d->_getHexRgbValue(); +} + +void ElaColorDialog::paintEvent(QPaintEvent* event) +{ + Q_D(ElaColorDialog); + QPainter painter(this); + painter.save(); + painter.setRenderHint(QPainter::Antialiasing); + painter.setPen(Qt::NoPen); + // 背景绘制 + painter.setBrush(ElaThemeColor(d->_themeMode, DialogBase)); + painter.drawRect(rect()); + + // 按钮背景绘制 + painter.setBrush(ElaThemeColor(d->_themeMode, DialogLayoutArea)); + painter.drawRect(QRect(0, height() - 78, width(), 78)); + painter.restore(); + QDialog::paintEvent(event); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaComboBox.cpp b/3rdparty/ElaWidgetTools/src/ElaComboBox.cpp new file mode 100644 index 0000000..31ab722 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaComboBox.cpp @@ -0,0 +1,191 @@ +#include "ElaComboBox.h" + +#include +#include +#include +#include +#include +#include + +#include "ElaComboBoxStyle.h" +#include "ElaScrollBar.h" +#include "ElaTheme.h" +#include "private/ElaComboBoxPrivate.h" +Q_PROPERTY_CREATE_Q_CPP(ElaComboBox, int, BorderRadius) +ElaComboBox::ElaComboBox(QWidget* parent) + : QComboBox(parent), d_ptr(new ElaComboBoxPrivate()) +{ + Q_D(ElaComboBox); + d->q_ptr = this; + d->_pBorderRadius = 3; + d->_themeMode = eTheme->getThemeMode(); + setObjectName("ElaComboBox"); + setFixedHeight(35); + d->_comboBoxStyle = new ElaComboBoxStyle(style()); + setStyle(d->_comboBoxStyle); + + //调用view 让container初始化 + setView(new QListView(this)); + QAbstractItemView* comboBoxView = this->view(); + comboBoxView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + ElaScrollBar* scrollBar = new ElaScrollBar(this); + comboBoxView->setVerticalScrollBar(scrollBar); + ElaScrollBar* floatVScrollBar = new ElaScrollBar(scrollBar, comboBoxView); + floatVScrollBar->setIsAnimation(true); + comboBoxView->setAutoScroll(false); + comboBoxView->setSelectionMode(QAbstractItemView::NoSelection); + comboBoxView->setObjectName("ElaComboBoxView"); + comboBoxView->setStyleSheet("#ElaComboBoxView{background-color:transparent;}"); + comboBoxView->setStyle(d->_comboBoxStyle); + QWidget* container = this->findChild(); + if (container) + { + container->setWindowFlags(Qt::Popup | Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint); + container->setAttribute(Qt::WA_TranslucentBackground); + container->setObjectName("ElaComboBoxContainer"); + container->setStyle(d->_comboBoxStyle); + QLayout* layout = container->layout(); + while (layout->count()) + { + layout->takeAt(0); + } + layout->addWidget(view()); + layout->setContentsMargins(6, 0, 6, 6); +#ifndef Q_OS_WIN + container->setStyleSheet("background-color:transparent;"); +#endif + } + QComboBox::setMaxVisibleItems(5); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { d->_themeMode = themeMode; }); +} + +ElaComboBox::~ElaComboBox() +{ +} + +void ElaComboBox::showPopup() +{ + Q_D(ElaComboBox); + bool oldAnimationEffects = qApp->isEffectEnabled(Qt::UI_AnimateCombo); + qApp->setEffectEnabled(Qt::UI_AnimateCombo, false); + QComboBox::showPopup(); + qApp->setEffectEnabled(Qt::UI_AnimateCombo, oldAnimationEffects); + if (count() > 0) + { + QWidget* container = this->findChild(); + if (container) + { + int containerHeight = 0; + if (count() >= maxVisibleItems()) + { + containerHeight = maxVisibleItems() * 35 + 8; + } + else + { + containerHeight = count() * 35 + 8; + } + view()->resize(view()->width(), containerHeight - 8); + container->move(container->x(), container->y() + 3); + QLayout* layout = container->layout(); + while (layout->count()) + { + layout->takeAt(0); + } + QPropertyAnimation* fixedSizeAnimation = new QPropertyAnimation(container, "maximumHeight"); + connect(fixedSizeAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + container->setFixedHeight(value.toUInt()); + }); + fixedSizeAnimation->setStartValue(1); + fixedSizeAnimation->setEndValue(containerHeight); + fixedSizeAnimation->setEasingCurve(QEasingCurve::OutCubic); + fixedSizeAnimation->setDuration(400); + fixedSizeAnimation->start(QAbstractAnimation::DeleteWhenStopped); + + QPropertyAnimation* viewPosAnimation = new QPropertyAnimation(view(), "pos"); + connect(viewPosAnimation, &QPropertyAnimation::finished, this, [=]() { + d->_isAllowHidePopup = true; + layout->addWidget(view()); + }); + QPoint viewPos = view()->pos(); + viewPosAnimation->setStartValue(QPoint(viewPos.x(), viewPos.y() - view()->height())); + viewPosAnimation->setEndValue(viewPos); + viewPosAnimation->setEasingCurve(QEasingCurve::OutCubic); + viewPosAnimation->setDuration(400); + viewPosAnimation->start(QAbstractAnimation::DeleteWhenStopped); + } + //指示器动画 + QPropertyAnimation* rotateAnimation = new QPropertyAnimation(d->_comboBoxStyle, "pExpandIconRotate"); + connect(rotateAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + update(); + }); + rotateAnimation->setDuration(300); + rotateAnimation->setEasingCurve(QEasingCurve::InOutSine); + rotateAnimation->setStartValue(d->_comboBoxStyle->getExpandIconRotate()); + rotateAnimation->setEndValue(-180); + rotateAnimation->start(QAbstractAnimation::DeleteWhenStopped); + QPropertyAnimation* markAnimation = new QPropertyAnimation(d->_comboBoxStyle, "pExpandMarkWidth"); + markAnimation->setDuration(300); + markAnimation->setEasingCurve(QEasingCurve::InOutSine); + markAnimation->setStartValue(d->_comboBoxStyle->getExpandMarkWidth()); + markAnimation->setEndValue(width() / 2 - d->_pBorderRadius - 6); + markAnimation->start(QAbstractAnimation::DeleteWhenStopped); + } +} + +void ElaComboBox::hidePopup() +{ + Q_D(ElaComboBox); + if (d->_isAllowHidePopup) + { + QWidget* container = this->findChild(); + int containerHeight = container->height(); + if (container) + { + QLayout* layout = container->layout(); + while (layout->count()) + { + layout->takeAt(0); + } + QPropertyAnimation* viewPosAnimation = new QPropertyAnimation(view(), "pos"); + connect(viewPosAnimation, &QPropertyAnimation::finished, this, [=]() { + layout->addWidget(view()); + QMouseEvent focusEvent(QEvent::MouseButtonPress, QPoint(-1, -1), QPoint(-1, -1), Qt::NoButton, Qt::NoButton, Qt::NoModifier); + QApplication::sendEvent(parentWidget(), &focusEvent); + QComboBox::hidePopup(); + container->setFixedHeight(containerHeight); + }); + QPoint viewPos = view()->pos(); + connect(viewPosAnimation, &QPropertyAnimation::finished, this, [=]() { view()->move(viewPos); }); + viewPosAnimation->setStartValue(viewPos); + viewPosAnimation->setEndValue(QPoint(viewPos.x(), viewPos.y() - view()->height())); + viewPosAnimation->setEasingCurve(QEasingCurve::InCubic); + viewPosAnimation->start(QAbstractAnimation::DeleteWhenStopped); + + QPropertyAnimation* fixedSizeAnimation = new QPropertyAnimation(container, "maximumHeight"); + connect(fixedSizeAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + container->setFixedHeight(value.toUInt()); + }); + fixedSizeAnimation->setStartValue(container->height()); + fixedSizeAnimation->setEndValue(1); + fixedSizeAnimation->setEasingCurve(QEasingCurve::InCubic); + fixedSizeAnimation->start(QAbstractAnimation::DeleteWhenStopped); + d->_isAllowHidePopup = false; + } + //指示器动画 + QPropertyAnimation* rotateAnimation = new QPropertyAnimation(d->_comboBoxStyle, "pExpandIconRotate"); + connect(rotateAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + update(); + }); + rotateAnimation->setDuration(300); + rotateAnimation->setEasingCurve(QEasingCurve::InOutSine); + rotateAnimation->setStartValue(d->_comboBoxStyle->getExpandIconRotate()); + rotateAnimation->setEndValue(0); + rotateAnimation->start(QAbstractAnimation::DeleteWhenStopped); + QPropertyAnimation* markAnimation = new QPropertyAnimation(d->_comboBoxStyle, "pExpandMarkWidth"); + markAnimation->setDuration(300); + markAnimation->setEasingCurve(QEasingCurve::InOutSine); + markAnimation->setStartValue(d->_comboBoxStyle->getExpandMarkWidth()); + markAnimation->setEndValue(0); + markAnimation->start(QAbstractAnimation::DeleteWhenStopped); + } +} diff --git a/3rdparty/ElaWidgetTools/src/ElaContentDialog.cpp b/3rdparty/ElaWidgetTools/src/ElaContentDialog.cpp new file mode 100644 index 0000000..1d574b0 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaContentDialog.cpp @@ -0,0 +1,278 @@ +#include "ElaContentDialog.h" + +#include + +#include "ElaMaskWidget.h" +#include "ElaText.h" +#include "ElaTheme.h" +#include "ElaWinShadowHelper.h" +#include "private/ElaContentDialogPrivate.h" +#include +#include +#include +#include +#include +#include +#include +#include + +ElaContentDialog::ElaContentDialog(QWidget* parent) + : QDialog{parent}, d_ptr(new ElaContentDialogPrivate()) +{ + Q_D(ElaContentDialog); + d->q_ptr = this; + + d->_maskWidget = new ElaMaskWidget(parent); + d->_maskWidget->move(0, 0); + d->_maskWidget->setFixedSize(parent->size()); + d->_maskWidget->setVisible(false); + + resize(400, height()); + setWindowModality(Qt::ApplicationModal); +#ifdef Q_OS_WIN + createWinId(); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 3) && QT_VERSION <= QT_VERSION_CHECK(6, 6, 1)) + window()->setWindowFlags((window()->windowFlags()) | Qt::WindowMinimizeButtonHint | Qt::FramelessWindowHint); +#endif +#else + window()->setWindowFlags((window()->windowFlags()) | Qt::FramelessWindowHint); +#endif + d->_leftButton = new ElaPushButton("cancel", this); + connect(d->_leftButton, &ElaPushButton::clicked, this, [=]() { + Q_EMIT leftButtonClicked(); + onLeftButtonClicked(); + d->_maskWidget->doMaskAnimation(0); + d->_doCloseAnimation(); + }); + d->_leftButton->setMinimumSize(0, 0); + d->_leftButton->setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)); + d->_leftButton->setFixedHeight(38); + d->_leftButton->setBorderRadius(6); + d->_middleButton = new ElaPushButton("minimum", this); + connect(d->_middleButton, &ElaPushButton::clicked, this, [=]() { + Q_EMIT middleButtonClicked(); + onMiddleButtonClicked(); + }); + d->_middleButton->setMinimumSize(0, 0); + d->_middleButton->setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)); + d->_middleButton->setFixedHeight(38); + d->_middleButton->setBorderRadius(6); + d->_rightButton = new ElaPushButton("exit", this); + connect(d->_rightButton, &ElaPushButton::clicked, this, [=]() { + Q_EMIT rightButtonClicked(); + onRightButtonClicked(); + d->_doCloseAnimation(); + }); + d->_rightButton->setLightDefaultColor(ElaThemeColor(ElaThemeType::Light, PrimaryNormal)); + d->_rightButton->setLightHoverColor(ElaThemeColor(ElaThemeType::Light, PrimaryHover)); + d->_rightButton->setLightPressColor(ElaThemeColor(ElaThemeType::Light, PrimaryPress)); + d->_rightButton->setLightTextColor(Qt::white); + d->_rightButton->setDarkDefaultColor(ElaThemeColor(ElaThemeType::Dark, PrimaryNormal)); + d->_rightButton->setDarkHoverColor(ElaThemeColor(ElaThemeType::Dark, PrimaryHover)); + d->_rightButton->setDarkPressColor(ElaThemeColor(ElaThemeType::Dark, PrimaryPress)); + d->_rightButton->setDarkTextColor(Qt::black); + d->_rightButton->setMinimumSize(0, 0); + d->_rightButton->setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)); + d->_rightButton->setFixedHeight(38); + d->_rightButton->setBorderRadius(6); + + d->_centralWidget = new QWidget(this); + QVBoxLayout* centralVLayout = new QVBoxLayout(d->_centralWidget); + centralVLayout->setContentsMargins(15, 25, 15, 10); + ElaText* title = new ElaText("退出", this); + title->setTextStyle(ElaTextType::Title); + ElaText* subTitle = new ElaText("确定要退出程序吗", this); + subTitle->setTextStyle(ElaTextType::Body); + centralVLayout->addWidget(title); + centralVLayout->addSpacing(2); + centralVLayout->addWidget(subTitle); + centralVLayout->addStretch(); + + d->_mainLayout = new QVBoxLayout(this); + d->_mainLayout->setContentsMargins(0, 0, 0, 0); + d->_buttonWidget = new QWidget(this); + d->_buttonWidget->setFixedHeight(60); + QHBoxLayout* buttonLayout = new QHBoxLayout(d->_buttonWidget); + buttonLayout->addWidget(d->_leftButton); + buttonLayout->addWidget(d->_middleButton); + buttonLayout->addWidget(d->_rightButton); + d->_mainLayout->addWidget(d->_centralWidget); + d->_mainLayout->addWidget(d->_buttonWidget); + + d->_themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { + d->_themeMode = themeMode; + }); +} + +ElaContentDialog::~ElaContentDialog() +{ + Q_D(ElaContentDialog); + d->_maskWidget->deleteLater(); +} + +void ElaContentDialog::onLeftButtonClicked() +{ +} + +void ElaContentDialog::onMiddleButtonClicked() +{ +} + +void ElaContentDialog::onRightButtonClicked() +{ +} + +void ElaContentDialog::setCentralWidget(QWidget* centralWidget) +{ + Q_D(ElaContentDialog); + d->_mainLayout->takeAt(0); + d->_mainLayout->takeAt(0); + delete d->_centralWidget; + d->_mainLayout->addWidget(centralWidget); + d->_mainLayout->addWidget(d->_buttonWidget); +} + +void ElaContentDialog::setLeftButtonText(QString text) +{ + Q_D(ElaContentDialog); + d->_leftButton->setText(text); +} + +void ElaContentDialog::setMiddleButtonText(QString text) +{ + Q_D(ElaContentDialog); + d->_middleButton->setText(text); +} + +void ElaContentDialog::setRightButtonText(QString text) +{ + Q_D(ElaContentDialog); + d->_rightButton->setText(text); +} + +void ElaContentDialog::close() +{ + Q_D(ElaContentDialog); + d->_doCloseAnimation(); +} + +void ElaContentDialog::showEvent(QShowEvent* event) +{ + Q_D(ElaContentDialog); + d->_maskWidget->setVisible(true); + d->_maskWidget->raise(); + d->_maskWidget->setFixedSize(parentWidget()->size()); + d->_maskWidget->doMaskAnimation(90); +#ifdef Q_OS_WIN +#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 3) && QT_VERSION <= QT_VERSION_CHECK(6, 6, 1)) + HWND hwnd = (HWND)d->_currentWinID; + setShadow(hwnd); + DWORD style = ::GetWindowLongPtr(hwnd, GWL_STYLE); + bool hasCaption = (style & WS_CAPTION) == WS_CAPTION; + if (!hasCaption) + { + ::SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_CAPTION); + } +#endif +#endif + QDialog::showEvent(event); +} + +void ElaContentDialog::paintEvent(QPaintEvent* event) +{ + Q_D(ElaContentDialog); + QPainter painter(this); + painter.save(); + painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + painter.setPen(Qt::NoPen); + painter.setBrush(ElaThemeColor(d->_themeMode, DialogBase)); + // 背景绘制 + painter.drawRect(rect()); + // 按钮栏背景绘制 + painter.setBrush(ElaThemeColor(d->_themeMode, DialogLayoutArea)); + painter.drawRoundedRect(QRectF(0, height() - 60, width(), 60), 8, 8); + painter.restore(); +} + +void ElaContentDialog::keyPressEvent(QKeyEvent* event) +{ + event->accept(); +} + +#ifdef Q_OS_WIN +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +bool ElaContentDialog::nativeEvent(const QByteArray& eventType, void* message, qintptr* result) +#else +bool ElaContentDialog::nativeEvent(const QByteArray& eventType, void* message, long* result) +#endif +{ + Q_D(ElaContentDialog); + if ((eventType != "windows_generic_MSG") || !message) + { + return false; + } + const auto msg = static_cast(message); + const HWND hwnd = msg->hwnd; + if (!hwnd || !msg) + { + return false; + } + d->_currentWinID = (qint64)hwnd; + const UINT uMsg = msg->message; + const WPARAM wParam = msg->wParam; + const LPARAM lParam = msg->lParam; + switch (uMsg) + { + case WM_WINDOWPOSCHANGING: + { + WINDOWPOS* wp = reinterpret_cast(lParam); + if (wp != nullptr && (wp->flags & SWP_NOSIZE) == 0) + { + wp->flags |= SWP_NOCOPYBITS; + *result = ::DefWindowProcW(hwnd, uMsg, wParam, lParam); + return true; + } + return false; + } + case WM_NCACTIVATE: + { + *result = TRUE; + return true; + } + case WM_NCCALCSIZE: + { +#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 3) && QT_VERSION <= QT_VERSION_CHECK(6, 6, 1)) + if (wParam == FALSE) + { + return false; + } + if (::IsZoomed(hwnd)) + { + setContentsMargins(8, 8, 8, 8); + } + else + { + setContentsMargins(0, 0, 0, 0); + } + *result = 0; + return true; +#else + if (wParam == FALSE) + { + return false; + } + RECT* clientRect = &((NCCALCSIZE_PARAMS*)(lParam))->rgrc[0]; + if (!::IsZoomed(hwnd)) + { + clientRect->top -= 1; + clientRect->bottom -= 1; + } + *result = WVR_REDRAW; + return true; +#endif + } + } + return QDialog::nativeEvent(eventType, message, result); +} +#endif diff --git a/3rdparty/ElaWidgetTools/src/ElaDockWidget.cpp b/3rdparty/ElaWidgetTools/src/ElaDockWidget.cpp new file mode 100644 index 0000000..8d1701a --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaDockWidget.cpp @@ -0,0 +1,214 @@ +#include "ElaDockWidget.h" + +#include +#include +#include +#include +#include +#include + +#include "ElaApplication.h" +#include "ElaDockWidgetPrivate.h" +#include "ElaDockWidgetTitleBar.h" +#include "ElaTheme.h" +#include "ElaWinShadowHelper.h" + +ElaDockWidget::ElaDockWidget(QWidget* parent, Qt::WindowFlags flags) + : QDockWidget(parent, flags), d_ptr(new ElaDockWidgetPrivate()) +{ + Q_D(ElaDockWidget); + d->q_ptr = this; + setObjectName("ElaDockWidget"); + + d->_titleBar = new ElaDockWidgetTitleBar(this); + setTitleBarWidget(d->_titleBar); + + // 主题变更动画 + d->_themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, d, &ElaDockWidgetPrivate::onThemeModeChanged); + + d->_isEnableMica = eApp->getIsEnableMica(); + connect(eApp, &ElaApplication::pIsEnableMicaChanged, this, [=]() { + d->_isEnableMica = eApp->getIsEnableMica(); + update(); + }); + connect(this, &ElaDockWidget::topLevelChanged, this, [=](bool topLevel) { + eApp->syncMica(this, topLevel); + }); +} + +ElaDockWidget::ElaDockWidget(const QString& title, QWidget* parent, Qt::WindowFlags flags) + : ElaDockWidget(parent, flags) +{ + this->setWindowTitle(title); +} + +ElaDockWidget::~ElaDockWidget() +{ +} + +void ElaDockWidget::paintEvent(QPaintEvent* event) +{ + Q_D(ElaDockWidget); + if (isFloating()) + { + QPainter painter(this); + painter.save(); + painter.setRenderHints(QPainter::Antialiasing); +#ifdef Q_OS_WIN + // 背景 + if (!d->_isEnableMica) + { + painter.setPen(Qt::NoPen); + painter.setBrush(ElaThemeColor(d->_themeMode, DialogBase)); + painter.drawRect(rect()); + } +#else + // 背景 + painter.setPen(Qt::NoPen); + painter.setBrush(d->_isEnableMica ? Qt::transparent : ElaThemeColor(d->_themeMode, DialogBase)); + QRect foregroundRect(d->_shadowBorderWidth, d->_shadowBorderWidth, width() - 2 * d->_shadowBorderWidth, height() - 2 * d->_shadowBorderWidth); + painter.drawRect(rect()); +#endif + painter.restore(); + } + QDockWidget::paintEvent(event); +} + +#ifdef Q_OS_WIN +bool ElaDockWidget::event(QEvent* event) +{ + Q_D(ElaDockWidget); + switch (event->type()) + { + case QEvent::Show: + case QEvent::MouseButtonRelease: + { + HWND hwnd = (HWND)d->_currentWinID; + DWORD style = ::GetWindowLongPtr(hwnd, GWL_STYLE); + ::SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_MAXIMIZEBOX | WS_THICKFRAME); + setShadow(hwnd); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 3) && QT_VERSION <= QT_VERSION_CHECK(6, 6, 1)) + bool hasCaption = (style & WS_CAPTION) == WS_CAPTION; + if (!hasCaption) + { + ::SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_CAPTION); + } +#endif + break; + } + default: + { + break; + } + } + return QDockWidget::event(event); +} + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +bool ElaDockWidget::nativeEvent(const QByteArray& eventType, void* message, qintptr* result) +#else +bool ElaDockWidget::nativeEvent(const QByteArray& eventType, void* message, long* result) +#endif +{ + Q_D(ElaDockWidget); + if ((eventType != "windows_generic_MSG") || !message) + { + return false; + } + const auto msg = static_cast(message); + const HWND hwnd = msg->hwnd; + if (!hwnd || !msg) + { + return false; + } + d->_currentWinID = (qint64)hwnd; + const UINT uMsg = msg->message; + const WPARAM wParam = msg->wParam; + const LPARAM lParam = msg->lParam; + switch (uMsg) + { + case WM_WINDOWPOSCHANGING: + { + WINDOWPOS* wp = reinterpret_cast(lParam); + if (wp != nullptr && (wp->flags & SWP_NOSIZE) == 0) + { + wp->flags |= SWP_NOCOPYBITS; + *result = ::DefWindowProcW(hwnd, uMsg, wParam, lParam); + return true; + } + return false; + } + case WM_NCACTIVATE: + { + *result = TRUE; + return true; + } + case WM_NCHITTEST: + { + POINT nativeLocalPos{GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; + ::ScreenToClient(hwnd, &nativeLocalPos); + RECT clientRect{0, 0, 0, 0}; + ::GetClientRect(hwnd, &clientRect); + auto clientWidth = clientRect.right - clientRect.left; + auto clientHeight = clientRect.bottom - clientRect.top; + bool left = nativeLocalPos.x < d->_margins; + bool right = nativeLocalPos.x > clientWidth - d->_margins; + bool top = nativeLocalPos.y < d->_margins; + bool bottom = nativeLocalPos.y > clientHeight - d->_margins; + *result = 0; + if (!window()->isFullScreen() && !window()->isMaximized()) + { + if (left && top) + { + *result = HTTOPLEFT; + } + else if (left && bottom) + { + *result = HTBOTTOMLEFT; + } + else if (right && top) + { + *result = HTTOPRIGHT; + } + else if (right && bottom) + { + *result = HTBOTTOMRIGHT; + } + else if (left) + { + *result = HTLEFT; + } + else if (right) + { + *result = HTRIGHT; + } + else if (top) + { + *result = HTTOP; + } + else if (bottom) + { + *result = HTBOTTOM; + } + } + if (0 != *result) + { + return true; + } + *result = HTCLIENT; + return true; + } + case WM_NCCALCSIZE: + { + if (wParam == FALSE) + { + return false; + } + *result = 0; + return true; + } + } + return QDockWidget::nativeEvent(eventType, message, result); +} +#endif diff --git a/3rdparty/ElaWidgetTools/src/ElaDoubleSpinBox.cpp b/3rdparty/ElaWidgetTools/src/ElaDoubleSpinBox.cpp new file mode 100644 index 0000000..95fb10a --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaDoubleSpinBox.cpp @@ -0,0 +1,65 @@ +#include "ElaDoubleSpinBox.h" + +#include +#include + +#include "DeveloperComponents/ElaSpinBoxStyle.h" +#include "ElaDoubleSpinBoxPrivate.h" +#include "ElaMenu.h" +#include "ElaTheme.h" +ElaDoubleSpinBox::ElaDoubleSpinBox(QWidget* parent) + : QDoubleSpinBox(parent), d_ptr(new ElaDoubleSpinBoxPrivate()) +{ + Q_D(ElaDoubleSpinBox); + d->q_ptr = this; + setFixedSize(120, 30); + setStyle(new ElaSpinBoxStyle(style())); + lineEdit()->setAlignment(Qt::AlignCenter); + lineEdit()->setStyleSheet("background-color:transparent"); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { + QPalette palette; + palette.setColor(QPalette::Base, Qt::transparent); + palette.setColor(QPalette::Text, ElaThemeColor(themeMode, BasicText)); + lineEdit()->setPalette(palette); + }); +} + +ElaDoubleSpinBox::~ElaDoubleSpinBox() +{ +} + +void ElaDoubleSpinBox::contextMenuEvent(QContextMenuEvent* event) +{ + Q_D(ElaDoubleSpinBox); + ElaMenu* menu = d->_createStandardContextMenu(); + if (!menu) + { + return; + } + menu->addSeparator(); + const uint se = stepEnabled(); + QAction* up = menu->addElaIconAction(ElaIconType::Plus, tr("增加")); + up->setEnabled(se & StepUpEnabled); + QAction* down = menu->addElaIconAction(ElaIconType::Minus, tr("减少")); + down->setEnabled(se & StepDownEnabled); + menu->addSeparator(); + + const QAbstractSpinBox* that = this; + const QPoint pos = (event->reason() == QContextMenuEvent::Mouse) + ? event->globalPos() + : mapToGlobal(QPoint(event->pos().x(), 0)) + QPoint(width() / 2, height() / 2); + const QAction* action = menu->exec(pos); + delete menu; + if (that && action) + { + if (action == up) + { + stepBy(1); + } + else if (action == down) + { + stepBy(-1); + } + } + event->accept(); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaDxgiManager.cpp b/3rdparty/ElaWidgetTools/src/ElaDxgiManager.cpp new file mode 100644 index 0000000..6bb61ec --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaDxgiManager.cpp @@ -0,0 +1,292 @@ +#include "ElaDxgiManager.h" +#ifdef Q_OS_WIN +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "ElaDxgi.h" +#include "ElaDxgiManagerPrivate.h" +Q_SINGLETON_CREATE_CPP(ElaDxgiManager); +ElaDxgiManager::ElaDxgiManager(QObject* parent) + : QObject{parent}, d_ptr(new ElaDxgiManagerPrivate()) +{ + Q_D(ElaDxgiManager); + d->q_ptr = this; + d->_dxgiThread = new QThread(this); + d->_dxgi = new ElaDxgi(); + setGrabArea(0, 0, GetSystemMetrics(SM_CXVIRTUALSCREEN), GetSystemMetrics(SM_CYVIRTUALSCREEN)); + bool ret = d->_dxgi->initialize(0, 0); + if (!ret) + { + for (int i = 1; i < d->_dxgi->getDxDeviceList().count(); i++) + { + bool ret = d->_dxgi->initialize(i, 0); + if (ret) + { + break; + } + } + } + if (!ret) + { + d->_dxgi->initialize(0, 0); + qCritical() << "No available screenshot devices"; + } + d->_dxgi->moveToThread(d->_dxgiThread); + d->_dxgiThread->start(); + connect(d, &ElaDxgiManagerPrivate::grabScreen, d->_dxgi, &ElaDxgi::onGrabScreen); + connect(d->_dxgi, &ElaDxgi::grabScreenOver, this, &ElaDxgiManager::grabImageUpdate); +} + +ElaDxgiManager::~ElaDxgiManager() +{ + Q_D(ElaDxgiManager); + if (d->_dxgi) + { + d->_dxgi->setIsGrabActive(false); + } + if (d->_dxgiThread->isRunning()) + { + d->_dxgiThread->quit(); + d->_dxgiThread->wait(); + } + delete d->_dxgi; +} + +QStringList ElaDxgiManager::getDxDeviceList() const +{ + Q_D(const ElaDxgiManager); + return d->_dxgi->getDxDeviceList(); +} + +QStringList ElaDxgiManager::getOutputDeviceList() const +{ + Q_D(const ElaDxgiManager); + return d->_dxgi->getOutputDeviceList(); +} + +QImage ElaDxgiManager::grabScreenToImage() const +{ + Q_D(const ElaDxgiManager); + if (!d->_dxgi->getIsInitSuccess()) + { + return QImage(); + } + return d->_dxgi->getGrabImage(); +} + +void ElaDxgiManager::startGrabScreen() +{ + Q_D(ElaDxgiManager); + d->_isAllowedGrabScreen = true; + if (!d->_dxgi->getIsGrabActive()) + { + d->_dxgi->setIsGrabActive(true); + Q_EMIT d->grabScreen(); + } +} + +void ElaDxgiManager::stopGrabScreen() +{ + Q_D(ElaDxgiManager); + d->_isAllowedGrabScreen = false; + d->_dxgi->setIsGrabActive(false); +} + +bool ElaDxgiManager::getIsGrabScreen() const +{ + Q_D(const ElaDxgiManager); + return d->_dxgi->getIsGrabActive(); +} + +bool ElaDxgiManager::setDxDeviceID(int dxID) +{ + Q_D(ElaDxgiManager); + if (dxID < 0 || d->_dxgi->getDxDeviceList().count() <= dxID) + { + return false; + } + d->_dxgi->setIsGrabActive(false); + while (!d->_dxgi->getIsGrabStoped()) + { + //等待任务结束 + QApplication::processEvents(); + } + if (d->_dxgi->initialize(dxID, d->_dxgi->getOutputDeviceID())) + { + if (d->_isAllowedGrabScreen) + { + d->_dxgi->setIsGrabActive(true); + Q_EMIT d->grabScreen(); + } + return true; + } + return false; +} + +int ElaDxgiManager::getDxDeviceID() const +{ + Q_D(const ElaDxgiManager); + return d->_dxgi->getDxDeviceID(); +} + +bool ElaDxgiManager::setOutputDeviceID(int deviceID) +{ + Q_D(ElaDxgiManager); + if (deviceID < 0 || d->_dxgi->getOutputDeviceList().count() <= deviceID) + { + return false; + } + + d->_dxgi->setIsGrabActive(false); + while (!d->_dxgi->getIsGrabStoped()) + { + //等待任务结束 + QApplication::processEvents(); + } + if (d->_dxgi->initialize(d->_dxgi->getDxDeviceID(), deviceID)) + { + if (d->_isAllowedGrabScreen) + { + d->_dxgi->setIsGrabActive(true); + Q_EMIT d->grabScreen(); + } + return true; + } + return false; +} + +int ElaDxgiManager::getOutputDeviceID() const +{ + Q_D(const ElaDxgiManager); + return d->_dxgi->getOutputDeviceID(); +} + +void ElaDxgiManager::setGrabArea(int width, int height) +{ + Q_D(ElaDxgiManager); + int maxWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN); + int maxHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN); + if (width <= 0 || width > maxWidth) + { + width = maxWidth; + } + if (height <= 0 || height > maxHeight) + { + height = maxHeight; + } + d->_dxgi->setIsGrabCenter(true); + d->_dxgi->setGrabArea(QRect(0, 0, width, height)); +} + +void ElaDxgiManager::setGrabArea(int x, int y, int width, int height) +{ + Q_D(ElaDxgiManager); + int maxWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN); + int maxHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN); + if (width <= 0 || width > maxWidth) + { + width = maxWidth; + } + if (height <= 0 || height > maxHeight) + { + height = maxHeight; + } + d->_dxgi->setIsGrabCenter(false); + d->_dxgi->setGrabArea(QRect(x, y, width, height)); +} + +QRect ElaDxgiManager::getGrabArea() const +{ + Q_D(const ElaDxgiManager); + return d->_dxgi->getGrabArea(); +} + +void ElaDxgiManager::setGrabFrameRate(int frameRateValue) +{ + Q_D(ElaDxgiManager); + if (frameRateValue > 0) + { + d->_dxgi->setGrabFrameRate(frameRateValue); + } +} + +int ElaDxgiManager::getGrabFrameRate() const +{ + Q_D(const ElaDxgiManager); + return d->_dxgi->getGrabFrameRate(); +} + +void ElaDxgiManager::setTimeoutMsValue(int timeoutValue) +{ + Q_D(ElaDxgiManager); + if (timeoutValue > 0) + { + d->_dxgi->setTimeoutMsValue(timeoutValue); + } +} + +int ElaDxgiManager::getTimeoutMsValue() const +{ + Q_D(const ElaDxgiManager); + return d->_dxgi->getTimeoutMsValue(); +} + +Q_PROPERTY_CREATE_Q_CPP(ElaDxgiScreen, int, BorderRadius) +ElaDxgiScreen::ElaDxgiScreen(QWidget* parent) + : QWidget(parent), d_ptr(new ElaDxgiScreenPrivate()) +{ + Q_D(ElaDxgiScreen); + d->q_ptr = this; + d->_pBorderRadius = 5; + d->_dxgiManager = ElaDxgiManager::getInstance(); + setFixedSize(700, 500); + connect(d->_dxgiManager, &ElaDxgiManager::grabImageUpdate, this, [=] { + if (isVisible()) + { + update(); + } + }); +} + +ElaDxgiScreen::~ElaDxgiScreen() +{ +} + +void ElaDxgiScreen::paintEvent(QPaintEvent* event) +{ + Q_D(ElaDxgiScreen); + if (d->_dxgiManager->getIsGrabScreen()) + { + QPainter painter(this); + painter.save(); + painter.setRenderHints(QPainter::SmoothPixmapTransform | QPainter::Antialiasing); + QPainterPath path; + path.addRoundedRect(rect(), d->_pBorderRadius, d->_pBorderRadius); + painter.drawImage(rect(), d->_dxgiManager->grabScreenToImage()); + painter.restore(); + } +} + +void ElaDxgiScreen::setIsSyncGrabSize(bool isSyncGrabSize) +{ + Q_D(ElaDxgiScreen); + if (isSyncGrabSize) + { + setFixedSize(d->_dxgiManager->getGrabArea().size()); + } +} + +bool ElaDxgiScreen::getIsSyncGrabSize() const +{ + Q_D(const ElaDxgiScreen); + return d->_isSyncGrabSize; +} +#endif diff --git a/3rdparty/ElaWidgetTools/src/ElaEventBus.cpp b/3rdparty/ElaWidgetTools/src/ElaEventBus.cpp new file mode 100644 index 0000000..b806de0 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaEventBus.cpp @@ -0,0 +1,81 @@ +#include "ElaEventBus.h" + +#include + +#include "ElaEventBusPrivate.h" +Q_SINGLETON_CREATE_CPP(ElaEventBus); +Q_PROPERTY_CREATE_Q_CPP(ElaEvent, QString, EventName); +Q_PROPERTY_CREATE_Q_CPP(ElaEvent, QString, FunctionName); +Q_PROPERTY_CREATE_Q_CPP(ElaEvent, Qt::ConnectionType, ConnectionType); +ElaEvent::ElaEvent(QObject* parent) + : QObject{parent}, d_ptr(new ElaEventPrivate()) +{ + Q_D(ElaEvent); + d->q_ptr = this; + d->_pConnectionType = Qt::AutoConnection; + d->_pFunctionName = ""; + d->_pEventName = ""; +} + +ElaEvent::ElaEvent(QString eventName, QString functionName, QObject* parent) + : QObject{parent}, d_ptr(new ElaEventPrivate()) +{ + Q_D(ElaEvent); + d->q_ptr = this; + d->_pConnectionType = Qt::AutoConnection; + d->_pEventName = eventName; + d->_pFunctionName = functionName; +} + +ElaEventBusType::EventBusReturnType ElaEvent::registerAndInit() +{ + return ElaEventBus::getInstance()->d_ptr->registerEvent(this); +} + +ElaEvent::~ElaEvent() +{ + ElaEventBus::getInstance()->d_ptr->unRegisterEvent(this); +} + +ElaEventBus::ElaEventBus(QObject* parent) + : QObject{parent}, d_ptr(new ElaEventBusPrivate()) +{ + Q_D(ElaEventBus); + d->q_ptr = this; +} + +ElaEventBus::~ElaEventBus() +{ +} + +ElaEventBusType::EventBusReturnType ElaEventBus::post(const QString& eventName, const QVariantMap& data) +{ + Q_D(ElaEventBus); + if (eventName.isEmpty()) + { + return ElaEventBusType::EventBusReturnType::EventNameInvalid; + } + if (d->_eventMap.contains(eventName)) + { + QList eventList = d->_eventMap.value(eventName); + foreach (auto event, eventList) + { + if (event->parent()) + { + QMetaObject::invokeMethod(event->parent(), event->getFunctionName().toLocal8Bit().constData(), event->getConnectionType(), Q_ARG(QVariantMap, data)); + } + } + } + return ElaEventBusType::EventBusReturnType::Success; +} + +QStringList ElaEventBus::getRegisteredEventsName() const +{ + Q_D(const ElaEventBus); + if (d->_eventMap.count() == 0) + { + return QStringList(); + } + QStringList eventsNameList = d->_eventMap.keys(); + return eventsNameList; +} diff --git a/3rdparty/ElaWidgetTools/src/ElaExponentialBlur.cpp b/3rdparty/ElaWidgetTools/src/ElaExponentialBlur.cpp new file mode 100644 index 0000000..92da249 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaExponentialBlur.cpp @@ -0,0 +1,27 @@ +#include "ElaExponentialBlur.h" + +#include +#include + +#include "ElaExponentialBlurPrivate.h" +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +#include +#endif +Q_SINGLETON_CREATE_CPP(ElaExponentialBlur) +ElaExponentialBlur::ElaExponentialBlur(QObject* parent) + : QObject{parent}, d_ptr(new ElaExponentialBlurPrivate()) +{ + Q_D(ElaExponentialBlur); + d->q_ptr = this; +} + +ElaExponentialBlur::~ElaExponentialBlur() +{ +} + +QPixmap ElaExponentialBlur::doExponentialBlur(QImage img, const quint16& blurRadius) +{ + QImage shadowImage = img.convertToFormat(QImage::Format_ARGB32); + ElaExponentialBlur::getInstance()->d_ptr->_drawExponentialBlur(shadowImage, blurRadius); + return QPixmap::fromImage(shadowImage); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaFlowLayout.cpp b/3rdparty/ElaWidgetTools/src/ElaFlowLayout.cpp new file mode 100644 index 0000000..6687b2c --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaFlowLayout.cpp @@ -0,0 +1,135 @@ +#include "ElaFlowLayout.h" + +#include +#include + +#include "private/ElaFlowLayoutPrivate.h" + +ElaFlowLayout::ElaFlowLayout(QWidget* parent, int margin, int hSpacing, int vSpacing) + : QLayout(parent), d_ptr(new ElaFlowLayoutPrivate()) +{ + Q_D(ElaFlowLayout); + d->q_ptr = this; + d->_hSpacing = hSpacing; + d->_vSpacing = vSpacing; + setContentsMargins(margin, margin, margin, margin); + d->_lastHeightMap.insert(this, 0); +} + +ElaFlowLayout::ElaFlowLayout(int margin, int hSpacing, int vSpacing) + : d_ptr(new ElaFlowLayoutPrivate()) +{ + Q_D(ElaFlowLayout); + d->q_ptr = this; + d->_hSpacing = hSpacing; + d->_vSpacing = vSpacing; + setContentsMargins(margin, margin, margin, margin); + d->_lastHeightMap.insert(this, 0); +} + +ElaFlowLayout::~ElaFlowLayout() +{ + QLayoutItem* item; + while ((item = ElaFlowLayout::takeAt(0))) + { + delete item; + } +} + +void ElaFlowLayout::addItem(QLayoutItem* item) +{ + Q_D(ElaFlowLayout); + d->_itemList.append(item); +} +int ElaFlowLayout::horizontalSpacing() const +{ + Q_D(const ElaFlowLayout); + if (d->_hSpacing >= 0) + { + return d->_hSpacing; + } + else + { + return d->_smartSpacing(QStyle::PM_LayoutHorizontalSpacing); + } +} + +int ElaFlowLayout::verticalSpacing() const +{ + Q_D(const ElaFlowLayout); + if (d->_vSpacing >= 0) + { + return d->_vSpacing; + } + else + { + return d->_smartSpacing(QStyle::PM_LayoutVerticalSpacing); + } +} +int ElaFlowLayout::count() const +{ + return d_ptr->_itemList.size(); +} + +QLayoutItem* ElaFlowLayout::itemAt(int index) const +{ + return d_ptr->_itemList.value(index); +} + +QLayoutItem* ElaFlowLayout::takeAt(int index) +{ + Q_D(ElaFlowLayout); + if (index >= 0 && index < d->_itemList.size()) + { + return d->_itemList.takeAt(index); + } + return nullptr; +} + +void ElaFlowLayout::setIsAnimation(bool isAnimation) +{ + Q_D(ElaFlowLayout); + d->_isAnimation = isAnimation; +} + +Qt::Orientations ElaFlowLayout::expandingDirections() const +{ + return {}; +} + +bool ElaFlowLayout::hasHeightForWidth() const +{ + return true; +} + +int ElaFlowLayout::heightForWidth(int width) const +{ + int height = d_ptr->_doLayout(QRect(0, 0, width, 0), true); + d_ptr->_lastHeightMap[const_cast(this)] = height; + return height; +} + +void ElaFlowLayout::setGeometry(const QRect& rect) +{ + Q_D(ElaFlowLayout); + QLayout::setGeometry(rect); + d->_doLayout(rect, false); +} + +QSize ElaFlowLayout::sizeHint() const +{ + return minimumSize(); +} + +QSize ElaFlowLayout::minimumSize() const +{ + QSize size; + for (const QLayoutItem* item : std::as_const(d_ptr->_itemList)) + { + size = size.expandedTo(item->minimumSize()); + } + + const QMargins margins = contentsMargins(); + size += QSize(margins.left() + margins.right(), margins.top() + margins.bottom()); + return size; +} diff --git a/3rdparty/ElaWidgetTools/src/ElaGraphicsItem.cpp b/3rdparty/ElaWidgetTools/src/ElaGraphicsItem.cpp new file mode 100644 index 0000000..7291380 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaGraphicsItem.cpp @@ -0,0 +1,208 @@ +#include "ElaGraphicsItem.h" + +#include +#include +#include + +#include "ElaGraphicsScene.h" +#include "private/ElaGraphicsItemPrivate.h" +#include "private/ElaGraphicsScenePrivate.h" +Q_PROPERTY_CREATE_Q_CPP(ElaGraphicsItem, int, Width) +Q_PROPERTY_CREATE_Q_CPP(ElaGraphicsItem, int, Height) +Q_PROPERTY_CREATE_Q_CPP(ElaGraphicsItem, QImage, ItemImage) +Q_PROPERTY_CREATE_Q_CPP(ElaGraphicsItem, QImage, ItemSelectedImage) +Q_PROPERTY_CREATE_Q_CPP(ElaGraphicsItem, QString, ItemName) +Q_PROPERTY_CREATE_Q_CPP(ElaGraphicsItem, QVariantMap, DataRoutes) +ElaGraphicsItem::ElaGraphicsItem(QGraphicsItem* parent) + : QGraphicsObject(parent), d_ptr(new ElaGraphicsItemPrivate()) +{ + Q_D(ElaGraphicsItem); + d->q_ptr = this; + setAcceptHoverEvents(true); + setAcceptedMouseButtons(Qt::AllButtons); + setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsSelectable | ItemAcceptsInputMethod); + d->_pWidth = 50; + d->_pHeight = 50; + d->_itemUID = QUuid::createUuid().toString().remove("{").remove("}").remove("-"); + d->_pItemImage = QImage(":/include/Image/Moon.jpg"); + d->_pItemSelectedImage = QImage(":/include/Image/Cirno.jpg"); + d->_pItemName = ""; + d->_pMaxLinkPortCount = 1; + d->_currentLinkPortState.resize(1); + d->_currentLinkPortState.fill(false); +} + +ElaGraphicsItem::ElaGraphicsItem(int width, int height, QGraphicsItem* parent) + : ElaGraphicsItem(parent) +{ + Q_D(ElaGraphicsItem); + d->_pWidth = width; + d->_pHeight = height; +} + +ElaGraphicsItem::~ElaGraphicsItem() +{ +} + +void ElaGraphicsItem::setMaxLinkPortCount(int maxLinkPortCount) +{ + Q_D(ElaGraphicsItem); + if (maxLinkPortCount < 0) + { + maxLinkPortCount = 0; + } + d->_pMaxLinkPortCount = maxLinkPortCount; + if (d->_currentLinkPortState.count() > maxLinkPortCount) + { + while (d->_currentLinkPortState.count() > maxLinkPortCount) + { + d->_currentLinkPortState.removeLast(); + } + } + else + { + while (d->_currentLinkPortState.count() < maxLinkPortCount) + { + d->_currentLinkPortState.append(false); + } + } +} + +int ElaGraphicsItem::getMaxLinkPortCount() const +{ + Q_D(const ElaGraphicsItem); + return d->_pMaxLinkPortCount; +} + +QString ElaGraphicsItem::getItemUID() const +{ + return d_ptr->_itemUID; +} + +void ElaGraphicsItem::setLinkPortState(bool isFullLink) +{ + Q_D(ElaGraphicsItem); + d->_currentLinkPortState.fill(isFullLink); +} + +void ElaGraphicsItem::setLinkPortState(bool isLink, int portIndex) +{ + Q_D(ElaGraphicsItem); + if (portIndex >= 0 && portIndex < d->_pMaxLinkPortCount) + { + d->_currentLinkPortState[portIndex] = isLink; + } +} + +QVector ElaGraphicsItem::getLinkPortState() const +{ + Q_D(const ElaGraphicsItem); + return d->_currentLinkPortState; +} + +bool ElaGraphicsItem::getLinkPortState(int portIndex) const +{ + Q_D(const ElaGraphicsItem); + if (portIndex >= 0 && portIndex < d->_pMaxLinkPortCount) + { + return d->_currentLinkPortState[portIndex]; + } + return false; +} + +int ElaGraphicsItem::getUsedLinkPortCount() const +{ + Q_D(const ElaGraphicsItem); + int currentLinkPortCount = 0; + for (auto isLink : d->_currentLinkPortState) + { + if (isLink) + { + currentLinkPortCount++; + } + } + return currentLinkPortCount; +} + +QVector ElaGraphicsItem::getUsedLinkPort() const +{ + Q_D(const ElaGraphicsItem); + QVector usedPortVector; + for (int i = 0; i < d->_pMaxLinkPortCount; i++) + { + if (d->_currentLinkPortState[i]) + { + usedPortVector.append(i); + } + } + return usedPortVector; +} + +int ElaGraphicsItem::getUnusedLinkPortCount() const +{ + Q_D(const ElaGraphicsItem); + return d->_pMaxLinkPortCount - getUsedLinkPortCount(); +} + +QVector ElaGraphicsItem::getUnusedLinkPort() const +{ + Q_D(const ElaGraphicsItem); + QVector unusedPortVector; + for (int i = 0; i < d->_pMaxLinkPortCount; i++) + { + if (!(d->_currentLinkPortState[i])) + { + unusedPortVector.append(i); + } + } + return unusedPortVector; +} + +QRectF ElaGraphicsItem::boundingRect() const +{ + return QRect(-d_ptr->_pWidth / 2, -d_ptr->_pHeight / 2, d_ptr->_pWidth, d_ptr->_pHeight); +} + +void ElaGraphicsItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + Q_D(ElaGraphicsItem); + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform); + if (isSelected()) + { + painter->drawImage(boundingRect(), d->_pItemSelectedImage); + } + else + { + painter->drawImage(boundingRect(), d->_pItemImage); + } + painter->restore(); +} + +QDataStream& operator<<(QDataStream& stream, const ElaGraphicsItem* item) +{ + stream << item->x(); + stream << item->y(); + stream << item->zValue(); + stream << item->getWidth(); + stream << item->getHeight(); + stream << item->d_func(); + return stream; +} + +QDataStream& operator>>(QDataStream& stream, ElaGraphicsItem* item) +{ + qreal itemX; + qreal itemY; + qreal itemZ; + int itemWidth; + int itemHeight; + stream >> itemX >> itemY >> itemZ >> itemWidth >> itemHeight; + item->setX(itemX); + item->setY(itemY); + item->setZValue(itemZ); + item->setWidth(itemWidth); + item->setHeight(itemHeight); + stream >> item->d_func(); + return stream; +} diff --git a/3rdparty/ElaWidgetTools/src/ElaGraphicsLineItem.cpp b/3rdparty/ElaWidgetTools/src/ElaGraphicsLineItem.cpp new file mode 100644 index 0000000..72eecc3 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaGraphicsLineItem.cpp @@ -0,0 +1,112 @@ +#include "ElaGraphicsLineItem.h" + +#include +#include + +#include "ElaGraphicsItem.h" +#include "ElaGraphicsLineItemPrivate.h" +Q_PRIVATE_CREATE_Q_CPP(ElaGraphicsLineItem, QPointF, StartPoint); +Q_PRIVATE_CREATE_Q_CPP(ElaGraphicsLineItem, QPointF, EndPoint); +Q_PRIVATE_CREATE_Q_CPP(ElaGraphicsLineItem, ElaGraphicsItem*, StartItem); +Q_PRIVATE_CREATE_Q_CPP(ElaGraphicsLineItem, ElaGraphicsItem*, EndItem); +Q_PRIVATE_CREATE_Q_CPP(ElaGraphicsLineItem, int, StartItemPort); +Q_PRIVATE_CREATE_Q_CPP(ElaGraphicsLineItem, int, EndItemPort); +ElaGraphicsLineItem::ElaGraphicsLineItem(ElaGraphicsItem* startItem, ElaGraphicsItem* endItem, int startItemPort, int endItemPort, QGraphicsItem* parent) + : QGraphicsPathItem(parent), d_ptr(new ElaGraphicsLineItemPrivate()) +{ + Q_D(ElaGraphicsLineItem); + d->q_ptr = this; + d->_pStartItem = startItem; + d->_pEndItem = endItem; + d->_pStartItemPort = startItemPort; + d->_pEndItemPort = endItemPort; + d->_linkItemMap.insert(d->_pStartItem, d->_pStartItemPort); + d->_linkItemMap.insert(d->_pEndItem, d->_pEndItemPort); + setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsSelectable | ItemAcceptsInputMethod); +} + +ElaGraphicsLineItem::ElaGraphicsLineItem(QPointF startPoint, QPointF endPoint, QGraphicsItem* parent) + : QGraphicsPathItem(parent), d_ptr(new ElaGraphicsLineItemPrivate()) +{ + Q_D(ElaGraphicsLineItem); + d->q_ptr = this; + d->_pStartPoint = startPoint; + d->_pEndPoint = endPoint; + d->_isCreateWithItem = false; + setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsSelectable | ItemAcceptsInputMethod); +} + +ElaGraphicsLineItem::~ElaGraphicsLineItem() +{ +} + +bool ElaGraphicsLineItem::isTargetLink(ElaGraphicsItem* item) const +{ + Q_D(const ElaGraphicsLineItem); + if (d->_linkItemMap.contains(item)) + { + return true; + } + return false; +} + +bool ElaGraphicsLineItem::isTargetLink(ElaGraphicsItem* item1, ElaGraphicsItem* item2) const +{ + Q_D(const ElaGraphicsLineItem); + if (d->_linkItemMap.contains(item1) && d->_linkItemMap.contains(item2)) + { + return true; + } + return false; +} + +bool ElaGraphicsLineItem::isTargetLink(ElaGraphicsItem* item1, ElaGraphicsItem* item2, int port1, int port2) const +{ + Q_D(const ElaGraphicsLineItem); + if (d->_linkItemMap.value(item1) == port1 && d->_linkItemMap.value(item2) == port2) + { + return true; + } + return false; +} + +void ElaGraphicsLineItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + Q_D(ElaGraphicsLineItem); + painter->save(); + painter->setRenderHints(QPainter::Antialiasing); + painter->setPen(QPen(Qt::black, 3)); + QPainterPath path; + qreal pathXStart = 0; + qreal pathYStart = 0; + qreal pathXEnd = 0; + qreal pathYEnd = 0; + if (d->_isCreateWithItem) + { + pathXStart = d->_pStartItem->x(); + pathYStart = d->_pStartItem->y(); + pathXEnd = d->_pEndItem->x(); + pathYEnd = d->_pEndItem->y(); + } + else + { + pathXStart = d->_pStartPoint.x(); + pathYStart = d->_pStartPoint.y(); + pathXEnd = d->_pEndPoint.x(); + pathYEnd = d->_pEndPoint.y(); + } + path.moveTo(pathXStart, pathYStart); // 设置起始点 + path.cubicTo((pathXStart + pathXEnd) / 2, pathYStart, (pathXStart + pathXEnd) / 2, pathYEnd, pathXEnd, pathYEnd); + setPath(path); + painter->drawPath(path); + painter->restore(); +} + +QRectF ElaGraphicsLineItem::boundingRect() const +{ + if (!QGraphicsPathItem::boundingRect().isValid()) + { + return scene()->sceneRect(); + } + return QGraphicsPathItem::boundingRect(); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaGraphicsScene.cpp b/3rdparty/ElaWidgetTools/src/ElaGraphicsScene.cpp new file mode 100644 index 0000000..e189f19 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaGraphicsScene.cpp @@ -0,0 +1,556 @@ +#include "ElaGraphicsScene.h" + +#include +#include +#include +#include +#include + +#include "ElaGraphicsItem.h" +#include "ElaGraphicsLineItem.h" +#include "private/ElaGraphicsScenePrivate.h" +Q_PROPERTY_CREATE_Q_CPP(ElaGraphicsScene, bool, IsCheckLinkPort) +Q_PROPERTY_CREATE_Q_CPP(ElaGraphicsScene, QString, SerializePath) +ElaGraphicsScene::ElaGraphicsScene(QObject* parent) + : QGraphicsScene(parent), d_ptr(new ElaGraphicsScenePrivate()) +{ + Q_D(ElaGraphicsScene); + d->q_ptr = this; + setItemIndexMethod(QGraphicsScene::NoIndex); + d->_pIsCheckLinkPort = false; + d->_sceneMode = ElaGraphicsSceneType::SceneMode::Default; + d->_pSerializePath = "./scene.bin"; +} + +ElaGraphicsScene::~ElaGraphicsScene() +{ +} + +void ElaGraphicsScene::addItem(ElaGraphicsItem* item) +{ + Q_D(ElaGraphicsScene); + if (!item) + { + return; + } + for (const auto& pair : d->_items.toStdMap()) + { + if (pair.second == item) + { + return; + } + } + item->setParent(this); + item->setZValue(d->_currentZ); + if (item->getItemName().isEmpty()) + { + item->setItemName(QString("ElaItem%1").arg(d->_currentZ)); + } + item->setPos(sceneRect().width() / 2, sceneRect().height() / 2); + QGraphicsScene::addItem(item); + d->_currentZ++; + d->_items.insert(item->getItemUID(), item); +} + +void ElaGraphicsScene::removeItem(ElaGraphicsItem* item) +{ + Q_D(ElaGraphicsScene); + if (!item) + { + return; + } + d->_items.remove(d->_items.key(item)); + removeItemLink(item); + QGraphicsScene::removeItem(item); + delete item; + update(); +} + +void ElaGraphicsScene::removeSelectedItems() +{ + QList selectedItemList = getSelectedElaItems(); + if (selectedItemList.count() == 0) + { + return; + } + for (auto item : selectedItemList) + { + removeItem(item); + } +} + +void ElaGraphicsScene::clear() +{ + Q_D(ElaGraphicsScene); + d->_itemsLink.clear(); + for (const auto& pair : d->_items.toStdMap()) + { + delete pair.second; + } + d->_items.clear(); + update(); +} + +QList ElaGraphicsScene::createAndAddItem(int width, int height, int count) +{ + if (count <= 0) + { + return QList(); + } + QList createItemList; + for (int i = 0; i < count; i++) + { + ElaGraphicsItem* item = new ElaGraphicsItem(); + item->setWidth(width); + item->setHeight(height); + createItemList.append(item); + addItem(item); + } + return createItemList; +} + +QList ElaGraphicsScene::getSelectedElaItems() const +{ + QList selectedItemList = selectedItems(); + QList selectedElaItemList; + for (auto item : selectedItemList) + { + ElaGraphicsItem* itemCast = dynamic_cast(item); + if (itemCast) + { + selectedElaItemList.append(itemCast); + } + } + return selectedElaItemList; +} + +QList ElaGraphicsScene::getElaItems() +{ + Q_D(ElaGraphicsScene); + return d->_items.values(); +} + +QList ElaGraphicsScene::getElaItems(QPoint pos) +{ + QList itemList = items(pos); + QList elaItemList; + for (auto item : itemList) + { + ElaGraphicsItem* elaItem = dynamic_cast(item); + if (elaItem) + { + elaItemList.append(elaItem); + } + } + return elaItemList; +} + +QList ElaGraphicsScene::getElaItems(QPointF pos) +{ + QList itemList = items(pos); + QList elaItemList; + for (auto item : itemList) + { + ElaGraphicsItem* elaItem = dynamic_cast(item); + if (elaItem) + { + elaItemList.append(elaItem); + } + } + return elaItemList; +} + +QList ElaGraphicsScene::getElaItems(QRect rect) +{ + QList itemList = items(rect); + QList elaItemList; + for (auto item : itemList) + { + ElaGraphicsItem* elaItem = dynamic_cast(item); + if (elaItem) + { + elaItemList.append(elaItem); + } + } + return elaItemList; +} + +QList ElaGraphicsScene::getElaItems(QRectF rect) +{ + QList itemList = items(rect); + QList elaItemList; + for (auto item : itemList) + { + ElaGraphicsItem* elaItem = dynamic_cast(item); + if (elaItem) + { + elaItemList.append(elaItem); + } + } + return elaItemList; +} + +void ElaGraphicsScene::setSceneMode(ElaGraphicsSceneType::SceneMode mode) +{ + Q_D(ElaGraphicsScene); + d->_sceneMode = mode; + if (mode == ElaGraphicsSceneType::SceneMode::DragMove) + { + views().at(0)->setDragMode(QGraphicsView::ScrollHandDrag); + } + else + { + views().at(0)->setDragMode(QGraphicsView::RubberBandDrag); + } +} + +ElaGraphicsSceneType::SceneMode ElaGraphicsScene::getSceneMode() const +{ + return d_ptr->_sceneMode; +} + +void ElaGraphicsScene::selectAllItems() +{ + Q_D(ElaGraphicsScene); + for (const auto& pair : d->_items.toStdMap()) + { + ElaGraphicsItem* item = pair.second; + item->setSelected(true); + } +} + +QList ElaGraphicsScene::getItemLinkList() const +{ + return d_ptr->_itemsLink; +} + +bool ElaGraphicsScene::addItemLink(ElaGraphicsItem* item1, ElaGraphicsItem* item2, int port1, int port2) +{ + Q_D(ElaGraphicsScene); + if (!item1 || !item2 || (item1 == item2) || port1 < 0 || port2 < 0 || item1->getMaxLinkPortCount() <= port1 || item2->getMaxLinkPortCount() <= port2) + { + return false; + } + if (d->_pIsCheckLinkPort) + { + if (!item1->getLinkPortState(port1) && !item2->getLinkPortState(port2)) + { + item1->setLinkPortState(true, port1); + item2->setLinkPortState(true, port2); + } + else + { + return false; + } + } + QVariantMap linkObject; + linkObject.insert(item1->getItemUID(), port1); + linkObject.insert(item2->getItemUID(), port2); + d->_itemsLink.append(linkObject); + ElaGraphicsLineItem* lineItem = new ElaGraphicsLineItem(item1, item2, port1, port2); + QGraphicsScene::addItem(lineItem); + d->_lineItemsList.append(lineItem); + update(); + return true; +} + +bool ElaGraphicsScene::removeItemLink(ElaGraphicsItem* item1) +{ + Q_D(ElaGraphicsScene); + if (!item1) + { + return false; + } + if (d->_pIsCheckLinkPort) + { + item1->setLinkPortState(false); + } + // 处理与该Item有关的连接 + foreach (auto& link, d->_itemsLink) + { + if (link.contains(item1->getItemUID())) + { + if (d->_pIsCheckLinkPort) + { + // 解除otherItem端口占用 + QStringList keys = link.keys(); + keys.removeOne(item1->getItemUID()); + ElaGraphicsItem* otherItem = d->_items.value(keys.at(0)); + otherItem->setLinkPortState(false, link.value(keys.at(0)).toInt()); + } + d->_itemsLink.removeOne(link); + } + } + // 处理连接图元 + foreach (auto lineItem, d->_lineItemsList) + { + if (lineItem->isTargetLink(item1)) + { + d->_lineItemsList.removeOne(lineItem); + QGraphicsScene::removeItem(lineItem); + delete lineItem; + } + } + update(); + return true; +} + +bool ElaGraphicsScene::removeItemLink(ElaGraphicsItem* item1, ElaGraphicsItem* item2, int port1, int port2) +{ + Q_D(ElaGraphicsScene); + if (!item1 || !item2) + { + return false; + } + bool isLinkExist = false; + foreach (auto& link, d->_itemsLink) + { + QVariant portVariant1 = link.value(item1->getItemUID()); + QVariant portVariant2 = link.value(item2->getItemUID()); + if (portVariant1.isValid() && portVariant2.isValid() && portVariant1.toUInt() == port1 && portVariant2.toUInt() == port2) + { + d->_itemsLink.removeOne(link); + // 这里处理连线图元 + isLinkExist = true; + break; + } + } + foreach (auto lineItem, d->_lineItemsList) + { + if (lineItem->isTargetLink(item1, item2, port1, port2)) + { + d->_lineItemsList.removeOne(lineItem); + QGraphicsScene::removeItem(lineItem); + delete lineItem; + break; + } + } + if (isLinkExist) + { + if (d->_pIsCheckLinkPort) + { + item1->setLinkPortState(false, port1); + item2->setLinkPortState(false, port2); + } + update(); + return true; + } + else + { + return false; + } +} + +QVector ElaGraphicsScene::getItemsDataRoute() const +{ + QVector dataRouteVector; + for (const auto& pair : d_ptr->_items.toStdMap()) + { + ElaGraphicsItem* item = pair.second; + dataRouteVector.append(item->getDataRoutes()); + } + return dataRouteVector; +} + +void ElaGraphicsScene::serialize() +{ + Q_D(ElaGraphicsScene); + QFile file(d->_pSerializePath); + if (!file.open(QIODevice::WriteOnly)) + { + qDebug() << "serialize Error"; + return; + } + QDataStream serialStream(&file); + serialStream << d; + file.flush(); + file.close(); +} + +void ElaGraphicsScene::deserialize() +{ + Q_D(ElaGraphicsScene); + QFile file(d->_pSerializePath); + if (!file.open(QIODevice::ReadOnly)) + { + qDebug() << "deserialize Error"; + return; + } + QDataStream deserialStream(&file); + deserialStream >> d; + file.close(); + update(); +} + +void ElaGraphicsScene::focusOutEvent(QFocusEvent* event) +{ + Q_D(ElaGraphicsScene); + d->_sceneMode = ElaGraphicsSceneType::SceneMode::Default; + d->_removeLinkLineItem(); + QGraphicsScene::focusOutEvent(event); +} + +void ElaGraphicsScene::keyPressEvent(QKeyEvent* event) +{ + Q_D(ElaGraphicsScene); + switch (event->key()) + { + case Qt::Key_Control: + { + d->_sceneMode = ElaGraphicsSceneType::SceneMode::MultiSelect; + break; + } + case Qt::Key_Shift: + { + d->_sceneMode = ElaGraphicsSceneType::SceneMode::ItemLink; + clearSelection(); + break; + } + case Qt::Key_Delete: + { + removeSelectedItems(); + break; + } + } + QGraphicsScene::keyPressEvent(event); +} + +void ElaGraphicsScene::keyReleaseEvent(QKeyEvent* event) +{ + Q_D(ElaGraphicsScene); + d->_sceneMode = ElaGraphicsSceneType::SceneMode::Default; + d->_removeLinkLineItem(); + QGraphicsScene::keyReleaseEvent(event); +} + +void ElaGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent* event) +{ + Q_D(ElaGraphicsScene); + + if (event->button() == Qt::LeftButton) + { + if (d->_sceneMode == ElaGraphicsSceneType::SceneMode::Default) + { + d->_isLeftButtonPress = true; + d->_lastPos = event->pos(); + } + } + QList selectedItemList = selectedItems(); + QGraphicsScene::mousePressEvent(event); + if (d->_sceneMode == ElaGraphicsSceneType::SceneMode::ItemLink) + { + for (auto item : selectedItemList) + { + item->setSelected(true); + } + d->_lastPos = event->pos(); + } +} + +void ElaGraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) +{ + Q_D(ElaGraphicsScene); + QList itemList = getElaItems(event->scenePos()); + if (event->button() == Qt::LeftButton) + { + d->_isLeftButtonPress = false; + switch (d->_sceneMode) + { + case ElaGraphicsSceneType::SceneMode::Default: + { + if (itemList.count() > 0) + { + Q_EMIT mouseLeftClickedItem(itemList[0]); + } + break; + } + case ElaGraphicsSceneType::SceneMode::ItemLink: + { + QList selectedItemList = getSelectedElaItems(); + if (selectedItemList.count() == 1 && !d->_linkLineItem) + { + d->_linkLineItem = new ElaGraphicsLineItem(selectedItemList.at(0)->pos(), selectedItemList.at(0)->pos()); + QGraphicsScene::addItem(d->_linkLineItem); + } + else if (selectedItemList.count() == 2) + { + if (d->_pIsCheckLinkPort) + { + Q_EMIT showItemLink(); + } + else + { + QVariantMap linkObject; + for (auto item : selectedItemList) + { + linkObject.insert(item->getItemUID(), 0); + } + d->_itemsLink.append(linkObject); + QGraphicsScene::mouseReleaseEvent(event); + d->_removeLinkLineItem(); + addItemLink(selectedItemList.at(0), selectedItemList.at(1)); + clearSelection(); + return; + } + } + else + { + d->_removeLinkLineItem(); + update(); + } + break; + } + default: + { + break; + } + } + } + else if (event->button() == Qt::RightButton) + { + if (itemList.count() > 0) + { + clearSelection(); + itemList[0]->setSelected(true); + Q_EMIT mouseRightClickedItem(itemList[0]); + } + } + QGraphicsScene::mouseReleaseEvent(event); +} + +void ElaGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent* event) +{ + Q_D(ElaGraphicsScene); + if (d->_sceneMode == ElaGraphicsSceneType::SceneMode::ItemLink) + { + if (getSelectedElaItems().count() == 1) + { + if (d->_linkLineItem) + { + d->_linkLineItem->setEndPoint(event->scenePos()); + d->_linkLineItem->update(); + } + } + } + else + { + if (d->_isLeftButtonPress) + { + for (auto lineItem : d->_lineItemsList) + { + lineItem->update(); + } + } + } + QGraphicsScene::mouseMoveEvent(event); +} + +void ElaGraphicsScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) +{ + QList itemList = getElaItems(event->scenePos()); + if (itemList.count() > 0) + { + Q_EMIT mouseDoubleClickedItem(itemList[0]); + } + QGraphicsScene::mouseDoubleClickEvent(event); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaGraphicsView.cpp b/3rdparty/ElaWidgetTools/src/ElaGraphicsView.cpp new file mode 100644 index 0000000..f1a5156 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaGraphicsView.cpp @@ -0,0 +1,69 @@ +#include "ElaGraphicsView.h" + +#include +#include +#include + +#include "private/ElaGraphicsViewPrivate.h" +Q_PROPERTY_CREATE_Q_CPP(ElaGraphicsView, qreal, MaxTransform); +Q_PROPERTY_CREATE_Q_CPP(ElaGraphicsView, qreal, MinTransform); +ElaGraphicsView::ElaGraphicsView(QWidget* parent) + : QGraphicsView(parent), d_ptr(new ElaGraphicsViewPrivate()) +{ + Q_D(ElaGraphicsView); + d->q_ptr = this; + d->_pMaxTransform = 5; + d->_pMinTransform = 0.15; + d->_initStyle(); +} + +ElaGraphicsView::ElaGraphicsView(QGraphicsScene* scene, QWidget* parent) + : QGraphicsView(scene, parent), d_ptr(new ElaGraphicsViewPrivate()) +{ + Q_D(ElaGraphicsView); + d->q_ptr = this; + d->_pMaxTransform = 5; + d->_pMinTransform = 0.15; + d->_initStyle(); +} + +ElaGraphicsView::~ElaGraphicsView() +{ +} + +void ElaGraphicsView::wheelEvent(QWheelEvent* event) +{ + Q_D(ElaGraphicsView); + if (event->modifiers() == Qt::CTRL) + { + // 放大 + if ((event->angleDelta().y() > 0) && transform().m11() <= d->_pMaxTransform) + { + this->scale(1.1, 1.1); + } + else if ((event->angleDelta().y() < 0) && transform().m11() >= d->_pMinTransform) + { + this->scale(1.0 / 1.1, 1.0 / 1.1); + } + return; + } + QGraphicsView::wheelEvent(event); +} + +void ElaGraphicsView::keyPressEvent(QKeyEvent* event) +{ + if (event->key() == Qt::Key_Alt) + { + setDragMode(QGraphicsView::ScrollHandDrag); + } + QGraphicsView::keyPressEvent(event); +} + +void ElaGraphicsView::keyReleaseEvent(QKeyEvent* event) +{ + if (event->key() == Qt::Key_Alt) + { + setDragMode(QGraphicsView::RubberBandDrag); + } + QGraphicsView::keyReleaseEvent(event); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaIcon.cpp b/3rdparty/ElaWidgetTools/src/ElaIcon.cpp new file mode 100644 index 0000000..be2a6b2 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaIcon.cpp @@ -0,0 +1,112 @@ +#include "ElaIcon.h" + +#include +#include +Q_SINGLETON_CREATE_CPP(ElaIcon) +ElaIcon::ElaIcon() +{ +} + +ElaIcon::~ElaIcon() +{ +} + +QIcon ElaIcon::getElaIcon(ElaIconType::IconName awesome) +{ + QFont iconFont = QFont("ElaAwesome"); + QPixmap pix(30, 30); + pix.fill(Qt::transparent); + QPainter painter; + painter.begin(&pix); + painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform); + // painter.setPen(QColor("#1570A5")); + // painter.setBrush(QColor("#1570A5")); + iconFont.setPixelSize(25); + painter.setFont(iconFont); + painter.drawText(pix.rect(), Qt::AlignCenter, QChar((unsigned short)awesome)); + painter.end(); + return QIcon(pix); +} + +QIcon ElaIcon::getElaIcon(ElaIconType::IconName awesome, QColor iconColor) +{ + QFont iconFont = QFont("ElaAwesome"); + QPixmap pix(30, 30); + pix.fill(Qt::transparent); + QPainter painter; + painter.begin(&pix); + painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform); + painter.setPen(iconColor); + // painter.setBrush(QColor("#1570A5")); + iconFont.setPixelSize(25); + painter.setFont(iconFont); + painter.drawText(pix.rect(), Qt::AlignCenter, QChar((unsigned short)awesome)); + painter.end(); + return QIcon(pix); +} + +QIcon ElaIcon::getElaIcon(ElaIconType::IconName awesome, int pixelSize) +{ + QFont iconFont = QFont("ElaAwesome"); + QPixmap pix(pixelSize, pixelSize); + pix.fill(Qt::transparent); + QPainter painter; + painter.begin(&pix); + painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform); + iconFont.setPixelSize(pixelSize); + painter.setFont(iconFont); + // 画图形字体 + painter.drawText(pix.rect(), Qt::AlignCenter, QChar((unsigned short)awesome)); + painter.end(); + return QIcon(pix); +} + +QIcon ElaIcon::getElaIcon(ElaIconType::IconName awesome, int pixelSize, QColor iconColor) +{ + QFont iconFont = QFont("ElaAwesome"); + QPixmap pix(pixelSize, pixelSize); + pix.fill(Qt::transparent); + QPainter painter; + painter.begin(&pix); + painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform); + painter.setPen(iconColor); + iconFont.setPixelSize(pixelSize); + painter.setFont(iconFont); + // 画图形字体 + painter.drawText(pix.rect(), Qt::AlignCenter, QChar((unsigned short)awesome)); + painter.end(); + return QIcon(pix); +} + +QIcon ElaIcon::getElaIcon(ElaIconType::IconName awesome, int pixelSize, int fixedWidth, int fixedHeight) +{ + QFont iconFont = QFont("ElaAwesome"); + QPixmap pix(fixedWidth, fixedHeight); + pix.fill(Qt::transparent); + QPainter painter; + painter.begin(&pix); + painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform); + iconFont.setPixelSize(pixelSize); + painter.setFont(iconFont); + // 画图形字体 + painter.drawText(pix.rect(), Qt::AlignCenter, QChar((unsigned short)awesome)); + painter.end(); + return QIcon(pix); +} + +QIcon ElaIcon::getElaIcon(ElaIconType::IconName awesome, int pixelSize, int fixedWidth, int fixedHeight, QColor iconColor) +{ + QFont iconFont = QFont("ElaAwesome"); + QPixmap pix(fixedWidth, fixedHeight); + pix.fill(Qt::transparent); + QPainter painter; + painter.begin(&pix); + painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform); + painter.setPen(iconColor); + iconFont.setPixelSize(pixelSize); + painter.setFont(iconFont); + // 画图形字体 + painter.drawText(pix.rect(), Qt::AlignCenter, QChar((unsigned short)awesome)); + painter.end(); + return QIcon(pix); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaIconButton.cpp b/3rdparty/ElaWidgetTools/src/ElaIconButton.cpp new file mode 100644 index 0000000..a8186a6 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaIconButton.cpp @@ -0,0 +1,227 @@ +#include "ElaIconButton.h" + +#include +#include +#include +#include +#include + +#include "ElaTheme.h" +#include "private/ElaIconButtonPrivate.h" +Q_PROPERTY_CREATE_Q_CPP(ElaIconButton, int, BorderRadius) +Q_PROPERTY_CREATE_Q_CPP(ElaIconButton, qreal, Opacity); +Q_PROPERTY_CREATE_Q_CPP(ElaIconButton, QColor, LightHoverColor); +Q_PROPERTY_CREATE_Q_CPP(ElaIconButton, QColor, DarkHoverColor); +Q_PROPERTY_CREATE_Q_CPP(ElaIconButton, QColor, LightIconColor); +Q_PROPERTY_CREATE_Q_CPP(ElaIconButton, QColor, DarkIconColor); +Q_PROPERTY_CREATE_Q_CPP(ElaIconButton, QColor, LightHoverIconColor); +Q_PROPERTY_CREATE_Q_CPP(ElaIconButton, QColor, DarkHoverIconColor); +Q_PROPERTY_CREATE_Q_CPP(ElaIconButton, bool, IsSelected); +ElaIconButton::ElaIconButton(QPixmap pix, QWidget* parent) + : QPushButton(parent), d_ptr(new ElaIconButtonPrivate()) +{ + Q_D(ElaIconButton); + d->q_ptr = this; + d->_iconPix = pix.copy(); + d->_pHoverAlpha = 0; + d->_pOpacity = 1; + d->_pLightHoverColor = ElaThemeColor(ElaThemeType::Light, BasicHoverAlpha); + d->_pDarkHoverColor = ElaThemeColor(ElaThemeType::Dark, BasicHoverAlpha); + d->_pLightIconColor = ElaThemeColor(ElaThemeType::Light, BasicText); + d->_pDarkIconColor = ElaThemeColor(ElaThemeType::Dark, BasicText); + d->_pLightHoverIconColor = ElaThemeColor(ElaThemeType::Light, BasicText); + d->_pDarkHoverIconColor = ElaThemeColor(ElaThemeType::Dark, BasicText); + d->_pIsSelected = false; + d->_pBorderRadius = 0; + d->_themeMode = eTheme->getThemeMode(); + connect(this, &ElaIconButton::pIsSelectedChanged, this, [=]() { update(); }); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { d->_themeMode = themeMode; }); +} + +ElaIconButton::ElaIconButton(ElaIconType::IconName awesome, QWidget* parent) + : QPushButton(parent), d_ptr(new ElaIconButtonPrivate()) +{ + Q_D(ElaIconButton); + d->q_ptr = this; + d->_pHoverAlpha = 0; + d->_pOpacity = 1; + d->_pLightHoverColor = ElaThemeColor(ElaThemeType::Light, BasicHoverAlpha); + d->_pDarkHoverColor = ElaThemeColor(ElaThemeType::Dark, BasicHoverAlpha); + d->_pLightIconColor = ElaThemeColor(ElaThemeType::Light, BasicText); + d->_pDarkIconColor = ElaThemeColor(ElaThemeType::Dark, BasicText); + d->_pLightHoverIconColor = ElaThemeColor(ElaThemeType::Light, BasicText); + d->_pDarkHoverIconColor = ElaThemeColor(ElaThemeType::Dark, BasicText); + d->_pIsSelected = false; + d->_pBorderRadius = 0; + d->_themeMode = eTheme->getThemeMode(); + QFont iconFont = QFont("ElaAwesome"); + iconFont.setPixelSize(15); + this->setFont(iconFont); + d->_pAwesome = awesome; + this->setText(QChar((unsigned short)awesome)); + connect(this, &ElaIconButton::pIsSelectedChanged, this, [=]() { update(); }); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { d->_themeMode = themeMode; }); +} + +ElaIconButton::ElaIconButton(ElaIconType::IconName awesome, int pixelSize, QWidget* parent) + : QPushButton(parent), d_ptr(new ElaIconButtonPrivate()) +{ + Q_D(ElaIconButton); + d->q_ptr = this; + d->_pHoverAlpha = 0; + d->_pOpacity = 1; + d->_pLightHoverColor = ElaThemeColor(ElaThemeType::Light, BasicHoverAlpha); + d->_pDarkHoverColor = ElaThemeColor(ElaThemeType::Dark, BasicHoverAlpha); + d->_pLightIconColor = ElaThemeColor(ElaThemeType::Light, BasicText); + d->_pDarkIconColor = ElaThemeColor(ElaThemeType::Dark, BasicText); + d->_pLightHoverIconColor = ElaThemeColor(ElaThemeType::Light, BasicText); + d->_pDarkHoverIconColor = ElaThemeColor(ElaThemeType::Dark, BasicText); + d->_pIsSelected = false; + d->_pBorderRadius = 0; + d->_themeMode = eTheme->getThemeMode(); + QFont iconFont = QFont("ElaAwesome"); + iconFont.setPixelSize(pixelSize); + this->setFont(iconFont); + d->_pAwesome = awesome; + this->setText(QChar((unsigned short)awesome)); + connect(this, &ElaIconButton::pIsSelectedChanged, this, [=]() { update(); }); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { d->_themeMode = themeMode; }); +} + +ElaIconButton::ElaIconButton(ElaIconType::IconName awesome, int pixelSize, int fixedWidth, int fixedHeight, QWidget* parent) + : QPushButton(parent), d_ptr(new ElaIconButtonPrivate()) +{ + Q_D(ElaIconButton); + d->q_ptr = this; + d->_pHoverAlpha = 0; + d->_pOpacity = 1; + d->_pLightHoverColor = ElaThemeColor(ElaThemeType::Light, BasicHoverAlpha); + d->_pDarkHoverColor = ElaThemeColor(ElaThemeType::Dark, BasicHoverAlpha); + d->_pLightIconColor = ElaThemeColor(ElaThemeType::Light, BasicText); + d->_pDarkIconColor = ElaThemeColor(ElaThemeType::Dark, BasicText); + d->_pLightHoverIconColor = ElaThemeColor(ElaThemeType::Light, BasicText); + d->_pDarkHoverIconColor = ElaThemeColor(ElaThemeType::Dark, BasicText); + d->_pIsSelected = false; + d->_pBorderRadius = 0; + d->_themeMode = eTheme->getThemeMode(); + QFont iconFont = QFont("ElaAwesome"); + iconFont.setPixelSize(pixelSize); + this->setFont(iconFont); + d->_pAwesome = awesome; + this->setText(QChar((unsigned short)awesome)); + this->setFixedSize(fixedWidth, fixedHeight); + connect(this, &ElaIconButton::pIsSelectedChanged, this, [=]() { update(); }); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { d->_themeMode = themeMode; }); +} + +ElaIconButton::~ElaIconButton() +{ +} + +void ElaIconButton::setAwesome(ElaIconType::IconName awesome) +{ + Q_D(ElaIconButton); + d->_pAwesome = awesome; + this->setText(QChar((unsigned short)awesome)); +} + +ElaIconType::IconName ElaIconButton::getAwesome() const +{ + return this->d_ptr->_pAwesome; +} + +void ElaIconButton::setPixmap(QPixmap pix) +{ + Q_D(ElaIconButton); + d->_iconPix = pix.copy(); +} + +bool ElaIconButton::event(QEvent* event) +{ + Q_D(ElaIconButton); + switch (event->type()) + { + case QEvent::Enter: + { + if (isEnabled() && !d->_pIsSelected) + { + d->_isAlphaAnimationFinished = false; + QPropertyAnimation* alphaAnimation = new QPropertyAnimation(d, "pHoverAlpha"); + connect(alphaAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + update(); + }); + connect(alphaAnimation, &QPropertyAnimation::finished, this, [=]() { + d->_isAlphaAnimationFinished = true; + }); + alphaAnimation->setDuration(175); + alphaAnimation->setStartValue(d->_pHoverAlpha); + alphaAnimation->setEndValue(d->_themeMode == ElaThemeType::Light ? d->_pLightHoverColor.alpha() : d->_pDarkHoverColor.alpha()); + alphaAnimation->start(QAbstractAnimation::DeleteWhenStopped); + } + break; + } + case QEvent::Leave: + { + if (isEnabled() && !d->_pIsSelected) + { + d->_isAlphaAnimationFinished = false; + QPropertyAnimation* alphaAnimation = new QPropertyAnimation(d, "pHoverAlpha"); + connect(alphaAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + update(); + }); + connect(alphaAnimation, &QPropertyAnimation::finished, this, [=]() { + d->_isAlphaAnimationFinished = true; + }); + alphaAnimation->setDuration(175); + alphaAnimation->setStartValue(d->_pHoverAlpha); + alphaAnimation->setEndValue(0); + alphaAnimation->start(QAbstractAnimation::DeleteWhenStopped); + } + break; + } + default: + { + break; + } + } + return QPushButton::event(event); +} + +void ElaIconButton::paintEvent(QPaintEvent* event) +{ + Q_D(ElaIconButton); + QPainter painter(this); + painter.save(); + painter.setOpacity(d->_pOpacity); + painter.setRenderHints(QPainter::SmoothPixmapTransform | QPainter::Antialiasing | QPainter::TextAntialiasing); + painter.setPen(Qt::NoPen); + if (d->_isAlphaAnimationFinished || d->_pIsSelected) + { + painter.setBrush(d->_pIsSelected ? d->_themeMode == ElaThemeType::Light ? d->_pLightHoverColor : d->_pDarkHoverColor + : isEnabled() ? underMouse() ? d->_themeMode == ElaThemeType::Light ? d->_pLightHoverColor : d->_pDarkHoverColor : Qt::transparent + : Qt::transparent); + } + else + { + QColor hoverColor = d->_themeMode == ElaThemeType::Light ? d->_pLightHoverColor : d->_pDarkHoverColor; + hoverColor.setAlpha(d->_pHoverAlpha); + painter.setBrush(hoverColor); + } + painter.drawRoundedRect(rect(), d->_pBorderRadius, d->_pBorderRadius); + // 图标绘制 + if (!d->_iconPix.isNull()) + { + QPainterPath path; + path.addEllipse(rect()); + painter.setClipPath(path); + painter.drawPixmap(rect(), d->_iconPix); + } + else + { + painter.setPen(isEnabled() ? d->_themeMode == ElaThemeType::Light ? underMouse() ? d->_pLightHoverIconColor : d->_pLightIconColor : underMouse() ? d->_pDarkHoverIconColor + : d->_pDarkIconColor + : ElaThemeColor(d->_themeMode, BasicTextDisable)); + painter.drawText(rect(), Qt::AlignCenter, QChar((unsigned short)d->_pAwesome)); + } + painter.restore(); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaImageCard.cpp b/3rdparty/ElaWidgetTools/src/ElaImageCard.cpp new file mode 100644 index 0000000..4e463e1 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaImageCard.cpp @@ -0,0 +1,64 @@ +#include "ElaImageCard.h" + +#include +#include +#include + +#include "ElaImageCardPrivate.h" +#include "ElaTheme.h" + +Q_PROPERTY_CREATE_Q_CPP(ElaImageCard, QImage, CardImage); +Q_PROPERTY_CREATE_Q_CPP(ElaImageCard, int, BorderRadius) +Q_PROPERTY_CREATE_Q_CPP(ElaImageCard, bool, IsPreserveAspectCrop) +Q_PROPERTY_CREATE_Q_CPP(ElaImageCard, qreal, MaximumAspectRatio) +ElaImageCard::ElaImageCard(QWidget* parent) + : QWidget(parent), d_ptr(new ElaImageCardPrivate()) +{ + Q_D(ElaImageCard); + d->q_ptr = this; + d->_pBorderRadius = 6; + d->_pMaximumAspectRatio = 2.2; + d->_pIsPreserveAspectCrop = true; + setMinimumSize(350, 260); + d->_themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { + d->_themeMode = themeMode; + }); +} + +ElaImageCard::~ElaImageCard() +{ +} + +void ElaImageCard::paintEvent(QPaintEvent* event) +{ + Q_D(ElaImageCard); + QPainter painter(this); + painter.save(); + painter.setPen(Qt::NoPen); + painter.setRenderHints(QPainter::SmoothPixmapTransform | QPainter::Antialiasing); + QPainterPath path; + path.addRoundedRect(rect(), d->_pBorderRadius, d->_pBorderRadius); + painter.setClipPath(path); + // 图片绘制 + if (d->_pIsPreserveAspectCrop) + { + qreal itemAspectRatio = (qreal)rect().width() / rect().height(); + if (itemAspectRatio < d->_pMaximumAspectRatio) + { + itemAspectRatio = d->_pMaximumAspectRatio; + qreal cropHeight = d->_pCardImage.width() / itemAspectRatio; + painter.drawImage(QRect(0, 0, this->height() * d->_pMaximumAspectRatio, rect().height()), d->_pCardImage, QRectF(0, 0, d->_pCardImage.width(), cropHeight)); + } + else + { + qreal cropHeight = d->_pCardImage.width() / itemAspectRatio; + painter.drawImage(rect(), d->_pCardImage, QRectF(0, 0, d->_pCardImage.width(), cropHeight)); + } + } + else + { + painter.drawImage(rect(), d->_pCardImage); + } + painter.restore(); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaInteractiveCard.cpp b/3rdparty/ElaWidgetTools/src/ElaInteractiveCard.cpp new file mode 100644 index 0000000..d0c4ade --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaInteractiveCard.cpp @@ -0,0 +1,93 @@ +#include "ElaInteractiveCard.h" + +#include +#include + +#include "ElaTheme.h" +#include "private/ElaInteractiveCardPrivate.h" +Q_PROPERTY_CREATE_Q_CPP(ElaInteractiveCard, int, BorderRadius) +Q_PROPERTY_CREATE_Q_CPP(ElaInteractiveCard, QString, Title); +Q_PROPERTY_CREATE_Q_CPP(ElaInteractiveCard, QString, SubTitle); +Q_PROPERTY_CREATE_Q_CPP(ElaInteractiveCard, int, TitlePixelSize); +Q_PROPERTY_CREATE_Q_CPP(ElaInteractiveCard, int, SubTitlePixelSize); +Q_PROPERTY_CREATE_Q_CPP(ElaInteractiveCard, int, TitleSpacing); +Q_PROPERTY_CREATE_Q_CPP(ElaInteractiveCard, QPixmap, CardPixmap); +Q_PROPERTY_CREATE_Q_CPP(ElaInteractiveCard, QSize, CardPixmapSize); +Q_PROPERTY_CREATE_Q_CPP(ElaInteractiveCard, int, CardPixmapBorderRadius) +Q_PROPERTY_CREATE_Q_CPP(ElaInteractiveCard, ElaCardPixType::PixMode, CardPixMode); +ElaInteractiveCard::ElaInteractiveCard(QWidget* parent) + : QPushButton(parent), d_ptr(new ElaInteractiveCardPrivate()) +{ + Q_D(ElaInteractiveCard); + d->q_ptr = this; + d->_pBorderRadius = 6; + d->_pTitlePixelSize = 15; + d->_pSubTitlePixelSize = 12; + setMinimumSize(270, 80); + d->_pCardPixmapSize = QSize(64, 64); + d->_pTitleSpacing = 2; + d->_pCardPixmapBorderRadius = 6; + d->_pCardPixMode = ElaCardPixType::PixMode::Ellipse; + d->_themeMode = eTheme->getThemeMode(); + setMouseTracking(true); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { d->_themeMode = themeMode; }); +} + +ElaInteractiveCard::~ElaInteractiveCard() +{ +} + +void ElaInteractiveCard::setCardPixmapSize(int width, int height) +{ + Q_D(ElaInteractiveCard); + d->_pCardPixmapSize = QSize(width, height); + Q_EMIT pCardPixmapSizeChanged(); +} + +void ElaInteractiveCard::paintEvent(QPaintEvent* event) +{ + Q_D(ElaInteractiveCard); + QPainter painter(this); + painter.save(); + painter.setRenderHints(QPainter::SmoothPixmapTransform | QPainter::Antialiasing | QPainter::TextAntialiasing); + painter.setPen(Qt::NoPen); + painter.setBrush(underMouse() ? ElaThemeColor(d->_themeMode, BasicHoverAlpha) : Qt::transparent); + painter.drawRoundedRect(rect(), d->_pBorderRadius, d->_pBorderRadius); + // 图片绘制 + if (!d->_pCardPixmap.isNull()) + { + painter.save(); + QPainterPath path; + if (d->_pCardPixMode == ElaCardPixType::PixMode::Ellipse) + { + path.addEllipse(QPointF(d->_pCardPixmapSize.width() / 2 + 10, height() / 2), d->_pCardPixmapSize.width() / 2, d->_pCardPixmapSize.height() / 2); + painter.setClipPath(path); + painter.drawPixmap(QRect(10, (height() - d->_pCardPixmapSize.height()) / 2, d->_pCardPixmapSize.width(), d->_pCardPixmapSize.height()), d->_pCardPixmap); // rect为绘制区域,image为要绘制的图片 + } + else if (d->_pCardPixMode == ElaCardPixType::PixMode::Default) + { + painter.drawPixmap(10, (height() - d->_pCardPixmapSize.height()) / 2, d->_pCardPixmapSize.width(), d->_pCardPixmapSize.height(), d->_pCardPixmap); + } + else if (d->_pCardPixMode == ElaCardPixType::PixMode::RoundedRect) + { + path.addRoundedRect(QRectF(10, (height() - d->_pCardPixmapSize.height()) / 2, d->_pCardPixmapSize.width(), d->_pCardPixmapSize.height()), d->_pCardPixmapBorderRadius, d->_pCardPixmapBorderRadius); + painter.setClipPath(path); + painter.drawPixmap(10, (height() - d->_pCardPixmapSize.height()) / 2, d->_pCardPixmapSize.width(), d->_pCardPixmapSize.height(), d->_pCardPixmap); + } + painter.restore(); + } + // 文字绘制 + painter.setPen(ElaThemeColor(d->_themeMode, BasicText)); + QFont font = this->font(); + font.setWeight(QFont::Bold); + font.setPixelSize(d->_pTitlePixelSize); + painter.setFont(font); + int textStartX = d->_pCardPixmapSize.width() + 26; + int textWidth = width() - textStartX; + painter.drawText(QRect(textStartX, rect().y(), textWidth, height() / 2 - d->_pTitleSpacing), Qt::TextWordWrap | Qt::AlignBottom | Qt::AlignLeft, d->_pTitle); + font.setWeight(QFont::Normal); + font.setPixelSize(d->_pSubTitlePixelSize); + painter.setFont(font); + painter.drawText(QRect(textStartX, height() / 2 + d->_pTitleSpacing, textWidth, height() / 2 - d->_pTitleSpacing), Qt::TextWordWrap | Qt::AlignTop | Qt::AlignLeft, d->_pSubTitle); + painter.restore(); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaKeyBinder.cpp b/3rdparty/ElaWidgetTools/src/ElaKeyBinder.cpp new file mode 100644 index 0000000..2522e33 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaKeyBinder.cpp @@ -0,0 +1,120 @@ +#include "ElaKeyBinder.h" +#include "ElaContentDialog.h" +#include "ElaKeyBinderContainer.h" +#include "ElaKeyBinderPrivate.h" +#include "ElaTheme.h" +#include +#include +#include +Q_PROPERTY_CREATE_Q_CPP(ElaKeyBinder, int, BorderRadius) +ElaKeyBinder::ElaKeyBinder(QWidget* parent) + : QLabel(parent), d_ptr(new ElaKeyBinderPrivate()) +{ + Q_D(ElaKeyBinder); + d->q_ptr = this; + d->_pBorderRadius = 5; + setFixedHeight(35); + setMouseTracking(true); + setStyleSheet("#ElaKeyBinder{background-color:transparent;}"); + QFont textFont = font(); + textFont.setLetterSpacing(QFont::AbsoluteSpacing, 0.5); + textFont.setPixelSize(15); + setFont(textFont); + d->_binderContainer = new ElaKeyBinderContainer(this); + setText(u8" 按键: " + QString(u8"未绑定") + " "); + d->_binderDialog = new ElaContentDialog(window()); + d->_binderDialog->setCentralWidget(d->_binderContainer); + d->_binderDialog->setLeftButtonText(u8"取消"); + d->_binderDialog->setMiddleButtonText(u8"重置"); + d->_binderDialog->setRightButtonText(u8"确认"); + connect(d->_binderDialog, &ElaContentDialog::middleButtonClicked, this, [=]() { + d->_binderContainer->logOrResetHistoryData(false); + }); + connect(d->_binderDialog, &ElaContentDialog::rightButtonClicked, this, [=]() { + d->_binderContainer->saveBinderChanged(); + }); + d->onThemeChanged(eTheme->getThemeMode()); + connect(eTheme, &ElaTheme::themeModeChanged, d, &ElaKeyBinderPrivate::onThemeChanged); +} + +ElaKeyBinder::~ElaKeyBinder() +{ +} + +void ElaKeyBinder::setBinderKeyText(QString binderKeyText) +{ + Q_D(ElaKeyBinder); + d->_binderContainer->setBinderKeyText(binderKeyText); + setText(u8" 按键: " + binderKeyText + " "); +} + +QString ElaKeyBinder::getBinderKeyText() const +{ + Q_D(const ElaKeyBinder); + return d->_binderContainer->getBinderKeyText(); +} + +void ElaKeyBinder::setNativeVirtualBinderKey(quint32 binderKey) +{ + Q_D(ElaKeyBinder); + d->_binderContainer->setNativeVirtualBinderKey(binderKey); +} + +quint32 ElaKeyBinder::getNativeVirtualBinderKey() const +{ + Q_D(const ElaKeyBinder); + return d->_binderContainer->getNativeVirtualBinderKey(); +} + +bool ElaKeyBinder::event(QEvent* event) +{ + switch (event->type()) + { + case QEvent::Enter: + case QEvent::Leave: + { + update(); + break; + } + default: + { + break; + } + } + return QLabel::event(event); +} + +void ElaKeyBinder::mouseReleaseEvent(QMouseEvent* event) +{ + Q_D(ElaKeyBinder); + if (event->button() == Qt::LeftButton) + { + d->_binderDialog->show(); + d->_binderContainer->setFocus(); + d->_binderContainer->logOrResetHistoryData(true); + } + QLabel::mouseReleaseEvent(event); +} + +void ElaKeyBinder::paintEvent(QPaintEvent* event) +{ + Q_D(ElaKeyBinder); + QPainter painter(this); + painter.save(); + painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + painter.setPen(ElaThemeColor(d->_themeMode, BasicBorder)); + painter.setBrush(underMouse() ? ElaThemeColor(d->_themeMode, BasicHover) : Qt::transparent); + QRect borderRect = rect(); + borderRect.adjust(1, 1, -1, -1); + painter.drawRoundedRect(borderRect, d->_pBorderRadius, d->_pBorderRadius); + // 图标绘制 + QFont iconFont = QFont("ElaAwesome"); + iconFont.setPixelSize(16); + painter.setFont(iconFont); + painter.setPen(ElaThemeColor(d->_themeMode, BasicText)); + QRect iconRect = rect(); + iconRect.adjust(0, 0, -10, 0); + painter.drawText(iconRect, Qt::AlignVCenter | Qt::AlignRight, QChar((unsigned short)ElaIconType::Pencil)); + painter.restore(); + QLabel::paintEvent(event); +} \ No newline at end of file diff --git a/3rdparty/ElaWidgetTools/src/ElaLCDNumber.cpp b/3rdparty/ElaWidgetTools/src/ElaLCDNumber.cpp new file mode 100644 index 0000000..4a37f10 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaLCDNumber.cpp @@ -0,0 +1,90 @@ +#include "ElaLCDNumber.h" +#include "ElaLCDNumberPrivate.h" +#include "ElaLCDNumberStyle.h" +#include "ElaTheme.h" +#include +#include +#include +ElaLCDNumber::ElaLCDNumber(QWidget* parent) + : QLCDNumber(parent), d_ptr(new ElaLCDNumberPrivate) +{ + Q_D(ElaLCDNumber); + d->q_ptr = this; + d->_pIsUseAutoClock = false; + d->_pAutoClockFormat = "yyyy-MM-dd hh:mm:ss"; + setDigitCount(d->_pAutoClockFormat.length()); + setSegmentStyle(QLCDNumber::Flat); + setObjectName("ElaLCDNumber"); + setStyleSheet("#ElaLCDNumber{background-color:transparent;}"); + d->_lcdNumberStyle = new ElaLCDNumberStyle(); + setStyle(d->_lcdNumberStyle); + d->_clockTimer = new QTimer(this); + connect(d->_clockTimer, &QTimer::timeout, this, [=]() { + display(QDateTime::currentDateTime().toString(d->_pAutoClockFormat)); + }); + + d->onThemeModeChanged(eTheme->getThemeMode()); + connect(eTheme, &ElaTheme::themeModeChanged, d, &ElaLCDNumberPrivate::onThemeModeChanged); +} + +ElaLCDNumber::ElaLCDNumber(uint numDigits, QWidget* parent) + : ElaLCDNumber(parent) +{ + setDigitCount(numDigits); +} + +ElaLCDNumber::~ElaLCDNumber() +{ +} + +void ElaLCDNumber::setIsUseAutoClock(bool isUseAutoClock) +{ + Q_D(ElaLCDNumber); + d->_pIsUseAutoClock = isUseAutoClock; + if (d->_pIsUseAutoClock) + { + setDigitCount(d->_pAutoClockFormat.length()); + display(QDateTime::currentDateTime().toString(d->_pAutoClockFormat)); + d->_clockTimer->start(200); + } + else + { + d->_clockTimer->stop(); + display(""); + } + Q_EMIT pIsUseAutoClockChanged(); +} + +bool ElaLCDNumber::getIsUseAutoClock() const +{ + Q_D(const ElaLCDNumber); + return d->_pIsUseAutoClock; +} + +void ElaLCDNumber::setAutoClockFormat(QString autoClockFormat) +{ + Q_D(ElaLCDNumber); + d->_pAutoClockFormat = autoClockFormat; + setDigitCount(d->_pAutoClockFormat.length()); + Q_EMIT pAutoClockFormatChanged(); +} + +QString ElaLCDNumber::getAutoClockFormat() const +{ + Q_D(const ElaLCDNumber); + return d->_pAutoClockFormat; +} + +void ElaLCDNumber::setIsTransparent(bool isTransparent) +{ + Q_D(ElaLCDNumber); + d->_lcdNumberStyle->setIsTransparent(isTransparent); + update(); + Q_EMIT pIsTransparentChanged(); +} + +bool ElaLCDNumber::getIsTransparent() const +{ + Q_D(const ElaLCDNumber); + return d->_lcdNumberStyle->getIsTransparent(); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaLineEdit.cpp b/3rdparty/ElaWidgetTools/src/ElaLineEdit.cpp new file mode 100644 index 0000000..c8b80d6 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaLineEdit.cpp @@ -0,0 +1,178 @@ +#include "ElaLineEdit.h" + +#include +#include +#include +#include +#include +#include + +#include "ElaEventBus.h" +#include "ElaLineEditStyle.h" +#include "ElaMenu.h" +#include "ElaTheme.h" +#include "private/ElaLineEditPrivate.h" +Q_PROPERTY_CREATE_Q_CPP(ElaLineEdit, int, BorderRadius) + +ElaLineEdit::ElaLineEdit(QWidget* parent) + : QLineEdit(parent), d_ptr(new ElaLineEditPrivate()) +{ + Q_D(ElaLineEdit); + d->q_ptr = this; + setObjectName("ElaLineEdit"); + d->_themeMode = eTheme->getThemeMode(); + d->_pBorderRadius = 6; + d->_pExpandMarkWidth = 0; + d->_pIsClearButtonEnable = true; + setFocusPolicy(Qt::StrongFocus); + // 事件总线 + d->_focusEvent = new ElaEvent("WMWindowClicked", "onWMWindowClickedEvent", d); + d->_focusEvent->registerAndInit(); + setMouseTracking(true); + QFont textFont = font(); + textFont.setLetterSpacing(QFont::AbsoluteSpacing, d->_textSpacing); + setFont(textFont); + setStyle(new ElaLineEditStyle(style())); + setStyleSheet("#ElaLineEdit{padding-left: 10px;}"); + d->onThemeChanged(eTheme->getThemeMode()); + connect(eTheme, &ElaTheme::themeModeChanged, d, &ElaLineEditPrivate::onThemeChanged); + setVisible(true); +} + +ElaLineEdit::~ElaLineEdit() +{ +} + +void ElaLineEdit::setIsClearButtonEnable(bool isClearButtonEnable) +{ + Q_D(ElaLineEdit); + d->_pIsClearButtonEnable = isClearButtonEnable; + setClearButtonEnabled(isClearButtonEnable); + Q_EMIT pIsClearButtonEnableChanged(); +} + +bool ElaLineEdit::getIsClearButtonEnable() const +{ + Q_D(const ElaLineEdit); + return d->_pIsClearButtonEnable; +} + +void ElaLineEdit::focusInEvent(QFocusEvent* event) +{ + Q_D(ElaLineEdit); + Q_EMIT focusIn(this->text()); + if (event->reason() == Qt::MouseFocusReason) + { + if (d->_pIsClearButtonEnable) + { + setClearButtonEnabled(true); + } + QPropertyAnimation* markAnimation = new QPropertyAnimation(d, "pExpandMarkWidth"); + connect(markAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + update(); + }); + markAnimation->setDuration(300); + markAnimation->setEasingCurve(QEasingCurve::InOutSine); + markAnimation->setStartValue(d->_pExpandMarkWidth); + markAnimation->setEndValue(width() / 2 - d->_pBorderRadius / 2); + markAnimation->start(QAbstractAnimation::DeleteWhenStopped); + } + QLineEdit::focusInEvent(event); +} + +void ElaLineEdit::focusOutEvent(QFocusEvent* event) +{ + Q_D(ElaLineEdit); + Q_EMIT focusOut(this->text()); + if (event->reason() != Qt::PopupFocusReason) + { + if (d->_pIsClearButtonEnable) + { + setClearButtonEnabled(false); + } + QPropertyAnimation* markAnimation = new QPropertyAnimation(d, "pExpandMarkWidth"); + connect(markAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + update(); + }); + markAnimation->setDuration(300); + markAnimation->setEasingCurve(QEasingCurve::InOutSine); + markAnimation->setStartValue(d->_pExpandMarkWidth); + markAnimation->setEndValue(0); + markAnimation->start(QAbstractAnimation::DeleteWhenStopped); + Q_EMIT wmFocusOut(text()); + } + QLineEdit::focusOutEvent(event); +} + +void ElaLineEdit::paintEvent(QPaintEvent* event) +{ + Q_D(ElaLineEdit); + QLineEdit::paintEvent(event); + QPainter painter(this); + painter.save(); + painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + painter.setPen(Qt::NoPen); + painter.setBrush(ElaThemeColor(d->_themeMode, PrimaryNormal)); + painter.drawRoundedRect(QRectF(width() / 2 - d->_pExpandMarkWidth, height() - 2.5, d->_pExpandMarkWidth * 2, 2.5), 2, 2); + painter.restore(); +} + +void ElaLineEdit::contextMenuEvent(QContextMenuEvent* event) +{ + ElaMenu* menu = new ElaMenu(this); + menu->setMenuItemHeight(27); + menu->setAttribute(Qt::WA_DeleteOnClose); + QAction* action = nullptr; + if (!isReadOnly()) + { + action = menu->addElaIconAction(ElaIconType::ArrowRotateLeft, tr("撤销"), QKeySequence::Undo); + action->setEnabled(isUndoAvailable()); + connect(action, &QAction::triggered, this, &ElaLineEdit::undo); + + action = menu->addElaIconAction(ElaIconType::ArrowRotateRight, tr("恢复"), QKeySequence::Redo); + action->setEnabled(isRedoAvailable()); + connect(action, &QAction::triggered, this, &ElaLineEdit::redo); + menu->addSeparator(); + } +#ifndef QT_NO_CLIPBOARD + if (!isReadOnly()) + { + action = menu->addElaIconAction(ElaIconType::KnifeKitchen, tr("剪切"), QKeySequence::Cut); + action->setEnabled(!isReadOnly() && hasSelectedText() && echoMode() == QLineEdit::Normal); + connect(action, &QAction::triggered, this, &ElaLineEdit::cut); + } + + action = menu->addElaIconAction(ElaIconType::Copy, tr("复制"), QKeySequence::Copy); + action->setEnabled(hasSelectedText() && echoMode() == QLineEdit::Normal); + connect(action, &QAction::triggered, this, &ElaLineEdit::copy); + + if (!isReadOnly()) + { + action = menu->addElaIconAction(ElaIconType::Paste, tr("粘贴"), QKeySequence::Paste); + action->setEnabled(!isReadOnly() && !QGuiApplication::clipboard()->text().isEmpty()); + connect(action, &QAction::triggered, this, &ElaLineEdit::paste); + } +#endif + if (!isReadOnly()) + { + action = menu->addElaIconAction(ElaIconType::DeleteLeft, tr("删除")); + action->setEnabled(!isReadOnly() && !text().isEmpty() && hasSelectedText()); + connect(action, &QAction::triggered, this, [=](bool checked) { + if (hasSelectedText()) + { + int startIndex = selectionStart(); + int endIndex = selectionEnd(); + setText(text().remove(startIndex, endIndex - startIndex)); + } + }); + } + if (!menu->isEmpty()) + { + menu->addSeparator(); + } + action = menu->addAction(tr("全选")); + action->setShortcut(QKeySequence::SelectAll); + action->setEnabled(!text().isEmpty() && !(selectedText() == text())); + connect(action, &QAction::triggered, this, &ElaLineEdit::selectAll); + menu->popup(event->globalPos()); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaListView.cpp b/3rdparty/ElaWidgetTools/src/ElaListView.cpp new file mode 100644 index 0000000..e01891f --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaListView.cpp @@ -0,0 +1,57 @@ +#include "ElaListView.h" + +#include + +#include "ElaListViewPrivate.h" +#include "ElaListViewStyle.h" +#include "ElaScrollBar.h" +ElaListView::ElaListView(QWidget* parent) + : QListView(parent), d_ptr(new ElaListViewPrivate()) +{ + Q_D(ElaListView); + d->q_ptr = this; + setObjectName("ElaListView"); + setStyleSheet("#ElaListView{background-color:transparent;}"); + d->_listViewStyle = new ElaListViewStyle(style()); + setStyle(d->_listViewStyle); + setMouseTracking(true); + setVerticalScrollBar(new ElaScrollBar(this)); + setHorizontalScrollBar(new ElaScrollBar(this)); + this->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + this->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); + setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); +} + +ElaListView::~ElaListView() +{ +} + +void ElaListView::setItemHeight(int itemHeight) +{ + Q_D(ElaListView); + if (itemHeight > 0) + { + d->_listViewStyle->setItemHeight(itemHeight); + doItemsLayout(); + } +} + +int ElaListView::getItemHeight() const +{ + Q_D(const ElaListView); + return d->_listViewStyle->getItemHeight(); +} + +void ElaListView::setIsTransparent(bool isTransparent) +{ + Q_D(ElaListView); + d->_listViewStyle->setIsTransparent(isTransparent); + update(); +} + +bool ElaListView::getIsTransparent() const +{ + Q_D(const ElaListView); + return d->_listViewStyle->getIsTransparent(); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaLog.cpp b/3rdparty/ElaWidgetTools/src/ElaLog.cpp new file mode 100644 index 0000000..fcb42f8 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaLog.cpp @@ -0,0 +1,33 @@ +#include "ElaLog.h" + +#include + +#include "ElaLogPrivate.h" + +Q_SINGLETON_CREATE_CPP(ElaLog) +Q_PROPERTY_CREATE_Q_CPP(ElaLog, QString, LogSavePath) +Q_PROPERTY_CREATE_Q_CPP(ElaLog, QString, LogFileName) +Q_PROPERTY_CREATE_Q_CPP(ElaLog, bool, IsLogFileNameWithTime) +ElaLog::ElaLog(QObject* parent) + : QObject{parent}, d_ptr(new ElaLogPrivate()) +{ + Q_D(ElaLog); + d->q_ptr = this; + d->_pLogFileName = "ElaLog"; + d->_pLogSavePath = QDir::currentPath(); + d->_pIsLogFileNameWithTime = false; + d->_clearLogFile(); + connect(this, &ElaLog::pLogSavePathChanged, d, &ElaLogPrivate::_clearLogFile); + connect(this, &ElaLog::pLogFileNameChanged, d, &ElaLogPrivate::_clearLogFile); + connect(this, &ElaLog::pIsLogFileNameWithTimeChanged, d, &ElaLogPrivate::_clearLogFile); +} + +ElaLog::~ElaLog() +{ +} + +void ElaLog::initMessageLog(bool isEnable) +{ + Q_D(ElaLog); + qInstallMessageHandler(isEnable ? d->_messageLogHander : 0); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaMenu.cpp b/3rdparty/ElaWidgetTools/src/ElaMenu.cpp new file mode 100644 index 0000000..02a2440 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaMenu.cpp @@ -0,0 +1,193 @@ +#include "ElaMenu.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "DeveloperComponents/ElaMenuStyle.h" +#include "private/ElaMenuPrivate.h" +ElaMenu::ElaMenu(QWidget* parent) + : QMenu(parent), d_ptr(new ElaMenuPrivate()) +{ + Q_D(ElaMenu); + d->q_ptr = this; + setWindowFlags(Qt::Popup | Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint); + setAttribute(Qt::WA_TranslucentBackground); + setObjectName("ElaMenu"); + d->_menuStyle = new ElaMenuStyle(style()); + setStyle(d->_menuStyle); + d->_pAnimationImagePosY = 0; +} + +ElaMenu::ElaMenu(const QString& title, QWidget* parent) + : ElaMenu(parent) +{ + setTitle(title); +} + +ElaMenu::~ElaMenu() +{ +} + +void ElaMenu::setMenuItemHeight(int menuItemHeight) +{ + Q_D(ElaMenu); + d->_menuStyle->setMenuItemHeight(menuItemHeight); +} + +int ElaMenu::getMenuItemHeight() const +{ + Q_D(const ElaMenu); + return d->_menuStyle->getMenuItemHeight(); +} + +QAction* ElaMenu::addMenu(QMenu* menu) +{ + return QMenu::addMenu(menu); +} + +ElaMenu* ElaMenu::addMenu(const QString& title) +{ + ElaMenu* menu = new ElaMenu(title, this); + QMenu::addAction(menu->menuAction()); + return menu; +} + +ElaMenu* ElaMenu::addMenu(const QIcon& icon, const QString& title) +{ + ElaMenu* menu = new ElaMenu(title, this); + menu->setIcon(icon); + QMenu::addAction(menu->menuAction()); + return menu; +} + +ElaMenu* ElaMenu::addMenu(ElaIconType::IconName icon, const QString& title) +{ + ElaMenu* menu = new ElaMenu(title, this); + QMenu::addAction(menu->menuAction()); + menu->menuAction()->setProperty("ElaIconType", QChar((unsigned short)icon)); + return menu; +} + +QAction* ElaMenu::addElaIconAction(ElaIconType::IconName icon, const QString& text) +{ + QAction* action = new QAction(text, this); + action->setProperty("ElaIconType", QChar((unsigned short)icon)); + QMenu::addAction(action); + return action; +} + +QAction* ElaMenu::addElaIconAction(ElaIconType::IconName icon, const QString& text, const QKeySequence& shortcut) +{ + QAction* action = new QAction(text, this); + action->setShortcut(shortcut); + action->setProperty("ElaIconType", QChar((unsigned short)icon)); + QMenu::addAction(action); + return action; +} + +bool ElaMenu::isHasChildMenu() const +{ + QList actionList = this->actions(); + for (auto action : actionList) + { + if (action->isSeparator()) + { + continue; + } + if (action->menu()) + { + return true; + } + } + return false; +} + +bool ElaMenu::isHasIcon() const +{ + QList actionList = this->actions(); + for (auto action : actionList) + { + if (action->isSeparator()) + { + continue; + } + QMenu* menu = action->menu(); + if (menu && (!menu->icon().isNull() || !menu->property("ElaIconType").toString().isEmpty())) + { + return true; + } + if (!action->icon().isNull() || !action->property("ElaIconType").toString().isEmpty()) + { + return true; + } + } + return false; +} + +void ElaMenu::showEvent(QShowEvent* event) +{ + Q_EMIT menuShow(); + Q_D(ElaMenu); + //消除阴影偏移 + move(this->pos().x() - 6, this->pos().y()); + if (!d->_animationPix.isNull()) + { + d->_animationPix = QPixmap(); + } + d->_animationPix = this->grab(this->rect()); + QPropertyAnimation* posAnimation = new QPropertyAnimation(d, "pAnimationImagePosY"); + connect(posAnimation, &QPropertyAnimation::finished, this, [=]() { + d->_animationPix = QPixmap(); + update(); + }); + connect(posAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + update(); + }); + posAnimation->setEasingCurve(QEasingCurve::OutCubic); + posAnimation->setDuration(400); + int targetPosY = height(); + if (targetPosY > 160) + { + if (targetPosY < 320) + { + targetPosY = 160; + } + else + { + targetPosY /= 2; + } + } + + if (pos().y() + d->_menuStyle->getMenuItemHeight() + 9 >= QCursor::pos().y()) + { + posAnimation->setStartValue(-targetPosY); + } + else + { + posAnimation->setStartValue(targetPosY); + } + + posAnimation->setEndValue(0); + posAnimation->start(QAbstractAnimation::DeleteWhenStopped); + QMenu::showEvent(event); +} + +void ElaMenu::paintEvent(QPaintEvent* event) +{ + Q_D(ElaMenu); + QPainter painter(this); + painter.setRenderHints(QPainter::Antialiasing); + if (!d->_animationPix.isNull()) + { + painter.drawPixmap(QRect(0, d->_pAnimationImagePosY, width(), height()), d->_animationPix); + } + else + { + QMenu::paintEvent(event); + } +} diff --git a/3rdparty/ElaWidgetTools/src/ElaMenuBar.cpp b/3rdparty/ElaWidgetTools/src/ElaMenuBar.cpp new file mode 100644 index 0000000..551495a --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaMenuBar.cpp @@ -0,0 +1,79 @@ +#include "ElaMenuBar.h" + +#include +#include +#include + +#include "ElaIcon.h" +#include "ElaMenu.h" +#include "ElaMenuBarStyle.h" +ElaMenuBar::ElaMenuBar(QWidget* parent) + : QMenuBar(parent) +{ + setMouseTracking(true); + setObjectName("ElaMenuBar"); + setStyle(new ElaMenuBarStyle(style())); + QToolButton* tool = this->findChild(); + if (tool->objectName() == "qt_menubar_ext_button") + { + QMenu* oldMenu = tool->menu(); + ElaMenu* menu = new ElaMenu(this); + menu->setObjectName("ElaExtendMenu"); + menu->setMenuItemHeight(27); + if (oldMenu) + { + oldMenu->deleteLater(); + } + tool->setMenu(menu); + } +} + +ElaMenuBar::~ElaMenuBar() +{ +} + +QAction* ElaMenuBar::addMenu(QMenu* menu) +{ + ElaMenu* elaMenu = dynamic_cast(menu); + if (elaMenu) + { + elaMenu->setMenuItemHeight(27); + } + return QMenuBar::addMenu(menu); +} + +ElaMenu* ElaMenuBar::addMenu(const QString& title) +{ + ElaMenu* menu = new ElaMenu(title, this); + menu->setMenuItemHeight(27); + QMenuBar::addAction(menu->menuAction()); + return menu; +} + +ElaMenu* ElaMenuBar::addMenu(const QIcon& icon, const QString& title) +{ + ElaMenu* menu = new ElaMenu(title, this); + menu->setMenuItemHeight(27); + menu->setIcon(icon); + QMenuBar::addAction(menu->menuAction()); + return menu; +} + +ElaMenu* ElaMenuBar::addMenu(ElaIconType::IconName icon, const QString& title) +{ + ElaMenu* menu = new ElaMenu(title, this); + menu->setMenuItemHeight(27); + menu->menuAction()->setProperty("ElaIconType", QChar((unsigned short)icon)); + menu->menuAction()->setIcon(ElaIcon::getInstance()->getElaIcon(ElaIconType::Broom, 1)); + QMenuBar::addAction(menu->menuAction()); + return menu; +} + +QAction* ElaMenuBar::addElaIconAction(ElaIconType::IconName icon, const QString& text) +{ + QAction* action = new QAction(text, this); + action->setProperty("ElaIconType", QChar((unsigned short)icon)); + action->setIcon(ElaIcon::getInstance()->getElaIcon(ElaIconType::Broom, 1)); + QMenuBar::addAction(action); + return action; +} diff --git a/3rdparty/ElaWidgetTools/src/ElaMessageBar.cpp b/3rdparty/ElaWidgetTools/src/ElaMessageBar.cpp new file mode 100644 index 0000000..5e75791 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaMessageBar.cpp @@ -0,0 +1,283 @@ +#include "ElaMessageBar.h" + +#include +#include +#include +#include +#include +#include + +#include "ElaIconButton.h" +#include "ElaTheme.h" +#include "private/ElaMessageBarPrivate.h" + +ElaMessageBar::ElaMessageBar(ElaMessageBarType::PositionPolicy policy, ElaMessageBarType::MessageMode messageMode, QString& title, QString& text, int displayMsec, QWidget* parent) + : QWidget{parent}, d_ptr(new ElaMessageBarPrivate()) +{ + Q_D(ElaMessageBar); + d->q_ptr = this; + d->_borderRadius = 6; + d->_title = title; + d->_text = text; + d->_policy = policy; + d->_messageMode = messageMode; + d->_themeMode = eTheme->getThemeMode(); + setFixedHeight(60); + setMouseTracking(true); + d->_pOpacity = 1; + setFont(QFont("微软雅黑")); + parent->installEventFilter(this); + d->_closeButton = new ElaIconButton(ElaIconType::Xmark, 17, d->_closeButtonWidth, 30, this); + switch (d->_messageMode) + { + case ElaMessageBarType::Success: + { + d->_closeButton->setLightHoverColor(QColor(0xE6, 0xFC, 0xE3)); + d->_closeButton->setDarkHoverColor(QColor(0xE6, 0xFC, 0xE3)); + d->_closeButton->setDarkIconColor(Qt::black); + break; + } + case ElaMessageBarType::Warning: + { + d->_closeButton->setLightHoverColor(QColor(0x5E, 0x4C, 0x22)); + d->_closeButton->setDarkHoverColor(QColor(0x5E, 0x4C, 0x22)); + d->_closeButton->setLightIconColor(Qt::white); + d->_closeButton->setDarkIconColor(Qt::white); + break; + } + case ElaMessageBarType::Information: + { + d->_closeButton->setLightHoverColor(QColor(0xEB, 0xEB, 0xEB)); + d->_closeButton->setDarkHoverColor(QColor(0xEB, 0xEB, 0xEB)); + d->_closeButton->setDarkIconColor(Qt::black); + break; + } + case ElaMessageBarType::Error: + { + d->_closeButton->setLightHoverColor(QColor(0xF7, 0xE1, 0xE4)); + d->_closeButton->setDarkHoverColor(QColor(0xF7, 0xE1, 0xE4)); + d->_closeButton->setDarkIconColor(Qt::black); + break; + } + } + d->_closeButton->setBorderRadius(5); + connect(d->_closeButton, &ElaIconButton::clicked, d, &ElaMessageBarPrivate::onCloseButtonClicked); + QHBoxLayout* mainLayout = new QHBoxLayout(this); + mainLayout->setContentsMargins(0, 0, 10, 0); + mainLayout->addStretch(); + mainLayout->addWidget(d->_closeButton); + setObjectName("ElaMessageBar"); + setStyleSheet("#ElaMessageBar{background-color:transparent;}"); + d->_messageBarCreate(displayMsec); +} + +ElaMessageBar::~ElaMessageBar() +{ +} + +void ElaMessageBar::success(ElaMessageBarType::PositionPolicy policy, QString title, QString text, int displayMsec, QWidget* parent) +{ + // qDebug() << QApplication::topLevelWidgets(); + if (!parent) + { + QList widgetList = QApplication::topLevelWidgets(); + for (auto widget : widgetList) + { + if (widget->property("ElaBaseClassName").toString() == "ElaWindow") + { + parent = widget; + } + } + if (!parent) + { + return; + } + } + + ElaMessageBar* bar = new ElaMessageBar(policy, ElaMessageBarType::Success, title, text, displayMsec, parent); + Q_UNUSED(bar); +} + +void ElaMessageBar::warning(ElaMessageBarType::PositionPolicy policy, QString title, QString text, int displayMsec, QWidget* parent) +{ + if (!parent) + { + QList widgetList = QApplication::topLevelWidgets(); + for (auto widget : widgetList) + { + if (widget->property("ElaBaseClassName").toString() == "ElaWindow") + { + parent = widget; + } + } + if (!parent) + { + return; + } + } + ElaMessageBar* bar = new ElaMessageBar(policy, ElaMessageBarType::Warning, title, text, displayMsec, parent); + Q_UNUSED(bar); +} + +void ElaMessageBar::information(ElaMessageBarType::PositionPolicy policy, QString title, QString text, int displayMsec, QWidget* parent) +{ + if (!parent) + { + QList widgetList = QApplication::topLevelWidgets(); + for (auto widget : widgetList) + { + if (widget->property("ElaBaseClassName").toString() == "ElaWindow") + { + parent = widget; + } + } + if (!parent) + { + return; + } + } + ElaMessageBar* bar = new ElaMessageBar(policy, ElaMessageBarType::Information, title, text, displayMsec, parent); + Q_UNUSED(bar); +} + +void ElaMessageBar::error(ElaMessageBarType::PositionPolicy policy, QString title, QString text, int displayMsec, QWidget* parent) +{ + if (!parent) + { + QList widgetList = QApplication::topLevelWidgets(); + for (auto widget : widgetList) + { + if (widget->property("ElaBaseClassName").toString() == "ElaWindow") + { + parent = widget; + } + } + if (!parent) + { + return; + } + } + ElaMessageBar* bar = new ElaMessageBar(policy, ElaMessageBarType::Error, title, text, displayMsec, parent); + Q_UNUSED(bar); +} + +void ElaMessageBar::paintEvent(QPaintEvent* event) +{ + Q_D(ElaMessageBar); + QPainter painter(this); + painter.setOpacity(d->_pOpacity); + painter.setRenderHints(QPainter::SmoothPixmapTransform | QPainter::Antialiasing | QPainter::TextAntialiasing); + // 高性能阴影 + eTheme->drawEffectShadow(&painter, rect(), d->_shadowBorderWidth, d->_borderRadius); + + // 背景和图标绘制 + painter.save(); + painter.setPen(d->_themeMode == ElaThemeType::Light ? QColor(0xBE, 0xBA, 0xBE) : QColor(0x52, 0x50, 0x52)); + switch (d->_messageMode) + { + case ElaMessageBarType::Success: + { + d->_drawSuccess(&painter); + break; + } + case ElaMessageBarType::Warning: + { + d->_drawWarning(&painter); + break; + } + case ElaMessageBarType::Information: + { + d->_drawInformation(&painter); + break; + } + case ElaMessageBarType::Error: + { + d->_drawError(&painter); + break; + } + } + // 文字绘制 + // 标题 + QFont font = this->font(); + font.setWeight(QFont::Bold); + font.setPixelSize(16); + painter.setFont(font); + int titleTextWidth = painter.fontMetrics().horizontalAdvance(d->_title) + 1; + if (titleTextWidth > 100) + { + titleTextWidth = 100; + } + int textFlags = Qt::AlignLeft | Qt::AlignVCenter | Qt::TextWordWrap | Qt::TextWrapAnywhere; + painter.drawText(QRect(d->_leftPadding + d->_titleLeftSpacing, -1, titleTextWidth, height()), textFlags, d->_title); + // 正文 + font.setWeight(QFont::Light); + font.setPixelSize(15); + painter.setFont(font); + painter.drawText(QRect(d->_leftPadding + d->_titleLeftSpacing + titleTextWidth + d->_textLeftSpacing, 0, width() - (d->_leftPadding + d->_titleLeftSpacing + titleTextWidth + d->_textLeftSpacing + d->_closeButtonWidth + d->_closeButtonLeftRightMargin / 2), height()), textFlags, d->_text); + int textHeight = painter.fontMetrics().boundingRect(QRect(d->_leftPadding + d->_titleLeftSpacing + titleTextWidth + d->_textLeftSpacing, 0, width() - (d->_leftPadding + d->_titleLeftSpacing + titleTextWidth + d->_textLeftSpacing + d->_closeButtonWidth + d->_closeButtonLeftRightMargin), height()), textFlags, d->_text).height(); + if (textHeight >= minimumHeight() - 20) + { + setMinimumHeight(textHeight + 20); + } + painter.restore(); +} + +bool ElaMessageBar::eventFilter(QObject* watched, QEvent* event) +{ + Q_D(ElaMessageBar); + if (watched == parentWidget()) + { + switch (event->type()) + { + case QEvent::Resize: + { + QResizeEvent* resizeEvent = dynamic_cast(event); + QSize offsetSize = parentWidget()->size() - resizeEvent->oldSize(); + if (d->_isNormalDisplay) + { + switch (d->_policy) + { + case ElaMessageBarType::Top: + { + this->move(parentWidget()->width() / 2 - minimumWidth() / 2, this->y()); + break; + } + case ElaMessageBarType::Bottom: + { + this->move(parentWidget()->width() / 2 - minimumWidth() / 2, this->pos().y() + offsetSize.height()); + break; + } + case ElaMessageBarType::Left: + case ElaMessageBarType::TopLeft: + { + this->move(d->_messageBarHorizontalMargin, this->pos().y()); + break; + } + case ElaMessageBarType::BottomLeft: + { + this->move(d->_messageBarHorizontalMargin, this->pos().y() + offsetSize.height()); + break; + } + case ElaMessageBarType::Right: + case ElaMessageBarType::TopRight: + { + this->move(parentWidget()->width() - minimumWidth() - d->_messageBarHorizontalMargin, this->y()); + break; + } + case ElaMessageBarType::BottomRight: + { + this->move(parentWidget()->width() - minimumWidth() - d->_messageBarHorizontalMargin, this->pos().y() + offsetSize.height()); + break; + } + } + } + break; + } + default: + { + break; + } + } + } + return QWidget::eventFilter(watched, event); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaMessageButton.cpp b/3rdparty/ElaWidgetTools/src/ElaMessageButton.cpp new file mode 100644 index 0000000..d6cc9a4 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaMessageButton.cpp @@ -0,0 +1,112 @@ +#include "ElaMessageButton.h" + +#include +#include +#include + +#include "ElaMessageBar.h" +#include "ElaTheme.h" +#include "private/ElaMessageButtonPrivate.h" +Q_PROPERTY_CREATE_Q_CPP(ElaMessageButton, int, BorderRadius) +Q_PROPERTY_CREATE_Q_CPP(ElaMessageButton, QString, BarTitle); +Q_PROPERTY_CREATE_Q_CPP(ElaMessageButton, QString, BarText); +Q_PROPERTY_CREATE_Q_CPP(ElaMessageButton, int, DisplayMsec); +Q_PROPERTY_CREATE_Q_CPP(ElaMessageButton, QWidget*, MessageTargetWidget) +Q_PROPERTY_CREATE_Q_CPP(ElaMessageButton, ElaMessageBarType::MessageMode, MessageMode); +Q_PROPERTY_CREATE_Q_CPP(ElaMessageButton, ElaMessageBarType::PositionPolicy, PositionPolicy); + +ElaMessageButton::ElaMessageButton(QWidget* parent) + : QPushButton(parent), d_ptr(new ElaMessageButtonPrivate()) +{ + Q_D(ElaMessageButton); + d->q_ptr = this; + d->_pBorderRadius = 3; + setMouseTracking(true); + setFixedSize(80, 38); + QFont font = this->font(); + font.setPixelSize(15); + setFont(font); + setText("Message"); + setObjectName("ElaMessageButton"); + setStyleSheet("#ElaMessageButton{background-color:transparent;}"); + d->_pDisplayMsec = 2000; + d->_pMessageMode = ElaMessageBarType::Success; + d->_pPositionPolicy = ElaMessageBarType::TopRight; + d->_themeMode = eTheme->getThemeMode(); + d->_pMessageTargetWidget = nullptr; + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { + d->_themeMode = themeMode; + }); + connect(this, &ElaMessageButton::clicked, this, [=]() { + switch(d->_pMessageMode) + { + case ElaMessageBarType::Success: + { + ElaMessageBar::success(d->_pPositionPolicy,d->_pBarTitle,d->_pBarText,d->_pDisplayMsec,d->_pMessageTargetWidget); + break; + } + case ElaMessageBarType::Warning: + { + ElaMessageBar::warning(d->_pPositionPolicy,d->_pBarTitle,d->_pBarText,d->_pDisplayMsec,d->_pMessageTargetWidget); + break; + } + case ElaMessageBarType::Information: + { + ElaMessageBar::information(d->_pPositionPolicy,d->_pBarTitle,d->_pBarText,d->_pDisplayMsec,d->_pMessageTargetWidget); + break; + } + case ElaMessageBarType::Error: + { + ElaMessageBar::error(d->_pPositionPolicy,d->_pBarTitle,d->_pBarText,d->_pDisplayMsec,d->_pMessageTargetWidget); + break; + } + } }); +} + +ElaMessageButton::ElaMessageButton(QString text, QWidget* parent) + : ElaMessageButton(parent) +{ + setText(text); +} + +ElaMessageButton::~ElaMessageButton() +{ +} + +void ElaMessageButton::mousePressEvent(QMouseEvent* event) +{ + Q_D(ElaMessageButton); + if (event->button() == Qt::LeftButton) + { + d->_isLeftButtonPress = true; + } + QPushButton::mousePressEvent(event); +} + +void ElaMessageButton::mouseReleaseEvent(QMouseEvent* event) +{ + Q_D(ElaMessageButton); + d->_isLeftButtonPress = false; + QPushButton::mouseReleaseEvent(event); +} + +void ElaMessageButton::paintEvent(QPaintEvent* event) +{ + Q_D(ElaMessageButton); + QPainter painter(this); + painter.setRenderHints(QPainter::SmoothPixmapTransform | QPainter::Antialiasing | QPainter::TextAntialiasing); + // 高性能阴影 + eTheme->drawEffectShadow(&painter, rect(), d->_shadowBorderWidth, d->_pBorderRadius); + + // 背景绘制 + painter.save(); + QRect foregroundRect(d->_penBorderWidth + d->_shadowBorderWidth, d->_penBorderWidth + d->_shadowBorderWidth, width() - 2 * (d->_penBorderWidth + d->_shadowBorderWidth), height() - 2 * (d->_penBorderWidth + d->_shadowBorderWidth)); + painter.setPen(QPen(ElaThemeColor(d->_themeMode, BasicBorder), d->_penBorderWidth)); + painter.setBrush(isEnabled() ? underMouse() ? ElaThemeColor(d->_themeMode, BasicHover) : ElaThemeColor(d->_themeMode, BasicBase) : ElaThemeColor(d->_themeMode, BasicDisable)); + painter.drawRoundedRect(foregroundRect, d->_pBorderRadius, d->_pBorderRadius); + + //文字绘制 + painter.setPen(isEnabled() ? d->_isLeftButtonPress ? ElaThemeColor(d->_themeMode, BasicTextPress) : ElaThemeColor(d->_themeMode, BasicText) : ElaThemeColor(d->_themeMode, BasicTextDisable)); + painter.drawText(rect(), Qt::AlignCenter, text()); + painter.restore(); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaMultiSelectComboBox.cpp b/3rdparty/ElaWidgetTools/src/ElaMultiSelectComboBox.cpp new file mode 100644 index 0000000..bafe0d5 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaMultiSelectComboBox.cpp @@ -0,0 +1,342 @@ +#include "ElaMultiSelectComboBox.h" + +#include +#include +#include +#include +#include +#include + +#include "DeveloperComponents/ElaComboBoxView.h" +#include "ElaApplication.h" +#include "ElaComboBoxStyle.h" +#include "ElaScrollBar.h" +#include "ElaTheme.h" +#include "private/ElaMultiSelectComboBoxPrivate.h" +Q_PROPERTY_CREATE_Q_CPP(ElaMultiSelectComboBox, int, BorderRadius) +ElaMultiSelectComboBox::ElaMultiSelectComboBox(QWidget* parent) + : QComboBox(parent), d_ptr(new ElaMultiSelectComboBoxPrivate()) +{ + Q_D(ElaMultiSelectComboBox); + d->q_ptr = this; + d->_pBorderRadius = 3; + d->_pExpandIconRotate = 0; + d->_pExpandMarkWidth = 0; + d->_themeMode = eTheme->getThemeMode(); + setFixedHeight(35); + + d->_comboBoxStyle = new ElaComboBoxStyle(style()); + setStyle(d->_comboBoxStyle); + + //调用view 让container初始化 + d->_comboView = new ElaComboBoxView(this); + setView(d->_comboView); + QAbstractItemView* comboBoxView = this->view(); + comboBoxView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + ElaScrollBar* scrollBar = new ElaScrollBar(this); + comboBoxView->setVerticalScrollBar(scrollBar); + ElaScrollBar* floatVScrollBar = new ElaScrollBar(scrollBar, comboBoxView); + floatVScrollBar->setIsAnimation(true); + comboBoxView->setAutoScroll(false); + comboBoxView->setSelectionMode(QAbstractItemView::NoSelection); + comboBoxView->setSelectionBehavior(QAbstractItemView::SelectRows); + comboBoxView->setObjectName("ElaComboBoxView"); + comboBoxView->setStyleSheet("#ElaComboBoxView{background-color:transparent;}"); + comboBoxView->setStyle(d->_comboBoxStyle); + QWidget* container = this->findChild(); + if (container) + { + container->setWindowFlags(Qt::Popup | Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint); + container->setAttribute(Qt::WA_TranslucentBackground); + container->setObjectName("ElaComboBoxContainer"); + container->setStyle(d->_comboBoxStyle); + QLayout* layout = container->layout(); + while (layout->count()) + { + layout->takeAt(0); + } + layout->addWidget(view()); + layout->setContentsMargins(6, 0, 6, 6); +#ifndef Q_OS_WIN + container->setStyleSheet("background-color:transparent;"); +#endif + } + QComboBox::setMaxVisibleItems(5); + connect(d->_comboView, &ElaComboBoxView::itemPressed, d, &ElaMultiSelectComboBoxPrivate::onItemPressed); + connect(this, QOverload::of(&ElaMultiSelectComboBox::currentIndexChanged), d, &ElaMultiSelectComboBoxPrivate::_refreshCurrentIndexs); + d->_itemSelection.resize(32); + d->_itemSelection.fill(false); + d->_itemSelection[0] = true; + QComboBox::setMaxVisibleItems(5); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { d->_themeMode = themeMode; }); +} + +ElaMultiSelectComboBox::~ElaMultiSelectComboBox() +{ +} + +void ElaMultiSelectComboBox::setCurrentSelection(QString selection) +{ + Q_D(ElaMultiSelectComboBox); + d->_itemSelection.fill(false); + d->_comboView->selectionModel()->clearSelection(); + for (int i = 0; i < this->count(); i++) + { + if (selection == itemText(i)) + { + QModelIndex index = model()->index(i, 0); + d->_comboView->selectionModel()->select(index, QItemSelectionModel::Select | QItemSelectionModel::Rows); + d->_itemSelection[index.row()] = true; + } + } + d->_refreshCurrentIndexs(); +} + +void ElaMultiSelectComboBox::setCurrentSelection(QStringList selection) +{ + Q_D(ElaMultiSelectComboBox); + d->_comboView->selectionModel()->clearSelection(); + d->_itemSelection.fill(false); + for (int i = 0; i < this->count(); i++) + { + if (selection.contains(itemText(i))) + { + QModelIndex index = model()->index(i, 0); + d->_comboView->selectionModel()->select(index, QItemSelectionModel::Select | QItemSelectionModel::Rows); + d->_itemSelection[index.row()] = true; + } + } + d->_refreshCurrentIndexs(); +} + +void ElaMultiSelectComboBox::setCurrentSelection(int index) +{ + Q_D(ElaMultiSelectComboBox); + if (index >= this->count() || index < 0) + { + return; + } + d->_comboView->selectionModel()->clearSelection(); + d->_itemSelection.fill(false); + QModelIndex currentIndex = model()->index(index, 0); + d->_comboView->selectionModel()->select(currentIndex, QItemSelectionModel::Select | QItemSelectionModel::Rows); + d->_itemSelection[index] = true; + d->_refreshCurrentIndexs(); +} + +void ElaMultiSelectComboBox::setCurrentSelection(QList selectionIndex) +{ + Q_D(ElaMultiSelectComboBox); + d->_itemSelection.fill(false); + d->_comboView->selectionModel()->clearSelection(); + for (auto index : selectionIndex) + { + if (index >= this->count() || index < 0) + { + continue; + } + QModelIndex currentIndex = model()->index(index, 0); + d->_comboView->selectionModel()->select(currentIndex, QItemSelectionModel::Select | QItemSelectionModel::Rows); + d->_itemSelection[index] = true; + } + d->_refreshCurrentIndexs(); +} + +QStringList ElaMultiSelectComboBox::getCurrentSelection() const +{ + return d_ptr->_selectedTextList; +} + +QList ElaMultiSelectComboBox::getCurrentSelectionIndex() const +{ + QList indexList; + for (int i = 0; i < d_ptr->_itemSelection.count(); i++) + { + if (d_ptr->_itemSelection[i]) + { + indexList.append(i); + } + } + return indexList; +} + +void ElaMultiSelectComboBox::paintEvent(QPaintEvent* e) +{ + Q_D(ElaMultiSelectComboBox); + QPainter painter(this); + painter.save(); + painter.setRenderHints(QPainter::SmoothPixmapTransform | QPainter::Antialiasing | QPainter::TextAntialiasing); + painter.setPen(ElaThemeColor(d->_themeMode, BasicBorder)); + painter.setBrush(isEnabled() ? underMouse() ? ElaThemeColor(d->_themeMode, BasicHover) : ElaThemeColor(d->_themeMode, BasicBase) : Qt::transparent); + QRect foregroundRect = rect(); + foregroundRect.adjust(6, 1, -6, -1); + painter.drawRoundedRect(foregroundRect, d->_pBorderRadius, d->_pBorderRadius); + // 底边线绘制 + painter.setPen(ElaThemeColor(d->_themeMode, BasicBaseLine)); + painter.drawLine(foregroundRect.x() + d->_pBorderRadius, foregroundRect.y() + foregroundRect.height(), foregroundRect.x() + foregroundRect.width() - d->_pBorderRadius, foregroundRect.y() + foregroundRect.height()); + + //文字绘制 + painter.setPen(isEnabled() ? ElaThemeColor(d->_themeMode, BasicText) : ElaThemeColor(d->_themeMode, BasicTextDisable)); + QString currentText = painter.fontMetrics().elidedText(d->_currentText, Qt::ElideRight, foregroundRect.width() - 27 - width() * 0.05); + painter.drawText(15, height() / 2 + painter.fontMetrics().ascent() / 2 - 1, currentText); + //展开指示器绘制 + painter.setPen(Qt::NoPen); + painter.setBrush(d->_themeMode == ElaThemeType::Light ? QColor(0x0E, 0x6F, 0xC3) : QColor(0x4C, 0xA0, 0xE0)); + painter.drawRoundedRect(QRectF(width() / 2 - d->_pExpandMarkWidth, height() - 3, d->_pExpandMarkWidth * 2, 3), 2, 2); + // 展开图标绘制 + if (count() > 0) + { + QFont iconFont = QFont("ElaAwesome"); + iconFont.setPixelSize(17); + painter.setFont(iconFont); + painter.setPen(isEnabled() ? ElaThemeColor(d->_themeMode, BasicText) : ElaThemeColor(d->_themeMode, BasicTextDisable)); + QRectF expandIconRect(width() - 25, 0, 20, height()); + painter.translate(expandIconRect.x() + (qreal)expandIconRect.width() / 2 - 2, expandIconRect.y() + (qreal)expandIconRect.height() / 2); + painter.rotate(d->_pExpandIconRotate); + painter.translate(-expandIconRect.x() - (qreal)expandIconRect.width() / 2 + 2, -expandIconRect.y() - (qreal)expandIconRect.height() / 2); + painter.drawText(expandIconRect, Qt::AlignVCenter, QChar((unsigned short)ElaIconType::AngleDown)); + painter.restore(); + } +} + +void ElaMultiSelectComboBox::showPopup() +{ + Q_D(ElaMultiSelectComboBox); + bool oldAnimationEffects = qApp->isEffectEnabled(Qt::UI_AnimateCombo); + qApp->setEffectEnabled(Qt::UI_AnimateCombo, false); + QComboBox::showPopup(); + qApp->setEffectEnabled(Qt::UI_AnimateCombo, oldAnimationEffects); + + if (count() > 0) + { + QWidget* container = this->findChild(); + if (container) + { + int containerHeight = 0; + if (count() >= maxVisibleItems()) + { + containerHeight = maxVisibleItems() * 35 + 8; + } + else + { + containerHeight = count() * 35 + 8; + } + view()->resize(view()->width(), containerHeight - 8); + container->move(container->x(), container->y() + 3); + QLayout* layout = container->layout(); + while (layout->count()) + { + layout->takeAt(0); + } + QPropertyAnimation* fixedSizeAnimation = new QPropertyAnimation(container, "maximumHeight"); + connect(fixedSizeAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + container->setFixedHeight(value.toUInt()); + }); + fixedSizeAnimation->setStartValue(1); + fixedSizeAnimation->setEndValue(containerHeight); + fixedSizeAnimation->setEasingCurve(QEasingCurve::OutCubic); + fixedSizeAnimation->setDuration(400); + fixedSizeAnimation->start(QAbstractAnimation::DeleteWhenStopped); + + QPropertyAnimation* viewPosAnimation = new QPropertyAnimation(view(), "pos"); + connect(viewPosAnimation, &QPropertyAnimation::finished, this, [=]() { + d->_isAllowHidePopup = true; + layout->addWidget(view()); + }); + QPoint viewPos = view()->pos(); + viewPosAnimation->setStartValue(QPoint(viewPos.x(), viewPos.y() - view()->height())); + viewPosAnimation->setEndValue(viewPos); + viewPosAnimation->setEasingCurve(QEasingCurve::OutCubic); + viewPosAnimation->setDuration(400); + viewPosAnimation->start(QAbstractAnimation::DeleteWhenStopped); + } + //指示器动画 + QPropertyAnimation* rotateAnimation = new QPropertyAnimation(d, "pExpandIconRotate"); + connect(rotateAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + update(); + }); + rotateAnimation->setDuration(300); + rotateAnimation->setEasingCurve(QEasingCurve::InOutSine); + rotateAnimation->setStartValue(d->_pExpandIconRotate); + rotateAnimation->setEndValue(-180); + rotateAnimation->start(QAbstractAnimation::DeleteWhenStopped); + QPropertyAnimation* markAnimation = new QPropertyAnimation(d, "pExpandMarkWidth"); + markAnimation->setDuration(300); + markAnimation->setEasingCurve(QEasingCurve::InOutSine); + markAnimation->setStartValue(d->_pExpandMarkWidth); + qreal step = (width() / 2 - d->_pBorderRadius) / count(); + markAnimation->setEndValue(step * d->_selectedTextList.count()); + markAnimation->start(QAbstractAnimation::DeleteWhenStopped); + } + d->_refreshCurrentIndexs(); +} + +void ElaMultiSelectComboBox::hidePopup() +{ + Q_D(ElaMultiSelectComboBox); + if (d->_isFirstPopup && !this->view()->underMouse()) + { + d->_isFirstPopup = false; + return; + } + if (eApp->containsCursorToItem(d->_comboView)) + { + return; + } + else + { + if (d->_isAllowHidePopup) + { + QWidget* container = this->findChild(); + int containerHeight = container->height(); + if (container) + { + QLayout* layout = container->layout(); + while (layout->count()) + { + layout->takeAt(0); + } + QPropertyAnimation* viewPosAnimation = new QPropertyAnimation(view(), "pos"); + connect(viewPosAnimation, &QPropertyAnimation::finished, this, [=]() { + layout->addWidget(view()); + QMouseEvent focusEvent(QEvent::MouseButtonPress, QPoint(-1, -1), QPoint(-1, -1), Qt::NoButton, Qt::NoButton, Qt::NoModifier); + QApplication::sendEvent(parentWidget(), &focusEvent); + QComboBox::hidePopup(); + container->setFixedHeight(containerHeight); + }); + QPoint viewPos = view()->pos(); + connect(viewPosAnimation, &QPropertyAnimation::finished, this, [=]() { view()->move(viewPos); }); + viewPosAnimation->setStartValue(viewPos); + viewPosAnimation->setEndValue(QPoint(viewPos.x(), viewPos.y() - view()->height())); + viewPosAnimation->setEasingCurve(QEasingCurve::InCubic); + viewPosAnimation->start(QAbstractAnimation::DeleteWhenStopped); + + QPropertyAnimation* fixedSizeAnimation = new QPropertyAnimation(container, "maximumHeight"); + connect(fixedSizeAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + container->setFixedHeight(value.toUInt()); + }); + fixedSizeAnimation->setStartValue(container->height()); + fixedSizeAnimation->setEndValue(1); + fixedSizeAnimation->setEasingCurve(QEasingCurve::InCubic); + fixedSizeAnimation->start(QAbstractAnimation::DeleteWhenStopped); + d->_isAllowHidePopup = false; + } + //指示器动画 + QPropertyAnimation* rotateAnimation = new QPropertyAnimation(d, "pExpandIconRotate"); + connect(rotateAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + update(); + }); + rotateAnimation->setDuration(300); + rotateAnimation->setEasingCurve(QEasingCurve::InOutSine); + rotateAnimation->setStartValue(d->_pExpandIconRotate); + rotateAnimation->setEndValue(0); + rotateAnimation->start(QAbstractAnimation::DeleteWhenStopped); + QPropertyAnimation* markAnimation = new QPropertyAnimation(d, "pExpandMarkWidth"); + markAnimation->setDuration(300); + markAnimation->setEasingCurve(QEasingCurve::InOutSine); + markAnimation->setStartValue(d->_pExpandMarkWidth); + markAnimation->setEndValue(0); + markAnimation->start(QAbstractAnimation::DeleteWhenStopped); + } + } +} diff --git a/3rdparty/ElaWidgetTools/src/ElaNavigationBar.cpp b/3rdparty/ElaWidgetTools/src/ElaNavigationBar.cpp new file mode 100644 index 0000000..5959994 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaNavigationBar.cpp @@ -0,0 +1,436 @@ +#include "ElaNavigationBar.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "ElaBaseListView.h" +#include "ElaFooterDelegate.h" +#include "ElaFooterModel.h" +#include "ElaIconButton.h" +#include "ElaInteractiveCard.h" +#include "ElaMenu.h" +#include "ElaNavigationModel.h" +#include "ElaNavigationNode.h" +#include "ElaNavigationView.h" +#include "ElaSuggestBox.h" +#include "ElaTheme.h" +#include "ElaToolButton.h" +#include "private/ElaNavigationBarPrivate.h" +#include "private/ElaSuggestBoxPrivate.h" +Q_PROPERTY_CREATE_Q_CPP(ElaNavigationBar, bool, IsTransparent) +ElaNavigationBar::ElaNavigationBar(QWidget* parent) + : QWidget{parent}, d_ptr(new ElaNavigationBarPrivate()) +{ + Q_D(ElaNavigationBar); + d->q_ptr = this; + setFixedWidth(300); + d->_pIsTransparent = true; + + //用户卡片 + d->_userCard = new ElaInteractiveCard(this); + d->_userCard->setCardPixmap(QPixmap(":/include/Image/Cirno.jpg")); + d->_userCard->setTitle("Ela Tool"); + d->_userCard->setSubTitle("Liniyous@gmail.com"); + connect(d->_userCard, &ElaInteractiveCard::clicked, this, &ElaNavigationBar::userInfoCardClicked); + d->_userButton = new ElaIconButton(QPixmap(":/include/Image/Cirno.jpg"), this); + d->_userButton->setFixedSize(36, 36); + d->_userButton->setVisible(false); + d->_userButton->setBorderRadius(8); + d->_userButtonLayout = new QVBoxLayout(); + d->_userButtonLayout->setAlignment(Qt::AlignLeft); + d->_userButtonLayout->setContentsMargins(0, 0, 0, 6); + d->_userButtonLayout->setSpacing(0); + d->_userButtonLayout->addWidget(d->_userButton); + + connect(d->_userButton, &ElaIconButton::clicked, this, &ElaNavigationBar::userInfoCardClicked); + QHBoxLayout* userCardLayout = new QHBoxLayout(); + userCardLayout->setContentsMargins(0, 0, 0, 0); + userCardLayout->addSpacing(3); + userCardLayout->addLayout(d->_userButtonLayout); + userCardLayout->addWidget(d->_userCard); + + // 搜索栏和按钮组 + d->_navigationButton = new ElaToolButton(this); + d->_navigationButton->setFixedSize(40, 38); + d->_navigationButton->setElaIcon(ElaIconType::Bars); + d->_navigationButton->setBorderRadius(8); + connect(d->_navigationButton, &ElaToolButton::clicked, d, &ElaNavigationBarPrivate::onNavigationButtonClicked); + + d->_searchButton = new ElaToolButton(this); + d->_searchButton->setFixedSize(40, 38); + d->_searchButton->setElaIcon(ElaIconType::MagnifyingGlass); + d->_searchButton->setBorderRadius(8); + connect(d->_searchButton, &ElaToolButton::clicked, d, &ElaNavigationBarPrivate::onNavigationButtonClicked); + d->_searchButton->setVisible(false); + + d->_navigationSuggestBox = new ElaSuggestBox(this); + d->_navigationSuggestBox->setMinimumWidth(0); + d->_navigationButtonLayout = new QVBoxLayout(); + d->_navigationButtonLayout->setContentsMargins(0, 0, 0, 0); + d->_navigationButtonLayout->setSpacing(0); + d->_navigationButtonLayout->addWidget(d->_navigationButton); + d->_navigationButtonLayout->addWidget(d->_searchButton); + + d->_navigationSuggestLayout = new QHBoxLayout(); + d->_navigationSuggestLayout->setContentsMargins(0, 0, 10, 0); + d->_navigationSuggestLayout->setSpacing(6); + d->_navigationSuggestLayout->addLayout(d->_navigationButtonLayout); + d->_navigationSuggestLayout->addWidget(d->_navigationSuggestBox); + + // 搜索跳转 + connect(d->_navigationSuggestBox, &ElaSuggestBox::suggestionClicked, this, [=](QString suggestText, QVariantMap suggestData) { + ElaNavigationNode* node = nullptr; + if (suggestData.value("ElaNodeType").toString() == "Stacked") + { + node = d->_navigationModel->getNavigationNode(suggestData.value("ElaPageKey").toString()); + if (node) + { + d->onTreeViewClicked(node->getModelIndex()); + } + } + else + { + node = d->_footerModel->getNavigationNode(suggestData.value("ElaPageKey").toString()); + if (node) + { + d->onFooterViewClicked(node->getModelIndex()); + } + } + }); + + // 导航模型 + d->_navigationModel = new ElaNavigationModel(this); + d->_navigationView = new ElaNavigationView(this); + d->_navigationView->setModel(d->_navigationModel); + connect(d->_navigationView, &ElaNavigationView::navigationClicked, this, [=](const QModelIndex& index) { d->onTreeViewClicked(index); }); + connect(d->_navigationView, &ElaNavigationView::navigationOpenNewWindow, d, &ElaNavigationBarPrivate::onNavigationOpenNewWindow); + + // 页脚 + d->_footerView = new ElaBaseListView(this); + d->_footerView->setFixedHeight(0); + d->_footerModel = new ElaFooterModel(this); + d->_footerView->setModel(d->_footerModel); + d->_footerDelegate = new ElaFooterDelegate(this); + d->_footerDelegate->setElaListView(d->_footerView); + d->_footerView->setItemDelegate(d->_footerDelegate); + connect(d->_footerView, &ElaBaseListView::mousePress, this, [=](const QModelIndex& index) { + d->_footerDelegate->setPressIndex(index); + d->_footerView->viewport()->update(); + }); + connect(d->_footerView, &ElaBaseListView::mouseDoubleClick, this, [=](const QModelIndex& index) { + d->_footerDelegate->setPressIndex(index); + d->_footerView->viewport()->update(); + }); + connect(d->_footerView, &ElaBaseListView::mouseRelease, this, [=](const QModelIndex& index) { + d->_footerDelegate->setPressIndex(QModelIndex()); + d->_footerView->viewport()->update(); + }); + connect(d->_footerView, &ElaBaseListView::clicked, this, [=](const QModelIndex& index) { d->onFooterViewClicked(index); }); + + QVBoxLayout* mainLayout = new QVBoxLayout(this); + mainLayout->setAlignment(Qt::AlignLeft); + mainLayout->setSpacing(0); + mainLayout->setContentsMargins(0, 10, 5, 0); + mainLayout->addLayout(userCardLayout); + mainLayout->addSpacing(4); + mainLayout->addLayout(d->_navigationSuggestLayout); + mainLayout->addSpacing(4); + mainLayout->addWidget(d->_navigationView); + mainLayout->addWidget(d->_footerView); + + //主题设置 + d->_themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { + d->_themeMode = themeMode; + }); +} + +ElaNavigationBar::~ElaNavigationBar() +{ +} + +void ElaNavigationBar::setUserInfoCardVisible(bool isVisible) +{ + Q_D(ElaNavigationBar); + d->_isShowUserCard = isVisible; + d->_userCard->setVisible(isVisible); + if (!isVisible) + { + d->_userButton->setVisible(false); + } +} + +void ElaNavigationBar::setUserInfoCardPixmap(QPixmap pix) +{ + Q_D(ElaNavigationBar); + d->_userCard->setCardPixmap(pix); + d->_userButton->setPixmap(pix); +} + +void ElaNavigationBar::setUserInfoCardTitle(QString title) +{ + Q_D(ElaNavigationBar); + d->_userCard->setTitle(title); +} + +void ElaNavigationBar::setUserInfoCardSubTitle(QString subTitle) +{ + Q_D(ElaNavigationBar); + d->_userCard->setSubTitle(subTitle); +} + +ElaNavigationType::NodeOperateReturnType ElaNavigationBar::addExpanderNode(QString expanderTitle, QString& expanderKey, ElaIconType::IconName awesome) +{ + Q_D(ElaNavigationBar); + ElaNavigationType::NodeOperateReturnType returnType = d_ptr->_navigationModel->addExpanderNode(expanderTitle, expanderKey, awesome); + if (returnType == ElaNavigationType::Success) + { + d->_initNodeModelIndex(QModelIndex()); + d->_resetNodeSelected(); + } + return returnType; +} + +ElaNavigationType::NodeOperateReturnType ElaNavigationBar::addExpanderNode(QString expanderTitle, QString& expanderKey, QString targetExpanderKey, ElaIconType::IconName awesome) +{ + Q_D(ElaNavigationBar); + ElaNavigationType::NodeOperateReturnType returnType = d->_navigationModel->addExpanderNode(expanderTitle, expanderKey, targetExpanderKey, awesome); + if (returnType == ElaNavigationType::Success) + { + d->_initNodeModelIndex(QModelIndex()); + d->_resetNodeSelected(); + } + return returnType; +} + +ElaNavigationType::NodeOperateReturnType ElaNavigationBar::addPageNode(QString pageTitle, QWidget* page, ElaIconType::IconName awesome) +{ + Q_D(ElaNavigationBar); + if (!page) + { + return ElaNavigationType::PageInvalid; + } + QString pageKey; + ElaNavigationType::NodeOperateReturnType returnType = d_ptr->_navigationModel->addPageNode(pageTitle, pageKey, awesome); + if (returnType == ElaNavigationType::Success) + { + d->_pageMetaMap.insert(pageKey, page->metaObject()); + d->_addStackedPage(page, pageKey); + d->_initNodeModelIndex(QModelIndex()); + d->_resetNodeSelected(); + } + return returnType; +} + +ElaNavigationType::NodeOperateReturnType ElaNavigationBar::addPageNode(QString pageTitle, QWidget* page, QString targetExpanderKey, ElaIconType::IconName awesome) +{ + Q_D(ElaNavigationBar); + if (!page) + { + return ElaNavigationType::PageInvalid; + } + if (targetExpanderKey.isEmpty()) + { + return ElaNavigationType::TargetNodeInvalid; + } + QString pageKey; + ElaNavigationType::NodeOperateReturnType returnType = d->_navigationModel->addPageNode(pageTitle, pageKey, targetExpanderKey, awesome); + if (returnType == ElaNavigationType::NodeOperateReturnType::Success) + { + d->_pageMetaMap.insert(pageKey, page->metaObject()); + ElaNavigationNode* node = d->_navigationModel->getNavigationNode(pageKey); + ElaNavigationNode* originalNode = node->getOriginalNode(); + if (d->_compactMenuMap.contains(originalNode)) + { + ElaMenu* menu = d->_compactMenuMap.value(originalNode); + QAction* action = menu->addElaIconAction(node->getAwesome(), node->getNodeTitle()); + connect(action, &QAction::triggered, this, [=]() { + d->onTreeViewClicked(node->getModelIndex()); + }); + } + else + { + ElaMenu* menu = new ElaMenu(const_cast(this)); + QAction* action = menu->addElaIconAction(node->getAwesome(), node->getNodeTitle()); + connect(action, &QAction::triggered, this, [=]() { + d_ptr->onTreeViewClicked(node->getModelIndex()); + }); + d_ptr->_compactMenuMap.insert(originalNode, menu); + } + d_ptr->_addStackedPage(page, pageKey); + d->_initNodeModelIndex(QModelIndex()); + d->_resetNodeSelected(); + } + return returnType; +} + +ElaNavigationType::NodeOperateReturnType ElaNavigationBar::addPageNode(QString pageTitle, QWidget* page, int keyPoints, ElaIconType::IconName awesome) +{ + Q_D(ElaNavigationBar); + if (!page) + { + return ElaNavigationType::PageInvalid; + } + QString pageKey; + ElaNavigationType::NodeOperateReturnType returnType = d_ptr->_navigationModel->addPageNode(pageTitle, pageKey, keyPoints, awesome); + if (returnType == ElaNavigationType::Success) + { + d->_pageMetaMap.insert(pageKey, page->metaObject()); + d->_addStackedPage(page, pageKey); + d->_initNodeModelIndex(QModelIndex()); + d->_resetNodeSelected(); + } + return returnType; +} + +ElaNavigationType::NodeOperateReturnType ElaNavigationBar::addPageNode(QString pageTitle, QWidget* page, QString targetExpanderKey, int keyPoints, ElaIconType::IconName awesome) +{ + Q_D(ElaNavigationBar); + if (!page) + { + return ElaNavigationType::PageInvalid; + } + if (targetExpanderKey.isEmpty()) + { + return ElaNavigationType::TargetNodeInvalid; + } + QString pageKey; + ElaNavigationType::NodeOperateReturnType returnType = d_ptr->_navigationModel->addPageNode(pageTitle, pageKey, targetExpanderKey, keyPoints, awesome); + if (returnType == ElaNavigationType::Success) + { + d->_pageMetaMap.insert(pageKey, page->metaObject()); + ElaNavigationNode* node = d_ptr->_navigationModel->getNavigationNode(pageKey); + ElaNavigationNode* originalNode = node->getOriginalNode(); + if (d_ptr->_compactMenuMap.contains(originalNode)) + { + ElaMenu* menu = d_ptr->_compactMenuMap.value(originalNode); + QAction* action = menu->addElaIconAction(node->getAwesome(), node->getNodeTitle()); + connect(action, &QAction::triggered, this, [=]() { + d_ptr->onTreeViewClicked(node->getModelIndex()); + }); + } + else + { + ElaMenu* menu = new ElaMenu(const_cast(this)); + QAction* action = menu->addElaIconAction(node->getAwesome(), node->getNodeTitle()); + connect(action, &QAction::triggered, this, [=]() { + d_ptr->onTreeViewClicked(node->getModelIndex()); + }); + d_ptr->_compactMenuMap.insert(originalNode, menu); + } + d_ptr->_addStackedPage(page, pageKey); + d->_initNodeModelIndex(QModelIndex()); + d->_resetNodeSelected(); + } + return returnType; +} + +ElaNavigationType::NodeOperateReturnType ElaNavigationBar::addFooterNode(QString footerTitle, QString& footerKey, int keyPoints, ElaIconType::IconName awesome) +{ + return addFooterNode(footerTitle, nullptr, footerKey, keyPoints, awesome); +} + +ElaNavigationType::NodeOperateReturnType ElaNavigationBar::addFooterNode(QString footerTitle, QWidget* page, QString& footerKey, int keyPoints, ElaIconType::IconName awesome) +{ + ElaNavigationType::NodeOperateReturnType returnType = d_ptr->_footerModel->addFooterNode(footerTitle, footerKey, page ? true : false, keyPoints, awesome); + if (returnType == ElaNavigationType::Success) + { + d_ptr->_addFooterPage(page, footerKey); + } + return returnType; +} + +void ElaNavigationBar::setNodeKeyPoints(QString nodeKey, int keyPoints) +{ + Q_D(ElaNavigationBar); + ElaNavigationNode* node = d->_navigationModel->getNavigationNode(nodeKey); + if (!node || node->getIsExpanderNode() || keyPoints < 0) + { + return; + } + node->setKeyPoints(keyPoints); + if (node->getIsFooterNode()) + { + d->_footerView->viewport()->update(); + } + else + { + d->_navigationView->viewport()->update(); + } +} + +int ElaNavigationBar::getNodeKeyPoints(QString nodeKey) const +{ + Q_D(const ElaNavigationBar); + ElaNavigationNode* node = d->_navigationModel->getNavigationNode(nodeKey); + if (!node || node->getIsExpanderNode()) + { + return -1; + } + return node->getKeyPoints(); +} + +void ElaNavigationBar::navigation(QString pageKey, bool isLogClicked) +{ + Q_D(ElaNavigationBar); + ElaNavigationNode* node = d->_navigationModel->getNavigationNode(pageKey); + if (!node) + { + node = d->_footerModel->getNavigationNode(pageKey); + } + if (node) + { + if (node->getIsFooterNode()) + { + d->onFooterViewClicked(node->getModelIndex(), isLogClicked); + } + else + { + if (!node->getIsExpanderNode()) + { + d->onTreeViewClicked(node->getModelIndex(), isLogClicked); + } + } + } +} + +void ElaNavigationBar::setDisplayMode(ElaNavigationType::NavigationDisplayMode displayMode, bool isAnimation) +{ + Q_D(ElaNavigationBar); + if (d->_currentDisplayMode == displayMode || displayMode == ElaNavigationType::Auto) + { + return; + } + d->_doComponentAnimation(displayMode, isAnimation); + d->_raiseNavigationBar(); +} + +void ElaNavigationBar::paintEvent(QPaintEvent* event) +{ + Q_D(ElaNavigationBar); + if (!d->_pIsTransparent) + { + QPainter painter(this); + painter.save(); + painter.setPen(ElaThemeColor(d->_themeMode, PopupBorder)); + painter.setBrush(ElaThemeColor(d->_themeMode, PopupBase)); + QRect baseRect = this->rect(); + baseRect.adjust(-1, 0, -1, 0); + QPainterPath path; + path.moveTo(baseRect.topLeft()); + path.lineTo(QPoint(baseRect.right() - 8, baseRect.y())); + path.arcTo(QRectF(baseRect.right() - 16, baseRect.y(), 16, 16), 90, -90); + path.lineTo(QPoint(baseRect.right(), baseRect.bottom() - 8)); + path.arcTo(QRectF(baseRect.right() - 16, baseRect.bottom() - 16, 16, 16), 0, -90); + path.lineTo(baseRect.bottomLeft()); + path.closeSubpath(); + painter.drawPath(path); + painter.restore(); + } + QWidget::paintEvent(event); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaNavigationRouter.cpp b/3rdparty/ElaWidgetTools/src/ElaNavigationRouter.cpp new file mode 100644 index 0000000..705fc85 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaNavigationRouter.cpp @@ -0,0 +1,68 @@ +#include "ElaNavigationRouter.h" + +#include "ElaNavigationRouterPrivate.h" + +Q_SINGLETON_CREATE_CPP(ElaNavigationRouter) +Q_PROPERTY_CREATE_Q_CPP(ElaNavigationRouter, int, MaxRouteCount) +ElaNavigationRouter::ElaNavigationRouter(QObject* parent) + : QObject{parent}, d_ptr(new ElaNavigationRouterPrivate()) +{ + Q_D(ElaNavigationRouter); + d->q_ptr = this; + d->_pMaxRouteCount = 25; +} + +ElaNavigationRouter::~ElaNavigationRouter() +{ +} + +ElaNavigationRouterType::NavigationRouteType ElaNavigationRouter::navigationRoute(QObject* routeObject, QString routeFunctionName, const QVariantMap& routeData, Qt::ConnectionType connectionType) +{ + Q_D(ElaNavigationRouter); + if (!routeObject) + { + return ElaNavigationRouterType::ObjectInvalid; + } + if (routeFunctionName.isEmpty()) + { + return ElaNavigationRouterType::FunctionNameInvalid; + } + if (d->_routeQueue.isEmpty()) + { + Q_EMIT navigationRouterStateChanged(true); + } + else + { + if (d->_routeQueue.count() >= d->_pMaxRouteCount) + { + d->_routeQueue.dequeue(); + } + } + QVariantMap saveData; + saveData.insert("ElaRouteObject", QVariant::fromValue(routeObject)); + saveData.insert("ElaRouteFunctionName", routeFunctionName); + saveData.insert("ElaRouteData", routeData); + saveData.insert("ElaRouteConnectionType", QVariant::fromValue(connectionType)); + d->_routeQueue.enqueue(saveData); + return ElaNavigationRouterType::Success; +} + +void ElaNavigationRouter::navigationRouteBack() +{ + Q_D(ElaNavigationRouter); + int routeQueneCount = d->_routeQueue.count(); + if (routeQueneCount > 0) + { + if (routeQueneCount == 1) + { + Q_EMIT navigationRouterStateChanged(false); + } + QVariantMap routeSaveData = d->_routeQueue.last(); + d->_routeQueue.removeLast(); + QObject* routeObject = routeSaveData.value("ElaRouteObject").value(); + QString routeFunctionName = routeSaveData.value("ElaRouteFunctionName").toString(); + QVariantMap routeData = routeSaveData.value("ElaRouteData").toMap(); + Qt::ConnectionType connectionType = routeSaveData.value("ElaRouteConnectionType").value(); + QMetaObject::invokeMethod(routeObject, routeFunctionName.toLocal8Bit().constData(), connectionType, Q_ARG(QVariantMap, routeData)); + } +} diff --git a/3rdparty/ElaWidgetTools/src/ElaPivot.cpp b/3rdparty/ElaWidgetTools/src/ElaPivot.cpp new file mode 100644 index 0000000..db1c0f4 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaPivot.cpp @@ -0,0 +1,168 @@ +#include "ElaPivot.h" + +#include +#include +#include + +#include "ElaPivotModel.h" +#include "ElaPivotPrivate.h" +#include "ElaPivotStyle.h" +#include "ElaPivotView.h" +ElaPivot::ElaPivot(QWidget* parent) + : QWidget{parent}, d_ptr(new ElaPivotPrivate()) +{ + Q_D(ElaPivot); + d->q_ptr = this; + d->_pTextPixelSize = 20; + setFixedHeight(40); + setObjectName("ElaPivot"); + setStyleSheet("#ElaPivot{background-color:transparent;}"); + setMouseTracking(true); + + d->_listView = new ElaPivotView(this); + d->_listView->setMinimumHeight(0); + d->_listView->setFlow(QListView::LeftToRight); + d->_listView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + d->_listView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + d->_listModel = new ElaPivotModel(this); + d->_listView->setModel(d->_listModel); + d->_listStyle = new ElaPivotStyle(d->_listView->style()); + d->_listView->setStyle(d->_listStyle); + d->_listView->setPivotStyle(d->_listStyle); + + QFont textFont = this->font(); + textFont.setLetterSpacing(QFont::AbsoluteSpacing, 0.5); + textFont.setPixelSize(d->_pTextPixelSize); + d->_listView->setFont(textFont); + + QScroller::grabGesture(d->_listView->viewport(), QScroller::LeftMouseButtonGesture); + QScroller* scroller = QScroller::scroller(d->_listView->viewport()); + QScrollerProperties properties = scroller->scrollerProperties(); + properties.setScrollMetric(QScrollerProperties::MousePressEventDelay, 0); + properties.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy, QScrollerProperties::OvershootAlwaysOn); + properties.setScrollMetric(QScrollerProperties::OvershootDragResistanceFactor, 0.35); + properties.setScrollMetric(QScrollerProperties::OvershootScrollTime, 0.5); + properties.setScrollMetric(QScrollerProperties::FrameRate, QScrollerProperties::Fps60); + scroller->setScrollerProperties(properties); + + connect(scroller, &QScroller::stateChanged, this, [=](QScroller::State newstate) { + if (newstate == QScroller::Pressed) + { + d->_listStyle->setPressIndex(d->_listView->indexAt(d->_listView->mapFromGlobal(QCursor::pos()))); + d->_listView->viewport()->update(); + } + else if (newstate == QScroller::Scrolling || newstate == QScroller::Inactive) + { + d->_listStyle->setPressIndex(QModelIndex()); + d->_listView->viewport()->update(); + } + }); + connect(d->_listView, &ElaPivotView::clicked, this, [=](const QModelIndex& index) { + if (index.row() != d->_listStyle->getCurrentIndex()) + { + Q_EMIT pCurrentIndexChanged(); + } + d->_listView->doCurrentIndexChangedAnimation(index); + d->_listStyle->setCurrentIndex(index.row()); + Q_EMIT pivotClicked(index.row()); + }); + connect(d->_listView, &ElaPivotView::doubleClicked, this, [=](const QModelIndex& index) { + Q_EMIT pivotDoubleClicked(index.row()); + }); + QVBoxLayout* mainLayout = new QVBoxLayout(this); + mainLayout->setContentsMargins(0, 0, 0, 0); + mainLayout->addWidget(d->_listView); +} + +ElaPivot::~ElaPivot() +{ +} + +void ElaPivot::appendPivot(QString pivotTitle) +{ + Q_D(ElaPivot); + d->_listModel->appendPivot(pivotTitle); + d->_checkCurrentIndex(); +} + +void ElaPivot::removePivot(QString pivotTitle) +{ + Q_D(ElaPivot); + d->_listModel->removePivot(pivotTitle); + d->_checkCurrentIndex(); +} + +void ElaPivot::setTextPixelSize(int textPixelSize) +{ + Q_D(ElaPivot); + if (textPixelSize > 0) + { + d->_pTextPixelSize = textPixelSize; + QFont textFont = this->font(); + textFont.setLetterSpacing(QFont::AbsoluteSpacing, 0.5); + textFont.setPixelSize(d->_pTextPixelSize); + d->_listView->setFont(textFont); + Q_EMIT pTextPixelSizeChanged(); + } +} + +int ElaPivot::getTextPixelSize() const +{ + Q_D(const ElaPivot); + return d->_pTextPixelSize; +} + +void ElaPivot::setCurrentIndex(int currentIndex) +{ + Q_D(ElaPivot); + if (currentIndex < d->_listModel->getPivotListCount()) + { + QModelIndex index = d->_listModel->index(currentIndex, 0); + d->_listView->doCurrentIndexChangedAnimation(index); + if (index.row() != d->_listStyle->getCurrentIndex()) + { + Q_EMIT pCurrentIndexChanged(); + } + d->_listStyle->setCurrentIndex(currentIndex); + } +} + +int ElaPivot::getCurrentIndex() const +{ + Q_D(const ElaPivot); + return d->_listView->currentIndex().row(); +} + +void ElaPivot::setPivotSpacing(int pivotSpacing) +{ + Q_D(ElaPivot); + if (pivotSpacing >= 0) + { + d->_listStyle->setPivotSpacing(pivotSpacing); + d->_listView->doItemsLayout(); + Q_EMIT pPivotSpacingChanged(); + } +} + +int ElaPivot::getPivotSpacing() const +{ + Q_D(const ElaPivot); + return d->_listStyle->getPivotSpacing(); +} + +void ElaPivot::setMarkWidth(int markWidth) +{ + Q_D(ElaPivot); + if (markWidth >= 0) + { + d->_listView->setMarkWidth(markWidth); + d->_listView->viewport()->update(); + Q_EMIT pMarkWidthChanged(); + } +} + +int ElaPivot::getMarkWidth() const +{ + Q_D(const ElaPivot); + return d->_listView->getMarkWidth(); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaPlainTextEdit.cpp b/3rdparty/ElaWidgetTools/src/ElaPlainTextEdit.cpp new file mode 100644 index 0000000..8c89fb8 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaPlainTextEdit.cpp @@ -0,0 +1,138 @@ +#include "ElaPlainTextEdit.h" + +#include +#include +#include +#include +#include + +#include "ElaEventBus.h" +#include "ElaMenu.h" +#include "ElaPlainTextEditPrivate.h" +#include "ElaPlainTextEditStyle.h" +#include "ElaScrollBar.h" +#include "ElaTheme.h" +ElaPlainTextEdit::ElaPlainTextEdit(QWidget* parent) + : QPlainTextEdit(parent), d_ptr(new ElaPlainTextEditPrivate()) +{ + Q_D(ElaPlainTextEdit); + d->q_ptr = this; + setObjectName("ElaPlainTextEdit"); + setStyleSheet("#ElaPlainTextEdit{background-color:transparent;}"); + setHorizontalScrollBar(new ElaScrollBar(this)); + setVerticalScrollBar(new ElaScrollBar(this)); + setMouseTracking(true); + // 事件总线 + d->_focusEvent = new ElaEvent("WMWindowClicked", "onWMWindowClickedEvent", d); + d->_focusEvent->registerAndInit(); + + d->_style = new ElaPlainTextEditStyle(style()); + setStyle(d->_style); + d->onThemeChanged(eTheme->getThemeMode()); + connect(eTheme, &ElaTheme::themeModeChanged, d, &ElaPlainTextEditPrivate::onThemeChanged); +} + +ElaPlainTextEdit::ElaPlainTextEdit(const QString& text, QWidget* parent) + : ElaPlainTextEdit(parent) +{ + setPlainText(text); +} + +ElaPlainTextEdit::~ElaPlainTextEdit() +{ +} + +void ElaPlainTextEdit::focusInEvent(QFocusEvent* event) +{ + Q_D(ElaPlainTextEdit); + if (event->reason() == Qt::MouseFocusReason) + { + QPropertyAnimation* markAnimation = new QPropertyAnimation(d->_style, "pExpandMarkWidth"); + connect(markAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + update(); + }); + markAnimation->setDuration(300); + markAnimation->setEasingCurve(QEasingCurve::InOutSine); + markAnimation->setStartValue(d->_style->getExpandMarkWidth()); + markAnimation->setEndValue(width() / 2 - 3); + markAnimation->start(QAbstractAnimation::DeleteWhenStopped); + } + QPlainTextEdit::focusInEvent(event); +} + +void ElaPlainTextEdit::focusOutEvent(QFocusEvent* event) +{ + Q_D(ElaPlainTextEdit); + if (event->reason() != Qt::PopupFocusReason) + { + QPropertyAnimation* markAnimation = new QPropertyAnimation(d->_style, "pExpandMarkWidth"); + connect(markAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + update(); + }); + markAnimation->setDuration(300); + markAnimation->setEasingCurve(QEasingCurve::InOutSine); + markAnimation->setStartValue(d->_style->getExpandMarkWidth()); + markAnimation->setEndValue(0); + markAnimation->start(QAbstractAnimation::DeleteWhenStopped); + } + QPlainTextEdit::focusOutEvent(event); +} + +void ElaPlainTextEdit::contextMenuEvent(QContextMenuEvent* event) +{ + ElaMenu* menu = new ElaMenu(this); + menu->setMenuItemHeight(27); + menu->setAttribute(Qt::WA_DeleteOnClose); + QAction* action = nullptr; + if (!isReadOnly()) + { + action = menu->addElaIconAction(ElaIconType::ArrowRotateLeft, "撤销", QKeySequence::Undo); + action->setEnabled(isUndoRedoEnabled() ? document()->isUndoAvailable() : false); + connect(action, &QAction::triggered, this, &ElaPlainTextEdit::undo); + + action = menu->addElaIconAction(ElaIconType::ArrowRotateRight, "恢复", QKeySequence::Redo); + action->setEnabled(isUndoRedoEnabled() ? document()->isRedoAvailable() : false); + connect(action, &QAction::triggered, this, &ElaPlainTextEdit::redo); + menu->addSeparator(); + } +#ifndef QT_NO_CLIPBOARD + if (!isReadOnly()) + { + action = menu->addElaIconAction(ElaIconType::KnifeKitchen, "剪切", QKeySequence::Cut); + action->setEnabled(!isReadOnly() && !textCursor().selectedText().isEmpty()); + connect(action, &QAction::triggered, this, &ElaPlainTextEdit::cut); + } + + action = menu->addElaIconAction(ElaIconType::Copy, "复制", QKeySequence::Copy); + action->setEnabled(!textCursor().selectedText().isEmpty()); + connect(action, &QAction::triggered, this, &ElaPlainTextEdit::copy); + + if (!isReadOnly()) + { + action = menu->addElaIconAction(ElaIconType::Paste, "粘贴", QKeySequence::Paste); + action->setEnabled(!isReadOnly() && !QGuiApplication::clipboard()->text().isEmpty()); + connect(action, &QAction::triggered, this, &ElaPlainTextEdit::paste); + } +#endif + if (!isReadOnly()) + { + action = menu->addElaIconAction(ElaIconType::DeleteLeft, "删除"); + action->setEnabled(!isReadOnly() && !toPlainText().isEmpty() && !textCursor().selectedText().isEmpty()); + connect(action, &QAction::triggered, this, [=](bool checked) { + if (!textCursor().selectedText().isEmpty()) + { + textCursor().deleteChar(); + } + }); + } + if (!menu->isEmpty()) + { + menu->addSeparator(); + } + action = menu->addAction("全选"); + action->setShortcut(QKeySequence::SelectAll); + action->setEnabled(!toPlainText().isEmpty() && !(textCursor().selectedText() == toPlainText())); + connect(action, &QAction::triggered, this, &ElaPlainTextEdit::selectAll); + menu->popup(event->globalPos()); + this->setFocus(); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaPopularCard.cpp b/3rdparty/ElaWidgetTools/src/ElaPopularCard.cpp new file mode 100644 index 0000000..58cf29c --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaPopularCard.cpp @@ -0,0 +1,225 @@ +#include "ElaPopularCard.h" + +#include +#include +#include +#include +#include + +#include "ElaPopularCardFloater.h" +#include "ElaPopularCardPrivate.h" +#include "ElaPushButton.h" +#include "ElaTheme.h" +Q_PROPERTY_CREATE_Q_CPP(ElaPopularCard, int, BorderRadius) +Q_PROPERTY_CREATE_Q_CPP(ElaPopularCard, QPixmap, CardPixmap) +Q_PROPERTY_CREATE_Q_CPP(ElaPopularCard, QString, Title) +Q_PROPERTY_CREATE_Q_CPP(ElaPopularCard, QString, SubTitle) +Q_PROPERTY_CREATE_Q_CPP(ElaPopularCard, QString, InteractiveTips) +Q_PROPERTY_CREATE_Q_CPP(ElaPopularCard, QString, DetailedText) +Q_PROPERTY_CREATE_Q_CPP(ElaPopularCard, QPixmap, CardFloatPixmap) +ElaPopularCard::ElaPopularCard(QWidget* parent) + : QWidget{parent}, d_ptr(new ElaPopularCardPrivate()) +{ + Q_D(ElaPopularCard); + setFixedSize(320, 120); + d->q_ptr = this; + d->_pBorderRadius = 8; + d->_pHoverYOffset = 0; + d->_pHoverOpacity = 0; + setObjectName("ElaPopularCard"); + setStyleSheet("#ElaPopularCard{background-color:transparent}"); + setMouseTracking(true); + + d->_pCardFloatArea = parentWidget(); + d->_floater = new ElaPopularCardFloater(this, d, d->_pCardFloatArea); + d->_floatTimer = new QTimer(this); + connect(d->_floatTimer, &QTimer::timeout, d, &ElaPopularCardPrivate::_showFloater); + + d->_themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { d->_themeMode = themeMode; }); +} + +ElaPopularCard::~ElaPopularCard() +{ +} + +void ElaPopularCard::setCardButtontext(QString cardButtonText) +{ + Q_D(ElaPopularCard); + if (cardButtonText.isEmpty()) + { + return; + } + d->_pCardButtontext = cardButtonText; + d->_floater->_overButton->setText(d->_pCardButtontext); + Q_EMIT pCardButtontextChanged(); +} + +QString ElaPopularCard::getCardButtontext() const +{ + Q_D(const ElaPopularCard); + return d->_pCardButtontext; +} + +void ElaPopularCard::setCardFloatArea(QWidget* floatArea) +{ + Q_D(ElaPopularCard); + if (!floatArea || floatArea == this) + { + return; + } + d->_pCardFloatArea = floatArea; + d->_floater->setParent(floatArea); + Q_EMIT pCardFloatAreaChanged(); +} + +QWidget* ElaPopularCard::getCardFloatArea() const +{ + Q_D(const ElaPopularCard); + return d->_pCardFloatArea; +} + +bool ElaPopularCard::event(QEvent* event) +{ + Q_D(ElaPopularCard); + switch (event->type()) + { + case QEvent::Enter: + { + d->_floatTimer->start(450); + QPropertyAnimation* hoverAnimation = new QPropertyAnimation(d, "pHoverYOffset"); + connect(hoverAnimation, &QPropertyAnimation::valueChanged, this, [=]() { update(); }); + hoverAnimation->setDuration(130); + hoverAnimation->setStartValue(d->_pHoverYOffset); + hoverAnimation->setEndValue(6); + hoverAnimation->start(QAbstractAnimation::DeleteWhenStopped); + QPropertyAnimation* opacityAnimation = new QPropertyAnimation(d, "pHoverOpacity"); + opacityAnimation->setDuration(130); + opacityAnimation->setStartValue(d->_pHoverOpacity); + opacityAnimation->setEndValue(1); + opacityAnimation->start(QAbstractAnimation::DeleteWhenStopped); + break; + } + case QEvent::Leave: + { + d->_floatTimer->stop(); + QPropertyAnimation* hoverAnimation = new QPropertyAnimation(d, "pHoverYOffset"); + connect(hoverAnimation, &QPropertyAnimation::valueChanged, this, [=]() { update(); }); + hoverAnimation->setDuration(130); + hoverAnimation->setStartValue(d->_pHoverYOffset); + hoverAnimation->setEndValue(0); + hoverAnimation->start(QAbstractAnimation::DeleteWhenStopped); + QPropertyAnimation* opacityAnimation = new QPropertyAnimation(d, "pHoverOpacity"); + opacityAnimation->setDuration(130); + opacityAnimation->setStartValue(d->_pHoverOpacity); + opacityAnimation->setEndValue(0); + opacityAnimation->start(QAbstractAnimation::DeleteWhenStopped); + break; + } + case QEvent::MouseButtonRelease: + { + Q_EMIT popularCardClicked(); + break; + } + default: + { + break; + } + } + return QWidget::event(event); +} + +void ElaPopularCard::paintEvent(QPaintEvent* event) +{ + Q_D(ElaPopularCard); + if (d->_isFloating) + { + return; + } + QPainter painter(this); + painter.save(); + painter.setRenderHints(QPainter::SmoothPixmapTransform | QPainter::Antialiasing | QPainter::TextAntialiasing); + if (underMouse()) + { + // 阴影绘制 + painter.setOpacity(d->_pHoverOpacity); + QRect shadowRect = rect(); + shadowRect.adjust(0, 0, 0, -d->_pHoverYOffset); + eTheme->drawEffectShadow(&painter, shadowRect, d->_shadowBorderWidth, d->_pBorderRadius); + } + QRectF foregroundRect(d->_shadowBorderWidth, d->_shadowBorderWidth - d->_pHoverYOffset + 1, width() - 2 * d->_shadowBorderWidth, height() - 2 * d->_shadowBorderWidth); + //背景绘制 + painter.setOpacity(1); + painter.setPen(underMouse() ? ElaThemeColor(d->_themeMode, PopupBorderHover) : ElaThemeColor(d->_themeMode, BasicBorder)); + painter.setBrush(ElaThemeColor(d->_themeMode, BasicBaseAlpha)); + painter.drawRoundedRect(foregroundRect, d->_pBorderRadius, d->_pBorderRadius); + //图片绘制 + painter.save(); + QRectF pixRect(foregroundRect.x() + foregroundRect.height() * 0.15, foregroundRect.y() + foregroundRect.height() * 0.15, foregroundRect.height() * 0.7, foregroundRect.height() * 0.7); + QPainterPath pixPath; + pixPath.addRoundedRect(pixRect, 4, 4); + painter.setClipPath(pixPath); + painter.drawPixmap(pixRect, d->_pCardPixmap, d->_pCardPixmap.rect()); + painter.restore(); + + //文字绘制 + //计算按钮最终矩形 + int buttonTargetWidth = d->_floater->_overButton->fontMetrics().horizontalAdvance(d->_floater->_overButton->text()) + 40; + if (buttonTargetWidth > width() * 0.25) + { + buttonTargetWidth = width() * 0.25; + } + //处理ElaPushButton的阴影偏移 + d->_buttonTargetRect = QRect(QPoint(width() + 2 * d->_floater->_floatGeometryOffset - d->_shadowBorderWidth + 3 - foregroundRect.height() * 0.15 - buttonTargetWidth, foregroundRect.height() * 0.15 - 3), QSize(buttonTargetWidth, 36)); + + // Title + painter.setPen(ElaThemeColor(d->_themeMode, BasicText)); + QFont font = painter.font(); + font.setWeight(QFont::Bold); + font.setPixelSize(15); + painter.setFont(font); + int titleHeight = painter.fontMetrics().height(); + QRectF titleRect(pixRect.right() + d->_textHSpacing, pixRect.y(), d->_floater->_floatGeometryOffset * 2 + foregroundRect.width() - pixRect.width() - d->_textHSpacing * 2 - foregroundRect.height() * 0.15 - buttonTargetWidth, titleHeight); + QString titleText = painter.fontMetrics().elidedText(d->_pTitle, Qt::ElideRight, titleRect.width()); + painter.drawText(titleRect, Qt::AlignLeft | Qt::AlignTop | Qt::TextSingleLine, titleText); + + // SubTitle + font.setWeight(QFont::DemiBold); + font.setPixelSize(13); + painter.setFont(font); + int subTitleHeight = painter.fontMetrics().height(); + QRectF subTitleRect(pixRect.right() + d->_textHSpacing, titleRect.bottom() + d->_textVSpacing, d->_floater->_floatGeometryOffset * 2 + foregroundRect.width() - pixRect.width() - d->_textHSpacing * 2 - foregroundRect.height() * 0.15 - buttonTargetWidth, + subTitleHeight); + QString subTitleText = painter.fontMetrics().elidedText(d->_pSubTitle, Qt::ElideRight, subTitleRect.width()); + painter.drawText(subTitleRect, Qt::AlignLeft | Qt::AlignTop | Qt::TextSingleLine, subTitleText); + + // InteractiveTips + int tipWidth = painter.fontMetrics().horizontalAdvance(d->_pInteractiveTips); + int tipHeight = painter.fontMetrics().height(); + if (!d->_pInteractiveTips.isEmpty()) + { + font.setWeight(QFont::DemiBold); + font.setPixelSize(12); + painter.setFont(font); + //覆盖背景绘制 + QRectF tipRect(foregroundRect.right() - d->_textHSpacing - tipWidth, foregroundRect.bottom() - d->_textHSpacing - tipHeight, foregroundRect.width() / 2 - d->_textHSpacing, tipHeight); + painter.setPen(Qt::NoPen); + painter.setBrush(ElaThemeColor(d->_themeMode, BasicBaseDeep)); + QRectF baseRect = tipRect; + baseRect.setRight(tipRect.x() + tipWidth); + baseRect.adjust(-7, -3, 4, 3); + d->_interactiveTipsBaseRect = baseRect; + painter.drawRoundedRect(baseRect, 6, 6); + //文字绘制 + painter.setPen(ElaThemeColor(d->_themeMode, BasicText)); + painter.drawText(tipRect, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, d->_pInteractiveTips); + } + else + { + QRectF tipRect(foregroundRect.right() - d->_textHSpacing - 50, foregroundRect.bottom() - d->_textHSpacing - tipHeight, foregroundRect.width() / 2 - d->_textHSpacing, tipHeight); + tipRect.setRight(tipRect.x() + tipWidth); + tipRect.adjust(-7, -3, 4, 3); + d->_interactiveTipsBaseRect = tipRect; + } + painter.restore(); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaProgressBar.cpp b/3rdparty/ElaWidgetTools/src/ElaProgressBar.cpp new file mode 100644 index 0000000..02b9b36 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaProgressBar.cpp @@ -0,0 +1,86 @@ +#include "ElaProgressBar.h" + +#include +#include + +#include "ElaProgressBarPrivate.h" +#include "ElaProgressBarStyle.h" +ElaProgressBar::ElaProgressBar(QWidget* parent) + : QProgressBar(parent), d_ptr(new ElaProgressBarPrivate()) +{ + Q_D(ElaProgressBar); + setObjectName("ElaProgressBar"); + setStyleSheet("#ElaProgressBar{background-color:transparent;}"); + d->q_ptr = this; + d->_style = new ElaProgressBarStyle(style()); + setStyle(d->_style); + d->_busyAnimation = new QPropertyAnimation(d->_style, "busyStartValue"); + connect(d->_busyAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + d->_style->setProperty("busyEndValue", value.toInt() + 75); + update(); + }); + d->_busyAnimation->setDuration(2000); + d->_busyAnimation->setLoopCount(-1); +} + +ElaProgressBar::~ElaProgressBar() +{ +} + +void ElaProgressBar::setMinimum(int minimum) +{ + Q_D(ElaProgressBar); + if (d->_isBusyAnimation && !(maximum() == 0 && minimum == 0)) + { + d->_isBusyAnimation = false; + d->_busyAnimation->stop(); + } + QProgressBar::setMinimum(minimum); +} + +void ElaProgressBar::setMaximum(int maximum) +{ + Q_D(ElaProgressBar); + if (d->_isBusyAnimation && !(minimum() == 0 && maximum == 0)) + { + d->_isBusyAnimation = false; + d->_busyAnimation->stop(); + } + QProgressBar::setMaximum(maximum); +} + +void ElaProgressBar::paintEvent(QPaintEvent* event) +{ + Q_D(ElaProgressBar); + if (!d->_isBusyAnimation && minimum() == 0 && maximum() == 0) + { + QStyleOptionProgressBar option; + option.initFrom(this); + d->_isBusyAnimation = true; + d->_busyAnimation->setStartValue(-75); + if (orientation() == Qt::Horizontal) + { + d->_busyAnimation->setEndValue(this->width()); + } + else + { + d->_busyAnimation->setEndValue(this->height()); + } + d->_busyAnimation->start(); + } + QProgressBar::paintEvent(event); +} + +void ElaProgressBar::resizeEvent(QResizeEvent* event) +{ + Q_D(ElaProgressBar); + if (orientation() == Qt::Horizontal) + { + d->_busyAnimation->setEndValue(this->width()); + } + else + { + d->_busyAnimation->setEndValue(this->height()); + } + QProgressBar::resizeEvent(event); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaPromotionCard.cpp b/3rdparty/ElaWidgetTools/src/ElaPromotionCard.cpp new file mode 100644 index 0000000..ea7d07c --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaPromotionCard.cpp @@ -0,0 +1,259 @@ +#include "ElaPromotionCard.h" + +#include +#include +#include +#include +#include +#include + +#include "ElaPromotionCardPrivate.h" +#include "ElaTheme.h" +Q_PROPERTY_CREATE_Q_CPP(ElaPromotionCard, int, BorderRadius) +Q_PROPERTY_CREATE_Q_CPP(ElaPromotionCard, QPixmap, CardPixmap) +Q_PROPERTY_CREATE_Q_CPP(ElaPromotionCard, QString, CardTitle) +Q_PROPERTY_CREATE_Q_CPP(ElaPromotionCard, QString, PromotionTitle) +Q_PROPERTY_CREATE_Q_CPP(ElaPromotionCard, QString, Title) +Q_PROPERTY_CREATE_Q_CPP(ElaPromotionCard, QString, SubTitle) +Q_PROPERTY_CREATE_Q_CPP(ElaPromotionCard, QColor, CardTitleColor) +Q_PROPERTY_CREATE_Q_CPP(ElaPromotionCard, QColor, PromotionTitleColor) +Q_PROPERTY_CREATE_Q_CPP(ElaPromotionCard, QColor, PromotionTitleBaseColor) +Q_PROPERTY_CREATE_Q_CPP(ElaPromotionCard, QColor, TitleColor) +Q_PROPERTY_CREATE_Q_CPP(ElaPromotionCard, QColor, SubTitleColor) +Q_PROPERTY_CREATE_Q_CPP(ElaPromotionCard, int, CardTitlePixelSize) +Q_PROPERTY_CREATE_Q_CPP(ElaPromotionCard, int, PromotionTitlePixelSize) +Q_PROPERTY_CREATE_Q_CPP(ElaPromotionCard, int, TitlePixelSize) +Q_PROPERTY_CREATE_Q_CPP(ElaPromotionCard, int, SubTitlePixelSize) +ElaPromotionCard::ElaPromotionCard(QWidget* parent) + : QWidget{parent}, d_ptr(new ElaPromotionCardPrivate()) +{ + Q_D(ElaPromotionCard); + d->q_ptr = this; + d->_pBorderRadius = 5; + d->_pPressRadius = 0; + d->_pHoverOpacity = 0; + d->_pHorizontalCardPixmapRatio = 1; + d->_pVerticalCardPixmapRatio = 1; + d->_pCardTitle = ""; + d->_pPromotionTitle = ""; + d->_pTitle = ""; + d->_pSubTitle = ""; + d->_pCardTitleColor = Qt::white; + d->_pPromotionTitleColor = Qt::white; + d->_pTitleColor = Qt::white; + d->_pSubTitleColor = Qt::white; + d->_pPromotionTitleBaseColor = QColor(0, 0, 0, 120); + d->_pCardTitlePixelSize = 22; + d->_pPromotionTitlePixelSize = 12; + d->_pTitlePixelSize = 25; + d->_pSubTitlePixelSize = 16; + setMouseTracking(true); + setObjectName("ElaPromotionCard"); + setStyleSheet("#ElaPromotionCard{background-color:transparent;}"); + d->_hoverGradient = new QRadialGradient(); + d->_hoverGradient->setRadius(170); + d->_hoverGradient->setColorAt(0, QColor(0xFF, 0xFF, 0xFF, 40)); + d->_hoverGradient->setColorAt(1, QColor(0xFF, 0xFF, 0xFF, 0)); + + d->_pressGradient = new QRadialGradient(); + d->_pressGradient->setRadius(170); + d->_pressGradient->setColorAt(0, QColor(0xFF, 0xFF, 0xFF, 0)); + d->_pressGradient->setColorAt(1, QColor(0xFF, 0xFF, 0xFF, 40)); +} + +ElaPromotionCard::~ElaPromotionCard() +{ + Q_D(const ElaPromotionCard); + delete d->_hoverGradient; + delete d->_pressGradient; +} + +void ElaPromotionCard::setHorizontalCardPixmapRatio(qreal pixmapRatio) +{ + Q_D(ElaPromotionCard); + if (pixmapRatio > 0 && pixmapRatio <= 1) + { + Q_EMIT pHorizontalCardPixmapRatioChanged(); + d->_pHorizontalCardPixmapRatio = pixmapRatio; + } +} + +qreal ElaPromotionCard::getHorizontalCardPixmapRatio() const +{ + Q_D(const ElaPromotionCard); + return d->_pHorizontalCardPixmapRatio; +} + +void ElaPromotionCard::setVerticalCardPixmapRatio(qreal pixmapRatio) +{ + Q_D(ElaPromotionCard); + if (pixmapRatio > 0 && pixmapRatio <= 1) + { + Q_EMIT pVerticalCardPixmapRatioChanged(); + d->_pVerticalCardPixmapRatio = pixmapRatio; + } +} + +qreal ElaPromotionCard::getVerticalCardPixmapRatio() const +{ + Q_D(const ElaPromotionCard); + return d->_pVerticalCardPixmapRatio; +} + +bool ElaPromotionCard::event(QEvent* event) +{ + Q_D(ElaPromotionCard); + switch (event->type()) + { + case QEvent::MouseButtonPress: + { + QMouseEvent* mouseEvent = dynamic_cast(event); + QPropertyAnimation* opacityAnimation = new QPropertyAnimation(d, "pPressOpacity"); + connect(opacityAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + update(); + }); + connect(opacityAnimation, &QPropertyAnimation::finished, this, [=]() { + d->_isPressAnimationFinished = true; + }); + opacityAnimation->setDuration(300); + opacityAnimation->setEasingCurve(QEasingCurve::InQuad); + opacityAnimation->setStartValue(1); + opacityAnimation->setEndValue(0); + opacityAnimation->start(QAbstractAnimation::DeleteWhenStopped); + + QPropertyAnimation* pressAnimation = new QPropertyAnimation(d, "pPressRadius"); + connect(pressAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + d->_pressGradient->setRadius(value.toReal()); + }); + pressAnimation->setDuration(300); + pressAnimation->setEasingCurve(QEasingCurve::InQuad); + pressAnimation->setStartValue(30); + pressAnimation->setEndValue(d->_getLongestDistance(mouseEvent->pos()) * 1.8); + pressAnimation->start(QAbstractAnimation::DeleteWhenStopped); + d->_isPressAnimationFinished = false; + d->_pressGradient->setFocalPoint(mouseEvent->pos()); + d->_pressGradient->setCenter(mouseEvent->pos()); + //点击后隐藏Hover效果 + d->_startHoverOpacityAnimation(false); + break; + } + case QEvent::MouseButtonRelease: + { + Q_EMIT promotionCardClicked(); + break; + } + case QEvent::MouseMove: + { + QMouseEvent* mouseEvent = dynamic_cast(event); + if (d->_pHoverOpacity < 1 && d->_isPressAnimationFinished) + { + d->_startHoverOpacityAnimation(true); + } + if (d->_isPressAnimationFinished) + { + d->_hoverGradient->setCenter(mouseEvent->pos()); + d->_hoverGradient->setFocalPoint(mouseEvent->pos()); + } + update(); + break; + } + case QEvent::Enter: + { + d->_startHoverOpacityAnimation(true); + break; + } + case QEvent::Leave: + { + d->_startHoverOpacityAnimation(false); + break; + } + default: + { + break; + } + } + return QWidget::event(event); +} + +void ElaPromotionCard::paintEvent(QPaintEvent* event) +{ + Q_D(ElaPromotionCard); + QPainter painter(this); + painter.save(); + painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform); + painter.setPen(Qt::NoPen); + //阴影绘制 + eTheme->drawEffectShadow(&painter, rect(), d->_shadowBorderWidth, d->_pBorderRadius); + QRect foregroundRect(d->_shadowBorderWidth, d->_shadowBorderWidth, width() - 2 * d->_shadowBorderWidth, height() - 2 * d->_shadowBorderWidth); + QPainterPath path; + path.addRoundedRect(foregroundRect, d->_pBorderRadius, d->_pBorderRadius); + painter.setClipPath(path); + //背景图片绘制 + if (!d->_pCardPixmap.isNull()) + { + //源区域计算 + QRect pixSourceRect = d->_pCardPixmap.rect(); + qreal horizontalOffset = d->_pCardPixmap.width() * (1 - d->_pHorizontalCardPixmapRatio) / 2; + qreal verticalOffset = d->_pCardPixmap.height() * (1 - d->_pVerticalCardPixmapRatio) / 2; + pixSourceRect.adjust(horizontalOffset, verticalOffset, -horizontalOffset, -verticalOffset); + painter.drawPixmap(foregroundRect, d->_pCardPixmap, pixSourceRect); + } + //文字绘制 + painter.save(); + QFont font = painter.font(); + //卡片标题 + font.setWeight(QFont::Bold); + font.setPixelSize(d->_pCardTitlePixelSize); + painter.setFont(font); + painter.setPen(d->_pCardTitleColor); + painter.drawText(QRect(25, 25, foregroundRect.width() - 25, foregroundRect.height()), Qt::AlignLeft | Qt::AlignTop | Qt::TextSingleLine, d->_pCardTitle); + //标题 + font.setWeight(QFont::Bold); + font.setPixelSize(d->_pTitlePixelSize); + painter.setFont(font); + painter.setPen(d->_pTitleColor); + int titleTextHeight = painter.fontMetrics().height(); + QRect titleRect(25, (height() - titleTextHeight) / 2, foregroundRect.width() - 25, titleTextHeight); + painter.drawText(titleRect, Qt::AlignLeft | Qt::AlignBottom | Qt::TextSingleLine, d->_pTitle); + //推广标题 + if (!d->_pPromotionTitle.isEmpty()) + { + font.setWeight(QFont::Normal); + font.setPixelSize(d->_pPromotionTitlePixelSize); + painter.setFont(font); + int promotionTitleTextWidth = painter.fontMetrics().horizontalAdvance(d->_pPromotionTitle); + int promotionTitleTextHeight = painter.fontMetrics().height(); + QRect promotionTitleTextRect(32, titleRect.top() - promotionTitleTextHeight - 5, foregroundRect.width() / 2 - 25, promotionTitleTextHeight); + //背景绘制 + painter.setPen(Qt::NoPen); + painter.setBrush(d->_pPromotionTitleBaseColor); + painter.drawRoundedRect(QRect(25, promotionTitleTextRect.top() - 2, promotionTitleTextWidth + 14, promotionTitleTextHeight + 4), 8, 8); + //文字绘制 + painter.setPen(d->_pPromotionTitleColor); + painter.drawText(promotionTitleTextRect, Qt::AlignLeft | Qt::AlignBottom | Qt::TextSingleLine, d->_pPromotionTitle); + } + //副标题 + font.setWeight(QFont::Medium); + font.setPixelSize(d->_pSubTitlePixelSize); + painter.setFont(font); + painter.setPen(d->_pSubTitleColor); + painter.drawText(QRect(25, titleRect.bottom(), foregroundRect.width() / 2 - 25, height() / 2), Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, d->_pSubTitle); + + painter.restore(); + //效果阴影绘制 + if (d->_isPressAnimationFinished) + { + //覆盖阴影绘制 + painter.setOpacity(d->_pHoverOpacity); + painter.setBrush(*d->_hoverGradient); + painter.drawEllipse(d->_hoverGradient->center(), d->_hoverGradient->radius(), d->_hoverGradient->radius()); + } + else + { + //点击阴影绘制 + painter.setOpacity(d->_pPressOpacity); + painter.setBrush(*d->_pressGradient); + painter.drawEllipse(d->_pressGradient->center(), d->_pPressRadius, d->_pPressRadius / 1.1); + } + painter.restore(); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaPromotionView.cpp b/3rdparty/ElaWidgetTools/src/ElaPromotionView.cpp new file mode 100644 index 0000000..8e21a80 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaPromotionView.cpp @@ -0,0 +1,196 @@ +#include "ElaPromotionView.h" + +#include +#include +#include + +#include "ElaPromotionCard.h" +#include "ElaPromotionViewPrivate.h" +#include "ElaTheme.h" +ElaPromotionView::ElaPromotionView(QWidget* parent) + : QWidget{parent}, d_ptr(new ElaPromotionViewPrivate()) +{ + Q_D(ElaPromotionView); + d->q_ptr = this; + d->_pCurrentIndex = 0; + d->_pCardExpandWidth = 600; + d->_pCardCollapseWidth = 300; + d->_pIsAutoScroll = false; + d->_pAutoScrollInterval = 5000; + setFixedHeight(300); + setObjectName("ElaPromotionView"); + setStyleSheet("#ElaPromotionView{background-color:transparent;}"); + + d->_autoScrollTimer = new QTimer(this); + connect(d->_autoScrollTimer, &QTimer::timeout, this, [=]() { + if (isVisible() && d->_promotionCardList.count() > 2) + { + d->onPromotionCardClicked(d->_promotionCardList[d->_getAdjacentIndex(Qt::LeftToRight, d->_pCurrentIndex)]); + } + }); + + d->_themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { d->_themeMode = themeMode; }); +} + +ElaPromotionView::~ElaPromotionView() +{ +} + +void ElaPromotionView::setCardExpandWidth(int width) +{ + Q_D(ElaPromotionView); + if (width <= 0) + { + return; + } + d->_pCardExpandWidth = width; + d->_updatePromotionCardGeometry(); +} + +int ElaPromotionView::getCardExpandWidth() const +{ + Q_D(const ElaPromotionView); + return d->_pCardExpandWidth; +} + +void ElaPromotionView::setCardCollapseWidth(int width) +{ + Q_D(ElaPromotionView); + if (width <= 0) + { + return; + } + d->_pCardCollapseWidth = width; + d->_updatePromotionCardGeometry(); +} + +int ElaPromotionView::getCardCollapseWidth() const +{ + Q_D(const ElaPromotionView); + return d->_pCardCollapseWidth; +} + +void ElaPromotionView::setCurrentIndex(int index) +{ + Q_D(ElaPromotionView); + if (index < 0 || index >= d->_promotionCardList.count()) + { + return; + } + d->onPromotionCardClicked(d->_promotionCardList[index]); +} + +int ElaPromotionView::getCurrentIndex() const +{ + Q_D(const ElaPromotionView); + return d->_pCurrentIndex; +} + +void ElaPromotionView::setIsAutoScroll(bool isAutoScroll) +{ + Q_D(ElaPromotionView); + if (isAutoScroll) + { + d->_autoScrollTimer->start(d->_pAutoScrollInterval); + } + else + { + d->_autoScrollTimer->stop(); + } + d->_pIsAutoScroll = isAutoScroll; + Q_EMIT pIsAutoScrollChanged(); +} + +bool ElaPromotionView::getIsAutoScroll() const +{ + Q_D(const ElaPromotionView); + return d->_pIsAutoScroll; +} + +void ElaPromotionView::setAutoScrollInterval(int autoScrollInterval) +{ + Q_D(ElaPromotionView); + if (autoScrollInterval < 400) + { + return; + } + d->_pAutoScrollInterval = autoScrollInterval; + Q_EMIT pAutoScrollIntervalChanged(); +} + +int ElaPromotionView::getAutoScrollInterval() const +{ + Q_D(const ElaPromotionView); + return d->_pAutoScrollInterval; +} + +void ElaPromotionView::appendPromotionCard(ElaPromotionCard* card) +{ + Q_D(ElaPromotionView); + if (!card || d->_promotionCardList.contains(card)) + { + return; + } + card->setMinimumSize(0, 0); + card->setMaximumSize(10000, 10000); + card->setParent(this); + d->_promotionCardList.append(card); + connect(card, &ElaPromotionCard::promotionCardClicked, this, [=]() { + d->onPromotionCardClicked(card); + }); + d->_updatePromotionCardGeometry(); +} + +void ElaPromotionView::wheelEvent(QWheelEvent* event) +{ + Q_D(ElaPromotionView); + if (d->_isAllowSwitch) + { + if (event->angleDelta().y() > 0) + { + //右滑 + d->_isAllowSwitch = false; + QTimer::singleShot(400, this, [=] { + d->_isAllowSwitch = true; + }); + d->onPromotionCardClicked(d->_promotionCardList[d->_getAdjacentIndex(Qt::RightToLeft, d->_pCurrentIndex)]); + } + else + { + //左滑 + d->_isAllowSwitch = false; + QTimer::singleShot(400, this, [=] { + d->_isAllowSwitch = true; + }); + d->onPromotionCardClicked(d->_promotionCardList[d->_getAdjacentIndex(Qt::LeftToRight, d->_pCurrentIndex)]); + } + } + event->accept(); +} + +void ElaPromotionView::paintEvent(QPaintEvent* event) +{ + Q_D(ElaPromotionView); + QPainter painter(this); + painter.save(); + painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + painter.setPen(Qt::NoPen); + painter.setBrush(ElaThemeColor(d->_themeMode, BasicIndicator)); + //页标指示器绘制 + int promotionCardCount = d->_promotionCardList.count(); + bool isCountOdd = promotionCardCount % 2; + QPoint startPoint = isCountOdd ? QPoint(width() / 2 - promotionCardCount / 2 * d->_indicatorSpacing, height() - d->_bottomMargin / 2) : QPoint(width() / 2 - promotionCardCount / 2 * d->_indicatorSpacing - d->_indicatorSpacing / 2, height() - d->_bottomMargin / 2); + for (int i = 0; i < promotionCardCount; i++) + { + if (i == d->_pCurrentIndex) + { + painter.drawEllipse(QPointF(startPoint.x() + i * d->_indicatorSpacing, startPoint.y()), 3.5, 3.5); + } + else + { + painter.drawEllipse(QPointF(startPoint.x() + i * d->_indicatorSpacing, startPoint.y()), 2.5, 2.5); + } + } + painter.restore(); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaPushButton.cpp b/3rdparty/ElaWidgetTools/src/ElaPushButton.cpp new file mode 100644 index 0000000..d9e06b3 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaPushButton.cpp @@ -0,0 +1,122 @@ +#include "ElaPushButton.h" + +#include +#include + +#include "ElaTheme.h" +#include "private/ElaPushButtonPrivate.h" +Q_PROPERTY_CREATE_Q_CPP(ElaPushButton, int, BorderRadius) +Q_PROPERTY_CREATE_Q_CPP(ElaPushButton, QColor, LightDefaultColor) +Q_PROPERTY_CREATE_Q_CPP(ElaPushButton, QColor, DarkDefaultColor) +Q_PROPERTY_CREATE_Q_CPP(ElaPushButton, QColor, LightHoverColor) +Q_PROPERTY_CREATE_Q_CPP(ElaPushButton, QColor, DarkHoverColor) +Q_PROPERTY_CREATE_Q_CPP(ElaPushButton, QColor, LightPressColor) +Q_PROPERTY_CREATE_Q_CPP(ElaPushButton, QColor, DarkPressColor) +ElaPushButton::ElaPushButton(QWidget* parent) + : QPushButton(parent), d_ptr(new ElaPushButtonPrivate()) +{ + Q_D(ElaPushButton); + d->q_ptr = this; + d->_pBorderRadius = 3; + d->_themeMode = eTheme->getThemeMode(); + d->_pLightDefaultColor = ElaThemeColor(ElaThemeType::Light, BasicBase); + d->_pDarkDefaultColor = ElaThemeColor(ElaThemeType::Dark, BasicBase); + d->_pLightHoverColor = ElaThemeColor(ElaThemeType::Light, BasicHover); + d->_pDarkHoverColor = ElaThemeColor(ElaThemeType::Dark, BasicHover); + d->_pLightPressColor = ElaThemeColor(ElaThemeType::Light, BasicPress); + d->_pDarkPressColor = ElaThemeColor(ElaThemeType::Dark, BasicPress); + d->_lightTextColor = ElaThemeColor(ElaThemeType::Light, BasicText); + d->_darkTextColor = ElaThemeColor(ElaThemeType::Dark, BasicText); + setMouseTracking(true); + setFixedHeight(38); + QFont font = this->font(); + font.setPixelSize(15); + setFont(font); + setObjectName("ElaPushButton"); + setStyleSheet("#ElaPushButton{background-color:transparent;}"); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { + d->_themeMode = themeMode; + }); +} + +ElaPushButton::ElaPushButton(QString text, QWidget* parent) + : ElaPushButton(parent) +{ + setText(text); +} + +ElaPushButton::~ElaPushButton() +{ +} + +void ElaPushButton::setLightTextColor(QColor color) +{ + Q_D(ElaPushButton); + d->_lightTextColor = color; +} + +QColor ElaPushButton::getLightTextColor() const +{ + Q_D(const ElaPushButton); + return d->_lightTextColor; +} + +void ElaPushButton::setDarkTextColor(QColor color) +{ + Q_D(ElaPushButton); + d->_darkTextColor = color; +} + +QColor ElaPushButton::getDarkTextColor() const +{ + Q_D(const ElaPushButton); + return d->_darkTextColor; +} + +void ElaPushButton::mousePressEvent(QMouseEvent* event) +{ + Q_D(ElaPushButton); + d->_isPressed = true; + QPushButton::mousePressEvent(event); +} + +void ElaPushButton::mouseReleaseEvent(QMouseEvent* event) +{ + Q_D(ElaPushButton); + d->_isPressed = false; + QPushButton::mouseReleaseEvent(event); +} + +void ElaPushButton::paintEvent(QPaintEvent* event) +{ + Q_D(ElaPushButton); + QPainter painter(this); + painter.setRenderHints(QPainter::SmoothPixmapTransform | QPainter::Antialiasing | QPainter::TextAntialiasing); + // 高性能阴影 + eTheme->drawEffectShadow(&painter, rect(), d->_shadowBorderWidth, d->_pBorderRadius); + + // 背景绘制 + painter.save(); + QRect foregroundRect(d->_shadowBorderWidth, d->_shadowBorderWidth, width() - 2 * (d->_shadowBorderWidth), height() - 2 * d->_shadowBorderWidth); + if (d->_themeMode == ElaThemeType::Light) + { + painter.setPen(ElaThemeColor(ElaThemeType::Light, BasicBorder)); + painter.setBrush(isEnabled() ? d->_isPressed ? d->_pLightPressColor : (underMouse() ? d->_pLightHoverColor : d->_pLightDefaultColor) : ElaThemeColor(d->_themeMode, BasicDisable)); + } + else + { + painter.setPen(Qt::NoPen); + painter.setBrush(isEnabled() ? d->_isPressed ? d->_pDarkPressColor : (underMouse() ? d->_pDarkHoverColor : d->_pDarkDefaultColor) : ElaThemeColor(d->_themeMode, BasicDisable)); + } + painter.drawRoundedRect(foregroundRect, d->_pBorderRadius, d->_pBorderRadius); + // 底边线绘制 + if (!d->_isPressed) + { + painter.setPen(ElaThemeColor(d->_themeMode, BasicBaseLine)); + painter.drawLine(foregroundRect.x() + d->_pBorderRadius, height() - d->_shadowBorderWidth, foregroundRect.width(), height() - d->_shadowBorderWidth); + } + //文字绘制 + painter.setPen(isEnabled() ? d->_themeMode == ElaThemeType::Light ? d->_lightTextColor : d->_darkTextColor : ElaThemeColor(d->_themeMode, BasicTextDisable)); + painter.drawText(foregroundRect, Qt::AlignCenter, text()); + painter.restore(); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaRadioButton.cpp b/3rdparty/ElaWidgetTools/src/ElaRadioButton.cpp new file mode 100644 index 0000000..c5e9383 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaRadioButton.cpp @@ -0,0 +1,28 @@ +#include "ElaRadioButton.h" + +#include "DeveloperComponents/ElaRadioButtonStyle.h" +#include "ElaTheme.h" +#include "private/ElaRadioButtonPrivate.h" +ElaRadioButton::ElaRadioButton(QWidget* parent) + : QRadioButton(parent), d_ptr(new ElaRadioButtonPrivate()) +{ + Q_D(ElaRadioButton); + d->q_ptr = this; + setFixedHeight(20); + QFont font = this->font(); + font.setPixelSize(15); + setFont(font); + setStyle(new ElaRadioButtonStyle(style())); + d->onThemeChanged(eTheme->getThemeMode()); + connect(eTheme, &ElaTheme::themeModeChanged, d, &ElaRadioButtonPrivate::onThemeChanged); +} + +ElaRadioButton::ElaRadioButton(const QString& text, QWidget* parent) + : ElaRadioButton(parent) +{ + setText(text); +} + +ElaRadioButton::~ElaRadioButton() +{ +} diff --git a/3rdparty/ElaWidgetTools/src/ElaReminderCard.cpp b/3rdparty/ElaWidgetTools/src/ElaReminderCard.cpp new file mode 100644 index 0000000..2e4fe12 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaReminderCard.cpp @@ -0,0 +1,115 @@ +#include "ElaReminderCard.h" + +#include +#include +#include + +#include "ElaTheme.h" +#include "private/ElaReminderCardPrivate.h" +Q_PROPERTY_CREATE_Q_CPP(ElaReminderCard, int, BorderRadius) +Q_PROPERTY_CREATE_Q_CPP(ElaReminderCard, QString, Title); +Q_PROPERTY_CREATE_Q_CPP(ElaReminderCard, QString, SubTitle); +Q_PROPERTY_CREATE_Q_CPP(ElaReminderCard, int, TitlePixelSize); +Q_PROPERTY_CREATE_Q_CPP(ElaReminderCard, int, SubTitlePixelSize); +Q_PROPERTY_CREATE_Q_CPP(ElaReminderCard, int, TitleSpacing); +Q_PROPERTY_CREATE_Q_CPP(ElaReminderCard, QPixmap, CardPixmap); +Q_PROPERTY_CREATE_Q_CPP(ElaReminderCard, QSize, CardPixmapSize); +Q_PROPERTY_CREATE_Q_CPP(ElaReminderCard, int, CardPixmapBorderRadius) +Q_PROPERTY_CREATE_Q_CPP(ElaReminderCard, ElaCardPixType::PixMode, CardPixMode); + +ElaReminderCard::ElaReminderCard(QWidget* parent) + : QPushButton{parent}, d_ptr(new ElaReminderCardPrivate()) +{ + Q_D(ElaReminderCard); + d->q_ptr = this; + d->_pBorderRadius = 6; + d->_pTitlePixelSize = 15; + d->_pSubTitlePixelSize = 12; + setFixedSize(330, 105); + d->_pCardPixmapSize = QSize(79, 79); + d->_pTitleSpacing = 3; + d->_pCardPixmapBorderRadius = 6; + d->_pCardPixMode = ElaCardPixType::PixMode::Default; + d->_themeMode = eTheme->getThemeMode(); + setMouseTracking(true); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { d->_themeMode = themeMode; }); +} + +ElaReminderCard::~ElaReminderCard() +{ +} + +void ElaReminderCard::setCardPixmapSize(int width, int height) +{ + Q_D(ElaReminderCard); + d->_pCardPixmapSize = QSize(width, height); + Q_EMIT pCardPixmapSizeChanged(); +} + +void ElaReminderCard::paintEvent(QPaintEvent* event) +{ + Q_D(ElaReminderCard); + QPainter painter(this); + painter.setRenderHints(QPainter::SmoothPixmapTransform | QPainter::Antialiasing | QPainter::TextAntialiasing); + // 高性能阴影 + eTheme->drawEffectShadow(&painter, rect(), d->_shadowBorderWidth, d->_pBorderRadius); + + // 背景绘制 + painter.save(); + painter.setRenderHints(QPainter::SmoothPixmapTransform | QPainter::Antialiasing | QPainter::TextAntialiasing); + painter.setPen(ElaThemeColor(d->_themeMode, BasicBorder)); + painter.setBrush(underMouse() ? ElaThemeColor(d->_themeMode, BasicHoverAlpha) : ElaThemeColor(d->_themeMode, BasicBaseAlpha)); + QRect foregroundRect(d->_shadowBorderWidth, d->_shadowBorderWidth, width() - 2 * d->_shadowBorderWidth, height() - 2 * d->_shadowBorderWidth); + int width = foregroundRect.width(); + painter.drawRoundedRect(foregroundRect, d->_pBorderRadius, d->_pBorderRadius); + + // 图片绘制 + int pixLeftMargin = width / 28; + if (!d->_pCardPixmap.isNull()) + { + painter.save(); + QPainterPath path; + if (d->_pCardPixMode == ElaCardPixType::PixMode::Ellipse) + { + path.addEllipse(QPointF(d->_pCardPixmapSize.width() / 2 + pixLeftMargin, height() / 2), d->_pCardPixmapSize.width() / 2, d->_pCardPixmapSize.height() / 2); + painter.setClipPath(path); + painter.drawPixmap(QRect(pixLeftMargin + d->_shadowBorderWidth, (height() - d->_pCardPixmapSize.height()) / 2, d->_pCardPixmapSize.width(), d->_pCardPixmapSize.height()), d->_pCardPixmap); // rect为绘制区域,image为要绘制的图片 + } + else if (d->_pCardPixMode == ElaCardPixType::PixMode::Default) + { + painter.drawPixmap(pixLeftMargin + d->_shadowBorderWidth, (height() - d->_pCardPixmapSize.height()) / 2, d->_pCardPixmapSize.width(), d->_pCardPixmapSize.height(), d->_pCardPixmap); + } + else if (d->_pCardPixMode == ElaCardPixType::PixMode::RoundedRect) + { + path.addRoundedRect(QRectF(pixLeftMargin, (height() - d->_pCardPixmapSize.height()) / 2, d->_pCardPixmapSize.width(), d->_pCardPixmapSize.height()), d->_pCardPixmapBorderRadius, d->_pCardPixmapBorderRadius); + painter.setClipPath(path); + painter.drawPixmap(pixLeftMargin + d->_shadowBorderWidth, (height() - d->_pCardPixmapSize.height()) / 2, d->_pCardPixmapSize.width(), d->_pCardPixmapSize.height(), d->_pCardPixmap); + } + painter.restore(); + } + + // 焦点圆型绘制 + painter.save(); + painter.setBrush(ElaThemeColor(d->_themeMode, PrimaryNormal)); + painter.drawEllipse(QPointF(width * 0.95 + d->_shadowBorderWidth, height() * 0.25), height() / 17, height() / 17); + painter.restore(); + + // 文字绘制 + QFont font = this->font(); + font.setWeight(QFont::Bold); + font.setPixelSize(d->_pTitlePixelSize); + painter.setPen(ElaThemeColor(d->_themeMode, BasicText)); + painter.setFont(font); + // 主标题 + int textLeftMargin = width / 11; + int textStartX = d->_pCardPixmapSize.width() + textLeftMargin + d->_shadowBorderWidth; + int textWidth = foregroundRect.width() - textStartX - d->_shadowBorderWidth - 5; + painter.drawText(QRect(textStartX, foregroundRect.y(), textWidth, this->height() / 2 - d->_pTitleSpacing), Qt::AlignLeft | Qt::AlignBottom | Qt::TextWordWrap | Qt::TextWrapAnywhere, d->_pTitle); + // 副标题 + font.setWeight(QFont::Normal); + font.setPixelSize(d->_pSubTitlePixelSize); + painter.setFont(font); + painter.setPen(ElaThemeColor(d->_themeMode, BasicDetailsText)); + painter.drawText(QRect(textStartX, height() / 2 + d->_pTitleSpacing, textWidth, height() / 2 - d->_pTitleSpacing), Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap | Qt::TextWrapAnywhere, d->_pSubTitle); + painter.restore(); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaScrollArea.cpp b/3rdparty/ElaWidgetTools/src/ElaScrollArea.cpp new file mode 100644 index 0000000..5a7dd29 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaScrollArea.cpp @@ -0,0 +1,79 @@ +#include "ElaScrollArea.h" + +#include +#include + +#include "ElaScrollAreaPrivate.h" +#include "ElaScrollBar.h" + +ElaScrollArea::ElaScrollArea(QWidget* parent) + : QScrollArea(parent), d_ptr(new ElaScrollAreaPrivate()) +{ + Q_D(ElaScrollArea); + d->q_ptr = this; + setObjectName("ElaScrollArea"); + setStyleSheet("#ElaScrollArea{background-color:transparent;border:0px;}"); + setHorizontalScrollBar(new ElaScrollBar(this)); + setVerticalScrollBar(new ElaScrollBar(this)); + QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); +} + +ElaScrollArea::~ElaScrollArea() +{ +} + +void ElaScrollArea::setIsGrabGesture(bool isEnable, qreal mousePressEventDelay) +{ + if (isEnable) + { + QScroller::grabGesture(this->viewport(), QScroller::LeftMouseButtonGesture); + QScrollerProperties properties = QScroller::scroller(this->viewport())->scrollerProperties(); + properties.setScrollMetric(QScrollerProperties::MousePressEventDelay, mousePressEventDelay); + properties.setScrollMetric(QScrollerProperties::OvershootDragResistanceFactor, 0.35); + properties.setScrollMetric(QScrollerProperties::OvershootScrollTime, 0.5); + properties.setScrollMetric(QScrollerProperties::FrameRate, QScrollerProperties::Fps60); + QScroller::scroller(this->viewport())->setScrollerProperties(properties); + } + else + { + QScroller::ungrabGesture(this->viewport()); + } +} + +void ElaScrollArea::setIsOverShoot(Qt::Orientation orientation, bool isEnable) +{ + QScrollerProperties properties = QScroller::scroller(this->viewport())->scrollerProperties(); + properties.setScrollMetric(orientation == Qt::Horizontal ? QScrollerProperties::HorizontalOvershootPolicy : QScrollerProperties::VerticalOvershootPolicy, isEnable ? QScrollerProperties::OvershootAlwaysOn : QScrollerProperties::OvershootAlwaysOff); + QScroller::scroller(this->viewport())->setScrollerProperties(properties); +} + +bool ElaScrollArea::getIsOverShoot(Qt::Orientation orientation) const +{ + QScrollerProperties properties = QScroller::scroller(this->viewport())->scrollerProperties(); + return properties.scrollMetric(orientation == Qt::Horizontal ? QScrollerProperties::HorizontalOvershootPolicy : QScrollerProperties::VerticalOvershootPolicy).toBool(); +} + +void ElaScrollArea::setIsAnimation(Qt::Orientation orientation, bool isAnimation) +{ + if (orientation == Qt::Horizontal) + { + dynamic_cast(this->horizontalScrollBar())->setIsAnimation(isAnimation); + } + else + { + dynamic_cast(this->verticalScrollBar())->setIsAnimation(isAnimation); + } +} + +bool ElaScrollArea::getIsAnimation(Qt::Orientation orientation) const +{ + if (orientation == Qt::Horizontal) + { + return dynamic_cast(this->horizontalScrollBar())->getIsAnimation(); + } + else + { + return dynamic_cast(this->verticalScrollBar())->getIsAnimation(); + } +} diff --git a/3rdparty/ElaWidgetTools/src/ElaScrollBar.cpp b/3rdparty/ElaWidgetTools/src/ElaScrollBar.cpp new file mode 100644 index 0000000..5fa347e --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaScrollBar.cpp @@ -0,0 +1,244 @@ +#include "ElaScrollBar.h" + +#include +#include +#include +#include +#include +#include + +#include "ElaMenu.h" +#include "ElaScrollBarStyle.h" +#include "private/ElaScrollBarPrivate.h" +Q_PROPERTY_CREATE_Q_CPP(ElaScrollBar, bool, IsAnimation) +Q_PROPERTY_CREATE_Q_CPP(ElaScrollBar, qreal, SpeedLimit) +ElaScrollBar::ElaScrollBar(QWidget* parent) + : QScrollBar(parent), d_ptr(new ElaScrollBarPrivate()) +{ + Q_D(ElaScrollBar); + d->q_ptr = this; + setSingleStep(1); + setObjectName("ElaScrollBar"); + setAttribute(Qt::WA_OpaquePaintEvent, false); + d->_pSpeedLimit = 20; + d->_pTargetMaximum = 0; + d->_pIsAnimation = false; + connect(this, &ElaScrollBar::rangeChanged, d, &ElaScrollBarPrivate::onRangeChanged); + ElaScrollBarStyle* scrollBarStyle = new ElaScrollBarStyle(style()); + scrollBarStyle->setScrollBar(this); + setStyle(scrollBarStyle); + d->_slideSmoothAnimation = new QPropertyAnimation(this, "value"); + d->_slideSmoothAnimation->setEasingCurve(QEasingCurve::OutSine); + d->_slideSmoothAnimation->setDuration(300); + connect(d->_slideSmoothAnimation, &QPropertyAnimation::finished, this, [=]() { d->_scrollValue = value(); }); + + d->_expandTimer = new QTimer(this); + connect(d->_expandTimer, &QTimer::timeout, this, [=]() { + d->_expandTimer->stop(); + d->_isExpand = underMouse(); + scrollBarStyle->startExpandAnimation(d->_isExpand); + }); +} + +ElaScrollBar::ElaScrollBar(Qt::Orientation orientation, QWidget* parent) + : ElaScrollBar(parent) +{ + setOrientation(orientation); +} + +ElaScrollBar::ElaScrollBar(QScrollBar* originScrollBar, QAbstractScrollArea* parent) + : ElaScrollBar(parent) +{ + Q_D(ElaScrollBar); + if (!originScrollBar || !parent) + { + qCritical() << "Invalid origin or parent!"; + return; + } + d->_originScrollArea = parent; + Qt::Orientation orientation = originScrollBar->orientation(); + setOrientation(orientation); + orientation == Qt::Horizontal ? parent->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff) : parent->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + parent->installEventFilter(this); + + d->_originScrollBar = originScrollBar; + d->_initAllConfig(); + + connect(d->_originScrollBar, &QScrollBar::valueChanged, this, [=](int value) { + d->_handleScrollBarValueChanged(this, value); + }); + connect(this, &QScrollBar::valueChanged, this, [=](int value) { + d->_handleScrollBarValueChanged(d->_originScrollBar, value); + }); + connect(d->_originScrollBar, &QScrollBar::rangeChanged, this, [=](int min, int max) { + d->_handleScrollBarRangeChanged(min, max); + }); +} + +ElaScrollBar::~ElaScrollBar() +{ +} + +bool ElaScrollBar::event(QEvent* event) +{ + Q_D(ElaScrollBar); + switch (event->type()) + { + case QEvent::Enter: + { + d->_expandTimer->stop(); + if (!d->_isExpand) + { + d->_expandTimer->start(350); + } + break; + } + case QEvent::Leave: + { + d->_expandTimer->stop(); + if (d->_isExpand) + { + d->_expandTimer->start(350); + } + break; + } + default: + { + break; + } + } + return QScrollBar::event(event); +} + +bool ElaScrollBar::eventFilter(QObject* watched, QEvent* event) +{ + Q_D(ElaScrollBar); + switch (event->type()) + { + case QEvent::Show: + case QEvent::Resize: + case QEvent::LayoutRequest: + { + d->_handleScrollBarGeometry(); + break; + } + default: + { + break; + } + } + return QScrollBar::eventFilter(watched, event); +} + +void ElaScrollBar::mousePressEvent(QMouseEvent* event) +{ + Q_D(ElaScrollBar); + d->_slideSmoothAnimation->stop(); + QScrollBar::mousePressEvent(event); + d->_scrollValue = value(); +} + +void ElaScrollBar::mouseReleaseEvent(QMouseEvent* event) +{ + Q_D(ElaScrollBar); + d->_slideSmoothAnimation->stop(); + QScrollBar::mouseReleaseEvent(event); + d->_scrollValue = value(); +} + +void ElaScrollBar::mouseMoveEvent(QMouseEvent* event) +{ + Q_D(ElaScrollBar); + d->_slideSmoothAnimation->stop(); + QScrollBar::mouseMoveEvent(event); + d->_scrollValue = value(); +} + +void ElaScrollBar::wheelEvent(QWheelEvent* event) +{ + Q_D(ElaScrollBar); + int verticalDelta = event->angleDelta().y(); + if (d->_slideSmoothAnimation->state() == QAbstractAnimation::Stopped) + { + d->_scrollValue = value(); + } + if (verticalDelta != 0) + { + if ((value() == minimum() && verticalDelta > 0) || (value() == maximum() && verticalDelta < 0)) + { + QScrollBar::wheelEvent(event); + return; + } + d->_scroll(event->modifiers(), verticalDelta); + } + else + { + int horizontalDelta = event->angleDelta().x(); + if ((value() == minimum() && horizontalDelta > 0) || (value() == maximum() && horizontalDelta < 0)) + { + QScrollBar::wheelEvent(event); + return; + } + d->_scroll(event->modifiers(), horizontalDelta); + } + event->accept(); +} + +void ElaScrollBar::contextMenuEvent(QContextMenuEvent* event) +{ + Q_D(ElaScrollBar); + bool horiz = this->orientation() == Qt::Horizontal; + QPointer menu = new ElaMenu(this); + menu->setMenuItemHeight(27); + // Scroll here + QAction* actScrollHere = menu->addElaIconAction(ElaIconType::UpDownLeftRight, tr("滚动到此处")); + menu->addSeparator(); + // Left edge Top + QAction* actScrollTop = menu->addElaIconAction(horiz ? ElaIconType::ArrowLeftToLine : ElaIconType::ArrowUpToLine, horiz ? tr("左边缘") : tr("顶端")); + // Right edge Bottom + QAction* actScrollBottom = menu->addElaIconAction(horiz ? ElaIconType::ArrowRightToLine : ElaIconType::ArrowDownToLine, horiz ? tr("右边缘") : tr("底部")); + menu->addSeparator(); + // Page left Page up + QAction* actPageUp = menu->addElaIconAction(horiz ? ElaIconType::AnglesLeft : ElaIconType::AnglesUp, horiz ? tr("向左翻页") : tr("向上翻页")); + //Page right Page down + QAction* actPageDn = menu->addElaIconAction(horiz ? ElaIconType::AnglesRight : ElaIconType::AnglesDown, horiz ? tr("向右翻页") : tr("向下翻页")); + menu->addSeparator(); + //Scroll left Scroll up + QAction* actScrollUp = menu->addElaIconAction(horiz ? ElaIconType::AngleLeft : ElaIconType::AngleUp, horiz ? tr("向左滚动") : tr("向上滚动")); + //Scroll right Scroll down + QAction* actScrollDn = menu->addElaIconAction(horiz ? ElaIconType::AngleRight : ElaIconType::AngleDown, horiz ? tr("向右滚动") : tr("向下滚动")); + QAction* actionSelected = menu->exec(event->globalPos()); + delete menu; + if (!actionSelected) + { + return; + } + if (actionSelected == actScrollHere) + { + setValue(d->_pixelPosToRangeValue(horiz ? event->pos().x() : event->pos().y())); + } + else if (actionSelected == actScrollTop) + { + triggerAction(QAbstractSlider::SliderToMinimum); + } + else if (actionSelected == actScrollBottom) + { + triggerAction(QAbstractSlider::SliderToMaximum); + } + else if (actionSelected == actPageUp) + { + triggerAction(QAbstractSlider::SliderPageStepSub); + } + else if (actionSelected == actPageDn) + { + triggerAction(QAbstractSlider::SliderPageStepAdd); + } + else if (actionSelected == actScrollUp) + { + triggerAction(QAbstractSlider::SliderSingleStepSub); + } + else if (actionSelected == actScrollDn) + { + triggerAction(QAbstractSlider::SliderSingleStepAdd); + } +} diff --git a/3rdparty/ElaWidgetTools/src/ElaScrollPage.cpp b/3rdparty/ElaWidgetTools/src/ElaScrollPage.cpp new file mode 100644 index 0000000..c5bec6e --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaScrollPage.cpp @@ -0,0 +1,145 @@ +#include "ElaScrollPage.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ElaBreadcrumbBar.h" +#include "ElaNavigationRouter.h" +#include "ElaScrollArea.h" +#include "ElaScrollBar.h" +#include "private/ElaScrollPagePrivate.h" +ElaScrollPage::ElaScrollPage(QWidget* parent) + : QWidget(parent), d_ptr(new ElaScrollPagePrivate()) +{ + Q_D(ElaScrollPage); + setProperty("ElaBaseClassName", "ElaScrollPage"); + d->q_ptr = this; + d->_pCustomWidget = nullptr; + d->_breadcrumbBar = new ElaBreadcrumbBar(this); + d->_breadcrumbBar->setTextPixelSize(28); + connect(d->_breadcrumbBar, &ElaBreadcrumbBar::breadcrumbClicked, this, [=](QString breadcrumb, QStringList lastBreadcrumbList) { + if (d->_centralWidgetMap.contains(breadcrumb)) + { + int widgetIndex = d->_centralWidgetMap.value(breadcrumb); + d->_switchCentralStackIndex(widgetIndex, d->_navigationTargetIndex); + d->_navigationTargetIndex = widgetIndex; + QVariantMap routeData = QVariantMap(); + routeData.insert("ElaScrollPageCheckSumKey", "BreadcrumbClicked"); + routeData.insert("LastBreadcrumbList", lastBreadcrumbList); + ElaNavigationRouter::getInstance()->navigationRoute(d,"onNavigationRouteBack", routeData); + } }); + d->_pageTitleLayout = new QHBoxLayout(); + d->_pageTitleLayout->setContentsMargins(0, 0, 0, 0); + d->_pageTitleLayout->addWidget(d->_breadcrumbBar); + + d->_centralStackedWidget = new QStackedWidget(this); + d->_centralStackedWidget->setContentsMargins(0, 0, 0, 0); + + d->_mainLayout = new QVBoxLayout(this); + d->_mainLayout->setContentsMargins(0, 0, 0, 0); + d->_mainLayout->addLayout(d->_pageTitleLayout); + d->_mainLayout->addWidget(d->_centralStackedWidget); + setContentsMargins(20, 20, 0, 0); +} + +ElaScrollPage::~ElaScrollPage() +{ +} + +void ElaScrollPage::addCentralWidget(QWidget* centralWidget, bool isWidgetResizeable, bool isVerticalGrabGesture, qreal mousePressEventDelay) +{ + Q_D(ElaScrollPage); + if (!centralWidget) + { + return; + } + if (centralWidget->windowTitle().isEmpty()) + { + centralWidget->setWindowTitle(QString("Page_%1").arg(d->_centralStackedWidget->count())); + } + if (d->_centralStackedWidget->count() == 0) + { + d->_breadcrumbBar->appendBreadcrumb(centralWidget->windowTitle()); + } + ElaScrollArea* scrollArea = new ElaScrollArea(this); + scrollArea->setMouseTracking(true); + scrollArea->setIsAnimation(Qt::Vertical, true); + scrollArea->setWidgetResizable(isWidgetResizeable); + scrollArea->setIsGrabGesture(isVerticalGrabGesture, mousePressEventDelay); + scrollArea->setIsOverShoot(Qt::Vertical, true); + scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + ElaScrollBar* floatVScrollBar = new ElaScrollBar(scrollArea->verticalScrollBar(), scrollArea); + floatVScrollBar->setIsAnimation(true); + scrollArea->setWidget(centralWidget); + centralWidget->setObjectName("ElaScrollPage_CentralPage"); + centralWidget->setStyleSheet("#ElaScrollPage_CentralPage{background-color:transparent;}"); + d->_centralWidgetMap.insert(centralWidget->windowTitle(), d->_centralStackedWidget->count()); + d->_centralStackedWidget->addWidget(scrollArea); +} + +void ElaScrollPage::setCustomWidget(QWidget* widget) +{ + Q_D(ElaScrollPage); + if (!widget || widget == this) + { + return; + } + if (d->_pCustomWidget) + { + d->_mainLayout->removeWidget(d->_pCustomWidget); + } + d->_mainLayout->insertWidget(1, widget); + d->_pCustomWidget = widget; + Q_EMIT pCustomWidgetChanged(); +} + +QWidget* ElaScrollPage::getCustomWidget() const +{ + Q_D(const ElaScrollPage); + return d->_pCustomWidget; +} + +void ElaScrollPage::navigation(int widgetIndex, bool isLogRoute) +{ + Q_D(ElaScrollPage); + if (widgetIndex >= d->_centralStackedWidget->count() || d->_navigationTargetIndex == widgetIndex) + { + return; + } + d->_switchCentralStackIndex(widgetIndex, d->_navigationTargetIndex); + d->_navigationTargetIndex = widgetIndex; + if (isLogRoute) + { + QVariantMap routeData = QVariantMap(); + routeData.insert("ElaScrollPageCheckSumKey", "Navigation"); + QStringList breadcrumbList = d->_breadcrumbBar->getBreadcrumbList(); + routeData.insert("ElaPageTitle", breadcrumbList.last()); + ElaNavigationRouter::getInstance()->navigationRoute(d, "onNavigationRouteBack", routeData); + } + d->_breadcrumbBar->appendBreadcrumb(d->_centralWidgetMap.key(widgetIndex)); +} + +void ElaScrollPage::setPageTitleSpacing(int spacing) +{ + Q_D(ElaScrollPage); + d->_pageTitleLayout->takeAt(0); + d->_pageTitleLayout->addSpacing(spacing); + d->_pageTitleLayout->addWidget(d->_breadcrumbBar); +} + +int ElaScrollPage::getPageTitleSpacing() const +{ + return d_ptr->_pageTitleSpacing; +} + +void ElaScrollPage::setTitleVisible(bool isVisible) +{ + Q_D(ElaScrollPage); + d->_breadcrumbBar->setVisible(isVisible); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaScrollPageArea.cpp b/3rdparty/ElaWidgetTools/src/ElaScrollPageArea.cpp new file mode 100644 index 0000000..f54df0f --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaScrollPageArea.cpp @@ -0,0 +1,34 @@ +#include "ElaScrollPageArea.h" + +#include +#include + +#include "ElaTheme.h" +#include "private/ElaScrollPageAreaPrivate.h" +Q_PROPERTY_CREATE_Q_CPP(ElaScrollPageArea, int, BorderRadius) +ElaScrollPageArea::ElaScrollPageArea(QWidget* parent) + : QWidget{parent}, d_ptr(new ElaScrollPageAreaPrivate()) +{ + Q_D(ElaScrollPageArea); + d->q_ptr = this; + d->_pBorderRadius = 6; + setFixedHeight(75); + d->_themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { d->_themeMode = themeMode; }); +} + +ElaScrollPageArea::~ElaScrollPageArea() +{ +} + +void ElaScrollPageArea::paintEvent(QPaintEvent* event) +{ + Q_D(ElaScrollPageArea); + QPainter painter(this); + painter.save(); + painter.setPen(ElaThemeColor(d->_themeMode, BasicBorder)); + painter.setBrush(ElaThemeColor(d->_themeMode, BasicBaseAlpha)); + QRect foregroundRect(1, 1, width() - 2, height() - 2); + painter.drawRoundedRect(foregroundRect, d->_pBorderRadius, d->_pBorderRadius); + painter.restore(); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaSlider.cpp b/3rdparty/ElaWidgetTools/src/ElaSlider.cpp new file mode 100644 index 0000000..55f90f6 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaSlider.cpp @@ -0,0 +1,21 @@ +#include "ElaSlider.h" + +#include + +#include "DeveloperComponents/ElaSliderStyle.h" +ElaSlider::ElaSlider(QWidget* parent) + : QSlider(parent) +{ + setOrientation(Qt::Horizontal); + setStyle(new ElaSliderStyle(style())); +} + +ElaSlider::ElaSlider(Qt::Orientation orientation, QWidget* parent) + : QSlider(orientation, parent) +{ + setStyle(new ElaSliderStyle(style())); +} + +ElaSlider::~ElaSlider() +{ +} diff --git a/3rdparty/ElaWidgetTools/src/ElaSpinBox.cpp b/3rdparty/ElaWidgetTools/src/ElaSpinBox.cpp new file mode 100644 index 0000000..a42de20 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaSpinBox.cpp @@ -0,0 +1,66 @@ +#include "ElaSpinBox.h" + +#include +#include +#include + +#include "DeveloperComponents/ElaSpinBoxStyle.h" +#include "ElaMenu.h" +#include "ElaSpinBoxPrivate.h" +#include "ElaTheme.h" +ElaSpinBox::ElaSpinBox(QWidget* parent) + : QSpinBox(parent), d_ptr(new ElaSpinBoxPrivate()) +{ + Q_D(ElaSpinBox); + d->q_ptr = this; + setFixedSize(120, 30); + setStyle(new ElaSpinBoxStyle(style())); + lineEdit()->setAlignment(Qt::AlignCenter); + lineEdit()->setStyleSheet("background-color:transparent"); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { + QPalette palette; + palette.setColor(QPalette::Base, Qt::transparent); + palette.setColor(QPalette::Text, ElaThemeColor(themeMode, BasicText)); + lineEdit()->setPalette(palette); + }); +} + +ElaSpinBox::~ElaSpinBox() +{ +} + +void ElaSpinBox::contextMenuEvent(QContextMenuEvent* event) +{ + Q_D(ElaSpinBox); + ElaMenu* menu = d->_createStandardContextMenu(); + if (!menu) + { + return; + } + menu->addSeparator(); + const uint se = stepEnabled(); + QAction* up = menu->addElaIconAction(ElaIconType::Plus, tr("增加")); + up->setEnabled(se & StepUpEnabled); + QAction* down = menu->addElaIconAction(ElaIconType::Minus, tr("减少")); + down->setEnabled(se & StepDownEnabled); + menu->addSeparator(); + + const QAbstractSpinBox* that = this; + const QPoint pos = (event->reason() == QContextMenuEvent::Mouse) + ? event->globalPos() + : mapToGlobal(QPoint(event->pos().x(), 0)) + QPoint(width() / 2, height() / 2); + const QAction* action = menu->exec(pos); + delete menu; + if (that && action) + { + if (action == up) + { + stepBy(1); + } + else if (action == down) + { + stepBy(-1); + } + } + event->accept(); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaStatusBar.cpp b/3rdparty/ElaWidgetTools/src/ElaStatusBar.cpp new file mode 100644 index 0000000..375356b --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaStatusBar.cpp @@ -0,0 +1,17 @@ +#include "ElaStatusBar.h" + +#include +#include + +#include "ElaStatusBarStyle.h" +ElaStatusBar::ElaStatusBar(QWidget* parent) + : QStatusBar(parent) +{ + setFixedHeight(28); + setContentsMargins(20, 0, 0, 0); + setStyle(new ElaStatusBarStyle(style())); +} + +ElaStatusBar::~ElaStatusBar() +{ +} diff --git a/3rdparty/ElaWidgetTools/src/ElaSuggestBox.cpp b/3rdparty/ElaWidgetTools/src/ElaSuggestBox.cpp new file mode 100644 index 0000000..4a34bff --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaSuggestBox.cpp @@ -0,0 +1,137 @@ +#include "ElaSuggestBox.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ElaBaseListView.h" +#include "ElaIcon.h" +#include "ElaLineEdit.h" +#include "ElaScrollBar.h" +#include "ElaSuggestBoxSearchViewContainer.h" +#include "ElaSuggestDelegate.h" +#include "ElaSuggestModel.h" +#include "ElaTheme.h" +#include "private/ElaSuggestBoxPrivate.h" + +Q_PROPERTY_CREATE_Q_CPP(ElaSuggestBox, int, BorderRadius) +Q_PROPERTY_CREATE_Q_CPP(ElaSuggestBox, Qt::CaseSensitivity, CaseSensitivity) +ElaSuggestBox::ElaSuggestBox(QWidget* parent) + : QWidget{parent}, d_ptr(new ElaSuggestBoxPrivate()) +{ + Q_D(ElaSuggestBox); + setFixedSize(280, 35); + d->q_ptr = this; + d->_pBorderRadius = 6; + d->_pCaseSensitivity = Qt::CaseInsensitive; + d->_searchEdit = new ElaLineEdit(this); + d->_searchEdit->setFixedHeight(35); + d->_searchEdit->setPlaceholderText("查找功能"); + d->_searchEdit->setClearButtonEnabled(true); + d->_lightSearchAction = new QAction(ElaIcon::getInstance()->getElaIcon(ElaIconType::MagnifyingGlass), "Search", this); + d->_darkSearchAction = new QAction(ElaIcon::getInstance()->getElaIcon(ElaIconType::MagnifyingGlass, QColor(0xFF, 0xFF, 0xFF)), "Search", this); + + d->_themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, d, &ElaSuggestBoxPrivate::onThemeModeChanged); + if (d->_themeMode == ElaThemeType::Light) + { + d->_searchEdit->addAction(d->_lightSearchAction, QLineEdit::TrailingPosition); + } + else + { + d->_searchEdit->addAction(d->_darkSearchAction, QLineEdit::TrailingPosition); + } + + connect(d->_lightSearchAction, &QAction::triggered, this, [=](bool checked) { + //qDebug() << "Search"; + }); + + connect(d->_darkSearchAction, &QAction::triggered, this, [=](bool checked) { + //qDebug() << "Search"; + }); + QVBoxLayout* mainLayout = new QVBoxLayout(this); + mainLayout->setSpacing(0); + mainLayout->setContentsMargins(0, 0, 0, 0); + mainLayout->addWidget(d->_searchEdit); + d->_searchViewBaseWidget = new ElaSuggestBoxSearchViewContainer(window()); + d->_shadowLayout = new QVBoxLayout(d->_searchViewBaseWidget); + d->_shadowLayout->setContentsMargins(0, 0, 0, 0); + d->_searchView = new ElaBaseListView(d->_searchViewBaseWidget); + ElaScrollBar* floatVScrollBar = new ElaScrollBar(d->_searchView->verticalScrollBar(), d->_searchView); + floatVScrollBar->setIsAnimation(true); + d->_searchViewBaseWidget->resize(292, 300); + d->_shadowLayout->addWidget(d->_searchView); + d->_searchModel = new ElaSuggestModel(this); + d->_searchDelegate = new ElaSuggestDelegate(this); + d->_searchView->setModel(d->_searchModel); + d->_searchView->setItemDelegate(d->_searchDelegate); + d->_searchViewBaseWidget->hide(); + connect(d->_searchEdit, &ElaLineEdit::textEdited, d, &ElaSuggestBoxPrivate::onSearchEditTextEdit); + connect(d->_searchEdit, &ElaLineEdit::focusIn, d, &ElaSuggestBoxPrivate::onSearchEditTextEdit); + connect(d->_searchView, &ElaBaseListView::clicked, d, &ElaSuggestBoxPrivate::onSearchViewClicked); + + // 焦点事件 + connect(d->_searchEdit, &ElaLineEdit::wmFocusOut, this, [d]() { + d->_startCloseAnimation(); + }); +} + +ElaSuggestBox::~ElaSuggestBox() +{ +} + +void ElaSuggestBox::setPlaceholderText(const QString& placeholderText) +{ + Q_D(ElaSuggestBox); + d->_searchEdit->setPlaceholderText(placeholderText); +} + +void ElaSuggestBox::addSuggestion(const QString& suggestText, const QVariantMap& suggestData) +{ + Q_D(ElaSuggestBox); + ElaSuggestion* suggest = new ElaSuggestion(this); + suggest->setSuggestText(suggestText); + suggest->setSuggestData(suggestData); + d->_suggestionVector.append(suggest); +} + +void ElaSuggestBox::addSuggestion(ElaIconType::IconName icon, const QString& suggestText, const QVariantMap& suggestData) +{ + Q_D(ElaSuggestBox); + ElaSuggestion* suggest = new ElaSuggestion(this); + suggest->setElaIcon(icon); + suggest->setSuggestText(suggestText); + suggest->setSuggestData(suggestData); + d->_suggestionVector.append(suggest); +} + +void ElaSuggestBox::removeSuggestion(const QString& suggestText) +{ + Q_D(ElaSuggestBox); + foreach (auto suggest, d->_suggestionVector) + { + if (suggest->getSuggestText() == suggestText) + { + d->_suggestionVector.removeOne(suggest); + suggest->deleteLater(); + } + } +} + +void ElaSuggestBox::removeSuggestion(int index) +{ + Q_D(ElaSuggestBox); + if (index >= d->_suggestionVector.count()) + { + return; + } + ElaSuggestion* suggest = d->_suggestionVector[index]; + d->_suggestionVector.removeOne(suggest); + suggest->deleteLater(); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaTabBar.cpp b/3rdparty/ElaWidgetTools/src/ElaTabBar.cpp new file mode 100644 index 0000000..70825fb --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaTabBar.cpp @@ -0,0 +1,84 @@ +#include "ElaTabBar.h" + +#include +#include +#include +#include + +#include "ElaTabBarPrivate.h" +#include "ElaTabBarStyle.h" +ElaTabBar::ElaTabBar(QWidget* parent) + : QTabBar(parent), d_ptr(new ElaTabBarPrivate()) +{ + Q_D(ElaTabBar); + d->q_ptr = this; + setObjectName("ElaTabBar"); + setStyleSheet("#ElaTabBar{background-color:transparent;}"); + setTabsClosable(true); + setMovable(true); + setAcceptDrops(true); + setStyle(new ElaTabBarStyle(style())); +} + +ElaTabBar::~ElaTabBar() +{ +} + +void ElaTabBar::mousePressEvent(QMouseEvent* event) +{ + Q_D(ElaTabBar); + QTabBar::mousePressEvent(event); + d->_lastDragPix = grab(tabRect(currentIndex())); + Q_EMIT tabBarPress(currentIndex()); +} + +void ElaTabBar::mouseMoveEvent(QMouseEvent* event) +{ + Q_D(ElaTabBar); + QRect moveRect = rect(); + moveRect.adjust(0, -height(), 0, height()); + QPoint currentPos = event->pos(); + if (currentPos.y() > moveRect.bottom() || currentPos.y() < moveRect.y()) + { + QDrag* drag = new QDrag(this); + drag->setPixmap(d->_lastDragPix); + qreal pixRatio = d->_lastDragPix.devicePixelRatioF(); + QPoint pixCenter = d->_lastDragPix.rect().center(); + drag->setHotSpot(pixCenter / pixRatio); + QMimeData* data = new QMimeData; + data->setProperty("DragType", "ElaTabBarDrag"); + data->setProperty("TabIndex", currentIndex()); + data->setProperty("TabIcon", tabIcon(currentIndex())); + data->setProperty("TabText", tabText(currentIndex())); + data->setProperty("ElaTabBarObject", QVariant::fromValue(this)); + data->setProperty("QDragObject", QVariant::fromValue(drag)); + drag->setMimeData(data); + Q_EMIT tabDragCreate(drag); + } + QTabBar::mouseMoveEvent(event); +} + +void ElaTabBar::dragEnterEvent(QDragEnterEvent* event) +{ + if (event->mimeData()->property("DragType").toString() == "ElaTabBarDrag") + { + event->acceptProposedAction(); + } + QTabBar::dragEnterEvent(event); +} + +void ElaTabBar::dropEvent(QDropEvent* event) +{ + if (event->mimeData()->property("ElaTabBarObject").value() != this) + { + event->accept(); + QMimeData* data = const_cast(event->mimeData()); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + data->setProperty("TabDropIndex", tabAt(event->position().toPoint())); +#else + data->setProperty("TabDropIndex", tabAt(event->pos())); +#endif + Q_EMIT tabDragDrop(data); + } + QTabBar::dropEvent(event); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaTabWidget.cpp b/3rdparty/ElaWidgetTools/src/ElaTabWidget.cpp new file mode 100644 index 0000000..59c02ea --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaTabWidget.cpp @@ -0,0 +1,66 @@ +#include "ElaTabWidget.h" + +#include +#include +#include +#include + +#include "ElaTabBar.h" +#include "ElaTabWidgetPrivate.h" +ElaTabWidget::ElaTabWidget(QWidget* parent) + : QTabWidget(parent), d_ptr(new ElaTabWidgetPrivate()) +{ + Q_D(ElaTabWidget); + d->q_ptr = this; + _pIsTabTransparent = false; + setObjectName("ElaTabWidget"); + setAcceptDrops(true); + ElaTabBar* tabBar = new ElaTabBar(this); + setTabBar(tabBar); + connect(tabBar, &ElaTabBar::tabBarPress, d, &ElaTabWidgetPrivate::onTabBarPress); + connect(tabBar, &ElaTabBar::tabDragCreate, d, &ElaTabWidgetPrivate::onTabDragCreate); + connect(tabBar, &ElaTabBar::tabDragDrop, d, &ElaTabWidgetPrivate::onTabDragDrop); + connect(tabBar, &ElaTabBar::tabCloseRequested, d, &ElaTabWidgetPrivate::onTabCloseRequested); +} + +ElaTabWidget::~ElaTabWidget() +{ +} + +void ElaTabWidget::setTabPosition(TabPosition position) +{ + if (position == QTabWidget::North || position == QTabWidget::South) + { + QTabWidget::setTabPosition(position); + } +} + +void ElaTabWidget::paintEvent(QPaintEvent* event) +{ + if (!_pIsTabTransparent) + { + QTabWidget::paintEvent(event); + } +} + +void ElaTabWidget::dragEnterEvent(QDragEnterEvent* event) +{ + if (event->mimeData()->property("DragType").toString() == "ElaTabBarDrag") + { + event->acceptProposedAction(); + } + QTabWidget::dragEnterEvent(event); +} + +void ElaTabWidget::dropEvent(QDropEvent* event) +{ + Q_D(ElaTabWidget); + if (event->mimeData()->property("ElaTabWidgetObject").value() != this) + { + QMimeData* data = const_cast(event->mimeData()); + data->setProperty("TabDropIndex", count()); + d->onTabDragDrop(event->mimeData()); + event->accept(); + } + QTabWidget::dropEvent(event); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaTableView.cpp b/3rdparty/ElaWidgetTools/src/ElaTableView.cpp new file mode 100644 index 0000000..5ef0e26 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaTableView.cpp @@ -0,0 +1,82 @@ +#include "ElaTableView.h" + +#include +#include + +#include "DeveloperComponents/ElaTableViewStyle.h" +#include "ElaScrollBar.h" +#include "ElaTableViewPrivate.h" +ElaTableView::ElaTableView(QWidget* parent) + : QTableView(parent), d_ptr(new ElaTableViewPrivate()) +{ + Q_D(ElaTableView); + d->q_ptr = this; + setMouseTracking(true); + setObjectName("ElaTableView"); + setStyleSheet( + "QTableView{background-color:transparent;}" + "QHeaderView{background-color:transparent;border:0px;}"); + setShowGrid(false); + setVerticalScrollBar(new ElaScrollBar(this)); + setHorizontalScrollBar(new ElaScrollBar(this)); + this->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + this->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); + setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + d->_tableViewStyle = new ElaTableViewStyle(style()); + setStyle(d->_tableViewStyle); +} + +ElaTableView::~ElaTableView() +{ +} + +void ElaTableView::setHeaderMargin(int headerMargin) +{ + Q_D(ElaTableView); + if (headerMargin >= 0) + { + d->_tableViewStyle->setHeaderMargin(headerMargin); + doItemsLayout(); + } +} + +int ElaTableView::getHeaderMargin() const +{ + Q_D(const ElaTableView); + return d->_tableViewStyle->getHeaderMargin(); +} + +void ElaTableView::showEvent(QShowEvent* event) +{ + Q_EMIT tableViewShow(); + QTableView::showEvent(event); +} + +void ElaTableView::hideEvent(QHideEvent* event) +{ + Q_EMIT tableViewHide(); + QTableView::hideEvent(event); +} + +void ElaTableView::mouseMoveEvent(QMouseEvent* event) +{ + Q_D(ElaTableView); + if (selectionBehavior() == QAbstractItemView::SelectRows) + { + d->_tableViewStyle->setCurrentHoverRow(indexAt(event->pos()).row()); + update(); + } + QTableView::mouseMoveEvent(event); +} + +void ElaTableView::leaveEvent(QEvent* event) +{ + Q_D(ElaTableView); + if (selectionBehavior() == QAbstractItemView::SelectRows) + { + d->_tableViewStyle->setCurrentHoverRow(-1); + update(); + } + QTableView::leaveEvent(event); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaText.cpp b/3rdparty/ElaWidgetTools/src/ElaText.cpp new file mode 100644 index 0000000..282aee0 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaText.cpp @@ -0,0 +1,158 @@ +#include "ElaText.h" + +#include +#include + +#include "ElaTheme.h" +#include "private/ElaTextPrivate.h" +ElaText::ElaText(QWidget* parent) + : QLabel(parent), d_ptr(new ElaTextPrivate()) +{ + Q_D(ElaText); + d->q_ptr = this; + setObjectName("ElaText"); + setStyleSheet("#ElaText{background-color:transparent;}"); + QFont textFont = font(); + textFont.setLetterSpacing(QFont::AbsoluteSpacing, d->_textSpacing); + textFont.setPixelSize(28); + setFont(textFont); + setWordWrap(true); + d->_themeMode = eTheme->getThemeMode(); + d->onThemeChanged(eTheme->getThemeMode()); + connect(eTheme, &ElaTheme::themeModeChanged, d, &ElaTextPrivate::onThemeChanged); +} + +ElaText::ElaText(QString text, QWidget* parent) + : ElaText(parent) +{ + setText(text); +} + +ElaText::ElaText(QString text, int pixelSize, QWidget* parent) + : ElaText(text, parent) +{ + QFont font = this->font(); + font.setPixelSize(pixelSize); + setFont(font); +} + +ElaText::~ElaText() +{ +} + +void ElaText::setIsWrapAnywhere(bool isWrapAnywhere) +{ + Q_D(ElaText); + setWordWrap(isWrapAnywhere); + d->_isWrapAnywhere = isWrapAnywhere; +} + +bool ElaText::getIsWrapAnywhere() const +{ + Q_D(const ElaText); + return d->_isWrapAnywhere; +} + +void ElaText::setTextPixelSize(int size) +{ + QFont font = this->font(); + font.setPixelSize(size); + setFont(font); +} + +int ElaText::getTextPixelSize() const +{ + return this->font().pixelSize(); +} + +void ElaText::setTextPointSize(int size) +{ + QFont font = this->font(); + font.setPointSize(size); + setFont(font); +} + +int ElaText::getTextPointSize() const +{ + return this->font().pointSize(); +} + +void ElaText::setTextStyle(ElaTextType::TextStyle textStyle) +{ + Q_D(ElaText); + QFont textFont = font(); + d->_textStyle = textStyle; + switch (textStyle) + { + case ElaTextType::NoStyle: + { + break; + } + case ElaTextType::Caption: + { + textFont.setPixelSize(12); + break; + } + case ElaTextType::Body: + { + textFont.setPixelSize(13); + break; + } + case ElaTextType::BodyStrong: + { + textFont.setPixelSize(13); + textFont.setWeight(QFont::DemiBold); + break; + } + case ElaTextType::Subtitle: + { + textFont.setPixelSize(20); + textFont.setWeight(QFont::DemiBold); + break; + } + case ElaTextType::Title: + { + textFont.setPixelSize(28); + textFont.setWeight(QFont::DemiBold); + break; + } + case ElaTextType::TitleLarge: + { + textFont.setPixelSize(40); + textFont.setWeight(QFont::DemiBold); + break; + } + case ElaTextType::Display: + { + textFont.setPixelSize(48); + textFont.setWeight(QFont::DemiBold); + break; + } + } + setFont(textFont); +} + +ElaTextType::TextStyle ElaText::getTextStyle() const +{ + Q_D(const ElaText); + return d->_textStyle; +} + +void ElaText::paintEvent(QPaintEvent* event) +{ + Q_D(ElaText); + if (wordWrap() && d->_isWrapAnywhere) + { + Q_D(ElaText); + QPainter painter(this); + painter.save(); + painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + painter.setPen(ElaThemeColor(d->_themeMode, BasicText)); + painter.drawText(rect(), Qt::AlignLeft | Qt::AlignVCenter | Qt::TextWordWrap | Qt::TextWrapAnywhere, text()); + painter.restore(); + } + else + { + QLabel::paintEvent(event); + } +} diff --git a/3rdparty/ElaWidgetTools/src/ElaTheme.cpp b/3rdparty/ElaWidgetTools/src/ElaTheme.cpp new file mode 100644 index 0000000..9bb861a --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaTheme.cpp @@ -0,0 +1,76 @@ +#include "ElaTheme.h" + +#include +#include + +#include "ElaThemePrivate.h" +Q_SINGLETON_CREATE_CPP(ElaTheme) +ElaTheme::ElaTheme(QObject* parent) + : QObject{parent}, d_ptr(new ElaThemePrivate()) +{ + Q_D(ElaTheme); + d->q_ptr = this; + d->_initThemeColor(); +} + +ElaTheme::~ElaTheme() +{ +} + +void ElaTheme::setThemeMode(ElaThemeType::ThemeMode themeMode) +{ + Q_D(ElaTheme); + d->_themeMode = themeMode; + Q_EMIT themeModeChanged(d->_themeMode); +} + +ElaThemeType::ThemeMode ElaTheme::getThemeMode() const +{ + Q_D(const ElaTheme); + return d->_themeMode; +} + +void ElaTheme::drawEffectShadow(QPainter* painter, QRect widgetRect, int shadowBorderWidth, int borderRadius) +{ + Q_D(ElaTheme); + painter->save(); + painter->setRenderHints(QPainter::Antialiasing); + QPainterPath path; + path.setFillRule(Qt::WindingFill); + QColor color = d->_themeMode == ElaThemeType::Light ? QColor(0x70, 0x70, 0x70) : QColor(0x9C, 0x9B, 0x9E); + for (int i = 0; i < shadowBorderWidth; i++) + { + path.addRoundedRect(shadowBorderWidth - i, shadowBorderWidth - i, widgetRect.width() - (shadowBorderWidth - i) * 2, widgetRect.height() - (shadowBorderWidth - i) * 2, borderRadius + i, borderRadius + i); + int alpha = 1 * (shadowBorderWidth - i + 1); + color.setAlpha(alpha > 255 ? 255 : alpha); + painter->setPen(color); + painter->drawPath(path); + } + painter->restore(); +} + +void ElaTheme::setThemeColor(ElaThemeType::ThemeMode themeMode, ElaThemeType::ThemeColor themeColor, QColor newColor) +{ + Q_D(ElaTheme); + if (themeMode == ElaThemeType::Light) + { + d->_lightThemeColorList[themeColor] = newColor; + } + else + { + d->_darkThemeColorList[themeColor] = newColor; + } +} + +const QColor& ElaTheme::getThemeColor(ElaThemeType::ThemeMode themeMode, ElaThemeType::ThemeColor themeColor) +{ + Q_D(ElaTheme); + if (themeMode == ElaThemeType::Light) + { + return d->_lightThemeColorList[themeColor]; + } + else + { + return d->_darkThemeColorList[themeColor]; + } +} diff --git a/3rdparty/ElaWidgetTools/src/ElaToggleButton.cpp b/3rdparty/ElaWidgetTools/src/ElaToggleButton.cpp new file mode 100644 index 0000000..0e52356 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaToggleButton.cpp @@ -0,0 +1,152 @@ +#include "ElaToggleButton.h" + +#include +#include +#include +#include + +#include "ElaTheme.h" +#include "private/ElaToggleButtonPrivate.h" +Q_PROPERTY_CREATE_Q_CPP(ElaToggleButton, int, BorderRadius) +Q_PROPERTY_CREATE_Q_CPP(ElaToggleButton, QString, Text) +ElaToggleButton::ElaToggleButton(QWidget* parent) + : QWidget(parent), d_ptr(new ElaToggleButtonPrivate()) +{ + Q_D(ElaToggleButton); + d->q_ptr = this; + d->_pBorderRadius = 3; + d->_themeMode = eTheme->getThemeMode(); + d->_pToggleAlpha = 0; + setMouseTracking(true); + setFixedSize(80, 32); + QFont font = this->font(); + font.setPixelSize(15); + setFont(font); + setObjectName("ElaToggleButton"); + setStyleSheet("#ElaToggleButton{background-color:transparent;}"); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { + d->_themeMode = themeMode; + }); +} + +ElaToggleButton::ElaToggleButton(QString text, QWidget* parent) + : ElaToggleButton(parent) +{ + Q_D(ElaToggleButton); + d->_pText = text; +} + +ElaToggleButton::~ElaToggleButton() +{ +} + +void ElaToggleButton::setIsToggled(bool isToggled) +{ + Q_D(ElaToggleButton); + d->_isToggled = isToggled; + d->_pToggleAlpha = isToggled ? 255 : 0; + update(); + Q_EMIT toggled(isToggled); +} + +bool ElaToggleButton::getIsToggled() const +{ + Q_D(const ElaToggleButton); + return d->_isToggled; +} + +bool ElaToggleButton::event(QEvent* event) +{ + switch (event->type()) + { + case QEvent::Enter: + case QEvent::Leave: + { + update(); + break; + } + default: + { + break; + } + } + return QWidget::event(event); +} + +void ElaToggleButton::mousePressEvent(QMouseEvent* event) +{ + Q_D(ElaToggleButton); + d->_isPressed = true; + update(); + QWidget::mouseReleaseEvent(event); +} + +void ElaToggleButton::mouseReleaseEvent(QMouseEvent* event) +{ + Q_D(ElaToggleButton); + d->_isPressed = false; + d->_isAlphaAnimationFinished = false; + d->_isToggled = !d->_isToggled; + QPropertyAnimation* alphaAnimation = new QPropertyAnimation(d, "pToggleAlpha"); + connect(alphaAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + update(); + }); + connect(alphaAnimation, &QPropertyAnimation::finished, this, [=]() { + d->_isAlphaAnimationFinished = true; + }); + alphaAnimation->setDuration(250); + alphaAnimation->setStartValue(d->_pToggleAlpha); + if (d->_isToggled) + { + alphaAnimation->setEndValue(255); + } + else + { + alphaAnimation->setEndValue(0); + } + alphaAnimation->start(QAbstractAnimation::DeleteWhenStopped); + Q_EMIT this->toggled(d->_isToggled); + QWidget::mouseReleaseEvent(event); +} + +void ElaToggleButton::paintEvent(QPaintEvent* event) +{ + Q_D(ElaToggleButton); + QPainter painter(this); + painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + + painter.save(); + QRect foregroundRect(1, 1, width() - 2, height() - 2); + if (d->_isAlphaAnimationFinished) + { + if (d->_isToggled) + { + painter.setPen(ElaThemeColor(d->_themeMode, BasicBorder)); + painter.setBrush(isEnabled() ? d->_isPressed ? ElaThemeColor(d->_themeMode, PrimaryPress) : (underMouse() ? ElaThemeColor(d->_themeMode, PrimaryHover) : ElaThemeColor(d->_themeMode, PrimaryNormal)) : ElaThemeColor(d->_themeMode, BasicDisable)); + } + else + { + painter.setPen(ElaThemeColor(d->_themeMode, BasicBorder)); + painter.setBrush(isEnabled() ? d->_isPressed ? ElaThemeColor(d->_themeMode, BasicPress) : (underMouse() ? ElaThemeColor(d->_themeMode, BasicHover) : ElaThemeColor(d->_themeMode, BasicBase)) : ElaThemeColor(d->_themeMode, BasicDisable)); + } + } + else + { + painter.setPen(Qt::NoPen); + QColor toggleColor = ElaThemeColor(d->_themeMode, PrimaryNormal); + toggleColor.setAlpha(d->_pToggleAlpha); + painter.setBrush(toggleColor); + } + painter.drawRoundedRect(foregroundRect, d->_pBorderRadius, d->_pBorderRadius); + // 底边线绘制 + if (!d->_isPressed && !d->_isToggled) + { + painter.setPen(ElaThemeColor(d->_themeMode, BasicBaseLine)); + painter.drawLine(foregroundRect.x() + d->_pBorderRadius, height() - 1, foregroundRect.x() + foregroundRect.width() - d->_pBorderRadius, height() - 1); + } + + //文字绘制 + painter.setPen(isEnabled() ? d->_isToggled ? ElaThemeColor(d->_themeMode, BasicTextInvert) : ElaThemeColor(d->_themeMode, BasicText) : ElaThemeColor(d->_themeMode, BasicTextDisable)); + painter.drawText(foregroundRect, Qt::AlignCenter, d->_pText); + painter.restore(); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaToggleSwitch.cpp b/3rdparty/ElaWidgetTools/src/ElaToggleSwitch.cpp new file mode 100644 index 0000000..7601a17 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaToggleSwitch.cpp @@ -0,0 +1,178 @@ +#include "ElaToggleSwitch.h" + +#include +#include +#include +#include + +#include "ElaTheme.h" +#include "private/ElaToggleSwitchPrivate.h" +ElaToggleSwitch::ElaToggleSwitch(QWidget* parent) + : QWidget{parent}, d_ptr(new ElaToggleSwitchPrivate()) +{ + Q_D(ElaToggleSwitch); + d->q_ptr = this; + setMouseTracking(true); + setFixedSize(44, 22); + d->_circleCenterX = -1; + d->_isToggled = false; + d->_themeMode = eTheme->getThemeMode(); + setProperty("circleCenterX", 0.01); + setProperty("circleRadius", 0.01); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { d->_themeMode = themeMode; }); +} + +ElaToggleSwitch::~ElaToggleSwitch() +{ +} + +void ElaToggleSwitch::setIsToggled(bool isToggled) +{ + Q_D(ElaToggleSwitch); + if (d->_isToggled == isToggled) + { + return; + } + if (d->_isToggled) + { + d->_startPosAnimation(width() - height() / 2 - d->_margin * 2, height() / 2, isToggled); + } + else + { + d->_startPosAnimation(height() / 2, width() - height() / 2 - d->_margin * 2, isToggled); + } +} + +bool ElaToggleSwitch::getIsToggled() const +{ + return d_ptr->_isToggled; +} + +bool ElaToggleSwitch::event(QEvent* event) +{ + Q_D(ElaToggleSwitch); + switch (event->type()) + { + case QEvent::Enter: + { + if (isEnabled()) + { + d->_startRadiusAnimation(height() * 0.3, height() * 0.35); + } + break; + } + case QEvent::Leave: + { + if (isEnabled()) + { + d->_startRadiusAnimation(height() * 0.35, height() * 0.3); + } + break; + } + case QEvent::MouseMove: + { + update(); + break; + } + default: + { + break; + } + } + return QWidget::event(event); +} + +void ElaToggleSwitch::mousePressEvent(QMouseEvent* event) +{ + Q_D(ElaToggleSwitch); + d->_adjustCircleCenterX(); + d->_isLeftButtonPress = true; + d->_lastMouseX = event->pos().x(); + d->_startRadiusAnimation(d->_circleRadius, height() * 0.25); + QWidget::mousePressEvent(event); +} + +void ElaToggleSwitch::mouseReleaseEvent(QMouseEvent* event) +{ + Q_D(ElaToggleSwitch); + d->_isLeftButtonPress = false; + QWidget::mouseReleaseEvent(event); + if (d->_isMousePressMove) + { + d->_isMousePressMove = false; + if (d->_circleCenterX > width() / 2) + { + d->_startPosAnimation(d->_circleCenterX, width() - height() / 2 - d->_margin * 2, true); + } + else + { + d->_startPosAnimation(d->_circleCenterX, height() / 2, false); + } + } + else + { + if (d->_isToggled) + { + d->_startPosAnimation(d->_circleCenterX, height() / 2, false); + } + else + { + d->_startPosAnimation(d->_circleCenterX, width() - height() / 2 - d->_margin * 2, true); + } + } + d->_startRadiusAnimation(height() * 0.25, height() * 0.35); +} + +void ElaToggleSwitch::mouseMoveEvent(QMouseEvent* event) +{ + Q_D(ElaToggleSwitch); + if (d->_isLeftButtonPress) + { + d->_isMousePressMove = true; + int moveX = event->pos().x() - d->_lastMouseX; + d->_lastMouseX = event->pos().x(); + d->_circleCenterX += moveX; + d->_adjustCircleCenterX(); + } + QWidget::mouseMoveEvent(event); +} + +void ElaToggleSwitch::paintEvent(QPaintEvent* event) +{ + Q_D(ElaToggleSwitch); + QPainter painter(this); + painter.save(); + painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + // 背景绘制 + painter.setPen(d->_isToggled ? QPen(ElaThemeColor(d->_themeMode, BasicBorder), 1.5) : QPen(ElaThemeColor(d->_themeMode, BasicBorderDeep), 1.5)); + painter.setBrush(isEnabled() ? d->_isToggled ? ElaThemeColor(d->_themeMode, PrimaryNormal) : (underMouse() ? ElaThemeColor(d->_themeMode, BasicHover) : ElaThemeColor(d->_themeMode, BasicBase)) : ElaThemeColor(d->_themeMode, BasicDisable)); + QPainterPath path; + path.moveTo(width() - height() - d->_margin, height() - d->_margin); + path.arcTo(QRectF(QPointF(width() - height() - d->_margin, d->_margin), QSize(height() - d->_margin * 2, height() - d->_margin * 2)), -90, 180); + path.lineTo(height() / 2 + d->_margin, d->_margin); + path.arcTo(QRectF(QPointF(d->_margin, d->_margin), QSize(height() - d->_margin * 2, height() - d->_margin * 2)), 90, 180); + path.lineTo(width() - height() - d->_margin, height() - d->_margin); + path.closeSubpath(); + painter.drawPath(path); + + // 圆心绘制 + painter.setPen(Qt::NoPen); + painter.setBrush(isEnabled() ? d->_isToggled ? ElaThemeColor(d->_themeMode, BasicTextInvert) : ElaThemeColor(d->_themeMode, ToggleSwitchNoToggledCenter) : ElaThemeColor(d->_themeMode, BasicTextDisable)); + if (d->_circleRadius == 0) + { + d->_circleRadius = this->isEnabled() ? (underMouse() ? height() * 0.35 : height() * 0.3) : height() * 0.3; + } + if (d->_isLeftButtonPress) + { + painter.drawEllipse(QPointF(d->_circleCenterX, height() / 2), d->_circleRadius, d->_circleRadius); + } + else + { + if (d->_circleCenterX == -1) + { + d->_circleCenterX = d->_isToggled ? width() - height() / 2 - d->_margin * 2 : height() / 2; + } + painter.drawEllipse(QPointF(d->_circleCenterX, height() / 2), d->_circleRadius, d->_circleRadius); + } + painter.restore(); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaToolBar.cpp b/3rdparty/ElaWidgetTools/src/ElaToolBar.cpp new file mode 100644 index 0000000..4a501e8 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaToolBar.cpp @@ -0,0 +1,114 @@ +#include "ElaToolBar.h" + +#include +#include +#include + +#include "ElaIcon.h" +#include "ElaTheme.h" +#include "ElaToolBarPrivate.h" +#include "ElaToolBarStyle.h" +ElaToolBar::ElaToolBar(QWidget* parent) + : QToolBar(parent), d_ptr(new ElaToolBarPrivate()) +{ + Q_D(ElaToolBar); + d->q_ptr = this; + setObjectName("ElaToolBar"); + setStyle(new ElaToolBarStyle(style())); + layout()->setSpacing(10); + layout()->setContentsMargins(3, 3, 3, 3); + + d->_themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { + d->_themeMode = themeMode; + if (this->isFloating()) + { + update(); + } + }); + setAttribute(Qt::WA_TranslucentBackground); + + connect(this, &ElaToolBar::topLevelChanged, this, [=](bool topLevel) { + if (topLevel) + { + layout()->setContentsMargins(d->_shadowBorderWidth + 3, d->_shadowBorderWidth + 3, d->_shadowBorderWidth + 3, d->_shadowBorderWidth + 3); + } + else + { + layout()->setContentsMargins(3, 3, 3, 3); + } + }); +} + +ElaToolBar::ElaToolBar(const QString& title, QWidget* parent) + : ElaToolBar(parent) +{ + setWindowTitle(title); +} + +ElaToolBar::~ElaToolBar() +{ +} + +void ElaToolBar::setToolBarSpacing(int spacing) +{ + layout()->setSpacing(spacing); +} + +int ElaToolBar::getToolBarSpacing() const +{ + return layout()->spacing(); +} + +QAction* ElaToolBar::addElaIconAction(ElaIconType::IconName icon, const QString& text) +{ + QAction* action = new QAction(text, this); + action->setProperty("ElaIconType", QChar((unsigned short)icon)); + action->setIcon(ElaIcon::getInstance()->getElaIcon(ElaIconType::Broom, 1)); + addAction(action); + return action; +} + +QAction* ElaToolBar::addElaIconAction(ElaIconType::IconName icon, const QString& text, const QKeySequence& shortcut) +{ + QAction* action = new QAction(text, this); + action->setShortcut(shortcut); + action->setProperty("ElaIconType", QChar((unsigned short)icon)); + action->setIcon(ElaIcon::getInstance()->getElaIcon(ElaIconType::Broom, 1)); + addAction(action); + return action; +} + +void ElaToolBar::paintEvent(QPaintEvent* event) +{ + Q_D(ElaToolBar); + QPainter painter(this); + painter.save(); + painter.setRenderHints(QPainter::Antialiasing); + if (isFloating()) + { + // 高性能阴影 + eTheme->drawEffectShadow(&painter, rect(), d->_shadowBorderWidth, 6); + //背景 + painter.setPen(ElaThemeColor(d->_themeMode, PopupBorder)); + painter.setBrush(ElaThemeColor(d->_themeMode, DialogBase)); + QRect foregroundRect(d->_shadowBorderWidth, d->_shadowBorderWidth, width() - 2 * d->_shadowBorderWidth, height() - 2 * d->_shadowBorderWidth); + painter.drawRoundedRect(foregroundRect, 5, 5); + QStyle* style = this->style(); + QStyleOptionToolBar opt; + initStyleOption(&opt); + opt.rect = style->subElementRect(QStyle::SE_ToolBarHandle, &opt, this); + if (opt.rect.isValid()) + { + style->drawPrimitive(QStyle::PE_IndicatorToolBarHandle, &opt, &painter, this); + } + } + else + { + painter.setPen(Qt::NoPen); + painter.setBrush(Qt::transparent); + painter.drawRoundedRect(rect(), 5, 5); + QToolBar::paintEvent(event); + } + painter.restore(); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaToolButton.cpp b/3rdparty/ElaWidgetTools/src/ElaToolButton.cpp new file mode 100644 index 0000000..9e8d06f --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaToolButton.cpp @@ -0,0 +1,128 @@ +#include "ElaToolButton.h" + +#include +#include +#include +#include +#include + +#include "DeveloperComponents/ElaToolButtonStyle.h" +#include "ElaIcon.h" +#include "ElaMenu.h" +#include "ElaToolButtonPrivate.h" +ElaToolButton::ElaToolButton(QWidget* parent) + : QToolButton(parent), d_ptr(new ElaToolButtonPrivate()) +{ + Q_D(ElaToolButton); + d->q_ptr = this; + setIconSize(QSize(22, 22)); + setPopupMode(QToolButton::InstantPopup); + d->_toolButtonStyle = new ElaToolButtonStyle(style()); + setStyle(d->_toolButtonStyle); +} + +ElaToolButton::~ElaToolButton() +{ +} + +void ElaToolButton::setBorderRadius(int borderRadius) +{ + Q_D(ElaToolButton); + d->_toolButtonStyle->setBorderRadius(borderRadius); + Q_EMIT pBorderRadiusChanged(); +} + +int ElaToolButton::getBorderRadius() const +{ + Q_D(const ElaToolButton); + return d->_toolButtonStyle->getBorderRadius(); +} + +void ElaToolButton::setIsSelected(bool isSelected) +{ + Q_D(ElaToolButton); + d->_toolButtonStyle->setIsSelected(isSelected); + Q_EMIT pIsSelectedChanged(); +} + +bool ElaToolButton::getIsSelected() const +{ + Q_D(const ElaToolButton); + return d->_toolButtonStyle->getIsSelected(); +} + +void ElaToolButton::setIsTransparent(bool isTransparent) +{ + Q_D(ElaToolButton); + d->_toolButtonStyle->setIsTransparent(isTransparent); + update(); +} + +bool ElaToolButton::getIsTransparent() const +{ + Q_D(const ElaToolButton); + return d->_toolButtonStyle->getIsTransparent(); +} + +void ElaToolButton::setMenu(ElaMenu* menu) +{ + if (!menu || menu == this->menu()) + { + return; + } + menu->setMenuItemHeight(27); + QToolButton::setMenu(menu); + menu->installEventFilter(this); +} + +void ElaToolButton::setElaIcon(ElaIconType::IconName icon) +{ + setProperty("ElaIconType", QChar((unsigned short)icon)); + setIcon(ElaIcon::getInstance()->getElaIcon(ElaIconType::Broom, 1)); +} + +bool ElaToolButton::eventFilter(QObject* watched, QEvent* event) +{ + Q_D(ElaToolButton); + if (watched == menu()) + { + switch (event->type()) + { + case QEvent::Show: + { + //指示器动画 + QPropertyAnimation* rotateAnimation = new QPropertyAnimation(d->_toolButtonStyle, "pExpandIconRotate"); + connect(rotateAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + update(); + }); + rotateAnimation->setDuration(300); + rotateAnimation->setEasingCurve(QEasingCurve::InOutSine); + rotateAnimation->setStartValue(d->_toolButtonStyle->getExpandIconRotate()); + rotateAnimation->setEndValue(-180); + rotateAnimation->start(QAbstractAnimation::DeleteWhenStopped); + break; + } + case QEvent::Hide: + { + //指示器动画 + QPropertyAnimation* rotateAnimation = new QPropertyAnimation(d->_toolButtonStyle, "pExpandIconRotate"); + connect(rotateAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + update(); + }); + rotateAnimation->setDuration(300); + rotateAnimation->setEasingCurve(QEasingCurve::InOutSine); + rotateAnimation->setStartValue(d->_toolButtonStyle->getExpandIconRotate()); + rotateAnimation->setEndValue(0); + rotateAnimation->start(QAbstractAnimation::DeleteWhenStopped); + QMouseEvent focusEvent(QEvent::MouseButtonPress, QPoint(-1, -1), QPoint(-1, -1), Qt::NoButton, Qt::NoButton, Qt::NoModifier); + QApplication::sendEvent(parentWidget(), &focusEvent); + break; + } + default: + { + break; + } + } + } + return QObject::eventFilter(watched, event); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaToolTip.cpp b/3rdparty/ElaWidgetTools/src/ElaToolTip.cpp new file mode 100644 index 0000000..61a2c9a --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaToolTip.cpp @@ -0,0 +1,100 @@ +#include "ElaToolTip.h" + +#include +#include + +#include "ElaText.h" +#include "ElaTheme.h" +#include "ElaToolTipPrivate.h" +Q_PROPERTY_CREATE_Q_CPP(ElaToolTip, int, BorderRadius) +Q_PROPERTY_CREATE_Q_CPP(ElaToolTip, int, DisplayMsec) +Q_PROPERTY_CREATE_Q_CPP(ElaToolTip, int, ShowDelayMsec) +Q_PROPERTY_CREATE_Q_CPP(ElaToolTip, int, HideDelayMsec) +ElaToolTip::ElaToolTip(QWidget* parent) + : QWidget{parent}, d_ptr(new ElaToolTipPrivate()) +{ + Q_D(ElaToolTip); + d->q_ptr = this; + d->_pBorderRadius = 5; + d->_pDisplayMsec = -1; + d->_pShowDelayMsec = 0; + d->_pHideDelayMsec = 0; + d->_pCustomWidget = nullptr; + setObjectName("ElaToolTip"); + parent->installEventFilter(d); + + setAttribute(Qt::WA_TransparentForMouseEvents); + setAttribute(Qt::WA_TranslucentBackground); + setWindowFlags(Qt::Tool | Qt::FramelessWindowHint); + + d->_toolTipText = new ElaText(this); + d->_toolTipText->setWordWrap(false); + d->_toolTipText->setTextPixelSize(17); + d->_mainLayout = new QVBoxLayout(this); + d->_mainLayout->setContentsMargins(d->_shadowBorderWidth * 2, d->_shadowBorderWidth * 2, d->_shadowBorderWidth * 2, d->_shadowBorderWidth * 2); + d->_mainLayout->addWidget(d->_toolTipText); + + d->_themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { + d->_themeMode = themeMode; + update(); + }); + hide(); +} + +ElaToolTip::~ElaToolTip() +{ +} + +void ElaToolTip::setToolTip(QString toolTip) +{ + Q_D(ElaToolTip); + d->_toolTipText->setText(toolTip); + Q_EMIT pToolTipChanged(); +} + +QString ElaToolTip::getToolTip() const +{ + Q_D(const ElaToolTip); + return d->_toolTipText->text(); +} + +void ElaToolTip::setCustomWidget(QWidget* customWidget) +{ + Q_D(ElaToolTip); + if (!customWidget || customWidget == this) + { + return; + } + if (d->_pCustomWidget) + { + d->_mainLayout->removeWidget(d->_pCustomWidget); + d->_pCustomWidget->deleteLater(); + } + d->_toolTipText->hide(); + d->_mainLayout->addWidget(customWidget); + d->_pCustomWidget = customWidget; + Q_EMIT pCustomWidgetChanged(); +} + +QWidget* ElaToolTip::getCustomWidget() const +{ + Q_D(const ElaToolTip); + return d->_pCustomWidget; +} + +void ElaToolTip::paintEvent(QPaintEvent* event) +{ + Q_D(ElaToolTip); + QPainter painter(this); + painter.save(); + painter.setRenderHint(QPainter::Antialiasing); + //阴影 + eTheme->drawEffectShadow(&painter, rect(), d->_shadowBorderWidth, d->_pBorderRadius); + QRect foregroundRect = rect(); + foregroundRect.adjust(d->_shadowBorderWidth, d->_shadowBorderWidth, -d->_shadowBorderWidth, -d->_shadowBorderWidth); + painter.setPen(ElaThemeColor(d->_themeMode, PopupBorder)); + painter.setBrush(ElaThemeColor(d->_themeMode, PopupBase)); + painter.drawRoundedRect(foregroundRect, d->_pBorderRadius, d->_pBorderRadius); + painter.restore(); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaTreeView.cpp b/3rdparty/ElaWidgetTools/src/ElaTreeView.cpp new file mode 100644 index 0000000..2b798b0 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaTreeView.cpp @@ -0,0 +1,73 @@ +#include "ElaTreeView.h" + +#include "ElaScrollBar.h" +#include "ElaTreeViewPrivate.h" +#include "ElaTreeViewStyle.h" +ElaTreeView::ElaTreeView(QWidget* parent) + : QTreeView(parent), d_ptr(new ElaTreeViewPrivate()) +{ + Q_D(ElaTreeView); + d->q_ptr = this; + setObjectName("ElaTreeView"); + setStyleSheet( + "#ElaTreeView{background-color:transparent;}" + "QHeaderView{background-color:transparent;border:0px;}"); + + setAnimated(true); + setMouseTracking(true); + + ElaScrollBar* hScrollBar = new ElaScrollBar(this); + hScrollBar->setIsAnimation(true); + connect(hScrollBar, &ElaScrollBar::rangeAnimationFinished, this, [=]() { + doItemsLayout(); + }); + setHorizontalScrollBar(hScrollBar); + setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + + ElaScrollBar* vScrollBar = new ElaScrollBar(this); + vScrollBar->setIsAnimation(true); + connect(vScrollBar, &ElaScrollBar::rangeAnimationFinished, this, [=]() { + doItemsLayout(); + }); + setVerticalScrollBar(vScrollBar); + setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + + d->_treeViewStyle = new ElaTreeViewStyle(style()); + setStyle(d->_treeViewStyle); +} + +ElaTreeView::~ElaTreeView() +{ +} + +void ElaTreeView::setItemHeight(int itemHeight) +{ + Q_D(ElaTreeView); + if (itemHeight > 0) + { + d->_treeViewStyle->setItemHeight(itemHeight); + doItemsLayout(); + } +} + +int ElaTreeView::getItemHeight() const +{ + Q_D(const ElaTreeView); + return d->_treeViewStyle->getItemHeight(); +} + +void ElaTreeView::setHeaderMargin(int headerMargin) +{ + Q_D(ElaTreeView); + if (headerMargin >= 0) + { + d->_treeViewStyle->setHeaderMargin(headerMargin); + doItemsLayout(); + } +} + +int ElaTreeView::getHeaderMargin() const +{ + Q_D(const ElaTreeView); + return d->_treeViewStyle->getHeaderMargin(); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaWidget.cpp b/3rdparty/ElaWidgetTools/src/ElaWidget.cpp new file mode 100644 index 0000000..66ecc98 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaWidget.cpp @@ -0,0 +1,143 @@ +#include "ElaWidget.h" + +#include +#include +#include +#include +#include + +#include "ElaApplication.h" +#include "ElaTheme.h" +#include "private/ElaWidgetPrivate.h" +Q_TAKEOVER_NATIVEEVENT_CPP(ElaWidget, d_func()->_appBar); +ElaWidget::ElaWidget(QWidget* parent) + : QWidget{parent}, d_ptr(new ElaWidgetPrivate()) +{ + Q_D(ElaWidget); + d->q_ptr = this; + resize(500, 500); // 默认宽高 + setWindowTitle("ElaWidget"); + setObjectName("ElaWidget"); + + // 自定义AppBar + d->_appBar = new ElaAppBar(this); + d->_appBar->setIsStayTop(true); + d->_appBar->setWindowButtonFlags(ElaAppBarType::StayTopButtonHint | ElaAppBarType::MinimizeButtonHint | ElaAppBarType::MaximizeButtonHint | ElaAppBarType::CloseButtonHint); + connect(d->_appBar, &ElaAppBar::routeBackButtonClicked, this, &ElaWidget::routeBackButtonClicked); + connect(d->_appBar, &ElaAppBar::navigationButtonClicked, this, &ElaWidget::navigationButtonClicked); + connect(d->_appBar, &ElaAppBar::themeChangeButtonClicked, this, &ElaWidget::themeChangeButtonClicked); + connect(d->_appBar, &ElaAppBar::closeButtonClicked, this, &ElaWidget::closeButtonClicked); + + // 主题 + d->_themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) { + d->_themeMode = themeMode; + update(); + }); + + d->_isEnableMica = eApp->getIsEnableMica(); + connect(eApp, &ElaApplication::pIsEnableMicaChanged, this, [=]() { + d->_isEnableMica = eApp->getIsEnableMica(); + update(); + }); + eApp->syncMica(this); +} + +ElaWidget::~ElaWidget() +{ +} + +void ElaWidget::setIsStayTop(bool isStayTop) +{ + Q_D(ElaWidget); + d->_appBar->setIsStayTop(isStayTop); +} + +bool ElaWidget::getIsStayTop() const +{ + return d_ptr->_appBar->getIsStayTop(); +} + +void ElaWidget::setIsFixedSize(bool isFixedSize) +{ + Q_D(ElaWidget); + d->_appBar->setIsFixedSize(isFixedSize); +} + +bool ElaWidget::getIsFixedSize() const +{ + return d_ptr->_appBar->getIsFixedSize(); +} + +void ElaWidget::setIsDefaultClosed(bool isDefaultClosed) +{ + Q_D(ElaWidget); + d->_appBar->setIsDefaultClosed(isDefaultClosed); + Q_EMIT pIsDefaultClosedChanged(); +} + +bool ElaWidget::getIsDefaultClosed() const +{ + Q_D(const ElaWidget); + return d->_appBar->getIsDefaultClosed(); +} + +void ElaWidget::setAppBarHeight(int appBarHeight) +{ + Q_D(ElaWidget); + d->_appBar->setAppBarHeight(appBarHeight); + Q_EMIT pAppBarHeightChanged(); +} + +int ElaWidget::getAppBarHeight() const +{ + Q_D(const ElaWidget); + return d->_appBar->getAppBarHeight(); +} + +void ElaWidget::moveToCenter() +{ + if (isMaximized() || isFullScreen()) + { + return; + } +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + auto geometry = screen()->availableGeometry(); +#else + auto geometry = qApp->screenAt(this->geometry().center())->geometry(); +#endif + setGeometry((geometry.left() + geometry.right() - width()) / 2, (geometry.top() + geometry.bottom() - height()) / 2, width(), height()); +} + +void ElaWidget::setWindowButtonFlag(ElaAppBarType::ButtonType buttonFlag, bool isEnable) +{ + Q_D(ElaWidget); + d->_appBar->setWindowButtonFlag(buttonFlag, isEnable); +} + +void ElaWidget::setWindowButtonFlags(ElaAppBarType::ButtonFlags buttonFlags) +{ + Q_D(ElaWidget); + d->_appBar->setWindowButtonFlags(buttonFlags); +} + +ElaAppBarType::ButtonFlags ElaWidget::getWindowButtonFlags() const +{ + return d_ptr->_appBar->getWindowButtonFlags(); +} + +void ElaWidget::paintEvent(QPaintEvent* event) +{ + Q_D(ElaWidget); + if (!d->_isEnableMica) + { + QPainter painter(this); + painter.save(); + painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + painter.setPen(Qt::NoPen); + painter.setBrush(ElaThemeColor(d->_themeMode, WindowBase)); + painter.drawRect(rect()); + painter.restore(); + } + QWidget::paintEvent(event); +} diff --git a/3rdparty/ElaWidgetTools/src/ElaWidgetToolsConfig.cmake.in b/3rdparty/ElaWidgetTools/src/ElaWidgetToolsConfig.cmake.in new file mode 100644 index 0000000..c958816 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaWidgetToolsConfig.cmake.in @@ -0,0 +1,7 @@ +@PACKAGE_INIT@ + +set( @PROJECT_NAME@_LIBRARIES ElaWidgetTools) +set( @PROJECT_NAME@_INCLUDE_DIRS @PACKAGE_INCLUDE_DIRS@) +set( @PROJECT_NAME@_LIBRARY_DIRS @PACKAGE_LIB_DIR@) + +check_required_components(${PROJECT_NAME}) diff --git a/3rdparty/ElaWidgetTools/src/ElaWindow.cpp b/3rdparty/ElaWidgetTools/src/ElaWindow.cpp new file mode 100644 index 0000000..e36d36a --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/ElaWindow.cpp @@ -0,0 +1,395 @@ +#include "ElaWindow.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ElaApplication.h" +#include "ElaCentralStackedWidget.h" +#include "ElaEventBus.h" +#include "ElaMenu.h" +#include "ElaNavigationBar.h" +#include "ElaNavigationRouter.h" +#include "ElaTheme.h" +#include "ElaWindowStyle.h" +#include "private/ElaAppBarPrivate.h" +#include "private/ElaNavigationBarPrivate.h" +#include "private/ElaWindowPrivate.h" +Q_PROPERTY_CREATE_Q_CPP(ElaWindow, int, ThemeChangeTime) +Q_PROPERTY_CREATE_Q_CPP(ElaWindow, ElaNavigationType::NavigationDisplayMode, NavigationBarDisplayMode) +Q_TAKEOVER_NATIVEEVENT_CPP(ElaWindow, d_func()->_appBar); +ElaWindow::ElaWindow(QWidget* parent) + : QMainWindow{parent}, d_ptr(new ElaWindowPrivate()) +{ + Q_D(ElaWindow); + d->q_ptr = this; + + setProperty("ElaBaseClassName", "ElaWindow"); + resize(1020, 680); // 默认宽高 + + d->_pThemeChangeTime = 700; + d->_pNavigationBarDisplayMode = ElaNavigationType::NavigationDisplayMode::Auto; + connect(this, &ElaWindow::pNavigationBarDisplayModeChanged, d, &ElaWindowPrivate::onDisplayModeChanged); + + // 自定义AppBar + d->_appBar = new ElaAppBar(this); + connect(d->_appBar, &ElaAppBar::routeBackButtonClicked, this, []() { + ElaNavigationRouter::getInstance()->navigationRouteBack(); + }); + connect(d->_appBar, &ElaAppBar::closeButtonClicked, this, &ElaWindow::closeButtonClicked); + // 导航栏 + d->_navigationBar = new ElaNavigationBar(this); + // 返回按钮状态变更 + connect(ElaNavigationRouter::getInstance(), &ElaNavigationRouter::navigationRouterStateChanged, this, [d](bool isEnable) { + d->_appBar->setRouteBackButtonEnable(isEnable); + }); + + // 转发用户卡片点击信号 + connect(d->_navigationBar, &ElaNavigationBar::userInfoCardClicked, this, &ElaWindow::userInfoCardClicked); + // 转发点击信号 + connect(d->_navigationBar, &ElaNavigationBar::navigationNodeClicked, this, &ElaWindow::navigationNodeClicked); + //跳转处理 + connect(d->_navigationBar, &ElaNavigationBar::navigationNodeClicked, d, &ElaWindowPrivate::onNavigationNodeClicked); + //新增窗口 + connect(d->_navigationBar, &ElaNavigationBar::navigationNodeAdded, d, &ElaWindowPrivate::onNavigationNodeAdded); + + // 中心堆栈窗口 + d->_centerStackedWidget = new ElaCentralStackedWidget(this); + d->_centerStackedWidget->setContentsMargins(0, 0, 0, 0); + QWidget* centralWidget = new QWidget(this); + d->_centerLayout = new QHBoxLayout(centralWidget); + d->_centerLayout->setSpacing(0); + d->_centerLayout->addWidget(d->_navigationBar); + d->_centerLayout->addWidget(d->_centerStackedWidget); + d->_centerLayout->setContentsMargins(d->_contentsMargins, 0, 0, 0); + + // 事件总线 + d->_focusEvent = new ElaEvent("WMWindowClicked", "onWMWindowClickedEvent", d); + d->_focusEvent->registerAndInit(); + + // 展开导航栏 + connect(d->_appBar, &ElaAppBar::navigationButtonClicked, d, &ElaWindowPrivate::onNavigationButtonClicked); + + // 主题变更动画 + d->_themeMode = eTheme->getThemeMode(); + connect(eTheme, &ElaTheme::themeModeChanged, d, &ElaWindowPrivate::onThemeModeChanged); + connect(d->_appBar, &ElaAppBar::themeChangeButtonClicked, d, &ElaWindowPrivate::onThemeReadyChange); + d->_isInitFinished = true; + setCentralWidget(centralWidget); + centralWidget->installEventFilter(this); + + setObjectName("ElaWindow"); + setStyleSheet("#ElaWindow{background-color:transparent;}"); + setStyle(new ElaWindowStyle(style())); + + //延时渲染 + QTimer::singleShot(1, this, [=] { + QPalette palette = this->palette(); + palette.setBrush(QPalette::Window, ElaThemeColor(d->_themeMode, WindowBase)); + this->setPalette(palette); + }); + eApp->syncMica(this); + connect(eApp, &ElaApplication::pIsEnableMicaChanged, this, [=]() { + d->onThemeModeChanged(d->_themeMode); + }); +} + +ElaWindow::~ElaWindow() +{ +} + +void ElaWindow::setIsStayTop(bool isStayTop) +{ + Q_D(ElaWindow); + d->_appBar->setIsStayTop(isStayTop); + Q_EMIT pIsStayTopChanged(); +} + +bool ElaWindow::getIsStayTop() const +{ + return d_ptr->_appBar->getIsStayTop(); +} + +void ElaWindow::setIsFixedSize(bool isFixedSize) +{ + Q_D(ElaWindow); + d->_appBar->setIsFixedSize(isFixedSize); + Q_EMIT pIsFixedSizeChanged(); +} + +bool ElaWindow::getIsFixedSize() const +{ + return d_ptr->_appBar->getIsFixedSize(); +} + +void ElaWindow::setIsDefaultClosed(bool isDefaultClosed) +{ + Q_D(ElaWindow); + d->_appBar->setIsDefaultClosed(isDefaultClosed); + Q_EMIT pIsDefaultClosedChanged(); +} + +bool ElaWindow::getIsDefaultClosed() const +{ + return d_ptr->_appBar->getIsDefaultClosed(); +} + +void ElaWindow::setAppBarHeight(int appBarHeight) +{ + Q_D(ElaWindow); + d->_appBar->setAppBarHeight(appBarHeight); + Q_EMIT pAppBarHeightChanged(); +} + +int ElaWindow::getAppBarHeight() const +{ + Q_D(const ElaWindow); + return d->_appBar->getAppBarHeight(); +} + +QWidget* ElaWindow::getCustomWidget() const +{ + Q_D(const ElaWindow); + return d->_appBar->getCustomWidget(); +} + +void ElaWindow::setCustomWidgetMaximumWidth(int width) +{ + Q_D(ElaWindow); + d->_appBar->setCustomWidgetMaximumWidth(width); + Q_EMIT pCustomWidgetMaximumWidthChanged(); +} + +int ElaWindow::getCustomWidgetMaximumWidth() const +{ + Q_D(const ElaWindow); + return d->_appBar->getCustomWidgetMaximumWidth(); +} + +void ElaWindow::setIsCentralStackedWidgetTransparent(bool isTransparent) +{ + Q_D(ElaWindow); + d->_centerStackedWidget->setIsTransparent(isTransparent); +} + +bool ElaWindow::getIsCentralStackedWidgetTransparent() const +{ + Q_D(const ElaWindow); + return d->_centerStackedWidget->getIsTransparent(); +} + +void ElaWindow::moveToCenter() +{ + if (isMaximized() || isFullScreen()) + { + return; + } +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + auto geometry = screen()->availableGeometry(); +#else + auto geometry = qApp->screenAt(this->geometry().center())->geometry(); +#endif + setGeometry((geometry.left() + geometry.right() - width()) / 2, (geometry.top() + geometry.bottom() - height()) / 2, width(), height()); +} + +void ElaWindow::setCustomWidget(ElaAppBarType::CustomArea customArea, QWidget* widget) +{ + Q_D(ElaWindow); + d->_appBar->setCustomWidget(customArea, widget); + Q_EMIT customWidgetChanged(); +} + +void ElaWindow::setIsNavigationBarEnable(bool isVisible) +{ + Q_D(ElaWindow); + d->_isNavigationEnable = isVisible; + d->_navigationBar->setVisible(isVisible); + d->_centerLayout->setContentsMargins(isVisible ? d->_contentsMargins : 0, 0, 0, 0); + d->_centerStackedWidget->setIsHasRadius(isVisible); +} + +bool ElaWindow::getIsNavigationBarEnable() const +{ + return d_ptr->_isNavigationEnable; +} + +void ElaWindow::setUserInfoCardVisible(bool isVisible) +{ + Q_D(ElaWindow); + d->_navigationBar->setUserInfoCardVisible(isVisible); +} + +void ElaWindow::setUserInfoCardPixmap(QPixmap pix) +{ + Q_D(ElaWindow); + d->_navigationBar->setUserInfoCardPixmap(pix); +} + +void ElaWindow::setUserInfoCardTitle(QString title) +{ + Q_D(ElaWindow); + d->_navigationBar->setUserInfoCardTitle(title); +} + +void ElaWindow::setUserInfoCardSubTitle(QString subTitle) +{ + Q_D(ElaWindow); + d->_navigationBar->setUserInfoCardSubTitle(subTitle); +} + +ElaNavigationType::NodeOperateReturnType ElaWindow::addExpanderNode(QString expanderTitle, QString& expanderKey, ElaIconType::IconName awesome) const +{ + Q_D(const ElaWindow); + return d->_navigationBar->addExpanderNode(expanderTitle, expanderKey, awesome); +} + +ElaNavigationType::NodeOperateReturnType ElaWindow::addExpanderNode(QString expanderTitle, QString& expanderKey, QString targetExpanderKey, ElaIconType::IconName awesome) const +{ + Q_D(const ElaWindow); + return d->_navigationBar->addExpanderNode(expanderTitle, expanderKey, targetExpanderKey, awesome); +} + +ElaNavigationType::NodeOperateReturnType ElaWindow::addPageNode(QString pageTitle, QWidget* page, ElaIconType::IconName awesome) const +{ + Q_D(const ElaWindow); + return d->_navigationBar->addPageNode(pageTitle, page, awesome); +} + +ElaNavigationType::NodeOperateReturnType ElaWindow::addPageNode(QString pageTitle, QWidget* page, QString targetExpanderKey, ElaIconType::IconName awesome) const +{ + Q_D(const ElaWindow); + return d->_navigationBar->addPageNode(pageTitle, page, targetExpanderKey, awesome); +} + +ElaNavigationType::NodeOperateReturnType ElaWindow::addPageNode(QString pageTitle, QWidget* page, int keyPoints, ElaIconType::IconName awesome) const +{ + Q_D(const ElaWindow); + return d->_navigationBar->addPageNode(pageTitle, page, keyPoints, awesome); +} + +ElaNavigationType::NodeOperateReturnType ElaWindow::addPageNode(QString pageTitle, QWidget* page, QString targetExpanderKey, int keyPoints, ElaIconType::IconName awesome) const +{ + Q_D(const ElaWindow); + return d->_navigationBar->addPageNode(pageTitle, page, targetExpanderKey, keyPoints, awesome); +} + +ElaNavigationType::NodeOperateReturnType ElaWindow::addFooterNode(QString footerTitle, QString& footerKey, int keyPoints, ElaIconType::IconName awesome) const +{ + Q_D(const ElaWindow); + return d->_navigationBar->addFooterNode(footerTitle, nullptr, footerKey, keyPoints, awesome); +} + +ElaNavigationType::NodeOperateReturnType ElaWindow::addFooterNode(QString footerTitle, QWidget* page, QString& footerKey, int keyPoints, ElaIconType::IconName awesome) const +{ + Q_D(const ElaWindow); + return d->_navigationBar->addFooterNode(footerTitle, page, footerKey, keyPoints, awesome); +} + +void ElaWindow::setNodeKeyPoints(QString nodeKey, int keyPoints) +{ + Q_D(ElaWindow); + d->_navigationBar->setNodeKeyPoints(nodeKey, keyPoints); +} + +int ElaWindow::getNodeKeyPoints(QString nodeKey) const +{ + Q_D(const ElaWindow); + return d->_navigationBar->getNodeKeyPoints(nodeKey); +} + +void ElaWindow::navigation(QString pageKey) +{ + Q_D(ElaWindow); + d->_navigationBar->navigation(pageKey); +} + +void ElaWindow::setWindowButtonFlag(ElaAppBarType::ButtonType buttonFlag, bool isEnable) +{ + Q_D(ElaWindow); + d->_appBar->setWindowButtonFlag(buttonFlag, isEnable); +} + +void ElaWindow::setWindowButtonFlags(ElaAppBarType::ButtonFlags buttonFlags) +{ + Q_D(ElaWindow); + d->_appBar->setWindowButtonFlags(buttonFlags); +} + +ElaAppBarType::ButtonFlags ElaWindow::getWindowButtonFlags() const +{ + return d_ptr->_appBar->getWindowButtonFlags(); +} + +void ElaWindow::closeWindow() +{ + Q_D(ElaWindow); + d->_isWindowClosing = true; + d->_appBar->closeWindow(); +} + +bool ElaWindow::eventFilter(QObject* watched, QEvent* event) +{ + Q_D(ElaWindow); + switch (event->type()) + { + case QEvent::Resize: + { + d->_doNavigationDisplayModeChange(); + break; + } + default: + { + break; + } + } + return QMainWindow::eventFilter(watched, event); +} + +QMenu* ElaWindow::createPopupMenu() +{ + ElaMenu* menu = nullptr; + QList dockwidgets = findChildren(); + if (dockwidgets.size()) + { + menu = new ElaMenu(this); + for (int i = 0; i < dockwidgets.size(); ++i) + { + QDockWidget* dockWidget = dockwidgets.at(i); + if (dockWidget->parentWidget() == this) + { + menu->addAction(dockwidgets.at(i)->toggleViewAction()); + } + } + menu->addSeparator(); + } + + QList toolbars = findChildren(); + if (toolbars.size()) + { + if (!menu) + { + menu = new ElaMenu(this); + } + for (int i = 0; i < toolbars.size(); ++i) + { + QToolBar* toolBar = toolbars.at(i); + if (toolBar->parentWidget() == this) + { + menu->addAction(toolbars.at(i)->toggleViewAction()); + } + } + } + if (menu) + { + menu->setMenuItemHeight(28); + } + return menu; +} diff --git a/3rdparty/ElaWidgetTools/src/include/Def.h b/3rdparty/ElaWidgetTools/src/include/Def.h new file mode 100644 index 0000000..3fe7d38 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/Def.h @@ -0,0 +1,3519 @@ +#ifndef DEF_H +#define DEF_H +#include + +#include "stdafx.h" + +//枚举类导出 兼容QT5低版本 +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) +#define Q_BEGIN_ENUM_CREATE(CLASS) \ + namespace CLASS \ + { \ + Q_NAMESPACE_EXPORT(ELA_EXPORT) + +#define Q_END_ENUM_CREATE(CLASS) } + +#define Q_ENUM_CREATE(CLASS) Q_ENUM_NS(CLASS) +#else +#define Q_BEGIN_ENUM_CREATE(CLASS) \ + class ELA_EXPORT CLASS : public QObject \ + { \ + Q_OBJECT \ + public: + +#define Q_END_ENUM_CREATE(CLASS) \ +private: \ + Q_DISABLE_COPY(CLASS) \ + } \ + ; + +#define Q_ENUM_CREATE(CLASS) Q_ENUM(CLASS) +#endif + +Q_BEGIN_ENUM_CREATE(ElaThemeType) +enum ThemeMode +{ + Light = 0x0000, + Dark = 0x0001, +}; +Q_ENUM_CREATE(ThemeMode) + +enum ThemeColor +{ + ScrollBarHandle, + ToggleSwitchNoToggledCenter, + WindowBase, + WindowCentralStackBase, + PrimaryNormal, + PrimaryHover, + PrimaryPress, + PopupBorder, + PopupBorderHover, + PopupBase, + PopupHover, + DialogBase, + DialogLayoutArea, + BasicText, + BasicTextInvert, + BasicDetailsText, + BasicTextNoFocus, + BasicTextDisable, + BasicTextPress, + BasicBorder, + BasicBorderDeep, + BasicBorderHover, + BasicBase, + BasicBaseDeep, + BasicDisable, + BasicHover, + BasicPress, + BasicSelectedHover, + BasicBaseLine, + BasicHemline, + BasicIndicator, + BasicChute, + BasicAlternating, + BasicBaseAlpha, + BasicBaseDeepAlpha, + BasicHoverAlpha, + BasicPressAlpha, + BasicSelectedAlpha, + BasicSelectedHoverAlpha, + StatusDanger, +}; +Q_ENUM_CREATE(ThemeColor) +Q_END_ENUM_CREATE(ElaThemeType) + +Q_BEGIN_ENUM_CREATE(ElaAppBarType) +enum ButtonType +{ + RouteBackButtonHint = 0x0001, + NavigationButtonHint = 0x0002, + StayTopButtonHint = 0x0004, + ThemeChangeButtonHint = 0x0008, + MinimizeButtonHint = 0x00010, + MaximizeButtonHint = 0x0020, + CloseButtonHint = 0x0040, +}; +Q_ENUM_CREATE(ButtonType) +Q_DECLARE_FLAGS(ButtonFlags, ButtonType) + +enum CustomArea +{ + LeftArea = 0x0001, + MiddleArea = 0x0002, + RightArea = 0x0003, +}; +Q_ENUM_CREATE(CustomArea) + +enum WMMouseActionType +{ + WMLBUTTONDOWN = 0x0001, + WMLBUTTONUP = 0x0002, + WMLBUTTONDBLCLK = 0x0004, + WMNCLBUTTONDOWN = 0x0008, +}; +Q_ENUM_CREATE(WMMouseActionType) +Q_DECLARE_FLAGS(WMMouseActionTypes, WMMouseActionType) +Q_END_ENUM_CREATE(ElaAppBarType) +Q_DECLARE_OPERATORS_FOR_FLAGS(ElaAppBarType::ButtonFlags) +Q_DECLARE_OPERATORS_FOR_FLAGS(ElaAppBarType::WMMouseActionTypes) + +Q_BEGIN_ENUM_CREATE(ElaTextType) +enum TextStyle +{ + NoStyle = 0x0000, + Caption = 0x0001, + Body = 0x0002, + BodyStrong = 0x0003, + Subtitle = 0x0004, + Title = 0x0005, + TitleLarge = 0x0006, + Display = 0x0007, +}; +Q_ENUM_CREATE(TextStyle) +Q_END_ENUM_CREATE(ElaTextType) + +Q_BEGIN_ENUM_CREATE(ElaNavigationType) +enum NodeOperateReturnType +{ + Success = 0x0000, + TargetNodeInvalid = 0x0001, + TargetNodeTypeError = 0x0002, + TargetNodeDepthLimit = 0x0003, + PageInvalid = 0x0004, + FooterUpperLimit = 0x0005, +}; +Q_ENUM_CREATE(NodeOperateReturnType) + +enum NavigationDisplayMode +{ + Auto = 0x0000, + Minimal = 0x0001, + Compact = 0x0002, + Maximal = 0x0003, +}; +Q_ENUM_CREATE(NavigationDisplayMode) + +enum NavigationNodeType +{ + PageNode = 0x0000, + FooterNode = 0x0001, +}; +Q_ENUM_CREATE(NavigationNodeType) +Q_END_ENUM_CREATE(ElaNavigationType) + +Q_BEGIN_ENUM_CREATE(ElaNavigationRouterType) +enum NavigationRouteType +{ + Success = 0x0000, + ObjectInvalid = 0x0001, + FunctionNameInvalid = 0x0002, +}; +Q_ENUM_CREATE(NavigationRouteType) +Q_END_ENUM_CREATE(ElaNavigationRouterType) + +Q_BEGIN_ENUM_CREATE(ElaEventBusType) +enum EventBusReturnType +{ + Success = 0x0000, + EventInvalid = 0x0001, + EventNameInvalid = 0x0002, + +}; +Q_ENUM_CREATE(EventBusReturnType) +Q_END_ENUM_CREATE(ElaEventBusType) + +Q_BEGIN_ENUM_CREATE(ElaCardPixType) +enum PixMode +{ + Default = 0x0000, + RoundedRect = 0x0001, + Ellipse = 0x0002, +}; +Q_ENUM_CREATE(PixMode) +Q_END_ENUM_CREATE(ElaCardPixType) + +Q_BEGIN_ENUM_CREATE(ElaGraphicsSceneType) +enum SceneMode +{ + Default = 0x0000, + DragMove = 0x0001, + MultiSelect = 0x0002, + ItemLink = 0x0003, +}; +Q_ENUM_CREATE(SceneMode) +Q_END_ENUM_CREATE(ElaGraphicsSceneType) + +Q_BEGIN_ENUM_CREATE(ElaMessageBarType) +enum PositionPolicy +{ + Top = 0x0000, + Left = 0x0001, + Bottom = 0x0002, + Right = 0x0003, + TopRight = 0x0004, + TopLeft = 0x0005, + BottomRight = 0x0006, + BottomLeft = 0x0007, +}; +Q_ENUM_CREATE(PositionPolicy) + +enum MessageMode +{ + Success = 0x0000, + Warning = 0x0001, + Information = 0x0002, + Error = 0x0003, +}; +Q_ENUM_CREATE(MessageMode) +Q_END_ENUM_CREATE(ElaMessageBarType) + +Q_BEGIN_ENUM_CREATE(ElaIconType) +enum IconName +{ + None = 0x0, + Broom = 0xe800, + Number00 = 0xe801, + Numbe0 = 0xe802, + Numbe1 = 0xe803, + Numbe2 = 0xe804, + Numbe3 = 0xe805, + Numbe4 = 0xe806, + Numbe5 = 0xe807, + Numbe6 = 0xe809, + Numbe7 = 0xe808, + Numbe8 = 0xe80b, + Numbe9 = 0xe80a, + Degrees360 = 0xe80c, + A = 0xe80d, + Abacus = 0xe80e, + AccentGrave = 0xe80f, + Acorn = 0xe810, + AddressBook = 0xe811, + AddressCard = 0xe812, + AirConditioner = 0xe813, + Airplay = 0xe814, + AlarmClock = 0xe815, + AlarmExclamation = 0xe816, + AlarmPlus = 0xe817, + Album = 0xe818, + AlbumCirclePlus = 0xe819, + AlbumCircleUser = 0xe81a, + AlbumCollection = 0xe81b, + AlbumCollectionCirclePlus = 0xe81c, + AlarmSnooze = 0xe81d, + AlbumCollectionCircleUser = 0xe81e, + Alicorn = 0xe81f, + Alien8bit = 0xe820, + AlignCenter = 0xe821, + AlignLeft = 0xe822, + AlignRight = 0xe823, + Ampersand = 0xe824, + Alt = 0xe825, + AnchorCircleExclamation = 0xe826, + AlignSlash = 0xe827, + Apartment = 0xe83b, + Ankh = 0xe83a, + AngleUp = 0xe839, + AnglesUpDown = 0xe838, + AnglesRight = 0xe837, + AnglesUp = 0xe836, + AnglesDown = 0xe835, + AnglesLeft = 0xe834, + AngleRight = 0xe833, + AngleDown = 0xe832, + AnchorCircleCheck = 0xe831, + Alien = 0xe830, + Angle90 = 0xe82f, + Angel = 0xe82e, + Angle = 0xe82c, + AnchorCircleXmark = 0xe82d, + AnchorLock = 0xe82b, + Anchor = 0xe82a, + AlignJustify = 0xe829, + AmpGuitar = 0xe828, + Aperture = 0xe83c, + Apostrophe = 0xe83d, + AppleCore = 0xe83e, + AngleLeft = 0xe83f, + AppleWhole = 0xe840, + Archway = 0xe841, + ArrowDown = 0xe842, + ArrowDown19 = 0xe843, + ArrowDownUpAcrossLine = 0xe857, + ArrowDown91 = 0xe844, + ArrowDownArrowUp = 0xe845, + ArrowDownAZ = 0xe846, + ArrowDownBigSmall = 0xe847, + ArrowDownFromArc = 0xe848, + ArrowDownFromDottedLine = 0xe849, + ArrowDownFromLine = 0xe84a, + ArrowDownLeft = 0xe84b, + ArrowDownLeftAndArrowUpRightToCenter = 0xe84c, + ArrowDownLong = 0xe84d, + ArrowDownRight = 0xe84e, + ArrowDownShortWide = 0xe84f, + ArrowDownUpLock = 0xe858, + ArrowDownWideShort = 0xe859, + ArrowDownZA = 0xe85a, + ArrowLeft = 0xe85b, + ArrowLeftFromArc = 0xe85c, + ArrowLeftFromLine = 0xe85d, + ArrowLeftLong = 0xe85e, + ArrowLeftLongToLine = 0xe85f, + ArrowLeftToArc = 0xe860, + ArrowPointer = 0xe861, + ArrowDownToSquare = 0xe862, + ArrowLeftToLine = 0xe863, + ArrowRightToArc = 0xe86c, + ArrowRightToBracket = 0xe86d, + ArrowRightToCity = 0xe86e, + ArrowRightToLine = 0xe86f, + ArrowRotateLeft = 0xe870, + ArrowRotateRight = 0xe871, + ArrowsCross = 0xe872, + ArrowsDownToLine = 0xe873, + ArrowsDownToPeople = 0xe874, + ArrowsFromDottedLine = 0xe875, + ArrowsFromLine = 0xe876, + ArrowsLeftRight = 0xe877, + ArrowsSpin = 0xe880, + ArrowsSplitUpAndLeft = 0xe881, + ArrowsToCircle = 0xe882, + ArrowsToDot = 0xe883, + ArrowsToDottedLine = 0xe884, + ArrowsToEye = 0xe885, + ArrowsToLine = 0xe886, + ArrowsTurnRight = 0xe887, + ArrowsTurnToDots = 0xe888, + ArrowsUpDown = 0xe889, + ArrowsUpDownLeftRight = 0xe88a, + ArrowsUpToLine = 0xe88b, + ArrowTurnRight = 0xe894, + ArrowTurnUp = 0xe895, + ArrowUp = 0xe896, + ArrowUp19 = 0xe897, + ArrowUp91 = 0xe898, + ArrowUpArrowDown = 0xe899, + ArrowUpAZ = 0xe89a, + ArrowUpBigSmall = 0xe89b, + ArrowUpFromArc = 0xe89c, + ArrowUpFromBracket = 0xe89d, + ArrowUpFromDottedLine = 0xe89e, + ArrowUpFromGroundWater = 0xe89f, + ArrowUpRightDots = 0xe8a8, + ArrowUpRightFromSquare = 0xe8a9, + ArrowUpShortWide = 0xe8aa, + ArrowUpSmallBig = 0xe8ab, + ArrowUpSquareTriangle = 0xe8ac, + ArrowUpToArc = 0xe8ad, + ArrowUpToDottedLine = 0xe8ae, + ArrowUpToLine = 0xe8af, + ArrowUpTriangleSquare = 0xe8b0, + ArrowUpWideShort = 0xe8b1, + ArrowUpZA = 0xe8b2, + Asterisk = 0xe8b3, + AwardSimple = 0xe8bc, + Axe = 0xe8bd, + AxeBattle = 0xe8be, + B = 0xe8bf, + Baby = 0xe8c0, + BabyCarriage = 0xe8c1, + Backpack = 0xe8c2, + Backward = 0xe8c3, + BackwardFast = 0xe8c4, + BackwardStep = 0xe8c5, + Bacon = 0xe8c6, + Bacteria = 0xe8c7, + Bagel = 0xe8d0, + BagSeedling = 0xe8d1, + BagShopping = 0xe8d2, + BagShoppingMinus = 0xe8d3, + BagShoppingPlus = 0xe8d4, + BagsShopping = 0xe8d5, + Baguette = 0xe8d6, + Bahai = 0xe8d7, + BahtSign = 0xe8d8, + Balloon = 0xe8d9, + Balloons = 0xe8da, + Ballot = 0xe8db, + ArrowDownSquareTriangle = 0xe850, + ArrowDownSmallBig = 0xe851, + ArrowDownToArc = 0xe852, + ArrowDownToBracket = 0xe853, + ArrowDownToDottedLine = 0xe854, + ArrowDownToLine = 0xe855, + ArrowDownTriangleSquare = 0xe856, + ArrowProgress = 0xe864, + ArrowRight = 0xe865, + ArrowRightArrowLeft = 0xe866, + ArrowRightFromArc = 0xe867, + ArrowRightFromBracket = 0xe868, + ArrowRightFromLine = 0xe869, + ArrowRightLong = 0xe86a, + ArrowRightLongToLine = 0xe86b, + ArrowsLeftRightToLine = 0xe878, + ArrowsMaximize = 0xe879, + ArrowsMinimize = 0xe87a, + ArrowsRepeat = 0xe87b, + ArrowsRepeat1 = 0xe87c, + ArrowsRetweet = 0xe87d, + ArrowsRotate = 0xe87e, + ArrowsRotateReverse = 0xe87f, + ArrowTrendDown = 0xe88c, + ArrowTrendUp = 0xe88d, + ArrowTurnDown = 0xe88e, + ArrowTurnDownLeft = 0xe88f, + ArrowTurnDownRight = 0xe890, + ArrowTurnLeft = 0xe891, + ArrowTurnLeftDown = 0xe892, + ArrowTurnLeftUp = 0xe893, + ArrowUpFromLine = 0xe8a0, + ArrowUpFromSquare = 0xe8a1, + ArrowUpFromWaterPump = 0xe8a2, + ArrowUpLeft = 0xe8a3, + ArrowUpLeftFromCircle = 0xe8a4, + ArrowUpLong = 0xe8a5, + ArrowUpRight = 0xe8a6, + ArrowUpRightAndArrowDownLeftFromCenter = 0xe8a7, + At = 0xe8b4, + Atom = 0xe8b5, + AtomSimple = 0xe8b6, + AudioDescription = 0xe8b7, + AudioDescriptionSlash = 0xe8b8, + AustralSign = 0xe8b9, + Avocado = 0xe8ba, + Award = 0xe8bb, + Badge = 0xe8c8, + Bacterium = 0xe8c9, + BadgeCheck = 0xe8ca, + BadgeDollar = 0xe8cb, + BadgePercent = 0xe8cc, + BadgerHoney = 0xe8cd, + BadgeSheriff = 0xe8ce, + Badminton = 0xe8cf, + BallotCheck = 0xe8dc, + BallPile = 0xe8dd, + Ban = 0xe8de, + Banana = 0xe8df, + BanBug = 0xe8e0, + Bandage = 0xe8e1, + BangladeshiTakaSign = 0xe8e2, + Banjo = 0xe8e3, + BanParking = 0xe8e4, + BanSmoking = 0xe8e5, + Barcode = 0xe8e6, + BarcodeRead = 0xe8e7, + BarcodeScan = 0xe8e8, + Bars = 0xe8e9, + BarsFilter = 0xe8ea, + BarsProgress = 0xe8eb, + BarsSort = 0xe8ec, + BarsStaggered = 0xe8ed, + Baseball = 0xe8ee, + BaseballBatBall = 0xe8ef, + Basketball = 0xe8f0, + BasketballHoop = 0xe8f1, + BasketShopping = 0xe8f2, + BasketShoppingMinus = 0xe8f3, + BasketShoppingPlus = 0xe8f4, + BasketShoppingSimple = 0xe8f5, + Bat = 0xe8f6, + Bath = 0xe8f7, + BatteryBolt = 0xe8f8, + BatteryEmpty = 0xe8f9, + BatteryExclamation = 0xe8fa, + BatteryFull = 0xe8fb, + BatteryHalf = 0xe8fc, + BatteryLow = 0xe8fd, + BatteryQuarter = 0xe8fe, + BatterySlash = 0xe8ff, + BatteryThreeQuarters = 0xe900, + Bed = 0xe901, + BedBunk = 0xe902, + BedEmpty = 0xe903, + BedFront = 0xe904, + BedPulse = 0xe905, + Bee = 0xe906, + BeerMug = 0xe907, + BeerMugEmpty = 0xe908, + Bell = 0xe909, + BellConcierge = 0xe90a, + BellExclamation = 0xe90b, + BellOn = 0xe90c, + BellPlus = 0xe90d, + BellRing = 0xe90e, + Bells = 0xe90f, + BellSchool = 0xe910, + BellSchoolSlash = 0xe911, + BellSlash = 0xe912, + BenchTree = 0xe913, + BezierCurve = 0xe914, + Bicycle = 0xe915, + Billboard = 0xe916, + Binary = 0xe917, + BinaryCircleCheck = 0xe918, + BinaryLock = 0xe919, + BinarySlash = 0xe91a, + BinBottles = 0xe91b, + BinBottlesRecycle = 0xe91c, + Binoculars = 0xe91d, + BinRecycle = 0xe91e, + Biohazard = 0xe91f, + Bird = 0xe920, + BitcoinSign = 0xe921, + Blanket = 0xe922, + BlanketFire = 0xe923, + Blender = 0xe924, + BlenderPhone = 0xe925, + Blinds = 0xe926, + BlindsOpen = 0xe927, + BlindsRaised = 0xe928, + Block = 0xe929, + BlockBrick = 0xe92a, + BlockBrickFire = 0xe92b, + BlockQuestion = 0xe92c, + BlockQuote = 0xe92d, + Blog = 0xe92e, + Blueberries = 0xe92f, + Bluetooth = 0xe930, + Bold = 0xe931, + Bolt = 0xe932, + BoltAuto = 0xe933, + BoltLightning = 0xe934, + BoltSlash = 0xe935, + Bomb = 0xe936, + Bone = 0xe937, + BoneBreak = 0xe938, + Bong = 0xe939, + Book = 0xe93a, + BookArrowRight = 0xe93b, + BookArrowUp = 0xe93c, + BookAtlas = 0xe93d, + BookBible = 0xe93e, + BookBlank = 0xe93f, + BookBookmark = 0xe940, + BookCircleArrowRight = 0xe941, + BookCircleArrowUp = 0xe942, + BookCopy = 0xe943, + BookFont = 0xe944, + BookHeart = 0xe945, + BookJournalWhills = 0xe946, + Bookmark = 0xe947, + BookmarkSlash = 0xe948, + BookMedical = 0xe949, + BookOpen = 0xe94a, + BookOpenCover = 0xe94b, + BookOpenReader = 0xe94c, + BookQuran = 0xe94d, + Books = 0xe94e, + BookSection = 0xe94f, + BookSkull = 0xe950, + BooksMedical = 0xe951, + BookSparkles = 0xe952, + BookTanakh = 0xe953, + BookUser = 0xe954, + Boombox = 0xe955, + Boot = 0xe956, + BoothCurtain = 0xe957, + BootHeeled = 0xe958, + BorderAll = 0xe959, + BorderBottom = 0xe95a, + BorderBottomRight = 0xe95b, + BorderCenterH = 0xe95c, + BorderCenterV = 0xe95d, + BorderInner = 0xe95e, + BorderLeft = 0xe95f, + BorderNone = 0xe960, + BorderOuter = 0xe961, + BorderRight = 0xe962, + BorderTop = 0xe963, + BorderTopLeft = 0xe964, + BoreHole = 0xe965, + BottleDroplet = 0xe966, + BottleWater = 0xe967, + BowArrow = 0xe968, + BowlChopsticks = 0xe969, + BowlChopsticksNoodles = 0xe96a, + BowlFood = 0xe96b, + BowlHot = 0xe96c, + BowlingBall = 0xe96d, + BowlingBallPin = 0xe96e, + BowlingPins = 0xe96f, + BowlRice = 0xe970, + BowlScoop = 0xe971, + BowlScoops = 0xe972, + BowlSoftServe = 0xe973, + BowlSpoon = 0xe974, + Box = 0xe975, + BoxArchive = 0xe976, + BoxBallot = 0xe977, + BoxCheck = 0xe978, + BoxCircleCheck = 0xe979, + BoxDollar = 0xe97a, + BoxesPacking = 0xe97b, + BoxesStacked = 0xe97c, + BoxHeart = 0xe97d, + BoxingGlove = 0xe97e, + BoxOpen = 0xe97f, + BoxOpenFull = 0xe980, + BoxTaped = 0xe981, + BoxTissue = 0xe982, + BracketCurly = 0xe983, + BracketCurlyRight = 0xe984, + BracketRound = 0xe985, + BracketRoundRight = 0xe986, + BracketsCurly = 0xe987, + BracketSquare = 0xe988, + BracketSquareRight = 0xe989, + BracketsRound = 0xe98a, + BracketsSquare = 0xe98b, + Braille = 0xe98c, + Brain = 0xe98d, + BrainArrowCurvedRight = 0xe98e, + BrainCircuit = 0xe98f, + BrakeWarning = 0xe990, + BrazilianRealSign = 0xe991, + BreadLoaf = 0xe992, + BreadSlice = 0xe993, + BreadSliceButter = 0xe994, + Bridge = 0xe995, + BridgeCircleCheck = 0xe996, + BridgeCircleExclamation = 0xe997, + BridgeCircleXmark = 0xe998, + BridgeLock = 0xe999, + BridgeSuspension = 0xe99a, + BridgeWater = 0xe99b, + Briefcase = 0xe99c, + BriefcaseArrowRight = 0xe99d, + BriefcaseBlank = 0xe99e, + BriefcaseMedical = 0xe99f, + Brightness = 0xe9a0, + BrightnessLow = 0xe9a1, + BringForward = 0xe9a2, + BringFront = 0xe9a3, + Broccoli = 0xe9a4, + Clover = 0xe9a5, + BroomBall = 0xe9a6, + BroomWide = 0xe9a7, + Browser = 0xe9a8, + Browsers = 0xe9a9, + Brush = 0xe9aa, + Bucket = 0xe9ab, + Bug = 0xe9ac, + Bugs = 0xe9ad, + BugSlash = 0xe9ae, + Building = 0xe9af, + BuildingCircleArrowRight = 0xe9b0, + BuildingCircleCheck = 0xe9b1, + BuildingCircleExclamation = 0xe9b2, + BuildingCircleXmark = 0xe9b3, + BuildingColumns = 0xe9b4, + BuildingFlag = 0xe9b5, + BuildingLock = 0xe9b6, + BuildingMagnifyingGlass = 0xe9b7, + BuildingMemo = 0xe9b8, + BuildingNgo = 0xe9b9, + Buildings = 0xe9ba, + BuildingShield = 0xe9bb, + BuildingUn = 0xe9bc, + BuildingUser = 0xe9bd, + BuildingWheat = 0xe9be, + Bulldozer = 0xe9bf, + Bullhorn = 0xe9c0, + Bullseye = 0xe9c1, + BullseyeArrow = 0xe9c2, + BullseyePointer = 0xe9c3, + Buoy = 0xe9c4, + BuoyMooring = 0xe9c5, + Burger = 0xe9c6, + BurgerCheese = 0xe9c7, + BurgerFries = 0xe9c8, + BurgerGlass = 0xe9c9, + BurgerLettuce = 0xe9ca, + BurgerSoda = 0xe9cb, + Burrito = 0xe9cc, + Burst = 0xe9cd, + Bus = 0xe9ce, + BusinessTime = 0xe9cf, + BusSchool = 0xe9d0, + BusSimple = 0xe9d1, + Butter = 0xe9d2, + C = 0xe9d3, + Cabin = 0xe9d4, + CabinetFiling = 0xe9d5, + CableCar = 0xe9d6, + Cactus = 0xe9d7, + CakeCandles = 0xe9d8, + CakeSlice = 0xe9d9, + Calculator = 0xe9da, + CalculatorSimple = 0xe9db, + Calendar = 0xe9dc, + CalendarArrowDown = 0xe9dd, + CalendarArrowUp = 0xe9de, + CalendarCheck = 0xe9df, + CalendarCircleExclamation = 0xe9e0, + CalendarCircleMinus = 0xe9e1, + CalendarCirclePlus = 0xe9e2, + CalendarCircleUser = 0xe9e3, + CalendarClock = 0xe9e4, + CalendarDay = 0xe9e5, + CalendarDays = 0xe9e6, + CalendarExclamation = 0xe9e7, + CalendarImage = 0xe9e8, + CalendarLines = 0xe9e9, + CalendarMinus = 0xe9ea, + CalendarHeart = 0xe9eb, + CalendarLinesPen = 0xe9ec, + CalendarPlus = 0xe9ed, + Calendars = 0xe9ee, + CalendarPen = 0xe9ef, + CalendarStar = 0xe9f0, + CalendarWeek = 0xe9f1, + CalendarXmark = 0xe9f2, + CameraCctv = 0xe9f3, + CalendarUsers = 0xe9f4, + CameraMovie = 0xe9f5, + Camcorder = 0xe9f6, + CameraRetro = 0xe9f7, + CameraRotate = 0xe9f8, + CameraSecurity = 0xe9f9, + CameraPolaroid = 0xe9fa, + CameraSlash = 0xe9fb, + CalendarRange = 0xe9fc, + CameraViewfinder = 0xe9fd, + Camera = 0xe9fe, + CameraWeb = 0xe9ff, + Campground = 0xea00, + CandleHolder = 0xea01, + CameraWebSlash = 0xea02, + Campfire = 0xea03, + Candy = 0xea04, + CandyCane = 0xea05, + CandyBar = 0xea06, + CanFood = 0xea07, + CandyCorn = 0xea08, + Cannabis = 0xea09, + Cannon = 0xea0a, + Capsules = 0xea0b, + Car = 0xea0c, + Caravan = 0xea0d, + CaravanSimple = 0xea0e, + CarBattery = 0xea0f, + CarBolt = 0xea10, + CarBuilding = 0xea11, + CarBump = 0xea12, + CarBurst = 0xea13, + CarBus = 0xea14, + CarCircleBolt = 0xea15, + CardClub = 0xea16, + CardDiamond = 0xea17, + CardHeart = 0xea18, + Cards = 0xea19, + CardsBlank = 0xea1a, + CardSpade = 0xea1b, + CaretDown = 0xea1c, + CaretLeft = 0xea1d, + CaretRight = 0xea1e, + CaretUp = 0xea1f, + CarGarage = 0xea20, + CarMirrors = 0xea21, + CarOn = 0xea22, + CarRear = 0xea23, + Carrot = 0xea24, + Cars = 0xea25, + CarSide = 0xea26, + CarSideBolt = 0xea27, + CartArrowDown = 0xea28, + CartArrowUp = 0xea29, + CartCircleArrowDown = 0xea2a, + CartCircleArrowUp = 0xea2b, + CartCircleCheck = 0xea2c, + CartCircleExclamation = 0xea2d, + CartCirclePlus = 0xea2e, + CartCircleXmark = 0xea2f, + CartFlatbed = 0xea30, + CartFlatbedBoxes = 0xea31, + CartFlatbedEmpty = 0xea32, + CartFlatbedSuitcase = 0xea33, + CarTilt = 0xea34, + CartMinus = 0xea35, + CartShopping = 0xea36, + CartPlus = 0xea37, + CartShoppingFast = 0xea38, + CarTunnel = 0xea39, + CartXmark = 0xea3a, + CarWash = 0xea3b, + CarWrench = 0xea3c, + CashRegister = 0xea3d, + CassetteBetamax = 0xea3e, + CassetteTape = 0xea3f, + CassetteVhs = 0xea40, + Castle = 0xea41, + Cat = 0xea42, + CatSpace = 0xea43, + Cauldron = 0xea44, + CediSign = 0xea45, + CentSign = 0xea46, + Certificate = 0xea47, + Chair = 0xea48, + ChairOffice = 0xea49, + Chalkboard = 0xea4a, + ChalkboardUser = 0xea4b, + ChampagneGlass = 0xea4c, + ChampagneGlasses = 0xea4d, + ChargingStation = 0xea4e, + ChartArea = 0xea4f, + ChartBar = 0xea50, + ChartBullet = 0xea51, + ChartCandlestick = 0xea52, + ChartColumn = 0xea53, + ChartGantt = 0xea54, + ChartKanban = 0xea55, + ChartLine = 0xea56, + ChartLineDown = 0xea57, + ChartLineUp = 0xea58, + ChartLineUpDown = 0xea59, + ChartMixed = 0xea5a, + ChartMixedUpCircleCurrency = 0xea5b, + ChartMixedUpCircleDollar = 0xea5c, + ChartNetwork = 0xea5d, + ChartPie = 0xea5e, + ChartPieSimple = 0xea5f, + ChartPieSimpleCircleCurrency = 0xea60, + ChartPieSimpleCircleDollar = 0xea61, + ChartPyramid = 0xea62, + ChartRadar = 0xea63, + ChartScatter = 0xea64, + ChartScatter3d = 0xea65, + ChartScatterBubble = 0xea66, + ChartSimple = 0xea67, + ChartSimpleHorizontal = 0xea68, + ChartTreeMap = 0xea69, + ChartUser = 0xea6a, + ChartWaterfall = 0xea6b, + Check = 0xea6c, + CheckDouble = 0xea6d, + CheckToSlot = 0xea6e, + Cheese = 0xea6f, + CheeseSwiss = 0xea70, + Cherries = 0xea71, + Chess = 0xea72, + ChessBishop = 0xea73, + ChessBishopPiece = 0xea74, + ChessBoard = 0xea75, + ChessClock = 0xea76, + ChessClockFlip = 0xea77, + ChessKing = 0xea78, + ChessKingPiece = 0xea79, + ChessKnight = 0xea7a, + ChessKnightPiece = 0xea7b, + ChessPawn = 0xea7c, + ChessPawnPiece = 0xea7d, + ChessQueen = 0xea7e, + ChessQueenPiece = 0xea7f, + ChessRook = 0xea80, + ChessRookPiece = 0xea81, + Chestnut = 0xea82, + ChevronDown = 0xea83, + ChevronLeft = 0xea84, + ChevronRight = 0xea85, + ChevronsDown = 0xea86, + ChevronsLeft = 0xea87, + ChevronsRight = 0xea88, + ChevronsUp = 0xea89, + ChevronUp = 0xea8a, + ChfSign = 0xea8b, + Child = 0xea8c, + ChildCombatant = 0xea8d, + ChildDress = 0xea8e, + ChildReaching = 0xea8f, + Children = 0xea90, + Chimney = 0xea91, + Chopsticks = 0xea92, + Church = 0xea93, + Circle = 0xea94, + Circle0 = 0xea95, + Circle1 = 0xea96, + Circle2 = 0xea97, + Circle3 = 0xea98, + Circle4 = 0xea99, + Circle5 = 0xea9a, + Circle6 = 0xea9b, + Circle7 = 0xea9c, + Circle8 = 0xea9d, + Circle9 = 0xea9e, + CircleA = 0xea9f, + CircleAmpersand = 0xeaa0, + CircleArrowDown = 0xeaa1, + CircleArrowDownLeft = 0xeaa2, + CircleArrowDownRight = 0xeaa3, + CircleArrowLeft = 0xeaa4, + CircleArrowRight = 0xeaa5, + CircleArrowUp = 0xeaa6, + CircleArrowUpLeft = 0xeaa7, + CircleArrowUpRight = 0xeaa8, + CircleB = 0xeaa9, + CircleBolt = 0xeaaa, + CircleBookmark = 0xeaab, + CircleBookOpen = 0xeaac, + CircleC = 0xeaad, + CircleCalendar = 0xeaae, + CircleCamera = 0xeaaf, + CircleCaretDown = 0xeab0, + CircleCaretLeft = 0xeab1, + CircleCaretRight = 0xeab2, + CircleCaretUp = 0xeab3, + CircleCheck = 0xeab4, + CircleChevronDown = 0xeab5, + CircleChevronLeft = 0xeab6, + CircleChevronRight = 0xeab7, + CircleChevronUp = 0xeab8, + CircleD = 0xeab9, + CircleDashed = 0xeaba, + CircleDivide = 0xeabb, + CircleDollar = 0xeabc, + CircleDollarToSlot = 0xeabd, + CircleDot = 0xeabe, + CircleDown = 0xeabf, + CircleDownLeft = 0xeac0, + CircleDownRight = 0xeac1, + CircleE = 0xeac2, + CircleEllipsis = 0xeac3, + CircleEllipsisVertical = 0xeac4, + CircleEnvelope = 0xeac5, + CircleEuro = 0xeac6, + CircleExclamation = 0xeac7, + CircleExclamationCheck = 0xeac8, + CircleF = 0xeac9, + CircleG = 0xeaca, + CircleH = 0xeacb, + CircleHalf = 0xeacc, + CircleHalfStroke = 0xeacd, + CircleHeart = 0xeace, + CircleI = 0xeacf, + CircleInfo = 0xead0, + CircleJ = 0xead1, + CircleK = 0xead2, + CircleL = 0xead3, + CircleLeft = 0xead4, + CircleLocationArrow = 0xead5, + CircleM = 0xead6, + CircleMicrophone = 0xead7, + CircleMicrophoneLines = 0xead8, + CircleMinus = 0xead9, + CircleN = 0xeada, + CircleNodes = 0xeadb, + CircleNotch = 0xeadc, + CircleO = 0xeadd, + CircleP = 0xeade, + CircleParking = 0xeadf, + CirclePause = 0xeae0, + CirclePhone = 0xeae1, + CirclePhoneFlip = 0xeae2, + CirclePhoneHangup = 0xeae3, + CirclePlay = 0xeae4, + CirclePlus = 0xeae5, + CircleQ = 0xeae6, + CircleQuarter = 0xeae7, + CircleQuarters = 0xeae8, + CircleQuarterStroke = 0xeae9, + CircleQuestion = 0xeaea, + CircleR = 0xeaeb, + CircleRadiation = 0xeaec, + CircleRight = 0xeaed, + CircleS = 0xeaee, + CircleSmall = 0xeaef, + CircleSort = 0xeaf0, + CircleSortDown = 0xeaf1, + CircleSortUp = 0xeaf2, + CirclesOverlap = 0xeaf3, + CircleStar = 0xeaf4, + CircleSterling = 0xeaf5, + CircleStop = 0xeaf6, + CircleT = 0xeaf7, + CircleThreeQuarters = 0xeaf8, + CircleThreeQuartersStroke = 0xeaf9, + CircleTrash = 0xeafa, + CircleU = 0xeafb, + CircleUp = 0xeafc, + CircleUpLeft = 0xeafd, + CircleUpRight = 0xeafe, + CircleUser = 0xeaff, + CircleV = 0xeb00, + CircleVideo = 0xeb01, + CircleW = 0xeb02, + CircleWaveformLines = 0xeb03, + CircleX = 0xeb04, + CircleXmark = 0xeb05, + CircleY = 0xeb06, + CircleYen = 0xeb07, + CircleZ = 0xeb08, + Citrus = 0xeb09, + CitrusSlice = 0xeb0a, + City = 0xeb0b, + Clapperboard = 0xeb0c, + ClapperboardPlay = 0xeb0d, + Clarinet = 0xeb0e, + ClawMarks = 0xeb0f, + Clipboard = 0xeb10, + ClipboardCheck = 0xeb11, + ClipboardList = 0xeb12, + ClipboardListCheck = 0xeb13, + ClipboardMedical = 0xeb14, + ClipboardPrescription = 0xeb15, + ClipboardQuestion = 0xeb16, + ClipboardUser = 0xeb17, + Clock = 0xeb18, + ClockDesk = 0xeb19, + ClockEight = 0xeb1a, + ClockEightThirty = 0xeb1b, + ClockEleven = 0xeb1c, + ClockElevenThirty = 0xeb1d, + ClockFive = 0xeb1e, + ClockFiveThirty = 0xeb1f, + ClockFourThirty = 0xeb20, + ClockNine = 0xeb21, + ClockNineThirty = 0xeb22, + ClockOne = 0xeb23, + ClockOneThirty = 0xeb24, + ClockRotateLeft = 0xeb25, + ClockSeven = 0xeb26, + ClockSevenThirty = 0xeb27, + ClockSix = 0xeb28, + ClockSixThirty = 0xeb29, + ClockTen = 0xeb2a, + ClockTenThirty = 0xeb2b, + ClockThree = 0xeb2c, + ClockThreeThirty = 0xeb2d, + ClockTwelve = 0xeb2e, + ClockTwelveThirty = 0xeb2f, + ClockTwo = 0xeb30, + ClockTwoThirty = 0xeb31, + Clone = 0xeb32, + ClosedCaptioning = 0xeb33, + ClosedCaptioningSlash = 0xeb34, + ClothesHanger = 0xeb35, + Cloud = 0xeb36, + CloudArrowDown = 0xeb37, + CloudArrowUp = 0xeb38, + CloudBinary = 0xeb39, + CloudBolt = 0xeb3a, + CloudBoltMoon = 0xeb3b, + CloudBoltSun = 0xeb3c, + CloudCheck = 0xeb3d, + CloudDrizzle = 0xeb3e, + CloudExclamation = 0xeb3f, + CloudFog = 0xeb40, + CloudHail = 0xeb41, + CloudHailMixed = 0xeb42, + CloudMeatball = 0xeb43, + CloudMinus = 0xeb44, + CloudMoon = 0xeb45, + CloudMoonRain = 0xeb46, + CloudMusic = 0xeb47, + CloudPlus = 0xeb48, + CloudQuestion = 0xeb49, + CloudRain = 0xeb4a, + CloudRainbow = 0xeb4b, + Clouds = 0xeb4c, + CloudShowers = 0xeb4d, + CloudShowersHeavy = 0xeb4e, + CloudShowersWater = 0xeb4f, + CloudSlash = 0xeb50, + CloudSleet = 0xeb51, + CloudsMoon = 0xeb52, + CloudSnow = 0xeb53, + CloudsSun = 0xeb54, + CloudSun = 0xeb55, + CloudSunRain = 0xeb56, + CloudWord = 0xeb57, + CloudXmark = 0xeb58, + FaceSaluting = 0xeb59, + Club = 0xeb5a, + Coconut = 0xeb5b, + Code = 0xeb5c, + CodeBranch = 0xeb5d, + CodeCommit = 0xeb5e, + CodeCompare = 0xeb5f, + CodeFork = 0xeb60, + CodePullRequest = 0xeb61, + CodePullRequestClosed = 0xeb62, + CodeMerge = 0xeb63, + CodePullRequestDraft = 0xeb64, + CodeSimple = 0xeb65, + CoffeeBean = 0xeb66, + CoffeeBeans = 0xeb67, + CoffeePot = 0xeb68, + CoffinCross = 0xeb69, + Coffin = 0xeb6a, + Coin = 0xeb6b, + CoinBlank = 0xeb6c, + CoinVertical = 0xeb6d, + CoinFront = 0xeb6e, + Colon = 0xeb6f, + Coins = 0xeb70, + ColonSign = 0xeb71, + Columns3 = 0xeb72, + Comet = 0xeb73, + Comma = 0xeb74, + Comment = 0xeb75, + CommentArrowDown = 0xeb76, + CommentArrowUp = 0xeb77, + CommentArrowUpRight = 0xeb78, + CommentCaptions = 0xeb79, + CommentCheck = 0xeb7a, + CommentDollar = 0xeb7b, + CommentDots = 0xeb7c, + CommentCode = 0xeb7d, + Command = 0xeb7e, + CommentImage = 0xeb7f, + CommentHeart = 0xeb80, + CommentExclamation = 0xeb81, + CommentLines = 0xeb82, + CommentMedical = 0xeb83, + CommentMiddle = 0xeb84, + CommentMiddleTop = 0xeb85, + CommentMinus = 0xeb86, + CommentMusic = 0xeb87, + CommentPen = 0xeb88, + CommentPlus = 0xeb89, + CommentQuestion = 0xeb8a, + CommentQuote = 0xeb8b, + Comments = 0xeb8c, + CommentsDollar = 0xeb8d, + CommentSlash = 0xeb8e, + CommentSmile = 0xeb8f, + CommentSms = 0xeb90, + CommentsQuestion = 0xeb91, + CommentsQuestionCheck = 0xeb92, + CommentText = 0xeb93, + CommentXmark = 0xeb94, + CompactDisc = 0xeb95, + Compass = 0xeb96, + CompassDrafting = 0xeb97, + CompassSlash = 0xeb98, + Compress = 0xeb99, + CompressWide = 0xeb9a, + ComputerClassic = 0xeb9b, + ComputerMouse = 0xeb9c, + ComputerMouseScrollwheel = 0xeb9d, + ComputerSpeaker = 0xeb9e, + ContainerStorage = 0xeb9f, + ConveyorBelt = 0xeba0, + ConveyorBeltBoxes = 0xeba1, + ConveyorBeltArm = 0xeba2, + CookieBite = 0xeba3, + Computer = 0xeba4, + Copyright = 0xeba5, + Cookie = 0xeba6, + Corn = 0xeba7, + Corner = 0xeba8, + Copy = 0xeba9, + CourtSport = 0xebaa, + Cow = 0xebab, + Couch = 0xebac, + CowbellCirclePlus = 0xebad, + Cowbell = 0xebae, + CrateEmpty = 0xebaf, + CreditCardFront = 0xebb0, + Crab = 0xebb1, + CreditCardBlank = 0xebb2, + CreditCard = 0xebb3, + CrateApple = 0xebb4, + ConveyorBeltEmpty = 0xebb5, + Crop = 0xebb6, + Crosshairs = 0xebb7, + CropSimple = 0xebb8, + Cross = 0xebb9, + Croissant = 0xebba, + CricketBatBall = 0xebbb, + CrosshairsSimple = 0xebbc, + Crow = 0xebbd, + Crown = 0xebbe, + Crutch = 0xebbf, + Crutches = 0xebc0, + CruzeiroSign = 0xebc1, + CrystalBall = 0xebc2, + Cube = 0xebc3, + Cubes = 0xebc4, + CubesStacked = 0xebc5, + Cucumber = 0xebc6, + Cupcake = 0xebc7, + CupStraw = 0xebc8, + CupStrawSwoosh = 0xebc9, + CupTogo = 0xebca, + CurlingStone = 0xebcb, + Custard = 0xebcc, + D = 0xebcd, + Dagger = 0xebce, + Dash = 0xebcf, + Database = 0xebd0, + Deer = 0xebd1, + DeerRudolph = 0xebd2, + DeleteLeft = 0xebd3, + DeleteRight = 0xebd4, + Desktop = 0xebd5, + Democrat = 0xebd6, + DesktopArrowDown = 0xebd7, + Dharmachakra = 0xebd8, + DiagramCells = 0xebd9, + DiagramLeanCanvas = 0xebda, + DiagramNested = 0xebdb, + DiagramNext = 0xebdc, + DiagramPredecessor = 0xebdd, + DiagramPrevious = 0xebde, + DiagramProject = 0xebdf, + DiagramSankey = 0xebe0, + DiagramSubtask = 0xebe1, + DiagramSuccessor = 0xebe2, + DiagramVenn = 0xebe3, + Dial = 0xebe4, + DialHigh = 0xebe5, + DialLow = 0xebe6, + DialMax = 0xebe7, + DialMed = 0xebe8, + DialMedLow = 0xebe9, + DialMin = 0xebea, + DialOff = 0xebeb, + Diamond = 0xebec, + DiamondExclamation = 0xebed, + DiamondHalf = 0xebee, + DiamondHalfStroke = 0xebef, + DiamondTurnRight = 0xebf0, + Dice = 0xebf1, + DiceD4 = 0xebf2, + DiceD6 = 0xebf3, + DiceD8 = 0xebf4, + DiceD10 = 0xebf5, + DiceD12 = 0xebf6, + DiceD20 = 0xebf7, + DiceFive = 0xebf8, + DiceFour = 0xebf9, + DiceOne = 0xebfa, + DiceSix = 0xebfb, + DiceThree = 0xebfc, + DiceTwo = 0xebfd, + Dinosaur = 0xebfe, + Diploma = 0xebff, + DiscDrive = 0xec00, + Disease = 0xec01, + Display = 0xec02, + DisplayArrowDown = 0xec03, + DisplayChartUp = 0xec04, + DisplayChartUpCircleCurrency = 0xec05, + DisplayChartUpCircleDollar = 0xec06, + DisplayCode = 0xec07, + DisplayMedical = 0xec08, + DisplaySlash = 0xec09, + DistributeSpacingHorizontal = 0xec0a, + DistributeSpacingVertical = 0xec0b, + Ditto = 0xec0c, + Divide = 0xec0d, + Dna = 0xec0e, + Dog = 0xec0f, + DogLeashed = 0xec10, + DollarSign = 0xec11, + Dolly = 0xec12, + DollyEmpty = 0xec13, + Dolphin = 0xec14, + DongSign = 0xec15, + DoNotEnter = 0xec16, + Donut = 0xec17, + DoorClosed = 0xec18, + DoorOpen = 0xec19, + Dove = 0xec1a, + Down = 0xec1b, + DownFromDottedLine = 0xec1c, + DownFromLine = 0xec1d, + DownLeft = 0xec1e, + DownLeftAndUpRightToCenter = 0xec1f, + Download = 0xec20, + DownLong = 0xec21, + DownRight = 0xec22, + DownToBracket = 0xec23, + DownToDottedLine = 0xec24, + DownToLine = 0xec25, + Dragon = 0xec26, + DrawCircle = 0xec27, + DrawPolygon = 0xec28, + DrawSquare = 0xec29, + Dreidel = 0xec2a, + Drone = 0xec2b, + DroneFront = 0xec2c, + Droplet = 0xec2d, + DropletDegree = 0xec2e, + DropletPercent = 0xec2f, + DropletSlash = 0xec30, + Drum = 0xec31, + DrumSteelpan = 0xec32, + Drumstick = 0xec33, + DrumstickBite = 0xec34, + Dryer = 0xec35, + DryerHeat = 0xec36, + Duck = 0xec37, + Dumbbell = 0xec38, + Dumpster = 0xec39, + DumpsterFire = 0xec3a, + Dungeon = 0xec3b, + E = 0xec3c, + Ear = 0xec3d, + EarDeaf = 0xec3e, + EarListen = 0xec3f, + EarMuffs = 0xec40, + EarthAfrica = 0xec41, + EarthAmericas = 0xec42, + EarthAsia = 0xec43, + EarthEurope = 0xec44, + EarthOceania = 0xec45, + Eclipse = 0xec46, + Egg = 0xec47, + EggFried = 0xec48, + Eggplant = 0xec49, + Eject = 0xec4a, + Elephant = 0xec4b, + Elevator = 0xec4c, + Ellipsis = 0xec4d, + EllipsisStroke = 0xec4e, + EllipsisStrokeVertical = 0xec4f, + EllipsisVertical = 0xec50, + EmptySet = 0xec51, + Engine = 0xec52, + EngineWarning = 0xec53, + Envelope = 0xec54, + EnvelopeCircleCheck = 0xec55, + EnvelopeDot = 0xec56, + EnvelopeOpen = 0xec57, + EnvelopeOpenDollar = 0xec58, + EnvelopeOpenText = 0xec59, + Envelopes = 0xec5a, + EnvelopesBulk = 0xec5b, + Equals = 0xec5c, + Eraser = 0xec5d, + Escalator = 0xec5e, + Ethernet = 0xec5f, + EuroSign = 0xec60, + Excavator = 0xec61, + Exclamation = 0xec62, + Expand = 0xec63, + ExpandWide = 0xec64, + Explosion = 0xec65, + Eye = 0xec66, + EyeDropper = 0xec67, + EyeDropperFull = 0xec68, + EyeDropperHalf = 0xec69, + EyeEvil = 0xec6a, + EyeLowVision = 0xec6b, + Eyes = 0xec6c, + EyeSlash = 0xec6d, + F = 0xec6e, + FaceAngry = 0xec6f, + FaceAngryHorns = 0xec70, + FaceAnguished = 0xec71, + FaceAnxiousSweat = 0xec72, + FaceAstonished = 0xec73, + FaceAwesome = 0xec74, + FaceBeamHandOverMouth = 0xec75, + FaceClouds = 0xec76, + FaceConfounded = 0xec77, + FaceConfused = 0xec78, + FaceCowboyHat = 0xec79, + FaceDiagonalMouth = 0xec7a, + FaceDisappointed = 0xec7b, + FaceDisguise = 0xec7c, + FaceDizzy = 0xec7d, + FaceDotted = 0xec7e, + FaceDowncastSweat = 0xec7f, + FaceDrooling = 0xec80, + FaceExhaling = 0xec81, + FaceExplode = 0xec82, + FaceExpressionless = 0xec83, + FaceEyesXmarks = 0xec84, + FaceFearful = 0xec85, + FaceFlushed = 0xec86, + FaceFrown = 0xec87, + FaceFrownOpen = 0xec88, + FaceFrownSlight = 0xec89, + FaceGlasses = 0xec8a, + FaceGrimace = 0xec8b, + FaceGrin = 0xec8c, + FaceGrinBeam = 0xec8d, + FaceGrinBeamSweat = 0xec8e, + FaceGrinHearts = 0xec8f, + FaceGrinSquint = 0xec90, + FaceGrinSquintTears = 0xec91, + FaceGrinStars = 0xec92, + FaceGrinTears = 0xec93, + FaceGrinTongue = 0xec94, + FaceGrinTongueSquint = 0xec95, + FaceGrinTongueWink = 0xec96, + FaceGrinWide = 0xec97, + FaceGrinWink = 0xec98, + FaceHandOverMouth = 0xec99, + FaceHandPeeking = 0xec9a, + FaceHandYawn = 0xec9b, + FaceHeadBandage = 0xec9c, + FaceHoldingBackTears = 0xec9d, + FaceHushed = 0xec9e, + FaceIcicles = 0xec9f, + FaceKiss = 0xeca0, + FaceKissBeam = 0xeca1, + FaceKissClosedEyes = 0xeca2, + FaceKissWinkHeart = 0xeca3, + FaceLaugh = 0xeca4, + FaceLaughBeam = 0xeca5, + FaceLaughSquint = 0xeca6, + FaceLaughWink = 0xeca7, + FaceLying = 0xeca8, + FaceMask = 0xeca9, + FaceMeh = 0xecaa, + FaceMehBlank = 0xecab, + FaceMelting = 0xecac, + FaceMonocle = 0xecad, + FaceNauseated = 0xecae, + FaceNoseSteam = 0xecaf, + FaceParty = 0xecb0, + FacePensive = 0xecb1, + FacePersevering = 0xecb2, + FacePleading = 0xecb3, + FacePouting = 0xecb4, + FaceRaisedEyebrow = 0xecb5, + FaceRelieved = 0xecb6, + FaceRollingEyes = 0xecb7, + FaceSadCry = 0xecb8, + FaceSadSweat = 0xecb9, + FaceSadTear = 0xecba, + Hotdog = 0xecbb, + FaceScream = 0xecbc, + FaceShush = 0xecbd, + FaceSleeping = 0xecbe, + FaceSleepy = 0xecbf, + FaceSmileBeam = 0xecc0, + FaceSmile = 0xecc1, + FaceSmileHalo = 0xecc2, + FaceSmileHearts = 0xecc3, + FaceSmileHorns = 0xecc4, + FaceSmilePlus = 0xecc5, + FaceSmileRelaxed = 0xecc6, + FaceSmileTear = 0xecc7, + FaceSmileTongue = 0xecc8, + FaceSmileUpsideDown = 0xecc9, + FaceSmileWink = 0xecca, + FaceSmilingHands = 0xeccb, + FaceSpiralEyes = 0xeccc, + FaceSmirking = 0xeccd, + FaceSunglasses = 0xecce, + FaceSurprise = 0xeccf, + FaceSwear = 0xecd0, + FaceThermometer = 0xecd1, + FaceThinking = 0xecd2, + FaceTired = 0xecd3, + FaceTissue = 0xecd4, + FaceTongueMoney = 0xecd5, + FaceTongueSweat = 0xecd6, + FaceUnamused = 0xecd7, + FaceViewfinder = 0xecd8, + FaceVomit = 0xecd9, + FaceWeary = 0xecda, + FaceWoozy = 0xecdb, + FaceWorried = 0xecdc, + FaceZany = 0xecdd, + FaceZipper = 0xecde, + Falafel = 0xecdf, + Family = 0xece0, + FamilyDress = 0xece1, + FamilyPants = 0xece2, + Fan = 0xece3, + FanTable = 0xece4, + Farm = 0xece5, + Faucet = 0xece6, + FaucetDrip = 0xece7, + Fax = 0xece8, + Feather = 0xece9, + FeatherPointed = 0xecea, + Fence = 0xeceb, + FerrisWheel = 0xecec, + Ferry = 0xeced, + FieldHockeyStickBall = 0xecee, + File = 0xecef, + FileArrowDown = 0xecf0, + FileArrowUp = 0xecf1, + FileAudio = 0xecf2, + FileBinary = 0xecf3, + FileCertificate = 0xecf4, + FileChartColumn = 0xecf5, + FileChartPie = 0xecf6, + FileCheck = 0xecf7, + FileCircleCheck = 0xecf8, + FileCircleExclamation = 0xecf9, + FileCircleInfo = 0xecfa, + FileCircleMinus = 0xecfb, + FileCirclePlus = 0xecfc, + FileCircleQuestion = 0xecfd, + FileCircleXmark = 0xecfe, + FileCode = 0xecff, + FileContract = 0xed00, + FileCsv = 0xed01, + FileDashedLine = 0xed02, + FileDoc = 0xed03, + FileEps = 0xed04, + FileExclamation = 0xed05, + FileGif = 0xed06, + FileExport = 0xed07, + FileImport = 0xed08, + FileExcel = 0xed09, + FileHeart = 0xed0a, + FileInvoiceDollar = 0xed0b, + FileImage = 0xed0c, + FileInvoice = 0xed0d, + FileJpg = 0xed0e, + FileLines = 0xed0f, + FileMagnifyingGlass = 0xed10, + FileLock = 0xed11, + FileMedical = 0xed12, + FileMinus = 0xed13, + FileMov = 0xed14, + FileMp4 = 0xed15, + FileMp3 = 0xed16, + FilePdf = 0xed17, + FileMusic = 0xed18, + FilePen = 0xed19, + FilePlusMinus = 0xed1a, + FilePrescription = 0xed1b, + FilePlus = 0xed1c, + FilePowerpoint = 0xed1d, + FilePpt = 0xed1e, + Files = 0xed1f, + FileShield = 0xed20, + FileSignature = 0xed21, + FileSlash = 0xed22, + FilesMedical = 0xed23, + FileSpreadsheet = 0xed24, + FileSvg = 0xed25, + FileUser = 0xed26, + FileVector = 0xed27, + FileVideo = 0xed28, + FileWaveform = 0xed29, + FileWord = 0xed2a, + FileXls = 0xed2b, + FileXmark = 0xed2c, + FileXml = 0xed2d, + FileZip = 0xed2e, + FileZipper = 0xed2f, + Fill = 0xed30, + FillDrip = 0xed31, + Film = 0xed32, + FilmCanister = 0xed33, + Films = 0xed34, + FilmSimple = 0xed35, + FilmSlash = 0xed36, + Filter = 0xed37, + FilterCircleDollar = 0xed38, + FilterCircleXmark = 0xed39, + FilterList = 0xed3a, + Filters = 0xed3b, + FilterSlash = 0xed3c, + Fingerprint = 0xed3d, + Fire = 0xed3e, + FireBurner = 0xed3f, + FireExtinguisher = 0xed40, + FireFlame = 0xed41, + FireFlameCurved = 0xed42, + FireFlameSimple = 0xed43, + FireHydrant = 0xed44, + Fireplace = 0xed45, + FireSmoke = 0xed46, + Fish = 0xed47, + FishBones = 0xed48, + FishCooked = 0xed49, + FishFins = 0xed4a, + FishingRod = 0xed4b, + Flag = 0xed4c, + FlagCheckered = 0xed4d, + FlagPennant = 0xed4e, + FlagSwallowtail = 0xed4f, + FlagUsa = 0xed50, + Flashlight = 0xed51, + Flask = 0xed52, + FlaskGear = 0xed53, + FlaskRoundPoison = 0xed54, + FlaskRoundPotion = 0xed55, + FlaskVial = 0xed56, + Flatbread = 0xed57, + FlatbreadStuffed = 0xed58, + FloppyDisk = 0xed59, + FloppyDiskCircleArrowRight = 0xed5a, + FloppyDiskCircleXmark = 0xed5b, + FloppyDiskPen = 0xed5c, + FloppyDisks = 0xed5d, + FlorinSign = 0xed5e, + Flower = 0xed5f, + FlowerDaffodil = 0xed60, + FlowerTulip = 0xed61, + Flute = 0xed62, + FluxCapacitor = 0xed63, + FlyingDisc = 0xed64, + Folder = 0xed65, + FolderArrowDown = 0xed66, + FolderArrowUp = 0xed67, + FolderBookmark = 0xed68, + FolderCheck = 0xed69, + FolderClosed = 0xed6a, + FolderGear = 0xed6b, + FolderGrid = 0xed6c, + FolderHeart = 0xed6d, + FolderImage = 0xed6e, + FolderMagnifyingGlass = 0xed6f, + FolderMedical = 0xed70, + FolderMinus = 0xed71, + FolderMusic = 0xed72, + FolderOpen = 0xed73, + FolderPlus = 0xed74, + Folders = 0xed75, + FolderTree = 0xed76, + FolderUser = 0xed77, + FolderXmark = 0xed78, + FonduePot = 0xed79, + Font = 0xed7a, + FontAwesome = 0xed7b, + FontCase = 0xed7c, + Football = 0xed7d, + FootballHelmet = 0xed7e, + Fork = 0xed7f, + ForkKnife = 0xed80, + Forklift = 0xed81, + Fort = 0xed82, + Forward = 0xed83, + ForwardFast = 0xed84, + ForwardStep = 0xed85, + Frame = 0xed86, + FrancSign = 0xed87, + FrenchFries = 0xed88, + Frog = 0xed89, + Function = 0xed8a, + Futbol = 0xed8b, + G = 0xed8c, + Galaxy = 0xed8d, + GalleryThumbnails = 0xed8e, + GameBoard = 0xed8f, + GameBoardSimple = 0xed90, + GameConsoleHandheld = 0xed91, + GameConsoleHandheldCrank = 0xed92, + Gamepad = 0xed93, + GamepadModern = 0xed94, + Garage = 0xed95, + GarageCar = 0xed96, + GarageOpen = 0xed97, + Garlic = 0xed98, + GasPump = 0xed99, + GasPumpSlash = 0xed9a, + Gauge = 0xed9b, + GaugeCircleBolt = 0xed9c, + GaugeCircleMinus = 0xed9d, + GaugeCirclePlus = 0xed9e, + GaugeHigh = 0xed9f, + GaugeLow = 0xeda0, + GaugeMax = 0xeda1, + GaugeMin = 0xeda2, + GaugeSimple = 0xeda3, + GaugeSimpleHigh = 0xeda4, + GaugeSimpleLow = 0xeda5, + GaugeSimpleMax = 0xeda6, + GaugeSimpleMin = 0xeda7, + Gavel = 0xeda8, + Gear = 0xeda9, + GearCode = 0xedaa, + GearComplex = 0xedab, + GearComplexCode = 0xedac, + Gears = 0xedad, + Gem = 0xedae, + Genderless = 0xedaf, + Ghost = 0xedb0, + Gif = 0xedb1, + Gift = 0xedb2, + GiftCard = 0xedb3, + Gifts = 0xedb4, + GingerbreadMan = 0xedb5, + Glass = 0xedb6, + GlassCitrus = 0xedb7, + GlassEmpty = 0xedb8, + Glasses = 0xedb9, + GlassesRound = 0xedba, + GlassHalf = 0xedbb, + GlassWater = 0xedbc, + GlassWaterDroplet = 0xedbd, + Globe = 0xedbe, + GlobePointer = 0xedbf, + GlobeSnow = 0xedc0, + GlobeStand = 0xedc1, + GoalNet = 0xedc2, + GolfBallTee = 0xedc3, + GolfClub = 0xedc4, + GolfFlagHole = 0xedc5, + Gopuram = 0xedc6, + GraduationCap = 0xedc7, + Gramophone = 0xedc8, + Grapes = 0xedc9, + Grate = 0xedca, + GrateDroplet = 0xedcb, + GreaterThan = 0xedcc, + GreaterThanEqual = 0xedcd, + Grid = 0xedce, + Grid2 = 0xedcf, + Grid2Plus = 0xedd0, + Grid4 = 0xedd1, + Grid5 = 0xedd2, + GridDividers = 0xedd3, + GridHorizontal = 0xedd4, + GridRound = 0xedd5, + GridRound2 = 0xedd6, + GridRound2Plus = 0xedd7, + GridRound4 = 0xedd8, + GridRound5 = 0xedd9, + Grill = 0xedda, + GrillFire = 0xeddb, + GrillHot = 0xeddc, + Grip = 0xeddd, + GripDots = 0xedde, + GripDotsVertical = 0xeddf, + GripLines = 0xede0, + GripVertical = 0xede1, + GripLinesVertical = 0xede2, + GroupArrowsRotate = 0xede3, + Gun = 0xede4, + GuitarElectric = 0xede5, + Guitars = 0xede6, + Guitar = 0xede7, + GuaraniSign = 0xede8, + GunSlash = 0xede9, + GunSquirt = 0xedea, + H = 0xedeb, + H1 = 0xedec, + H2 = 0xeded, + H3 = 0xedee, + H4 = 0xedef, + H5 = 0xedf0, + H6 = 0xedf1, + Hammer = 0xedf2, + HammerBrush = 0xedf3, + HammerCrash = 0xedf4, + HammerWar = 0xedf5, + Hamsa = 0xedf6, + Hand = 0xedf7, + HandBackFist = 0xedf8, + HandBackPointDown = 0xedf9, + HandBackPointLeft = 0xedfa, + HandBackPointRibbon = 0xedfb, + HandBackPointRight = 0xedfc, + HandBackPointUp = 0xedfd, + Handcuffs = 0xedfe, + HandDots = 0xedff, + HandFingersCrossed = 0xee00, + HandFist = 0xee01, + HandHeart = 0xee02, + HandHolding = 0xee03, + HandHoldingBox = 0xee04, + HandHoldingCircleDollar = 0xee05, + HandHoldingDollar = 0xee06, + HandHoldingDroplet = 0xee07, + HandHoldingHand = 0xee08, + HandHoldingHeart = 0xee09, + HandHoldingMagic = 0xee0a, + HandHoldingMedical = 0xee0b, + HandHoldingSeedling = 0xee0c, + HandHoldingSkull = 0xee0d, + HandHorns = 0xee0e, + HandLizard = 0xee0f, + HandLove = 0xee10, + HandMiddleFinger = 0xee11, + HandPeace = 0xee12, + HandPointDown = 0xee13, + HandPointer = 0xee14, + HandPointLeft = 0xee15, + HandPointRibbon = 0xee16, + HandPointRight = 0xee17, + HandPointUp = 0xee18, + Hands = 0xee19, + HandsAslInterpreting = 0xee1a, + HandsBound = 0xee1b, + HandsBubbles = 0xee1c, + HandScissors = 0xee1d, + HandsClapping = 0xee1e, + Handshake = 0xee1f, + HandshakeAngle = 0xee20, + HandshakeSimple = 0xee21, + HandshakeSimpleSlash = 0xee22, + HandshakeSlash = 0xee23, + HandsHolding = 0xee24, + HandsHoldingChild = 0xee25, + HandsHoldingCircle = 0xee26, + HandsHoldingDiamond = 0xee27, + HandsHoldingDollar = 0xee28, + HandsHoldingHeart = 0xee29, + HandSparkles = 0xee2a, + HandSpock = 0xee2b, + HandsPraying = 0xee2c, + HandWave = 0xee2d, + Hanukiah = 0xee2e, + HardDrive = 0xee2f, + Hashtag = 0xee30, + HashtagLock = 0xee31, + HatBeach = 0xee32, + HatChef = 0xee33, + HatCowboy = 0xee34, + HatCowboySide = 0xee35, + HatSanta = 0xee36, + HatWinter = 0xee37, + HatWitch = 0xee38, + HatWizard = 0xee39, + Heading = 0xee3a, + Headphones = 0xee3b, + HeadphonesSimple = 0xee3c, + Headset = 0xee3d, + HeadSide = 0xee3e, + HeadSideBrain = 0xee3f, + HeadSideCough = 0xee40, + HeadSideCoughSlash = 0xee41, + HeadSideGear = 0xee42, + HeadSideGoggles = 0xee43, + HeadSideHeadphones = 0xee44, + HeadSideHeart = 0xee45, + HeadSideMask = 0xee46, + HeadSideMedical = 0xee47, + HeadSideVirus = 0xee48, + Heart = 0xee49, + HeartCircleBolt = 0xee4a, + HeartCircleCheck = 0xee4b, + HeartCircleExclamation = 0xee4c, + HeartCircleMinus = 0xee4d, + HeartCirclePlus = 0xee4e, + HeartCircleXmark = 0xee4f, + HeartCrack = 0xee50, + HeartHalf = 0xee51, + HeartHalfStroke = 0xee52, + HeartPulse = 0xee53, + Heat = 0xee54, + Helicopter = 0xee55, + HelicopterSymbol = 0xee56, + HelmetBattle = 0xee57, + HelmetSafety = 0xee58, + HelmetUn = 0xee59, + Hexagon = 0xee5a, + HexagonCheck = 0xee5b, + HexagonDivide = 0xee5c, + HexagonExclamation = 0xee5d, + HexagonImage = 0xee5e, + HexagonMinus = 0xee5f, + HexagonPlus = 0xee60, + HexagonVerticalNft = 0xee61, + HexagonVerticalNftSlanted = 0xee62, + HexagonXmark = 0xee63, + HighDefinition = 0xee64, + Highlighter = 0xee65, + HighlighterLine = 0xee66, + HillAvalanche = 0xee67, + HillRockslide = 0xee68, + Hippo = 0xee69, + HockeyMask = 0xee6a, + HockeyPuck = 0xee6b, + HockeyStickPuck = 0xee6c, + HockeySticks = 0xee6d, + HollyBerry = 0xee6e, + HoneyPot = 0xee6f, + HoodCloak = 0xee70, + HorizontalRule = 0xee71, + Horse = 0xee72, + HorseHead = 0xee73, + HorseSaddle = 0xee74, + Hose = 0xee75, + HoseReel = 0xee76, + Hospital = 0xee77, + Hospitals = 0xee78, + HospitalUser = 0xee79, + P = 0xee7a, + Hotel = 0xee7b, + HotTubPerson = 0xee7c, + Hourglass = 0xee7d, + HourglassClock = 0xee7e, + HourglassEnd = 0xee7f, + HourglassHalf = 0xee80, + HourglassStart = 0xee81, + House = 0xee82, + HouseBlank = 0xee83, + HouseBuilding = 0xee84, + HouseChimney = 0xee85, + HouseChimneyBlank = 0xee86, + HouseChimneyCrack = 0xee87, + HouseChimneyHeart = 0xee88, + HouseChimneyUser = 0xee89, + HouseChimneyWindow = 0xee8a, + HouseChimneyMedical = 0xee8b, + HouseCircleCheck = 0xee8c, + HouseCircleExclamation = 0xee8d, + HouseCircleXmark = 0xee8e, + HouseDay = 0xee8f, + HouseCrack = 0xee90, + HouseFire = 0xee91, + HouseFlag = 0xee92, + HouseFloodWater = 0xee93, + HouseFloodWaterCircleArrowRight = 0xee94, + HouseHeart = 0xee95, + HouseLaptop = 0xee96, + HouseLock = 0xee97, + HouseMedical = 0xee98, + HouseMedicalCircleCheck = 0xee99, + HouseMedicalCircleExclamation = 0xee9a, + HouseMedicalCircleXmark = 0xee9b, + HouseMedicalFlag = 0xee9c, + HouseNight = 0xee9d, + HousePersonLeave = 0xee9e, + HousePersonReturn = 0xee9f, + HouseSignal = 0xeea0, + HouseTree = 0xeea1, + HouseTsunami = 0xeea2, + HouseTurret = 0xeea3, + HouseUser = 0xeea4, + HouseWater = 0xeea5, + HouseWindow = 0xeea6, + HryvniaSign = 0xeea7, + HundredPoints = 0xeea8, + Hurricane = 0xeea9, + Hyphen = 0xeeaa, + I = 0xeeab, + IceCream = 0xeeac, + IceSkate = 0xeead, + Icicles = 0xeeae, + Icons = 0xeeaf, + ICursor = 0xeeb0, + IdBadge = 0xeeb1, + IdCard = 0xeeb2, + IdCardClip = 0xeeb3, + Igloo = 0xeeb4, + Image = 0xeeb5, + ImageLandscape = 0xeeb6, + ImagePolaroid = 0xeeb7, + ImagePolaroidUser = 0xeeb8, + ImagePortrait = 0xeeb9, + Images = 0xeeba, + ImageSlash = 0xeebb, + ImagesUser = 0xeebc, + ImageUser = 0xeebd, + Inboxes = 0xeebe, + InboxFull = 0xeebf, + InboxIn = 0xeec0, + Inbox = 0xeec1, + IndianRupeeSign = 0xeec2, + Industry = 0xeec3, + InboxOut = 0xeec4, + IndustryWindows = 0xeec5, + Infinity = 0xeec6, + Indent = 0xeec7, + Info = 0xeec8, + Inhaler = 0xeec9, + InputNumeric = 0xeeca, + InputPipe = 0xeecb, + InputText = 0xeecc, + Integral = 0xeecd, + Interrobang = 0xeece, + Intersection = 0xeecf, + J = 0xeed0, + JackOLantern = 0xeed1, + Jar = 0xeed2, + Italic = 0xeed3, + IslandTropical = 0xeed4, + JarWheat = 0xeed5, + Jedi = 0xeed6, + JetFighter = 0xeed7, + Joystick = 0xeed8, + Jug = 0xeed9, + JetFighterUp = 0xeeda, + Joint = 0xeedb, + JugDetergent = 0xeedc, + JugBottle = 0xeedd, + K = 0xeede, + Kaaba = 0xeedf, + Kazoo = 0xeee0, + Kerning = 0xeee1, + Key = 0xeee2, + Keyboard = 0xeee3, + KeyboardBrightness = 0xeee4, + KeyboardBrightnessLow = 0xeee5, + KeyboardDown = 0xeee6, + KeyboardLeft = 0xeee7, + Keynote = 0xeee8, + KeySkeleton = 0xeee9, + KeySkeletonLeftRight = 0xeeea, + Khanda = 0xeeeb, + Kidneys = 0xeeec, + KipSign = 0xeeed, + KitchenSet = 0xeeee, + Kite = 0xeeef, + KitMedical = 0xeef0, + KiwiBird = 0xeef1, + KiwiFruit = 0xeef2, + Knife = 0xeef3, + KnifeKitchen = 0xeef4, + L = 0xeef5, + LacrosseStick = 0xeef6, + LacrosseStickBall = 0xeef7, + Lambda = 0xeef8, + Lamp = 0xeef9, + LampDesk = 0xeefa, + LampFloor = 0xeefb, + LampStreet = 0xeefc, + Landmark = 0xeefd, + LandmarkDome = 0xeefe, + LandmarkFlag = 0xeeff, + LandmarkMagnifyingGlass = 0xef00, + LandMineOn = 0xef01, + Language = 0xef02, + Laptop = 0xef03, + LaptopArrowDown = 0xef04, + LaptopBinary = 0xef05, + LaptopCode = 0xef06, + LaptopFile = 0xef07, + LaptopMedical = 0xef08, + LaptopMobile = 0xef09, + LaptopSlash = 0xef0a, + LariSign = 0xef0b, + Lasso = 0xef0c, + LassoSparkles = 0xef0d, + LayerGroup = 0xef0e, + LayerMinus = 0xef0f, + LayerPlus = 0xef10, + Leaf = 0xef11, + LeafHeart = 0xef12, + LeafMaple = 0xef13, + LeafOak = 0xef14, + LeafyGreen = 0xef15, + Left = 0xef16, + LeftFromLine = 0xef17, + LeftLong = 0xef18, + LeftLongToLine = 0xef19, + LeftRight = 0xef1a, + LeftToLine = 0xef1b, + Lemon = 0xef1c, + LessThan = 0xef1d, + LessThanEqual = 0xef1e, + LifeRing = 0xef1f, + Lightbulb = 0xef20, + LightbulbCfl = 0xef21, + LightbulbCflOn = 0xef22, + LightbulbDollar = 0xef23, + LightbulbExclamation = 0xef24, + LightbulbExclamationOn = 0xef25, + LightbulbGear = 0xef26, + LightbulbOn = 0xef27, + LightbulbSlash = 0xef28, + LightCeiling = 0xef29, + LightEmergency = 0xef2a, + LightEmergencyOn = 0xef2b, + Lighthouse = 0xef2c, + LightsHoliday = 0xef2d, + LightSwitch = 0xef2e, + LightSwitchOff = 0xef2f, + LightSwitchOn = 0xef30, + LineColumns = 0xef31, + LineHeight = 0xef32, + LinesLeaning = 0xef33, + Link = 0xef34, + LinkHorizontal = 0xef35, + LinkHorizontalSlash = 0xef36, + LinkSimple = 0xef37, + LinkSimpleSlash = 0xef38, + LinkSlash = 0xef39, + Lips = 0xef3a, + LiraSign = 0xef3b, + List = 0xef3c, + ListCheck = 0xef3d, + ListDropdown = 0xef3e, + ListMusic = 0xef3f, + ListOl = 0xef40, + ListRadio = 0xef41, + ListTimeline = 0xef42, + ListTree = 0xef43, + ListUl = 0xef44, + LitecoinSign = 0xef45, + Loader = 0xef46, + Lobster = 0xef47, + LocationArrow = 0xef48, + LocationArrowUp = 0xef49, + LocationCheck = 0xef4a, + LocationCrosshairs = 0xef4b, + LocationCrosshairsSlash = 0xef4c, + LocationDot = 0xef4d, + LocationDotSlash = 0xef4e, + LocationExclamation = 0xef4f, + LocationMinus = 0xef50, + LocationPen = 0xef51, + LocationPin = 0xef52, + LocationPinLock = 0xef53, + LocationPinSlash = 0xef54, + LocationPlus = 0xef55, + LocationQuestion = 0xef56, + LocationSmile = 0xef57, + LocationXmark = 0xef58, + Lock = 0xef59, + LockA = 0xef5a, + LockHashtag = 0xef5b, + LockKeyhole = 0xef5c, + LockKeyholeOpen = 0xef5d, + LockOpen = 0xef5e, + Locust = 0xef5f, + Lollipop = 0xef60, + Loveseat = 0xef61, + LuchadorMask = 0xef62, + Lungs = 0xef63, + LungsVirus = 0xef64, + M = 0xef65, + Mace = 0xef66, + Magnet = 0xef67, + MagnifyingGlass = 0xef68, + MagnifyingGlassArrowRight = 0xef69, + MagnifyingGlassArrowsRotate = 0xef6a, + MagnifyingGlassChart = 0xef6b, + MagnifyingGlassDollar = 0xef6c, + MagnifyingGlassLocation = 0xef6d, + MagnifyingGlassMinus = 0xef6e, + MagnifyingGlassMusic = 0xef6f, + MagnifyingGlassPlay = 0xef70, + MagnifyingGlassPlus = 0xef71, + MagnifyingGlassWaveform = 0xef72, + Mailbox = 0xef73, + MailboxFlagUp = 0xef74, + ManatSign = 0xef75, + Mandolin = 0xef76, + Mango = 0xef77, + Manhole = 0xef78, + Map = 0xef79, + MapLocation = 0xef7a, + MapLocationDot = 0xef7b, + MapPin = 0xef7c, + Marker = 0xef7d, + Mars = 0xef7e, + MarsAndVenus = 0xef7f, + MarsAndVenusBurst = 0xef80, + MarsDouble = 0xef81, + MarsStroke = 0xef82, + MarsStrokeRight = 0xef83, + MarsStrokeUp = 0xef84, + MartiniGlass = 0xef85, + MartiniGlassCitrus = 0xef86, + MartiniGlassEmpty = 0xef87, + Mask = 0xef88, + MaskFace = 0xef89, + MaskSnorkel = 0xef8a, + MasksTheater = 0xef8b, + MaskVentilator = 0xef8c, + MattressPillow = 0xef8d, + Maximize = 0xef8e, + Meat = 0xef8f, + Medal = 0xef90, + Megaphone = 0xef91, + Melon = 0xef92, + MelonSlice = 0xef93, + Memo = 0xef94, + MemoCircleCheck = 0xef95, + MemoCircleInfo = 0xef96, + MemoPad = 0xef97, + Memory = 0xef98, + Menorah = 0xef99, + Mercury = 0xef9a, + Merge = 0xef9b, + Message = 0xef9c, + MessageArrowDown = 0xef9d, + MessageArrowUp = 0xef9e, + MessageArrowUpRight = 0xef9f, + MessageBot = 0xefa0, + MessageCaptions = 0xefa1, + MessageCheck = 0xefa2, + MessageCode = 0xefa3, + MessageDollar = 0xefa4, + MessageDots = 0xefa5, + MessageExclamation = 0xefa6, + MessageHeart = 0xefa7, + MessageImage = 0xefa8, + MessageLines = 0xefa9, + MessageMedical = 0xefaa, + MessageMiddle = 0xefab, + MessageMiddleTop = 0xefac, + MessageMinus = 0xefad, + MessageMusic = 0xefae, + MessagePen = 0xefaf, + MessagePlus = 0xefb0, + MessageQuestion = 0xefb1, + MessageQuote = 0xefb2, + Messages = 0xefb3, + MessagesDollar = 0xefb4, + MessageSlash = 0xefb5, + MessageSmile = 0xefb6, + MessageSms = 0xefb7, + MessagesQuestion = 0xefb8, + MessageText = 0xefb9, + MessageXmark = 0xefba, + Meteor = 0xefbb, + Meter = 0xefbc, + MeterBolt = 0xefbd, + MeterDroplet = 0xefbe, + MeterFire = 0xefbf, + Microchip = 0xefc0, + MicrochipAi = 0xefc1, + Microphone = 0xefc2, + MicrophoneLines = 0xefc3, + MicrophoneLinesSlash = 0xefc4, + MicrophoneSlash = 0xefc5, + MicrophoneStand = 0xefc6, + Microscope = 0xefc7, + Microwave = 0xefc8, + MillSign = 0xefc9, + Minimize = 0xefca, + Minus = 0xefcb, + Mistletoe = 0xefcc, + Mitten = 0xefcd, + Mobile = 0xefce, + MobileButton = 0xefcf, + MobileNotch = 0xefd0, + MobileRetro = 0xefd1, + MobileScreen = 0xefd2, + MobileScreenButton = 0xefd3, + MobileSignal = 0xefd4, + MobileSignalOut = 0xefd5, + MoneyBill = 0xefd6, + MoneyBill1 = 0xefd7, + MoneyBill1Wave = 0xefd8, + MoneyBills = 0xefd9, + MoneyBillSimple = 0xefda, + MoneyBillSimpleWave = 0xefdb, + MoneyBillsSimple = 0xefdc, + MoneyBillTransfer = 0xefdd, + MoneyBillTrendUp = 0xefde, + MoneyBillWave = 0xefdf, + MoneyBillWheat = 0xefe0, + MoneyCheck = 0xefe1, + MoneyCheckDollar = 0xefe2, + MoneyCheckDollarPen = 0xefe3, + MoneyCheckPen = 0xefe4, + MoneyFromBracket = 0xefe5, + MoneySimpleFromBracket = 0xefe6, + MonitorWaveform = 0xefe7, + Monkey = 0xefe8, + Monument = 0xefe9, + Moon = 0xefea, + MoonCloud = 0xefeb, + MoonOverSun = 0xefec, + MoonStars = 0xefed, + Moped = 0xefee, + MortarPestle = 0xefef, + Mosque = 0xeff0, + Mosquito = 0xeff1, + MosquitoNet = 0xeff2, + Motorcycle = 0xeff3, + Mound = 0xeff4, + Mountain = 0xeff5, + MountainCity = 0xeff6, + Mountains = 0xeff7, + MountainSun = 0xeff8, + MouseField = 0xeff9, + Mp3Player = 0xeffa, + Mug = 0xeffb, + MugHot = 0xeffc, + MugMarshmallows = 0xeffd, + MugSaucer = 0xeffe, + MugTea = 0xefff, + MugTeaSaucer = 0xf000, + Mushroom = 0xf001, + Music = 0xf002, + MusicMagnifyingGlass = 0xf003, + MusicNote = 0xf004, + MusicNoteSlash = 0xf005, + MusicSlash = 0xf006, + Mustache = 0xf007, + N = 0xf008, + NairaSign = 0xf009, + Narwhal = 0xf00a, + NestingDolls = 0xf00b, + NetworkWired = 0xf00c, + Neuter = 0xf00d, + Newspaper = 0xf00e, + Nfc = 0xf00f, + NfcLock = 0xf010, + NfcMagnifyingGlass = 0xf011, + NfcPen = 0xf012, + NfcSignal = 0xf013, + NfcSlash = 0xf014, + NfcSymbol = 0xf015, + NfcTrash = 0xf016, + Nose = 0xf017, + Notdef = 0xf018, + Note = 0xf019, + Notebook = 0xf01a, + NoteMedical = 0xf01b, + NotEqual = 0xf01c, + Notes = 0xf01d, + NotesMedical = 0xf01e, + NoteSticky = 0xf01f, + O = 0xf020, + ObjectExclude = 0xf021, + ObjectGroup = 0xf022, + ObjectIntersect = 0xf023, + ObjectsAlignBottom = 0xf024, + ObjectsAlignCenterHorizontal = 0xf025, + ObjectsAlignCenterVertical = 0xf026, + ObjectsAlignLeft = 0xf027, + ObjectsAlignRight = 0xf028, + ObjectsAlignTop = 0xf029, + ObjectsColumn = 0xf02a, + ObjectSubtract = 0xf02b, + ObjectUngroup = 0xf02c, + ObjectUnion = 0xf02d, + Octagon = 0xf02e, + OctagonCheck = 0xf02f, + OctagonDivide = 0xf030, + OctagonExclamation = 0xf031, + OctagonMinus = 0xf032, + OctagonPlus = 0xf033, + OctagonXmark = 0xf034, + OilCan = 0xf035, + OilCanDrip = 0xf036, + OilTemperature = 0xf037, + OilWell = 0xf038, + Olive = 0xf039, + OliveBranch = 0xf03a, + Om = 0xf03b, + Omega = 0xf03c, + Onion = 0xf03d, + Option = 0xf03e, + Ornament = 0xf03f, + Otter = 0xf040, + Outdent = 0xf041, + Outlet = 0xf042, + Oven = 0xf043, + Overline = 0xf044, + SignalSlash = 0xf045, + PageCaretDown = 0xf046, + Page = 0xf047, + PageCaretUp = 0xf048, + Pager = 0xf049, + Paintbrush = 0xf04a, + PaintbrushFine = 0xf04b, + PaintbrushPencil = 0xf04c, + PaintRoller = 0xf04d, + Pallet = 0xf04e, + Palette = 0xf04f, + PalletBox = 0xf050, + PalletBoxes = 0xf051, + Pancakes = 0xf052, + PanelEws = 0xf053, + PanelFire = 0xf054, + PanFood = 0xf055, + PanFrying = 0xf056, + Panorama = 0xf057, + Paperclip = 0xf058, + PaperclipVertical = 0xf059, + PaperPlane = 0xf05a, + PaperPlaneTop = 0xf05b, + ParachuteBox = 0xf05c, + Paragraph = 0xf05d, + PartyBell = 0xf05e, + ParagraphLeft = 0xf05f, + PartyHorn = 0xf060, + Passport = 0xf061, + Paste = 0xf062, + Pause = 0xf063, + Paw = 0xf064, + PawClaws = 0xf065, + PawSimple = 0xf066, + Peace = 0xf067, + Peach = 0xf068, + Peanut = 0xf069, + Peanuts = 0xf06a, + Peapod = 0xf06b, + Pear = 0xf06c, + Pedestal = 0xf06d, + Pegasus = 0xf06e, + Pen = 0xf06f, + Pencil = 0xf070, + PencilMechanical = 0xf071, + PencilSlash = 0xf072, + PenCircle = 0xf073, + PenClip = 0xf074, + PenClipSlash = 0xf075, + PenFancy = 0xf076, + PenFancySlash = 0xf077, + PenField = 0xf078, + PenNib = 0xf079, + PenLine = 0xf07a, + PenNibSlash = 0xf07b, + PenPaintbrush = 0xf07c, + PenRuler = 0xf07d, + PenSlash = 0xf07e, + PenSwirl = 0xf07f, + PenToSquare = 0xf080, + People = 0xf081, + PeopleArrows = 0xf082, + PeopleCarryBox = 0xf083, + PeopleDress = 0xf084, + PeopleDressSimple = 0xf085, + PeopleGroup = 0xf086, + PeopleLine = 0xf087, + PeoplePants = 0xf088, + PeoplePantsSimple = 0xf089, + PeoplePulling = 0xf08a, + PeopleRobbery = 0xf08b, + PeopleRoof = 0xf08c, + PeopleSimple = 0xf08d, + Pepper = 0xf08e, + PepperHot = 0xf08f, + Person = 0xf090, + Percent = 0xf091, + Period = 0xf092, + PersonArrowDownToLine = 0xf093, + PersonBiking = 0xf094, + PersonArrowUpFromLine = 0xf095, + PersonBikingMountain = 0xf096, + PersonBooth = 0xf097, + PersonBreastfeeding = 0xf098, + PersonBurst = 0xf099, + PersonCane = 0xf09a, + PersonCarryBox = 0xf09b, + PersonChalkboard = 0xf09c, + PersonCircleCheck = 0xf09d, + PersonCircleExclamation = 0xf09e, + PersonCircleMinus = 0xf09f, + PersonCirclePlus = 0xf0a0, + PersonCircleQuestion = 0xf0a1, + PersonCircleXmark = 0xf0a2, + PersonDigging = 0xf0a3, + PersonDollyEmpty = 0xf0a4, + PersonDotsFromLine = 0xf0a5, + PersonDressBurst = 0xf0a6, + PersonDress = 0xf0a7, + PersonDolly = 0xf0a8, + PersonDressFairy = 0xf0a9, + PersonDressSimple = 0xf0aa, + PersonDrowning = 0xf0ab, + PersonFairy = 0xf0ac, + PersonFalling = 0xf0ad, + PersonFallingBurst = 0xf0ae, + PersonFromPortal = 0xf0af, + PersonHalfDress = 0xf0b0, + PersonHarassing = 0xf0b1, + PersonHiking = 0xf0b2, + PersonMilitaryPointing = 0xf0b3, + PersonMilitaryRifle = 0xf0b4, + PersonMilitaryToPerson = 0xf0b5, + PersonPinball = 0xf0b6, + PersonPraying = 0xf0b7, + PersonPregnant = 0xf0b8, + PersonRays = 0xf0b9, + PersonRifle = 0xf0ba, + PersonRunning = 0xf0bb, + PersonRunningFast = 0xf0bc, + PersonSeat = 0xf0bd, + PersonSeatReclined = 0xf0be, + PersonShelter = 0xf0bf, + PersonSign = 0xf0c0, + PersonSimple = 0xf0c1, + PersonSkating = 0xf0c2, + PersonSkiing = 0xf0c3, + PersonSkiingNordic = 0xf0c4, + PersonSkiJumping = 0xf0c5, + PersonSkiLift = 0xf0c6, + PersonSledding = 0xf0c7, + PersonSnowboarding = 0xf0c8, + PersonSnowmobiling = 0xf0c9, + PersonSwimming = 0xf0ca, + PersonThroughWindow = 0xf0cb, + PersonToDoor = 0xf0cc, + PersonToPortal = 0xf0cd, + PersonWalking = 0xf0ce, + PersonWalkingArrowLoopLeft = 0xf0cf, + PersonWalkingArrowRight = 0xf0d0, + PersonWalkingDashedLineArrowRight = 0xf0d1, + PersonWalkingLuggage = 0xf0d2, + PersonWalkingWithCane = 0xf0d3, + PesetaSign = 0xf0d4, + PesoSign = 0xf0d5, + Phone = 0xf0d6, + PhoneArrowDownLeft = 0xf0d7, + PhoneArrowRight = 0xf0d8, + PhoneArrowUpRight = 0xf0d9, + PhoneFlip = 0xf0da, + PhoneHangup = 0xf0db, + PhoneIntercom = 0xf0dc, + PhoneMissed = 0xf0dd, + PhoneOffice = 0xf0de, + PhonePlus = 0xf0df, + PhoneRotary = 0xf0e0, + PhoneSlash = 0xf0e1, + PhoneVolume = 0xf0e2, + PhoneXmark = 0xf0e3, + PhotoFilm = 0xf0e4, + PhotoFilmMusic = 0xf0e5, + Pi = 0xf0e6, + Piano = 0xf0e7, + PiggyBank = 0xf0e8, + Pig = 0xf0e9, + PianoKeyboard = 0xf0ea, + Pickaxe = 0xf0eb, + Pinata = 0xf0ec, + Pinball = 0xf0ed, + Pie = 0xf0ee, + Pills = 0xf0ef, + Pickleball = 0xf0f0, + Pineapple = 0xf0f1, + Pipe = 0xf0f2, + PipeCircleCheck = 0xf0f3, + PipeCollar = 0xf0f4, + PipeSection = 0xf0f5, + PipeSmoking = 0xf0f6, + PipeValve = 0xf0f7, + Pizza = 0xf0f8, + PizzaSlice = 0xf0f9, + PlaceOfWorship = 0xf0fa, + Plane = 0xf0fb, + PlaneArrival = 0xf0fc, + PlaneCircleCheck = 0xf0fd, + PlaneCircleExclamation = 0xf0fe, + PlaneCircleXmark = 0xf0ff, + PlaneDeparture = 0xf100, + PlaneEngines = 0xf101, + PlaneLock = 0xf102, + PlaneProp = 0xf103, + PlaneSlash = 0xf104, + PlaneTail = 0xf105, + PlanetMoon = 0xf106, + PlanetRinged = 0xf107, + PlaneUp = 0xf108, + PlaneUpSlash = 0xf109, + PlantWilt = 0xf10a, + PlateUtensils = 0xf10b, + PlateWheat = 0xf10c, + Play = 0xf10d, + PlayPause = 0xf10e, + Plug = 0xf10f, + PlugCircleBolt = 0xf110, + PlugCircleCheck = 0xf111, + PlugCircleExclamation = 0xf112, + PlugCircleMinus = 0xf113, + PlugCirclePlus = 0xf114, + PlugCircleXmark = 0xf115, + Plus = 0xf116, + PlusLarge = 0xf117, + PlusMinus = 0xf118, + Podcast = 0xf119, + Podium = 0xf11a, + PodiumStar = 0xf11b, + PoliceBox = 0xf11c, + PollPeople = 0xf11d, + Pompebled = 0xf11e, + Poo = 0xf11f, + Pool8Ball = 0xf120, + Poop = 0xf121, + PooStorm = 0xf122, + Popcorn = 0xf123, + Popsicle = 0xf124, + Potato = 0xf125, + PotFood = 0xf126, + PowerOff = 0xf127, + Prescription = 0xf128, + PrescriptionBottle = 0xf129, + PrescriptionBottleMedical = 0xf12a, + PrescriptionBottlePill = 0xf12b, + PresentationScreen = 0xf12c, + Pretzel = 0xf12d, + Print = 0xf12e, + PrintMagnifyingGlass = 0xf12f, + PrintSlash = 0xf130, + Projector = 0xf131, + Pump = 0xf132, + Pumpkin = 0xf133, + PumpMedical = 0xf134, + PumpSoap = 0xf135, + Puzzle = 0xf136, + PuzzlePiece = 0xf137, + PuzzlePieceSimple = 0xf138, + Q = 0xf139, + Qrcode = 0xf13a, + Question = 0xf13b, + QuoteLeft = 0xf13c, + QuoteRight = 0xf13d, + Quotes = 0xf13e, + R = 0xf13f, + Rabbit = 0xf140, + RabbitRunning = 0xf141, + Raccoon = 0xf142, + Racquet = 0xf143, + Radar = 0xf144, + Radiation = 0xf145, + Radio = 0xf146, + RadioTuner = 0xf147, + Rainbow = 0xf148, + Raindrops = 0xf149, + Ram = 0xf14a, + RampLoading = 0xf14b, + RankingStar = 0xf14c, + Raygun = 0xf14d, + Receipt = 0xf14e, + RecordVinyl = 0xf14f, + Rectangle = 0xf150, + RectangleAd = 0xf151, + RectangleBarcode = 0xf152, + RectangleCode = 0xf153, + RectangleHistory = 0xf154, + RectangleHistoryCirclePlus = 0xf155, + RectangleHistoryCircleUser = 0xf156, + RectangleList = 0xf157, + RectanglePro = 0xf158, + RectanglesMixed = 0xf159, + RectangleTerminal = 0xf15a, + RectangleVertical = 0xf15b, + RectangleVerticalHistory = 0xf15c, + RectangleWide = 0xf15d, + RectangleXmark = 0xf15e, + Recycle = 0xf15f, + Reel = 0xf160, + ReflectHorizontal = 0xf161, + ReflectVertical = 0xf162, + Refrigerator = 0xf163, + Registered = 0xf164, + Repeat = 0xf165, + Repeat1 = 0xf166, + Reply = 0xf167, + ReplyAll = 0xf168, + ReplyClock = 0xf169, + Republican = 0xf16a, + Restroom = 0xf16b, + RestroomSimple = 0xf16c, + Retweet = 0xf16d, + Rhombus = 0xf16e, + Ribbon = 0xf16f, + Right = 0xf170, + RightFromBracket = 0xf171, + RightFromLine = 0xf172, + RightLeft = 0xf173, + RightLeftLarge = 0xf174, + RightLong = 0xf175, + RightLongToLine = 0xf176, + RightToBracket = 0xf177, + RightToLine = 0xf178, + Ring = 0xf179, + RingDiamond = 0xf17a, + RingsWedding = 0xf17b, + Road = 0xf17c, + RoadBarrier = 0xf17d, + RoadBridge = 0xf17e, + RoadCircleCheck = 0xf17f, + RoadCircleExclamation = 0xf180, + RoadCircleXmark = 0xf181, + RoadLock = 0xf182, + RoadSpikes = 0xf183, + Robot = 0xf184, + RobotAstromech = 0xf185, + Rocket = 0xf186, + RocketLaunch = 0xf187, + RollerCoaster = 0xf188, + Rotate = 0xf189, + RotateExclamation = 0xf18a, + RotateLeft = 0xf18b, + RotateReverse = 0xf18c, + RotateRight = 0xf18d, + Route = 0xf18e, + RouteHighway = 0xf18f, + RouteInterstate = 0xf190, + Router = 0xf191, + Rss = 0xf192, + RubleSign = 0xf193, + Rug = 0xf194, + RugbyBall = 0xf195, + Ruler = 0xf196, + RulerCombined = 0xf197, + RulerHorizontal = 0xf198, + RulerTriangle = 0xf199, + RulerVertical = 0xf19a, + RupeeSign = 0xf19b, + RupiahSign = 0xf19c, + Rv = 0xf19d, + S = 0xf19e, + Sack = 0xf19f, + SackDollar = 0xf1a0, + SackXmark = 0xf1a1, + Sailboat = 0xf1a2, + Salad = 0xf1a3, + SaltShaker = 0xf1a4, + Sandwich = 0xf1a5, + Satellite = 0xf1a6, + SatelliteDish = 0xf1a7, + Sausage = 0xf1a8, + Saxophone = 0xf1a9, + SaxophoneFire = 0xf1aa, + ScaleBalanced = 0xf1ab, + ScaleUnbalanced = 0xf1ac, + ScaleUnbalancedFlip = 0xf1ad, + Scalpel = 0xf1ae, + ScalpelLineDashed = 0xf1af, + ScannerGun = 0xf1b0, + ScannerImage = 0xf1b1, + ScannerKeyboard = 0xf1b2, + ScannerTouchscreen = 0xf1b3, + Scarecrow = 0xf1b4, + Scarf = 0xf1b5, + School = 0xf1b6, + SchoolCircleCheck = 0xf1b7, + SchoolCircleExclamation = 0xf1b8, + SchoolCircleXmark = 0xf1b9, + SchoolFlag = 0xf1ba, + SchoolLock = 0xf1bb, + Scissors = 0xf1bc, + Screencast = 0xf1bd, + ScreenUsers = 0xf1be, + Screwdriver = 0xf1bf, + ScrewdriverWrench = 0xf1c0, + Scribble = 0xf1c1, + Scroll = 0xf1c2, + ScrollOld = 0xf1c3, + ScrollTorah = 0xf1c4, + Scrubber = 0xf1c5, + Scythe = 0xf1c6, + SdCard = 0xf1c7, + SdCards = 0xf1c8, + Seal = 0xf1c9, + SealExclamation = 0xf1ca, + SealQuestion = 0xf1cb, + SeatAirline = 0xf1cc, + Section = 0xf1cd, + Seedling = 0xf1ce, + Semicolon = 0xf1cf, + SendBack = 0xf1d0, + SendBackward = 0xf1d1, + Sensor = 0xf1d2, + SensorCloud = 0xf1d3, + SensorFire = 0xf1d4, + SensorOn = 0xf1d5, + SensorTriangleExclamation = 0xf1d6, + Server = 0xf1d7, + Shapes = 0xf1d8, + Share = 0xf1d9, + ShareAll = 0xf1da, + ShareFromSquare = 0xf1db, + ShareNodes = 0xf1dc, + Sheep = 0xf1dd, + SheetPlastic = 0xf1de, + ShekelSign = 0xf1df, + Shelves = 0xf1e0, + ShelvesEmpty = 0xf1e1, + Shield = 0xf1e2, + ShieldCat = 0xf1e3, + ShieldCheck = 0xf1e4, + ShieldCross = 0xf1e5, + ShieldDog = 0xf1e6, + ShieldExclamation = 0xf1e7, + ShieldHalved = 0xf1e8, + ShieldHeart = 0xf1e9, + ShieldKeyhole = 0xf1ea, + ShieldMinus = 0xf1eb, + ShieldPlus = 0xf1ec, + ShieldQuartered = 0xf1ed, + ShieldSlash = 0xf1ee, + ShieldVirus = 0xf1ef, + ShieldXmark = 0xf1f0, + Ship = 0xf1f1, + Shirt = 0xf1f2, + ShirtLongSleeve = 0xf1f3, + ShirtRunning = 0xf1f4, + ShirtTankTop = 0xf1f5, + ShishKebab = 0xf1f6, + ShoePrints = 0xf1f7, + Shop = 0xf1f8, + ShopLock = 0xf1f9, + ShopSlash = 0xf1fa, + Shovel = 0xf1fb, + ShovelSnow = 0xf1fc, + Shower = 0xf1fd, + ShowerDown = 0xf1fe, + Shredder = 0xf1ff, + Shrimp = 0xf200, + Shuffle = 0xf201, + Shutters = 0xf202, + Shuttlecock = 0xf203, + ShuttleSpace = 0xf204, + Sickle = 0xf205, + Sidebar = 0xf206, + SidebarFlip = 0xf207, + Sigma = 0xf208, + Signal = 0xf209, + SignalBars = 0xf20a, + SignalBarsFair = 0xf20b, + SignalBarsGood = 0xf20c, + SignalBarsSlash = 0xf20d, + SignalBarsWeak = 0xf20e, + SignalFair = 0xf20f, + SignalGood = 0xf210, + UserHairBuns = 0xf211, + SignalStream = 0xf212, + SignalStreamSlash = 0xf213, + SignalStrong = 0xf214, + SignalWeak = 0xf215, + Signature = 0xf216, + SignatureLock = 0xf217, + SignatureSlash = 0xf218, + SignHanging = 0xf219, + SignPost = 0xf21a, + SignPosts = 0xf21b, + SignPostsWrench = 0xf21c, + SignsPost = 0xf21d, + SimCard = 0xf21e, + SimCards = 0xf21f, + Sink = 0xf220, + Siren = 0xf221, + SirenOn = 0xf222, + Sitemap = 0xf223, + Skeleton = 0xf224, + SkeletonRibs = 0xf225, + SkiBoot = 0xf226, + SkiBootSki = 0xf227, + Skull = 0xf228, + SkullCow = 0xf229, + SkullCrossbones = 0xf22a, + Slash = 0xf22b, + SlashBack = 0xf22c, + SlashForward = 0xf22d, + Sleigh = 0xf22e, + Slider = 0xf22f, + Sliders = 0xf230, + SlidersSimple = 0xf231, + SlidersUp = 0xf232, + SlotMachine = 0xf233, + Smog = 0xf234, + Smoke = 0xf235, + Smoking = 0xf236, + Snake = 0xf237, + Snooze = 0xf238, + SnowBlowing = 0xf239, + Snowflake = 0xf23a, + SnowflakeDroplets = 0xf23b, + Snowflakes = 0xf23c, + Snowman = 0xf23d, + SnowmanHead = 0xf23e, + Snowplow = 0xf23f, + Soap = 0xf240, + Socks = 0xf241, + SoftServe = 0xf242, + SolarPanel = 0xf243, + SolarSystem = 0xf244, + Sort = 0xf245, + SortDown = 0xf246, + SortUp = 0xf247, + Spa = 0xf248, + SpaceStationMoon = 0xf249, + SpaceStationMoonConstruction = 0xf24a, + Spade = 0xf24b, + SpaghettiMonsterFlying = 0xf24c, + Sparkles = 0xf24d, + Speaker = 0xf24e, + Speakers = 0xf24f, + SpiderBlackWidow = 0xf250, + SpiderWeb = 0xf251, + Sparkle = 0xf252, + Spinner = 0xf253, + Spider = 0xf254, + SpinnerScale = 0xf255, + SpinnerThird = 0xf256, + Splotch = 0xf257, + Split = 0xf258, + SprayCan = 0xf259, + SprayCanSparkles = 0xf25a, + Sportsball = 0xf25b, + Sprinkler = 0xf25c, + SprinklerCeiling = 0xf25d, + Square = 0xf25e, + Square3 = 0xf25f, + Square0 = 0xf260, + Square1 = 0xf261, + SpellCheck = 0xf262, + Square4 = 0xf263, + Square5 = 0xf264, + Spoon = 0xf265, + Square2 = 0xf266, + Square7 = 0xf267, + Square8 = 0xf268, + Square6 = 0xf269, + Square9 = 0xf26a, + SquareALock = 0xf26b, + SquareA = 0xf26c, + SquareArrowDown = 0xf26d, + SquareAmpersand = 0xf26e, + SquareArrowDownRight = 0xf26f, + SquareArrowDownLeft = 0xf270, + SquareArrowLeft = 0xf271, + SquareArrowRight = 0xf272, + SquareArrowUp = 0xf273, + SquareArrowUpLeft = 0xf274, + SquareArrowUpRight = 0xf275, + SquareB = 0xf276, + SquareBolt = 0xf277, + SquareC = 0xf278, + SquareCaretDown = 0xf279, + SquareCaretLeft = 0xf27a, + SquareCaretRight = 0xf27b, + SquareCaretUp = 0xf27c, + SquareCheck = 0xf27d, + SquareChevronDown = 0xf27e, + SquareChevronLeft = 0xf27f, + SquareChevronRight = 0xf280, + SquareChevronUp = 0xf281, + SquareCode = 0xf282, + SquareD = 0xf283, + SquareDashed = 0xf284, + SquareDashedCirclePlus = 0xf285, + SquareDivide = 0xf286, + SquareDollar = 0xf287, + SquareDown = 0xf288, + SquareDownLeft = 0xf289, + SquareDownRight = 0xf28a, + SquareE = 0xf28b, + SquareEllipsis = 0xf28c, + SquareEllipsisVertical = 0xf28d, + SquareEnvelope = 0xf28e, + SquareExclamation = 0xf28f, + SquareF = 0xf290, + SquareFragile = 0xf291, + SquareFull = 0xf292, + SquareG = 0xf293, + SquareH = 0xf294, + SquareHeart = 0xf295, + SquareI = 0xf296, + SquareInfo = 0xf297, + SquareJ = 0xf298, + SquareK = 0xf299, + SquareKanban = 0xf29a, + SquareL = 0xf29b, + SquareLeft = 0xf29c, + SquareList = 0xf29d, + SquareM = 0xf29e, + SquareMinus = 0xf29f, + SquareN = 0xf2a0, + SquareNfi = 0xf2a1, + SquareO = 0xf2a2, + SquareP = 0xf2a3, + SquareParking = 0xf2a4, + SquareParkingSlash = 0xf2a5, + SquarePen = 0xf2a6, + SquarePersonConfined = 0xf2a7, + SquarePhone = 0xf2a8, + SquarePhoneFlip = 0xf2a9, + SquarePhoneHangup = 0xf2aa, + SquarePlus = 0xf2ab, + SquarePollHorizontal = 0xf2ac, + SquarePollVertical = 0xf2ad, + SquareQ = 0xf2ae, + SquareQuarters = 0xf2af, + SquareQuestion = 0xf2b0, + SquareQuote = 0xf2b1, + SquareR = 0xf2b2, + SquareRight = 0xf2b3, + SquareRing = 0xf2b4, + SquareRoot = 0xf2b5, + SquareRootVariable = 0xf2b6, + SquareRss = 0xf2b7, + SquareS = 0xf2b8, + SquareShareNodes = 0xf2b9, + SquareSliders = 0xf2ba, + SquareSlidersVertical = 0xf2bb, + SquareSmall = 0xf2bc, + SquareStar = 0xf2bd, + SquareT = 0xf2be, + SquareTerminal = 0xf2bf, + SquareThisWayUp = 0xf2c0, + SquareU = 0xf2c1, + SquareUp = 0xf2c2, + SquareUpLeft = 0xf2c3, + SquareUpRight = 0xf2c4, + SquareUser = 0xf2c5, + SquareV = 0xf2c6, + SquareVirus = 0xf2c7, + SquareW = 0xf2c8, + SquareX = 0xf2c9, + SquareXmark = 0xf2ca, + SquareY = 0xf2cb, + SquareZ = 0xf2cc, + Squid = 0xf2cd, + Squirrel = 0xf2ce, + Staff = 0xf2cf, + StaffSnake = 0xf2d0, + Stairs = 0xf2d1, + Stamp = 0xf2d2, + StandardDefinition = 0xf2d3, + Stapler = 0xf2d4, + Star = 0xf2d5, + StarAndCrescent = 0xf2d6, + StarChristmas = 0xf2d7, + StarExclamation = 0xf2d8, + Starfighter = 0xf2d9, + StarfighterTwinIonEngine = 0xf2da, + StarfighterTwinIonEngineAdvanced = 0xf2db, + StarHalf = 0xf2dc, + StarHalfStroke = 0xf2dd, + StarOfDavid = 0xf2de, + StarOfLife = 0xf2df, + Stars = 0xf2e0, + StarSharp = 0xf2e1, + StarSharpHalf = 0xf2e2, + StarSharpHalfStroke = 0xf2e3, + Starship = 0xf2e4, + StarshipFreighter = 0xf2e5, + StarShooting = 0xf2e6, + Steak = 0xf2e7, + SteeringWheel = 0xf2e8, + SterlingSign = 0xf2e9, + Stethoscope = 0xf2ea, + Stocking = 0xf2eb, + Stomach = 0xf2ec, + Stop = 0xf2ed, + Stopwatch = 0xf2ee, + Stopwatch20 = 0xf2ef, + Store = 0xf2f0, + StoreLock = 0xf2f1, + StoreSlash = 0xf2f2, + Strawberry = 0xf2f3, + StreetView = 0xf2f4, + Stretcher = 0xf2f5, + Strikethrough = 0xf2f6, + Stroopwafel = 0xf2f7, + Subscript = 0xf2f8, + Subtitles = 0xf2f9, + SubtitlesSlash = 0xf2fa, + Suitcase = 0xf2fb, + SuitcaseMedical = 0xf2fc, + SuitcaseRolling = 0xf2fd, + Sun = 0xf2fe, + SunBright = 0xf2ff, + SunCloud = 0xf300, + SunDust = 0xf301, + Sunglasses = 0xf302, + SunHaze = 0xf303, + SunPlantWilt = 0xf304, + Sunrise = 0xf305, + Sunset = 0xf306, + Superscript = 0xf307, + Sushi = 0xf308, + SushiRoll = 0xf309, + Swap = 0xf30a, + SwapArrows = 0xf30b, + Swatchbook = 0xf30c, + Sword = 0xf30d, + SwordLaser = 0xf30e, + SwordLaserAlt = 0xf30f, + Swords = 0xf310, + SwordsLaser = 0xf311, + Symbols = 0xf312, + Synagogue = 0xf313, + Syringe = 0xf314, + T = 0xf315, + Table = 0xf316, + TableCells = 0xf317, + TableCellsLarge = 0xf318, + TableColumns = 0xf319, + TableLayout = 0xf31a, + TableList = 0xf31b, + TablePicnic = 0xf31c, + TablePivot = 0xf31d, + TableRows = 0xf31e, + Tablet = 0xf31f, + TabletButton = 0xf320, + TableTennisPaddleBall = 0xf321, + TableTree = 0xf322, + TabletRugged = 0xf323, + Tablets = 0xf324, + TabletScreen = 0xf325, + TabletScreenButton = 0xf326, + TachographDigital = 0xf327, + Taco = 0xf328, + Tag = 0xf329, + Tags = 0xf32a, + Tally = 0xf32b, + Tally1 = 0xf32c, + Tally2 = 0xf32d, + Tally3 = 0xf32e, + Tally4 = 0xf32f, + Tamale = 0xf330, + TankWater = 0xf331, + Tape = 0xf332, + Tarp = 0xf333, + TarpDroplet = 0xf334, + Taxi = 0xf335, + TaxiBus = 0xf336, + TeddyBear = 0xf337, + Teeth = 0xf338, + TeethOpen = 0xf339, + Telescope = 0xf33a, + TemperatureArrowDown = 0xf33b, + TemperatureArrowUp = 0xf33c, + TemperatureEmpty = 0xf33d, + TemperatureFull = 0xf33e, + TemperatureHalf = 0xf33f, + TemperatureHigh = 0xf340, + TemperatureList = 0xf341, + TemperatureLow = 0xf342, + TemperatureQuarter = 0xf343, + TemperatureSnow = 0xf344, + TemperatureSun = 0xf345, + TemperatureThreeQuarters = 0xf346, + TengeSign = 0xf347, + TennisBall = 0xf348, + Tent = 0xf349, + TentArrowDownToLine = 0xf34a, + TentArrowLeftRight = 0xf34b, + TentArrowsDown = 0xf34c, + TentArrowTurnLeft = 0xf34d, + TentDoublePeak = 0xf34e, + Tents = 0xf34f, + Terminal = 0xf350, + Text = 0xf351, + TextHeight = 0xf352, + TextSize = 0xf353, + TextSlash = 0xf354, + TextWidth = 0xf355, + Thermometer = 0xf356, + Theta = 0xf357, + ThoughtBubble = 0xf358, + ThumbsDown = 0xf359, + ThumbsUp = 0xf35a, + Thumbtack = 0xf35b, + Tick = 0xf35c, + Ticket = 0xf35d, + TicketAirline = 0xf35e, + TicketPerforated = 0xf35f, + Tickets = 0xf360, + TicketsAirline = 0xf361, + TicketSimple = 0xf362, + TicketsPerforated = 0xf363, + TicketsSimple = 0xf364, + Tilde = 0xf365, + Timeline = 0xf366, + TimelineArrow = 0xf367, + Timer = 0xf368, + Tire = 0xf369, + TireFlat = 0xf36a, + TirePressureWarning = 0xf36b, + TireRugged = 0xf36c, + ToggleLargeOff = 0xf36d, + ToggleLargeOn = 0xf36e, + ToggleOff = 0xf36f, + ToggleOn = 0xf370, + Toilet = 0xf371, + ToiletPaper = 0xf372, + ToiletPaperBlank = 0xf373, + ToiletPaperBlankUnder = 0xf374, + ToiletPaperCheck = 0xf375, + ToiletPaperSlash = 0xf376, + ToiletPaperUnder = 0xf377, + ToiletPaperUnderSlash = 0xf378, + ToiletPaperXmark = 0xf379, + ToiletPortable = 0xf37a, + ToiletsPortable = 0xf37b, + Tomato = 0xf37c, + Tombstone = 0xf37d, + TombstoneBlank = 0xf37e, + Toolbox = 0xf37f, + Tooth = 0xf380, + Toothbrush = 0xf381, + ToriiGate = 0xf382, + Tornado = 0xf383, + TowerBroadcast = 0xf384, + TowerCell = 0xf385, + TowerControl = 0xf386, + TowerObservation = 0xf387, + Tractor = 0xf388, + Trademark = 0xf389, + TrafficCone = 0xf38a, + TrafficLight = 0xf38b, + TrafficLightGo = 0xf38c, + TrafficLightSlow = 0xf38d, + TrafficLightStop = 0xf38e, + Trailer = 0xf38f, + Train = 0xf390, + TrainSubway = 0xf391, + TrainSubwayTunnel = 0xf392, + TrainTrack = 0xf393, + TrainTram = 0xf394, + TrainTunnel = 0xf395, + TransformerBolt = 0xf396, + Transgender = 0xf397, + Transporter = 0xf398, + Transporter1 = 0xf399, + Transporter2 = 0xf39a, + Transporter3 = 0xf39b, + Transporter4 = 0xf39c, + Transporter5 = 0xf39d, + Transporter6 = 0xf39e, + Transporter7 = 0xf39f, + TransporterEmpty = 0xf3a0, + Trash = 0xf3a1, + TrashArrowUp = 0xf3a2, + TrashCan = 0xf3a3, + TrashCanArrowUp = 0xf3a4, + TrashCanCheck = 0xf3a5, + TrashCanClock = 0xf3a6, + TrashCanList = 0xf3a7, + TrashCanPlus = 0xf3a8, + TrashCanSlash = 0xf3a9, + TrashCanUndo = 0xf3aa, + TrashCanXmark = 0xf3ab, + TrashCheck = 0xf3ac, + TrashClock = 0xf3ad, + TrashList = 0xf3ae, + TrashPlus = 0xf3af, + TrashSlash = 0xf3b0, + TrashUndo = 0xf3b1, + TrashXmark = 0xf3b2, + TreasureChest = 0xf3b3, + Tree = 0xf3b4, + TreeChristmas = 0xf3b5, + TreeCity = 0xf3b6, + TreeDeciduous = 0xf3b7, + TreeDecorated = 0xf3b8, + TreeLarge = 0xf3b9, + TreePalm = 0xf3ba, + Trees = 0xf3bb, + TRex = 0xf3bc, + Triangle = 0xf3bd, + TriangleExclamation = 0xf3be, + TriangleInstrument = 0xf3bf, + TrianglePersonDigging = 0xf3c0, + Tricycle = 0xf3c1, + TricycleAdult = 0xf3c2, + Trillium = 0xf3c3, + Trophy = 0xf3c4, + TrophyStar = 0xf3c5, + Trowel = 0xf3c6, + TrowelBricks = 0xf3c7, + Truck = 0xf3c8, + TruckArrowRight = 0xf3c9, + TruckBolt = 0xf3ca, + TruckClock = 0xf3cb, + TruckContainer = 0xf3cc, + TruckContainerEmpty = 0xf3cd, + TruckDroplet = 0xf3ce, + TruckFast = 0xf3cf, + TruckField = 0xf3d0, + TruckFieldUn = 0xf3d1, + TruckFire = 0xf3d2, + TruckFlatbed = 0xf3d3, + TruckFront = 0xf3d4, + TruckLadder = 0xf3d5, + TruckMedical = 0xf3d6, + TruckMonster = 0xf3d7, + TruckMoving = 0xf3d8, + TruckPickup = 0xf3d9, + TruckPlane = 0xf3da, + TruckPlow = 0xf3db, + TruckRamp = 0xf3dc, + TruckRampBox = 0xf3dd, + TruckRampCouch = 0xf3de, + TruckTow = 0xf3df, + TruckUtensils = 0xf3e0, + Trumpet = 0xf3e1, + Tty = 0xf3e2, + TtyAnswer = 0xf3e3, + TugrikSign = 0xf3e4, + Turkey = 0xf3e5, + TurkishLiraSign = 0xf3e6, + TurnDown = 0xf3e7, + TurnDownLeft = 0xf3e8, + TurnDownRight = 0xf3e9, + TurnLeft = 0xf3ea, + TurnLeftDown = 0xf3eb, + TurnLeftUp = 0xf3ec, + TurnRight = 0xf3ed, + Turntable = 0xf3ee, + TurnUp = 0xf3ef, + Turtle = 0xf3f0, + Tv = 0xf3f1, + TvMusic = 0xf3f2, + TvRetro = 0xf3f3, + Typewriter = 0xf3f4, + U = 0xf3f5, + Ufo = 0xf3f6, + UfoBeam = 0xf3f7, + Umbrella = 0xf3f8, + UmbrellaBeach = 0xf3f9, + UmbrellaSimple = 0xf3fa, + Underline = 0xf3fb, + Unicorn = 0xf3fc, + UniformMartialArts = 0xf3fd, + Union = 0xf3fe, + UniversalAccess = 0xf3ff, + Unlock = 0xf400, + UnlockKeyhole = 0xf401, + Up = 0xf402, + UpDown = 0xf403, + UpDownLeftRight = 0xf404, + UpFromBracket = 0xf405, + UpFromDottedLine = 0xf406, + UpFromLine = 0xf407, + UpLeft = 0xf408, + Upload = 0xf409, + UpLong = 0xf40a, + UpRight = 0xf40b, + UpRightAndDownLeftFromCenter = 0xf40c, + UpRightFromSquare = 0xf40d, + UpToDottedLine = 0xf40e, + UpToLine = 0xf40f, + UsbDrive = 0xf410, + User = 0xf411, + UserAlien = 0xf412, + UserAstronaut = 0xf413, + UserBountyHunter = 0xf414, + UserCheck = 0xf415, + UserChef = 0xf416, + UserClock = 0xf417, + UserCowboy = 0xf418, + UserCrown = 0xf419, + UserDoctor = 0xf41a, + UserDoctorHair = 0xf41b, + UserDoctorHairLong = 0xf41c, + UserDoctorMessage = 0xf41d, + UserGear = 0xf41e, + UserGraduate = 0xf41f, + UserGroup = 0xf420, + UserGroupCrown = 0xf421, + UserGroupSimple = 0xf422, + UserHair = 0xf423, + XmarksLines = 0xf424, + XmarkToSlot = 0xf425, + XRay = 0xf426, + Y = 0xf427, + YenSign = 0xf428, + YinYang = 0xf429, + Z = 0xf42a, + UserHairLong = 0xf42b, + UserHairMullet = 0xf42c, + UserHeadset = 0xf42d, + UserHelmetSafety = 0xf42e, + UserInjured = 0xf42f, + UserLarge = 0xf430, + UserLargeSlash = 0xf431, + UserLock = 0xf432, + UserMagnifyingGlass = 0xf433, + UserMinus = 0xf434, + UserMusic = 0xf435, + UserNinja = 0xf436, + UserNurse = 0xf437, + UserNurseHair = 0xf438, + UserNurseHairLong = 0xf439, + UserPen = 0xf43a, + UserPilot = 0xf43b, + UserPilotTie = 0xf43c, + UserPlus = 0xf43d, + UserPolice = 0xf43e, + UserPoliceTie = 0xf43f, + UserRobot = 0xf440, + UserRobotXmarks = 0xf441, + Users = 0xf442, + UsersBetweenLines = 0xf443, + UserSecret = 0xf444, + UsersGear = 0xf445, + UserShakespeare = 0xf446, + UserShield = 0xf447, + UserSlash = 0xf448, + UsersLine = 0xf449, + UsersMedical = 0xf44a, + UsersRectangle = 0xf44b, + UsersSlash = 0xf44c, + UsersViewfinder = 0xf44d, + UserTag = 0xf44e, + UserTie = 0xf44f, + UserUnlock = 0xf450, + UserTieHairLong = 0xf451, + UserVisor = 0xf452, + UserVneck = 0xf453, + UserTieHair = 0xf454, + UserVneckHair = 0xf455, + UserVneckHairLong = 0xf456, + UsersRays = 0xf457, + UserXmark = 0xf458, + Utensils = 0xf459, + UtensilsSlash = 0xf45a, + UtilityPole = 0xf45b, + UtilityPoleDouble = 0xf45c, + V = 0xf45d, + Vacuum = 0xf45e, + VacuumRobot = 0xf45f, + ValueAbsolute = 0xf460, + VanShuttle = 0xf461, + VectorCircle = 0xf462, + Vault = 0xf463, + VectorPolygon = 0xf464, + VectorSquare = 0xf465, + Venus = 0xf466, + VenusDouble = 0xf467, + VenusMars = 0xf468, + VestPatches = 0xf469, + Vial = 0xf46a, + VialCircleCheck = 0xf46b, + Vials = 0xf46c, + Vest = 0xf46d, + Video = 0xf46e, + VideoArrowDownLeft = 0xf46f, + VideoSlash = 0xf470, + VideoPlus = 0xf471, + Violin = 0xf472, + VirusCovidSlash = 0xf473, + Viruses = 0xf474, + VirusSlash = 0xf475, + Virus = 0xf476, + Vihara = 0xf477, + VirusCovid = 0xf478, + Volcano = 0xf479, + VentDamper = 0xf47a, + VolumeSlash = 0xf47b, + VolumeXmark = 0xf47c, + VrCardboard = 0xf47d, + Volume = 0xf47e, + VolumeLow = 0xf47f, + Volleyball = 0xf480, + VolumeOff = 0xf481, + Waffle = 0xf482, + VialVirus = 0xf483, + VolumeHigh = 0xf484, + Voicemail = 0xf485, + W = 0xf486, + VideoArrowUpRight = 0xf487, + WagonCovered = 0xf488, + Walker = 0xf489, + WalkieTalkie = 0xf48a, + Wallet = 0xf48b, + Wand = 0xf48c, + WandMagic = 0xf48d, + WandMagicSparkles = 0xf48e, + WandSparkles = 0xf48f, + Warehouse = 0xf490, + WarehouseFull = 0xf491, + WashingMachine = 0xf492, + Watch = 0xf493, + WatchApple = 0xf494, + WatchCalculator = 0xf495, + WatchFitness = 0xf496, + WatchSmart = 0xf497, + Water = 0xf498, + WaterArrowDown = 0xf499, + WaterArrowUp = 0xf49a, + WaterLadder = 0xf49b, + WatermelonSlice = 0xf49c, + Wave = 0xf49d, + Waveform = 0xf49e, + WaveformLines = 0xf49f, + WavePulse = 0xf4a0, + WaveSine = 0xf4a1, + WaveSquare = 0xf4a2, + WavesSine = 0xf4a3, + WaveTriangle = 0xf4a4, + Webhook = 0xf4a5, + WeightHanging = 0xf4a6, + WeightScale = 0xf4a7, + Whale = 0xf4a8, + Wheat = 0xf4a9, + WheatAwn = 0xf4aa, + WheatAwnCircleExclamation = 0xf4ab, + WheatAwnSlash = 0xf4ac, + WheatSlash = 0xf4ad, + Wheelchair = 0xf4ae, + WheelchairMove = 0xf4af, + WhiskeyGlass = 0xf4b0, + WhiskeyGlassIce = 0xf4b1, + Whistle = 0xf4b2, + Wifi = 0xf4b3, + WifiExclamation = 0xf4b4, + WifiFair = 0xf4b5, + WifiSlash = 0xf4b6, + WifiWeak = 0xf4b7, + Wind = 0xf4b8, + Window = 0xf4b9, + WindowFlip = 0xf4ba, + WindowFrame = 0xf4bb, + WindowFrameOpen = 0xf4bc, + WindowMaximize = 0xf4bd, + WindowMinimize = 0xf4be, + WindowRestore = 0xf4bf, + Windsock = 0xf4c0, + WindTurbine = 0xf4c1, + WindWarning = 0xf4c2, + WineBottle = 0xf4c3, + WineGlass = 0xf4c4, + WineGlassCrack = 0xf4c5, + WineGlassEmpty = 0xf4c6, + WonSign = 0xf4c7, + Worm = 0xf4c8, + Wreath = 0xf4c9, + WreathLaurel = 0xf4ca, + Wrench = 0xf4cb, + WrenchSimple = 0xf4cc, + X = 0xf4cd, + Xmark = 0xf4ce, + XmarkLarge = 0xf4cf, +}; +Q_ENUM_CREATE(IconName) +Q_END_ENUM_CREATE(ElaIconType) +#endif // DEF_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaAcrylicUrlCard.h b/3rdparty/ElaWidgetTools/src/include/ElaAcrylicUrlCard.h new file mode 100644 index 0000000..6e347cc --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaAcrylicUrlCard.h @@ -0,0 +1,39 @@ +#ifndef ELAACRYLICURLCARD_H +#define ELAACRYLICURLCARD_H + +#include +#include + +#include "Def.h" +#include "stdafx.h" +class ElaAcrylicUrlCardPrivate; +class ELA_EXPORT ElaAcrylicUrlCard : public QPushButton +{ + Q_OBJECT + Q_Q_CREATE(ElaAcrylicUrlCard); + Q_PROPERTY_CREATE_Q_H(int, BorderRadius) + Q_PROPERTY_CREATE_Q_H(qreal, MainOpacity) + Q_PROPERTY_CREATE_Q_H(qreal, NoiseOpacity) + Q_PROPERTY_CREATE_Q_H(int, BrushAlpha) + Q_PROPERTY_CREATE_Q_H(QString, Title); + Q_PROPERTY_CREATE_Q_H(QString, SubTitle); + Q_PROPERTY_CREATE_Q_H(int, TitlePixelSize); + Q_PROPERTY_CREATE_Q_H(int, SubTitlePixelSize); + Q_PROPERTY_CREATE_Q_H(int, TitleSpacing); + Q_PROPERTY_CREATE_Q_H(int, SubTitleSpacing); + Q_PROPERTY_CREATE_Q_H(QPixmap, CardPixmap); + Q_PROPERTY_CREATE_Q_H(QSize, CardPixmapSize); + Q_PROPERTY_CREATE_Q_H(int, CardPixmapBorderRadius) + Q_PROPERTY_CREATE_Q_H(ElaCardPixType::PixMode, CardPixMode); + Q_PROPERTY_CREATE_Q_H(QString, Url); + +public: + explicit ElaAcrylicUrlCard(QWidget* parent = nullptr); + ~ElaAcrylicUrlCard(); + void setCardPixmapSize(int width, int height); + +protected: + virtual void paintEvent(QPaintEvent* event) override; +}; + +#endif // ELAACRYLICURLCARD_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaAppBar.h b/3rdparty/ElaWidgetTools/src/include/ElaAppBar.h new file mode 100644 index 0000000..a209163 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaAppBar.h @@ -0,0 +1,94 @@ +#ifndef ELAAPPBAR_H +#define ELAAPPBAR_H + +#include + +#include "Def.h" +#include "stdafx.h" + +#ifdef Q_OS_WIN +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +#define Q_TAKEOVER_NATIVEEVENT_H virtual bool nativeEvent(const QByteArray& eventType, void* message, qintptr* result) override; +#else +#define Q_TAKEOVER_NATIVEEVENT_H virtual bool nativeEvent(const QByteArray& eventType, void* message, long* result) override; +#endif +#else +#define Q_TAKEOVER_NATIVEEVENT_H +#endif + +#ifdef Q_OS_WIN +#define ELAAPPBAR_HANDLE(ElaAppBar) \ + if (ElaAppBar) \ + { \ + int ret = ElaAppBar->takeOverNativeEvent(eventType, message, result); \ + if (ret == -1) \ + { \ + return QWidget::nativeEvent(eventType, message, result); \ + } \ + return (bool)ret; \ + } \ + return QWidget::nativeEvent(eventType, message, result); +#endif + +#ifdef Q_OS_WIN +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +#define Q_TAKEOVER_NATIVEEVENT_CPP(CLASS, ElaAppBar) \ + bool CLASS::nativeEvent(const QByteArray& eventType, void* message, qintptr* result) \ + { \ + ELAAPPBAR_HANDLE(ElaAppBar) \ + } +#else +#define Q_TAKEOVER_NATIVEEVENT_CPP(CLASS, ElaAppBar) \ + bool CLASS::nativeEvent(const QByteArray& eventType, void* message, long* result) \ + { \ + ELAAPPBAR_HANDLE(ElaAppBar) \ + } +#endif +#else +#define Q_TAKEOVER_NATIVEEVENT_CPP(CLASS, ElaAppBar) +#endif + +class ElaAppBarPrivate; +class ELA_EXPORT ElaAppBar : public QWidget +{ + Q_OBJECT + Q_Q_CREATE(ElaAppBar) + Q_PROPERTY_CREATE_Q_H(bool, IsStayTop) + Q_PROPERTY_CREATE_Q_H(bool, IsFixedSize) + Q_PROPERTY_CREATE_Q_H(bool, IsDefaultClosed) + Q_PROPERTY_CREATE_Q_H(bool, IsOnlyAllowMinAndClose) + Q_PROPERTY_CREATE_Q_H(int, AppBarHeight) + Q_PROPERTY_CREATE_Q_H(int, CustomWidgetMaximumWidth) +public: + explicit ElaAppBar(QWidget* parent = nullptr); + ~ElaAppBar(); + + void setCustomWidget(ElaAppBarType::CustomArea customArea, QWidget* customWidget); + QWidget* getCustomWidget() const; + + void setWindowButtonFlag(ElaAppBarType::ButtonType buttonFlag, bool isEnable = true); + void setWindowButtonFlags(ElaAppBarType::ButtonFlags buttonFlags); + ElaAppBarType::ButtonFlags getWindowButtonFlags() const; + + void setRouteBackButtonEnable(bool isEnable); + + void closeWindow(); +#ifdef Q_OS_WIN +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + int takeOverNativeEvent(const QByteArray& eventType, void* message, qintptr* result); +#else + int takeOverNativeEvent(const QByteArray& eventType, void* message, long* result); +#endif +#endif +Q_SIGNALS: + Q_SIGNAL void routeBackButtonClicked(); + Q_SIGNAL void navigationButtonClicked(); + Q_SIGNAL void themeChangeButtonClicked(); + Q_SIGNAL void closeButtonClicked(); + Q_SIGNAL void customWidgetChanged(); + +protected: + virtual bool eventFilter(QObject* obj, QEvent* event) override; +}; + +#endif // ELAAPPBAR_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaApplication.h b/3rdparty/ElaWidgetTools/src/include/ElaApplication.h new file mode 100644 index 0000000..b4f3f82 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaApplication.h @@ -0,0 +1,28 @@ +#ifndef ELAAPPLICATION_H +#define ELAAPPLICATION_H + +#include +#include + +#include "singleton.h" +#include "stdafx.h" +#define eApp ElaApplication::getInstance() +class ElaApplicationPrivate; +class ELA_EXPORT ElaApplication : public QObject +{ + Q_OBJECT + Q_Q_CREATE(ElaApplication) + Q_SINGLETON_CREATE_H(ElaApplication) + Q_PROPERTY_CREATE_Q_H(bool, IsEnableMica) + Q_PROPERTY_CREATE_Q_H(QString, MicaImagePath) +private: + explicit ElaApplication(QObject* parent = nullptr); + ~ElaApplication(); + +public: + void init(); + void syncMica(QWidget* widget, bool isSync = true); + static bool containsCursorToItem(QWidget* item); +}; + +#endif // ELAAPPLICATION_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaBreadcrumbBar.h b/3rdparty/ElaWidgetTools/src/include/ElaBreadcrumbBar.h new file mode 100644 index 0000000..0222ea4 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaBreadcrumbBar.h @@ -0,0 +1,28 @@ +#ifndef ELABREADCRUMBBAR_H +#define ELABREADCRUMBBAR_H +#include + +#include "stdafx.h" + +class ElaBreadcrumbBarPrivate; +class ELA_EXPORT ElaBreadcrumbBar : public QWidget +{ + Q_OBJECT + Q_Q_CREATE(ElaBreadcrumbBar) + Q_PROPERTY_CREATE_Q_H(int, TextPixelSize) + Q_PROPERTY_CREATE_Q_H(bool, IsAutoRemove) +public: + explicit ElaBreadcrumbBar(QWidget* parent = nullptr); + ~ElaBreadcrumbBar(); + void setBreadcrumbList(QStringList breadcrumbList); + QStringList appendBreadcrumb(QString breadcrumb); + QStringList removeBreadcrumb(QString breadcrumb); + + int getBreadcrumbListCount() const; + QStringList getBreadcrumbList() const; + +Q_SIGNALS: + Q_SIGNAL void breadcrumbClicked(QString breadcrumb, QStringList lastBreadcrumbList); +}; + +#endif // ELABREADCRUMBBAR_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaCalendar.h b/3rdparty/ElaWidgetTools/src/include/ElaCalendar.h new file mode 100644 index 0000000..00957c4 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaCalendar.h @@ -0,0 +1,26 @@ +#ifndef ELACALENDAR_H +#define ELACALENDAR_H + +#include + +#include "stdafx.h" +class ElaCalendarPrivate; +class ELA_EXPORT ElaCalendar : public QWidget +{ + Q_OBJECT + Q_Q_CREATE(ElaCalendar) + Q_PROPERTY_CREATE_Q_H(int, BorderRaiuds) + Q_PROPERTY_CREATE_Q_H(QDate, SelectedDate) + Q_PROPERTY_CREATE_Q_H(QDate, MinimumDate) + Q_PROPERTY_CREATE_Q_H(QDate, MaximumDate) +public: + explicit ElaCalendar(QWidget* parent = nullptr); + ~ElaCalendar(); +Q_SIGNALS: + Q_SIGNAL void clicked(QDate date); + +protected: + virtual void paintEvent(QPaintEvent* event) override; +}; + +#endif // ELACALENDAR_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaCalendarPicker.h b/3rdparty/ElaWidgetTools/src/include/ElaCalendarPicker.h new file mode 100644 index 0000000..e0ef592 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaCalendarPicker.h @@ -0,0 +1,25 @@ +#ifndef ELACALENDARPICKER_H +#define ELACALENDARPICKER_H + +#include + +#include "stdafx.h" +class ElaCalendarPickerPrivate; +class ELA_EXPORT ElaCalendarPicker : public QPushButton +{ + Q_OBJECT + Q_Q_CREATE(ElaCalendarPicker) + Q_PROPERTY_CREATE_Q_H(int, BorderRadius) + Q_PRIVATE_CREATE_Q_H(QDate, SelectedDate) +public: + explicit ElaCalendarPicker(QWidget* parent = nullptr); + ~ElaCalendarPicker(); + +Q_SIGNALS: + Q_SIGNAL void selectedDateChanged(QDate date); + +protected: + virtual void paintEvent(QPaintEvent* event) override; +}; + +#endif // ELACALENDARPICKER_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaCheckBox.h b/3rdparty/ElaWidgetTools/src/include/ElaCheckBox.h new file mode 100644 index 0000000..4fdcea3 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaCheckBox.h @@ -0,0 +1,17 @@ +#ifndef ELACHECKBOX_H +#define ELACHECKBOX_H + +#include + +#include "stdafx.h" +class ELA_EXPORT ElaCheckBox : public QCheckBox +{ + Q_OBJECT + Q_PROPERTY_CREATE(int, BorderRadius) +public: + explicit ElaCheckBox(QWidget* parent = nullptr); + explicit ElaCheckBox(const QString& text, QWidget* parent = nullptr); + ~ElaCheckBox(); +}; + +#endif // ELACHECKBOX_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaColorDialog.h b/3rdparty/ElaWidgetTools/src/include/ElaColorDialog.h new file mode 100644 index 0000000..ce3cb8e --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaColorDialog.h @@ -0,0 +1,29 @@ +#ifndef ELACOLORDIALOG_H +#define ELACOLORDIALOG_H + +#include + +#include "ElaAppBar.h" +#include "stdafx.h" +class ElaColorDialogPrivate; +class ELA_EXPORT ElaColorDialog : public QDialog +{ + Q_OBJECT + Q_Q_CREATE(ElaColorDialog) + Q_PROPERTY_CREATE_Q_H(QColor, CurrentColor) + Q_TAKEOVER_NATIVEEVENT_H +public: + explicit ElaColorDialog(QWidget* parent = nullptr); + ~ElaColorDialog(); + + QList getCustomColorList() const; + QColor getCustomColor(int index) const; + QString getCurrentColorRGB() const; +Q_SIGNALS: + Q_SIGNAL void colorSelected(const QColor& color); + +protected: + virtual void paintEvent(QPaintEvent* event) override; +}; + +#endif // ELACOLORDIALOG_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaComboBox.h b/3rdparty/ElaWidgetTools/src/include/ElaComboBox.h new file mode 100644 index 0000000..16dd1d0 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaComboBox.h @@ -0,0 +1,23 @@ +#ifndef ELACOMBOBOX_H +#define ELACOMBOBOX_H + +#include + +#include "stdafx.h" + +class ElaComboBoxPrivate; +class ELA_EXPORT ElaComboBox : public QComboBox +{ + Q_OBJECT + Q_Q_CREATE(ElaComboBox); + Q_PROPERTY_CREATE_Q_H(int, BorderRadius) +public: + explicit ElaComboBox(QWidget* parent = nullptr); + ~ElaComboBox(); + +protected: + virtual void showPopup() override; + virtual void hidePopup() override; +}; + +#endif // ELACOMBOBOX_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaContentDialog.h b/3rdparty/ElaWidgetTools/src/include/ElaContentDialog.h new file mode 100644 index 0000000..939f87c --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaContentDialog.h @@ -0,0 +1,44 @@ +#ifndef ELACONTENTDIALOG_H +#define ELACONTENTDIALOG_H +#include +#include + +#include "stdafx.h" + +class ElaContentDialogPrivate; +class ELA_EXPORT ElaContentDialog : public QDialog +{ + Q_OBJECT + Q_Q_CREATE(ElaContentDialog) +public: + explicit ElaContentDialog(QWidget* parent); + ~ElaContentDialog(); + Q_SLOT virtual void onLeftButtonClicked(); + Q_SLOT virtual void onMiddleButtonClicked(); + Q_SLOT virtual void onRightButtonClicked(); + void setCentralWidget(QWidget* centralWidget); + + void setLeftButtonText(QString text); + void setMiddleButtonText(QString text); + void setRightButtonText(QString text); + + void close(); +Q_SIGNALS: + Q_SIGNAL void leftButtonClicked(); + Q_SIGNAL void middleButtonClicked(); + Q_SIGNAL void rightButtonClicked(); + +protected: + virtual void showEvent(QShowEvent* event) override; + virtual void paintEvent(QPaintEvent* event) override; + virtual void keyPressEvent(QKeyEvent* event) override; +#ifdef Q_OS_WIN +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + virtual bool nativeEvent(const QByteArray& eventType, void* message, qintptr* result) override; +#else + virtual bool nativeEvent(const QByteArray& eventType, void* message, long* result) override; +#endif +#endif +}; + +#endif// ELACONTENTDIALOG_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaDockWidget.h b/3rdparty/ElaWidgetTools/src/include/ElaDockWidget.h new file mode 100644 index 0000000..88a11cb --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaDockWidget.h @@ -0,0 +1,29 @@ +#ifndef ELADOCKWIDGET_H +#define ELADOCKWIDGET_H + +#include + +#include "stdafx.h" +class ElaDockWidgetPrivate; +class ELA_EXPORT ElaDockWidget : public QDockWidget +{ + Q_OBJECT + Q_Q_CREATE(ElaDockWidget) +public: + explicit ElaDockWidget(QWidget* parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags()); + explicit ElaDockWidget(const QString& title, QWidget* parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags()); + ~ElaDockWidget(); + +protected: + virtual void paintEvent(QPaintEvent* event) override; +#ifdef Q_OS_WIN + virtual bool event(QEvent* event) override; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + virtual bool nativeEvent(const QByteArray& eventType, void* message, qintptr* result) override; +#else + virtual bool nativeEvent(const QByteArray& eventType, void* message, long* result) override; +#endif +#endif +}; + +#endif // ELADOCKWIDGET_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaDoubleSpinBox.h b/3rdparty/ElaWidgetTools/src/include/ElaDoubleSpinBox.h new file mode 100644 index 0000000..ab9f16f --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaDoubleSpinBox.h @@ -0,0 +1,21 @@ +#ifndef ELADOUBLESPINBOX_H +#define ELADOUBLESPINBOX_H + +#include + +#include "stdafx.h" + +class ElaDoubleSpinBoxPrivate; +class ELA_EXPORT ElaDoubleSpinBox : public QDoubleSpinBox +{ + Q_OBJECT + Q_Q_CREATE(ElaDoubleSpinBox) +public: + explicit ElaDoubleSpinBox(QWidget* parent = nullptr); + ~ElaDoubleSpinBox(); + +protected: + void contextMenuEvent(QContextMenuEvent* event) override; +}; + +#endif // ELADOUBLESPINBOX_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaDxgiManager.h b/3rdparty/ElaWidgetTools/src/include/ElaDxgiManager.h new file mode 100644 index 0000000..076190c --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaDxgiManager.h @@ -0,0 +1,58 @@ +#ifndef ELADXGIMANAGER_H +#define ELADXGIMANAGER_H + +#include +#ifdef Q_OS_WIN +#include "singleton.h" +#include "stdafx.h" + +class ElaDxgiManagerPrivate; +class ELA_EXPORT ElaDxgiManager : public QObject +{ + Q_OBJECT + Q_Q_CREATE(ElaDxgiManager) + Q_SINGLETON_CREATE_H(ElaDxgiManager); + +private: + explicit ElaDxgiManager(QObject* parent = nullptr); + ~ElaDxgiManager(); + +public: + QStringList getDxDeviceList() const; + QStringList getOutputDeviceList() const; + QImage grabScreenToImage() const; + void startGrabScreen(); + void stopGrabScreen(); + bool getIsGrabScreen() const; + bool setDxDeviceID(int dxID); + int getDxDeviceID() const; + bool setOutputDeviceID(int deviceID); + int getOutputDeviceID() const; + void setGrabArea(int width, int height); //从屏幕中心向外延伸 + void setGrabArea(int x, int y, int width, int height); + QRect getGrabArea() const; + void setGrabFrameRate(int frameRateValue); + int getGrabFrameRate() const; + void setTimeoutMsValue(int timeoutValue); + int getTimeoutMsValue() const; +Q_SIGNALS: + Q_SIGNAL void grabImageUpdate(); +}; + +class ElaDxgiScreenPrivate; +class ELA_EXPORT ElaDxgiScreen : public QWidget +{ + Q_OBJECT + Q_Q_CREATE(ElaDxgiScreen) + Q_PROPERTY_CREATE_Q_H(int, BorderRadius) +public: + explicit ElaDxgiScreen(QWidget* parent = nullptr); + ~ElaDxgiScreen(); + void setIsSyncGrabSize(bool isSyncGrabSize); + bool getIsSyncGrabSize() const; + +protected: + void paintEvent(QPaintEvent* event) override; +}; +#endif +#endif // ELADXGIMANAGER_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaEventBus.h b/3rdparty/ElaWidgetTools/src/include/ElaEventBus.h new file mode 100644 index 0000000..9b998bc --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaEventBus.h @@ -0,0 +1,45 @@ +#ifndef ELAEVENTBUS_H +#define ELAEVENTBUS_H + +#include +#include + +#include "Def.h" +#include "singleton.h" +#include "stdafx.h" +class ElaEventPrivate; +class ELA_EXPORT ElaEvent : public QObject +{ + Q_OBJECT + Q_Q_CREATE(ElaEvent) + Q_PROPERTY_CREATE_Q_H(QString, EventName); + Q_PROPERTY_CREATE_Q_H(QString, FunctionName); + Q_PROPERTY_CREATE_Q_H(Qt::ConnectionType, ConnectionType); + +public: + explicit ElaEvent(QObject* parent = nullptr); + explicit ElaEvent(QString eventName, QString functionName, QObject* parent = nullptr); + ~ElaEvent(); + ElaEventBusType::EventBusReturnType registerAndInit(); +}; + +class ElaEventBusPrivate; +class ELA_EXPORT ElaEventBus : public QObject +{ + Q_OBJECT + Q_Q_CREATE(ElaEventBus) + Q_SINGLETON_CREATE_H(ElaEventBus); + +private: + explicit ElaEventBus(QObject* parent = nullptr); + ~ElaEventBus(); + +public: + ElaEventBusType::EventBusReturnType post(const QString& eventName, const QVariantMap& data = {}); + QStringList getRegisteredEventsName() const; + +private: + friend class ElaEvent; +}; + +#endif // ELAEVENTBUS_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaExponentialBlur.h b/3rdparty/ElaWidgetTools/src/include/ElaExponentialBlur.h new file mode 100644 index 0000000..924d401 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaExponentialBlur.h @@ -0,0 +1,23 @@ +#ifndef ELAEXPONENTIALBLUR_H +#define ELAEXPONENTIALBLUR_H + +#include + +#include "singleton.h" +#include "stdafx.h" + +class ElaExponentialBlurPrivate; +class ELA_EXPORT ElaExponentialBlur : public QObject +{ + Q_OBJECT + Q_SINGLETON_CREATE_H(ElaExponentialBlur) + Q_Q_CREATE(ElaExponentialBlur) +private: + explicit ElaExponentialBlur(QObject* parent = nullptr); + ~ElaExponentialBlur(); + +public: + static QPixmap doExponentialBlur(QImage img, const quint16& blurRadius); +}; + +#endif // ELAEXPONENTIALBLUR_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaFlowLayout.h b/3rdparty/ElaWidgetTools/src/include/ElaFlowLayout.h new file mode 100644 index 0000000..63b4a91 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaFlowLayout.h @@ -0,0 +1,35 @@ +#ifndef ELAFLOWLAYOUT_H +#define ELAFLOWLAYOUT_H + +#include +#include +#include + +#include "stdafx.h" +class ElaFlowLayoutPrivate; +class ELA_EXPORT ElaFlowLayout : public QLayout +{ + Q_OBJECT + Q_Q_CREATE(ElaFlowLayout) +public: + explicit ElaFlowLayout(QWidget* parent, int margin = -1, int hSpacing = -1, int vSpacing = -1); + explicit ElaFlowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1); + ~ElaFlowLayout(); + + void addItem(QLayoutItem* item) override; + int horizontalSpacing() const; + int verticalSpacing() const; + Qt::Orientations expandingDirections() const override; + bool hasHeightForWidth() const override; + int heightForWidth(int) const override; + int count() const override; + QLayoutItem* itemAt(int index) const override; + QSize minimumSize() const override; + void setGeometry(const QRect& rect) override; + QSize sizeHint() const override; + QLayoutItem* takeAt(int index) override; + + void setIsAnimation(bool isAnimation); +}; + +#endif // ELAFLOWLAYOUT_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaGraphicsItem.h b/3rdparty/ElaWidgetTools/src/include/ElaGraphicsItem.h new file mode 100644 index 0000000..c175c1e --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaGraphicsItem.h @@ -0,0 +1,48 @@ +#ifndef ELAGRAPHICSITEM_H +#define ELAGRAPHICSITEM_H + +#include +#include +#include +#include + +#include "stdafx.h" +class ElaGraphicsScene; +class ElaGraphicsItemPrivate; +class ELA_EXPORT ElaGraphicsItem : public QGraphicsObject +{ + Q_OBJECT + Q_Q_CREATE(ElaGraphicsItem) + Q_PROPERTY_CREATE_Q_H(int, Width) + Q_PROPERTY_CREATE_Q_H(int, Height) + Q_PROPERTY_CREATE_Q_H(QImage, ItemImage) + Q_PROPERTY_CREATE_Q_H(QImage, ItemSelectedImage) + Q_PROPERTY_CREATE_Q_H(QString, ItemName) + Q_PROPERTY_CREATE_Q_H(QVariantMap, DataRoutes) + Q_PROPERTY_CREATE_Q_H(int, MaxLinkPortCount) +public: + explicit ElaGraphicsItem(QGraphicsItem* parent = nullptr); + explicit ElaGraphicsItem(int width, int height, QGraphicsItem* parent = nullptr); + ~ElaGraphicsItem(); + + QString getItemUID() const; + + void setLinkPortState(bool isFullLink); + void setLinkPortState(bool isLink, int portIndex); + + bool getLinkPortState(int portIndex) const; + QVector getLinkPortState() const; + + int getUsedLinkPortCount() const; + QVector getUsedLinkPort() const; + int getUnusedLinkPortCount() const; + QVector getUnusedLinkPort() const; + +protected: + virtual QRectF boundingRect() const override; + virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = nullptr) override; + friend QDataStream& operator<<(QDataStream& stream, const ElaGraphicsItem* item); + friend QDataStream& operator>>(QDataStream& stream, ElaGraphicsItem* item); +}; + +#endif // ELAGRAPHICSITEM_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaGraphicsLineItem.h b/3rdparty/ElaWidgetTools/src/include/ElaGraphicsLineItem.h new file mode 100644 index 0000000..cb9d4f2 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaGraphicsLineItem.h @@ -0,0 +1,33 @@ +#ifndef ELAGRAPHICSLINEITEM_H +#define ELAGRAPHICSLINEITEM_H + +#include + +#include "stdafx.h" +class ElaGraphicsItem; +class ElaGraphicsLineItemPrivate; +class ELA_EXPORT ElaGraphicsLineItem : public QGraphicsPathItem +{ + Q_Q_CREATE(ElaGraphicsLineItem) + Q_PRIVATE_CREATE_Q_H(QPointF, StartPoint); + Q_PRIVATE_CREATE_Q_H(QPointF, EndPoint); + Q_PRIVATE_CREATE_Q_H(ElaGraphicsItem*, StartItem); + Q_PRIVATE_CREATE_Q_H(ElaGraphicsItem*, EndItem); + Q_PRIVATE_CREATE_Q_H(int, StartItemPort); + Q_PRIVATE_CREATE_Q_H(int, EndItemPort); + +public: + explicit ElaGraphicsLineItem(ElaGraphicsItem* startItem, ElaGraphicsItem* endItem, int startItemPort, int endItemPort, QGraphicsItem* parent = nullptr); + explicit ElaGraphicsLineItem(QPointF startPoint, QPointF endPoint, QGraphicsItem* parent = nullptr); + ~ElaGraphicsLineItem(); + + bool isTargetLink(ElaGraphicsItem* item) const; + bool isTargetLink(ElaGraphicsItem* item1, ElaGraphicsItem* item2) const; + bool isTargetLink(ElaGraphicsItem* item1, ElaGraphicsItem* item2, int port1, int port2) const; + +protected: + void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = nullptr) override; + QRectF boundingRect() const override; +}; + +#endif // ELAGRAPHICSLINEITEM_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaGraphicsScene.h b/3rdparty/ElaWidgetTools/src/include/ElaGraphicsScene.h new file mode 100644 index 0000000..d45153b --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaGraphicsScene.h @@ -0,0 +1,70 @@ +#ifndef ELAGRAPHICSSCENE_H +#define ELAGRAPHICSSCENE_H + +#include +#include +#include +#include +#include +#include + +#include "Def.h" +#include "stdafx.h" + +class ElaGraphicsItem; +class ElaGraphicsScenePrivate; +class ELA_EXPORT ElaGraphicsScene : public QGraphicsScene +{ + Q_OBJECT + Q_Q_CREATE(ElaGraphicsScene) + Q_PROPERTY_CREATE_Q_H(bool, IsCheckLinkPort) + Q_PROPERTY_CREATE_Q_H(QString, SerializePath) +public: + explicit ElaGraphicsScene(QObject* parent = nullptr); + ~ElaGraphicsScene(); + void addItem(ElaGraphicsItem* item); + void removeItem(ElaGraphicsItem* item); + void removeSelectedItems(); + void clear(); + + QList createAndAddItem(int width, int height, int count = 1); + QList getSelectedElaItems() const; + QList getElaItems(); + QList getElaItems(QPoint pos); + QList getElaItems(QPointF pos); + QList getElaItems(QRect rect); + QList getElaItems(QRectF rect); + + void setSceneMode(ElaGraphicsSceneType::SceneMode mode); + ElaGraphicsSceneType::SceneMode getSceneMode() const; + + void selectAllItems(); + + QList getItemLinkList() const; + bool addItemLink(ElaGraphicsItem* item1, ElaGraphicsItem* item2, int port1 = 0, int port2 = 0); + bool removeItemLink(ElaGraphicsItem* item1); + bool removeItemLink(ElaGraphicsItem* item1, ElaGraphicsItem* item2, int port1 = 0, int port2 = 0); + + QVector getItemsDataRoute() const; + + // 序列化 反序列化 + void serialize(); + void deserialize(); + +Q_SIGNALS: + void showItemLink(); + void mouseLeftClickedItem(ElaGraphicsItem* item); + void mouseRightClickedItem(ElaGraphicsItem* item); + void mouseDoubleClickedItem(ElaGraphicsItem* item); + +protected: + virtual void focusOutEvent(QFocusEvent* event) override; + virtual void keyPressEvent(QKeyEvent* event) override; + virtual void keyReleaseEvent(QKeyEvent* event) override; + virtual void mousePressEvent(QGraphicsSceneMouseEvent* event) override; + virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) override; + virtual void mouseMoveEvent(QGraphicsSceneMouseEvent* event) override; + virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) override; +}; + +#endif // ELAGRAPHICSSCENE_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaGraphicsView.h b/3rdparty/ElaWidgetTools/src/include/ElaGraphicsView.h new file mode 100644 index 0000000..7bf8a62 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaGraphicsView.h @@ -0,0 +1,26 @@ +#ifndef ELAGRAPHICSVIEW_H +#define ELAGRAPHICSVIEW_H + +#include + +#include "stdafx.h" +class ElaGraphicsViewPrivate; +class ELA_EXPORT ElaGraphicsView : public QGraphicsView +{ + Q_OBJECT + Q_Q_CREATE(ElaGraphicsView) + Q_PROPERTY_CREATE_Q_H(qreal, MaxTransform); + Q_PROPERTY_CREATE_Q_H(qreal, MinTransform); + +public: + explicit ElaGraphicsView(QWidget* parent = nullptr); + explicit ElaGraphicsView(QGraphicsScene* scene, QWidget* parent = nullptr); + ~ElaGraphicsView(); + +protected: + void wheelEvent(QWheelEvent* event) override; + void keyPressEvent(QKeyEvent* event) override; + void keyReleaseEvent(QKeyEvent* event) override; +}; + +#endif // ELAGRAPHICSVIEW_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaIcon.h b/3rdparty/ElaWidgetTools/src/include/ElaIcon.h new file mode 100644 index 0000000..7b31010 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaIcon.h @@ -0,0 +1,24 @@ +#ifndef ELAICON_H +#define ELAICON_H +#include + +#include "Def.h" +#include "singleton.h" +#include "stdafx.h" +class ELA_EXPORT ElaIcon +{ + Q_SINGLETON_CREATE_H(ElaIcon) +private: + explicit ElaIcon(); + ~ElaIcon(); + +public: + QIcon getElaIcon(ElaIconType::IconName awesome); + QIcon getElaIcon(ElaIconType::IconName awesome, QColor iconColor); + QIcon getElaIcon(ElaIconType::IconName awesome, int pixelSize); + QIcon getElaIcon(ElaIconType::IconName awesome, int pixelSize, QColor iconColor); + QIcon getElaIcon(ElaIconType::IconName awesome, int pixelSize, int fixedWidth, int fixedHeight); + QIcon getElaIcon(ElaIconType::IconName awesome, int pixelSize, int fixedWidth, int fixedHeight, QColor iconColor); +}; + +#endif // ELAICON_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaIconButton.h b/3rdparty/ElaWidgetTools/src/include/ElaIconButton.h new file mode 100644 index 0000000..80028fe --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaIconButton.h @@ -0,0 +1,39 @@ +#ifndef ELAICONBUTTON_H +#define ELAICONBUTTON_H + +#include + +#include "Def.h" +#include "stdafx.h" +class ElaIconButtonPrivate; +class ELA_EXPORT ElaIconButton : public QPushButton +{ + Q_OBJECT + Q_Q_CREATE(ElaIconButton) + Q_PROPERTY_CREATE_Q_H(int, BorderRadius) + Q_PROPERTY_CREATE_Q_H(qreal, Opacity); + Q_PROPERTY_CREATE_Q_H(QColor, LightHoverColor); + Q_PROPERTY_CREATE_Q_H(QColor, DarkHoverColor); + Q_PROPERTY_CREATE_Q_H(QColor, LightIconColor); + Q_PROPERTY_CREATE_Q_H(QColor, DarkIconColor); + Q_PROPERTY_CREATE_Q_H(QColor, LightHoverIconColor); + Q_PROPERTY_CREATE_Q_H(QColor, DarkHoverIconColor); + Q_PROPERTY_CREATE_Q_H(bool, IsSelected); + +public: + ElaIconButton(QPixmap pix, QWidget* parent = nullptr); + ElaIconButton(ElaIconType::IconName awesome, QWidget* parent = nullptr); + ElaIconButton(ElaIconType::IconName awesome, int pixelSize, QWidget* parent = nullptr); + ElaIconButton(ElaIconType::IconName awesome, int pixelSize, int fixedWidth, int fixedHeight, QWidget* parent = nullptr); + ~ElaIconButton(); + void setAwesome(ElaIconType::IconName awesome); + ElaIconType::IconName getAwesome() const; + + void setPixmap(QPixmap pix); + +protected: + virtual bool event(QEvent* event) override; + virtual void paintEvent(QPaintEvent* event) override; +}; + +#endif // ELAICONBUTTON_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaImageCard.h b/3rdparty/ElaWidgetTools/src/include/ElaImageCard.h new file mode 100644 index 0000000..1e9c966 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaImageCard.h @@ -0,0 +1,25 @@ +#ifndef ELAIMAGECARD_H +#define ELAIMAGECARD_H + +#include + +#include "stdafx.h" + +class ElaImageCardPrivate; +class ELA_EXPORT ElaImageCard : public QWidget +{ + Q_OBJECT + Q_Q_CREATE(ElaImageCard) + Q_PROPERTY_CREATE_Q_H(QImage, CardImage); + Q_PROPERTY_CREATE_Q_H(int, BorderRadius) + Q_PROPERTY_CREATE_Q_H(bool, IsPreserveAspectCrop) + Q_PROPERTY_CREATE_Q_H(qreal, MaximumAspectRatio) +public: + explicit ElaImageCard(QWidget* parent = nullptr); + ~ElaImageCard(); + +protected: + virtual void paintEvent(QPaintEvent* event) override; +}; + +#endif // ELAIMAGECARD_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaInteractiveCard.h b/3rdparty/ElaWidgetTools/src/include/ElaInteractiveCard.h new file mode 100644 index 0000000..0de8566 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaInteractiveCard.h @@ -0,0 +1,35 @@ +#ifndef ELAINTERACTIVECARD_H +#define ELAINTERACTIVECARD_H + +#include +#include + +#include "Def.h" +#include "stdafx.h" + +class ElaInteractiveCardPrivate; +class ELA_EXPORT ElaInteractiveCard : public QPushButton +{ + Q_OBJECT + Q_Q_CREATE(ElaInteractiveCard) + Q_PROPERTY_CREATE_Q_H(int, BorderRadius) + Q_PROPERTY_CREATE_Q_H(QString, Title); + Q_PROPERTY_CREATE_Q_H(QString, SubTitle); + Q_PROPERTY_CREATE_Q_H(int, TitlePixelSize); + Q_PROPERTY_CREATE_Q_H(int, SubTitlePixelSize); + Q_PROPERTY_CREATE_Q_H(int, TitleSpacing); + Q_PROPERTY_CREATE_Q_H(QPixmap, CardPixmap); + Q_PROPERTY_CREATE_Q_H(QSize, CardPixmapSize); + Q_PROPERTY_CREATE_Q_H(int, CardPixmapBorderRadius) + Q_PROPERTY_CREATE_Q_H(ElaCardPixType::PixMode, CardPixMode); + +public: + explicit ElaInteractiveCard(QWidget* parent = nullptr); + ~ElaInteractiveCard(); + void setCardPixmapSize(int width, int height); + +protected: + void paintEvent(QPaintEvent* event) override; +}; + +#endif // ELAINTERACTIVECARD_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaKeyBinder.h b/3rdparty/ElaWidgetTools/src/include/ElaKeyBinder.h new file mode 100644 index 0000000..a2c7363 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaKeyBinder.h @@ -0,0 +1,29 @@ +#ifndef ELAWIDGETTOOLS_SRC_ELAKEYBINDER_H_ +#define ELAWIDGETTOOLS_SRC_ELAKEYBINDER_H_ + +#include "Def.h" +#include + +class ElaKeyBinderPrivate; +class ELA_EXPORT ElaKeyBinder : public QLabel +{ + Q_OBJECT + Q_Q_CREATE(ElaKeyBinder) + Q_PROPERTY_CREATE_Q_H(int, BorderRadius) + Q_PROPERTY_CREATE_Q_H(QString, BinderKeyText) + Q_PROPERTY_CREATE_Q_H(quint32, NativeVirtualBinderKey) +public: + explicit ElaKeyBinder(QWidget* parent = nullptr); + ~ElaKeyBinder(); + +Q_SIGNALS: + Q_SIGNAL void binderKeyTextChanged(QString binderKeyText); + Q_SIGNAL void nativeVirtualBinderKeyChanged(quint32 binderKey); + +protected: + virtual bool event(QEvent* event) override; + virtual void mouseReleaseEvent(QMouseEvent* event) override; + virtual void paintEvent(QPaintEvent* event) override; +}; + +#endif//ELAWIDGETTOOLS_SRC_ELAKEYBINDER_H_ diff --git a/3rdparty/ElaWidgetTools/src/include/ElaLCDNumber.h b/3rdparty/ElaWidgetTools/src/include/ElaLCDNumber.h new file mode 100644 index 0000000..e4e8599 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaLCDNumber.h @@ -0,0 +1,23 @@ +#ifndef ELAWIDGETTOOLS_ELALCDNUMBER_H +#define ELAWIDGETTOOLS_ELALCDNUMBER_H + +#include + +#include "stdafx.h" + +class ElaLCDNumberPrivate; +class ELA_EXPORT ElaLCDNumber : public QLCDNumber +{ + Q_OBJECT + Q_Q_CREATE(ElaLCDNumber) + Q_PROPERTY_CREATE_Q_H(bool, IsUseAutoClock) + Q_PROPERTY_CREATE_Q_H(QString, AutoClockFormat) + Q_PROPERTY_CREATE_Q_H(bool, IsTransparent) + +public: + explicit ElaLCDNumber(QWidget* parent = nullptr); + explicit ElaLCDNumber(uint numDigits, QWidget* parent = nullptr); + ~ElaLCDNumber() override; +}; + +#endif //ELAWIDGETTOOLS_ELALCDNUMBER_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaLineEdit.h b/3rdparty/ElaWidgetTools/src/include/ElaLineEdit.h new file mode 100644 index 0000000..ecd4859 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaLineEdit.h @@ -0,0 +1,31 @@ +#ifndef ELALINEEDIT_H +#define ELALINEEDIT_H + +#include + +#include "stdafx.h" + +class ElaLineEditPrivate; +class ELA_EXPORT ElaLineEdit : public QLineEdit +{ + Q_OBJECT + Q_Q_CREATE(ElaLineEdit) + Q_PROPERTY_CREATE_Q_H(int, BorderRadius) + Q_PROPERTY_CREATE_Q_H(bool, IsClearButtonEnable) +public: + explicit ElaLineEdit(QWidget* parent = nullptr); + ~ElaLineEdit(); + +Q_SIGNALS: + Q_SIGNAL void focusIn(QString text); + Q_SIGNAL void focusOut(QString text); + Q_SIGNAL void wmFocusOut(QString text); + +protected: + void focusInEvent(QFocusEvent* event) override; + void focusOutEvent(QFocusEvent* event) override; + void paintEvent(QPaintEvent* event) override; + void contextMenuEvent(QContextMenuEvent* event) override; +}; + +#endif // ELALINEEDIT_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaListView.h b/3rdparty/ElaWidgetTools/src/include/ElaListView.h new file mode 100644 index 0000000..1014f47 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaListView.h @@ -0,0 +1,20 @@ +#ifndef ELALISTVIEW_H +#define ELALISTVIEW_H + +#include + +#include "stdafx.h" + +class ElaListViewPrivate; +class ELA_EXPORT ElaListView : public QListView +{ + Q_OBJECT + Q_Q_CREATE(ElaListView) + Q_PROPERTY_CREATE_Q_H(int, ItemHeight) + Q_PROPERTY_CREATE_Q_H(bool, IsTransparent) +public: + explicit ElaListView(QWidget* parent = nullptr); + ~ElaListView(); +}; + +#endif // ELALISTVIEW_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaLog.h b/3rdparty/ElaWidgetTools/src/include/ElaLog.h new file mode 100644 index 0000000..5188a1c --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaLog.h @@ -0,0 +1,29 @@ +#ifndef ELALOG_H +#define ELALOG_H + +#include + +#include "singleton.h" +#include "stdafx.h" + +class ElaLogPrivate; +class ELA_EXPORT ElaLog : public QObject +{ + Q_OBJECT + Q_Q_CREATE(ElaLog) + Q_PROPERTY_CREATE_Q_H(QString, LogSavePath) + Q_PROPERTY_CREATE_Q_H(QString, LogFileName) + Q_PROPERTY_CREATE_Q_H(bool, IsLogFileNameWithTime) + Q_SINGLETON_CREATE_H(ElaLog); + +private: + explicit ElaLog(QObject* parent = nullptr); + ~ElaLog(); + +public: + void initMessageLog(bool isEnable); +Q_SIGNALS: + void logMessage(QString log); +}; + +#endif // ELALOG_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaMenu.h b/3rdparty/ElaWidgetTools/src/include/ElaMenu.h new file mode 100644 index 0000000..be25212 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaMenu.h @@ -0,0 +1,40 @@ +#ifndef ELAMENU_H +#define ELAMENU_H + +#include +#include + +#include "Def.h" +#include "stdafx.h" +class ElaMenuPrivate; +class ELA_EXPORT ElaMenu : public QMenu +{ + Q_OBJECT + Q_Q_CREATE(ElaMenu) + +public: + explicit ElaMenu(QWidget* parent = nullptr); + explicit ElaMenu(const QString& title, QWidget* parent = nullptr); + ~ElaMenu(); + void setMenuItemHeight(int menuItemHeight); + int getMenuItemHeight() const; + + QAction* addMenu(QMenu* menu); + ElaMenu* addMenu(const QString& title); + ElaMenu* addMenu(const QIcon& icon, const QString& title); + ElaMenu* addMenu(ElaIconType::IconName icon, const QString& title); + + QAction* addElaIconAction(ElaIconType::IconName icon, const QString& text); + QAction* addElaIconAction(ElaIconType::IconName icon, const QString& text, const QKeySequence& shortcut); + + bool isHasChildMenu() const; + bool isHasIcon() const; +Q_SIGNALS: + Q_SIGNAL void menuShow(); + +protected: + virtual void showEvent(QShowEvent* event) override; + virtual void paintEvent(QPaintEvent* event) override; +}; + +#endif // ELAMENU_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaMenuBar.h b/3rdparty/ElaWidgetTools/src/include/ElaMenuBar.h new file mode 100644 index 0000000..4671e20 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaMenuBar.h @@ -0,0 +1,26 @@ +#ifndef ELAMENUBAR_H +#define ELAMENUBAR_H + +#include + +#include "Def.h" +#include "stdafx.h" + +class ElaMenu; +class ELA_EXPORT ElaMenuBar : public QMenuBar +{ + Q_OBJECT +public: + explicit ElaMenuBar(QWidget* parent = nullptr); + ~ElaMenuBar(); + + QAction* addMenu(QMenu* menu); + ElaMenu* addMenu(const QString& title); + ElaMenu* addMenu(const QIcon& icon, const QString& title); + ElaMenu* addMenu(ElaIconType::IconName, const QString& title); + + QAction* addElaIconAction(ElaIconType::IconName icon, const QString& text); + QAction* addElaIconAction(ElaIconType::IconName icon, const QString& text, const QKeySequence& shortcut); +}; + +#endif // ELAMENUBAR_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaMessageBar.h b/3rdparty/ElaWidgetTools/src/include/ElaMessageBar.h new file mode 100644 index 0000000..4e0b464 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaMessageBar.h @@ -0,0 +1,31 @@ +#ifndef ELAMESSAGEBAR_H +#define ELAMESSAGEBAR_H + +#include + +#include "Def.h" +#include "stdafx.h" + +class ElaMessageBarPrivate; +class ELA_EXPORT ElaMessageBar : public QWidget +{ + Q_OBJECT + Q_Q_CREATE(ElaMessageBar) + +public: + static void success(ElaMessageBarType::PositionPolicy policy, QString title, QString text, int displayMsec, QWidget* parent = nullptr); + static void warning(ElaMessageBarType::PositionPolicy policy, QString title, QString text, int displayMsec, QWidget* parent = nullptr); + static void information(ElaMessageBarType::PositionPolicy policy, QString title, QString text, int displayMsec, QWidget* parent = nullptr); + static void error(ElaMessageBarType::PositionPolicy policy, QString title, QString text, int displayMsec, QWidget* parent = nullptr); + +protected: + virtual void paintEvent(QPaintEvent* event) override; + virtual bool eventFilter(QObject* watched, QEvent* event) override; + +private: + friend class ElaMessageBarManager; + explicit ElaMessageBar(ElaMessageBarType::PositionPolicy policy, ElaMessageBarType::MessageMode messageMode, QString& title, QString& text, int displayMsec, QWidget* parent = nullptr); + ~ElaMessageBar(); +}; + +#endif // ELAMESSAGEBAR_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaMessageButton.h b/3rdparty/ElaWidgetTools/src/include/ElaMessageButton.h new file mode 100644 index 0000000..e95018b --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaMessageButton.h @@ -0,0 +1,33 @@ +#ifndef ELAMESSAGEBUTTON_H +#define ELAMESSAGEBUTTON_H + +#include +#include + +#include "Def.h" +#include "stdafx.h" +class ElaMessageButtonPrivate; +class ELA_EXPORT ElaMessageButton : public QPushButton +{ + Q_OBJECT + Q_Q_CREATE(ElaMessageButton) + Q_PROPERTY_CREATE_Q_H(int, BorderRadius) + Q_PROPERTY_CREATE_Q_H(QString, BarTitle); + Q_PROPERTY_CREATE_Q_H(QString, BarText); + Q_PROPERTY_CREATE_Q_H(int, DisplayMsec); + Q_PROPERTY_CREATE_Q_H(QWidget*, MessageTargetWidget) + Q_PROPERTY_CREATE_Q_H(ElaMessageBarType::MessageMode, MessageMode); + Q_PROPERTY_CREATE_Q_H(ElaMessageBarType::PositionPolicy, PositionPolicy); + +public: + explicit ElaMessageButton(QWidget* parent = nullptr); + explicit ElaMessageButton(QString text, QWidget* parent = nullptr); + ~ElaMessageButton(); + +protected: + virtual void mousePressEvent(QMouseEvent* event) override; + virtual void mouseReleaseEvent(QMouseEvent* event) override; + virtual void paintEvent(QPaintEvent* event) override; +}; + +#endif // ELAMESSAGEBUTTON_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaMultiSelectComboBox.h b/3rdparty/ElaWidgetTools/src/include/ElaMultiSelectComboBox.h new file mode 100644 index 0000000..02bdf4a --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaMultiSelectComboBox.h @@ -0,0 +1,31 @@ +#ifndef ELAMULTISELECTCOMBOBOX_H +#define ELAMULTISELECTCOMBOBOX_H +#include + +#include "stdafx.h" +class ElaMultiSelectComboBoxPrivate; +class ELA_EXPORT ElaMultiSelectComboBox : public QComboBox +{ + Q_OBJECT + Q_Q_CREATE(ElaMultiSelectComboBox) + Q_PROPERTY_CREATE_Q_H(int, BorderRadius) +public: + explicit ElaMultiSelectComboBox(QWidget* parent = nullptr); + ~ElaMultiSelectComboBox(); + void setCurrentSelection(QString selection); + void setCurrentSelection(QStringList selection); + void setCurrentSelection(int index); + void setCurrentSelection(QList selectionIndex); + QStringList getCurrentSelection() const; + QList getCurrentSelectionIndex() const; +Q_SIGNALS: + Q_SIGNAL void itemSelectionChanged(QVector itemSelection); + Q_SIGNAL void currentTextListChanged(QStringList selectedTextList); + +protected: + void paintEvent(QPaintEvent* e) override; + void showPopup() override; + void hidePopup() override; +}; + +#endif // ELAMULTISELECTCOMBOBOX_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaNavigationBar.h b/3rdparty/ElaWidgetTools/src/include/ElaNavigationBar.h new file mode 100644 index 0000000..d535b4f --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaNavigationBar.h @@ -0,0 +1,47 @@ +#ifndef ELANAVIGATIONBAR_H +#define ELANAVIGATIONBAR_H + +#include + +#include "Def.h" +#include "stdafx.h" + +class ElaNavigationBarPrivate; +class ELA_EXPORT ElaNavigationBar : public QWidget +{ + Q_OBJECT + Q_Q_CREATE(ElaNavigationBar) + Q_PROPERTY_CREATE_Q_H(bool, IsTransparent) +public: + explicit ElaNavigationBar(QWidget* parent = nullptr); + ~ElaNavigationBar(); + void setUserInfoCardVisible(bool isVisible); + void setUserInfoCardPixmap(QPixmap pix); + void setUserInfoCardTitle(QString title); + void setUserInfoCardSubTitle(QString subTitle); + + ElaNavigationType::NodeOperateReturnType addExpanderNode(QString expanderTitle, QString& expanderKey, ElaIconType::IconName awesome = ElaIconType::None); + ElaNavigationType::NodeOperateReturnType addExpanderNode(QString expanderTitle, QString& expanderKey, QString targetExpanderKey, ElaIconType::IconName awesome = ElaIconType::None); + ElaNavigationType::NodeOperateReturnType addPageNode(QString pageTitle, QWidget* page, ElaIconType::IconName awesome = ElaIconType::None); + ElaNavigationType::NodeOperateReturnType addPageNode(QString pageTitle, QWidget* page, QString targetExpanderKey, ElaIconType::IconName awesome = ElaIconType::None); + ElaNavigationType::NodeOperateReturnType addPageNode(QString pageTitle, QWidget* page, int keyPoints = 0, ElaIconType::IconName awesome = ElaIconType::None); + ElaNavigationType::NodeOperateReturnType addPageNode(QString pageTitle, QWidget* page, QString targetExpanderKey, int keyPoints = 0, ElaIconType::IconName awesome = ElaIconType::None); + ElaNavigationType::NodeOperateReturnType addFooterNode(QString footerTitle, QString& footerKey, int keyPoints = 0, ElaIconType::IconName awesome = ElaIconType::None); + ElaNavigationType::NodeOperateReturnType addFooterNode(QString footerTitle, QWidget* page, QString& footerKey, int keyPoints = 0, ElaIconType::IconName awesome = ElaIconType::None); + + void setNodeKeyPoints(QString nodeKey, int keyPoints); + int getNodeKeyPoints(QString nodeKey) const; + + void navigation(QString pageKey, bool isLogClicked = true); + void setDisplayMode(ElaNavigationType::NavigationDisplayMode displayMode, bool isAnimation = true); + +Q_SIGNALS: + Q_SIGNAL void userInfoCardClicked(); + Q_SIGNAL void navigationNodeClicked(ElaNavigationType::NavigationNodeType nodeType, QString nodeKey); + Q_SIGNAL void navigationNodeAdded(ElaNavigationType::NavigationNodeType nodeType, QString nodeKey, QWidget* page); + +protected: + virtual void paintEvent(QPaintEvent* event) override; +}; + +#endif // ELANAVIGATIONBAR_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaNavigationRouter.h b/3rdparty/ElaWidgetTools/src/include/ElaNavigationRouter.h new file mode 100644 index 0000000..887d00d --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaNavigationRouter.h @@ -0,0 +1,30 @@ +#ifndef ELANAVIGATIONROUTER_H +#define ELANAVIGATIONROUTER_H + +#include +#include + +#include "Def.h" +#include "singleton.h" +#include "stdafx.h" + +class ElaNavigationRouterPrivate; +class ELA_EXPORT ElaNavigationRouter : public QObject +{ + Q_OBJECT + Q_Q_CREATE(ElaNavigationRouter) + Q_SINGLETON_CREATE_H(ElaNavigationRouter) + Q_PROPERTY_CREATE_Q_H(int, MaxRouteCount) +private: + explicit ElaNavigationRouter(QObject* parent = nullptr); + ~ElaNavigationRouter(); + +Q_SIGNALS: + Q_SIGNAL void navigationRouterStateChanged(bool state); + +public: + ElaNavigationRouterType::NavigationRouteType navigationRoute(QObject* routeObject, QString routeFunctionName, const QVariantMap& routeData = {}, Qt::ConnectionType connectionType = Qt::AutoConnection); + void navigationRouteBack(); +}; + +#endif // ELANAVIGATIONROUTER_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaPivot.h b/3rdparty/ElaWidgetTools/src/include/ElaPivot.h new file mode 100644 index 0000000..6bed272 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaPivot.h @@ -0,0 +1,28 @@ +#ifndef ELAPIVOT_H +#define ELAPIVOT_H +#include + +#include "stdafx.h" + +class ElaPivotPrivate; +class ELA_EXPORT ElaPivot : public QWidget +{ + Q_OBJECT + Q_Q_CREATE(ElaPivot) + Q_PROPERTY_CREATE_Q_H(int, TextPixelSize) + Q_PROPERTY_CREATE_Q_H(int, CurrentIndex) + Q_PROPERTY_CREATE_Q_H(int, PivotSpacing) + Q_PROPERTY_CREATE_Q_H(int, MarkWidth) +public: + explicit ElaPivot(QWidget* parent = nullptr); + ~ElaPivot(); + + void appendPivot(QString pivotTitle); + void removePivot(QString pivotTitle); + +Q_SIGNALS: + Q_SIGNAL void pivotClicked(int index); + Q_SIGNAL void pivotDoubleClicked(int index); +}; + +#endif // ELAPIVOT_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaPlainTextEdit.h b/3rdparty/ElaWidgetTools/src/include/ElaPlainTextEdit.h new file mode 100644 index 0000000..67e2a15 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaPlainTextEdit.h @@ -0,0 +1,24 @@ +#ifndef ELAPLAINTEXTEDIT_H +#define ELAPLAINTEXTEDIT_H + +#include + +#include "stdafx.h" + +class ElaPlainTextEditPrivate; +class ELA_EXPORT ElaPlainTextEdit : public QPlainTextEdit +{ + Q_OBJECT + Q_Q_CREATE(ElaPlainTextEdit) +public: + explicit ElaPlainTextEdit(QWidget* parent = nullptr); + explicit ElaPlainTextEdit(const QString& text, QWidget* parent = nullptr); + virtual ~ElaPlainTextEdit(); + +protected: + virtual void focusInEvent(QFocusEvent* event) override; + virtual void focusOutEvent(QFocusEvent* event) override; + virtual void contextMenuEvent(QContextMenuEvent* event) override; +}; + +#endif // ELAPLAINTEXTEDIT_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaPopularCard.h b/3rdparty/ElaWidgetTools/src/include/ElaPopularCard.h new file mode 100644 index 0000000..e2bc526 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaPopularCard.h @@ -0,0 +1,34 @@ +#ifndef ELAPOPULARCARD_H +#define ELAPOPULARCARD_H + +#include +#include + +#include "stdafx.h" +class ElaPopularCardPrivate; +class ELA_EXPORT ElaPopularCard : public QWidget +{ + Q_OBJECT + Q_Q_CREATE(ElaPopularCard) + Q_PROPERTY_CREATE_Q_H(int, BorderRadius) + Q_PROPERTY_CREATE_Q_H(QPixmap, CardPixmap) + Q_PROPERTY_CREATE_Q_H(QString, Title) + Q_PROPERTY_CREATE_Q_H(QString, SubTitle) + Q_PROPERTY_CREATE_Q_H(QString, InteractiveTips) + Q_PROPERTY_CREATE_Q_H(QString, DetailedText) + Q_PROPERTY_CREATE_Q_H(QString, CardButtontext) + Q_PROPERTY_CREATE_Q_H(QWidget*, CardFloatArea) + Q_PROPERTY_CREATE_Q_H(QPixmap, CardFloatPixmap) +public: + explicit ElaPopularCard(QWidget* parent = nullptr); + ~ElaPopularCard(); +Q_SIGNALS: + Q_SIGNAL void popularCardClicked(); + Q_SIGNAL void popularCardButtonClicked(); + +protected: + virtual bool event(QEvent* event) override; + virtual void paintEvent(QPaintEvent* event) override; +}; + +#endif // ELAPOPULARCARD_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaProgressBar.h b/3rdparty/ElaWidgetTools/src/include/ElaProgressBar.h new file mode 100644 index 0000000..345d809 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaProgressBar.h @@ -0,0 +1,24 @@ +#ifndef ELAPROGRESSBAR_H +#define ELAPROGRESSBAR_H + +#include + +#include "stdafx.h" + +class ElaProgressBarPrivate; +class ELA_EXPORT ElaProgressBar : public QProgressBar +{ + Q_OBJECT + Q_Q_CREATE(ElaProgressBar) +public: + explicit ElaProgressBar(QWidget* parent = nullptr); + ~ElaProgressBar(); + void setMinimum(int minimum); + void setMaximum(int maximum); + +protected: + virtual void paintEvent(QPaintEvent* event) override; + virtual void resizeEvent(QResizeEvent* event) override; +}; + +#endif // ELAPROGRESSBAR_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaPromotionCard.h b/3rdparty/ElaWidgetTools/src/include/ElaPromotionCard.h new file mode 100644 index 0000000..60b3799 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaPromotionCard.h @@ -0,0 +1,39 @@ +#ifndef ELAPROMOTIONCARD_H +#define ELAPROMOTIONCARD_H +#include + +#include "stdafx.h" +class ElaPromotionCardPrivate; +class ELA_EXPORT ElaPromotionCard : public QWidget +{ + Q_OBJECT + Q_Q_CREATE(ElaPromotionCard) + Q_PROPERTY_CREATE_Q_H(int, BorderRadius) + Q_PROPERTY_CREATE_Q_H(QPixmap, CardPixmap) + Q_PROPERTY_CREATE_Q_H(QString, CardTitle) + Q_PROPERTY_CREATE_Q_H(QString, PromotionTitle) + Q_PROPERTY_CREATE_Q_H(QString, Title) + Q_PROPERTY_CREATE_Q_H(QString, SubTitle) + Q_PROPERTY_CREATE_Q_H(QColor, CardTitleColor) + Q_PROPERTY_CREATE_Q_H(QColor, PromotionTitleColor) + Q_PROPERTY_CREATE_Q_H(QColor, PromotionTitleBaseColor) + Q_PROPERTY_CREATE_Q_H(QColor, TitleColor) + Q_PROPERTY_CREATE_Q_H(QColor, SubTitleColor) + Q_PROPERTY_CREATE_Q_H(int, CardTitlePixelSize) + Q_PROPERTY_CREATE_Q_H(int, PromotionTitlePixelSize) + Q_PROPERTY_CREATE_Q_H(int, TitlePixelSize) + Q_PROPERTY_CREATE_Q_H(int, SubTitlePixelSize) + Q_PROPERTY_CREATE_Q_H(qreal, HorizontalCardPixmapRatio) + Q_PROPERTY_CREATE_Q_H(qreal, VerticalCardPixmapRatio) +public: + explicit ElaPromotionCard(QWidget* parent = nullptr); + ~ElaPromotionCard(); +Q_SIGNALS: + Q_SIGNAL void promotionCardClicked(); + +protected: + virtual bool event(QEvent* event) override; + virtual void paintEvent(QPaintEvent* event) override; +}; + +#endif // ELAPROMOTIONCARD_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaPromotionView.h b/3rdparty/ElaWidgetTools/src/include/ElaPromotionView.h new file mode 100644 index 0000000..a0b4744 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaPromotionView.h @@ -0,0 +1,30 @@ +#ifndef ELAPROMOTIONVIEW_H +#define ELAPROMOTIONVIEW_H + +#include +#include + +#include "stdafx.h" +class ElaPromotionCard; +class ElaPromotionViewPrivate; +class ELA_EXPORT ElaPromotionView : public QWidget +{ + Q_OBJECT + Q_Q_CREATE(ElaPromotionView) + Q_PROPERTY_CREATE_Q_H(int, CardExpandWidth) + Q_PROPERTY_CREATE_Q_H(int, CardCollapseWidth) + Q_PROPERTY_CREATE_Q_H(int, CurrentIndex) + Q_PROPERTY_CREATE_Q_H(bool, IsAutoScroll) + Q_PROPERTY_CREATE_Q_H(int, AutoScrollInterval) +public: + explicit ElaPromotionView(QWidget* parent = nullptr); + ~ElaPromotionView(); + + void appendPromotionCard(ElaPromotionCard* card); + +protected: + virtual void wheelEvent(QWheelEvent* event) override; + virtual void paintEvent(QPaintEvent* event) override; +}; + +#endif // ELAPROMOTIONVIEW_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaPushButton.h b/3rdparty/ElaWidgetTools/src/include/ElaPushButton.h new file mode 100644 index 0000000..1cb9f71 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaPushButton.h @@ -0,0 +1,36 @@ +#ifndef ELAPUSHBUTTON_H +#define ELAPUSHBUTTON_H + +#include + +#include "stdafx.h" +class ElaPushButtonPrivate; +class ELA_EXPORT ElaPushButton : public QPushButton +{ + Q_OBJECT + Q_Q_CREATE(ElaPushButton) + Q_PROPERTY_CREATE_Q_H(int, BorderRadius) + Q_PROPERTY_CREATE_Q_H(QColor, LightDefaultColor) + Q_PROPERTY_CREATE_Q_H(QColor, DarkDefaultColor) + Q_PROPERTY_CREATE_Q_H(QColor, LightHoverColor) + Q_PROPERTY_CREATE_Q_H(QColor, DarkHoverColor) + Q_PROPERTY_CREATE_Q_H(QColor, LightPressColor) + Q_PROPERTY_CREATE_Q_H(QColor, DarkPressColor) +public: + explicit ElaPushButton(QWidget* parent = nullptr); + explicit ElaPushButton(QString text, QWidget* parent = nullptr); + ~ElaPushButton(); + + void setLightTextColor(QColor color); + QColor getLightTextColor() const; + + void setDarkTextColor(QColor color); + QColor getDarkTextColor() const; + +protected: + virtual void mousePressEvent(QMouseEvent* event) override; + virtual void mouseReleaseEvent(QMouseEvent* event) override; + virtual void paintEvent(QPaintEvent* event) override; +}; + +#endif // ELAPUSHBUTTON_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaRadioButton.h b/3rdparty/ElaWidgetTools/src/include/ElaRadioButton.h new file mode 100644 index 0000000..abca875 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaRadioButton.h @@ -0,0 +1,18 @@ +#ifndef ELARADIOBUTTON_H +#define ELARADIOBUTTON_H + +#include + +#include "stdafx.h" +class ElaRadioButtonPrivate; +class ELA_EXPORT ElaRadioButton : public QRadioButton +{ + Q_OBJECT + Q_Q_CREATE(ElaRadioButton) +public: + explicit ElaRadioButton(QWidget* parent = nullptr); + explicit ElaRadioButton(const QString& text, QWidget* parent = nullptr); + ~ElaRadioButton(); +}; + +#endif // ELARADIOBUTTON_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaReminderCard.h b/3rdparty/ElaWidgetTools/src/include/ElaReminderCard.h new file mode 100644 index 0000000..dde3acf --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaReminderCard.h @@ -0,0 +1,32 @@ +#ifndef ELAREMINDERCARD_H +#define ELAREMINDERCARD_H + +#include + +#include "Def.h" +class ElaReminderCardPrivate; +class ELA_EXPORT ElaReminderCard : public QPushButton +{ + Q_OBJECT + Q_Q_CREATE(ElaReminderCard) + Q_PROPERTY_CREATE_Q_H(int, BorderRadius) + Q_PROPERTY_CREATE_Q_H(QString, Title); + Q_PROPERTY_CREATE_Q_H(QString, SubTitle); + Q_PROPERTY_CREATE_Q_H(int, TitlePixelSize); + Q_PROPERTY_CREATE_Q_H(int, SubTitlePixelSize); + Q_PROPERTY_CREATE_Q_H(int, TitleSpacing); + Q_PROPERTY_CREATE_Q_H(QPixmap, CardPixmap); + Q_PROPERTY_CREATE_Q_H(QSize, CardPixmapSize); + Q_PROPERTY_CREATE_Q_H(int, CardPixmapBorderRadius) + Q_PROPERTY_CREATE_Q_H(ElaCardPixType::PixMode, CardPixMode); + +public: + explicit ElaReminderCard(QWidget* parent = nullptr); + ~ElaReminderCard(); + void setCardPixmapSize(int width, int height); + +protected: + void paintEvent(QPaintEvent* event) override; +}; + +#endif // ELAREMINDERCARD_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaScrollArea.h b/3rdparty/ElaWidgetTools/src/include/ElaScrollArea.h new file mode 100644 index 0000000..40ab107 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaScrollArea.h @@ -0,0 +1,26 @@ +#ifndef ELASCROLLAREA_H +#define ELASCROLLAREA_H + +#include + +#include "stdafx.h" + +class ElaScrollAreaPrivate; +class ELA_EXPORT ElaScrollArea : public QScrollArea +{ + Q_OBJECT + Q_Q_CREATE(ElaScrollArea) +public: + explicit ElaScrollArea(QWidget* parent = nullptr); + ~ElaScrollArea(); + + void setIsGrabGesture(bool isEnable, qreal mousePressEventDelay = 0.5); + + void setIsOverShoot(Qt::Orientation orientation, bool isEnable); + bool getIsOverShoot(Qt::Orientation orientation) const; + + void setIsAnimation(Qt::Orientation orientation, bool isAnimation); + bool getIsAnimation(Qt::Orientation orientation) const; +}; + +#endif // ELASCROLLAREA_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaScrollBar.h b/3rdparty/ElaWidgetTools/src/include/ElaScrollBar.h new file mode 100644 index 0000000..e96d2e6 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaScrollBar.h @@ -0,0 +1,35 @@ +#ifndef ELASCROLLBAR_H +#define ELASCROLLBAR_H + +#include +#include + +#include "stdafx.h" + +class ElaScrollBarPrivate; +class ELA_EXPORT ElaScrollBar : public QScrollBar +{ + Q_OBJECT + Q_Q_CREATE(ElaScrollBar) + Q_PROPERTY_CREATE_Q_H(bool, IsAnimation) + Q_PROPERTY_CREATE_Q_H(qreal, SpeedLimit) +public: + explicit ElaScrollBar(QWidget* parent = nullptr); + explicit ElaScrollBar(Qt::Orientation orientation, QWidget* parent = nullptr); + explicit ElaScrollBar(QScrollBar* originScrollBar, QAbstractScrollArea* parent = nullptr); + ~ElaScrollBar(); + +Q_SIGNALS: + Q_SIGNAL void rangeAnimationFinished(); + +protected: + virtual bool event(QEvent* event) override; + virtual bool eventFilter(QObject* watched, QEvent* event) override; + virtual void mousePressEvent(QMouseEvent* event) override; + virtual void mouseReleaseEvent(QMouseEvent* event) override; + virtual void mouseMoveEvent(QMouseEvent* event) override; + virtual void wheelEvent(QWheelEvent* event) override; + virtual void contextMenuEvent(QContextMenuEvent* event) override; +}; + +#endif // ELASCROLLBAR_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaScrollPage.h b/3rdparty/ElaWidgetTools/src/include/ElaScrollPage.h new file mode 100644 index 0000000..fdab235 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaScrollPage.h @@ -0,0 +1,29 @@ +#ifndef ELASCROLLPAGE_H +#define ELASCROLLPAGE_H + +#include + +#include "stdafx.h" +class ElaScrollArea; +class ElaText; +class QHBoxLayout; +class ElaScrollPagePrivate; +class ELA_EXPORT ElaScrollPage : public QWidget +{ + Q_OBJECT + Q_Q_CREATE(ElaScrollPage) + Q_PROPERTY_CREATE_Q_H(QWidget*, CustomWidget) +public: + explicit ElaScrollPage(QWidget* parent = nullptr); + ~ElaScrollPage(); + + void addCentralWidget(QWidget* centralWidget, bool isWidgetResizeable = true, bool isVerticalGrabGesture = true, qreal mousePressEventDelay = 0.5); + + void navigation(int widgetIndex, bool isLogRoute = true); + + void setPageTitleSpacing(int spacing); + int getPageTitleSpacing() const; + void setTitleVisible(bool isVisible); +}; + +#endif // ELASCROLLPAGE_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaScrollPageArea.h b/3rdparty/ElaWidgetTools/src/include/ElaScrollPageArea.h new file mode 100644 index 0000000..05e562b --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaScrollPageArea.h @@ -0,0 +1,21 @@ +#ifndef ELASCROLLPAGEAREA_H +#define ELASCROLLPAGEAREA_H + +#include + +#include "stdafx.h" +class ElaScrollPageAreaPrivate; +class ELA_EXPORT ElaScrollPageArea : public QWidget +{ + Q_OBJECT + Q_Q_CREATE(ElaScrollPageArea) + Q_PROPERTY_CREATE_Q_H(int, BorderRadius) +public: + explicit ElaScrollPageArea(QWidget* parent = nullptr); + ~ElaScrollPageArea(); + +protected: + virtual void paintEvent(QPaintEvent* event) override; +}; + +#endif // ELASCROLLPAGEAREA_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaSlider.h b/3rdparty/ElaWidgetTools/src/include/ElaSlider.h new file mode 100644 index 0000000..7089e51 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaSlider.h @@ -0,0 +1,16 @@ +#ifndef ELASLIDER_H +#define ELASLIDER_H + +#include + +#include "stdafx.h" +class ELA_EXPORT ElaSlider : public QSlider +{ + Q_OBJECT +public: + explicit ElaSlider(QWidget* parent = nullptr); + explicit ElaSlider(Qt::Orientation orientation, QWidget* parent = nullptr); + ~ElaSlider(); +}; + +#endif // ELASLIDER_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaSpinBox.h b/3rdparty/ElaWidgetTools/src/include/ElaSpinBox.h new file mode 100644 index 0000000..03e6849 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaSpinBox.h @@ -0,0 +1,21 @@ +#ifndef ELASPINBOX_H +#define ELASPINBOX_H + +#include + +#include "stdafx.h" + +class ElaSpinBoxPrivate; +class ELA_EXPORT ElaSpinBox : public QSpinBox +{ + Q_OBJECT + Q_Q_CREATE(ElaSpinBox) +public: + explicit ElaSpinBox(QWidget* parent = nullptr); + ~ElaSpinBox(); + +protected: + void contextMenuEvent(QContextMenuEvent* event) override; +}; + +#endif // ELASPINBOX_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaStatusBar.h b/3rdparty/ElaWidgetTools/src/include/ElaStatusBar.h new file mode 100644 index 0000000..3ec6625 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaStatusBar.h @@ -0,0 +1,15 @@ +#ifndef ELASTATUSBAR_H +#define ELASTATUSBAR_H + +#include + +#include "stdafx.h" +class ELA_EXPORT ElaStatusBar : public QStatusBar +{ + Q_OBJECT +public: + explicit ElaStatusBar(QWidget* parent = nullptr); + ~ElaStatusBar(); +}; + +#endif // ELASTATUSBAR_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaSuggestBox.h b/3rdparty/ElaWidgetTools/src/include/ElaSuggestBox.h new file mode 100644 index 0000000..7c646f2 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaSuggestBox.h @@ -0,0 +1,30 @@ +#ifndef ELASUGGESTBOX_H +#define ELASUGGESTBOX_H + +#include +#include + +#include "Def.h" +#include "stdafx.h" + +class ElaSuggestBoxPrivate; +class ELA_EXPORT ElaSuggestBox : public QWidget +{ + Q_OBJECT + Q_Q_CREATE(ElaSuggestBox) + Q_PROPERTY_CREATE_Q_H(int, BorderRadius) + Q_PROPERTY_CREATE_Q_H(Qt::CaseSensitivity, CaseSensitivity) +public: + explicit ElaSuggestBox(QWidget* parent = nullptr); + ~ElaSuggestBox(); + void setPlaceholderText(const QString& placeholderText); + + void addSuggestion(const QString& suggestText, const QVariantMap& suggestData = {}); + void addSuggestion(ElaIconType::IconName icon, const QString& suggestText, const QVariantMap& suggestData = {}); + void removeSuggestion(const QString& suggestText); + void removeSuggestion(int index); +Q_SIGNALS: + Q_SIGNAL void suggestionClicked(QString suggestText, QVariantMap suggestData); +}; + +#endif // ELASUGGESTBOX_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaTabBar.h b/3rdparty/ElaWidgetTools/src/include/ElaTabBar.h new file mode 100644 index 0000000..20bbcec --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaTabBar.h @@ -0,0 +1,28 @@ +#ifndef ELATABBAR_H +#define ELATABBAR_H + +#include +#include + +#include "stdafx.h" +class ElaTabBarPrivate; +class ELA_EXPORT ElaTabBar : public QTabBar +{ + Q_OBJECT + Q_Q_CREATE(ElaTabBar) +public: + explicit ElaTabBar(QWidget* parent = nullptr); + ~ElaTabBar(); +Q_SIGNALS: + Q_SIGNAL void tabBarPress(int index); + Q_SIGNAL void tabDragCreate(QDrag* drag); + Q_SIGNAL void tabDragDrop(const QMimeData* mimeData); + +protected: + virtual void mousePressEvent(QMouseEvent* event) override; + virtual void mouseMoveEvent(QMouseEvent* event) override; + virtual void dragEnterEvent(QDragEnterEvent* event) override; + virtual void dropEvent(QDropEvent* event) override; +}; + +#endif // ELATABBAR_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaTabWidget.h b/3rdparty/ElaWidgetTools/src/include/ElaTabWidget.h new file mode 100644 index 0000000..cf0737c --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaTabWidget.h @@ -0,0 +1,26 @@ +#ifndef ELATABWIDGET_H +#define ELATABWIDGET_H + +#include + +#include "stdafx.h" + +class ElaTabWidgetPrivate; +class ELA_EXPORT ElaTabWidget : public QTabWidget +{ + Q_OBJECT + Q_Q_CREATE(ElaTabWidget) + Q_PROPERTY_CREATE(bool, IsTabTransparent); + +public: + explicit ElaTabWidget(QWidget* parent = nullptr); + ~ElaTabWidget(); + void setTabPosition(TabPosition position); + +protected: + virtual void paintEvent(QPaintEvent* event) override; + virtual void dragEnterEvent(QDragEnterEvent* event) override; + virtual void dropEvent(QDropEvent* event) override; +}; + +#endif // ELATABWIDGET_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaTableView.h b/3rdparty/ElaWidgetTools/src/include/ElaTableView.h new file mode 100644 index 0000000..c19ca2f --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaTableView.h @@ -0,0 +1,29 @@ +#ifndef ELATABLEVIEW_H +#define ELATABLEVIEW_H + +#include + +#include "stdafx.h" + +class ElaTableViewPrivate; +class ELA_EXPORT ElaTableView : public QTableView +{ + Q_OBJECT + Q_Q_CREATE(ElaTableView) + Q_PROPERTY_CREATE_Q_H(int, HeaderMargin) +public: + explicit ElaTableView(QWidget* parent = nullptr); + ~ElaTableView(); + +Q_SIGNALS: + Q_SIGNAL void tableViewShow(); + Q_SIGNAL void tableViewHide(); + +protected: + virtual void showEvent(QShowEvent* event) override; + virtual void hideEvent(QHideEvent* event) override; + virtual void mouseMoveEvent(QMouseEvent* event) override; + virtual void leaveEvent(QEvent* event) override; +}; + +#endif // ELATABLEVIEW_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaText.h b/3rdparty/ElaWidgetTools/src/include/ElaText.h new file mode 100644 index 0000000..e709304 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaText.h @@ -0,0 +1,32 @@ +#ifndef ELATEXT_H +#define ELATEXT_H + +#include + +#include "Def.h" +#include "stdafx.h" +class ElaTextPrivate; +class ELA_EXPORT ElaText : public QLabel +{ + Q_OBJECT + Q_Q_CREATE(ElaText) +public: + explicit ElaText(QWidget* parent = nullptr); + explicit ElaText(QString text, QWidget* parent = nullptr); + explicit ElaText(QString text, int pixelSize, QWidget* parent = nullptr); + ~ElaText(); + void setIsWrapAnywhere(bool isWrapAnywhere); + bool getIsWrapAnywhere() const; + + void setTextPixelSize(int size); + int getTextPixelSize() const; + void setTextPointSize(int size); + int getTextPointSize() const; + void setTextStyle(ElaTextType::TextStyle textStyle); + ElaTextType::TextStyle getTextStyle() const; + +protected: + virtual void paintEvent(QPaintEvent* event) override; +}; + +#endif // ELATEXT_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaTheme.h b/3rdparty/ElaWidgetTools/src/include/ElaTheme.h new file mode 100644 index 0000000..c7dc9e0 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaTheme.h @@ -0,0 +1,35 @@ +#ifndef ELATHEME_H +#define ELATHEME_H + +#include + +#include "Def.h" +#include "singleton.h" +#include "stdafx.h" + +#define eTheme ElaTheme::getInstance() +#define ElaThemeColor(themeMode, themeColor) eTheme->getThemeColor(themeMode, ElaThemeType::themeColor) +class QPainter; +class ElaThemePrivate; +class ELA_EXPORT ElaTheme : public QObject +{ + Q_OBJECT + Q_Q_CREATE(ElaTheme) + Q_SINGLETON_CREATE_H(ElaTheme) +private: + explicit ElaTheme(QObject* parent = nullptr); + ~ElaTheme(); + +public: + void setThemeMode(ElaThemeType::ThemeMode themeMode); + ElaThemeType::ThemeMode getThemeMode() const; + + void drawEffectShadow(QPainter* painter, QRect widgetRect, int shadowBorderWidth, int borderRadius); + + void setThemeColor(ElaThemeType::ThemeMode themeMode, ElaThemeType::ThemeColor themeColor, QColor newColor); + const QColor& getThemeColor(ElaThemeType::ThemeMode themeMode, ElaThemeType::ThemeColor themeColor); +Q_SIGNALS: + Q_SIGNAL void themeModeChanged(ElaThemeType::ThemeMode themeMode); +}; + +#endif // ELATHEME_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaToggleButton.h b/3rdparty/ElaWidgetTools/src/include/ElaToggleButton.h new file mode 100644 index 0000000..d6306bd --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaToggleButton.h @@ -0,0 +1,31 @@ +#ifndef ELATOGGLEBUTTON_H +#define ELATOGGLEBUTTON_H + +#include + +#include "stdafx.h" +class ElaToggleButtonPrivate; +class ELA_EXPORT ElaToggleButton : public QWidget +{ + Q_OBJECT + Q_Q_CREATE(ElaToggleButton) + Q_PROPERTY_CREATE_Q_H(int, BorderRadius) + Q_PROPERTY_CREATE_Q_H(QString, Text) +public: + explicit ElaToggleButton(QWidget* parent = nullptr); + explicit ElaToggleButton(QString text, QWidget* parent = nullptr); + ~ElaToggleButton(); + + void setIsToggled(bool isToggled); + bool getIsToggled() const; +Q_SIGNALS: + Q_SIGNAL void toggled(bool checked); + +protected: + virtual bool event(QEvent* event) override; + virtual void mousePressEvent(QMouseEvent* event) override; + virtual void mouseReleaseEvent(QMouseEvent* event) override; + virtual void paintEvent(QPaintEvent* event) override; +}; + +#endif // ELATOGGLEBUTTON_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaToggleSwitch.h b/3rdparty/ElaWidgetTools/src/include/ElaToggleSwitch.h new file mode 100644 index 0000000..ea6102a --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaToggleSwitch.h @@ -0,0 +1,29 @@ +#ifndef ELATOGGLESWITCH_H +#define ELATOGGLESWITCH_H + +#include + +#include "stdafx.h" +class ElaToggleSwitchPrivate; +class ELA_EXPORT ElaToggleSwitch : public QWidget +{ + Q_OBJECT + Q_Q_CREATE(ElaToggleSwitch); + +public: + explicit ElaToggleSwitch(QWidget* parent = nullptr); + ~ElaToggleSwitch(); + void setIsToggled(bool isToggled); + bool getIsToggled() const; +Q_SIGNALS: + Q_SIGNAL void toggled(bool checked); + +protected: + virtual bool event(QEvent* event) override; + virtual void mousePressEvent(QMouseEvent* event) override; + virtual void mouseReleaseEvent(QMouseEvent* event) override; + virtual void mouseMoveEvent(QMouseEvent* event) override; + virtual void paintEvent(QPaintEvent* event) override; +}; + +#endif // ELATOGGLESWITCH_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaToolBar.h b/3rdparty/ElaWidgetTools/src/include/ElaToolBar.h new file mode 100644 index 0000000..19aa8a3 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaToolBar.h @@ -0,0 +1,27 @@ +#ifndef ELATOOLBAR_H +#define ELATOOLBAR_H + +#include + +#include "Def.h" +class ElaToolBarPrivate; +class ELA_EXPORT ElaToolBar : public QToolBar +{ + Q_OBJECT + Q_Q_CREATE(ElaToolBar) +public: + explicit ElaToolBar(QWidget* parent = nullptr); + explicit ElaToolBar(const QString& title, QWidget* parent = nullptr); + ~ElaToolBar(); + + void setToolBarSpacing(int spacing); + int getToolBarSpacing() const; + + QAction* addElaIconAction(ElaIconType::IconName icon, const QString& text); + QAction* addElaIconAction(ElaIconType::IconName icon, const QString& text, const QKeySequence& shortcut); + +protected: + virtual void paintEvent(QPaintEvent* event) override; +}; + +#endif // ELATOOLBAR_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaToolButton.h b/3rdparty/ElaWidgetTools/src/include/ElaToolButton.h new file mode 100644 index 0000000..91051b1 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaToolButton.h @@ -0,0 +1,30 @@ +#ifndef ELATOOLBUTTON_H +#define ELATOOLBUTTON_H + +#include + +#include "Def.h" +class ElaMenu; +class ElaToolButtonPrivate; +class ELA_EXPORT ElaToolButton : public QToolButton +{ + Q_OBJECT + Q_Q_CREATE(ElaToolButton) + Q_PROPERTY_CREATE_Q_H(int, BorderRadius); + Q_PROPERTY_CREATE_Q_H(bool, IsSelected); + +public: + explicit ElaToolButton(QWidget* parent = nullptr); + ~ElaToolButton(); + + void setIsTransparent(bool isTransparent); + bool getIsTransparent() const; + + void setMenu(ElaMenu* menu); + void setElaIcon(ElaIconType::IconName icon); + +protected: + virtual bool eventFilter(QObject* watched, QEvent* event) override; +}; + +#endif // ELATOOLBUTTON_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaToolTip.h b/3rdparty/ElaWidgetTools/src/include/ElaToolTip.h new file mode 100644 index 0000000..9d92d65 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaToolTip.h @@ -0,0 +1,26 @@ +#ifndef ELATOOLTIP_H +#define ELATOOLTIP_H + +#include + +#include "stdafx.h" +class ElaToolTipPrivate; +class ELA_EXPORT ElaToolTip : public QWidget +{ + Q_OBJECT + Q_Q_CREATE(ElaToolTip) + Q_PROPERTY_CREATE_Q_H(int, BorderRadius) + Q_PROPERTY_CREATE_Q_H(int, DisplayMsec) + Q_PROPERTY_CREATE_Q_H(int, ShowDelayMsec) + Q_PROPERTY_CREATE_Q_H(int, HideDelayMsec) + Q_PROPERTY_CREATE_Q_H(QString, ToolTip) + Q_PROPERTY_CREATE_Q_H(QWidget*, CustomWidget) +public: + explicit ElaToolTip(QWidget* parent = nullptr); + ~ElaToolTip(); + +protected: + virtual void paintEvent(QPaintEvent* event); +}; + +#endif // ELATOOLTIP_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaTreeView.h b/3rdparty/ElaWidgetTools/src/include/ElaTreeView.h new file mode 100644 index 0000000..6b0fab8 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaTreeView.h @@ -0,0 +1,20 @@ +#ifndef ELATREEVIEW_H +#define ELATREEVIEW_H + +#include + +#include "stdafx.h" + +class ElaTreeViewPrivate; +class ELA_EXPORT ElaTreeView : public QTreeView +{ + Q_OBJECT + Q_Q_CREATE(ElaTreeView) + Q_PROPERTY_CREATE_Q_H(int, ItemHeight) + Q_PROPERTY_CREATE_Q_H(int, HeaderMargin) +public: + explicit ElaTreeView(QWidget* parent = nullptr); + ~ElaTreeView(); +}; + +#endif // ELATREEVIEW_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaWidget.h b/3rdparty/ElaWidgetTools/src/include/ElaWidget.h new file mode 100644 index 0000000..c3f75c8 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaWidget.h @@ -0,0 +1,36 @@ +#ifndef ELAWIDGET_H +#define ELAWIDGET_H + +#include + +#include "Def.h" +#include "ElaAppBar.h" +class ElaWidgetPrivate; +class ELA_EXPORT ElaWidget : public QWidget +{ + Q_OBJECT + Q_Q_CREATE(ElaWidget) + Q_PROPERTY_CREATE_Q_H(bool, IsStayTop) + Q_PROPERTY_CREATE_Q_H(bool, IsFixedSize) + Q_PROPERTY_CREATE_Q_H(bool, IsDefaultClosed) + Q_PROPERTY_CREATE_Q_H(int, AppBarHeight) + Q_TAKEOVER_NATIVEEVENT_H +public: + explicit ElaWidget(QWidget* parent = nullptr); + ~ElaWidget(); + void moveToCenter(); + + void setWindowButtonFlag(ElaAppBarType::ButtonType buttonFlag, bool isEnable = true); + void setWindowButtonFlags(ElaAppBarType::ButtonFlags buttonFlags); + ElaAppBarType::ButtonFlags getWindowButtonFlags() const; +Q_SIGNALS: + Q_SIGNAL void routeBackButtonClicked(); + Q_SIGNAL void navigationButtonClicked(); + Q_SIGNAL void themeChangeButtonClicked(); + Q_SIGNAL void closeButtonClicked(); + +protected: + virtual void paintEvent(QPaintEvent* event) override; +}; + +#endif // ELAWIDGET_H diff --git a/3rdparty/ElaWidgetTools/src/include/ElaWidgetTools.qrc b/3rdparty/ElaWidgetTools/src/include/ElaWidgetTools.qrc new file mode 100644 index 0000000..6a24d08 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaWidgetTools.qrc @@ -0,0 +1,9 @@ + + + Font/ElaAwesome.ttf + Image/Cirno.jpg + Image/Moon.jpg + Image/noise.png + Image/MicaBase.png + + diff --git a/3rdparty/ElaWidgetTools/src/include/ElaWindow.h b/3rdparty/ElaWidgetTools/src/include/ElaWindow.h new file mode 100644 index 0000000..d0fff72 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/ElaWindow.h @@ -0,0 +1,66 @@ +#ifndef ELAWINDOW_H +#define ELAWINDOW_H + +#include + +#include "Def.h" +#include "ElaAppBar.h" +#include "stdafx.h" +class ElaWindowPrivate; +class ELA_EXPORT ElaWindow : public QMainWindow +{ + Q_OBJECT + Q_Q_CREATE(ElaWindow) + Q_PROPERTY_CREATE_Q_H(bool, IsStayTop) + Q_PROPERTY_CREATE_Q_H(bool, IsFixedSize) + Q_PROPERTY_CREATE_Q_H(bool, IsDefaultClosed) + Q_PROPERTY_CREATE_Q_H(int, AppBarHeight) + Q_PROPERTY_CREATE_Q_H(int, CustomWidgetMaximumWidth) + Q_PROPERTY_CREATE_Q_H(int, ThemeChangeTime) + Q_PROPERTY_CREATE_Q_H(bool, IsCentralStackedWidgetTransparent) + Q_PROPERTY_CREATE_Q_H(ElaNavigationType::NavigationDisplayMode, NavigationBarDisplayMode) + Q_TAKEOVER_NATIVEEVENT_H +public: + explicit ElaWindow(QWidget* parent = nullptr); + ~ElaWindow(); + + void moveToCenter(); + + void setCustomWidget(ElaAppBarType::CustomArea customArea, QWidget* customWidget); + QWidget* getCustomWidget() const; + void setIsNavigationBarEnable(bool isEnable); + bool getIsNavigationBarEnable() const; + void setUserInfoCardVisible(bool isVisible); + void setUserInfoCardPixmap(QPixmap pix); + void setUserInfoCardTitle(QString title); + void setUserInfoCardSubTitle(QString subTitle); + ElaNavigationType::NodeOperateReturnType addExpanderNode(QString expanderTitle, QString& expanderKey, ElaIconType::IconName awesome = ElaIconType::None) const; + ElaNavigationType::NodeOperateReturnType addExpanderNode(QString expanderTitle, QString& expanderKey, QString targetExpanderKey, ElaIconType::IconName awesome = ElaIconType::None) const; + ElaNavigationType::NodeOperateReturnType addPageNode(QString pageTitle, QWidget* page, ElaIconType::IconName awesome = ElaIconType::None) const; + ElaNavigationType::NodeOperateReturnType addPageNode(QString pageTitle, QWidget* page, QString targetExpanderKey, ElaIconType::IconName awesome = ElaIconType::None) const; + ElaNavigationType::NodeOperateReturnType addPageNode(QString pageTitle, QWidget* page, int keyPoints = 0, ElaIconType::IconName awesome = ElaIconType::None) const; + ElaNavigationType::NodeOperateReturnType addPageNode(QString pageTitle, QWidget* page, QString targetExpanderKey, int keyPoints = 0, ElaIconType::IconName awesome = ElaIconType::None) const; + ElaNavigationType::NodeOperateReturnType addFooterNode(QString footerTitle, QString& footerKey, int keyPoints = 0, ElaIconType::IconName awesome = ElaIconType::None) const; + ElaNavigationType::NodeOperateReturnType addFooterNode(QString footerTitle, QWidget* page, QString& footerKey, int keyPoints = 0, ElaIconType::IconName awesome = ElaIconType::None) const; + + void setNodeKeyPoints(QString nodeKey, int keyPoints); + int getNodeKeyPoints(QString nodeKey) const; + + void navigation(QString pageKey); + void setWindowButtonFlag(ElaAppBarType::ButtonType buttonFlag, bool isEnable = true); + void setWindowButtonFlags(ElaAppBarType::ButtonFlags buttonFlags); + ElaAppBarType::ButtonFlags getWindowButtonFlags() const; + + void closeWindow(); +Q_SIGNALS: + Q_SIGNAL void userInfoCardClicked(); + Q_SIGNAL void closeButtonClicked(); + Q_SIGNAL void navigationNodeClicked(ElaNavigationType::NavigationNodeType nodeType, QString nodeKey); + Q_SIGNAL void customWidgetChanged(); + +protected: + virtual bool eventFilter(QObject* watched, QEvent* event) override; + virtual QMenu* createPopupMenu() override; +}; + +#endif // ELAWINDOW_H diff --git a/3rdparty/ElaWidgetTools/src/include/Font/ElaAwesome.ttf b/3rdparty/ElaWidgetTools/src/include/Font/ElaAwesome.ttf new file mode 100644 index 0000000..a2d3c33 Binary files /dev/null and b/3rdparty/ElaWidgetTools/src/include/Font/ElaAwesome.ttf differ diff --git a/3rdparty/ElaWidgetTools/src/include/Image/Cirno.jpg b/3rdparty/ElaWidgetTools/src/include/Image/Cirno.jpg new file mode 100644 index 0000000..0677457 Binary files /dev/null and b/3rdparty/ElaWidgetTools/src/include/Image/Cirno.jpg differ diff --git a/3rdparty/ElaWidgetTools/src/include/Image/MicaBase.png b/3rdparty/ElaWidgetTools/src/include/Image/MicaBase.png new file mode 100644 index 0000000..36dcf5e Binary files /dev/null and b/3rdparty/ElaWidgetTools/src/include/Image/MicaBase.png differ diff --git a/3rdparty/ElaWidgetTools/src/include/Image/Moon.jpg b/3rdparty/ElaWidgetTools/src/include/Image/Moon.jpg new file mode 100644 index 0000000..7c6ebbe Binary files /dev/null and b/3rdparty/ElaWidgetTools/src/include/Image/Moon.jpg differ diff --git a/3rdparty/ElaWidgetTools/src/include/Image/noise.png b/3rdparty/ElaWidgetTools/src/include/Image/noise.png new file mode 100644 index 0000000..a78e829 Binary files /dev/null and b/3rdparty/ElaWidgetTools/src/include/Image/noise.png differ diff --git a/3rdparty/ElaWidgetTools/src/include/singleton.h b/3rdparty/ElaWidgetTools/src/include/singleton.h new file mode 100644 index 0000000..a17f320 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/singleton.h @@ -0,0 +1,59 @@ +#ifndef SINGLETON_H +#define SINGLETON_H + +#include + +template +class Singleton +{ +public: + static T* getInstance(); + +private: + Q_DISABLE_COPY(Singleton) +}; + +template +T* Singleton::getInstance() +{ + static QMutex mutex; + QMutexLocker locker(&mutex); + static T* instance = nullptr; + if (instance == nullptr) + { + instance = new T(); + } + return instance; +} + +#define Q_SINGLETON_CREATE(Class) \ +private: \ + friend class Singleton; \ + \ +public: \ + static Class* getInstance() \ + { \ + return Singleton::getInstance(); \ + } + +#define Q_SINGLETON_CREATE_H(Class) \ +private: \ + static Class* _instance; \ + \ +public: \ + static Class* getInstance(); + +#define Q_SINGLETON_CREATE_CPP(Class) \ + Class* Class::_instance = nullptr; \ + Class* Class::getInstance() \ + { \ + static QMutex mutex; \ + QMutexLocker locker(&mutex); \ + if (_instance == nullptr) \ + { \ + _instance = new Class(); \ + } \ + return _instance; \ + } + +#endif // SINGLETON_H diff --git a/3rdparty/ElaWidgetTools/src/include/stdafx.h b/3rdparty/ElaWidgetTools/src/include/stdafx.h new file mode 100644 index 0000000..4b5326f --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/include/stdafx.h @@ -0,0 +1,104 @@ +#ifndef STDAFX_H +#define STDAFX_H + +#include + +#ifdef ELAWIDGETTOOLS_LIBRARY +#define ELA_EXPORT Q_DECL_EXPORT +#else +#define ELA_EXPORT Q_DECL_IMPORT +#endif + +#define Q_PROPERTY_CREATE(TYPE, M) \ + Q_PROPERTY(TYPE p##M MEMBER _p##M NOTIFY p##M##Changed) \ +public: \ + Q_SIGNAL void p##M##Changed(); \ + void set##M(TYPE M) \ + { \ + _p##M = M; \ + Q_EMIT p##M##Changed(); \ + } \ + TYPE get##M() const \ + { \ + return _p##M; \ + } \ + \ +private: \ + TYPE _p##M; + +// Q_D Q_Q普通属性快速创建 +#define Q_PROPERTY_CREATE_Q_H(TYPE, M) \ + Q_PROPERTY(TYPE p##M READ get##M WRITE set##M NOTIFY p##M##Changed) \ +public: \ + Q_SIGNAL void p##M##Changed(); \ + void set##M(TYPE M); \ + TYPE get##M() const; + +// Q_D Q_Q指针变量快速创建 +#define Q_PRIVATE_CREATE_Q_H(TYPE, M) \ +public: \ + void set##M(TYPE M); \ + TYPE get##M() const; + +#define Q_PROPERTY_CREATE_Q_CPP(CLASS, TYPE, M) \ + void CLASS::set##M(TYPE M) \ + { \ + Q_D(CLASS); \ + d->_p##M = M; \ + Q_EMIT p##M##Changed(); \ + } \ + TYPE CLASS::get##M() const \ + { \ + return d_ptr->_p##M; \ + } + +#define Q_PRIVATE_CREATE_Q_CPP(CLASS, TYPE, M) \ + void CLASS::set##M(TYPE M) \ + { \ + Q_D(CLASS); \ + d->_p##M = M; \ + } \ + TYPE CLASS::get##M() const \ + { \ + return d_ptr->_p##M; \ + } + +#define Q_PROPERTY_CREATE_D(TYPE, M) \ +private: \ + TYPE _p##M; + +#define Q_PRIVATE_CREATE_D(TYPE, M) \ +private: \ + TYPE _p##M; + +#define Q_PRIVATE_CREATE(TYPE, M) \ +public: \ + void set##M(TYPE M) \ + { \ + _p##M = M; \ + } \ + TYPE get##M() const \ + { \ + return _p##M; \ + } \ + \ +private: \ + TYPE _p##M; + +#define Q_Q_CREATE(CLASS) \ +protected: \ + explicit CLASS(CLASS##Private& dd, CLASS* parent = nullptr); \ + QScopedPointer d_ptr; \ + \ +private: \ + Q_DISABLE_COPY(CLASS) \ + Q_DECLARE_PRIVATE(CLASS); + +#define Q_D_CREATE(CLASS) \ +protected: \ + CLASS* q_ptr; \ + \ +private: \ + Q_DECLARE_PUBLIC(CLASS); + +#endif // STDAFX_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaAcrylicUrlCardPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaAcrylicUrlCardPrivate.cpp new file mode 100644 index 0000000..12969a2 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaAcrylicUrlCardPrivate.cpp @@ -0,0 +1,10 @@ +#include "ElaAcrylicUrlCardPrivate.h" + +ElaAcrylicUrlCardPrivate::ElaAcrylicUrlCardPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaAcrylicUrlCardPrivate::~ElaAcrylicUrlCardPrivate() +{ +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaAcrylicUrlCardPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaAcrylicUrlCardPrivate.h new file mode 100644 index 0000000..e101289 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaAcrylicUrlCardPrivate.h @@ -0,0 +1,41 @@ +#ifndef ELAACRYLICURLCARDPRIVATE_H +#define ELAACRYLICURLCARDPRIVATE_H + +#include +#include +#include + +#include "Def.h" +#include "stdafx.h" +class ElaAcrylicUrlCard; +class ElaAcrylicUrlCardPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaAcrylicUrlCard) + Q_PROPERTY_CREATE_D(int, BorderRadius) + Q_PROPERTY_CREATE_D(qreal, MainOpacity) + Q_PROPERTY_CREATE_D(qreal, NoiseOpacity) + Q_PROPERTY_CREATE_D(int, BrushAlpha) + Q_PROPERTY_CREATE_D(QString, Title); + Q_PROPERTY_CREATE_D(QString, SubTitle); + Q_PROPERTY_CREATE_D(int, TitlePixelSize); + Q_PROPERTY_CREATE_D(int, SubTitlePixelSize); + Q_PROPERTY_CREATE_D(int, TitleSpacing); + Q_PROPERTY_CREATE_D(int, SubTitleSpacing); + Q_PROPERTY_CREATE_D(QPixmap, CardPixmap); + Q_PROPERTY_CREATE_D(QSize, CardPixmapSize); + Q_PROPERTY_CREATE_D(int, CardPixmapBorderRadius) + Q_PROPERTY_CREATE_D(ElaCardPixType::PixMode, CardPixMode); + Q_PROPERTY_CREATE_D(QString, Url); + +public: + explicit ElaAcrylicUrlCardPrivate(QObject* parent = nullptr); + ~ElaAcrylicUrlCardPrivate(); + +private: + QPixmap _noisePix; + ElaThemeType::ThemeMode _themeMode; + qreal _borderWidth{1.5}; +}; + +#endif // ELAACRYLICURLCARDPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaAppBarPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaAppBarPrivate.cpp new file mode 100644 index 0000000..0b265b7 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaAppBarPrivate.cpp @@ -0,0 +1,277 @@ +#include "ElaAppBarPrivate.h" + +#include "ElaToolButton.h" +#ifdef Q_OS_WIN +#include +#endif +#include +#include +#include +#include +#include +#include + +#include "ElaAppBar.h" +#include "ElaIconButton.h" +#include "ElaNavigationBar.h" +#include "ElaText.h" +ElaAppBarPrivate::ElaAppBarPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaAppBarPrivate::~ElaAppBarPrivate() +{ +} + +void ElaAppBarPrivate::onMinButtonClicked() +{ + Q_Q(ElaAppBar); + q->window()->showMinimized(); +} + +void ElaAppBarPrivate::onMaxButtonClicked() +{ + Q_Q(ElaAppBar); + if (q->window()->isMaximized()) + { + q->window()->showNormal(); + } + else + { + q->window()->showMaximized(); + } +} + +void ElaAppBarPrivate::onCloseButtonClicked() +{ + Q_Q(ElaAppBar); + if (_pIsDefaultClosed) + { + q->window()->close(); + } + else + { + Q_EMIT q_ptr->closeButtonClicked(); + } +} + +void ElaAppBarPrivate::onStayTopButtonClicked() +{ +#ifdef Q_OS_WIN + HWND hwnd = (HWND)_currentWinID; + ::SetWindowPos(hwnd, _pIsStayTop ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); +#else + Q_Q(const ElaAppBar); + bool isVisible = q->window()->isVisible(); + q->window()->setWindowFlag(Qt::WindowStaysOnTopHint, _pIsStayTop); + if (isVisible) + { + q->window()->show(); + } +#endif + _stayTopButton->setIsSelected(_pIsStayTop); + _stayTopButton->update(); +} + +void ElaAppBarPrivate::_changeMaxButtonAwesome(bool isMaximized) +{ + if (isMaximized) + { + _maxButton->setElaIcon(ElaIconType::WindowRestore); + } + else + { + _maxButton->setElaIcon(ElaIconType::Square); + } +} + +void ElaAppBarPrivate::_showSystemMenu(QPoint point) +{ + Q_Q(const ElaAppBar); +#ifdef Q_OS_WIN + QScreen* screen = qApp->screenAt(QCursor::pos()); + if (!screen) + { + screen = QGuiApplication::primaryScreen(); + } + if (!screen) + { + return; + } + const QPoint origin = screen->geometry().topLeft(); + auto nativePos = QPointF(QPointF(point - origin) * screen->devicePixelRatio()).toPoint() + origin; + HWND hwnd = reinterpret_cast(q->window()->winId()); + const HMENU hMenu = ::GetSystemMenu(hwnd, FALSE); + if (q->window()->isMaximized() || q->window()->isFullScreen()) + { + ::EnableMenuItem(hMenu, SC_MOVE, MFS_DISABLED); + ::EnableMenuItem(hMenu, SC_RESTORE, MFS_ENABLED); + } + else + { + ::EnableMenuItem(hMenu, SC_MOVE, MFS_ENABLED); + ::EnableMenuItem(hMenu, SC_RESTORE, MFS_DISABLED); + } + if (!_pIsFixedSize && !q->window()->isMaximized() && !q->window()->isFullScreen()) + { + ::EnableMenuItem(hMenu, SC_SIZE, MFS_ENABLED); + ::EnableMenuItem(hMenu, SC_MAXIMIZE, MFS_ENABLED); + } + else + { + ::EnableMenuItem(hMenu, SC_SIZE, MFS_DISABLED); + ::EnableMenuItem(hMenu, SC_MAXIMIZE, MFS_DISABLED); + } + const int result = ::TrackPopupMenu(hMenu, (TPM_RETURNCMD | (QGuiApplication::isRightToLeft() ? TPM_RIGHTALIGN : TPM_LEFTALIGN)), nativePos.x(), + nativePos.y(), 0, hwnd, nullptr); + if (result != FALSE) + { + ::PostMessageW(hwnd, WM_SYSCOMMAND, result, 0); + } +#endif +} + +void ElaAppBarPrivate::_updateCursor(int edges) +{ + Q_Q(const ElaAppBar); + switch (edges) + { + case 0: + { + q->window()->setCursor(Qt::ArrowCursor); + break; + } + case Qt::LeftEdge: + case Qt::RightEdge: + { + q->window()->setCursor(Qt::SizeHorCursor); + break; + } + case Qt::TopEdge: + case Qt::BottomEdge: + { + q->window()->setCursor(Qt::SizeVerCursor); + break; + } + case Qt::LeftEdge | Qt::TopEdge: + case Qt::RightEdge | Qt::BottomEdge: + { + q->window()->setCursor(Qt::SizeFDiagCursor); + break; + } + case Qt::RightEdge | Qt::TopEdge: + case Qt::LeftEdge | Qt::BottomEdge: + { + q->window()->setCursor(Qt::SizeBDiagCursor); + break; + } + default: + { + break; + } + } +} + +bool ElaAppBarPrivate::_containsCursorToItem(QWidget* item) +{ + Q_Q(const ElaAppBar); + if (!item || !item->isVisible()) + { + return false; + } + auto point = item->window()->mapFromGlobal(QCursor::pos()); + QRectF rect = QRectF(item->mapTo(item->window(), QPoint(0, 0)), item->size()); + if (item == q) + { + if (_containsCursorToItem(_routeBackButton) || _containsCursorToItem(_navigationButton) || _containsCursorToItem(_pCustomWidget) || _containsCursorToItem(_stayTopButton) || _containsCursorToItem(_themeChangeButton) || _containsCursorToItem(_minButton) || _containsCursorToItem(_maxButton) || _containsCursorToItem(_closeButton)) + { + return false; + } + } + if (rect.contains(point)) + { + return true; + } + return false; +} + +void ElaAppBarPrivate::_onThemeModeChange(ElaThemeType::ThemeMode themeMode) +{ + if (themeMode == ElaThemeType::Light) + { + _themeChangeButton->setElaIcon(ElaIconType::MoonStars); + } + else + { + _themeChangeButton->setElaIcon(ElaIconType::SunBright); + } +} + +int ElaAppBarPrivate::_calculateMinimumWidth() +{ + Q_Q(ElaAppBar); + int width = 0; + if (_titleLabel->isVisible()) + { + width += _titleLabel->width(); + width += 10; + } + if (_iconLabel->isVisible()) + { + width += _iconLabel->width(); + width += 10; + } + bool isHasNavigationBar = false; + if (q->parentWidget()->findChild()) + { + isHasNavigationBar = true; + width += 305; + } + else + { + width += 5; + } + if (_pCustomWidget) + { + int customWidgetWidth = _pCustomWidget->width(); + if (isHasNavigationBar) + { + if (customWidgetWidth > 300) + { + width += customWidgetWidth - 300; + } + } + else + { + width += customWidgetWidth; + } + } + QList buttonList = q->findChildren(); + for (auto button : buttonList) + { + if (button->isVisible() && button->objectName() != "NavigationButton") + { + width += button->width(); + } + } + return width; +} + +QVBoxLayout* ElaAppBarPrivate::_createVLayout(QWidget* widget) +{ + if (!widget) + { + return nullptr; + } + QVBoxLayout* vLayout = new QVBoxLayout(); + vLayout->setContentsMargins(0, 0, 0, 0); + vLayout->setSpacing(0); + if (widget == _iconLabel || widget == _titleLabel) + { + vLayout->addSpacing(6); + } + vLayout->addWidget(widget); + vLayout->addStretch(); + return vLayout; +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaAppBarPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaAppBarPrivate.h new file mode 100644 index 0000000..01f28a3 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaAppBarPrivate.h @@ -0,0 +1,64 @@ +#ifndef ELAAPPBARPRIVATE_H +#define ELAAPPBARPRIVATE_H +#include + +#include "Def.h" +#include "stdafx.h" +class QLabel; +class QScreen; +class QHBoxLayout; +class QVBoxLayout; +class ElaText; +class ElaAppBar; +class ElaIconButton; +class ElaToolButton; +class ElaAppBarPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaAppBar) + Q_PROPERTY_CREATE_D(bool, IsStayTop) + Q_PROPERTY_CREATE_D(bool, IsFixedSize) + Q_PROPERTY_CREATE_D(bool, IsDefaultClosed) + Q_PROPERTY_CREATE_D(bool, IsOnlyAllowMinAndClose) + Q_PROPERTY_CREATE_D(int, AppBarHeight) + Q_PROPERTY_CREATE_D(QWidget*, CustomWidget) + Q_PROPERTY_CREATE_D(int, CustomWidgetMaximumWidth) +public: + explicit ElaAppBarPrivate(QObject* parent = nullptr); + ~ElaAppBarPrivate(); + Q_SLOT void onMinButtonClicked(); + Q_SLOT void onMaxButtonClicked(); + Q_SLOT void onCloseButtonClicked(); + Q_SLOT void onStayTopButtonClicked(); + +private: + QHBoxLayout* _mainLayout{nullptr}; + QVBoxLayout* _iconLabelLayout{nullptr}; + QVBoxLayout* _titleLabelLayout{nullptr}; + ElaAppBarType::ButtonFlags _buttonFlags; + ElaToolButton* _routeBackButton{nullptr}; + ElaToolButton* _navigationButton{nullptr}; + ElaToolButton* _themeChangeButton{nullptr}; + ElaToolButton* _stayTopButton{nullptr}; + ElaToolButton* _minButton{nullptr}; + ElaToolButton* _maxButton{nullptr}; + ElaIconButton* _closeButton{nullptr}; + QScreen* _lastScreen{nullptr}; + ElaText* _titleLabel{nullptr}; + QLabel* _iconLabel{nullptr}; + qint64 _currentWinID{0}; + int _lastMinTrackWidth{0}; + quint64 _clickTimer{0}; + int _edges{0}; + int _margins{8}; + bool _isHoverMaxButton{false}; + void _changeMaxButtonAwesome(bool isMaximized); + void _showSystemMenu(QPoint point); + void _updateCursor(int edges); + bool _containsCursorToItem(QWidget* item); + void _onThemeModeChange(ElaThemeType::ThemeMode themeMode); + int _calculateMinimumWidth(); + QVBoxLayout* _createVLayout(QWidget* widget); +}; + +#endif // ELAAPPBARPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaApplicationPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaApplicationPrivate.cpp new file mode 100644 index 0000000..adb9544 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaApplicationPrivate.cpp @@ -0,0 +1,141 @@ +#include "ElaApplicationPrivate.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ElaApplication.h" +#include "ElaMicaBaseInitObject.h" +ElaApplicationPrivate::ElaApplicationPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaApplicationPrivate::~ElaApplicationPrivate() +{ +} + +void ElaApplicationPrivate::onThemeModeChanged(ElaThemeType::ThemeMode themeMode) +{ + _themeMode = themeMode; + _updateAllMicaWidget(); +} + +bool ElaApplicationPrivate::eventFilter(QObject* watched, QEvent* event) +{ + switch (event->type()) + { + case QEvent::Show: + case QEvent::Move: + case QEvent::Resize: + { + if (_pIsEnableMica) + { + QWidget* widget = qobject_cast(watched); + if (widget) + { + _updateMica(widget); + } + } + break; + } + case QEvent::Destroy: + { + QWidget* widget = qobject_cast(watched); + if (widget) + { + _micaWidgetList.removeOne(widget); + } + break; + } + default: + { + break; + } + } + return QObject::eventFilter(watched, event); +} + +void ElaApplicationPrivate::_initMicaBaseImage(QImage img) +{ + Q_Q(ElaApplication); + if (img.isNull()) + { + return; + } + QThread* initThread = new QThread(); + ElaMicaBaseInitObject* initObject = new ElaMicaBaseInitObject(this); + connect(initThread, &QThread::finished, initObject, &ElaMicaBaseInitObject::deleteLater); + connect(initObject, &ElaMicaBaseInitObject::initFinished, initThread, [=]() { + Q_EMIT q->pIsEnableMicaChanged(); + _updateAllMicaWidget(); + initThread->quit(); + initThread->wait(); + initThread->deleteLater(); + }); + initObject->moveToThread(initThread); + initThread->start(); + connect(this, &ElaApplicationPrivate::initMicaBase, initObject, &ElaMicaBaseInitObject::onInitMicaBase); + Q_EMIT initMicaBase(img); +} + +QRect ElaApplicationPrivate::_calculateWindowVirtualGeometry(QWidget* widget) +{ + QRect geometry = widget->geometry(); + qreal xImageRatio = 1, yImageRatio = 1; + QRect relativeGeometry; + if (qApp->screens().count() > 1) + { + QScreen* currentScreen = qApp->screenAt(geometry.topLeft()); + if (currentScreen) + { + QRect screenGeometry = currentScreen->availableGeometry(); + xImageRatio = (qreal)_lightBaseImage.width() / screenGeometry.width(); + yImageRatio = (qreal)_lightBaseImage.height() / screenGeometry.height(); + relativeGeometry = QRect((geometry.x() - screenGeometry.x()) * xImageRatio, (geometry.y() - screenGeometry.y()) * yImageRatio, geometry.width() * xImageRatio, geometry.height() * yImageRatio); + return relativeGeometry; + } + } + QRect primaryScreenGeometry = qApp->primaryScreen()->availableGeometry(); + xImageRatio = (qreal)_lightBaseImage.width() / primaryScreenGeometry.width(); + yImageRatio = (qreal)_lightBaseImage.height() / primaryScreenGeometry.height(); + relativeGeometry = QRect((geometry.x() - primaryScreenGeometry.x()) * xImageRatio, (geometry.y() - primaryScreenGeometry.y()) * yImageRatio, geometry.width() * xImageRatio, geometry.height() * yImageRatio); + return relativeGeometry; +} + +void ElaApplicationPrivate::_updateMica(QWidget* widget, bool isProcessEvent) +{ + if (widget->isVisible()) + { + QPalette palette = widget->palette(); + if (_themeMode == ElaThemeType::Light) + { + palette.setBrush(QPalette::Window, _lightBaseImage.copy(_calculateWindowVirtualGeometry(widget)).scaled(widget->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + } + else + { + palette.setBrush(QPalette::Window, _darkBaseImage.copy(_calculateWindowVirtualGeometry(widget)).scaled(widget->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + } + widget->setPalette(palette); + if (isProcessEvent) + { + QApplication::processEvents(); + } + } +} + +void ElaApplicationPrivate::_updateAllMicaWidget() +{ + if (_pIsEnableMica) + { + for (auto widget : _micaWidgetList) + { + _updateMica(widget, false); + } + } +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaApplicationPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaApplicationPrivate.h new file mode 100644 index 0000000..e8e78fa --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaApplicationPrivate.h @@ -0,0 +1,38 @@ +#ifndef ELAAPPLICATIONPRIVATE_H +#define ELAAPPLICATIONPRIVATE_H + +#include +#include +#include + +#include "Def.h" +class ElaApplication; +class ElaApplicationPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaApplication) + Q_PROPERTY_CREATE_D(bool, IsEnableMica) + Q_PROPERTY_CREATE_D(QString, MicaImagePath) +public: + explicit ElaApplicationPrivate(QObject* parent = nullptr); + ~ElaApplicationPrivate(); + Q_SLOT void onThemeModeChanged(ElaThemeType::ThemeMode themeMode); +Q_SIGNALS: + Q_SIGNAL void initMicaBase(QImage img); + +protected: + virtual bool eventFilter(QObject* watched, QEvent* event) override; + +private: + friend class ElaMicaBaseInitObject; + ElaThemeType::ThemeMode _themeMode; + QList _micaWidgetList; + QImage _lightBaseImage; + QImage _darkBaseImage; + void _initMicaBaseImage(QImage img); + QRect _calculateWindowVirtualGeometry(QWidget* widget); + void _updateMica(QWidget* widget, bool isProcessEvent = true); + void _updateAllMicaWidget(); +}; + +#endif // ELAAPPLICATIONPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaBreadcrumbBarPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaBreadcrumbBarPrivate.cpp new file mode 100644 index 0000000..f22dd90 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaBreadcrumbBarPrivate.cpp @@ -0,0 +1,10 @@ +#include "ElaBreadcrumbBarPrivate.h" + +ElaBreadcrumbBarPrivate::ElaBreadcrumbBarPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaBreadcrumbBarPrivate::~ElaBreadcrumbBarPrivate() +{ +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaBreadcrumbBarPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaBreadcrumbBarPrivate.h new file mode 100644 index 0000000..1ca0888 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaBreadcrumbBarPrivate.h @@ -0,0 +1,28 @@ +#ifndef ELABREADCRUMBBARPRIVATE_H +#define ELABREADCRUMBBARPRIVATE_H + +#include + +#include "stdafx.h" +class ElaBaseListView; +class ElaBreadcrumbBarModel; +class ElaBreadcrumbBarDelegate; + +class ElaBreadcrumbBar; +class ElaBreadcrumbBarPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaBreadcrumbBar) + Q_PROPERTY_CREATE_D(int, TextPixelSize) + Q_PROPERTY_CREATE_D(bool, IsAutoRemove) +public: + explicit ElaBreadcrumbBarPrivate(QObject* parent = nullptr); + ~ElaBreadcrumbBarPrivate(); + +private: + ElaBreadcrumbBarModel* _listModel{nullptr}; + ElaBreadcrumbBarDelegate* _listDelegate{nullptr}; + ElaBaseListView* _listView{nullptr}; +}; + +#endif // ELABREADCRUMBBARPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaCalendarPickerPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaCalendarPickerPrivate.cpp new file mode 100644 index 0000000..378a875 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaCalendarPickerPrivate.cpp @@ -0,0 +1,39 @@ +#include "ElaCalendarPickerPrivate.h" + +#include +#include + +#include "ElaCalendar.h" +#include "ElaCalendarPicker.h" +#include "ElaCalendarPickerContainer.h" +ElaCalendarPickerPrivate::ElaCalendarPickerPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaCalendarPickerPrivate::~ElaCalendarPickerPrivate() +{ +} + +void ElaCalendarPickerPrivate::onCalendarPickerClicked() +{ + Q_Q(ElaCalendarPicker); + QPoint endPoint(q->mapToGlobal(QPoint((q->width() - _calendarPickerContainer->width()) / 2, q->height() + 5))); + QPropertyAnimation* showAnimation = new QPropertyAnimation(_calendarPickerContainer, "pos"); + showAnimation->setEasingCurve(QEasingCurve::OutCubic); + showAnimation->setDuration(250); + showAnimation->setStartValue(QPoint(endPoint.x(), endPoint.y() - 10)); + showAnimation->setEndValue(endPoint); + _calendarPickerContainer->show(); + showAnimation->start(QAbstractAnimation::DeleteWhenStopped); +} + +void ElaCalendarPickerPrivate::onCalendarSelectedDateChanged() +{ + Q_Q(ElaCalendarPicker); + Q_EMIT q->selectedDateChanged(_calendar->getSelectedDate()); + if (_calendarPickerContainer->isVisible()) + { + _calendarPickerContainer->hide(); + } +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaCalendarPickerPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaCalendarPickerPrivate.h new file mode 100644 index 0000000..f12b66a --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaCalendarPickerPrivate.h @@ -0,0 +1,27 @@ +#ifndef ELACALENDARPICKERPRIVATE_H +#define ELACALENDARPICKERPRIVATE_H + +#include + +#include "Def.h" +class ElaCalendar; +class ElaCalendarPicker; +class ElaCalendarPickerContainer; +class ElaCalendarPickerPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaCalendarPicker) + Q_PROPERTY_CREATE_D(int, BorderRadius) +public: + explicit ElaCalendarPickerPrivate(QObject* parent = nullptr); + ~ElaCalendarPickerPrivate(); + Q_SLOT void onCalendarPickerClicked(); + Q_SLOT void onCalendarSelectedDateChanged(); + +private: + ElaThemeType::ThemeMode _themeMode; + ElaCalendar* _calendar{nullptr}; + ElaCalendarPickerContainer* _calendarPickerContainer{nullptr}; +}; + +#endif // ELACALENDARPICKERPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaCalendarPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaCalendarPrivate.cpp new file mode 100644 index 0000000..bfe74bc --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaCalendarPrivate.cpp @@ -0,0 +1,238 @@ +#include "ElaCalendarPrivate.h" + +#include +#include +#include + +#include "ElaBaseListView.h" +#include "ElaCalendar.h" +#include "ElaCalendarDelegate.h" +#include "ElaCalendarModel.h" +#include "ElaToolButton.h" +ElaCalendarPrivate::ElaCalendarPrivate(QObject* parent) + : QObject{parent} +{ + _pZoomRatio = 1; + _pPixOpacity = 1; +} + +ElaCalendarPrivate::~ElaCalendarPrivate() +{ +} + +void ElaCalendarPrivate::onSwitchButtonClicked() +{ + if (!_isSwitchAnimationFinished) + { + return; + } + Q_Q(ElaCalendar); + ElaCalendarType displayMode = _calendarModel->getDisplayMode(); + if (displayMode == ElaCalendarType::DayMode) + { + _oldCalendarViewPix = q->grab(QRect(_borderWidth, _borderWidth + 45, q->width() - 2 * _borderWidth, q->height() - 2 * _borderWidth - 45)); + _calendarModel->setDisplayMode(ElaCalendarType::MonthMode); + _calendarTitleView->setVisible(false); + QApplication::processEvents(); + _calendarView->setSpacing(15); + _scrollToDate(_pSelectedDate.isValid() ? _pSelectedDate : QDate::currentDate()); + _newCalendarViewPix = q->grab(QRect(_borderWidth, _borderWidth + 45, q->width() - 2 * _borderWidth, q->height() - 2 * _borderWidth - 45)); + _doSwitchAnimation(false); + } + else if (displayMode == ElaCalendarType::MonthMode) + { + _oldCalendarViewPix = q->grab(QRect(_borderWidth, _borderWidth + 45, q->width() - 2 * _borderWidth, q->height() - 2 * _borderWidth - 45)); + _calendarModel->setDisplayMode(ElaCalendarType::YearMode); + _scrollToDate(_pSelectedDate.isValid() ? _pSelectedDate : QDate::currentDate()); + _newCalendarViewPix = q->grab(QRect(_borderWidth, _borderWidth + 45, q->width() - 2 * _borderWidth, q->height() - 2 * _borderWidth - 45)); + _doSwitchAnimation(false); + } +} + +void ElaCalendarPrivate::onCalendarViewClicked(const QModelIndex& index) +{ + Q_Q(ElaCalendar); + if (!_isSwitchAnimationFinished) + { + return; + } + switch (_calendarModel->getDisplayMode()) + { + case YearMode: + { + _oldCalendarViewPix = q->grab(QRect(_borderWidth, _borderWidth + 45, q->width() - 2 * _borderWidth, q->height() - 2 * _borderWidth - 45)); + _lastSelectedYear = _calendarModel->getMinimumDate().year() + index.row(); + _calendarModel->setDisplayMode(ElaCalendarType::MonthMode); + _scrollToDate(QDate(_lastSelectedYear, _lastSelectedMonth, 15)); + _newCalendarViewPix = q->grab(QRect(_borderWidth, _borderWidth + 45, q->width() - 2 * _borderWidth, q->height() - 2 * _borderWidth - 45)); + _doSwitchAnimation(true); + break; + } + case MonthMode: + { + _oldCalendarViewPix = q->grab(QRect(_borderWidth, _borderWidth + 45, q->width() - 2 * _borderWidth, q->height() - 2 * _borderWidth - 45)); + _lastSelectedMonth = index.row() % 12 + 1; + _calendarModel->setDisplayMode(ElaCalendarType::DayMode); + _calendarTitleView->setVisible(true); + _calendarView->setSpacing(0); + _scrollToDate(QDate(_lastSelectedYear, _lastSelectedMonth, 15)); + _calendarView->selectionModel()->setCurrentIndex(_calendarModel->getIndexFromDate(_pSelectedDate), QItemSelectionModel::Select); + _newCalendarViewPix = q->grab(QRect(_borderWidth, _borderWidth + 45, q->width() - 2 * _borderWidth, q->height() - 2 * _borderWidth - 45)); + _calendarTitleView->setVisible(false); + _doSwitchAnimation(true); + break; + } + case DayMode: + { + QDate date = _calendarModel->getDateFromIndex(index); + if (date.isValid()) + { + _calendarView->clearSelection(); + q->setSelectedDate(date); + } + Q_EMIT q->clicked(_pSelectedDate); + break; + } + } +} + +void ElaCalendarPrivate::onUpButtonClicked() +{ + QScrollBar* vscrollBar = _calendarView->verticalScrollBar(); + QPropertyAnimation* scrollAnimation = new QPropertyAnimation(vscrollBar, "value"); + scrollAnimation->setEasingCurve(QEasingCurve::OutSine); + scrollAnimation->setDuration(300); + scrollAnimation->setStartValue(vscrollBar->value()); + scrollAnimation->setEndValue(vscrollBar->value() - 150); + scrollAnimation->start(QAbstractAnimation::DeleteWhenStopped); +} + +void ElaCalendarPrivate::onDownButtonClicked() +{ + QScrollBar* vscrollBar = _calendarView->verticalScrollBar(); + QPropertyAnimation* scrollAnimation = new QPropertyAnimation(vscrollBar, "value"); + scrollAnimation->setEasingCurve(QEasingCurve::OutSine); + scrollAnimation->setDuration(300); + scrollAnimation->setStartValue(vscrollBar->value()); + scrollAnimation->setEndValue(vscrollBar->value() + 150); + scrollAnimation->start(QAbstractAnimation::DeleteWhenStopped); +} + +void ElaCalendarPrivate::_scrollToDate(QDate date) +{ + int index = _calendarModel->getIndexFromDate(date).row(); + switch (_calendarModel->getDisplayMode()) + { + case YearMode: + case MonthMode: + { + _calendarView->scrollTo(_calendarModel->index(index - 4), QAbstractItemView::PositionAtTop); + break; + } + case DayMode: + { + _calendarView->scrollTo(_calendarModel->index(index - 14), QAbstractItemView::PositionAtTop); + break; + } + } +} + +void ElaCalendarPrivate::_doSwitchAnimation(bool isZoomIn) +{ + Q_Q(ElaCalendar); + if (!_isSwitchAnimationFinished) + { + return; + } + _isDrawNewPix = false; + _isSwitchAnimationFinished = false; + _calendarDelegate->setIsTransparent(true); + QPropertyAnimation* oldPixZoomAnimation = new QPropertyAnimation(this, "pZoomRatio"); + connect(oldPixZoomAnimation, &QPropertyAnimation::valueChanged, this, [=]() { + q->update(); + }); + connect(oldPixZoomAnimation, &QPropertyAnimation::finished, this, [=]() { + _isDrawNewPix = true; + QPropertyAnimation* newPixZoomAnimation = new QPropertyAnimation(this, "pZoomRatio"); + connect(newPixZoomAnimation, &QPropertyAnimation::valueChanged, this, [=]() { + q->update(); + }); + connect(newPixZoomAnimation, &QPropertyAnimation::finished, this, [=]() { + if (_calendarModel->getDisplayMode() == ElaCalendarType::DayMode) + { + _calendarTitleView->setVisible(true); + } + _isSwitchAnimationFinished = true; + _calendarDelegate->setIsTransparent(false); + }); + if (isZoomIn) + { + // 放大 年-月-日 + newPixZoomAnimation->setStartValue(0.85); + newPixZoomAnimation->setEndValue(1); + } + else + { + newPixZoomAnimation->setStartValue(1.5); + newPixZoomAnimation->setEndValue(1); + } + newPixZoomAnimation->setDuration(300); + newPixZoomAnimation->setEasingCurve(QEasingCurve::OutCubic); + newPixZoomAnimation->start(QAbstractAnimation::DeleteWhenStopped); + }); + if (isZoomIn) + { + // 放大 年-月-日 + oldPixZoomAnimation->setStartValue(1); + oldPixZoomAnimation->setEndValue(1.15); + } + else + { + // 缩小 日-月-年 + oldPixZoomAnimation->setStartValue(1); + oldPixZoomAnimation->setEndValue(0.85); + } + oldPixZoomAnimation->setDuration(150); + oldPixZoomAnimation->start(QAbstractAnimation::DeleteWhenStopped); + + QPropertyAnimation* oldPixOpacityAnimation = new QPropertyAnimation(this, "pPixOpacity"); + connect(oldPixZoomAnimation, &QPropertyAnimation::finished, this, [=]() { + QPropertyAnimation* newPixOpacityAnimation = new QPropertyAnimation(this, "pPixOpacity"); + newPixOpacityAnimation->setStartValue(0); + newPixOpacityAnimation->setEndValue(1); + newPixOpacityAnimation->setDuration(300); + newPixOpacityAnimation->start(QAbstractAnimation::DeleteWhenStopped); + }); + oldPixOpacityAnimation->setStartValue(1); + oldPixOpacityAnimation->setEndValue(0); + oldPixOpacityAnimation->setDuration(150); + oldPixOpacityAnimation->start(QAbstractAnimation::DeleteWhenStopped); +} + +void ElaCalendarPrivate::_updateSwitchButtonText() +{ + QModelIndex modelIndex = _calendarView->indexAt(QPoint(_calendarView->rect().center().x() - 20, _calendarView->rect().center().y())); + if (!modelIndex.isValid()) + { + return; + } + ElaCalendarData data = _calendarModel->data(modelIndex, Qt::UserRole).value(); + switch (_calendarModel->getDisplayMode()) + { + case YearMode: + { + _modeSwitchButton->setText(QString("%1-%2").arg(data.year - 5).arg(data.year + 10)); + break; + } + case MonthMode: + { + _modeSwitchButton->setText(QString("%1年").arg(data.year)); + break; + } + case DayMode: + { + _modeSwitchButton->setText(QString("%1年%2月").arg(data.year).arg(data.month)); + break; + } + } +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaCalendarPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaCalendarPrivate.h new file mode 100644 index 0000000..1656894 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaCalendarPrivate.h @@ -0,0 +1,51 @@ +#ifndef ELACALENDARPRIVATE_H +#define ELACALENDARPRIVATE_H + +#include +#include +#include + +#include "Def.h" +class ElaCalendar; +class ElaBaseListView; +class ElaCalendarModel; +class ElaCalendarDelegate; +class ElaToolButton; +class ElaCalendarPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaCalendar) + Q_PROPERTY_CREATE_D(int, BorderRaiuds) + Q_PROPERTY_CREATE_D(QDate, SelectedDate) + Q_PROPERTY_CREATE(qreal, ZoomRatio) + Q_PROPERTY_CREATE(qreal, PixOpacity) +public: + explicit ElaCalendarPrivate(QObject* parent = nullptr); + ~ElaCalendarPrivate(); + Q_SLOT void onSwitchButtonClicked(); + Q_SLOT void onCalendarViewClicked(const QModelIndex& index); + Q_SLOT void onUpButtonClicked(); + Q_SLOT void onDownButtonClicked(); + +private: + QPixmap _oldCalendarViewPix; + QPixmap _newCalendarViewPix; + int _lastSelectedYear{0}; + int _lastSelectedMonth{1}; + qreal _borderWidth{1.5}; + ElaThemeType::ThemeMode _themeMode; + ElaBaseListView* _calendarView{nullptr}; + ElaCalendarModel* _calendarModel{nullptr}; + ElaCalendarDelegate* _calendarDelegate{nullptr}; + ElaBaseListView* _calendarTitleView{nullptr}; + ElaToolButton* _modeSwitchButton{nullptr}; + ElaToolButton* _upButton{nullptr}; + ElaToolButton* _downButton{nullptr}; + bool _isSwitchAnimationFinished{true}; + bool _isDrawNewPix{false}; + void _scrollToDate(QDate date); + void _doSwitchAnimation(bool isZoomIn); + void _updateSwitchButtonText(); +}; + +#endif // ELACALENDARPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaColorDialogPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaColorDialogPrivate.cpp new file mode 100644 index 0000000..e311bdf --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaColorDialogPrivate.cpp @@ -0,0 +1,286 @@ +#include "ElaColorDialogPrivate.h" + +#include +#include + +#include "ElaBaseListView.h" +#include "ElaColorDialog.h" +#include "ElaColorDisplayModel.h" +#include "ElaColorPreview.h" +#include "ElaColorValueSliderStyle.h" +#include "ElaComboBox.h" +#include "ElaIntValidator.h" +#include "ElaLineEdit.h" +#include "ElaText.h" +ElaColorDialogPrivate::ElaColorDialogPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaColorDialogPrivate::~ElaColorDialogPrivate() +{ +} + +void ElaColorDialogPrivate::onColorPickerColorChanged(QColor selectedColor) +{ + Q_Q(ElaColorDialog); + QColor valueColor = selectedColor.toHsv(); + valueColor.setHsv(valueColor.hue(), valueColor.saturation(), _colorValueSlider->value()); + _pCurrentColor = valueColor; + _updateHtmlEditValue(); + _updateEditValue(); + _updateColorPreview(); + _updateColorValueSlider(); + Q_EMIT q->pCurrentColorChanged(); +} + +void ElaColorDialogPrivate::onColorValueSliderChanged(int value) +{ + Q_Q(ElaColorDialog); + QColor baseColor = _pCurrentColor.toHsv(); + baseColor.setHsv(baseColor.hue(), baseColor.saturation(), _colorValueSlider->value()); + q->setCurrentColor(baseColor); +} + +void ElaColorDialogPrivate::onColorModeChanged(int index) +{ + if (index == 0) + { + _firstText->setText("红色"); + _secondText->setText("绿色"); + _thridText->setText("蓝色"); + _firstEdit->setValidator(new ElaIntValidator(0, 255, this)); + } + else if (index == 1) + { + _firstText->setText("色调"); + _secondText->setText("饱和度"); + _thridText->setText("值"); + _firstEdit->setValidator(new ElaIntValidator(0, 359, this)); + } + _updateEditValue(); +} + +void ElaColorDialogPrivate::onHtmlEditFocusOut(QString text) +{ + // 自动补全 + if (text == "#") + { + _htmlEdit->setText("#000000"); + } + else if (text.length() > 1 && text.length() < 7) + { + _htmlEdit->setText(_completeColorText(text)); + } +} + +void ElaColorDialogPrivate::onHtmlEditChanged(const QString& text) +{ + Q_Q(ElaColorDialog); + if (text.length() > 1) + { + q->setCurrentColor(QColor(_completeColorText(text))); + } +} + +void ElaColorDialogPrivate::onColorEditChanged(const QString& text) +{ + Q_Q(ElaColorDialog); + q->setCurrentColor(_getColorFromEdit()); +} + +void ElaColorDialogPrivate::onBasicColorViewClicked(const QModelIndex& index) +{ + Q_Q(ElaColorDialog); + QColor basicColor = index.data(Qt::UserRole).value(); + q->setCurrentColor(basicColor); +} + +void ElaColorDialogPrivate::onCustomColorViewClicked(const QModelIndex& index) +{ + Q_Q(ElaColorDialog); + QColor color = index.data(Qt::UserRole).value(); + if (color.isValid()) + { + q->setCurrentColor(color); + } +} + +void ElaColorDialogPrivate::onAddCustomColorButtonClicked() +{ + QModelIndexList selectedIndexs = _customColorView->selectionModel()->selectedIndexes(); + if (selectedIndexs.count() > 0) + { + _customColorModel->replaceDisplayColor(_pCurrentColor, selectedIndexs[0].row()); + } +} + +void ElaColorDialogPrivate::onRemoveCustomColorButtonClicked() +{ + QModelIndexList selectedIndexs = _customColorView->selectionModel()->selectedIndexes(); + if (selectedIndexs.count() > 0) + { + _customColorModel->replaceDisplayColor(QColor(), selectedIndexs[0].row()); + } +} + +void ElaColorDialogPrivate::_initBasicColor() +{ + QList basicColorList; + basicColorList << QColor(0xF0, 0x87, 0x84) + << QColor(0xEB, 0x33, 0x24) + << QColor(0x77, 0x43, 0x42) + << QColor(0x8E, 0x40, 0x3A) + << QColor(0x3A, 0x06, 0x03) + << QColor(0x9F, 0xFC, 0xFD) + << QColor(0x73, 0xFB, 0xFD) + << QColor(0x32, 0x82, 0xF6) + << QColor(0x00, 0x23, 0xF5) + << QColor(0x00, 0x12, 0x9A) + << QColor(0x16, 0x41, 0x7C) + << QColor(0x00, 0x0C, 0x7B) + << QColor(0xFF, 0xFE, 0x91) + << QColor(0xFF, 0xFD, 0x55) + << QColor(0xF0, 0x9B, 0x59) + << QColor(0xF0, 0x86, 0x50) + << QColor(0x78, 0x43, 0x15) + << QColor(0x81, 0x7F, 0x26) + << QColor(0x7E, 0x84, 0xF7) + << QColor(0x73, 0x2B, 0xF5) + << QColor(0x35, 0x80, 0xBB) + << QColor(0x00, 0x02, 0x3D) + << QColor(0x58, 0x13, 0x5E) + << QColor(0x3A, 0x08, 0x3E) + << QColor(0xA1, 0xFB, 0x8E) + << QColor(0xA1, 0xFA, 0x4F) + << QColor(0x75, 0xF9, 0x4D) + << QColor(0x75, 0xFA, 0x61) + << QColor(0x75, 0xFA, 0x8D) + << QColor(0x81, 0x80, 0x49) + << QColor(0xEF, 0x88, 0xBE) + << QColor(0xEE, 0x8A, 0xF8) + << QColor(0xEA, 0x3F, 0xF7) + << QColor(0xEA, 0x36, 0x80) + << QColor(0x7F, 0x82, 0xBB) + << QColor(0x75, 0x16, 0x3F) + << QColor(0x37, 0x7D, 0x22) + << QColor(0x37, 0x7E, 0x47) + << QColor(0x36, 0x7E, 0x7F) + << QColor(0x50, 0x7F, 0x80) + << QColor(0x18, 0x3E, 0x0C) + << QColor(0x17, 0x3F, 0x3F) + << QColor(0x74, 0x1B, 0x7C) + << QColor(0x39, 0x10, 0x7B) + << QColor(0x00, 0x00, 0x00) + << QColor(0x80, 0x80, 0x80) + << QColor(0xC0, 0xC0, 0xC0) + << QColor(0xFF, 0xFF, 0xFF); + _basicColorModel->appendDisplayColor(basicColorList); +} + +void ElaColorDialogPrivate::_initCustomColor() +{ + QList customColorList; + for (int i = 0; i < 24; i++) + { + customColorList.append(QColor()); + } + _customColorModel->appendDisplayColor(customColorList); +} + +void ElaColorDialogPrivate::_updateHtmlEditValue() +{ + if (!_htmlEdit->hasFocus()) + { + // 非编辑模式下 进行自动补全 + _htmlEdit->setText(_getHexRgbValue()); + } +} + +void ElaColorDialogPrivate::_updateEditValue() +{ + if (_modeComboBox->currentIndex() == 0) + { + _firstEdit->setText(QString::number(_pCurrentColor.red())); + _secondEdit->setText(QString::number(_pCurrentColor.green())); + _thridEdit->setText(QString::number(_pCurrentColor.blue())); + } + else + { + QColor hsvColor = _pCurrentColor.toHsv(); + int hue = hsvColor.hue(); + if (hue < 0) + { + hue = 0; + } + _firstEdit->setText(QString::number(hue)); + _secondEdit->setText(QString::number(hsvColor.saturation())); + _thridEdit->setText(QString::number(hsvColor.value())); + } +} + +void ElaColorDialogPrivate::_updateColorPreview() +{ + _colorPreview->setBaseColor(_pCurrentColor); + _colorPreview->update(); +} + +void ElaColorDialogPrivate::_updateColorValueSlider() +{ + _colorValueSlider->blockSignals(true); + _colorValueSlider->setValue(_pCurrentColor.value()); + _colorValueSlider->blockSignals(false); + _colorValueSliderStyle->setBaseColor(_pCurrentColor); + _colorValueSlider->update(); +} + +QString ElaColorDialogPrivate::_completeColorText(QString text) const +{ + text.remove("#"); + while (text.length() < 6) + { + text.prepend("0"); + } + text.prepend("#"); + return text; +} + +QString ElaColorDialogPrivate::_getHexRgbValue() const +{ + QString colorHex; + QString red = QString::number(_pCurrentColor.red(), 16); + if (red.length() < 2) + { + red.prepend("0"); + } + QString green = QString::number(_pCurrentColor.green(), 16); + if (green.length() < 2) + { + green.prepend("0"); + } + QString blue = QString::number(_pCurrentColor.blue(), 16); + if (blue.length() < 2) + { + blue.prepend("0"); + } + colorHex = red + green + blue; + colorHex = colorHex.toUpper(); + colorHex.prepend("#"); + return colorHex; +} + +QColor ElaColorDialogPrivate::_getColorFromEdit() const +{ + QColor editColor; + if (_modeComboBox->currentIndex() == 0) + { + // RGB + editColor.setRgb(_firstEdit->text().toUInt(), _secondEdit->text().toUInt(), _thridEdit->text().toUInt()); + } + else + { + // HSV + editColor.setHsv(_firstEdit->text().toUInt(), _secondEdit->text().toUInt(), _thridEdit->text().toUInt()); + } + return editColor; +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaColorDialogPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaColorDialogPrivate.h new file mode 100644 index 0000000..2dce662 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaColorDialogPrivate.h @@ -0,0 +1,87 @@ +#ifndef ELACOLORDIALOGPRIVATE_H +#define ELACOLORDIALOGPRIVATE_H + +#include +#include +#include + +#include "Def.h" +#include "stdafx.h" +class ElaAppBar; +class ElaColorDialog; +class ElaColorPicker; +class ElaBaseListView; +class ElaColorDisplayModel; +class ElaColorDisplayDelegate; +class ElaColorPreview; +class ElaLineEdit; +class ElaComboBox; +class ElaText; +class ElaPushButton; +class ElaToolButton; +class QSlider; +class ElaColorValueSliderStyle; +class ElaColorDialogPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaColorDialog) + Q_PROPERTY_CREATE_D(QColor, CurrentColor) +public: + explicit ElaColorDialogPrivate(QObject* parent = nullptr); + ~ElaColorDialogPrivate(); + + Q_SLOT void onColorPickerColorChanged(QColor selectedColor); + Q_SLOT void onColorValueSliderChanged(int value); + + Q_SLOT void onColorModeChanged(int index); + Q_SLOT void onHtmlEditFocusOut(QString text); + Q_SLOT void onHtmlEditChanged(const QString& text); + Q_SLOT void onColorEditChanged(const QString& text); + + Q_SLOT void onBasicColorViewClicked(const QModelIndex& index); + Q_SLOT void onCustomColorViewClicked(const QModelIndex& index); + + Q_SLOT void onAddCustomColorButtonClicked(); + Q_SLOT void onRemoveCustomColorButtonClicked(); + +private: + ElaThemeType::ThemeMode _themeMode; + ElaAppBar* _appBar{nullptr}; + ElaColorPicker* _colorPicker{nullptr}; + ElaColorPreview* _colorPreview{nullptr}; + QSlider* _colorValueSlider{nullptr}; + ElaColorValueSliderStyle* _colorValueSliderStyle{nullptr}; + ElaLineEdit* _htmlEdit{nullptr}; + ElaComboBox* _modeComboBox{nullptr}; + ElaLineEdit* _firstEdit{nullptr}; + ElaLineEdit* _secondEdit{nullptr}; + ElaLineEdit* _thridEdit{nullptr}; + ElaText* _firstText{nullptr}; + ElaText* _secondText{nullptr}; + ElaText* _thridText{nullptr}; + + ElaBaseListView* _basicColorView{nullptr}; + ElaColorDisplayModel* _basicColorModel{nullptr}; + ElaColorDisplayDelegate* _basicColorDelegate{nullptr}; + + ElaToolButton* _addCustomColorButton{nullptr}; + ElaToolButton* _removeCustomColorButton{nullptr}; + ElaBaseListView* _customColorView{nullptr}; + ElaColorDisplayModel* _customColorModel{nullptr}; + ElaColorDisplayDelegate* _customColorDelegate{nullptr}; + + ElaPushButton* _overButton{nullptr}; + ElaPushButton* _cancelButton{nullptr}; + + void _initBasicColor(); + void _initCustomColor(); + void _updateHtmlEditValue(); + void _updateEditValue(); + void _updateColorPreview(); + void _updateColorValueSlider(); + QString _completeColorText(QString text) const; + QString _getHexRgbValue() const; + QColor _getColorFromEdit() const; +}; + +#endif // ELACOLORDIALOGPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaComboBoxPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaComboBoxPrivate.cpp new file mode 100644 index 0000000..eae3ac9 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaComboBoxPrivate.cpp @@ -0,0 +1,10 @@ +#include "ElaComboBoxPrivate.h" + +ElaComboBoxPrivate::ElaComboBoxPrivate(QObject *parent) + : QObject{parent} +{} + +ElaComboBoxPrivate::~ElaComboBoxPrivate() +{ + +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaComboBoxPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaComboBoxPrivate.h new file mode 100644 index 0000000..17443be --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaComboBoxPrivate.h @@ -0,0 +1,26 @@ +#ifndef ELACOMBOBOXPRIVATE_H +#define ELACOMBOBOXPRIVATE_H + +#include + +#include "Def.h" +#include "stdafx.h" +class ElaComboBox; +class ElaComboBoxStyle; +class ElaComboBoxPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaComboBox); + Q_PROPERTY_CREATE_D(int, BorderRadius) + +public: + explicit ElaComboBoxPrivate(QObject* parent = nullptr); + ~ElaComboBoxPrivate(); + +private: + bool _isAllowHidePopup{false}; + ElaComboBoxStyle* _comboBoxStyle{nullptr}; + ElaThemeType::ThemeMode _themeMode; +}; + +#endif // ELACOMBOBOXPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaContentDialogPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaContentDialogPrivate.cpp new file mode 100644 index 0000000..49b30ac --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaContentDialogPrivate.cpp @@ -0,0 +1,19 @@ +#include "ElaContentDialogPrivate.h" + +#include "ElaContentDialog.h" +#include "ElaMaskWidget.h" +ElaContentDialogPrivate::ElaContentDialogPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaContentDialogPrivate::~ElaContentDialogPrivate() +{ +} + +void ElaContentDialogPrivate::_doCloseAnimation() +{ + Q_Q(ElaContentDialog); + _maskWidget->doMaskAnimation(0); + q->QDialog::close(); +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaContentDialogPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaContentDialogPrivate.h new file mode 100644 index 0000000..94e9cba --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaContentDialogPrivate.h @@ -0,0 +1,36 @@ +#ifndef ELACONTENTDIALOGPRIVATE_H +#define ELACONTENTDIALOGPRIVATE_H + +#include + +#include "Def.h" +#include "stdafx.h" +class QVBoxLayout; +class ElaPushButton; +class ElaContentDialog; +class ElaMaskWidget; +class ElaContentDialogPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaContentDialog) +public: + explicit ElaContentDialogPrivate(QObject* parent = nullptr); + ~ElaContentDialogPrivate(); + +private: + qint64 _currentWinID{0}; + ElaThemeType::ThemeMode _themeMode; + ElaMaskWidget* _maskWidget{nullptr}; + QWidget* _centralWidget{nullptr}; + QWidget* _buttonWidget{nullptr}; + QVBoxLayout* _mainLayout{nullptr}; + QString _leftButtonText{"cancel"}; + QString _middleButtonText{"minimum"}; + QString _rightButtonText{"exit"}; + ElaPushButton* _leftButton{nullptr}; + ElaPushButton* _middleButton{nullptr}; + ElaPushButton* _rightButton{nullptr}; + void _doCloseAnimation(); +}; + +#endif // ELACONTENTDIALOGPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaDockWidgetPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaDockWidgetPrivate.cpp new file mode 100644 index 0000000..f787b7e --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaDockWidgetPrivate.cpp @@ -0,0 +1,21 @@ +#include "ElaDockWidgetPrivate.h" + +#include + +#include "ElaDockWidget.h" +#include "ElaDockWidgetTitleBar.h" +ElaDockWidgetPrivate::ElaDockWidgetPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaDockWidgetPrivate::~ElaDockWidgetPrivate() +{ +} + +void ElaDockWidgetPrivate::onThemeModeChanged(ElaThemeType::ThemeMode themeMode) +{ + Q_Q(ElaDockWidget); + q->update(); + _themeMode = themeMode; +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaDockWidgetPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaDockWidgetPrivate.h new file mode 100644 index 0000000..54d2ee4 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaDockWidgetPrivate.h @@ -0,0 +1,28 @@ +#ifndef ELADOCKWIDGETPRIVATE_H +#define ELADOCKWIDGETPRIVATE_H + +#include + +#include "Def.h" +#include "stdafx.h" +class ElaDockWidget; +class ElaDockWidgetTitleBar; +class ElaDockWidgetPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaDockWidget) +public: + explicit ElaDockWidgetPrivate(QObject* parent = nullptr); + ~ElaDockWidgetPrivate(); + Q_SLOT void onThemeModeChanged(ElaThemeType::ThemeMode themeMode); + +private: + qint64 _currentWinID{0}; + int _margins{8}; + ElaThemeType::ThemeMode _themeMode; + bool _isEnableMica; + int _shadowBorderWidth{6}; + ElaDockWidgetTitleBar* _titleBar{nullptr}; +}; + +#endif // ELADOCKWIDGETPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaDoubleSpinBoxPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaDoubleSpinBoxPrivate.cpp new file mode 100644 index 0000000..df8275c --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaDoubleSpinBoxPrivate.cpp @@ -0,0 +1,78 @@ +#include "ElaDoubleSpinBoxPrivate.h" + +#include +#include +#include + +#include "ElaDoubleSpinBox.h" +#include "ElaMenu.h" +ElaDoubleSpinBoxPrivate::ElaDoubleSpinBoxPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaDoubleSpinBoxPrivate::~ElaDoubleSpinBoxPrivate() +{ +} + +ElaMenu* ElaDoubleSpinBoxPrivate::_createStandardContextMenu() +{ + Q_Q(ElaDoubleSpinBox); + QLineEdit* lineEdit = q->lineEdit(); + ElaMenu* menu = new ElaMenu(q); + menu->setMenuItemHeight(27); + menu->setAttribute(Qt::WA_DeleteOnClose); + QAction* action = nullptr; + if (!lineEdit->isReadOnly()) + { + action = menu->addElaIconAction(ElaIconType::ArrowRotateLeft, tr("撤销"), QKeySequence::Undo); + action->setEnabled(lineEdit->isUndoAvailable()); + connect(action, &QAction::triggered, lineEdit, &QLineEdit::undo); + + action = menu->addElaIconAction(ElaIconType::ArrowRotateRight, tr("恢复"), QKeySequence::Redo); + action->setEnabled(lineEdit->isRedoAvailable()); + connect(action, &QAction::triggered, lineEdit, &QLineEdit::redo); + menu->addSeparator(); + } +#ifndef QT_NO_CLIPBOARD + if (!lineEdit->isReadOnly()) + { + action = menu->addElaIconAction(ElaIconType::KnifeKitchen, tr("剪切"), QKeySequence::Cut); + action->setEnabled(!lineEdit->isReadOnly() && lineEdit->hasSelectedText() && lineEdit->echoMode() == QLineEdit::Normal); + connect(action, &QAction::triggered, lineEdit, &QLineEdit::cut); + } + + action = menu->addElaIconAction(ElaIconType::Copy, tr("复制"), QKeySequence::Copy); + action->setEnabled(lineEdit->hasSelectedText() && lineEdit->echoMode() == QLineEdit::Normal); + connect(action, &QAction::triggered, lineEdit, &QLineEdit::copy); + + if (!lineEdit->isReadOnly()) + { + action = menu->addElaIconAction(ElaIconType::Paste, tr("粘贴"), QKeySequence::Paste); + action->setEnabled(!lineEdit->isReadOnly() && !QGuiApplication::clipboard()->text().isEmpty()); + connect(action, &QAction::triggered, lineEdit, &QLineEdit::paste); + } +#endif + if (!lineEdit->isReadOnly()) + { + action = menu->addElaIconAction(ElaIconType::DeleteLeft, tr("删除")); + action->setEnabled(!lineEdit->isReadOnly() && !lineEdit->text().isEmpty() && lineEdit->hasSelectedText()); + connect(action, &QAction::triggered, this, [=](bool checked) { + if (lineEdit->hasSelectedText()) + { + int startIndex = lineEdit->selectionStart(); + int endIndex = lineEdit->selectionEnd(); + lineEdit->setText(lineEdit->text().remove(startIndex, endIndex - startIndex)); + } + }); + } + if (!menu->isEmpty()) + { + menu->addSeparator(); + } + action = menu->addAction(tr("全选")); + action->setShortcut(QKeySequence::SelectAll); + action->setEnabled(!lineEdit->text().isEmpty() && !(lineEdit->selectedText() == lineEdit->text())); + connect(action, &QAction::triggered, q, &ElaDoubleSpinBox::selectAll); + return menu; +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaDoubleSpinBoxPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaDoubleSpinBoxPrivate.h new file mode 100644 index 0000000..a456c4d --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaDoubleSpinBoxPrivate.h @@ -0,0 +1,21 @@ +#ifndef ELADOUBLESPINBOXPRIVATE_H +#define ELADOUBLESPINBOXPRIVATE_H + +#include + +#include "stdafx.h" +class ElaMenu; +class ElaDoubleSpinBox; +class ElaDoubleSpinBoxPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaDoubleSpinBox) +public: + explicit ElaDoubleSpinBoxPrivate(QObject* parent = nullptr); + ~ElaDoubleSpinBoxPrivate(); + +private: + ElaMenu* _createStandardContextMenu(); +}; + +#endif // ELADOUBLESPINBOXPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaDxgiManagerPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaDxgiManagerPrivate.cpp new file mode 100644 index 0000000..da184bb --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaDxgiManagerPrivate.cpp @@ -0,0 +1,20 @@ +#include "ElaDxgiManagerPrivate.h" +#ifdef Q_OS_WIN +ElaDxgiManagerPrivate::ElaDxgiManagerPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaDxgiManagerPrivate::~ElaDxgiManagerPrivate() +{ +} + +ElaDxgiScreenPrivate::ElaDxgiScreenPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaDxgiScreenPrivate::~ElaDxgiScreenPrivate() +{ +} +#endif diff --git a/3rdparty/ElaWidgetTools/src/private/ElaDxgiManagerPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaDxgiManagerPrivate.h new file mode 100644 index 0000000..3e6350a --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaDxgiManagerPrivate.h @@ -0,0 +1,38 @@ +#ifndef ELADXGIMANAGERPRIVATE_H +#define ELADXGIMANAGERPRIVATE_H +#include +#ifdef Q_OS_WIN +#include "stdafx.h" +class ElaDxgi; +class ElaDxgiManager; +class ElaDxgiManagerPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaDxgiManager) +public: + explicit ElaDxgiManagerPrivate(QObject* parent = nullptr); + ~ElaDxgiManagerPrivate(); + +private: + Q_SIGNAL void grabScreen(); + bool _isAllowedGrabScreen{false}; + ElaDxgi* _dxgi{nullptr}; + QThread* _dxgiThread{nullptr}; +}; + +class ElaDxgiScreen; +class ElaDxgiScreenPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaDxgiScreen) + Q_PROPERTY_CREATE_D(int, BorderRadius) +public: + explicit ElaDxgiScreenPrivate(QObject* parent = nullptr); + ~ElaDxgiScreenPrivate(); + +private: + ElaDxgiManager* _dxgiManager{nullptr}; + bool _isSyncGrabSize{false}; +}; +#endif +#endif // ELADXGIMANAGERPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaEventBusPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaEventBusPrivate.cpp new file mode 100644 index 0000000..0d95a3c --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaEventBusPrivate.cpp @@ -0,0 +1,74 @@ +#include "ElaEventBusPrivate.h" + +#include "ElaEventBus.h" +ElaEventPrivate::ElaEventPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaEventPrivate::~ElaEventPrivate() +{ +} + +ElaEventBusPrivate::ElaEventBusPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaEventBusPrivate::~ElaEventBusPrivate() +{ +} + +ElaEventBusType::EventBusReturnType ElaEventBusPrivate::registerEvent(ElaEvent* event) +{ + if (!event) + { + return ElaEventBusType::EventBusReturnType::EventInvalid; + } + if (event->getEventName().isEmpty()) + { + return ElaEventBusType::EventBusReturnType::EventNameInvalid; + } + if (_eventMap.contains(event->getEventName())) + { + QList eventList = _eventMap.value(event->getEventName()); + if (eventList.contains(event)) + { + return ElaEventBusType::EventBusReturnType::EventInvalid; + } + eventList.append(event); + _eventMap[event->getEventName()] = eventList; + } + else + { + QList eventList; + eventList.append(event); + _eventMap.insert(event->getEventName(), eventList); + } + return ElaEventBusType::EventBusReturnType::Success; +} + +void ElaEventBusPrivate::unRegisterEvent(ElaEvent* event) +{ + if (!event) + { + return; + } + if (event->getEventName().isEmpty()) + { + return; + } + if (_eventMap.contains(event->getEventName())) + { + if (_eventMap[event->getEventName()].count() == 1) + { + _eventMap.remove(event->getEventName()); + } + else + { + QList eventList = _eventMap.value(event->getEventName()); + eventList.removeOne(event); + _eventMap[event->getEventName()] = eventList; + } + } +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaEventBusPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaEventBusPrivate.h new file mode 100644 index 0000000..be964ff --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaEventBusPrivate.h @@ -0,0 +1,38 @@ +#ifndef ELAEVENTBUSPRIVATE_H +#define ELAEVENTBUSPRIVATE_H + +#include +#include + +#include "Def.h" +#include "stdafx.h" +class ElaEvent; +class ElaEventPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaEvent) + Q_PROPERTY_CREATE_D(QString, EventName); + Q_PROPERTY_CREATE_D(QString, FunctionName); + Q_PROPERTY_CREATE_D(Qt::ConnectionType, ConnectionType); + +public: + explicit ElaEventPrivate(QObject* parent = nullptr); + ~ElaEventPrivate(); +}; + +class ElaEventBus; +class ElaEventBusPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaEventBus) +public: + explicit ElaEventBusPrivate(QObject* parent = nullptr); + ~ElaEventBusPrivate(); + ElaEventBusType::EventBusReturnType registerEvent(ElaEvent* event); + void unRegisterEvent(ElaEvent* event); + +private: + QMap> _eventMap; +}; + +#endif // ELAEVENTBUSPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaExponentialBlurPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaExponentialBlurPrivate.cpp new file mode 100644 index 0000000..32ebb67 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaExponentialBlurPrivate.cpp @@ -0,0 +1,102 @@ +#include "ElaExponentialBlurPrivate.h" + +#include +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +#include +#endif +int ElaExponentialBlurPrivate::_aprec = 12; +int ElaExponentialBlurPrivate::_zprec = 7; +ElaExponentialBlurPrivate::ElaExponentialBlurPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaExponentialBlurPrivate::~ElaExponentialBlurPrivate() +{ +} + +void ElaExponentialBlurPrivate::_drawExponentialBlur(QImage& image, const quint16& qRadius) +{ + if (qRadius < 1) + { + return; + } + image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); + int alpha = (int)((1 << _aprec) * (1.0f - std::exp(-2.3f / (qRadius + 1.f)))); + int height = image.height(); + int width = image.width(); + for (int row = 0; row < height; row++) + { + _drawRowBlur(image, row, alpha); + } + + for (int col = 0; col < width; col++) + { + _drawColumnBlur(image, col, alpha); + } +} + +void ElaExponentialBlurPrivate::_drawRowBlur(QImage& image, const int& row, const int& alpha) +{ + int zR, zG, zB, zA; + + QRgb* ptr = (QRgb*)image.scanLine(row); + int width = image.width(); + + zR = *((unsigned char*)ptr) << _zprec; + zG = *((unsigned char*)ptr + 1) << _zprec; + zB = *((unsigned char*)ptr + 2) << _zprec; + zA = *((unsigned char*)ptr + 3) << _zprec; + + for (int index = 0; index < width; index++) + { + _drawInnerBlur((unsigned char*)&ptr[index], zR, zG, zB, zA, alpha); + } + for (int index = width - 2; index >= 0; index--) + { + _drawInnerBlur((unsigned char*)&ptr[index], zR, zG, zB, zA, alpha); + } +} + +void ElaExponentialBlurPrivate::_drawColumnBlur(QImage& image, const int& column, const int& alpha) +{ + int zR, zG, zB, zA; + + QRgb* ptr = (QRgb*)image.bits(); + ptr += column; + int height = image.height(); + int width = image.width(); + + zR = *((unsigned char*)ptr) << _zprec; + zG = *((unsigned char*)ptr + 1) << _zprec; + zB = *((unsigned char*)ptr + 2) << _zprec; + zA = *((unsigned char*)ptr + 3) << _zprec; + + for (int index = width; index < (height - 1) * width; index += width) + { + _drawInnerBlur((unsigned char*)&ptr[index], zR, zG, zB, zA, alpha); + } + for (int index = (height - 2) * width; index >= 0; index -= width) + { + _drawInnerBlur((unsigned char*)&ptr[index], zR, zG, zB, zA, alpha); + } +} + +void ElaExponentialBlurPrivate::_drawInnerBlur(unsigned char* bptr, int& zR, int& zG, int& zB, int& zA, int alpha) +{ + int R, G, B, A; + R = *bptr; + G = *(bptr + 1); + B = *(bptr + 2); + A = *(bptr + 3); + + zR += (alpha * ((R << _zprec) - zR)) >> _aprec; + zG += (alpha * ((G << _zprec) - zG)) >> _aprec; + zB += (alpha * ((B << _zprec) - zB)) >> _aprec; + zA += (alpha * ((A << _zprec) - zA)) >> _aprec; + + *bptr = zR >> _zprec; + *(bptr + 1) = zG >> _zprec; + *(bptr + 2) = zB >> _zprec; + *(bptr + 3) = zA >> _zprec; +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaExponentialBlurPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaExponentialBlurPrivate.h new file mode 100644 index 0000000..18197bd --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaExponentialBlurPrivate.h @@ -0,0 +1,26 @@ +#ifndef ELAEXPONENTIALBLURPRIVATE_H +#define ELAEXPONENTIALBLURPRIVATE_H + +#include + +#include "stdafx.h" + +class ElaExponentialBlur; +class ElaExponentialBlurPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaExponentialBlur) +public: + explicit ElaExponentialBlurPrivate(QObject* parent = nullptr); + ~ElaExponentialBlurPrivate(); + +private: + static int _aprec; + static int _zprec; + static void _drawExponentialBlur(QImage& image, const quint16& qRadius); + static void _drawRowBlur(QImage& image, const int& row, const int& alpha); + static void _drawColumnBlur(QImage& image, const int& column, const int& alpha); + static void _drawInnerBlur(unsigned char* bptr, int& zR, int& zG, int& zB, int& zA, int alpha); +}; + +#endif // ELAEXPONENTIALBLURPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaFlowLayoutPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaFlowLayoutPrivate.cpp new file mode 100644 index 0000000..a484678 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaFlowLayoutPrivate.cpp @@ -0,0 +1,105 @@ +#include "ElaFlowLayoutPrivate.h" + +#include +#include + +#include "ElaFlowLayout.h" +ElaFlowLayoutPrivate::ElaFlowLayoutPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaFlowLayoutPrivate::~ElaFlowLayoutPrivate() +{ +} + +int ElaFlowLayoutPrivate::_doLayout(const QRect& rect, bool testOnly) const +{ + Q_Q(const ElaFlowLayout); + int left, top, right, bottom; + q->getContentsMargins(&left, &top, &right, &bottom); + QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom); + int x = effectiveRect.x(); + int y = effectiveRect.y(); + int lineHeight = 0; + + for (QLayoutItem* item : std::as_const(_itemList)) + { + const QWidget* wid = item->widget(); + int spaceX = q->horizontalSpacing(); + if (spaceX == -1) + { + spaceX = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal); + } + int spaceY = q->verticalSpacing(); + if (spaceY == -1) + { + spaceY = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical); + } + + int nextX = x + item->sizeHint().width() + spaceX; + if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) + { + x = effectiveRect.x(); + y = y + lineHeight + spaceY; + nextX = x + item->sizeHint().width() + spaceX; + lineHeight = 0; + } + if (!_lastGeometryMap.contains(item)) + { + _lastGeometryMap.insert(item, QPoint(x, y)); + } + if (!testOnly) + { + if ((item->geometry().x() == 0 && item->geometry().y() == 0)) + { + item->setGeometry(QRect(QPoint(x, y), item->sizeHint())); + } + else if (item->geometry().x() != x || item->geometry().y() != y) + { + if (_isAnimation) + { + // 阻止多重动画 + if (_lastGeometryMap[item] == QPoint(x, y)) + { + lineHeight = qMax(lineHeight, item->sizeHint().height()); + return y + lineHeight - rect.y() + bottom; + } + QPropertyAnimation* geometryAnimation = new QPropertyAnimation(item->widget(), "geometry"); + geometryAnimation->setStartValue(item->widget()->geometry()); + geometryAnimation->setEndValue(QRect(QPoint(x, y), item->sizeHint())); + geometryAnimation->setDuration(300); + geometryAnimation->setEasingCurve(QEasingCurve::InOutSine); + geometryAnimation->start(QAbstractAnimation::DeleteWhenStopped); + _lastGeometryMap[item] = QPoint(x, y); + } + else + { + item->setGeometry(QRect(QPoint(x, y), item->sizeHint())); + } + } + } + x = nextX; + lineHeight = qMax(lineHeight, item->sizeHint().height()); + } + return y + lineHeight - rect.y() + bottom; +} + +int ElaFlowLayoutPrivate::_smartSpacing(QStyle::PixelMetric pm) const +{ + Q_Q(const ElaFlowLayout); + QObject* parent = q->parent(); + if (!parent) + { + return -1; + } + else if (parent->isWidgetType()) + { + QWidget* pw = static_cast(parent); + return pw->style()->pixelMetric(pm, nullptr, pw); + } + else + { + return static_cast(parent)->spacing(); + } +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaFlowLayoutPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaFlowLayoutPrivate.h new file mode 100644 index 0000000..7295cc8 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaFlowLayoutPrivate.h @@ -0,0 +1,30 @@ +#ifndef ELAFLOWLAYOUTPRIVATE_H +#define ELAFLOWLAYOUTPRIVATE_H + +#include +#include +#include +#include + +#include "stdafx.h" +class ElaFlowLayout; +class ElaFlowLayoutPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaFlowLayout) +public: + explicit ElaFlowLayoutPrivate(QObject* parent = nullptr); + ~ElaFlowLayoutPrivate(); + +private: + int _doLayout(const QRect& rect, bool testOnly) const; + int _smartSpacing(QStyle::PixelMetric pm) const; + mutable QMap _lastHeightMap; + mutable QMap _lastGeometryMap; + QList _itemList; + bool _isAnimation{false}; + int _hSpacing; + int _vSpacing; +}; + +#endif // ELAFLOWLAYOUTPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaGraphicsItemPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaGraphicsItemPrivate.cpp new file mode 100644 index 0000000..d6cacf2 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaGraphicsItemPrivate.cpp @@ -0,0 +1,34 @@ +#include "ElaGraphicsItemPrivate.h" + +ElaGraphicsItemPrivate::ElaGraphicsItemPrivate(QObject* parent) + : QObject(parent) +{ +} + +ElaGraphicsItemPrivate::~ElaGraphicsItemPrivate() +{ +} + +QDataStream& operator<<(QDataStream& stream, const ElaGraphicsItemPrivate* data) +{ + stream << data->_itemUID; + stream << data->_pItemName; + stream << data->_pMaxLinkPortCount; + stream << data->_currentLinkPortState; + stream << data->_pDataRoutes; + stream << data->_pItemImage; + stream << data->_pItemSelectedImage; + return stream; +} + +QDataStream& operator>>(QDataStream& stream, ElaGraphicsItemPrivate* data) +{ + stream >> data->_itemUID; + stream >> data->_pItemName; + stream >> data->_pMaxLinkPortCount; + stream >> data->_currentLinkPortState; + stream >> data->_pDataRoutes; + stream >> data->_pItemImage; + stream >> data->_pItemSelectedImage; + return stream; +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaGraphicsItemPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaGraphicsItemPrivate.h new file mode 100644 index 0000000..27910f2 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaGraphicsItemPrivate.h @@ -0,0 +1,31 @@ +#ifndef ELAGRAPHICSITEMPRIVATE_H +#define ELAGRAPHICSITEMPRIVATE_H + +#include +#include + +#include "ElaGraphicsItem.h" +class ElaGraphicsItemPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaGraphicsItem) + Q_PROPERTY_CREATE_D(int, Width) + Q_PROPERTY_CREATE_D(int, Height) + Q_PROPERTY_CREATE_D(QImage, ItemImage) + Q_PROPERTY_CREATE_D(QImage, ItemSelectedImage) + Q_PROPERTY_CREATE_D(QString, ItemName) + Q_PROPERTY_CREATE_D(QVariantMap, DataRoutes) + Q_PROPERTY_CREATE_D(int, MaxLinkPortCount) +public: + explicit ElaGraphicsItemPrivate(QObject* parent = nullptr); + ~ElaGraphicsItemPrivate(); + + friend QDataStream& operator<<(QDataStream& stream, const ElaGraphicsItemPrivate* data); + friend QDataStream& operator>>(QDataStream& stream, ElaGraphicsItemPrivate* data); + +private: + QString _itemUID; + QVector _currentLinkPortState; +}; + +#endif // ELAGRAPHICSITEMPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaGraphicsLineItemPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaGraphicsLineItemPrivate.cpp new file mode 100644 index 0000000..49bb78a --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaGraphicsLineItemPrivate.cpp @@ -0,0 +1,9 @@ +#include "ElaGraphicsLineItemPrivate.h" + +ElaGraphicsLineItemPrivate::ElaGraphicsLineItemPrivate() +{ +} + +ElaGraphicsLineItemPrivate::~ElaGraphicsLineItemPrivate() +{ +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaGraphicsLineItemPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaGraphicsLineItemPrivate.h new file mode 100644 index 0000000..b208af4 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaGraphicsLineItemPrivate.h @@ -0,0 +1,30 @@ +#ifndef ELAGRAPHICSLINEITEMPRIVATE_H +#define ELAGRAPHICSLINEITEMPRIVATE_H + +#include +#include +#include + +#include "stdafx.h" +class ElaGraphicsItem; +class ElaGraphicsLineItem; +class ElaGraphicsLineItemPrivate +{ + Q_D_CREATE(ElaGraphicsLineItem) + Q_PRIVATE_CREATE_D(QPointF, StartPoint); + Q_PRIVATE_CREATE_D(QPointF, EndPoint); + Q_PRIVATE_CREATE_D(ElaGraphicsItem*, StartItem); + Q_PRIVATE_CREATE_D(ElaGraphicsItem*, EndItem); + Q_PRIVATE_CREATE_D(int, StartItemPort); + Q_PRIVATE_CREATE_D(int, EndItemPort); + +public: + explicit ElaGraphicsLineItemPrivate(); + ~ElaGraphicsLineItemPrivate(); + +private: + QMap _linkItemMap; + bool _isCreateWithItem{true}; +}; + +#endif // ELAGRAPHICSLINEITEMPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaGraphicsScenePrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaGraphicsScenePrivate.cpp new file mode 100644 index 0000000..483f3fa --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaGraphicsScenePrivate.cpp @@ -0,0 +1,84 @@ +#include "ElaGraphicsScenePrivate.h" + +#include "ElaGraphicsItem.h" +#include "ElaGraphicsLineItem.h" +#include "ElaGraphicsScene.h" +ElaGraphicsScenePrivate::ElaGraphicsScenePrivate(QObject* parent) + : QObject(parent) +{ +} + +ElaGraphicsScenePrivate::~ElaGraphicsScenePrivate() +{ +} + +QDataStream& operator<<(QDataStream& stream, const ElaGraphicsScenePrivate* data) +{ + QStringList keyList = data->_items.keys(); + QList itemList = data->_items.values(); + stream << keyList; + for (int i = 0; i < keyList.count(); i++) + { + stream << itemList[i]; + } + stream << data->_itemsLink; + return stream; +} + +QDataStream& operator>>(QDataStream& stream, ElaGraphicsScenePrivate* data) +{ + QStringList keyList; + stream >> keyList; + QList itemList = data->_serializeItem(keyList.count()); + for (int i = 0; i < keyList.count(); i++) + { + stream >> itemList[i]; + } + for (int i = 0; i < keyList.count(); i++) + { + data->_items.insert(keyList[i], itemList[i]); + } + stream >> data->_itemsLink; + data->_deserializeLink(); + return stream; +} + +QList ElaGraphicsScenePrivate::_serializeItem(int count) +{ + Q_Q(ElaGraphicsScene); + QList itemList; + for (int i = 0; i < count; i++) + { + ElaGraphicsItem* item = new ElaGraphicsItem(); + item->setParent(q); + itemList.append(item); + q->QGraphicsScene::addItem(item); + _currentZ++; + } + return itemList; +} + +void ElaGraphicsScenePrivate::_removeLinkLineItem() +{ + Q_Q(ElaGraphicsScene); + if (_linkLineItem) + { + q->QGraphicsScene::removeItem(_linkLineItem); + delete _linkLineItem; + _linkLineItem = nullptr; + } +} + +void ElaGraphicsScenePrivate::_deserializeLink() +{ + Q_Q(ElaGraphicsScene); + for (int i = 0; i < _itemsLink.count(); i++) + { + QVariantMap itemLinkData = _itemsLink[i]; + QList uidList = itemLinkData.keys(); + QList portList = itemLinkData.values(); + ElaGraphicsLineItem* lineItem = new ElaGraphicsLineItem(_items.value(uidList[0]), _items.value(uidList[1]), portList[0].toInt(), portList[1].toInt()); + q->QGraphicsScene::addItem(lineItem); + _lineItemsList.append(lineItem); + } +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaGraphicsScenePrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaGraphicsScenePrivate.h new file mode 100644 index 0000000..9a28b52 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaGraphicsScenePrivate.h @@ -0,0 +1,40 @@ +#ifndef ELAGRAPHICSSCENEPRIVATE_H +#define ELAGRAPHICSSCENEPRIVATE_H + +#include +#include +#include + +#include "ElaGraphicsScene.h" +#include "stdafx.h" +class ElaGraphicsItem; +class ElaGraphicsLineItem; +class ElaGraphicsScenePrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaGraphicsScene) + Q_PROPERTY_CREATE_D(bool, IsCheckLinkPort) + Q_PROPERTY_CREATE_D(QString, SerializePath) +public: + explicit ElaGraphicsScenePrivate(QObject* parent = nullptr); + ~ElaGraphicsScenePrivate(); + + friend QDataStream& operator<<(QDataStream& stream, const ElaGraphicsScenePrivate* data); + friend QDataStream& operator>>(QDataStream& stream, ElaGraphicsScenePrivate* data); + +private: + bool _isLeftButtonPress{false}; + QMap _items; // 存储所有item + ElaGraphicsSceneType::SceneMode _sceneMode; + QList _itemsLink; // item连接状态 + QList _lineItemsList; + ElaGraphicsLineItem* _linkLineItem{nullptr}; + qreal _currentZ{1}; + QPointF _lastPos; + QPointF _lastLeftPressPos; + QList _serializeItem(int count); + void _removeLinkLineItem(); + void _deserializeLink(); +}; + +#endif // ELAGRAPHICSSCENEPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaGraphicsViewPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaGraphicsViewPrivate.cpp new file mode 100644 index 0000000..2fa7ad0 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaGraphicsViewPrivate.cpp @@ -0,0 +1,26 @@ +#include "ElaGraphicsViewPrivate.h" + +#include "ElaGraphicsView.h" +#include "ElaScrollBar.h" +ElaGraphicsViewPrivate::ElaGraphicsViewPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaGraphicsViewPrivate::~ElaGraphicsViewPrivate() +{ +} + +void ElaGraphicsViewPrivate::_initStyle() +{ + Q_Q(ElaGraphicsView); + q->setDragMode(QGraphicsView::RubberBandDrag); + q->setTransformationAnchor(QGraphicsView::AnchorUnderMouse); + q->setResizeAnchor(QGraphicsView::AnchorUnderMouse); + q->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform); + q->setMouseTracking(true); + q->setObjectName("ElaGraphicsView"); + q->setStyleSheet("#ElaGraphicsView{background-color:white;border:0px;}"); + q->setVerticalScrollBar(new ElaScrollBar(q)); + q->setHorizontalScrollBar(new ElaScrollBar(q)); +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaGraphicsViewPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaGraphicsViewPrivate.h new file mode 100644 index 0000000..dd023b8 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaGraphicsViewPrivate.h @@ -0,0 +1,23 @@ +#ifndef ELAGRAPHICSVIEWPRIVATE_H +#define ELAGRAPHICSVIEWPRIVATE_H + +#include + +#include "stdafx.h" +class ElaGraphicsView; +class ElaGraphicsViewPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaGraphicsView) + Q_PROPERTY_CREATE_D(qreal, MaxTransform); + Q_PROPERTY_CREATE_D(qreal, MinTransform); + +public: + explicit ElaGraphicsViewPrivate(QObject* parent = nullptr); + ~ElaGraphicsViewPrivate(); + +private: + void _initStyle(); +}; + +#endif // ELAGRAPHICSVIEWPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaIconButtonPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaIconButtonPrivate.cpp new file mode 100644 index 0000000..c20c2bf --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaIconButtonPrivate.cpp @@ -0,0 +1,10 @@ +#include "ElaIconButtonPrivate.h" + +ElaIconButtonPrivate::ElaIconButtonPrivate(QObject *parent) + : QObject{parent} +{} + +ElaIconButtonPrivate::~ElaIconButtonPrivate() +{ + +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaIconButtonPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaIconButtonPrivate.h new file mode 100644 index 0000000..84e45ba --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaIconButtonPrivate.h @@ -0,0 +1,36 @@ +#ifndef ELAICONBUTTONPRIVATE_H +#define ELAICONBUTTONPRIVATE_H + +#include +#include +#include + +#include "Def.h" +#include "stdafx.h" +class ElaIconButton; +class ElaIconButtonPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaIconButton) + Q_PROPERTY_CREATE_D(int, BorderRadius) + Q_PROPERTY_CREATE_D(qreal, Opacity); + Q_PROPERTY_CREATE_D(ElaIconType::IconName, Awesome) + Q_PROPERTY_CREATE_D(QColor, LightHoverColor); + Q_PROPERTY_CREATE_D(QColor, DarkHoverColor); + Q_PROPERTY_CREATE_D(QColor, LightIconColor); + Q_PROPERTY_CREATE_D(QColor, DarkIconColor); + Q_PROPERTY_CREATE_D(QColor, LightHoverIconColor); + Q_PROPERTY_CREATE_D(QColor, DarkHoverIconColor); + Q_PROPERTY_CREATE_D(bool, IsSelected); + Q_PROPERTY_CREATE(int, HoverAlpha) +public: + explicit ElaIconButtonPrivate(QObject* parent = nullptr); + ~ElaIconButtonPrivate(); + +private: + QPixmap _iconPix; + bool _isAlphaAnimationFinished{true}; + ElaThemeType::ThemeMode _themeMode; +}; + +#endif // ELAICONBUTTONPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaImageCardPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaImageCardPrivate.cpp new file mode 100644 index 0000000..8b2c799 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaImageCardPrivate.cpp @@ -0,0 +1,10 @@ +#include "ElaImageCardPrivate.h" + +ElaImageCardPrivate::ElaImageCardPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaImageCardPrivate::~ElaImageCardPrivate() +{ +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaImageCardPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaImageCardPrivate.h new file mode 100644 index 0000000..a724262 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaImageCardPrivate.h @@ -0,0 +1,26 @@ +#ifndef ELAIMAGECARDPRIVATE_H +#define ELAIMAGECARDPRIVATE_H + +#include +#include + +#include "Def.h" + +class ElaImageCard; +class ElaImageCardPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaImageCard) + Q_PROPERTY_CREATE_D(QImage, CardImage); + Q_PROPERTY_CREATE_D(int, BorderRadius) + Q_PROPERTY_CREATE_D(bool, IsPreserveAspectCrop) + Q_PROPERTY_CREATE_D(qreal, MaximumAspectRatio) +public: + explicit ElaImageCardPrivate(QObject* parent = nullptr); + ~ElaImageCardPrivate(); + +private: + ElaThemeType::ThemeMode _themeMode; +}; + +#endif // ELAIMAGECARDPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaInteractiveCardPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaInteractiveCardPrivate.cpp new file mode 100644 index 0000000..02dbdcd --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaInteractiveCardPrivate.cpp @@ -0,0 +1,10 @@ +#include "ElaInteractiveCardPrivate.h" + +ElaInteractiveCardPrivate::ElaInteractiveCardPrivate(QObject *parent) + : QObject{parent} +{} + +ElaInteractiveCardPrivate::~ElaInteractiveCardPrivate() +{ + +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaInteractiveCardPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaInteractiveCardPrivate.h new file mode 100644 index 0000000..43ebf72 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaInteractiveCardPrivate.h @@ -0,0 +1,35 @@ +#ifndef ELAINTERACTIVECARDPRIVATE_H +#define ELAINTERACTIVECARDPRIVATE_H + +#include +#include +#include +#include + +#include "Def.h" +#include "stdafx.h" +class ElaInteractiveCard; +class ElaInteractiveCardPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaInteractiveCard) + Q_PROPERTY_CREATE_D(int, BorderRadius) + Q_PROPERTY_CREATE_D(QString, Title); + Q_PROPERTY_CREATE_D(QString, SubTitle); + Q_PROPERTY_CREATE_D(int, TitlePixelSize); + Q_PROPERTY_CREATE_D(int, SubTitlePixelSize); + Q_PROPERTY_CREATE_D(int, TitleSpacing); + Q_PROPERTY_CREATE_D(QPixmap, CardPixmap); + Q_PROPERTY_CREATE_D(QSize, CardPixmapSize); + Q_PROPERTY_CREATE_D(int, CardPixmapBorderRadius) + Q_PROPERTY_CREATE_D(ElaCardPixType::PixMode, CardPixMode); + +public: + explicit ElaInteractiveCardPrivate(QObject* parent = nullptr); + ~ElaInteractiveCardPrivate(); + +private: + ElaThemeType::ThemeMode _themeMode; +}; + +#endif // ELAINTERACTIVECARDPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaKeyBinderPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaKeyBinderPrivate.cpp new file mode 100644 index 0000000..4f333a5 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaKeyBinderPrivate.cpp @@ -0,0 +1,27 @@ +#include "ElaKeyBinderPrivate.h" +#include "ElaKeyBinder.h" +ElaKeyBinderPrivate::ElaKeyBinderPrivate(QObject* parent) + : QObject(parent) +{ +} + +ElaKeyBinderPrivate::~ElaKeyBinderPrivate() +{ +} + +void ElaKeyBinderPrivate::onThemeChanged(ElaThemeType::ThemeMode themeMode) +{ + Q_Q(ElaKeyBinder); + _themeMode = themeMode; + QPalette palette = q->palette(); + if (themeMode == ElaThemeType::Light) + { + palette.setColor(QPalette::WindowText, Qt::black); + q->setPalette(palette); + } + else + { + palette.setColor(QPalette::WindowText, Qt::white); + q->setPalette(palette); + } +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaKeyBinderPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaKeyBinderPrivate.h new file mode 100644 index 0000000..15a5ede --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaKeyBinderPrivate.h @@ -0,0 +1,25 @@ +#ifndef ELAWIDGETTOOLS_SRC_PRIVATE_ELAKEYBINDERPRIVATE_H_ +#define ELAWIDGETTOOLS_SRC_PRIVATE_ELAKEYBINDERPRIVATE_H_ + +#include "Def.h" +#include +class ElaKeyBinder; +class ElaContentDialog; +class ElaKeyBinderContainer; +class ElaKeyBinderPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaKeyBinder) + Q_PROPERTY_CREATE_D(int, BorderRadius) +public: + explicit ElaKeyBinderPrivate(QObject* parent = nullptr); + ~ElaKeyBinderPrivate(); + Q_SLOT void onThemeChanged(ElaThemeType::ThemeMode themeMode); + +private: + ElaThemeType::ThemeMode _themeMode; + ElaContentDialog* _binderDialog{nullptr}; + ElaKeyBinderContainer* _binderContainer{nullptr}; +}; + +#endif//ELAWIDGETTOOLS_SRC_PRIVATE_ELAKEYBINDERPRIVATE_H_ diff --git a/3rdparty/ElaWidgetTools/src/private/ElaLCDNumberPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaLCDNumberPrivate.cpp new file mode 100644 index 0000000..c95c9ac --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaLCDNumberPrivate.cpp @@ -0,0 +1,20 @@ +#include "ElaLCDNumberPrivate.h" +#include "ElaLCDNumber.h" +#include "ElaTheme.h" +ElaLCDNumberPrivate::ElaLCDNumberPrivate(QObject* parent) + : QObject(parent) +{ +} + +ElaLCDNumberPrivate::~ElaLCDNumberPrivate() +{ +} + +void ElaLCDNumberPrivate::onThemeModeChanged(ElaThemeType::ThemeMode themeMode) +{ + Q_Q(ElaLCDNumber); + q->foregroundRole(); + QPalette palette = q->palette(); + palette.setColor(QPalette::WindowText, ElaThemeColor(themeMode, BasicText)); + q->setPalette(palette); +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaLCDNumberPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaLCDNumberPrivate.h new file mode 100644 index 0000000..d80aa51 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaLCDNumberPrivate.h @@ -0,0 +1,27 @@ +#ifndef ELAWIDGETTOOLS_ELALCDNUMBERPRIVATE_H +#define ELAWIDGETTOOLS_ELALCDNUMBERPRIVATE_H + +#include "Def.h" + +#include +class QTimer; +class ElaLCDNumber; +class ElaLCDNumberStyle; +class ElaLCDNumberPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaLCDNumber) + Q_PROPERTY_CREATE_D(bool, IsUseAutoClock) + Q_PROPERTY_CREATE_D(QString, AutoClockFormat) +public: + explicit ElaLCDNumberPrivate(QObject* parent = nullptr); + ~ElaLCDNumberPrivate() override; + + Q_SLOT void onThemeModeChanged(ElaThemeType::ThemeMode themeMode); + +private: + QTimer* _clockTimer{nullptr}; + ElaLCDNumberStyle* _lcdNumberStyle{nullptr}; +}; + +#endif //ELAWIDGETTOOLS_ELALCDNUMBERPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaLineEditPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaLineEditPrivate.cpp new file mode 100644 index 0000000..51768e0 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaLineEditPrivate.cpp @@ -0,0 +1,57 @@ +#include "ElaLineEditPrivate.h" + +#include "ElaApplication.h" +#include "ElaLineEdit.h" + +ElaLineEditPrivate::ElaLineEditPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaLineEditPrivate::~ElaLineEditPrivate() +{ +} + +void ElaLineEditPrivate::onWMWindowClickedEvent(QVariantMap data) +{ + Q_Q(ElaLineEdit); + ElaAppBarType::WMMouseActionType actionType = data.value("WMClickType").value(); + if (actionType == ElaAppBarType::WMLBUTTONDOWN) + { + if (q->hasSelectedText() && q->hasFocus()) + { + q->clearFocus(); + } + } + else if (actionType == ElaAppBarType::WMLBUTTONUP || actionType == ElaAppBarType::WMNCLBUTTONDOWN) + { + if (ElaApplication::containsCursorToItem(q) || (actionType == ElaAppBarType::WMLBUTTONUP && q->hasSelectedText())) + { + return; + } + if (q->hasFocus()) + { + q->clearFocus(); + } + } +} + +void ElaLineEditPrivate::onThemeChanged(ElaThemeType::ThemeMode themeMode) +{ + Q_Q(ElaLineEdit); + _themeMode = themeMode; + if (themeMode == ElaThemeType::Light) + { + QPalette palette; + palette.setColor(QPalette::Text, Qt::black); + palette.setColor(QPalette::PlaceholderText, QColor(0x00, 0x00, 0x00, 128)); + q->setPalette(palette); + } + else + { + QPalette palette; + palette.setColor(QPalette::Text, Qt::white); + palette.setColor(QPalette::PlaceholderText, QColor(0xBA, 0xBA, 0xBA)); + q->setPalette(palette); + } +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaLineEditPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaLineEditPrivate.h new file mode 100644 index 0000000..2729018 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaLineEditPrivate.h @@ -0,0 +1,29 @@ +#ifndef ELALINEEDITPRIVATE_H +#define ELALINEEDITPRIVATE_H + +#include + +#include "Def.h" +#include "stdafx.h" +class ElaEvent; +class ElaLineEdit; +class ElaLineEditPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaLineEdit) + Q_PROPERTY_CREATE_D(int, BorderRadius) + Q_PROPERTY_CREATE_D(bool, IsClearButtonEnable) + Q_PROPERTY_CREATE(qreal, ExpandMarkWidth) +public: + explicit ElaLineEditPrivate(QObject* parent = nullptr); + ~ElaLineEditPrivate(); + Q_INVOKABLE void onWMWindowClickedEvent(QVariantMap data); + Q_SLOT void onThemeChanged(ElaThemeType::ThemeMode themeMode); + +private: + ElaThemeType::ThemeMode _themeMode; + ElaEvent* _focusEvent{nullptr}; + qreal _textSpacing{0.5}; +}; + +#endif // ELALINEEDITPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaListViewPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaListViewPrivate.cpp new file mode 100644 index 0000000..a19d6a1 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaListViewPrivate.cpp @@ -0,0 +1,10 @@ +#include "ElaListViewPrivate.h" + +ElaListViewPrivate::ElaListViewPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaListViewPrivate::~ElaListViewPrivate() +{ +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaListViewPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaListViewPrivate.h new file mode 100644 index 0000000..304a40d --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaListViewPrivate.h @@ -0,0 +1,22 @@ +#ifndef ELALISTVIEWPRIVATE_H +#define ELALISTVIEWPRIVATE_H + +#include + +#include "stdafx.h" + +class ElaListView; +class ElaListViewStyle; +class ElaListViewPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaListView) +public: + explicit ElaListViewPrivate(QObject* parent = nullptr); + ~ElaListViewPrivate(); + +private: + ElaListViewStyle* _listViewStyle{nullptr}; +}; + +#endif // ELALISTVIEWPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaLogPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaLogPrivate.cpp new file mode 100644 index 0000000..79de5ea --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaLogPrivate.cpp @@ -0,0 +1,103 @@ +#include "ElaLogPrivate.h" + +#include +#include +#include +#include +#ifndef QT_NO_DEBUG +#include +#endif +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +#include +#endif +#include "ElaLog.h" +Q_GLOBAL_STATIC(QMutex, messageLogMutex) +Q_GLOBAL_STATIC(QString, logFileNameTime) +ElaLogPrivate::ElaLogPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaLogPrivate::~ElaLogPrivate() +{ +} + +void ElaLogPrivate::_messageLogHander(QtMsgType type, const QMessageLogContext& ctx, const QString& msg) +{ + if (type > QtCriticalMsg) + { + return; + } + QString logInfo; + QString logTime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + switch (type) + { + case QtDebugMsg: + { + logInfo = QString("[信息-%1](函数: %2 , 行数: %3) -> %4").arg(logTime, ctx.function, QString::number(ctx.line), msg); + break; + } + case QtWarningMsg: + { + logInfo = QString("[警告-%1](函数: %2 , 行数: %3) -> %4").arg(logTime, ctx.function, QString::number(ctx.line), msg); + break; + } + case QtCriticalMsg: + { + logInfo = QString("[错误-%1](函数: %2 , 行数: %3) -> %4").arg(logTime, ctx.function, QString::number(ctx.line), msg); + break; + } + default: + { + qCritical("发生致命错误!"); + break; + } + } + qDebug() << logInfo; + ElaLog* log = ElaLog::getInstance(); + Q_EMIT log->logMessage(logInfo); + messageLogMutex->lock(); + QFile logfile; + if (log->getIsLogFileNameWithTime()) + { + logfile.setFileName(log->getLogSavePath() + "\\" + log->getLogFileName() + *logFileNameTime + ".txt"); + } + else + { + logfile.setFileName(log->getLogSavePath() + "\\" + log->getLogFileName() + ".txt"); + } + if (logfile.open(QIODevice::WriteOnly | QIODevice::Append)) + { + QTextStream logFileStream(&logfile); +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + logFileStream << logInfo << Qt::endl; +#else + logFileStream << logInfo << endl; +#endif + logfile.close(); + } + messageLogMutex->unlock(); +} + +void ElaLogPrivate::_clearLogFile() +{ + if (_pIsLogFileNameWithTime) + { + QString logTime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh-mm-ss"); + logTime.prepend("_"); + logTime.replace(" ", "_"); + logFileNameTime->clear(); + logFileNameTime->append(logTime); + } + else + { + QFile file(_pLogSavePath + "\\" + _pLogFileName + ".txt"); + if (file.exists()) + { + if (file.open(QIODevice::WriteOnly | QIODevice::Text | QFile::Truncate)) + { + file.close(); + } + } + } +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaLogPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaLogPrivate.h new file mode 100644 index 0000000..936de75 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaLogPrivate.h @@ -0,0 +1,24 @@ +#ifndef ELALOGPRIVATE_H +#define ELALOGPRIVATE_H + +#include + +#include "stdafx.h" +class ElaLog; +class ElaLogPrivate : public QObject +{ + Q_OBJECT + Q_PROPERTY_CREATE_D(QString, LogSavePath) + Q_PROPERTY_CREATE_D(QString, LogFileName) + Q_PROPERTY_CREATE_D(bool, IsLogFileNameWithTime) + Q_D_CREATE(ElaLog) +public: + explicit ElaLogPrivate(QObject* parent = nullptr); + ~ElaLogPrivate(); + +private: + static void _messageLogHander(QtMsgType type, const QMessageLogContext& ctx, const QString& msg); + void _clearLogFile(); +}; + +#endif // ELALOGPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaMenuPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaMenuPrivate.cpp new file mode 100644 index 0000000..a33ba68 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaMenuPrivate.cpp @@ -0,0 +1,16 @@ +#include "ElaMenuPrivate.h" + +#include +#include +#include +#include + +#include "ElaMenu.h" +ElaMenuPrivate::ElaMenuPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaMenuPrivate::~ElaMenuPrivate() +{ +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaMenuPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaMenuPrivate.h new file mode 100644 index 0000000..4e404cb --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaMenuPrivate.h @@ -0,0 +1,27 @@ +#ifndef ELAMENUPRIVATE_H +#define ELAMENUPRIVATE_H + +#include +#include +#include + +#include "stdafx.h" +class ElaMenu; +class ElaMenuStyle; +class ElaMenuPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaMenu) + Q_PROPERTY_CREATE(int, AnimationImagePosY) +public: + explicit ElaMenuPrivate(QObject* parent = nullptr); + ~ElaMenuPrivate(); + +private: + QPixmap _animationPix; + bool _isCloseAnimation{false}; + QPoint _mousePressPoint; + ElaMenuStyle* _menuStyle{nullptr}; +}; + +#endif // ELAMENUPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaMessageBarPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaMessageBarPrivate.cpp new file mode 100644 index 0000000..441e5e0 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaMessageBarPrivate.cpp @@ -0,0 +1,542 @@ +#include "ElaMessageBarPrivate.h" + +#include +#include +#include +#include +#include +#include + +#include "ElaIconButton.h" +#include "ElaMessageBar.h" +Q_SINGLETON_CREATE_CPP(ElaMessageBarManager) +QMap*> _messageBarActiveMap; +ElaMessageBarManager::ElaMessageBarManager(QObject* parent) +{ +} + +ElaMessageBarManager::~ElaMessageBarManager() +{ +} + +void ElaMessageBarManager::requestMessageBarEvent(ElaMessageBar* messageBar) +{ + if (!messageBar) + { + return; + } + if (_messageBarEventMap.contains(messageBar)) + { + QList eventList = _messageBarEventMap.value(messageBar); + QVariantMap eventData = eventList.last(); + eventList.removeLast(); + if (eventList.isEmpty()) + { + _messageBarEventMap.remove(messageBar); + } + else + { + _messageBarEventMap[messageBar] = eventList; + } + //触发事件 + QString functionName = eventData.value("EventFunctionName").toString(); + QVariantMap functionData = eventData.value("EventFunctionData").toMap(); + QMetaObject::invokeMethod(messageBar->d_func(), functionName.toLocal8Bit().constData(), Qt::AutoConnection, Q_ARG(QVariantMap, functionData)); + } +} + +void ElaMessageBarManager::postMessageBarCreateEvent(ElaMessageBar* messageBar) +{ + if (!messageBar) + { + return; + } + updateActiveMap(messageBar, true); // 计算坐标前增加 + if (!_messageBarEventMap.contains(messageBar)) + { + QList eventList; + QVariantMap eventData; + eventData.insert("EventFunctionName", "messageBarEnd"); + eventList.append(eventData); + _messageBarEventMap.insert(messageBar, eventList); + } +} + +void ElaMessageBarManager::postMessageBarEndEvent(ElaMessageBar* messageBar) +{ + if (!messageBar) + { + return; + } + updateActiveMap(messageBar, false); + //Other MessageBar事件入栈 记录同一策略事件 + ElaMessageBarType::PositionPolicy policy = messageBar->d_ptr->_policy; + foreach (auto otherMessageBar, *_messageBarActiveMap.value(policy)) + { + if (otherMessageBar->d_ptr->_judgeCreateOrder(messageBar)) + { + QList eventList = _messageBarEventMap[otherMessageBar]; + //优先执行先触发的事件 End事件保持首位 + QVariantMap eventData; + eventData.insert("EventFunctionName", "onOtherMessageBarEnd"); + QVariantMap functionData; + functionData.insert("TargetPosY", otherMessageBar->d_ptr->_calculateTargetPosY()); + eventData.insert("EventFunctionData", functionData); + //若处于创建动画阶段 则合并事件动画 + if (otherMessageBar->d_ptr->getWorkMode() == WorkStatus::CreateAnimation) + { + while (eventList.count() > 1) + { + eventList.removeLast(); + } + } + eventList.insert(1, eventData); + _messageBarEventMap[otherMessageBar] = eventList; + otherMessageBar->d_ptr->tryToRequestMessageBarEvent(); + } + } +} + +void ElaMessageBarManager::forcePostMessageBarEndEvent(ElaMessageBar* messageBar) +{ + if (!messageBar) + { + return; + } + //清除事件堆栈记录 + _messageBarEventMap.remove(messageBar); + //发布终止事件 + postMessageBarEndEvent(messageBar); +} + +int ElaMessageBarManager::getMessageBarEventCount(ElaMessageBar* messageBar) +{ + if (!messageBar) + { + return -1; + } + if (!_messageBarEventMap.contains(messageBar)) + { + return -1; + } + QList eventList = _messageBarEventMap[messageBar]; + return eventList.count(); +} + +void ElaMessageBarManager::updateActiveMap(ElaMessageBar* messageBar, bool isActive) +{ + if (!messageBar) + { + return; + } + ElaMessageBarType::PositionPolicy policy = messageBar->d_ptr->_policy; + if (isActive) + { + if (_messageBarActiveMap.contains(policy)) + { + _messageBarActiveMap[policy]->append(messageBar); + } + else + { + QList* messageBarList = new QList(); + messageBarList->append(messageBar); + _messageBarActiveMap.insert(policy, messageBarList); + } + } + else + { + if (_messageBarActiveMap.contains(policy)) + { + if (_messageBarActiveMap[policy]->count() > 0) + { + _messageBarActiveMap[policy]->removeOne(messageBar); + } + } + } +} + +ElaMessageBarPrivate::ElaMessageBarPrivate(QObject* parent) + : QObject{parent} +{ + setProperty("MessageBarClosedY", 0); + setProperty("MessageBarFinishY", 0); + _createTime = QDateTime::currentMSecsSinceEpoch(); +} + +ElaMessageBarPrivate::~ElaMessageBarPrivate() +{ +} + +void ElaMessageBarPrivate::tryToRequestMessageBarEvent() +{ + Q_Q(ElaMessageBar); + if (!_isMessageBarCreateAnimationFinished || _isMessageBarEventAnimationStart) + { + return; + } + ElaMessageBarManager::getInstance()->requestMessageBarEvent(q); +} + +WorkStatus ElaMessageBarPrivate::getWorkMode() const +{ + if (!_isMessageBarCreateAnimationFinished) + { + return WorkStatus::CreateAnimation; + } + if (_isMessageBarEventAnimationStart) + { + return WorkStatus::OtherEventAnimation; + } + return WorkStatus::Idle; +} + +void ElaMessageBarPrivate::onOtherMessageBarEnd(QVariantMap eventData) +{ + Q_Q(ElaMessageBar); + _isMessageBarEventAnimationStart = true; + qreal targetPosY = eventData.value("TargetPosY").toReal(); + QPropertyAnimation* closePosAnimation = new QPropertyAnimation(this, "MessageBarClosedY"); + connect(closePosAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { q->move(q->pos().x(), value.toUInt()); }); + connect(closePosAnimation, &QPropertyAnimation::finished, this, [=]() { + _isMessageBarEventAnimationStart = false; + if (ElaMessageBarManager::getInstance()->getMessageBarEventCount(q) > 1) + { + ElaMessageBarManager::getInstance()->requestMessageBarEvent(q); + } + if (_isReadyToEnd) + { + ElaMessageBarManager::getInstance()->requestMessageBarEvent(q); + } + }); + closePosAnimation->setEasingCurve(QEasingCurve::InOutSine); + closePosAnimation->setDuration(200); + closePosAnimation->setStartValue(q->pos().y()); + closePosAnimation->setEndValue(targetPosY); + closePosAnimation->start(QAbstractAnimation::DeleteWhenStopped); +} + +void ElaMessageBarPrivate::messageBarEnd(QVariantMap eventData) +{ + Q_Q(ElaMessageBar); + ElaMessageBarManager::getInstance()->postMessageBarEndEvent(q); + QPropertyAnimation* barFinishedOpacityAnimation = new QPropertyAnimation(this, "pOpacity"); + connect(barFinishedOpacityAnimation, &QPropertyAnimation::valueChanged, this, [=]() { + _closeButton->setOpacity(_pOpacity); + q->update(); + }); + connect(barFinishedOpacityAnimation, &QPropertyAnimation::finished, this, [=]() { + q->deleteLater(); + }); + barFinishedOpacityAnimation->setDuration(300); + barFinishedOpacityAnimation->setEasingCurve(QEasingCurve::InOutSine); + barFinishedOpacityAnimation->setStartValue(1); + barFinishedOpacityAnimation->setEndValue(0); + barFinishedOpacityAnimation->start(QAbstractAnimation::DeleteWhenStopped); +} + +void ElaMessageBarPrivate::onCloseButtonClicked() +{ + Q_Q(ElaMessageBar); + if (_isReadyToEnd) + { + return; + } + _isReadyToEnd = true; + _isNormalDisplay = false; + ElaMessageBarManager::getInstance()->forcePostMessageBarEndEvent(q); + QPropertyAnimation* opacityAnimation = new QPropertyAnimation(this, "pOpacity"); + connect(opacityAnimation, &QPropertyAnimation::valueChanged, this, [=]() { + _closeButton->setOpacity(_pOpacity); + q->update(); + }); + connect(opacityAnimation, &QPropertyAnimation::finished, q, [=]() { q->deleteLater(); }); + opacityAnimation->setStartValue(_pOpacity); + opacityAnimation->setEndValue(0); + opacityAnimation->setDuration(220); + opacityAnimation->setEasingCurve(QEasingCurve::InOutSine); + opacityAnimation->start(QAbstractAnimation::DeleteWhenStopped); +} + +void ElaMessageBarPrivate::_messageBarCreate(int displayMsec) +{ + Q_Q(ElaMessageBar); + q->show(); + QFont font = q->font(); + font.setPixelSize(16); + font.setWeight(QFont::Bold); + q->setFont(font); + int titleWidth = q->fontMetrics().horizontalAdvance(_title); + font.setPixelSize(14); + font.setWeight(QFont::Medium); + q->setFont(font); + int textWidth = q->fontMetrics().horizontalAdvance(_text); + int fixedWidth = _closeButtonLeftRightMargin + _leftPadding + _titleLeftSpacing + _textLeftSpacing + _closeButtonWidth + titleWidth + textWidth + 2 * _shadowBorderWidth; + q->setFixedWidth(fixedWidth > 500 ? 500 : fixedWidth); + ElaMessageBarManager::getInstance()->postMessageBarCreateEvent(q); + int startX = 0; + int startY = 0; + int endX = 0; + int endY = 0; + _calculateInitialPos(startX, startY, endX, endY); + // 滑入动画 + QPropertyAnimation* barPosAnimation = new QPropertyAnimation(q, "pos"); + connect(barPosAnimation, &QPropertyAnimation::finished, q, [=]() { + _isNormalDisplay = true; + _isMessageBarCreateAnimationFinished = true; + if(ElaMessageBarManager::getInstance()->getMessageBarEventCount(q) > 1) + { + ElaMessageBarManager::getInstance()->requestMessageBarEvent(q); + } + QTimer::singleShot(displayMsec, q, [=]() { + _isReadyToEnd = true; + ElaMessageBarManager::getInstance()->requestMessageBarEvent(q); + }); }); + switch (_policy) + { + case ElaMessageBarType::Top: + case ElaMessageBarType::Bottom: + { + barPosAnimation->setDuration(250); + break; + } + default: + { + barPosAnimation->setDuration(450); + break; + } + } + barPosAnimation->setStartValue(QPoint(startX, startY)); + barPosAnimation->setEndValue(QPoint(endX, endY)); + barPosAnimation->setEasingCurve(QEasingCurve::InOutSine); + barPosAnimation->start(QAbstractAnimation::DeleteWhenStopped); +} + +void ElaMessageBarPrivate::_calculateInitialPos(int& startX, int& startY, int& endX, int& endY) +{ + Q_Q(ElaMessageBar); + QList resultList = _getOtherMessageBarTotalData(); + int minimumHeightTotal = resultList[0]; + int indexLessCount = resultList[1]; + switch (_policy) + { + case ElaMessageBarType::Top: + { + // 25动画距离 + startX = q->parentWidget()->width() / 2 - q->minimumWidth() / 2; + startY = minimumHeightTotal + _messageBarSpacing * indexLessCount + _messageBarVerticalTopMargin - 25; + endX = startX; + endY = minimumHeightTotal + _messageBarSpacing * indexLessCount + _messageBarVerticalTopMargin; + break; + } + case ElaMessageBarType::Left: + { + startX = -q->minimumWidth(); + startY = minimumHeightTotal + _messageBarSpacing * indexLessCount + q->parentWidget()->height() / 2; + endX = _messageBarHorizontalMargin; + endY = startY; + break; + } + case ElaMessageBarType::Bottom: + { + startX = q->parentWidget()->width() / 2 - q->minimumWidth() / 2; + startY = q->parentWidget()->height() - q->minimumHeight() - minimumHeightTotal - _messageBarSpacing * indexLessCount - _messageBarVerticalBottomMargin - 25; + endX = startX; + endY = q->parentWidget()->height() - q->minimumHeight() - minimumHeightTotal - _messageBarSpacing * indexLessCount - _messageBarVerticalBottomMargin; + break; + } + case ElaMessageBarType::Right: + { + startX = q->parentWidget()->width(); + startY = minimumHeightTotal + _messageBarSpacing * indexLessCount + q->parentWidget()->height() / 2; + endX = q->parentWidget()->width() - q->minimumWidth() - _messageBarHorizontalMargin; + endY = startY; + break; + } + case ElaMessageBarType::TopRight: + { + startX = q->parentWidget()->width(); + startY = minimumHeightTotal + _messageBarSpacing * indexLessCount + _messageBarVerticalTopMargin; + endX = q->parentWidget()->width() - q->minimumWidth() - _messageBarHorizontalMargin; + endY = startY; + break; + } + case ElaMessageBarType::TopLeft: + { + startX = -q->minimumWidth(); + startY = minimumHeightTotal + _messageBarSpacing * indexLessCount + _messageBarVerticalTopMargin; + endX = _messageBarHorizontalMargin; + endY = startY; + break; + } + case ElaMessageBarType::BottomRight: + { + startX = q->parentWidget()->width(); + startY = q->parentWidget()->height() - q->minimumHeight() - minimumHeightTotal - _messageBarSpacing * indexLessCount - _messageBarVerticalBottomMargin; + endX = q->parentWidget()->width() - q->minimumWidth() - _messageBarHorizontalMargin; + endY = startY; + break; + } + case ElaMessageBarType::BottomLeft: + { + startX = -q->minimumWidth(); + startY = q->parentWidget()->height() - q->minimumHeight() - minimumHeightTotal - _messageBarSpacing * indexLessCount - _messageBarVerticalBottomMargin; + endX = _messageBarHorizontalMargin; + endY = startY; + break; + } + } + if (endY < _messageBarVerticalTopMargin || endY > q->parentWidget()->height() - _messageBarVerticalBottomMargin - q->minimumHeight()) + { + ElaMessageBarManager::getInstance()->updateActiveMap(q, false); + q->deleteLater(); + } +} + +QList ElaMessageBarPrivate::_getOtherMessageBarTotalData(bool isJudgeCreateOrder) +{ + Q_Q(ElaMessageBar); + QList resultList; + int minimumHeightTotal = 0; + int indexLessCount = 0; + QList* messageBarList = _messageBarActiveMap[_policy]; + for (auto messageBar : *messageBarList) + { + if (messageBar == q) + { + continue; + } + if (!isJudgeCreateOrder || (isJudgeCreateOrder && _judgeCreateOrder(messageBar))) + { + indexLessCount++; + minimumHeightTotal += messageBar->minimumHeight(); + } + } + resultList.append(minimumHeightTotal); + resultList.append(indexLessCount); + return resultList; +} + +qreal ElaMessageBarPrivate::_calculateTargetPosY() +{ + Q_Q(ElaMessageBar); + QList resultList = _getOtherMessageBarTotalData(true); + int minimumHeightTotal = resultList[0]; + int indexLessCount = resultList[1]; + switch (_policy) + { + case ElaMessageBarType::Top: + case ElaMessageBarType::TopRight: + case ElaMessageBarType::TopLeft: + { + return minimumHeightTotal + _messageBarSpacing * indexLessCount + _messageBarVerticalTopMargin; + } + case ElaMessageBarType::Left: + case ElaMessageBarType::Right: + { + return minimumHeightTotal + _messageBarSpacing * indexLessCount + q->parentWidget()->height() / 2; + } + case ElaMessageBarType::Bottom: + case ElaMessageBarType::BottomRight: + case ElaMessageBarType::BottomLeft: + { + return q->parentWidget()->height() - q->minimumHeight() - minimumHeightTotal - _messageBarSpacing * indexLessCount - _messageBarVerticalBottomMargin; + } + } + return 0; +} + +bool ElaMessageBarPrivate::_judgeCreateOrder(ElaMessageBar* otherMessageBar) +{ + if (otherMessageBar->d_ptr->_createTime < _createTime) + { + //otherMessageBar先创建 + return true; + } + else + { + return false; + } +} + +void ElaMessageBarPrivate::_drawSuccess(QPainter* painter) +{ + Q_Q(ElaMessageBar); + painter->setBrush(QColor(0xE0, 0xF6, 0xDD)); + QRect foregroundRect(_shadowBorderWidth, _shadowBorderWidth, q->width() - 2 * _shadowBorderWidth, q->height() - 2 * _shadowBorderWidth); + painter->drawRoundedRect(foregroundRect, _borderRadius, _borderRadius); + // 图标绘制 + painter->save(); + painter->setPen(Qt::white); + QPainterPath textPath; + textPath.addEllipse(QPoint(_leftPadding + 6, q->height() / 2), 9, 9); + painter->setClipPath(textPath); + painter->fillPath(textPath, QColor(0x11, 0x77, 0x10)); + QFont iconFont = QFont("ElaAwesome"); + iconFont.setPixelSize(12); + painter->setFont(iconFont); + painter->drawText(_leftPadding, 0, q->width(), q->height(), Qt::AlignVCenter, QChar((unsigned short)ElaIconType::Check)); + painter->restore(); + // 文字颜色 + painter->setPen(QPen(Qt::black)); +} + +void ElaMessageBarPrivate::_drawWarning(QPainter* painter) +{ + Q_Q(ElaMessageBar); + painter->setBrush(QColor(0x6B, 0x56, 0x27)); + QRect foregroundRect(_shadowBorderWidth, _shadowBorderWidth, q->width() - 2 * _shadowBorderWidth, q->height() - 2 * _shadowBorderWidth); + painter->drawRoundedRect(foregroundRect, _borderRadius, _borderRadius); + // 图标绘制 + // exclamation + painter->save(); + painter->setPen(Qt::black); + QPainterPath textPath; + textPath.addEllipse(QPoint(_leftPadding + 6, q->height() / 2), 9, 9); + painter->setClipPath(textPath); + painter->fillPath(textPath, QColor(0xF8, 0xE2, 0x23)); + painter->drawText(_leftPadding + 4, 0, q->width(), q->height(), Qt::AlignVCenter, "!"); + painter->restore(); + // 文字颜色 + painter->setPen(QColor(0xFA, 0xFA, 0xFA)); +} + +void ElaMessageBarPrivate::_drawInformation(QPainter* painter) +{ + Q_Q(ElaMessageBar); + painter->setBrush(QColor(0xF4, 0xF4, 0xF4)); + QRect foregroundRect(_shadowBorderWidth, _shadowBorderWidth, q->width() - 2 * _shadowBorderWidth, q->height() - 2 * _shadowBorderWidth); + painter->drawRoundedRect(foregroundRect, _borderRadius, _borderRadius); + // 图标绘制 + painter->save(); + painter->setPen(Qt::white); + QPainterPath textPath; + textPath.addEllipse(QPoint(_leftPadding + 6, q->height() / 2), 9, 9); + painter->setClipPath(textPath); + painter->fillPath(textPath, QColor(0x00, 0x66, 0xB4)); + painter->drawText(_leftPadding + 4, 0, q->width(), q->height(), Qt::AlignVCenter, "i"); + painter->restore(); + // 文字颜色 + painter->setPen(Qt::black); +} + +void ElaMessageBarPrivate::_drawError(QPainter* painter) +{ + Q_Q(ElaMessageBar); + painter->setBrush(QColor(0xFE, 0xE7, 0xEA)); + QRect foregroundRect(_shadowBorderWidth, _shadowBorderWidth, q->width() - 2 * _shadowBorderWidth, q->height() - 2 * _shadowBorderWidth); + painter->drawRoundedRect(foregroundRect, _borderRadius, _borderRadius); + // 图标绘制 + painter->save(); + painter->setPen(Qt::white); + QPainterPath textPath; + textPath.addEllipse(QPoint(_leftPadding + 6, q->height() / 2), 9, 9); + painter->setClipPath(textPath); + painter->fillPath(textPath, QColor(0xBA, 0x2D, 0x20)); + QFont iconFont = QFont("ElaAwesome"); + iconFont.setPixelSize(13); + painter->setFont(iconFont); + painter->drawText(_leftPadding + 1, 0, q->width(), q->height(), Qt::AlignVCenter, QChar((unsigned short)ElaIconType::Xmark)); + painter->restore(); + // 文字颜色 + painter->setPen(Qt::black); +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaMessageBarPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaMessageBarPrivate.h new file mode 100644 index 0000000..39f213e --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaMessageBarPrivate.h @@ -0,0 +1,110 @@ +#ifndef ELAMESSAGEBARPRIVATE_H +#define ELAMESSAGEBARPRIVATE_H + +#include +#include +#include +#include + +#include "Def.h" +#include "singleton.h" +#include "stdafx.h" + +enum WorkStatus +{ + Idle = 0x0000, + CreateAnimation = 0x0001, + OtherEventAnimation = 0x0002, +}; + +class ElaMessageBar; +class ElaMessageBarManager : public QObject +{ + Q_OBJECT + Q_SINGLETON_CREATE_H(ElaMessageBarManager) +private: + explicit ElaMessageBarManager(QObject* parent = nullptr); + ~ElaMessageBarManager(); + +public: + //请求事件堆栈调用 + void requestMessageBarEvent(ElaMessageBar* messageBar); + //发布创建事件 + void postMessageBarCreateEvent(ElaMessageBar* messageBar); + //发布终止事件 + void postMessageBarEndEvent(ElaMessageBar* messageBar); + //强制发布终止事件 + void forcePostMessageBarEndEvent(ElaMessageBar* messageBar); + //获取当前事件数量 + int getMessageBarEventCount(ElaMessageBar* messageBar); + //更新活动序列 + void updateActiveMap(ElaMessageBar* messageBar, bool isActive); + +private: + QMap> _messageBarEventMap; +}; + +class ElaIconButton; +class QPainter; +class ElaMessageBarPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaMessageBar) + Q_PROPERTY_CREATE(qreal, Opacity) +public: + explicit ElaMessageBarPrivate(QObject* parent = nullptr); + ~ElaMessageBarPrivate(); + void tryToRequestMessageBarEvent(); + WorkStatus getWorkMode() const; + Q_INVOKABLE void onOtherMessageBarEnd(QVariantMap eventData); + Q_INVOKABLE void messageBarEnd(QVariantMap eventData); + Q_SLOT void onCloseButtonClicked(); + +private: + friend class ElaMessageBarManager; + ElaThemeType::ThemeMode _themeMode; + int _borderRadius{6}; + QString _title{""}; + QString _text{""}; + ElaMessageBarType::PositionPolicy _policy; + ElaMessageBarType::MessageMode _messageMode; + qreal _createTime{0}; + + // 位置数据 + int _leftPadding{20}; // 左边框到图标中心 + int _titleLeftSpacing{30}; // 图标中心到Title左侧 + int _textLeftSpacing{15}; // Title右侧到Text左侧 + int _closeButtonLeftRightMargin{20}; // closeButton左右总Margin + int _closeButtonWidth{30}; + int _messageBarHorizontalMargin{20}; + int _messageBarVerticalBottomMargin{20}; + int _messageBarVerticalTopMargin{50}; + int _messageBarSpacing{15}; + int _shadowBorderWidth{6}; + + // 逻辑数据 + bool _isMessageBarCreateAnimationFinished{false}; + bool _isReadyToEnd{false}; + bool _isNormalDisplay{false}; + bool _isMessageBarEventAnimationStart{false}; + ElaIconButton* _closeButton{nullptr}; + Q_INVOKABLE void _messageBarCreate(int displayMsec); + + // 初始坐标计算 + void _calculateInitialPos(int& startX, int& startY, int& endX, int& endY); + //获取总高度和次序信息 + QList _getOtherMessageBarTotalData(bool isJudgeCreateOrder = false); + //计算目标坐标 + qreal _calculateTargetPosY(); + + //创建次序判断 + bool _judgeCreateOrder(ElaMessageBar* otherMessageBar); + + // 绘制函数 + void _drawSuccess(QPainter* painter); + void _drawWarning(QPainter* painter); + void _drawInformation(QPainter* painter); + void _drawError(QPainter* painter); +}; + +#endif // ELAMESSAGEBARPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaMessageButtonPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaMessageButtonPrivate.cpp new file mode 100644 index 0000000..e7f5e17 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaMessageButtonPrivate.cpp @@ -0,0 +1,13 @@ +#include "ElaMessageButtonPrivate.h" + +#include + +#include "ElaMessageButton.h" +ElaMessageButtonPrivate::ElaMessageButtonPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaMessageButtonPrivate::~ElaMessageButtonPrivate() +{ +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaMessageButtonPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaMessageButtonPrivate.h new file mode 100644 index 0000000..dec7d65 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaMessageButtonPrivate.h @@ -0,0 +1,32 @@ +#ifndef ELAMESSAGEBUTTONPRIVATE_H +#define ELAMESSAGEBUTTONPRIVATE_H + +#include + +#include "Def.h" +#include "stdafx.h" +class ElaMessageButton; +class ElaMessageButtonPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaMessageButton) + Q_PROPERTY_CREATE_D(int, BorderRadius) + Q_PROPERTY_CREATE_D(QString, BarTitle); + Q_PROPERTY_CREATE_D(QString, BarText); + Q_PROPERTY_CREATE_D(int, DisplayMsec); + Q_PROPERTY_CREATE_D(QWidget*, MessageTargetWidget) + Q_PROPERTY_CREATE_D(ElaMessageBarType::MessageMode, MessageMode); + Q_PROPERTY_CREATE_D(ElaMessageBarType::PositionPolicy, PositionPolicy); + +public: + explicit ElaMessageButtonPrivate(QObject* parent = nullptr); + ~ElaMessageButtonPrivate(); + +private: + bool _isLeftButtonPress{false}; + int _shadowBorderWidth{3}; + int _penBorderWidth{1}; + ElaThemeType::ThemeMode _themeMode; +}; + +#endif // ELAMESSAGEBUTTONPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaMultiSelectComboBoxPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaMultiSelectComboBoxPrivate.cpp new file mode 100644 index 0000000..f6982bb --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaMultiSelectComboBoxPrivate.cpp @@ -0,0 +1,91 @@ +#include "ElaMultiSelectComboBoxPrivate.h" + +#include + +#include "ElaComboBoxView.h" +#include "ElaMultiSelectComboBox.h" +ElaMultiSelectComboBoxPrivate::ElaMultiSelectComboBoxPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaMultiSelectComboBoxPrivate::~ElaMultiSelectComboBoxPrivate() +{ +} + +void ElaMultiSelectComboBoxPrivate::onItemPressed(const QModelIndex& index) +{ + Q_Q(ElaMultiSelectComboBox); + if (!_comboView->selectionModel()->isSelected(index)) + { + _itemSelection[index.row()] = true; + } + else + { + _itemSelection[index.row()] = false; + } + _refreshCurrentIndexs(); + QPropertyAnimation* markAnimation = new QPropertyAnimation(this, "pExpandMarkWidth"); + connect(markAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + q->update(); + }); + markAnimation->setDuration(300); + markAnimation->setEasingCurve(QEasingCurve::InOutSine); + markAnimation->setStartValue(_pExpandMarkWidth); + qreal step = (q->width() / 2 - _pBorderRadius) / q->count(); + markAnimation->setEndValue(step * _selectedTextList.count()); + markAnimation->start(QAbstractAnimation::DeleteWhenStopped); +} + +void ElaMultiSelectComboBoxPrivate::_refreshCurrentIndexs() +{ + Q_Q(ElaMultiSelectComboBox); + QString str; + _adjustSelectedVector(); + QVector selectedIndexVector; + for (int i = 0; i < q->count(); i++) + { + // 该位选中 + QModelIndex index = q->model()->index(i, 0); + if (_itemSelection[i]) + { + _comboView->selectionModel()->select(index, QItemSelectionModel::Select | QItemSelectionModel::Rows); + if (!str.isEmpty()) + { + str.append(","); + } + str.append(q->itemText(i)); + selectedIndexVector.append(true); + } + else + { + _comboView->selectionModel()->select(index, QItemSelectionModel::Deselect); + selectedIndexVector.append(false); + } + } + if (str != _currentText) + { + q->update(); + _currentText = str; +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + _selectedTextList = _currentText.split(",", Qt::SkipEmptyParts); +#else + if (_selectedTextList.count() == 1 && _selectedTextList[0].isEmpty()) + { + _selectedTextList.clear(); + } +#endif + q->setCurrentIndex(-1); + Q_EMIT q->itemSelectionChanged(selectedIndexVector); + Q_EMIT q->currentTextListChanged(_selectedTextList); + } +} + +void ElaMultiSelectComboBoxPrivate::_adjustSelectedVector() +{ + Q_Q(ElaMultiSelectComboBox); + while (_itemSelection.count() < q->count()) + { + _itemSelection.append(false); + } +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaMultiSelectComboBoxPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaMultiSelectComboBoxPrivate.h new file mode 100644 index 0000000..588ee57 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaMultiSelectComboBoxPrivate.h @@ -0,0 +1,36 @@ +#ifndef ELAMULTISELECTCOMBOBOXPRIVATE_H +#define ELAMULTISELECTCOMBOBOXPRIVATE_H +#include +#include + +#include "Def.h" +#include "stdafx.h" +class ElaComboBoxStyle; +class ElaComboBoxView; +class ElaMultiSelectComboBox; +class ElaMultiSelectComboBoxPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaMultiSelectComboBox) + Q_PROPERTY_CREATE_D(int, BorderRadius) + Q_PROPERTY_CREATE(qreal, ExpandIconRotate) + Q_PROPERTY_CREATE(qreal, ExpandMarkWidth) +public: + explicit ElaMultiSelectComboBoxPrivate(QObject* parent = nullptr); + ~ElaMultiSelectComboBoxPrivate(); + Q_SLOT void onItemPressed(const QModelIndex& index); + +private: + ElaComboBoxStyle* _comboBoxStyle{nullptr}; + ElaComboBoxView* _comboView{nullptr}; + QVector _itemSelection; + bool _isFirstPopup{false}; + bool _isAllowHidePopup{false}; + QString _currentText; + QStringList _selectedTextList; + void _refreshCurrentIndexs(); + void _adjustSelectedVector(); + ElaThemeType::ThemeMode _themeMode; +}; + +#endif // ELAMULTISELECTCOMBOBOXPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaNavigationBarPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaNavigationBarPrivate.cpp new file mode 100644 index 0000000..01aa642 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaNavigationBarPrivate.cpp @@ -0,0 +1,705 @@ +#include "ElaNavigationBarPrivate.h" + +#include +#include + +#include "ElaApplication.h" +#include "ElaBaseListView.h" +#include "ElaCustomWidget.h" +#include "ElaFooterDelegate.h" +#include "ElaFooterModel.h" +#include "ElaIconButton.h" +#include "ElaInteractiveCard.h" +#include "ElaMenu.h" +#include "ElaNavigationBar.h" +#include "ElaNavigationModel.h" +#include "ElaNavigationNode.h" +#include "ElaNavigationRouter.h" +#include "ElaNavigationView.h" +#include "ElaSuggestBox.h" +#include "ElaSuggestBoxPrivate.h" +#include "ElaToolButton.h" + +ElaNavigationBarPrivate::ElaNavigationBarPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaNavigationBarPrivate::~ElaNavigationBarPrivate() +{ +} + +void ElaNavigationBarPrivate::onNavigationButtonClicked() +{ + Q_Q(ElaNavigationBar); + if (_currentDisplayMode == ElaNavigationType::Compact) + { + q->setDisplayMode(ElaNavigationType::Maximal); + } + else + { + q->setDisplayMode(ElaNavigationType::Compact); + } +} + +void ElaNavigationBarPrivate::onNavigationOpenNewWindow(QString nodeKey) +{ + Q_Q(ElaNavigationBar); + const QMetaObject* meta = _pageMetaMap.value(nodeKey); + if (!meta) + { + return; + } + QWidget* widget = static_cast(meta->newInstance()); + if (widget) + { + ElaCustomWidget* floatWidget = new ElaCustomWidget(q); + floatWidget->setWindowIcon(widget->windowIcon()); + floatWidget->setWindowTitle(widget->windowTitle()); + floatWidget->setCentralWidget(widget); + floatWidget->show(); + } +} + +void ElaNavigationBarPrivate::onNavigationRouteBack(QVariantMap routeData) +{ + Q_Q(ElaNavigationBar); + QString pageKey = routeData.value("ElaPageKey").toString(); + q->navigation(pageKey, false); +} + +void ElaNavigationBarPrivate::onTreeViewClicked(const QModelIndex& index, bool isLogRoute) +{ + Q_Q(ElaNavigationBar); + if (index.isValid()) + { + ElaNavigationNode* node = static_cast(index.internalPointer()); + if (!node) + { + return; + } + if (node->getIsExpanderNode()) + { + if (_currentDisplayMode == ElaNavigationType::Compact) + { + if (node->getIsHasPageChild()) + { + //展开菜单 + ElaMenu* menu = _compactMenuMap.value(node); + if (menu) + { + QPoint nodeTopRight = _navigationView->mapToGlobal(_navigationView->visualRect(node->getModelIndex()).topRight()); + menu->popup(QPoint(nodeTopRight.x() + 10, nodeTopRight.y())); + } + } + } + else + { + if (node->getIsHasChild()) + { + QVariantMap data; + if (_navigationView->isExpanded(index)) + { + // 收起 + data.insert("Collapse", QVariant::fromValue(node)); + node->setIsExpanded(false); + _navigationView->navigationNodeStateChange(data); + _navigationView->collapse(index); + } + else + { + // 展开 + data.insert("Expand", QVariant::fromValue(node)); + node->setIsExpanded(true); + _navigationView->navigationNodeStateChange(data); + _navigationView->expand(index); + } + } + } + } + else + { + if (node->getKeyPoints()) + { + node->setKeyPoints(0); + _navigationView->update(); + } + ElaNavigationNode* selectedNode = _navigationModel->getSelectedNode(); + if (selectedNode != node) + { + // 记录跳转 + if (isLogRoute) + { + QVariantMap routeData = QVariantMap(); + QStringList pageKeyList; + if (selectedNode) + { + pageKeyList.append(selectedNode->getNodeKey()); + } + else + { + if (_footerModel->getSelectedNode()) + { + pageKeyList.append(_footerModel->getSelectedNode()->getNodeKey()); + } + } + routeData.insert("ElaPageKey", pageKeyList); + ElaNavigationRouter::getInstance()->navigationRoute(this, "onNavigationRouteBack", routeData); + } + Q_EMIT q->navigationNodeClicked(ElaNavigationType::PageNode, node->getNodeKey()); + + if (_footerModel->getSelectedNode()) + { + _footerView->clearSelection(); + QVariantMap footerPostData = QVariantMap(); + footerPostData.insert("SelectMarkChanged", true); + footerPostData.insert("LastSelectedNode", QVariant::fromValue(_footerModel->getSelectedNode())); + footerPostData.insert("SelectedNode", QVariant::fromValue(nullptr)); + _footerModel->setSelectedNode(nullptr); + _footerDelegate->navigationNodeStateChange(footerPostData); + } + QVariantMap postData = QVariantMap(); + postData.insert("SelectMarkChanged", true); + if (_navigationModel->getSelectedExpandedNode()) + { + postData.insert("LastSelectedNode", QVariant::fromValue(_navigationModel->getSelectedExpandedNode())); + } + else + { + postData.insert("LastSelectedNode", QVariant::fromValue(_navigationModel->getSelectedNode())); + } + if (_currentDisplayMode == ElaNavigationType::Compact) + { + ElaNavigationNode* originNode = node->getOriginalNode(); + if (originNode == node) + { + postData.insert("SelectedNode", QVariant::fromValue(node)); + } + else + { + if (originNode == _navigationModel->getSelectedExpandedNode()) + { + _navigationModel->setSelectedNode(node); + _resetNodeSelected(); + return; + } + _navigationModel->setSelectedExpandedNode(originNode); + postData.insert("SelectedNode", QVariant::fromValue(originNode)); + } + } + else + { + postData.insert("SelectedNode", QVariant::fromValue(node)); + } + _navigationModel->setSelectedNode(node); + _navigationView->navigationNodeStateChange(postData); + if (!node->getIsVisible() && _currentDisplayMode != ElaNavigationType::Compact) + { + _expandSelectedNodeParent(); + } + } + } + _resetNodeSelected(); + } +} + +void ElaNavigationBarPrivate::onFooterViewClicked(const QModelIndex& index, bool isLogRoute) +{ + Q_Q(ElaNavigationBar); + ElaNavigationNode* node = index.data(Qt::UserRole).value(); + if (node->getKeyPoints()) + { + node->setKeyPoints(0); + _navigationView->update(); + } + ElaNavigationNode* selectedNode = _footerModel->getSelectedNode(); + + if (selectedNode != node) + { + // 记录跳转 + if (isLogRoute && node->getIsHasFooterPage()) + { + QVariantMap routeData = QVariantMap(); + QStringList pageKeyList; + if (selectedNode) + { + pageKeyList.append(selectedNode->getNodeKey()); + } + else + { + if (_navigationModel->getSelectedNode()) + { + pageKeyList.append(_navigationModel->getSelectedNode()->getNodeKey()); + } + } + routeData.insert("ElaPageKey", pageKeyList); + ElaNavigationRouter::getInstance()->navigationRoute(this, "onNavigationRouteBack", routeData); + } + Q_EMIT q->navigationNodeClicked(ElaNavigationType::FooterNode, node->getNodeKey()); + + if (node->getIsHasFooterPage()) + { + if (_navigationModel->getSelectedNode() || _navigationModel->getSelectedExpandedNode()) + { + QVariantMap mainPostData = QVariantMap(); + mainPostData.insert("SelectMarkChanged", true); + mainPostData.insert("LastSelectedNode", QVariant::fromValue(_navigationModel->getSelectedExpandedNode() ? _navigationModel->getSelectedExpandedNode() : _navigationModel->getSelectedNode())); + mainPostData.insert("SelectedNode", QVariant::fromValue(nullptr)); + _navigationView->clearSelection(); + _navigationView->navigationNodeStateChange(mainPostData); + _navigationModel->setSelectedExpandedNode(nullptr); + _navigationModel->setSelectedNode(nullptr); + } + _footerView->clearSelection(); + _footerView->selectionModel()->select(index, QItemSelectionModel::Select); + QVariantMap postData = QVariantMap(); + postData.insert("SelectMarkChanged", true); + postData.insert("LastSelectedNode", QVariant::fromValue(_footerModel->getSelectedNode())); + postData.insert("SelectedNode", QVariant::fromValue(node)); + _footerDelegate->navigationNodeStateChange(postData); + _footerModel->setSelectedNode(node); + } + } +} + +void ElaNavigationBarPrivate::_resetNodeSelected() +{ + _navigationView->clearSelection(); + ElaNavigationNode* selectedNode = _navigationModel->getSelectedNode(); + if (!selectedNode || !selectedNode->getModelIndex().isValid()) + { + return; + } + if (selectedNode->getParentNode()->getIsRootNode() || selectedNode->getIsVisible()) + { + _navigationView->selectionModel()->select(selectedNode->getModelIndex(), QItemSelectionModel::Select); + if (_navigationModel->getSelectedExpandedNode()) + { + QVariantMap postData = QVariantMap(); + postData.insert("SelectMarkChanged", true); + postData.insert("LastSelectedNode", QVariant::fromValue(_navigationModel->getSelectedExpandedNode())); + postData.insert("SelectedNode", QVariant::fromValue(selectedNode)); + _navigationView->navigationNodeStateChange(postData); + } + _navigationModel->setSelectedExpandedNode(nullptr); + } + else + { + ElaNavigationNode* parentNode = selectedNode->getParentNode(); + while (parentNode && !parentNode->getParentNode()->getIsRootNode() && !parentNode->getIsVisible()) + { + parentNode = parentNode->getParentNode(); + } + if (!parentNode) + { + return; + } + // 单级节点展开/收起时Mark变化 + if (!_navigationModel->getSelectedExpandedNode()) + { + QVariantMap postData = QVariantMap(); + postData.insert("SelectMarkChanged", true); + postData.insert("LastSelectedNode", QVariant::fromValue(_navigationModel->getSelectedNode())); + postData.insert("SelectedNode", QVariant::fromValue(parentNode)); + _navigationView->navigationNodeStateChange(postData); + } + else + { + // 多级节点 + if (_navigationModel->getSelectedExpandedNode() != parentNode) + { + // 同一起源节点展开/收起时的Mark变化 + if (_navigationModel->getSelectedExpandedNode()->getOriginalNode() == parentNode->getOriginalNode()) + { + QVariantMap postData = QVariantMap(); + postData.insert("SelectMarkChanged", true); + postData.insert("LastSelectedNode", QVariant::fromValue(_navigationModel->getSelectedExpandedNode())); + postData.insert("SelectedNode", QVariant::fromValue(parentNode)); + _navigationView->navigationNodeStateChange(postData); + } + } + } + _navigationModel->setSelectedExpandedNode(parentNode); + _navigationView->selectionModel()->select(parentNode->getModelIndex(), QItemSelectionModel::Select); + } +} + +void ElaNavigationBarPrivate::_expandSelectedNodeParent() +{ + ElaNavigationNode* parentNode = _navigationModel->getSelectedNode()->getParentNode(); + while (parentNode && !parentNode->getIsRootNode()) + { + QVariantMap data; + data.insert("Expand", QVariant::fromValue(parentNode)); + _navigationView->navigationNodeStateChange(data); + parentNode->setIsExpanded(true); + _navigationView->expand(parentNode->getModelIndex()); + parentNode = parentNode->getParentNode(); + } +} + +void ElaNavigationBarPrivate::_initNodeModelIndex(const QModelIndex& parentIndex) +{ + int rowCount = _navigationModel->rowCount(parentIndex); + for (int row = 0; row < rowCount; ++row) + { + QModelIndex index = _navigationModel->index(row, 0, parentIndex); + ElaNavigationNode* childNode = static_cast(index.internalPointer()); + childNode->setModelIndex(index); + if (_navigationModel->hasChildren(index)) + { + _initNodeModelIndex(index); + } + } +} + +void ElaNavigationBarPrivate::_addStackedPage(QWidget* page, QString pageKey) +{ + Q_Q(ElaNavigationBar); + page->setProperty("ElaPageKey", pageKey); + Q_EMIT q->navigationNodeAdded(ElaNavigationType::PageNode, pageKey, page); + ElaNavigationNode* node = _navigationModel->getNavigationNode(pageKey); + QVariantMap suggestData; + suggestData.insert("ElaNodeType", "Stacked"); + suggestData.insert("ElaPageKey", pageKey); + _navigationSuggestBox->addSuggestion(node->getAwesome(), node->getNodeTitle(), suggestData); +} + +void ElaNavigationBarPrivate::_addFooterPage(QWidget* page, QString footKey) +{ + Q_Q(ElaNavigationBar); + Q_EMIT q->navigationNodeAdded(ElaNavigationType::FooterNode, footKey, page); + if (page) + { + page->setProperty("ElaPageKey", footKey); + } + _footerView->setFixedHeight(40 * _footerModel->getFooterNodeCount()); + ElaNavigationNode* node = _footerModel->getNavigationNode(footKey); + QVariantMap suggestData; + suggestData.insert("ElaNodeType", "Footer"); + suggestData.insert("ElaPageKey", footKey); + _navigationSuggestBox->addSuggestion(node->getAwesome(), node->getNodeTitle(), suggestData); +} + +void ElaNavigationBarPrivate::_raiseNavigationBar() +{ + Q_Q(ElaNavigationBar); + q->raise(); +} + +void ElaNavigationBarPrivate::_doComponentAnimation(ElaNavigationType::NavigationDisplayMode displayMode, bool isAnimation) +{ + switch (displayMode) + { + case ElaNavigationType::Minimal: + { + _doNavigationBarWidthAnimation(displayMode, isAnimation); + if (_currentDisplayMode == ElaNavigationType::Maximal) + { + _searchButton->setVisible(true); + _userCard->setVisible(false); + _navigationSuggestBox->setVisible(false); + if (_isShowUserCard) + { + _userButton->setVisible(true); + } + _handleNavigationExpandState(true); + } + _currentDisplayMode = displayMode; + break; + } + case ElaNavigationType::Compact: + { + _doNavigationBarWidthAnimation(displayMode, isAnimation); + _doNavigationViewWidthAnimation(isAnimation); + if (_currentDisplayMode != ElaNavigationType::Minimal) + { + _handleMaximalToCompactLayout(); + _doNavigationButtonAnimation(true, isAnimation); + _doSearchButtonAnimation(true, isAnimation); + _doUserButtonAnimation(true, isAnimation); + _navigationSuggestBox->setVisible(false); + _handleNavigationExpandState(true); + } + _currentDisplayMode = displayMode; + break; + } + case ElaNavigationType::Maximal: + { + _resetLayout(); + _handleCompactToMaximalLayout(); + _doNavigationBarWidthAnimation(displayMode, isAnimation); + _doUserButtonAnimation(false, isAnimation); + _doNavigationButtonAnimation(false, isAnimation); + _doSearchButtonAnimation(false, isAnimation); + _navigationSuggestBox->setVisible(true); + _currentDisplayMode = displayMode; + _handleNavigationExpandState(false); + break; + } + default: + { + break; + } + } +} + +void ElaNavigationBarPrivate::_handleNavigationExpandState(bool isSave) +{ + if (isSave) + { + // 保存展开状态 收起根节点所有子树 + _lastExpandedNodesList = _navigationModel->getRootExpandedNodes(); + for (auto node : _lastExpandedNodesList) + { + onTreeViewClicked(node->getModelIndex(), false); + } + } + else + { + for (auto node : _lastExpandedNodesList) + { + // 修正动画覆盖 + _navigationView->resize(295, _navigationView->height()); + onTreeViewClicked(node->getModelIndex(), false); + } + } +} + +void ElaNavigationBarPrivate::_handleMaximalToCompactLayout() +{ + // 动画过程布局 + while (_navigationButtonLayout->count()) + { + _navigationButtonLayout->takeAt(0); + } + if (_isShowUserCard) + { + _navigationButtonLayout->addSpacing(76); + } + else + { + _navigationButtonLayout->addSpacing(40); + } + + _navigationSuggestLayout->addStretch(); + + while (_userButtonLayout->count()) + { + _userButtonLayout->takeAt(0); + } + _userButtonLayout->addSpacing(36); +} + +void ElaNavigationBarPrivate::_handleCompactToMaximalLayout() +{ + // 动画过程布局 + while (_navigationButtonLayout->count()) + { + _navigationButtonLayout->takeAt(0); + } + _navigationButtonLayout->addSpacing(38); + _navigationSuggestLayout->insertSpacing(0, 46); + + while (_userButtonLayout->count()) + { + _userButtonLayout->takeAt(0); + } + if (_isShowUserCard) + { + _userButtonLayout->addSpacing(74); + } +} + +void ElaNavigationBarPrivate::_resetLayout() +{ + // 恢复初始布局 + while (_navigationButtonLayout->count()) + { + _navigationButtonLayout->takeAt(0); + } + _navigationButtonLayout->addWidget(_navigationButton); + _navigationButtonLayout->addWidget(_searchButton); + + while (_navigationSuggestLayout->count()) + { + _navigationSuggestLayout->takeAt(0); + } + _navigationSuggestLayout->addLayout(_navigationButtonLayout); + _navigationSuggestLayout->addWidget(_navigationSuggestBox); + + while (_userButtonLayout->count()) + { + _userButtonLayout->takeAt(0); + } + _userButtonLayout->addWidget(_userButton); +} + +void ElaNavigationBarPrivate::_doNavigationBarWidthAnimation(ElaNavigationType::NavigationDisplayMode displayMode, bool isAnimation) +{ + Q_Q(ElaNavigationBar); + QPropertyAnimation* navigationBarWidthAnimation = new QPropertyAnimation(q, "maximumWidth"); + navigationBarWidthAnimation->setEasingCurve(QEasingCurve::OutCubic); + navigationBarWidthAnimation->setStartValue(q->width()); + navigationBarWidthAnimation->setDuration(isAnimation ? 285 : 0); + switch (displayMode) + { + case ElaNavigationType::Minimal: + { + connect(navigationBarWidthAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + q->setFixedWidth(value.toUInt()); + }); + navigationBarWidthAnimation->setEndValue(0); + break; + } + case ElaNavigationType::Compact: + { + connect(navigationBarWidthAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + q->setFixedWidth(value.toUInt()); + }); + navigationBarWidthAnimation->setEndValue(47); + break; + } + case ElaNavigationType::Maximal: + { + connect(navigationBarWidthAnimation, &QPropertyAnimation::finished, this, [=]() { + _resetLayout(); + }); + connect(navigationBarWidthAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + q->setFixedWidth(value.toUInt()); + }); + navigationBarWidthAnimation->setEndValue(300); + break; + } + default: + { + break; + } + } + navigationBarWidthAnimation->start(QAbstractAnimation::DeleteWhenStopped); +} + +void ElaNavigationBarPrivate::_doNavigationViewWidthAnimation(bool isAnimation) +{ + QPropertyAnimation* navigationViewWidthAnimation = new QPropertyAnimation(this, "pNavigationViewWidth"); + connect(navigationViewWidthAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + _navigationView->setColumnWidth(0, value.toUInt()); + }); + navigationViewWidthAnimation->setEasingCurve(QEasingCurve::OutCubic); + navigationViewWidthAnimation->setStartValue(_navigationView->columnWidth(0)); + navigationViewWidthAnimation->setEndValue(40); + navigationViewWidthAnimation->setDuration(isAnimation ? 285 : 0); + navigationViewWidthAnimation->start(QAbstractAnimation::DeleteWhenStopped); +} + +void ElaNavigationBarPrivate::_doNavigationButtonAnimation(bool isCompact, bool isAnimation) +{ + if (isCompact) + { + // 导航按钮 + QPropertyAnimation* navigationButtonAnimation = new QPropertyAnimation(_navigationButton, "pos"); + connect(navigationButtonAnimation, &QPropertyAnimation::finished, this, [=]() { + _resetLayout(); + }); + QPoint navigationButtonPos = _navigationButton->pos(); + navigationButtonAnimation->setStartValue(navigationButtonPos); + if (_isShowUserCard) + { + navigationButtonAnimation->setEndValue(QPoint(0, 56)); + } + else + { + navigationButtonAnimation->setEndValue(navigationButtonPos); + } + navigationButtonAnimation->setEasingCurve(QEasingCurve::OutCubic); + navigationButtonAnimation->setDuration(isAnimation ? 285 : 0); + navigationButtonAnimation->start(QAbstractAnimation::DeleteWhenStopped); + } + else + { + QPropertyAnimation* navigationButtonAnimation = new QPropertyAnimation(_navigationButton, "pos"); + QPoint navigationButtonPos = _navigationButton->pos(); + navigationButtonAnimation->setStartValue(navigationButtonPos); + if (_isShowUserCard) + { + navigationButtonAnimation->setEndValue(QPoint(0, 94)); + } + else + { + navigationButtonAnimation->setEndValue(navigationButtonPos); + } + navigationButtonAnimation->setEasingCurve(QEasingCurve::InOutSine); + navigationButtonAnimation->setDuration(isAnimation ? 130 : 0); + navigationButtonAnimation->start(QAbstractAnimation::DeleteWhenStopped); + } +} + +void ElaNavigationBarPrivate::_doSearchButtonAnimation(bool isCompact, bool isAnimation) +{ + if (isCompact) + { + QPoint navigationButtonPos = _navigationButton->pos(); + // 搜索按钮 + QPropertyAnimation* searchButtonAnimation = new QPropertyAnimation(_searchButton, "pos"); + if (_isShowUserCard) + { + searchButtonAnimation->setStartValue(QPoint(200, navigationButtonPos.y())); + searchButtonAnimation->setEndValue(QPoint(0, navigationButtonPos.y())); + } + else + { + searchButtonAnimation->setStartValue(QPoint(200, navigationButtonPos.y() + 38)); + searchButtonAnimation->setEndValue(QPoint(0, navigationButtonPos.y() + 38)); + } + searchButtonAnimation->setEasingCurve(QEasingCurve::OutCubic); + searchButtonAnimation->setDuration(isAnimation ? 285 : 0); + searchButtonAnimation->start(QAbstractAnimation::DeleteWhenStopped); + _searchButton->setVisible(true); + } + else + { + _searchButton->setVisible(false); + } +} + +void ElaNavigationBarPrivate::_doUserButtonAnimation(bool isCompact, bool isAnimation) +{ + if (isCompact) + { + _userCard->setVisible(false); + if (_isShowUserCard) + { + _userButton->setVisible(true); + } + QPropertyAnimation* userButtonAnimation = new QPropertyAnimation(_userButton, "geometry"); + connect(userButtonAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + _userButton->setFixedSize(value.toRect().size()); + }); + userButtonAnimation->setEasingCurve(QEasingCurve::OutCubic); + userButtonAnimation->setStartValue(QRect(13, 18, 64, 64)); + userButtonAnimation->setEndValue(QRect(3, 10, 36, 36)); + userButtonAnimation->setDuration(isAnimation ? 285 : 0); + userButtonAnimation->start(QAbstractAnimation::DeleteWhenStopped); + } + else + { + QPropertyAnimation* userButtonAnimation = new QPropertyAnimation(_userButton, "geometry"); + connect(userButtonAnimation, &QPropertyAnimation::finished, this, [=]() { + if (_isShowUserCard) + { + _userCard->setVisible(true); + } + _userButton->setFixedSize(36, 36); + _userButton->setGeometry(QRect(3, 10, 36, 36)); + _userButton->setVisible(false); + }); + connect(userButtonAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + _userButton->setFixedSize(value.toRect().size()); + }); + userButtonAnimation->setEasingCurve(QEasingCurve::InOutSine); + userButtonAnimation->setStartValue(QRect(3, 10, 36, 36)); + userButtonAnimation->setEndValue(QRect(13, 18, 64, 64)); + userButtonAnimation->setDuration(isAnimation ? 130 : 0); + userButtonAnimation->start(QAbstractAnimation::DeleteWhenStopped); + } +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaNavigationBarPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaNavigationBarPrivate.h new file mode 100644 index 0000000..07524d9 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaNavigationBarPrivate.h @@ -0,0 +1,92 @@ +#ifndef ELANAVIGATIONBARPRIVATE_H +#define ELANAVIGATIONBARPRIVATE_H + +#include +#include + +#include "Def.h" +#include "stdafx.h" +class QLayout; +class ElaMenu; +class QVBoxLayout; +class QHBoxLayout; +class QLinearGradient; + +class ElaNavigationBar; +class ElaNavigationNode; +class ElaNavigationModel; +class ElaNavigationView; +class ElaNavigationDelegate; +class ElaSuggestBox; +class ElaInteractiveCard; + +class ElaBaseListView; +class ElaFooterModel; +class ElaFooterDelegate; +class ElaIconButton; +class ElaToolButton; +class ElaNavigationBarPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaNavigationBar) + Q_PROPERTY_CREATE_D(bool, IsTransparent) + Q_PROPERTY_CREATE(int, NavigationViewWidth); + +public: + explicit ElaNavigationBarPrivate(QObject* parent = nullptr); + ~ElaNavigationBarPrivate(); + Q_SLOT void onNavigationButtonClicked(); + Q_SLOT void onNavigationOpenNewWindow(QString nodeKey); + + Q_INVOKABLE void onNavigationRouteBack(QVariantMap routeData); + + //核心跳转逻辑 + void onTreeViewClicked(const QModelIndex& index, bool isLogRoute = true); + void onFooterViewClicked(const QModelIndex& index, bool isLogRoute = true); + +private: + ElaThemeType::ThemeMode _themeMode; + QMap _pageMetaMap; + QMap _compactMenuMap; + QVBoxLayout* _navigationButtonLayout{nullptr}; + QHBoxLayout* _navigationSuggestLayout{nullptr}; + QVBoxLayout* _userButtonLayout{nullptr}; + + ElaIconButton* _userButton{nullptr}; + ElaToolButton* _searchButton{nullptr}; + ElaToolButton* _navigationButton{nullptr}; + ElaNavigationModel* _navigationModel{nullptr}; + ElaNavigationView* _navigationView{nullptr}; + ElaBaseListView* _footerView{nullptr}; + ElaFooterModel* _footerModel{nullptr}; + ElaFooterDelegate* _footerDelegate{nullptr}; + ElaSuggestBox* _navigationSuggestBox{nullptr}; + ElaInteractiveCard* _userCard{nullptr}; + bool _isShowUserCard{true}; + + QList _lastExpandedNodesList; + + ElaNavigationType::NavigationDisplayMode _currentDisplayMode{ElaNavigationType::NavigationDisplayMode::Maximal}; + void _resetNodeSelected(); + void _expandSelectedNodeParent(); + void _initNodeModelIndex(const QModelIndex& parentIndex); + + void _addStackedPage(QWidget* page, QString pageKey); + void _addFooterPage(QWidget* page, QString footKey); + + void _raiseNavigationBar(); + + void _doComponentAnimation(ElaNavigationType::NavigationDisplayMode displayMode, bool isAnimation); + void _handleNavigationExpandState(bool isSave); + void _handleMaximalToCompactLayout(); + void _handleCompactToMaximalLayout(); + void _resetLayout(); + + void _doNavigationBarWidthAnimation(ElaNavigationType::NavigationDisplayMode displayMode, bool isAnimation); + void _doNavigationViewWidthAnimation(bool isAnimation); + void _doNavigationButtonAnimation(bool isCompact, bool isAnimation); + void _doSearchButtonAnimation(bool isCompact, bool isAnimation); + void _doUserButtonAnimation(bool isCompact, bool isAnimation); +}; + +#endif // ELANAVIGATIONBARPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaNavigationRouterPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaNavigationRouterPrivate.cpp new file mode 100644 index 0000000..96113d8 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaNavigationRouterPrivate.cpp @@ -0,0 +1,10 @@ +#include "ElaNavigationRouterPrivate.h" + +ElaNavigationRouterPrivate::ElaNavigationRouterPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaNavigationRouterPrivate::~ElaNavigationRouterPrivate() +{ +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaNavigationRouterPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaNavigationRouterPrivate.h new file mode 100644 index 0000000..e0c472f --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaNavigationRouterPrivate.h @@ -0,0 +1,22 @@ +#ifndef ELANAVIGATIONROUTERPRIVATE_H +#define ELANAVIGATIONROUTERPRIVATE_H +#include +#include +#include + +#include "stdafx.h" +class ElaNavigationRouter; +class ElaNavigationRouterPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaNavigationRouter) + Q_PROPERTY_CREATE_D(int, MaxRouteCount) +public: + explicit ElaNavigationRouterPrivate(QObject* parent = nullptr); + ~ElaNavigationRouterPrivate(); + +private: + QQueue _routeQueue; +}; + +#endif // ELANAVIGATIONROUTERPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaPivotPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaPivotPrivate.cpp new file mode 100644 index 0000000..8f15de9 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaPivotPrivate.cpp @@ -0,0 +1,28 @@ +#include "ElaPivotPrivate.h" + +#include +#include + +#include "ElaPivot.h" +#include "ElaPivotStyle.h" +#include "ElaPivotView.h" +ElaPivotPrivate::ElaPivotPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaPivotPrivate::~ElaPivotPrivate() +{ +} + +void ElaPivotPrivate::_checkCurrentIndex() +{ + Q_Q(ElaPivot); + QModelIndex currentIndex = _listView->currentIndex(); + if (currentIndex.row() != _listStyle->getCurrentIndex()) + { + _listView->doCurrentIndexChangedAnimation(currentIndex); + _listStyle->setCurrentIndex(currentIndex.row()); + Q_EMIT q->pCurrentIndexChanged(); + } +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaPivotPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaPivotPrivate.h new file mode 100644 index 0000000..73ec5cc --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaPivotPrivate.h @@ -0,0 +1,28 @@ +#ifndef ELAPIVOTPRIVATE_H +#define ELAPIVOTPRIVATE_H + +#include + +#include "stdafx.h" + +class ElaPivot; +class ElaPivotModel; +class ElaPivotStyle; +class ElaPivotView; +class ElaPivotPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaPivot) + Q_PROPERTY_CREATE_D(int, TextPixelSize) +public: + explicit ElaPivotPrivate(QObject* parent = nullptr); + ~ElaPivotPrivate(); + +private: + ElaPivotModel* _listModel{nullptr}; + ElaPivotStyle* _listStyle{nullptr}; + ElaPivotView* _listView{nullptr}; + void _checkCurrentIndex(); +}; + +#endif // ELAPIVOTPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaPlainTextEditPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaPlainTextEditPrivate.cpp new file mode 100644 index 0000000..31a686e --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaPlainTextEditPrivate.cpp @@ -0,0 +1,55 @@ +#include "ElaPlainTextEditPrivate.h" + +#include "ElaApplication.h" +#include "ElaPlainTextEdit.h" +ElaPlainTextEditPrivate::ElaPlainTextEditPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaPlainTextEditPrivate::~ElaPlainTextEditPrivate() +{ +} + +void ElaPlainTextEditPrivate::onWMWindowClickedEvent(QVariantMap data) +{ + Q_Q(ElaPlainTextEdit); + ElaAppBarType::WMMouseActionType actionType = data.value("WMClickType").value(); + if (actionType == ElaAppBarType::WMLBUTTONDOWN) + { + if (!q->toPlainText().isEmpty() && q->hasFocus()) + { + q->clearFocus(); + } + } + else if (actionType == ElaAppBarType::WMLBUTTONUP || actionType == ElaAppBarType::WMNCLBUTTONDOWN) + { + if (ElaApplication::containsCursorToItem(q) || (actionType == ElaAppBarType::WMLBUTTONUP && !q->toPlainText().isEmpty())) + { + return; + } + if (q->hasFocus()) + { + q->clearFocus(); + } + } +} + +void ElaPlainTextEditPrivate::onThemeChanged(ElaThemeType::ThemeMode themeMode) +{ + Q_Q(ElaPlainTextEdit); + if (themeMode == ElaThemeType::Light) + { + QPalette palette; + palette.setColor(QPalette::Text, Qt::black); + palette.setColor(QPalette::PlaceholderText, QColor(0x00, 0x00, 0x00, 128)); + q->setPalette(palette); + } + else + { + QPalette palette; + palette.setColor(QPalette::Text, Qt::white); + palette.setColor(QPalette::PlaceholderText, QColor(0xBA, 0xBA, 0xBA)); + q->setPalette(palette); + } +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaPlainTextEditPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaPlainTextEditPrivate.h new file mode 100644 index 0000000..48765aa --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaPlainTextEditPrivate.h @@ -0,0 +1,27 @@ +#ifndef ELAPLAINTEXTEDITPRIVATE_H +#define ELAPLAINTEXTEDITPRIVATE_H + +#include + +#include "Def.h" +class ElaEvent; +class ElaPlainTextEdit; +class ElaPlainTextEditStyle; +class ElaPlainTextEditPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaPlainTextEdit) + +public: + explicit ElaPlainTextEditPrivate(QObject* parent = nullptr); + ~ElaPlainTextEditPrivate(); + Q_INVOKABLE void onWMWindowClickedEvent(QVariantMap data); + Q_SLOT void onThemeChanged(ElaThemeType::ThemeMode themeMode); + +private: + ElaThemeType::ThemeMode _themeMode; + ElaPlainTextEditStyle* _style{nullptr}; + ElaEvent* _focusEvent{nullptr}; +}; + +#endif // ELAPLAINTEXTEDITPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaPopularCardPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaPopularCardPrivate.cpp new file mode 100644 index 0000000..2a2f80b --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaPopularCardPrivate.cpp @@ -0,0 +1,25 @@ +#include "ElaPopularCardPrivate.h" + +#include + +#include "ElaPopularCard.h" +#include "ElaPopularCardFloater.h" + +ElaPopularCardPrivate::ElaPopularCardPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaPopularCardPrivate::~ElaPopularCardPrivate() +{ +} + +void ElaPopularCardPrivate::_showFloater() +{ + Q_Q(ElaPopularCard); + q->update(); + _isFloating = true; + _pHoverYOffset = 0; + _floatTimer->stop(); + _floater->showFloater(); +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaPopularCardPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaPopularCardPrivate.h new file mode 100644 index 0000000..10d2d1b --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaPopularCardPrivate.h @@ -0,0 +1,45 @@ +#ifndef ELAPOPULARCARDPRIVATE_H +#define ELAPOPULARCARDPRIVATE_H + +#include +#include + +#include "Def.h" +class ElaPopularCard; +class ElaPopularCardFloater; +class ElaPopularCardPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaPopularCard) + Q_PROPERTY_CREATE_D(int, BorderRadius) + Q_PROPERTY_CREATE_D(QPixmap, CardPixmap) + Q_PROPERTY_CREATE_D(QString, Title) + Q_PROPERTY_CREATE_D(QString, SubTitle) + Q_PROPERTY_CREATE_D(QString, InteractiveTips) + Q_PROPERTY_CREATE_D(QString, DetailedText) + Q_PROPERTY_CREATE_D(QString, CardButtontext) + Q_PROPERTY_CREATE_D(QWidget*, CardFloatArea) + Q_PROPERTY_CREATE_D(QPixmap, CardFloatPixmap) + Q_PROPERTY_CREATE(qreal, HoverYOffset); + Q_PROPERTY_CREATE(qreal, HoverOpacity); + +public: + explicit ElaPopularCardPrivate(QObject* parent = nullptr); + ~ElaPopularCardPrivate(); + +private: + friend class ElaPopularCardFloater; + ElaThemeType::ThemeMode _themeMode; + QTimer* _floatTimer{nullptr}; + ElaPopularCardFloater* _floater{nullptr}; + bool _isFloating{false}; + int _shadowBorderWidth{6}; + int _textHSpacing{20}; + int _textVSpacing{5}; + QRectF _interactiveTipsBaseRect; + QRect _buttonTargetRect; + + void _showFloater(); +}; + +#endif // ELAPOPULARCARDPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaProgressBarPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaProgressBarPrivate.cpp new file mode 100644 index 0000000..5df3ea0 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaProgressBarPrivate.cpp @@ -0,0 +1,10 @@ +#include "ElaProgressBarPrivate.h" + +ElaProgressBarPrivate::ElaProgressBarPrivate(QObject *parent) + : QObject{parent} +{} + +ElaProgressBarPrivate::~ElaProgressBarPrivate() +{ + +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaProgressBarPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaProgressBarPrivate.h new file mode 100644 index 0000000..87fa21c --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaProgressBarPrivate.h @@ -0,0 +1,25 @@ +#ifndef ELAPROGRESSBARPRIVATE_H +#define ELAPROGRESSBARPRIVATE_H + +#include + +#include "stdafx.h" + +class ElaProgressBarStyle; +class QPropertyAnimation; +class ElaProgressBar; +class ElaProgressBarPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaProgressBar) +public: + explicit ElaProgressBarPrivate(QObject* parent = nullptr); + ~ElaProgressBarPrivate(); + +private: + bool _isBusyAnimation{false}; + ElaProgressBarStyle* _style{nullptr}; + QPropertyAnimation* _busyAnimation{nullptr}; +}; + +#endif // ELAPROGRESSBARPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaPromotionCardPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaPromotionCardPrivate.cpp new file mode 100644 index 0000000..bbc54f4 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaPromotionCardPrivate.cpp @@ -0,0 +1,45 @@ +#include "ElaPromotionCardPrivate.h" + +#include +#include +#include + +#include "ElaPromotionCard.h" +ElaPromotionCardPrivate::ElaPromotionCardPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaPromotionCardPrivate::~ElaPromotionCardPrivate() +{ +} + +qreal ElaPromotionCardPrivate::_getLongestDistance(QPoint point) +{ + Q_Q(ElaPromotionCard); + qreal topLeftDis = _distance(point, QPoint(0, 0)); + qreal topRightDis = _distance(point, QPoint(q->width(), 0)); + qreal bottomLeftDis = _distance(point, QPoint(0, q->height())); + qreal bottomRightDis = _distance(point, QPoint(q->width(), q->height())); + QList disList{topLeftDis, topRightDis, bottomLeftDis, bottomRightDis}; + std::sort(disList.begin(), disList.end()); + return disList[3]; +} + +qreal ElaPromotionCardPrivate::_distance(QPoint point1, QPoint point2) +{ + return std::sqrt((point1.x() - point2.x()) * (point1.x() - point2.x()) + (point1.y() - point2.y()) * (point1.y() - point2.y())); +} + +void ElaPromotionCardPrivate::_startHoverOpacityAnimation(bool isVisible) +{ + Q_Q(ElaPromotionCard); + QPropertyAnimation* opacityAnimation = new QPropertyAnimation(this, "pHoverOpacity"); + connect(opacityAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + q->update(); + }); + opacityAnimation->setDuration(250); + opacityAnimation->setStartValue(_pHoverOpacity); + opacityAnimation->setEndValue(isVisible ? 1 : 0); + opacityAnimation->start(QAbstractAnimation::DeleteWhenStopped); +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaPromotionCardPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaPromotionCardPrivate.h new file mode 100644 index 0000000..6f75e0e --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaPromotionCardPrivate.h @@ -0,0 +1,49 @@ +#ifndef ELAPROMOTIONCARDPRIVATE_H +#define ELAPROMOTIONCARDPRIVATE_H + +#include +#include +#include + +#include "stdafx.h" +class QRadialGradient; +class ElaPromotionCard; +class ElaPromotionCardPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaPromotionCard) + Q_PROPERTY_CREATE_D(int, BorderRadius) + Q_PROPERTY_CREATE_D(QPixmap, CardPixmap) + Q_PROPERTY_CREATE_D(QString, CardTitle) + Q_PROPERTY_CREATE_D(QString, PromotionTitle) + Q_PROPERTY_CREATE_D(QString, Title) + Q_PROPERTY_CREATE_D(QString, SubTitle) + Q_PROPERTY_CREATE_D(QColor, CardTitleColor) + Q_PROPERTY_CREATE_D(QColor, PromotionTitleColor) + Q_PROPERTY_CREATE_D(QColor, PromotionTitleBaseColor) + Q_PROPERTY_CREATE_D(QColor, TitleColor) + Q_PROPERTY_CREATE_D(QColor, SubTitleColor) + Q_PROPERTY_CREATE_D(int, CardTitlePixelSize) + Q_PROPERTY_CREATE_D(int, PromotionTitlePixelSize) + Q_PROPERTY_CREATE_D(int, TitlePixelSize) + Q_PROPERTY_CREATE_D(int, SubTitlePixelSize) + Q_PROPERTY_CREATE_D(qreal, HorizontalCardPixmapRatio) + Q_PROPERTY_CREATE_D(qreal, VerticalCardPixmapRatio) + Q_PROPERTY_CREATE(qreal, PressRadius) + Q_PROPERTY_CREATE(qreal, HoverOpacity) + Q_PROPERTY_CREATE(qreal, PressOpacity) +public: + explicit ElaPromotionCardPrivate(QObject* parent = nullptr); + ~ElaPromotionCardPrivate(); + +private: + int _shadowBorderWidth{6}; + QRadialGradient* _hoverGradient{nullptr}; + QRadialGradient* _pressGradient{nullptr}; + bool _isPressAnimationFinished{true}; + qreal _getLongestDistance(QPoint point); + qreal _distance(QPoint point1, QPoint point2); + void _startHoverOpacityAnimation(bool isVisible); +}; + +#endif // ELAPROMOTIONCARDPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaPromotionViewPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaPromotionViewPrivate.cpp new file mode 100644 index 0000000..9e39a2e --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaPromotionViewPrivate.cpp @@ -0,0 +1,186 @@ +#include "ElaPromotionViewPrivate.h" + +#include + +#include "ElaPromotionCard.h" +#include "ElaPromotionView.h" +ElaPromotionViewPrivate::ElaPromotionViewPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaPromotionViewPrivate::~ElaPromotionViewPrivate() +{ +} + +void ElaPromotionViewPrivate::onPromotionCardClicked(ElaPromotionCard* clickedCard) +{ + Q_Q(ElaPromotionView); + int newCurrentIndex = _promotionCardList.indexOf(clickedCard); + int oldCurrentIndex = _pCurrentIndex; + if (newCurrentIndex == oldCurrentIndex) + { + return; + } + //卡片移动动画 + bool isRightToLeft = _promotionCardList[oldCurrentIndex]->x() < _promotionCardList[newCurrentIndex]->x(); + int originIndex = _getAdjacentIndex(Qt::RightToLeft, newCurrentIndex); + for (int i = 0; i < _promotionCardList.count(); i++) + { + ElaPromotionCard* card = _promotionCardList[originIndex]; + QPropertyAnimation* geometryAnimation = new QPropertyAnimation(card, "geometry"); + QRect geometry = card->geometry(); + geometryAnimation->setEasingCurve(QEasingCurve::OutCubic); + geometryAnimation->setDuration(650); + geometryAnimation->setStartValue(geometry); + if (i == 0) + { + //最左侧卡片 + QRect targetGeometry(-_pCardCollapseWidth + _leftPadding, 0, _pCardCollapseWidth, q->height() - _bottomMargin); + if (_promotionCardList.count() > 2) + { + if (!isRightToLeft) + { + geometryAnimation->setKeyValueAt(0.7, QRect(geometry.x() + _pCardCollapseWidth * 0.7 + _cardSpacing, 0, _pCardCollapseWidth, q->height() - _bottomMargin)); + geometryAnimation->setKeyValueAt(0.71, QRect(-_pCardCollapseWidth, 0, _pCardCollapseWidth, q->height() - _bottomMargin)); + } + geometryAnimation->setEndValue(targetGeometry); + } + else + { + if (isRightToLeft) + { + geometryAnimation->setEndValue(targetGeometry); + } + else + { + geometryAnimation->setEndValue(QRect(_leftPadding + _pCardExpandWidth + _cardSpacing * 2, 0, _pCardCollapseWidth, q->height() - _bottomMargin)); + } + } + _startHorizontalCardPixmapRatioAnimation(card, card->getHorizontalCardPixmapRatio(), 0.5); + } + else if (i == 1) + { + //展开的卡片 + geometryAnimation->setEndValue(QRect(_leftPadding + _cardSpacing, 0, _pCardExpandWidth, q->height() - _bottomMargin)); + _startHorizontalCardPixmapRatioAnimation(card, card->getHorizontalCardPixmapRatio(), 1); + } + else + { + QRect targetGeometry(_leftPadding + _pCardExpandWidth + _cardSpacing * i + _pCardCollapseWidth * (i - 2), 0, _pCardCollapseWidth, q->height() - _bottomMargin); + if (isRightToLeft && targetGeometry.x() > card->x()) + { + connect(geometryAnimation, &QPropertyAnimation::valueChanged, this, [=]() { + if (card->geometry().right() <= 0) + { + geometryAnimation->pause(); + qreal timeRatio = geometryAnimation->currentTime() / 650.0; + geometryAnimation->setKeyValueAt(timeRatio, QRect(_promotionCardList[_getAdjacentIndex(Qt::RightToLeft, originIndex)]->geometry().right() + _pCardCollapseWidth * (1 - timeRatio), 0, _pCardCollapseWidth, q->height() - _bottomMargin)); + geometryAnimation->setEndValue(targetGeometry); + geometryAnimation->resume(); + } + }); + geometryAnimation->setEndValue(QRect(-_pCardCollapseWidth * 2 + _leftPadding, 0, _pCardCollapseWidth, q->height() - _bottomMargin)); + } + else + { + geometryAnimation->setEndValue(targetGeometry); + } + card->setHorizontalCardPixmapRatio(0.5); + } + originIndex = _getAdjacentIndex(Qt::LeftToRight, originIndex); + geometryAnimation->start(QAbstractAnimation::DeleteWhenStopped); + } + _pCurrentIndex = newCurrentIndex; + q->update(); + Q_EMIT q->pCurrentIndexChanged(); +} + +void ElaPromotionViewPrivate::_startCardGeometryAnimation(ElaPromotionCard* card, QRect start, QRect end) +{ + QPropertyAnimation* geometryAnimation = new QPropertyAnimation(card, "geometry"); + geometryAnimation->setEasingCurve(QEasingCurve::OutCubic); + geometryAnimation->setDuration(650); + geometryAnimation->setStartValue(start); + geometryAnimation->setEndValue(end); + geometryAnimation->start(QAbstractAnimation::DeleteWhenStopped); +} + +void ElaPromotionViewPrivate::_startHorizontalCardPixmapRatioAnimation(ElaPromotionCard* card, qreal start, qreal end) +{ + QPropertyAnimation* ratioAnimation = new QPropertyAnimation(card, "pHorizontalCardPixmapRatio"); + ratioAnimation->setEasingCurve(QEasingCurve::OutCubic); + ratioAnimation->setDuration(650); + ratioAnimation->setStartValue(start); + ratioAnimation->setEndValue(end); + ratioAnimation->start(QAbstractAnimation::DeleteWhenStopped); +} + +void ElaPromotionViewPrivate::_updatePromotionCardGeometry() +{ + Q_Q(ElaPromotionView); + if (_promotionCardList.count() < 3) + { + if (_promotionCardList.count() == 1) + { + _promotionCardList[0]->setGeometry(_leftPadding + _cardSpacing, 0, _pCardExpandWidth, q->height() - _bottomMargin); + } + else + { + _promotionCardList[0]->setGeometry(_leftPadding + _cardSpacing, 0, _pCardExpandWidth, q->height() - _bottomMargin); + QRect lastGeometry = _promotionCardList[0]->geometry(); + _promotionCardList[1]->setGeometry(QRect(lastGeometry.right() + _cardSpacing, lastGeometry.y(), _pCardCollapseWidth, lastGeometry.height())); + _promotionCardList[1]->setHorizontalCardPixmapRatio(0.5); + } + } + else + { + for (int i = 0; i < _promotionCardList.count(); i++) + { + if (i == 0) + { + //展开的卡片 + _promotionCardList[i]->setGeometry(_leftPadding + _cardSpacing, 0, _pCardExpandWidth, q->height() - _bottomMargin); + _promotionCardList[i]->setHorizontalCardPixmapRatio(1); + } + else if (i == _promotionCardList.count() - 1) + { + //最左侧卡片 + _promotionCardList[i]->setGeometry(-_pCardCollapseWidth + _leftPadding, 0, _pCardCollapseWidth, q->height() - _bottomMargin); + _promotionCardList[i]->setHorizontalCardPixmapRatio(0.5); + } + else + { + QRect lastGeometry = _promotionCardList[i - 1]->geometry(); + _promotionCardList[i]->setGeometry(QRect(lastGeometry.right() + _cardSpacing, lastGeometry.y(), _pCardCollapseWidth, lastGeometry.height())); + _promotionCardList[i]->setHorizontalCardPixmapRatio(0.5); + } + } + } +} + +int ElaPromotionViewPrivate::_getAdjacentIndex(Qt::LayoutDirection direction, int index) +{ + if (direction == Qt::LeftToRight) + { + if (index == _promotionCardList.count() - 1) + { + return 0; + } + return index + 1; + } + else + { + if (index > 0) + { + return index - 1; + } + return _promotionCardList.count() - 1; + } +} + +int ElaPromotionViewPrivate::_getRightLimitX() +{ + int count = _promotionCardList.count(); + return _leftPadding + _pCardExpandWidth + _cardSpacing * (count - 1) + _pCardCollapseWidth * (count - 3); +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaPromotionViewPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaPromotionViewPrivate.h new file mode 100644 index 0000000..ae653a8 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaPromotionViewPrivate.h @@ -0,0 +1,40 @@ +#ifndef ELAPROMOTIONVIEWPRIVATE_H +#define ELAPROMOTIONVIEWPRIVATE_H + +#include +#include + +#include "Def.h" +class ElaPromotionCard; +class ElaPromotionView; +class ElaPromotionViewPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaPromotionView) + Q_PROPERTY_CREATE_D(int, CardExpandWidth) + Q_PROPERTY_CREATE_D(int, CardCollapseWidth) + Q_PROPERTY_CREATE_D(int, CurrentIndex) + Q_PROPERTY_CREATE_D(bool, IsAutoScroll) + Q_PROPERTY_CREATE_D(int, AutoScrollInterval) +public: + explicit ElaPromotionViewPrivate(QObject* parent = nullptr); + ~ElaPromotionViewPrivate(); + void onPromotionCardClicked(ElaPromotionCard* clickedCard); + +private: + ElaThemeType::ThemeMode _themeMode; + QTimer* _autoScrollTimer{nullptr}; + int _leftPadding{40}; + int _cardSpacing{5}; + int _bottomMargin{30}; + int _indicatorSpacing{12}; + bool _isAllowSwitch{true}; + QList _promotionCardList; + void _startCardGeometryAnimation(ElaPromotionCard* card, QRect start, QRect end); + void _startHorizontalCardPixmapRatioAnimation(ElaPromotionCard* card, qreal start, qreal end); + void _updatePromotionCardGeometry(); + int _getAdjacentIndex(Qt::LayoutDirection direction, int index); + int _getRightLimitX(); +}; + +#endif // ELAPROMOTIONVIEWPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaPushButtonPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaPushButtonPrivate.cpp new file mode 100644 index 0000000..9e618e3 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaPushButtonPrivate.cpp @@ -0,0 +1,12 @@ +#include "ElaPushButtonPrivate.h" + +#include "ElaPushButton.h" + +ElaPushButtonPrivate::ElaPushButtonPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaPushButtonPrivate::~ElaPushButtonPrivate() +{ +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaPushButtonPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaPushButtonPrivate.h new file mode 100644 index 0000000..828ec50 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaPushButtonPrivate.h @@ -0,0 +1,33 @@ +#ifndef ELAPUSHBUTTONPRIVATE_H +#define ELAPUSHBUTTONPRIVATE_H + +#include +#include + +#include "Def.h" +#include "stdafx.h" +class ElaPushButton; +class ElaPushButtonPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaPushButton) + Q_PROPERTY_CREATE_D(int, BorderRadius) + Q_PROPERTY_CREATE_D(QColor, LightDefaultColor) + Q_PROPERTY_CREATE_D(QColor, DarkDefaultColor) + Q_PROPERTY_CREATE_D(QColor, LightHoverColor) + Q_PROPERTY_CREATE_D(QColor, DarkHoverColor) + Q_PROPERTY_CREATE_D(QColor, LightPressColor) + Q_PROPERTY_CREATE_D(QColor, DarkPressColor) +public: + explicit ElaPushButtonPrivate(QObject* parent = nullptr); + ~ElaPushButtonPrivate(); + +private: + QColor _lightTextColor; + QColor _darkTextColor; + bool _isPressed{false}; + int _shadowBorderWidth{3}; + ElaThemeType::ThemeMode _themeMode; +}; + +#endif // ELAPUSHBUTTONPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaRadioButtonPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaRadioButtonPrivate.cpp new file mode 100644 index 0000000..6f46cf7 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaRadioButtonPrivate.cpp @@ -0,0 +1,20 @@ +#include "ElaRadioButtonPrivate.h" + +#include "ElaRadioButton.h" +#include "ElaTheme.h" +ElaRadioButtonPrivate::ElaRadioButtonPrivate(QObject* parent) + : QObject(parent) +{ +} + +ElaRadioButtonPrivate::~ElaRadioButtonPrivate() +{ +} + +void ElaRadioButtonPrivate::onThemeChanged(ElaThemeType::ThemeMode themeMode) +{ + Q_Q(ElaRadioButton); + QPalette palette = q->palette(); + palette.setColor(QPalette::WindowText, ElaThemeColor(themeMode, BasicText)); + q->setPalette(palette); +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaRadioButtonPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaRadioButtonPrivate.h new file mode 100644 index 0000000..98d37ec --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaRadioButtonPrivate.h @@ -0,0 +1,17 @@ +#ifndef ELARADIOBUTTONPRIVATE_H +#define ELARADIOBUTTONPRIVATE_H +#include + +#include "Def.h" +class ElaRadioButton; +class ElaRadioButtonPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaRadioButton) +public: + explicit ElaRadioButtonPrivate(QObject* parent = nullptr); + ~ElaRadioButtonPrivate(); + Q_SLOT void onThemeChanged(ElaThemeType::ThemeMode themeMode); +}; + +#endif // ELARADIOBUTTONPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaReminderCardPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaReminderCardPrivate.cpp new file mode 100644 index 0000000..92d35ce --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaReminderCardPrivate.cpp @@ -0,0 +1,10 @@ +#include "ElaReminderCardPrivate.h" + +ElaReminderCardPrivate::ElaReminderCardPrivate(QObject *parent) + : QObject{parent} +{} + +ElaReminderCardPrivate::~ElaReminderCardPrivate() +{ + +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaReminderCardPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaReminderCardPrivate.h new file mode 100644 index 0000000..94f6c98 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaReminderCardPrivate.h @@ -0,0 +1,36 @@ +#ifndef ELAREMINDERCARDPRIVATE_H +#define ELAREMINDERCARDPRIVATE_H + +#include +#include +#include +#include + +#include "Def.h" +#include "stdafx.h" +class ElaReminderCard; +class ElaReminderCardPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaReminderCard) + Q_PROPERTY_CREATE_D(int, BorderRadius) + Q_PROPERTY_CREATE_D(QString, Title); + Q_PROPERTY_CREATE_D(QString, SubTitle); + Q_PROPERTY_CREATE_D(int, TitlePixelSize); + Q_PROPERTY_CREATE_D(int, SubTitlePixelSize); + Q_PROPERTY_CREATE_D(int, TitleSpacing); + Q_PROPERTY_CREATE_D(QPixmap, CardPixmap); + Q_PROPERTY_CREATE_D(QSize, CardPixmapSize); + Q_PROPERTY_CREATE_D(int, CardPixmapBorderRadius) + Q_PROPERTY_CREATE_D(ElaCardPixType::PixMode, CardPixMode); + +public: + explicit ElaReminderCardPrivate(QObject* parent = nullptr); + ~ElaReminderCardPrivate(); + +private: + int _shadowBorderWidth{6}; + ElaThemeType::ThemeMode _themeMode; +}; + +#endif // ELAREMINDERCARDPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaScrollAreaPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaScrollAreaPrivate.cpp new file mode 100644 index 0000000..d46ef56 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaScrollAreaPrivate.cpp @@ -0,0 +1,12 @@ +#include "ElaScrollAreaPrivate.h" + +#include "ElaScrollArea.h" + +ElaScrollAreaPrivate::ElaScrollAreaPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaScrollAreaPrivate::~ElaScrollAreaPrivate() +{ +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaScrollAreaPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaScrollAreaPrivate.h new file mode 100644 index 0000000..c921834 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaScrollAreaPrivate.h @@ -0,0 +1,19 @@ +#ifndef ELASCROLLAREAPRIVATE_H +#define ELASCROLLAREAPRIVATE_H + +#include +#include + +#include "stdafx.h" +class ElaScrollBar; +class ElaScrollArea; +class ElaScrollAreaPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaScrollArea) +public: + explicit ElaScrollAreaPrivate(QObject* parent = nullptr); + ~ElaScrollAreaPrivate(); +}; + +#endif // ELASCROLLAREAPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaScrollBarPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaScrollBarPrivate.cpp new file mode 100644 index 0000000..25197ea --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaScrollBarPrivate.cpp @@ -0,0 +1,145 @@ +#include "ElaScrollBarPrivate.h" + +#include +#include +#include + +#include "ElaScrollBar.h" +ElaScrollBarPrivate::ElaScrollBarPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaScrollBarPrivate::~ElaScrollBarPrivate() +{ +} + +void ElaScrollBarPrivate::onRangeChanged(int min, int max) +{ + Q_Q(ElaScrollBar); + if (q->isVisible() && _pIsAnimation && max != 0) + { + QPropertyAnimation* rangeSmoothAnimation = new QPropertyAnimation(this, "pTargetMaximum"); + connect(rangeSmoothAnimation, &QPropertyAnimation::finished, this, [=]() { + Q_EMIT q->rangeAnimationFinished(); + }); + connect(rangeSmoothAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) { + q->blockSignals(true); + q->setMaximum(value.toUInt()); + q->blockSignals(false); + q->update(); + }); + rangeSmoothAnimation->setEasingCurve(QEasingCurve::OutSine); + rangeSmoothAnimation->setDuration(250); + rangeSmoothAnimation->setStartValue(_pTargetMaximum); + rangeSmoothAnimation->setEndValue(max); + rangeSmoothAnimation->start(QAbstractAnimation::DeleteWhenStopped); + } + else + { + if (max == 0) + { + _scrollValue = -1; + } + _pTargetMaximum = max; + } +} + +void ElaScrollBarPrivate::_scroll(Qt::KeyboardModifiers modifiers, int delta) +{ + Q_Q(ElaScrollBar); + int stepsToScroll = 0; + qreal offset = qreal(delta) / 120; + int pageStep = 10; + int singleStep = q->singleStep(); + if ((modifiers & Qt::ControlModifier) || (modifiers & Qt::ShiftModifier)) + { + stepsToScroll = qBound(-pageStep, int(offset * pageStep), pageStep); + } + else + { + stepsToScroll = QApplication::wheelScrollLines() * offset * singleStep; + } + if (abs(_scrollValue - q->value()) > abs(stepsToScroll * _pSpeedLimit)) + { + _scrollValue = q->value(); + } + _scrollValue -= stepsToScroll; + _slideSmoothAnimation->stop(); + _slideSmoothAnimation->setStartValue(q->value()); + _slideSmoothAnimation->setEndValue(_scrollValue); + _slideSmoothAnimation->start(); +} + +int ElaScrollBarPrivate::_pixelPosToRangeValue(int pos) const +{ + Q_Q(const ElaScrollBar); + QStyleOptionSlider opt; + q->initStyleOption(&opt); + QRect gr = q->style()->subControlRect(QStyle::CC_ScrollBar, &opt, + QStyle::SC_ScrollBarGroove, q); + QRect sr = q->style()->subControlRect(QStyle::CC_ScrollBar, &opt, + QStyle::SC_ScrollBarSlider, q); + int sliderMin, sliderMax, sliderLength; + if (q->orientation() == Qt::Horizontal) + { + sliderLength = sr.width(); + sliderMin = gr.x(); + sliderMax = gr.right() - sliderLength + 1; + if (q->layoutDirection() == Qt::RightToLeft) + { + opt.upsideDown = !opt.upsideDown; + } + } + else + { + sliderLength = sr.height(); + sliderMin = gr.y(); + sliderMax = gr.bottom() - sliderLength + 1; + } + return QStyle::sliderValueFromPosition(q->minimum(), q->maximum(), pos - sliderMin, + sliderMax - sliderMin, opt.upsideDown); +} + +void ElaScrollBarPrivate::_initAllConfig() +{ + Q_Q(ElaScrollBar); + _handleScrollBarRangeChanged(_originScrollBar->minimum(), _originScrollBar->maximum()); + q->setSingleStep(_originScrollBar->singleStep()); + q->setPageStep(_originScrollBar->pageStep()); +} + +void ElaScrollBarPrivate::_handleScrollBarValueChanged(QScrollBar* scrollBar, int value) +{ + scrollBar->setValue(value); +} + +void ElaScrollBarPrivate::_handleScrollBarRangeChanged(int min, int max) +{ + Q_Q(ElaScrollBar); + q->setRange(min, max); + if (max <= 0) + { + q->setVisible(false); + } + else + { + q->setVisible(true); + } +} + +void ElaScrollBarPrivate::_handleScrollBarGeometry() +{ + Q_Q(ElaScrollBar); + q->raise(); + q->setSingleStep(_originScrollBar->singleStep()); + q->setPageStep(_originScrollBar->pageStep()); + if (q->orientation() == Qt::Horizontal) + { + q->setGeometry(0, _originScrollArea->height() - 10, _originScrollArea->width(), 10); + } + else + { + q->setGeometry(_originScrollArea->width() - 10, 0, 10, _originScrollArea->height()); + } +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaScrollBarPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaScrollBarPrivate.h new file mode 100644 index 0000000..80dbcc8 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaScrollBarPrivate.h @@ -0,0 +1,41 @@ +#ifndef ELASCROLLBARPRIVATE_H +#define ELASCROLLBARPRIVATE_H + +#include +#include +#include + +#include "stdafx.h" +class QTimer; +class QPropertyAnimation; +class ElaScrollBar; +class ElaScrollBarPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaScrollBar) + Q_PROPERTY_CREATE_D(bool, IsAnimation) + Q_PROPERTY_CREATE_D(qreal, SpeedLimit) + Q_PROPERTY_CREATE(int, TargetMaximum) +public: + explicit ElaScrollBarPrivate(QObject* parent = nullptr); + ~ElaScrollBarPrivate(); + Q_SLOT void onRangeChanged(int min, int max); + +private: + QScrollBar* _originScrollBar{nullptr}; + QAbstractScrollArea* _originScrollArea{nullptr}; + QTimer* _expandTimer{nullptr}; + bool _isExpand{false}; + QPropertyAnimation* _slideSmoothAnimation{nullptr}; + int _scrollValue{-1}; + void _scroll(Qt::KeyboardModifiers modifiers, int value); + int _pixelPosToRangeValue(int pos) const; + + // 映射处理函数 + void _initAllConfig(); + void _handleScrollBarValueChanged(QScrollBar* scrollBar, int value); + void _handleScrollBarRangeChanged(int min, int max); + void _handleScrollBarGeometry(); +}; + +#endif // ELASCROLLBARPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaScrollPageAreaPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaScrollPageAreaPrivate.cpp new file mode 100644 index 0000000..d68cc49 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaScrollPageAreaPrivate.cpp @@ -0,0 +1,10 @@ +#include "ElaScrollPageAreaPrivate.h" + +ElaScrollPageAreaPrivate::ElaScrollPageAreaPrivate(QObject *parent) + : QObject{parent} +{} + +ElaScrollPageAreaPrivate::~ElaScrollPageAreaPrivate() +{ + +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaScrollPageAreaPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaScrollPageAreaPrivate.h new file mode 100644 index 0000000..2d7386a --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaScrollPageAreaPrivate.h @@ -0,0 +1,22 @@ +#ifndef ELASCROLLPAGEAREAPRIVATE_H +#define ELASCROLLPAGEAREAPRIVATE_H + +#include + +#include "Def.h" +#include "stdafx.h" +class ElaScrollPageArea; +class ElaScrollPageAreaPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaScrollPageArea) + Q_PROPERTY_CREATE_D(int, BorderRadius) +public: + explicit ElaScrollPageAreaPrivate(QObject* parent = nullptr); + ~ElaScrollPageAreaPrivate(); + +private: + ElaThemeType::ThemeMode _themeMode; +}; + +#endif // ELASCROLLPAGEAREAPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaScrollPagePrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaScrollPagePrivate.cpp new file mode 100644 index 0000000..d41d2e5 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaScrollPagePrivate.cpp @@ -0,0 +1,75 @@ +#include "ElaScrollPagePrivate.h" + +#include +#include +#include +#include + +#include "ElaBreadcrumbBar.h" +#include "ElaScrollPage.h" + +ElaScrollPagePrivate::ElaScrollPagePrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaScrollPagePrivate::~ElaScrollPagePrivate() +{ +} + +void ElaScrollPagePrivate::onNavigationRouteBack(QVariantMap routeData) +{ + // 面包屑 + Q_Q(ElaScrollPage); + QString pageCheckSumKey = routeData.value("ElaScrollPageCheckSumKey").toString(); + if (pageCheckSumKey == "Navigation") + { + QString pageTitle = routeData.value("ElaPageTitle").toString(); + q->navigation(_centralWidgetMap.value(pageTitle), false); + } + else if (pageCheckSumKey == "BreadcrumbClicked") + { + QStringList lastBreadcrumbList = routeData.value("LastBreadcrumbList").toStringList(); + int widgetIndex = _centralWidgetMap.value(lastBreadcrumbList.last()); + _switchCentralStackIndex(widgetIndex, _navigationTargetIndex); + _navigationTargetIndex = widgetIndex; + _breadcrumbBar->setBreadcrumbList(lastBreadcrumbList); + } +} + +void ElaScrollPagePrivate::_switchCentralStackIndex(int targetIndex, int lastIndex) +{ + QWidget* currentWidget = _centralStackedWidget->widget(lastIndex); + QWidget* targetWidget = _centralStackedWidget->widget(targetIndex); + targetWidget->resize(currentWidget->size()); + targetWidget->setVisible(true); + + QPropertyAnimation* currentWidgetAnimation = new QPropertyAnimation(currentWidget, "pos"); + currentWidgetAnimation->setEasingCurve(QEasingCurve::InQuart); + currentWidgetAnimation->setDuration(280); + + QPropertyAnimation* targetWidgetAnimation = new QPropertyAnimation(targetWidget, "pos"); + connect(targetWidgetAnimation, &QPropertyAnimation::finished, this, [=]() { + _centralStackedWidget->setCurrentIndex(targetIndex); + }); + targetWidgetAnimation->setEasingCurve(QEasingCurve::InQuart); + targetWidgetAnimation->setDuration(280); + if (targetIndex > lastIndex) + { + //左滑 + currentWidgetAnimation->setStartValue(currentWidget->pos()); + currentWidgetAnimation->setEndValue(QPoint(-_centralStackedWidget->width(), 0)); + targetWidgetAnimation->setStartValue(QPoint(_centralStackedWidget->width(), 0)); + targetWidgetAnimation->setEndValue(QPoint(0, 0)); + } + else + { + //右滑 + currentWidgetAnimation->setStartValue(currentWidget->pos()); + currentWidgetAnimation->setEndValue(QPoint(_centralStackedWidget->width(), 0)); + targetWidgetAnimation->setStartValue(QPoint(-_centralStackedWidget->width(), 0)); + targetWidgetAnimation->setEndValue(QPoint(0, 0)); + } + currentWidgetAnimation->start(QAbstractAnimation::DeleteWhenStopped); + targetWidgetAnimation->start(QAbstractAnimation::DeleteWhenStopped); +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaScrollPagePrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaScrollPagePrivate.h new file mode 100644 index 0000000..fbbc47b --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaScrollPagePrivate.h @@ -0,0 +1,37 @@ +#ifndef ELASCROLLPAGEPRIVATE_H +#define ELASCROLLPAGEPRIVATE_H + +#include +#include + +#include "stdafx.h" +class ElaScrollPage; +class ElaScrollArea; +class QHBoxLayout; +class QVBoxLayout; +class ElaText; +class QStackedWidget; +class ElaBreadcrumbBar; +class ElaScrollPagePrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaScrollPage) + Q_PROPERTY_CREATE_D(QWidget*, CustomWidget) +public: + explicit ElaScrollPagePrivate(QObject* parent = nullptr); + ~ElaScrollPagePrivate(); + Q_INVOKABLE void onNavigationRouteBack(QVariantMap routeData); + +private: + QHBoxLayout* _pageTitleLayout{nullptr}; + QVBoxLayout* _mainLayout{nullptr}; + QStackedWidget* _centralStackedWidget{nullptr}; + ElaBreadcrumbBar* _breadcrumbBar{nullptr}; + QMap _centralWidgetMap; + int _navigationTargetIndex{0}; + int _pageTitleSpacing{0}; + bool _isGrabGesture{false}; + void _switchCentralStackIndex(int targetIndex, int lastIndex); +}; + +#endif // ELASCROLLPAGEPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaSpinBoxPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaSpinBoxPrivate.cpp new file mode 100644 index 0000000..63411e7 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaSpinBoxPrivate.cpp @@ -0,0 +1,78 @@ +#include "ElaSpinBoxPrivate.h" + +#include +#include +#include + +#include "ElaMenu.h" +#include "ElaSpinBox.h" +ElaSpinBoxPrivate::ElaSpinBoxPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaSpinBoxPrivate::~ElaSpinBoxPrivate() +{ +} + +ElaMenu* ElaSpinBoxPrivate::_createStandardContextMenu() +{ + Q_Q(ElaSpinBox); + QLineEdit* lineEdit = q->lineEdit(); + ElaMenu* menu = new ElaMenu(q); + menu->setMenuItemHeight(27); + menu->setAttribute(Qt::WA_DeleteOnClose); + QAction* action = nullptr; + if (!lineEdit->isReadOnly()) + { + action = menu->addElaIconAction(ElaIconType::ArrowRotateLeft, tr("撤销"), QKeySequence::Undo); + action->setEnabled(lineEdit->isUndoAvailable()); + connect(action, &QAction::triggered, lineEdit, &QLineEdit::undo); + + action = menu->addElaIconAction(ElaIconType::ArrowRotateRight, tr("恢复"), QKeySequence::Redo); + action->setEnabled(lineEdit->isRedoAvailable()); + connect(action, &QAction::triggered, lineEdit, &QLineEdit::redo); + menu->addSeparator(); + } +#ifndef QT_NO_CLIPBOARD + if (!lineEdit->isReadOnly()) + { + action = menu->addElaIconAction(ElaIconType::KnifeKitchen, tr("剪切"), QKeySequence::Cut); + action->setEnabled(!lineEdit->isReadOnly() && lineEdit->hasSelectedText() && lineEdit->echoMode() == QLineEdit::Normal); + connect(action, &QAction::triggered, lineEdit, &QLineEdit::cut); + } + + action = menu->addElaIconAction(ElaIconType::Copy, tr("复制"), QKeySequence::Copy); + action->setEnabled(lineEdit->hasSelectedText() && lineEdit->echoMode() == QLineEdit::Normal); + connect(action, &QAction::triggered, lineEdit, &QLineEdit::copy); + + if (!lineEdit->isReadOnly()) + { + action = menu->addElaIconAction(ElaIconType::Paste, tr("粘贴"), QKeySequence::Paste); + action->setEnabled(!lineEdit->isReadOnly() && !QGuiApplication::clipboard()->text().isEmpty()); + connect(action, &QAction::triggered, lineEdit, &QLineEdit::paste); + } +#endif + if (!lineEdit->isReadOnly()) + { + action = menu->addElaIconAction(ElaIconType::DeleteLeft, tr("删除")); + action->setEnabled(!lineEdit->isReadOnly() && !lineEdit->text().isEmpty() && lineEdit->hasSelectedText()); + connect(action, &QAction::triggered, this, [=](bool checked) { + if (lineEdit->hasSelectedText()) + { + int startIndex = lineEdit->selectionStart(); + int endIndex = lineEdit->selectionEnd(); + lineEdit->setText(lineEdit->text().remove(startIndex, endIndex - startIndex)); + } + }); + } + if (!menu->isEmpty()) + { + menu->addSeparator(); + } + action = menu->addAction(tr("全选")); + action->setShortcut(QKeySequence::SelectAll); + action->setEnabled(!lineEdit->text().isEmpty() && !(lineEdit->selectedText() == lineEdit->text())); + connect(action, &QAction::triggered, q, &ElaSpinBox::selectAll); + return menu; +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaSpinBoxPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaSpinBoxPrivate.h new file mode 100644 index 0000000..35ad340 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaSpinBoxPrivate.h @@ -0,0 +1,21 @@ +#ifndef ELASPINBOXPRIVATE_H +#define ELASPINBOXPRIVATE_H + +#include + +#include "stdafx.h" +class ElaSpinBox; +class ElaMenu; +class ElaSpinBoxPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaSpinBox) +public: + explicit ElaSpinBoxPrivate(QObject* parent = nullptr); + ~ElaSpinBoxPrivate(); + +private: + ElaMenu* _createStandardContextMenu(); +}; + +#endif // ELASPINBOXPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaSuggestBoxPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaSuggestBoxPrivate.cpp new file mode 100644 index 0000000..c7700e6 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaSuggestBoxPrivate.cpp @@ -0,0 +1,172 @@ +#include "ElaSuggestBoxPrivate.h" + +#include +#include + +#include "ElaBaseListView.h" +#include "ElaLineEdit.h" +#include "ElaSuggestBox.h" +#include "ElaSuggestBoxSearchViewContainer.h" +#include "ElaSuggestModel.h" + +ElaSuggestion::ElaSuggestion(QObject* parent) + : QObject(parent) +{ + _pElaIcon = ElaIconType::None; + _pSuggestText = ""; + _pSuggestData = QVariantMap(); +} + +ElaSuggestion::~ElaSuggestion() +{ +} + +ElaSuggestBoxPrivate::ElaSuggestBoxPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaSuggestBoxPrivate::~ElaSuggestBoxPrivate() +{ +} + +void ElaSuggestBoxPrivate::onThemeModeChanged(ElaThemeType::ThemeMode themeMode) +{ + _themeMode = themeMode; + _searchEdit->removeAction(_themeMode == ElaThemeType::Light ? _darkSearchAction : _lightSearchAction); + _searchEdit->addAction(_themeMode == ElaThemeType::Light ? _lightSearchAction : _darkSearchAction, QLineEdit::TrailingPosition); + _searchEdit->update(); +} + +void ElaSuggestBoxPrivate::onSearchEditTextEdit(const QString& searchText) +{ + Q_Q(ElaSuggestBox); + if (searchText.isEmpty()) + { + _startCloseAnimation(); + return; + } + QVector suggestionVector; + for (const auto& suggest : _suggestionVector) + { + if (suggest->getSuggestText().contains(searchText, _pCaseSensitivity)) + { + suggestionVector.append(suggest); + } + } + if (!suggestionVector.isEmpty()) + { + _searchModel->setSearchSuggestion(suggestionVector); + int rowCount = suggestionVector.count(); + if (rowCount > 4) + { + rowCount = 4; + } + if (!_searchViewBaseWidget->isVisible()) + { + q->raise(); + _searchViewBaseWidget->show(); + _searchViewBaseWidget->raise(); + QPoint cyclePoint = _searchViewBaseWidget->mapFromGlobal(q->mapToGlobal(QPoint(-5, q->height()))); + if (cyclePoint != QPoint(0, 0)) + { + _searchViewBaseWidget->move(cyclePoint); + } + _startSizeAnimation(QSize(q->width() + 10, 0), QSize(q->width() + 10, 40 * rowCount + 16)); + _searchView->move(_searchView->x(), -(40 * rowCount + 16)); + } + else + { + _startSizeAnimation(_searchViewBaseWidget->size(), QSize(q->width() + 12, 40 * rowCount + 16)); + } + _startExpandAnimation(); + } + else + { + _startCloseAnimation(); + } +} + +void ElaSuggestBoxPrivate::onSearchViewClicked(const QModelIndex& index) +{ + Q_Q(ElaSuggestBox); + _searchEdit->clear(); + _searchView->clearSelection(); + if (!index.isValid()) + { + return; + } + ElaSuggestion* suggest = _searchModel->getSearchSuggestion(index.row()); + Q_EMIT q->suggestionClicked(suggest->getSuggestText(), suggest->getSuggestData()); + _startCloseAnimation(); +} + +void ElaSuggestBoxPrivate::_startSizeAnimation(QSize oldSize, QSize newSize) +{ + if (_lastSize.isValid() && _lastSize == newSize) + { + return; + } + _shadowLayout->removeWidget(_searchView); + QPropertyAnimation* expandAnimation = new QPropertyAnimation(_searchViewBaseWidget, "size"); + connect(expandAnimation, &QPropertyAnimation::valueChanged, this, [=]() { + _searchView->resize(_searchViewBaseWidget->size()); + }); + connect(expandAnimation, &QPropertyAnimation::finished, this, [=]() { + _shadowLayout->addWidget(_searchView); + }); + expandAnimation->setDuration(300); + expandAnimation->setEasingCurve(QEasingCurve::InOutSine); + expandAnimation->setStartValue(oldSize); + expandAnimation->setEndValue(newSize); + expandAnimation->start(QAbstractAnimation::DeleteWhenStopped); + _lastSize = newSize; +} + +void ElaSuggestBoxPrivate::_startExpandAnimation() +{ + if (!_isExpandAnimationFinished) + { + return; + } + _isCloseAnimationFinished = true; + _isExpandAnimationFinished = false; + QPropertyAnimation* expandAnimation = new QPropertyAnimation(_searchView, "pos"); + connect(expandAnimation, &QPropertyAnimation::finished, this, [=]() { + _isExpandAnimationFinished = true; + _searchView->clearSelection(); + }); + expandAnimation->setDuration(300); + expandAnimation->setEasingCurve(QEasingCurve::InOutSine); + expandAnimation->setStartValue(_searchView->pos()); + expandAnimation->setEndValue(QPoint(8, 8)); + expandAnimation->start(QAbstractAnimation::DeleteWhenStopped); +} + +void ElaSuggestBoxPrivate::_startCloseAnimation() +{ + if (!_isCloseAnimationFinished) + { + return; + } + _isExpandAnimationFinished = true; + _isCloseAnimationFinished = false; + QPropertyAnimation* baseWidgetsAnimation = new QPropertyAnimation(_searchViewBaseWidget, "size"); + baseWidgetsAnimation->setDuration(300); + baseWidgetsAnimation->setEasingCurve(QEasingCurve::InOutSine); + baseWidgetsAnimation->setStartValue(_searchViewBaseWidget->size()); + baseWidgetsAnimation->setEndValue(QSize(_searchViewBaseWidget->width(), 0)); + baseWidgetsAnimation->start(QAbstractAnimation::DeleteWhenStopped); + QPropertyAnimation* closeAnimation = new QPropertyAnimation(_searchView, "pos"); + connect(closeAnimation, &QPropertyAnimation::finished, this, [=]() { + _isCloseAnimationFinished = true; + _searchModel->clearSearchNode(); + _searchViewBaseWidget->hide(); + }); + closeAnimation->setDuration(300); + closeAnimation->setEasingCurve(QEasingCurve::InOutSine); + closeAnimation->setStartValue(_searchView->pos()); + closeAnimation->setEndValue(QPoint(_searchView->pos().x(), -_searchView->height())); + closeAnimation->start(QAbstractAnimation::DeleteWhenStopped); + _lastSize = baseWidgetsAnimation->endValue().toSize(); +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaSuggestBoxPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaSuggestBoxPrivate.h new file mode 100644 index 0000000..e03c71a --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaSuggestBoxPrivate.h @@ -0,0 +1,63 @@ +#ifndef ELASUGGESTBOXPRIVATE_H +#define ELASUGGESTBOXPRIVATE_H + +#include +#include +#include +#include +#include + +#include "Def.h" +#include "stdafx.h" +class ElaSuggestion : public QObject +{ + Q_OBJECT + Q_PROPERTY_CREATE(ElaIconType::IconName, ElaIcon) + Q_PROPERTY_CREATE(QString, SuggestText) + Q_PROPERTY_CREATE(QVariantMap, SuggestData) +public: + explicit ElaSuggestion(QObject* parent = nullptr); + ~ElaSuggestion(); +}; + +class QVBoxLayout; +class ElaLineEdit; +class ElaNavigationNode; +class ElaSuggestModel; +class ElaBaseListView; +class ElaSuggestDelegate; +class ElaSuggestBox; +class ElaSuggestBoxSearchViewContainer; +class ElaSuggestBoxPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaSuggestBox) + Q_PROPERTY_CREATE_D(int, BorderRadius) + Q_PROPERTY_CREATE_D(Qt::CaseSensitivity, CaseSensitivity) +public: + explicit ElaSuggestBoxPrivate(QObject* parent = nullptr); + ~ElaSuggestBoxPrivate(); + Q_SLOT void onThemeModeChanged(ElaThemeType::ThemeMode themeMode); + Q_SLOT void onSearchEditTextEdit(const QString& searchText); + Q_SLOT void onSearchViewClicked(const QModelIndex& index); + +private: + ElaThemeType::ThemeMode _themeMode; + QAction* _lightSearchAction{nullptr}; + QAction* _darkSearchAction{nullptr}; + QVector _suggestionVector; + ElaSuggestBoxSearchViewContainer* _searchViewBaseWidget{nullptr}; + ElaLineEdit* _searchEdit{nullptr}; + ElaSuggestModel* _searchModel{nullptr}; + ElaBaseListView* _searchView{nullptr}; + ElaSuggestDelegate* _searchDelegate{nullptr}; + QVBoxLayout* _shadowLayout{nullptr}; + QSize _lastSize; + bool _isExpandAnimationFinished{true}; + bool _isCloseAnimationFinished{true}; + void _startSizeAnimation(QSize oldSize, QSize newSize); + void _startExpandAnimation(); + void _startCloseAnimation(); +}; + +#endif // ELASUGGESTBOXPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaTabBarPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaTabBarPrivate.cpp new file mode 100644 index 0000000..da65f72 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaTabBarPrivate.cpp @@ -0,0 +1,10 @@ +#include "ElaTabBarPrivate.h" + +ElaTabBarPrivate::ElaTabBarPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaTabBarPrivate::~ElaTabBarPrivate() +{ +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaTabBarPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaTabBarPrivate.h new file mode 100644 index 0000000..52ab129 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaTabBarPrivate.h @@ -0,0 +1,21 @@ +#ifndef ELATABBARPRIVATE_H +#define ELATABBARPRIVATE_H + +#include +#include + +#include "stdafx.h" +class ElaTabBar; +class ElaTabBarPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaTabBar) +public: + explicit ElaTabBarPrivate(QObject* parent = nullptr); + ~ElaTabBarPrivate(); + +private: + QPixmap _lastDragPix; +}; + +#endif // ELATABBARPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaTabWidgetPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaTabWidgetPrivate.cpp new file mode 100644 index 0000000..6237e0d --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaTabWidgetPrivate.cpp @@ -0,0 +1,106 @@ +#include "ElaTabWidgetPrivate.h" + +#include +#include +#include + +#include "ElaCustomTabWidget.h" +#include "ElaTabBar.h" +#include "ElaTabWidget.h" +ElaTabWidgetPrivate::ElaTabWidgetPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaTabWidgetPrivate::~ElaTabWidgetPrivate() +{ +} + +void ElaTabWidgetPrivate::onTabBarPress(int index) +{ +} + +void ElaTabWidgetPrivate::onTabDragCreate(QDrag* drag) +{ + Q_Q(ElaTabWidget); + QMimeData* mimeData = drag->mimeData(); + mimeData->setProperty("ElaTabWidgetObject", QVariant::fromValue(q)); + int index = mimeData->property("TabIndex").toInt(); + QString tabText = q->tabText(index); + QIcon tabIcon = q->tabIcon(index); + QWidget* dragWidget = q->widget(index); + QVariant originTabWidget = dragWidget->property("ElaOriginTabWidget"); + if (!originTabWidget.isValid()) + { + dragWidget->setProperty("ElaOriginTabWidget", QVariant::fromValue(q)); + } + mimeData->setProperty("DragWidget", QVariant::fromValue(dragWidget)); + drag->setMimeData(mimeData); + QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(-1, -1), QPoint(-1, -1), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); + QApplication::sendEvent(mimeData->property("ElaTabBarObject").value(), &releaseEvent); + if (drag->exec() == Qt::IgnoreAction) + { + // 创建新窗口 + ElaTabBar* originCustomTabBar = mimeData->property("ElaTabBarObject").value(); + if (originCustomTabBar && originCustomTabBar->objectName() == "ElaCustomTabBar") + { + originCustomTabBar->removeTab(index); + } + ElaTabWidget* originTabWidget = dragWidget->property("ElaOriginTabWidget").value(); + ElaCustomTabWidget* floatWidget = new ElaCustomTabWidget(originTabWidget); + ElaTabBar* customTabBar = floatWidget->getCustomTabBar(); + dragWidget->setProperty("CurrentCustomBar", QVariant::fromValue(customTabBar)); + ElaTabWidget* customTabWidget = floatWidget->getCustomTabWidget(); + customTabWidget->d_ptr->_customTabBar = customTabBar; + connect(customTabBar, &ElaTabBar::tabBarPress, customTabWidget->d_func(), &ElaTabWidgetPrivate::onTabBarPress); + connect(customTabBar, &ElaTabBar::tabDragCreate, customTabWidget->d_func(), &ElaTabWidgetPrivate::onTabDragCreate); + connect(customTabBar, &ElaTabBar::tabDragDrop, customTabWidget->d_func(), &ElaTabWidgetPrivate::onTabDragDrop); + floatWidget->addTab(dragWidget, tabIcon, tabText); + floatWidget->show(); + QPoint cursorPoint = QCursor::pos(); + floatWidget->move(cursorPoint.x() - floatWidget->width() / 2, cursorPoint.y() - 40); + } +} + +void ElaTabWidgetPrivate::onTabDragDrop(const QMimeData* mimeData) +{ + Q_Q(ElaTabWidget); + QWidget* dragWidget = mimeData->property("DragWidget").value(); + QString tabText = mimeData->property("TabText").toString(); + QIcon tabIcon = mimeData->property("TabIcon").value(); + ElaTabBar* customTabBar = mimeData->property("ElaTabBarObject").value(); + int index = mimeData->property("TabIndex").toInt(); + int dropIndex = mimeData->property("TabDropIndex").toInt(); + if (customTabBar && customTabBar->objectName() == "ElaCustomTabBar") + { + customTabBar->removeTab(index); + } + q->insertTab(dropIndex, dragWidget, tabIcon, tabText); + if (_customTabBar) + { + dragWidget->setProperty("CurrentCustomBar", QVariant::fromValue(_customTabBar)); + _customTabBar->insertTab(dropIndex, tabIcon, tabText); + } +} + +void ElaTabWidgetPrivate::onTabCloseRequested(int index) +{ + Q_Q(ElaTabWidget); + QWidget* closeWidget = q->widget(index); + ElaTabWidget* originTabWidget = closeWidget->property("ElaOriginTabWidget").value(); + if (originTabWidget && originTabWidget != q) + { + ElaTabBar* customTabBar = closeWidget->property("CurrentCustomBar").value(); + if (customTabBar) + { + customTabBar->removeTab(index); + closeWidget->setProperty("CurrentCustomBar", QVariant::fromValue(nullptr)); + } + originTabWidget->addTab(closeWidget, q->tabIcon(index), q->tabText(index)); + } + else + { + q->removeTab(index); + closeWidget->deleteLater(); + } +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaTabWidgetPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaTabWidgetPrivate.h new file mode 100644 index 0000000..55fc99e --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaTabWidgetPrivate.h @@ -0,0 +1,27 @@ +#ifndef ELATABWIDGETPRIVATE_H +#define ELATABWIDGETPRIVATE_H + +#include +#include +#include + +#include "stdafx.h" +class ElaTabWidget; +class ElaTabBar; +class ElaTabWidgetPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaTabWidget) +public: + explicit ElaTabWidgetPrivate(QObject* parent = nullptr); + ~ElaTabWidgetPrivate(); + Q_SLOT void onTabBarPress(int index); + Q_SLOT void onTabDragCreate(QDrag* drag); + Q_SLOT void onTabDragDrop(const QMimeData* mimeData); + Q_SLOT void onTabCloseRequested(int index); + +private: + ElaTabBar* _customTabBar{nullptr}; +}; + +#endif // ELATABWIDGETPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaTableViewPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaTableViewPrivate.cpp new file mode 100644 index 0000000..d499898 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaTableViewPrivate.cpp @@ -0,0 +1,10 @@ +#include "ElaTableViewPrivate.h" + +ElaTableViewPrivate::ElaTableViewPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaTableViewPrivate::~ElaTableViewPrivate() +{ +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaTableViewPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaTableViewPrivate.h new file mode 100644 index 0000000..be60ce2 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaTableViewPrivate.h @@ -0,0 +1,22 @@ +#ifndef ELATABLEVIEWPRIVATE_H +#define ELATABLEVIEWPRIVATE_H + +#include + +#include "stdafx.h" + +class ElaTableView; +class ElaTableViewStyle; +class ElaTableViewPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaTableView) +public: + explicit ElaTableViewPrivate(QObject* parent = nullptr); + ~ElaTableViewPrivate(); + +private: + ElaTableViewStyle* _tableViewStyle{nullptr}; +}; + +#endif // ELATABLEVIEWPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaTextPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaTextPrivate.cpp new file mode 100644 index 0000000..e22d867 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaTextPrivate.cpp @@ -0,0 +1,29 @@ +#include "ElaTextPrivate.h" + +#include "ElaText.h" + +ElaTextPrivate::ElaTextPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaTextPrivate::~ElaTextPrivate() +{ +} + +void ElaTextPrivate::onThemeChanged(ElaThemeType::ThemeMode themeMode) +{ + Q_Q(ElaText); + _themeMode = themeMode; + QPalette palette = q->palette(); + if (themeMode == ElaThemeType::Light) + { + palette.setColor(QPalette::WindowText, Qt::black); + q->setPalette(palette); + } + else + { + palette.setColor(QPalette::WindowText, Qt::white); + q->setPalette(palette); + } +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaTextPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaTextPrivate.h new file mode 100644 index 0000000..0f04d7d --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaTextPrivate.h @@ -0,0 +1,25 @@ +#ifndef ELATEXTPRIVATE_H +#define ELATEXTPRIVATE_H + +#include + +#include "Def.h" +#include "stdafx.h" +class ElaText; +class ElaTextPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaText) +public: + explicit ElaTextPrivate(QObject* parent = nullptr); + ~ElaTextPrivate(); + Q_SLOT void onThemeChanged(ElaThemeType::ThemeMode themeMode); + +private: + ElaThemeType::ThemeMode _themeMode; + bool _isWrapAnywhere{false}; + ElaTextType::TextStyle _textStyle{ElaTextType::NoStyle}; + qreal _textSpacing{0.5}; +}; + +#endif // ELATEXTPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaThemePrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaThemePrivate.cpp new file mode 100644 index 0000000..61bafbb --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaThemePrivate.cpp @@ -0,0 +1,110 @@ +#include "ElaThemePrivate.h" + +ElaThemePrivate::ElaThemePrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaThemePrivate::~ElaThemePrivate() +{ +} + +void ElaThemePrivate::_initThemeColor() +{ + //ElaScrollBar + _lightThemeColorList[ElaThemeType::ScrollBarHandle] = QColor(0xA0, 0xA0, 0xA0); + _darkThemeColorList[ElaThemeType::ScrollBarHandle] = QColor(0x9F, 0x9F, 0x9F); + + //ElaToggleSwitch + _lightThemeColorList[ElaThemeType::ToggleSwitchNoToggledCenter] = QColor(0x5A, 0x5A, 0x5A); + _darkThemeColorList[ElaThemeType::ToggleSwitchNoToggledCenter] = QColor(0xD0, 0xD0, 0xD0); + + // 主题颜色 + _lightThemeColorList[ElaThemeType::PrimaryNormal] = QColor(0x00, 0x67, 0xC0); + _darkThemeColorList[ElaThemeType::PrimaryNormal] = QColor(0x4C, 0xC2, 0xFF); + _lightThemeColorList[ElaThemeType::PrimaryHover] = QColor(0x19, 0x75, 0xC5); + _darkThemeColorList[ElaThemeType::PrimaryHover] = QColor(0x47, 0xB1, 0xE8); + _lightThemeColorList[ElaThemeType::PrimaryPress] = QColor(0x31, 0x83, 0xCA); + _darkThemeColorList[ElaThemeType::PrimaryPress] = QColor(0x42, 0xA1, 0xD2); + + // 通用颜色 + // 普通窗体 + _lightThemeColorList[ElaThemeType::WindowBase] = QColor(0xF3, 0xF3, 0xF3); + _darkThemeColorList[ElaThemeType::WindowBase] = QColor(0x20, 0x20, 0x20); + _lightThemeColorList[ElaThemeType::WindowCentralStackBase] = QColor(0xFF, 0xFF, 0xFF, 120); + _darkThemeColorList[ElaThemeType::WindowCentralStackBase] = QColor(0x3E, 0x3E, 0x3E, 60); + + // 浮动窗体 + _lightThemeColorList[ElaThemeType::PopupBorder] = QColor(0xD6, 0xD6, 0xD6); + _darkThemeColorList[ElaThemeType::PopupBorder] = QColor(0x47, 0x47, 0x47); + _lightThemeColorList[ElaThemeType::PopupBorderHover] = QColor(0xCC, 0xCC, 0xCC); + _darkThemeColorList[ElaThemeType::PopupBorderHover] = QColor(0x54, 0x54, 0x54); + _lightThemeColorList[ElaThemeType::PopupBase] = QColor(0xFA, 0xFA, 0xFA); + _darkThemeColorList[ElaThemeType::PopupBase] = QColor(0x2C, 0x2C, 0x2C); + _lightThemeColorList[ElaThemeType::PopupHover] = QColor(0xF0, 0xF0, 0xF0); + _darkThemeColorList[ElaThemeType::PopupHover] = QColor(0x38, 0x38, 0x38); + + // Dialog窗体 + _lightThemeColorList[ElaThemeType::DialogBase] = Qt::white; + _darkThemeColorList[ElaThemeType::DialogBase] = QColor(0x1F, 0x1F, 0x1F); + _lightThemeColorList[ElaThemeType::DialogLayoutArea] = QColor(0xF3, 0xF3, 0xF3); + _darkThemeColorList[ElaThemeType::DialogLayoutArea] = QColor(0x20, 0x20, 0x20); + + // 基础颜色 + _lightThemeColorList[ElaThemeType::BasicText] = Qt::black; + _darkThemeColorList[ElaThemeType::BasicText] = Qt::white; + _lightThemeColorList[ElaThemeType::BasicTextInvert] = Qt::white; + _darkThemeColorList[ElaThemeType::BasicTextInvert] = Qt::black; + _lightThemeColorList[ElaThemeType::BasicDetailsText] = QColor(0x87, 0x87, 0x87); + _darkThemeColorList[ElaThemeType::BasicDetailsText] = QColor(0xAD, 0xAD, 0xB0); + _lightThemeColorList[ElaThemeType::BasicTextNoFocus] = QColor(0x86, 0x86, 0x8A); + _darkThemeColorList[ElaThemeType::BasicTextNoFocus] = QColor(0x86, 0x86, 0x8A); + _lightThemeColorList[ElaThemeType::BasicTextDisable] = QColor(0xB6, 0xB6, 0xB6); + _darkThemeColorList[ElaThemeType::BasicTextDisable] = QColor(0xA7, 0xA7, 0xA7); + _lightThemeColorList[ElaThemeType::BasicTextPress] = QColor(0x5A, 0x5A, 0x5D); + _darkThemeColorList[ElaThemeType::BasicTextPress] = QColor(0xBB, 0xBB, 0xBF); + _lightThemeColorList[ElaThemeType::BasicBorder] = QColor(0xE5, 0xE5, 0xE5); + _darkThemeColorList[ElaThemeType::BasicBorder] = QColor(0x4B, 0x4B, 0x4B); + _lightThemeColorList[ElaThemeType::BasicBorderDeep] = QColor(0xA8, 0xA8, 0xA8); + _darkThemeColorList[ElaThemeType::BasicBorderDeep] = QColor(0x5C, 0x5C, 0x5C); + _lightThemeColorList[ElaThemeType::BasicBorderHover] = QColor(0xDA, 0xDA, 0xDA); + _darkThemeColorList[ElaThemeType::BasicBorderHover] = QColor(0x57, 0x57, 0x57); + _lightThemeColorList[ElaThemeType::BasicBase] = QColor(0xFD, 0xFD, 0xFD); + _darkThemeColorList[ElaThemeType::BasicBase] = QColor(0x34, 0x34, 0x34); + _lightThemeColorList[ElaThemeType::BasicBaseDeep] = QColor(0xE6, 0xE6, 0xE6); + _darkThemeColorList[ElaThemeType::BasicBaseDeep] = QColor(0x61, 0x61, 0x61); + _lightThemeColorList[ElaThemeType::BasicDisable] = QColor(0xF5, 0xF5, 0xF5); + _darkThemeColorList[ElaThemeType::BasicDisable] = QColor(0x2A, 0x2A, 0x2A); + _lightThemeColorList[ElaThemeType::BasicHover] = QColor(0xF3, 0xF3, 0xF3); + _darkThemeColorList[ElaThemeType::BasicHover] = QColor(0x40, 0x40, 0x40); + _lightThemeColorList[ElaThemeType::BasicPress] = QColor(0xF7, 0xF7, 0xF7); + _darkThemeColorList[ElaThemeType::BasicPress] = QColor(0x3A, 0x3A, 0x3A); + _lightThemeColorList[ElaThemeType::BasicBaseLine] = QColor(0xD1, 0xD1, 0xD1); + _darkThemeColorList[ElaThemeType::BasicBaseLine] = QColor(0x45, 0x45, 0x45); + _lightThemeColorList[ElaThemeType::BasicHemline] = QColor(0x86, 0x86, 0x86); + _darkThemeColorList[ElaThemeType::BasicHemline] = QColor(0x9A, 0x9A, 0x9A); + _lightThemeColorList[ElaThemeType::BasicIndicator] = QColor(0x75, 0x7C, 0x87); + _darkThemeColorList[ElaThemeType::BasicIndicator] = QColor(0x75, 0x7C, 0x87); + _lightThemeColorList[ElaThemeType::BasicChute] = QColor(0xD6, 0xD6, 0xD6); + _darkThemeColorList[ElaThemeType::BasicChute] = QColor(0x63, 0x63, 0x63); + + // 基础透明 + _lightThemeColorList[ElaThemeType::BasicAlternating] = QColor(0xEF, 0xEF, 0xEF, 160); + _darkThemeColorList[ElaThemeType::BasicAlternating] = QColor(0x45, 0x45, 0x45, 125); + _lightThemeColorList[ElaThemeType::BasicBaseAlpha] = QColor(0xFF, 0xFF, 0xFF, 160); + _darkThemeColorList[ElaThemeType::BasicBaseAlpha] = QColor(0x45, 0x45, 0x45, 95); + _lightThemeColorList[ElaThemeType::BasicBaseDeepAlpha] = QColor(0xCC, 0xCC, 0xCC, 160); + _darkThemeColorList[ElaThemeType::BasicBaseDeepAlpha] = QColor(0x72, 0x72, 0x72, 95); + _lightThemeColorList[ElaThemeType::BasicHoverAlpha] = QColor(0xCC, 0xCC, 0xCC, 60); + _darkThemeColorList[ElaThemeType::BasicHoverAlpha] = QColor(0x4B, 0x4B, 0x4B, 75); + _lightThemeColorList[ElaThemeType::BasicPressAlpha] = QColor(0xCC, 0xCC, 0xCC, 40); + _darkThemeColorList[ElaThemeType::BasicPressAlpha] = QColor(0x4B, 0x4B, 0x4B, 55); + _lightThemeColorList[ElaThemeType::BasicSelectedAlpha] = QColor(0xCC, 0xCC, 0xCC, 60); + _darkThemeColorList[ElaThemeType::BasicSelectedAlpha] = QColor(0x4B, 0x4B, 0x4B, 75); + _lightThemeColorList[ElaThemeType::BasicSelectedHoverAlpha] = QColor(0xCC, 0xCC, 0xCC, 40); + _darkThemeColorList[ElaThemeType::BasicSelectedHoverAlpha] = QColor(0x4B, 0x4B, 0x4B, 55); + + // 状态颜色 + _lightThemeColorList[ElaThemeType::StatusDanger] = QColor(0xE8, 0x11, 0x23); + _darkThemeColorList[ElaThemeType::StatusDanger] = QColor(0xE8, 0x11, 0x23); +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaThemePrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaThemePrivate.h new file mode 100644 index 0000000..1db5c43 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaThemePrivate.h @@ -0,0 +1,26 @@ +#ifndef ELATHEMEPRIVATE_H +#define ELATHEMEPRIVATE_H + +#include +#include +#include + +#include "Def.h" +#include "stdafx.h" +class ElaTheme; +class ElaThemePrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaTheme) +public: + explicit ElaThemePrivate(QObject* parent = nullptr); + ~ElaThemePrivate(); + +private: + ElaThemeType::ThemeMode _themeMode{ElaThemeType::Light}; + QColor _lightThemeColorList[40]; + QColor _darkThemeColorList[40]; + void _initThemeColor(); +}; + +#endif // ELATHEMEPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaToggleButtonPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaToggleButtonPrivate.cpp new file mode 100644 index 0000000..a6c2536 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaToggleButtonPrivate.cpp @@ -0,0 +1,12 @@ +#include "ElaToggleButtonPrivate.h" + +#include "ElaTheme.h" +#include "ElaToggleButton.h" +ElaToggleButtonPrivate::ElaToggleButtonPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaToggleButtonPrivate::~ElaToggleButtonPrivate() +{ +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaToggleButtonPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaToggleButtonPrivate.h new file mode 100644 index 0000000..9dc6ed3 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaToggleButtonPrivate.h @@ -0,0 +1,27 @@ +#ifndef ELATOGGLEBUTTONPRIVATE_H +#define ELATOGGLEBUTTONPRIVATE_H + +#include + +#include "Def.h" +#include "stdafx.h" +class ElaToggleButton; +class ElaToggleButtonPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaToggleButton) + Q_PROPERTY_CREATE_D(int, BorderRadius) + Q_PROPERTY_CREATE_D(QString, Text) + Q_PROPERTY_CREATE(int, ToggleAlpha) +public: + explicit ElaToggleButtonPrivate(QObject* parent = nullptr); + ~ElaToggleButtonPrivate(); + +private: + bool _isAlphaAnimationFinished{true}; + bool _isToggled{false}; + bool _isPressed{false}; + ElaThemeType::ThemeMode _themeMode; +}; + +#endif // ELATOGGLEBUTTONPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaToggleSwitchPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaToggleSwitchPrivate.cpp new file mode 100644 index 0000000..6c2cc7b --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaToggleSwitchPrivate.cpp @@ -0,0 +1,54 @@ +#include "ElaToggleSwitchPrivate.h" + +#include + +#include "ElaToggleSwitch.h" +ElaToggleSwitchPrivate::ElaToggleSwitchPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaToggleSwitchPrivate::~ElaToggleSwitchPrivate() +{ +} + +void ElaToggleSwitchPrivate::_startPosAnimation(qreal startX, qreal endX, bool isToggle) +{ + Q_Q(ElaToggleSwitch); + QPropertyAnimation* circleAnimation = new QPropertyAnimation(q, "circleCenterX"); + connect(circleAnimation, &QPropertyAnimation::valueChanged, q, [=](const QVariant& value) { + this->_circleCenterX = value.toReal(); + q->update(); }); + circleAnimation->setStartValue(startX); + circleAnimation->setEndValue(endX); + circleAnimation->setEasingCurve(QEasingCurve::InOutSine); + circleAnimation->start(QAbstractAnimation::DeleteWhenStopped); + _isToggled = isToggle; + Q_EMIT q->toggled(isToggle); +} + +void ElaToggleSwitchPrivate::_startRadiusAnimation(qreal startRadius, qreal endRadius) +{ + Q_Q(ElaToggleSwitch); + QPropertyAnimation* circleRadiusAnimation = new QPropertyAnimation(q, "circleRadius"); + connect(circleRadiusAnimation, &QPropertyAnimation::valueChanged, q, [=](const QVariant& value) { + this->_circleRadius = value.toReal(); + q->update(); }); + circleRadiusAnimation->setEasingCurve(QEasingCurve::InOutSine); + circleRadiusAnimation->setStartValue(startRadius); + circleRadiusAnimation->setEndValue(endRadius); + circleRadiusAnimation->start(QAbstractAnimation::DeleteWhenStopped); +} + +void ElaToggleSwitchPrivate::_adjustCircleCenterX() +{ + Q_Q(ElaToggleSwitch); + if (_circleCenterX > q->width() - q->height() / 2 - _margin * 2) + { + _circleCenterX = q->width() - q->height() / 2 - _margin * 2; + } + if (_circleCenterX < q->height() / 2) + { + _circleCenterX = q->height() / 2; + } +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaToggleSwitchPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaToggleSwitchPrivate.h new file mode 100644 index 0000000..33a064e --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaToggleSwitchPrivate.h @@ -0,0 +1,32 @@ +#ifndef ELATOGGLESWITCHPRIVATE_H +#define ELATOGGLESWITCHPRIVATE_H + +#include + +#include "Def.h" +#include "stdafx.h" +class ElaToggleSwitch; +class ElaToggleSwitchPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaToggleSwitch); + +public: + explicit ElaToggleSwitchPrivate(QObject* parent = nullptr); + ~ElaToggleSwitchPrivate(); + +private: + bool _isToggled{false}; + int _margin{1}; + qreal _circleCenterX{0}; + qreal _circleRadius{0}; + bool _isLeftButtonPress{false}; + bool _isMousePressMove{false}; + int _lastMouseX{0}; + ElaThemeType::ThemeMode _themeMode; + void _startPosAnimation(qreal startX, qreal endX, bool isToggle); + void _startRadiusAnimation(qreal startRadius, qreal endRadius); + void _adjustCircleCenterX(); +}; + +#endif // ELATOGGLESWITCHPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaToolBarPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaToolBarPrivate.cpp new file mode 100644 index 0000000..532b050 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaToolBarPrivate.cpp @@ -0,0 +1,10 @@ +#include "ElaToolBarPrivate.h" + +ElaToolBarPrivate::ElaToolBarPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaToolBarPrivate::~ElaToolBarPrivate() +{ +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaToolBarPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaToolBarPrivate.h new file mode 100644 index 0000000..8789cda --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaToolBarPrivate.h @@ -0,0 +1,20 @@ +#ifndef ELATOOLBARPRIVATE_H +#define ELATOOLBARPRIVATE_H +#include + +#include "Def.h" +class ElaToolBar; +class ElaToolBarPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaToolBar) +public: + explicit ElaToolBarPrivate(QObject* parent = nullptr); + ~ElaToolBarPrivate(); + +private: + ElaThemeType::ThemeMode _themeMode; + int _shadowBorderWidth{6}; +}; + +#endif // ELATOOLBARPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaToolButtonPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaToolButtonPrivate.cpp new file mode 100644 index 0000000..cc80774 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaToolButtonPrivate.cpp @@ -0,0 +1,10 @@ +#include "ElaToolButtonPrivate.h" + +ElaToolButtonPrivate::ElaToolButtonPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaToolButtonPrivate::~ElaToolButtonPrivate() +{ +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaToolButtonPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaToolButtonPrivate.h new file mode 100644 index 0000000..e266466 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaToolButtonPrivate.h @@ -0,0 +1,21 @@ +#ifndef ELATOOLBUTTONPRIVATE_H +#define ELATOOLBUTTONPRIVATE_H + +#include + +#include "stdafx.h" +class ElaToolButton; +class ElaToolButtonStyle; +class ElaToolButtonPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaToolButton) +public: + explicit ElaToolButtonPrivate(QObject* parent = nullptr); + ~ElaToolButtonPrivate(); + +private: + ElaToolButtonStyle* _toolButtonStyle{nullptr}; +}; + +#endif // ELATOOLBUTTONPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaToolTipPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaToolTipPrivate.cpp new file mode 100644 index 0000000..eb9765e --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaToolTipPrivate.cpp @@ -0,0 +1,73 @@ +#include "ElaToolTipPrivate.h" + +#include +#include +#include + +#include "ElaToolTip.h" +ElaToolTipPrivate::ElaToolTipPrivate(QObject* parent) + : QObject{parent} +{ + _pOpacity = 1; +} + +ElaToolTipPrivate::~ElaToolTipPrivate() +{ +} + +bool ElaToolTipPrivate::eventFilter(QObject* watched, QEvent* event) +{ + Q_Q(ElaToolTip); + switch (event->type()) + { + case QEvent::Enter: + { + QTimer::singleShot(_pShowDelayMsec, this, [=]() { + _doShowAnimation(); + }); + if (_pDisplayMsec > -1) + { + QTimer::singleShot(_pDisplayMsec, this, [=]() { + q->hide(); + }); + } + break; + } + case QEvent::Leave: + { + QTimer::singleShot(_pHideDelayMsec, this, [=]() { + q->hide(); + }); + break; + } + case QEvent::HoverMove: + case QEvent::MouseMove: + { + if (q->isVisible()) + { + QPoint cursorPoint = QCursor::pos(); + q->move(cursorPoint.x() + 10, cursorPoint.y()); + } + break; + } + default: + { + break; + } + } + return QObject::eventFilter(watched, event); +} + +void ElaToolTipPrivate::_doShowAnimation() +{ + Q_Q(ElaToolTip); + QPoint cursorPoint = QCursor::pos(); + q->move(cursorPoint.x() + 10, cursorPoint.y()); + q->show(); + QPropertyAnimation* showAnimation = new QPropertyAnimation(q, "windowOpacity"); + showAnimation->setEasingCurve(QEasingCurve::InOutSine); + showAnimation->setDuration(250); + showAnimation->setStartValue(0); + showAnimation->setEndValue(1); + showAnimation->start(QAbstractAnimation::DeleteWhenStopped); +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaToolTipPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaToolTipPrivate.h new file mode 100644 index 0000000..6be2a41 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaToolTipPrivate.h @@ -0,0 +1,37 @@ +#ifndef ELATOOLTIPPRIVATE_H +#define ELATOOLTIPPRIVATE_H + +#include + +#include "Def.h" +class ElaToolTip; +class QVBoxLayout; +class ElaText; +class ElaToolTipPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaToolTip) + Q_PROPERTY_CREATE_D(int, BorderRadius) + Q_PROPERTY_CREATE_D(int, DisplayMsec) + Q_PROPERTY_CREATE_D(int, ShowDelayMsec) + Q_PROPERTY_CREATE_D(int, HideDelayMsec) + Q_PROPERTY_CREATE_D(QString, ToolTip) + Q_PROPERTY_CREATE_D(QWidget*, CustomWidget) + Q_PROPERTY_CREATE(qreal, Opacity) + +public: + explicit ElaToolTipPrivate(QObject* parent = nullptr); + ~ElaToolTipPrivate(); + +protected: + virtual bool eventFilter(QObject* watched, QEvent* event); + +private: + ElaText* _toolTipText{nullptr}; + QVBoxLayout* _mainLayout{nullptr}; + ElaThemeType::ThemeMode _themeMode; + int _shadowBorderWidth{6}; + void _doShowAnimation(); +}; + +#endif // ELATOOLTIPPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaTreeViewPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaTreeViewPrivate.cpp new file mode 100644 index 0000000..5f661c4 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaTreeViewPrivate.cpp @@ -0,0 +1,10 @@ +#include "ElaTreeViewPrivate.h" + +ElaTreeViewPrivate::ElaTreeViewPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaTreeViewPrivate::~ElaTreeViewPrivate() +{ +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaTreeViewPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaTreeViewPrivate.h new file mode 100644 index 0000000..b5eacae --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaTreeViewPrivate.h @@ -0,0 +1,22 @@ +#ifndef ELATREEVIEWPRIVATE_H +#define ELATREEVIEWPRIVATE_H + +#include + +#include "stdafx.h" + +class ElaTreeView; +class ElaTreeViewStyle; +class ElaTreeViewPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaTreeView) +public: + explicit ElaTreeViewPrivate(QObject* parent = nullptr); + ~ElaTreeViewPrivate(); + +private: + ElaTreeViewStyle* _treeViewStyle{nullptr}; +}; + +#endif // ELATREEVIEWPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaWidgetPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaWidgetPrivate.cpp new file mode 100644 index 0000000..72010ec --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaWidgetPrivate.cpp @@ -0,0 +1,12 @@ +#include "ElaWidgetPrivate.h" + +#include "ElaWidget.h" + +ElaWidgetPrivate::ElaWidgetPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaWidgetPrivate::~ElaWidgetPrivate() +{ +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaWidgetPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaWidgetPrivate.h new file mode 100644 index 0000000..e7404aa --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaWidgetPrivate.h @@ -0,0 +1,23 @@ +#ifndef ELAWIDGETPRIVATE_H +#define ELAWIDGETPRIVATE_H + +#include + +#include "Def.h" +class ElaWidget; +class ElaAppBar; +class ElaWidgetPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaWidget) +public: + explicit ElaWidgetPrivate(QObject* parent = nullptr); + ~ElaWidgetPrivate(); + +private: + ElaThemeType::ThemeMode _themeMode; + bool _isEnableMica; + ElaAppBar* _appBar{nullptr}; +}; + +#endif // ELAWIDGETPRIVATE_H diff --git a/3rdparty/ElaWidgetTools/src/private/ElaWindowPrivate.cpp b/3rdparty/ElaWidgetTools/src/private/ElaWindowPrivate.cpp new file mode 100644 index 0000000..1a27f91 --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaWindowPrivate.cpp @@ -0,0 +1,280 @@ +#include "ElaWindowPrivate.h" + +#include +#include +#include +#include +#include + +#include "ElaAppBar.h" +#include "ElaAppBarPrivate.h" +#include "ElaApplication.h" +#include "ElaCentralStackedWidget.h" +#include "ElaNavigationBar.h" +#include "ElaTheme.h" +#include "ElaThemeAnimationWidget.h" +#include "ElaWindow.h" +ElaWindowPrivate::ElaWindowPrivate(QObject* parent) + : QObject{parent} +{ +} + +ElaWindowPrivate::~ElaWindowPrivate() +{ +} + +void ElaWindowPrivate::onNavigationButtonClicked() +{ + if (_isWMClickedAnimationFinished) + { + _isNavigationDisplayModeChanged = false; + _resetWindowLayout(true); + _navigationBar->setIsTransparent(false); + _navigationBar->setDisplayMode(ElaNavigationType::Maximal, false); + _navigationBar->move(-_navigationBar->width(), _navigationBar->pos().y()); + _navigationBar->resize(_navigationBar->width(), _centerStackedWidget->height() + 1); + QPropertyAnimation* navigationMoveAnimation = new QPropertyAnimation(_navigationBar, "pos"); + connect(navigationMoveAnimation, &QPropertyAnimation::finished, this, [=]() { + _isNavigationBarExpanded = true; + }); + navigationMoveAnimation->setEasingCurve(QEasingCurve::InOutSine); + navigationMoveAnimation->setDuration(300); + navigationMoveAnimation->setStartValue(_navigationBar->pos()); + navigationMoveAnimation->setEndValue(QPoint(0, 0)); + navigationMoveAnimation->start(QAbstractAnimation::DeleteWhenStopped); + _isWMClickedAnimationFinished = false; + } +} + +void ElaWindowPrivate::onWMWindowClickedEvent(QVariantMap data) +{ + ElaAppBarType::WMMouseActionType actionType = data.value("WMClickType").value(); + if (actionType == ElaAppBarType::WMLBUTTONDBLCLK || actionType == ElaAppBarType::WMLBUTTONUP || actionType == ElaAppBarType::WMNCLBUTTONDOWN) + { + if (ElaApplication::containsCursorToItem(_navigationBar)) + { + return; + } + if (_isNavigationBarExpanded) + { + QPropertyAnimation* navigationMoveAnimation = new QPropertyAnimation(_navigationBar, "pos"); + connect(navigationMoveAnimation, &QPropertyAnimation::valueChanged, this, [=]() { + if (_isNavigationDisplayModeChanged) + { + _navigationBar->setIsTransparent(true); + _isWMClickedAnimationFinished = true; + _resetWindowLayout(false); + navigationMoveAnimation->deleteLater(); + } + }); + connect(navigationMoveAnimation, &QPropertyAnimation::finished, this, [=]() { + _navigationBar->setIsTransparent(true); + if (!_isNavigationDisplayModeChanged) + { + _navigationBar->setDisplayMode(ElaNavigationType::Minimal, false); + _resetWindowLayout(false); + } + _isWMClickedAnimationFinished = true; + }); + navigationMoveAnimation->setEasingCurve(QEasingCurve::InOutSine); + navigationMoveAnimation->setDuration(300); + navigationMoveAnimation->setStartValue(_navigationBar->pos()); + navigationMoveAnimation->setEndValue(QPoint(-_navigationBar->width(), 0)); + navigationMoveAnimation->start(QAbstractAnimation::DeleteWhenStopped); + _isNavigationBarExpanded = false; + } + } +} + +void ElaWindowPrivate::onThemeReadyChange() +{ + Q_Q(ElaWindow); + // 主题变更绘制窗口 + _appBar->setIsOnlyAllowMinAndClose(true); + if (!_animationWidget) + { + QPoint centerPos = q->mapFromGlobal(QCursor::pos()); + _animationWidget = new ElaThemeAnimationWidget(q); + connect(_animationWidget, &ElaThemeAnimationWidget::animationFinished, this, [=]() { + _appBar->setIsOnlyAllowMinAndClose(false); + _animationWidget = nullptr; + }); + _animationWidget->move(0, 0); + _animationWidget->setOldWindowBackground(q->grab(q->rect()).toImage()); + if (eTheme->getThemeMode() == ElaThemeType::Light) + { + eTheme->setThemeMode(ElaThemeType::Dark); + } + else + { + eTheme->setThemeMode(ElaThemeType::Light); + } + _animationWidget->setNewWindowBackground(q->grab(q->rect()).toImage()); + _animationWidget->setCenter(centerPos); + qreal topLeftDis = _distance(centerPos, QPoint(0, 0)); + qreal topRightDis = _distance(centerPos, QPoint(q->width(), 0)); + qreal bottomLeftDis = _distance(centerPos, QPoint(0, q->height())); + qreal bottomRightDis = _distance(centerPos, QPoint(q->width(), q->height())); + QList disList{topLeftDis, topRightDis, bottomLeftDis, bottomRightDis}; + std::sort(disList.begin(), disList.end()); + _animationWidget->setEndRadius(disList[3]); + _animationWidget->resize(q->width(), q->height()); + _animationWidget->startAnimation(_pThemeChangeTime); + _animationWidget->show(); + } +} + +void ElaWindowPrivate::onDisplayModeChanged() +{ + _currentNavigationBarDisplayMode = _pNavigationBarDisplayMode; + switch (_pNavigationBarDisplayMode) + { + case ElaNavigationType::Auto: + { + _appBar->setWindowButtonFlag(ElaAppBarType::NavigationButtonHint, false); + _doNavigationDisplayModeChange(); + break; + } + case ElaNavigationType::Minimal: + { + _navigationBar->setDisplayMode(ElaNavigationType::Minimal, true); + _appBar->setWindowButtonFlag(ElaAppBarType::NavigationButtonHint); + break; + } + case ElaNavigationType::Compact: + { + _navigationBar->setDisplayMode(ElaNavigationType::Compact, true); + _appBar->setWindowButtonFlag(ElaAppBarType::NavigationButtonHint, false); + break; + } + case ElaNavigationType::Maximal: + { + _navigationBar->setDisplayMode(ElaNavigationType::Maximal, true); + _appBar->setWindowButtonFlag(ElaAppBarType::NavigationButtonHint, false); + break; + } + } +} + +void ElaWindowPrivate::onThemeModeChanged(ElaThemeType::ThemeMode themeMode) +{ + Q_Q(ElaWindow); + _themeMode = themeMode; + if (!eApp->getIsEnableMica()) + { + QPalette palette = q->palette(); + palette.setBrush(QPalette::Window, ElaThemeColor(_themeMode, WindowBase)); + q->setPalette(palette); + } +} + +void ElaWindowPrivate::onNavigationNodeClicked(ElaNavigationType::NavigationNodeType nodeType, QString nodeKey) +{ + int nodeIndex = _routeMap.value(nodeKey); + if (nodeIndex == -1) + { + // 页脚没有绑定页面 + return; + } + if (_navigationTargetIndex == nodeIndex || _centerStackedWidget->count() <= nodeIndex) + { + return; + } + _navigationTargetIndex = nodeIndex; + QTimer::singleShot(180, this, [=]() { + QWidget* currentWidget = _centerStackedWidget->widget(nodeIndex); + _centerStackedWidget->setCurrentIndex(nodeIndex); + QPropertyAnimation* currentWidgetAnimation = new QPropertyAnimation(currentWidget, "pos"); + currentWidgetAnimation->setEasingCurve(QEasingCurve::OutCubic); + currentWidgetAnimation->setDuration(300); + QPoint currentWidgetPos = currentWidget->pos(); + currentWidgetAnimation->setEndValue(currentWidgetPos); + currentWidgetPos.setY(currentWidgetPos.y() + 80); + currentWidgetAnimation->setStartValue(currentWidgetPos); + currentWidgetAnimation->start(QAbstractAnimation::DeleteWhenStopped); + }); +} + +void ElaWindowPrivate::onNavigationNodeAdded(ElaNavigationType::NavigationNodeType nodeType, QString nodeKey, QWidget* page) +{ + if (nodeType == ElaNavigationType::PageNode) + { + _routeMap.insert(nodeKey, _centerStackedWidget->count()); + _centerStackedWidget->addWidget(page); + } + else + { + if (page) + { + _routeMap.insert(nodeKey, _centerStackedWidget->count()); + _centerStackedWidget->addWidget(page); + } + else + { + _routeMap.insert(nodeKey, -1); + } + } +} + +qreal ElaWindowPrivate::_distance(QPoint point1, QPoint point2) +{ + return std::sqrt((point1.x() - point2.x()) * (point1.x() - point2.x()) + (point1.y() - point2.y()) * (point1.y() - point2.y())); +} + +void ElaWindowPrivate::_resetWindowLayout(bool isAnimation) +{ + if (isAnimation) + { + while (_centerLayout->count() > 0) + { + _centerLayout->takeAt(0); + } + } + else + { + if (_centerLayout->count() == 0) + { + _navigationBar->setDisplayMode(ElaNavigationType::Minimal, false); + _centerLayout->addWidget(_navigationBar); + _centerLayout->addWidget(_centerStackedWidget); + } + } +} + +void ElaWindowPrivate::_doNavigationDisplayModeChange() +{ + Q_Q(ElaWindow); + if (_isWindowClosing || !_isNavigationEnable || !_isInitFinished) + { + return; + } + if (_pNavigationBarDisplayMode == ElaNavigationType::Minimal) + { + _resetWindowLayout(false); + } + if (_pNavigationBarDisplayMode == ElaNavigationType::Auto) + { + _isNavigationDisplayModeChanged = true; + _resetWindowLayout(false); + int width = q->centralWidget()->width(); + if (width >= 850 && _currentNavigationBarDisplayMode != ElaNavigationType::Maximal) + { + _navigationBar->setDisplayMode(ElaNavigationType::Maximal); + _currentNavigationBarDisplayMode = ElaNavigationType::Maximal; + _appBar->setWindowButtonFlag(ElaAppBarType::NavigationButtonHint, false); + } + else if (width >= 550 && width < 850 && _currentNavigationBarDisplayMode != ElaNavigationType::Compact) + { + _navigationBar->setDisplayMode(ElaNavigationType::Compact); + _currentNavigationBarDisplayMode = ElaNavigationType::Compact; + _appBar->setWindowButtonFlag(ElaAppBarType::NavigationButtonHint, false); + } + else if (width < 550 && _currentNavigationBarDisplayMode != ElaNavigationType::Minimal) + { + _navigationBar->setDisplayMode(ElaNavigationType::Minimal); + _currentNavigationBarDisplayMode = ElaNavigationType::Minimal; + _appBar->setWindowButtonFlag(ElaAppBarType::NavigationButtonHint); + } + _isNavigationBarExpanded = false; + } +} diff --git a/3rdparty/ElaWidgetTools/src/private/ElaWindowPrivate.h b/3rdparty/ElaWidgetTools/src/private/ElaWindowPrivate.h new file mode 100644 index 0000000..f18eafa --- /dev/null +++ b/3rdparty/ElaWidgetTools/src/private/ElaWindowPrivate.h @@ -0,0 +1,64 @@ +#ifndef ELAWINDOWPRIVATE_H +#define ELAWINDOWPRIVATE_H + +#include +#include +#include + +#include "Def.h" +#include "stdafx.h" +class ElaEvent; +class ElaWindow; +class ElaNavigationBar; +class ElaAppBar; +class ElaCentralStackedWidget; +class QHBoxLayout; +class QVBoxLayout; +class ElaThemeAnimationWidget; +class ElaWindowPrivate : public QObject +{ + Q_OBJECT + Q_D_CREATE(ElaWindow) + Q_PROPERTY_CREATE_D(int, ThemeChangeTime) + Q_PROPERTY_CREATE_D(ElaNavigationType::NavigationDisplayMode, NavigationBarDisplayMode) + +public: + explicit ElaWindowPrivate(QObject* parent = nullptr); + ~ElaWindowPrivate(); + Q_SLOT void onNavigationButtonClicked(); + Q_INVOKABLE void onWMWindowClickedEvent(QVariantMap data); + Q_SLOT void onThemeReadyChange(); + Q_SLOT void onDisplayModeChanged(); + Q_SLOT void onThemeModeChanged(ElaThemeType::ThemeMode themeMode); + Q_SLOT void onNavigationNodeClicked(ElaNavigationType::NavigationNodeType nodeType, QString nodeKey); + Q_SLOT void onNavigationNodeAdded(ElaNavigationType::NavigationNodeType nodeType, QString nodeKey, QWidget* page); + +private: + ElaThemeType::ThemeMode _themeMode; + QImage _lightBaseImage; + QImage _darkBaseImage; + bool _isWindowClosing{false}; + + bool _isInitFinished{false}; + ElaEvent* _focusEvent{nullptr}; + ElaNavigationBar* _navigationBar{nullptr}; + ElaCentralStackedWidget* _centerStackedWidget{nullptr}; + ElaAppBar* _appBar{nullptr}; + QHBoxLayout* _centerLayout{nullptr}; + int _contentsMargins{5}; + bool _isNavigationDisplayModeChanged{false}; + bool _isNavigationEnable{true}; + bool _isNavigationBarExpanded{false}; + bool _isWMClickedAnimationFinished{true}; + ElaThemeAnimationWidget* _animationWidget{nullptr}; + + ElaNavigationType::NavigationDisplayMode _currentNavigationBarDisplayMode{ElaNavigationType::Maximal}; + + QMap _routeMap; // key__nodeKey title可以一致 value__stackIndex + int _navigationTargetIndex{0}; + qreal _distance(QPoint point1, QPoint point2); + void _resetWindowLayout(bool isAnimation); + void _doNavigationDisplayModeChange(); +}; + +#endif // ELAWINDOWPRIVATE_H diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..3128aa5 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,111 @@ +cmake_minimum_required(VERSION 3.16) +project(Embalming_Girl) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_AUTOUIC ON) + +set(CMAKE_PREFIX_PATH "E:/QT6.6.3/6.6.3/mingw_64") + +add_compile_options("$<$:/utf-8>") +add_compile_options("$<$:-finput-charset=UTF-8>") +add_compile_options("$<$:-fexec-charset=UTF-8>") + +find_package(Qt6 COMPONENTS + Core + Gui + Widgets + WebSockets + OpenGLWidgets + Multimedia + REQUIRED) + +add_subdirectory(3rdparty/ElaWidgetTools) + +file(GLOB_RECURSE PROJECT_SOURCES + "src/*.h" + "src/*.cpp" +) + +set(APP_SOURCES + main.cpp + ${PROJECT_SOURCES} + resources.qrc +) + +add_executable(Embalming_Girl ${APP_SOURCES}) + +target_include_directories(Embalming_Girl PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src + ${CMAKE_CURRENT_SOURCE_DIR}/src/model + ${CMAKE_CURRENT_SOURCE_DIR}/src/network + ${CMAKE_CURRENT_SOURCE_DIR}/src/ui + ${CMAKE_CURRENT_SOURCE_DIR}/src/voice +) + +target_link_libraries(Embalming_Girl + Qt::Core + Qt::Gui + Qt::Widgets + Qt::WebSockets + Qt::OpenGLWidgets + Qt::Multimedia + ElaWidgetTools +) + +if (WIN32 AND NOT DEFINED CMAKE_TOOLCHAIN_FILE) + set(DEBUG_SUFFIX) + if (MSVC AND CMAKE_BUILD_TYPE MATCHES "Debug") + set(DEBUG_SUFFIX "d") + endif () + set(QT_INSTALL_PATH "${CMAKE_PREFIX_PATH}") + if (NOT EXISTS "${QT_INSTALL_PATH}/bin") + set(QT_INSTALL_PATH "${QT_INSTALL_PATH}/..") + if (NOT EXISTS "${QT_INSTALL_PATH}/bin") + set(QT_INSTALL_PATH "${QT_INSTALL_PATH}/..") + endif () + endif () + if (EXISTS "${QT_INSTALL_PATH}/plugins/platforms/qwindows${DEBUG_SUFFIX}.dll") + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory + "$/plugins/platforms/") + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + "${QT_INSTALL_PATH}/plugins/platforms/qwindows${DEBUG_SUFFIX}.dll" + "$/plugins/platforms/") + endif () + foreach (QT_LIB Core Gui Widgets WebSockets Network OpenGLWidgets OpenGL Multimedia) + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + "${QT_INSTALL_PATH}/bin/Qt6${QT_LIB}${DEBUG_SUFFIX}.dll" + "$") + endforeach (QT_LIB) +endif () + +add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + "$" + "$") + +add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + "${CMAKE_CURRENT_SOURCE_DIR}/config" + "$/config") + +add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory + "$/plugins/multimedia/") + +if (EXISTS "${QT_INSTALL_PATH}/plugins/multimedia") + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + "${QT_INSTALL_PATH}/plugins/multimedia" + "$/plugins/multimedia/") +endif () + +add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + "${CMAKE_CURRENT_SOURCE_DIR}/Res" + "$/Res") diff --git a/Doc/Game.md b/Doc/Game.md new file mode 100644 index 0000000..179d015 --- /dev/null +++ b/Doc/Game.md @@ -0,0 +1,399 @@ +# 《冰冷的她醒来之前》数字版开发指导文档 + +## 1. 项目概述 + +### 技术选型 +- **客户端**:C++20 + Qt6 (Widgets 模式,非 QML) +- **服务端**:Go 1.22+ +- **通信层**:WebSocket + JSON(游戏逻辑与信令);WebRTC(实时语音媒体传输) +- **语音架构**:客户端点对点(P2P)或选择性转发(SFU),服务端提供信令中继与房间管理 +- **架构模式**:客户端-服务器架构,服务端持有绝对权威 + +### 核心设计原则 +1. **服务端绝对权威**:所有游戏状态、逻辑判定、胜负计算、随机过程(洗牌)均由服务端完成。客户端仅负责渲染与输入采集。 +2. **状态快照同步**:服务端以完整游戏状态快照的形式向客户端广播,客户端在收到快照后更新本地视图。禁止客户端本地预测游戏结果。 +3. **信息隔离**:反面朝上的卡牌、其他玩家的手牌内容、尚未结算的质疑牌等敏感信息,在未被服务端授权前不得向客户端传输。客户端无法通过内存读取或抓包获知隐藏信息。 +4. **数据可配置化**:卡牌的具体属性(名称、人力值、优先顺序、胜利条件、效果描述、张数配置)全部外置为配置文件,由用户自行填写,程序只读取配置运行。 + +--- + +## 2. 客户端架构说明 + +### 2.1 渲染框架 +采用 Qt6 Widgets + QGraphicsScene 构建游戏主场景。场景按功能划分为若干逻辑层: + +- **背景层**:桌面、环境装饰 +- **公共区域层**:少女的尸体牌、调和区、信息提示牌 +- **玩家区域层**:每个玩家对应一个独立面板,包含其已使用特技区、质疑区、手牌数量指示器 +- **本地手牌层**:当前玩家自己的手牌,以可交互的卡牌视图呈现 +- **动画层**:卡牌移动、翻转、传递的过渡动画覆盖在所有层之上 +- **UI控件层**:行动菜单、弹窗、倒计时、日志面板 + +### 2.2 卡牌视图对象 +每张卡牌对应一个独立的图形对象,具备以下状态: +- **正面/反面**:正面显示卡牌的立绘与文字,反面显示统一的卡背图案 +- **选中/未选中**:高亮边框或抬升效果 +- **可用/禁用**:根据当前回合阶段,不可操作的卡牌降低透明度或置灰 +- **锁定**:暂时退出玩家的手牌进入锁定状态,不可被交互 + +卡牌对象的正面图像资源通过卡牌类型标识符从外部加载,程序不硬编码任何卡牌美术资源路径。 + +### 2.3 动画系统 +关键动画效果包括: +- **放置动画**:卡牌从手牌区飞向目标区域(调和区、质疑区、已使用区) +- **翻转动画**:结算阶段时,反面朝上的卡牌逐渐翻转为正面,使用缩放模拟3D翻转 +- **传递动画**:新闻部等效果导致手牌在玩家之间传递时,卡牌沿贝塞尔曲线路径飞行 +- **高亮动画**:需要玩家做出选择时,可选目标脉冲闪烁 + +动画播放期间应阻塞玩家输入,防止在动画未完成时发送操作指令。 + +### 2.4 实时语音模块 +客户端集成独立的语音通信子系统,与游戏逻辑模块解耦。 + +**音频采集与播放** +- 使用平台原生音频 API 或 Qt Multimedia 模块进行麦克风采集与扬声器播放 +- 支持 16kHz/48kHz 采样率,单声道,Opus 编码 +- 本地播放其他玩家的音频流,支持独立音量调节 + +**网络传输** +- 媒体流通过 WebRTC 传输,信令复用现有的 WebSocket 连接 +- 支持两种拓扑模式: + - **P2P 网状网络**:3-4 人房间时,各客户端直接互连,服务端仅转发信令 + - **SFU 选择性转发**:5-6 人房间或 P2P 连接失败时,由服务端语音中继模块转发混音后的音频流 + +**信令流程** +- 玩家加入房间后,通过 WebSocket 发送语音就绪通知 +- 服务端广播房间内其他玩家的语音端点信息(WebRTC SDP offer/answer 与 ICE candidate 通过 WebSocket 隧道传输) +- 连接建立后,音频通道与游戏通道并行运行 + +**语音交互界面** +- 每位玩家头像旁显示语音状态指示器: + - 静音(麦克风关闭) + - 发言中(语音活动检测触发,显示声波动画) + - 连接异常 +- 全局语音开关按钮:一键关闭/开启麦克风 +- 独立音量滑块:可单独调节每位玩家的接收音量 +- 可选的按键发言模式(按住空格键说话) + +**语音与游戏联动** +- 玩家暂时退出或断开连接时,自动将其语音通道静音 +- 结算阶段动画播放期间,语音保持畅通,不强制静音 + +--- + +## 3. 服务端架构说明 + +### 3.1 并发模型 +服务端采用 Go 的 Goroutine + Channel 模型。每个游戏房间由一个独立的逻辑 Goroutine 管理,该 Goroutine 内部维护一个事件循环,按顺序处理玩家输入、效果结算、状态广播。同一房间内的所有操作串行执行,避免并发竞争。网络 I/O 与游戏逻辑解耦,WebSocket 连接层将玩家消息投递到对应房间的 Channel 中。 + +### 3.2 房间管理 +服务端维护一个房间列表,每个房间包含: +- 房间唯一标识 +- 玩家列表(含连接状态、语音就绪状态) +- 当前游戏状态机 +- 历史日志(用于断线重连后同步) + +房间支持的状态:等待中、设置中、进行中、结算中、已结束。 + +### 3.3 游戏状态机 +游戏生命周期划分为以下主阶段,顺序不可逆: + +1. **等待阶段**:玩家加入、准备、房主发起开始 +2. **设置阶段**:根据人数计算调和目标值,从牌库中移除指定卡牌,构建本局牌库 +3. **发牌阶段**:洗牌并均分到手牌,确定起始玩家 +4. **进行阶段**:主循环,玩家依次行动 +5. **结算阶段**:全员暂时退出后,依次执行调和判定、质疑判定、胜利判定 +6. **结束阶段**:公布结果,房间回到等待状态或自动解散 + +进行阶段内部可细分为子状态: +- 等待选牌 +- 等待选择行动方式(特技/调和/质疑) +- 等待选择目标(质疑对象或特技目标) +- 效果结算中 +- 等待效果响应(如其他玩家选牌) +- 回合结束检查 + +### 3.4 效果系统架构 +效果系统采用策略模式或接口驱动模式。每张卡牌的能力对应一个独立的效果处理器。效果处理器注册到全局效果表中,通过卡牌类型标识符查找触发。 + +效果处理器需声明以下能力: +- **触发时机**:打出时触发、回合开始时触发、回合结束时触发、结算阶段触发 +- **是否需要响应**:是否需要向其他玩家发送选择请求并等待回复 +- **响应类型**:选牌、选人、确认、二选一 +- **是否可被取消**:在何种条件下效果失效(如目标不存在、玩家已退出) + +效果结算采用链式或队列式处理:若一张卡牌的效果引发了另一张卡牌的条件(如外星人回应优等生),则效果管理器按顺序入队,依次处理。 + +### 3.5 实时语音中继模块 +服务端运行一个与游戏逻辑并行的语音信令与媒体中继层。 + +**信令中继** +- 复用现有 WebSocket 连接,不额外开放端口 +- 定义语音专属信令消息类型:SDP offer、SDP answer、ICE candidate、语音就绪、语音断开 +- 收到某玩家的信令后,向目标玩家转发,服务端不解析 SDP 内容 + +**SFU 媒体中继(可选)** +- 当房间人数大于 4 人或 P2P 打洞失败时,启用 SFU 模式 +- 服务端接收各客户端的音频流,进行混音或选择性转发 +- 使用 Go 的 WebRTC 库(如 pion/webrtc)实现 +- 混音策略:最多同时转发 3 路音频,避免带宽爆炸;优先转发最近发言者 + +**语音房间管理** +- 语音频道生命周期与游戏房间绑定:房间创建时初始化语音会话,房间销毁时关闭 +- 玩家加入房间后,可选择是否加入语音(默认不自动开启麦克风) +- 玩家断线时,服务端通知其他客户端移除该玩家的音频轨道 + +--- + +## 4. 通信协议框架 + +### 4.1 传输层 +基于 WebSocket 的全双工通信,消息载荷为 JSON。所有消息包含统一的元数据:消息类型、时间戳、序列号(用于丢包检测与断线重连)。 + +### 4.2 客户端向服务端发送的消息类型 +- **加入/离开房间** +- **准备/取消准备** +- **开始游戏**(仅房主) +- **选择手牌**(从手牌中指定一张) +- **确认行动**(选择特技、调和或质疑,质疑需附带目标玩家标识) +- **效果响应**(在效果处理器要求下,选择特定卡牌或玩家,或确认/取消) +- **聊天消息**(可选) +- **语音信令**(WebRTC SDP/ICE,透传至目标玩家) +- **语音状态**(麦克风开启/关闭) + +### 4.3 服务端向客户端广播的消息类型 +- **房间状态同步**(玩家列表、准备状态、房主) +- **游戏开始通知**(附带初始配置) +- **完整状态快照**(游戏进行中的核心同步消息,包含所有公开信息与当前玩家自己的私有信息) +- **卡牌移动事件**(某张卡牌从某区域移动到某区域,是否反面朝上) +- **卡牌翻转事件**(结算时翻开调和区或质疑区) +- **效果触发通知**(告知所有玩家某效果正在结算,以及需要响应的玩家) +- **请求选择**(向特定玩家发送弹窗请求,要求其在限时内做出选择) +- **玩家暂时退出通知** +- **结算阶段通知**(进入调和判定、质疑判定、胜利判定) +- **游戏结束通知**(获胜者列表、结局类型) +- **错误通知**(非法操作、超时、网络异常) +- **语音信令转发**(将某玩家的 SDP/ICE 转发给目标玩家) +- **语音状态同步**(广播某玩家的麦克风开启/关闭状态) + +### 4.4 状态快照的信息隔离策略 +状态快照是服务端向客户端同步的核心机制,必须严格遵守信息隔离: + +- **自己的手牌**:完整传输卡牌标识符、类型、人力值、优先顺序 +- **他人的手牌**:仅传输数量,不传输任何卡牌内容 +- **调和区**:游戏进行中仅传输卡牌标识符与放置顺序,不传输类型和人力值;结算阶段由服务端单独发送翻转事件 +- **质疑区**:对质疑对象(被放置卡牌的玩家)是否可见由服务端决定;对非对象玩家,仅传输质疑区存在卡牌的数量或标识符,不传输内容 +- **已使用区(特技区)**:正面朝上,完整传输所有信息 +- **牌库**:不传输 + +### 4.5 语音信令消息格式 +语音相关消息复用现有 WebSocket 通道,通过消息类型字段区分: + +- **SDP Offer**:发起方发送,包含目标玩家 ID 与 SDP 字符串 +- **SDP Answer**:响应方发送,包含目标玩家 ID 与 SDP 字符串 +- **ICE Candidate**:双方交换网络候选路径,包含目标玩家 ID、候选类型、地址信息 +- **Voice Ready**:客户端通知服务端本地语音引擎初始化完成 +- **Voice Toggle**:客户端通知服务端麦克风开启或关闭,服务端广播给房间内其他玩家 + +--- + +## 5. 游戏逻辑核心流程 + +### 5.1 设置与发牌 +根据玩家人数,从完整牌库中按配置移除指定类型和数量的卡牌,形成本局牌库。洗牌后按人数均分为手牌。持有特定起始标记卡牌的玩家成为起始玩家;若该卡牌已被移除,则随机指定。 + +### 5.2 回合流程 +当前玩家从手牌中选择一张卡牌,并选择以下三种行动之一: + +1. **特技**:将卡牌正面朝上放置于自己的已使用区,并触发该卡牌的效果。部分卡牌持有即生效,无需打出;打出后可能不产生额外效果。 +2. **调和**:将卡牌反面朝上放置于调和区(尸体旁)。放置时记录先后顺序。 +3. **质疑**:将卡牌反面朝上放置于另一名玩家的质疑区。不可质疑自己。 + +回合结束时,若玩家手牌仅剩最后一张,则该玩家暂时退出游戏。退出后不再参与回合轮转,其手牌被锁定(不再受到移动手牌类效果的影响),但仍可成为质疑目标。 + +### 5.3 效果失效与边界情况 +效果系统需处理以下边界情况: +- 若效果的目标已不存在(如目标玩家已退出,或目标区域为空),则效果立即中止。 +- 某些卡牌的效果需要持有者存活到特定时机才能生效;若持有者在效果生效前已退出,则效果自然失效。 +- 暂时退出的玩家跳过需要其主动响应的效果(如新闻部传递手牌时跳过已退出者,将牌传给其左手边未退出的玩家)。 + +### 5.4 结算阶段 +当所有玩家均暂时退出后,进入结算阶段,分为三个顺序执行的判定: + +**调和判定**:翻开调和区所有卡牌,统计人力值总和。若总和达到或超过目标值,则调和成功;否则调和失败。 + +**质疑判定**:将每个玩家质疑区的卡牌全部改为正面朝上,分别计算各玩家质疑区的人力值合计。合计值最高的一名或多名玩家被视为犯罪嫌疑人而受到监禁。若所有玩家的合计值相同(包括全为零),则无人被监禁。人力值为负数的卡牌在质疑判定时视为零。 + +**胜利判定**:按照优先顺序由低到高(从第一优先到第五优先)依次检查每名玩家的最后一张手牌。若某优先顺序下有玩家满足其胜利条件,则这些玩家立即获胜,后续优先顺序不再检查。若同一优先顺序下有多名玩家满足条件,则他们共同获胜或共同失败。即使被监禁的玩家也可以参加胜利判定。 + +### 5.5 结局类型 +- **正常胜利**:一至多名玩家满足胜利条件 +- **全灭结局**:没有任何玩家满足胜利条件 +- **混沌模式变体**:可在设置阶段开启。该模式下,玩家最后一张手牌也可以通过能力效果转移,但玩家仍然处于暂时退出状态。 + +--- + +## 6. 界面交互设计规范 + +### 6.1 主界面布局 +界面采用横向宽屏布局,中央为公共区域,四周为玩家面板。 + +- **顶部**:房间信息、当前回合玩家指示、剩余时间倒计时、全局语音开关按钮 +- **中央上方**:其他玩家的面板,以头像或名称标识,显示其手牌数量、已使用区、质疑区;头像旁显示语音状态指示灯 +- **中央**:少女的尸体牌与调和区。调和区以横向排列的卡牌槽位展示,卡牌反面朝上时显示卡背,结算时依次翻开 +- **底部**:当前玩家自己的手牌区,以扇形或横向排列展示,支持点击选中 +- **底部操作栏**:选中卡牌后弹出行动按钮(特技、调和、质疑)。选择质疑后,其他玩家头像高亮,点击目标后确认 +- **右下角**:游戏日志面板,滚动显示操作记录 +- **左侧边栏(可选)**:房间内玩家列表与独立音量控制滑块 + +### 6.2 交互状态机 +客户端界面需维护一个与服务器对应的本地交互状态: +- **空闲**:等待自己或他人的回合,禁止操作 +- **选牌中**:自己的回合,可点击手牌,选中后弹出行动菜单 +- **选目标中**:已选择质疑或需要指定目标的特技,等待点击目标玩家 +- **等待响应**:自己的效果处理中,或等待其他玩家响应效果 +- **选择响应中**:服务端要求自己选择卡牌或玩家作为效果响应 +- **观看结算**:结算阶段,观看动画,禁止操作 + +### 6.3 信息提示与辅助 +- 鼠标悬停在卡牌上时,显示该卡牌的详细说明(从配置读取) +- 调和区显示当前放置顺序编号 +- 当效果需要玩家做出选择时,界面中央弹出模态对话框,附带倒计时条 +- 超时未操作则自动执行默认行为或视为放弃(由服务端定义超时惩罚) + +### 6.4 语音交互细节 +- 发言者头像边框高亮或显示绿色脉冲环,直观指示当前谁在说话 +- 玩家手动关闭麦克风时,头像旁显示静音图标 +- 语音连接质量不佳时,显示弱网警告图标 +- 提供全局静音按钮(仅关闭接收,不影响发送)用于专注思考 + +--- + +## 7. 关键机制实现要点 + +### 7.1 信息隔离的技术实现 +信息隔离是防止作弊的核心。服务端在构建状态快照时,必须为每个玩家生成定制化的视图: +- 遍历所有卡牌,根据卡牌位置、朝向、以及当前接收快照的玩家身份,决定该卡牌在此快照中暴露哪些字段 +- 卡牌内容字段(类型、人力值、优先顺序)仅在卡牌正面朝上且对该玩家可见时填充;否则留空或设为占位符 +- 客户端渲染器根据卡牌内容是否存在,决定显示正面图像还是卡背图像 + +### 7.2 断线重连 +玩家断线后,服务端保留其游戏状态一段时间。重连时,服务端向该玩家发送当前完整状态快照,客户端重建界面。若断线期间有需要该玩家响应的效果,应延长等待时间或为其选择默认选项。语音连接随游戏连接一并重建。 + +### 7.3 时序与并发 +- 所有玩家操作必须带服务端时间戳排序 +- 效果结算期间,房间应处于“结算中”状态,拒绝新的玩家操作(聊天与语音除外) +- 动画播放时间由客户端本地控制,但逻辑上服务端在发送事件后立即进入下一状态,不等待客户端动画完成。客户端通过队列缓冲事件,保证动画按顺序播放 + +### 7.4 配置热加载 +卡牌库、设置规则、胜利条件等应存储于外部配置文件(如 JSON)。服务端启动时加载,游戏房间创建时复制一份配置快照,防止运行中修改配置影响已开局的房间。用户自行填写卡牌内容、人力值、优先顺序、张数、胜利条件、效果文本等字段,程序仅校验数据类型和逻辑一致性(如优先顺序范围、人力值范围)。 + +--- + +## 8. 开发阶段建议 + +### 第一阶段:网络与框架 +实现 WebSocket 连接、房间管理、玩家加入离开、准备开始。客户端完成主界面布局与卡牌对象的基本显示。 + +### 第二阶段:核心循环 +实现发牌、回合轮转、三种基础行动(特技/调和/质疑)、暂时退出机制。确保状态快照能正确同步,信息隔离生效。 + +### 第三阶段:效果系统 +搭建效果处理器框架。逐个实现卡牌效果,重点处理需要多玩家响应的复杂效果(如手牌传递、交换、查看)。确保效果失效边界情况被覆盖。 + +### 第四阶段:语音集成 +集成 WebRTC 或音频库,实现信令交换、P2P/SFU 媒体传输、语音状态指示器。先在等待房间中测试语音稳定性,再集成到游戏流程中。 + +### 第五阶段:结算与动画 +实现三阶段结算流程。客户端配合完成卡牌翻转动画、结算结果展示。处理共同胜利、全灭结局等分支。 + +### 第六阶段: polish 与变体 +添加混沌模式、音效、断线重连、观战模式、游戏日志导出。 + +--- + +## 9. 卡牌信息配置表 + +以下表格为游戏核心配置模板,请对照实体卡牌逐一填写。**程序启动时读取此表,运行时不再修改。** + +### 9.1 基础卡牌属性表 + +| 序号 | 卡牌类型标识符(英文,程序内使用) | 中文名称 | 人力值 (MP) | 优先顺序 | 胜利条件简述 | 效果触发时机 | 效果描述(玩家可见文本) | 单卡张数 | 备注 | +| :--: | :--------------------------------- | :------- | :---------: | :------: | :--------------------- | :----------- | :----------------------------------------------------------- | :------: | :--- | +| 1 | honor_student | 优等生 | 2 | 4 | 调和成功即可获胜 | | 除你之外的所有玩家闭上眼睛,然后持犯人牌的玩家吐出舌头。等他们将舌头收回后所有玩家睁开眼睛。(一边发号施令一边进行游戏) | 3 | | +| 2 | health_committee_member | 保健委员 | 1 | 4 | 调和成功即可获胜 | | 你可以选择1张其他玩家已使用(正面朝上)的非保健委员的牌,将它加入自己的手牌中。 | 2 | | +| 3 | accomplice | 共犯 | 0 | 3 | 犯人获胜即可获胜 | | 将1张放在任意玩家面前质疑位置的牌转移到1名其他玩家面前。 | 1 | | +| 4 | library_committee_member | 图书委员 | 1 | 4 | 调和成功即可获胜 | | 你可以查看放在调和位置的所有卡牌。(※查看时注意不要改变卡牌的摆放顺序) | 3 | | +| 5 | alien | 外星人 | -1 | 1 | 被监禁即可获胜 | | 当你持有这张牌时,若有人使用了优等生的特技,你可以伸出舌头,装作是犯人来回应他。当你正面打出这张牌时,不会产生任何效果。 | 1 | | +| 6 | young_lady | 大小姐 | 1 | 4 | 调和成功即可获胜 | | 随机抽取1名其他玩家的1张手牌,然后选择1张自己的手牌保持反面朝上的状态返还给他。(你可以将抽到的那张返还给他) | 3 | | +| 7 | student_council_president | 学生会长 | 3 | 4 | 调和成功即可获胜 | | 持有者成为起始玩家(只需宣称持有即可)。正面打出时不会产生任何效果。 | 1 | | +| 8 | go_home_club | 归宅部 | 0 | 5 | 没有任何人获胜即可获胜 | | 你可以将1张自己的手牌和1张放在调和位置上的牌交换。 | 3 | | +| 9 | infected_person | 感染者 | 0 | 2 | 调和失败即可获胜 | | 在你的下一个回合开始时,若这张牌仍然正面朝上摆在你面前,则可以将调和位置的1张牌加入自己的手牌中。 | 1 | | +| 10 | newspaper_club | 新闻部 | 1 | 4 | 调和成功即可获胜 | | 每位玩家选择1张自己的手牌,将它保持反面朝上的状态传给自己左手边的玩家。 | 3 | | +| 11 | culprit | 犯人 | 0 | 3 | 不被监禁即可获胜 | | 这张牌无论是正面朝上还是反面朝上都无法被使用。(但可因其他卡牌的能力效果而进行移动) | 1 | | +| 12 | class_monitor | 班长 | 2 | 4 | 调和成功即可获胜 | | 选择1名玩家,双方各选择1张自己的手牌,然后保持反面朝上的状态转交给对方。(也可以选择一个人和自己换牌) | 2 | | +| 13 | discipline_committee_member | 风纪委员 | 1 | 4 | 调和成功即可获胜 | | 你可以查看一名玩家的所有手牌。 | 2 | | +| 14 | | | | | | | | | | +| 15 | | | | | | | | | | + +> **字段说明** +> +> - **卡牌类型标识符**:程序代码中使用的枚举名或键名,建议英文驼峰命名,如 `StudentCouncilPresident` +> - **优先顺序**:胜利判定时的检查顺序,数字越小越早检查(1 最先,5 最后) +> - **效果触发时机**:可选值:`持有即生效` / `打出时触发` / `回合开始时触发` / `回合结束时触发` / `结算阶段触发` +> - **单卡张数**:该卡牌在完整牌库中的总张数 + +### 9.2 游戏设置规则表(按人数移除卡牌) + +| 玩家人数 | 调和目标值 | 需移除的卡牌类型 | 本局总牌数 | 每人手牌数 | +| :------: | :--------: | :----------------------------------------------------------- | :--------: | :--------: | +| 3 | 9 | 图书委员×1、共犯×1、优等生×1、风纪委员×1、大小姐×1、新闻部×1、归宅部×1 | 18 | 6 | +| 4 | 8 | 图书委员×1 | 24 | 6 | +| 5 | 7 | 无(使用全部卡牌) | 25 | 5 | +| 6 | 6 | 图书委员×1 | 24 | 4 | + +> **填写说明** +> +> - **调和目标值**:通常为 `12 - 玩家人数`,请确认实体规则 +> - **需移除的卡牌类型标识符**:填写从完整牌库中移除的卡牌,多张请用逗号分隔 +> - **每人手牌数**:总牌数 ÷ 人数,确认是否整除 + +### 9.3 特殊规则对照表 + +| 规则项 | 实体规则描述 | 程序实现要点 | +| :------------------------- | :----------------------------------------------------------- | :----------------------------------------------------------- | +| **起始玩家确定方式** | 持有"学生会长"的玩家成为起始玩家,只需宣称自己持有这张牌即可,并不需要第一张就打出 | 发牌完成后,服务端遍历所有玩家手牌,找到持有学生会长卡的玩家设为当前回合玩家。若该卡被移除(本局未加入牌库),则随机指定一名玩家为起始玩家。 | +| **犯人卡使用限制** | 这张牌无论是正面朝上还是反面朝上都无法被使用,但可因其他卡牌的能力效果而进行移动 | 行动验证阶段,若玩家选择犯人卡进行特技/调和/质疑,服务端直接拒绝并返回错误。犯人卡仅能通过班长交换、大小姐抽牌、新闻部传递、归宅部交换、共犯转移等被动方式移动。 | +| **感染者效果失效条件** | 若你在剩余手牌不到2张时使用这张牌,你无法活到下一个回合,效果自然失效 | 服务端允许玩家打出感染者,但在效果系统中标记:若打出后该玩家手牌数≤1(立即退出),则感染者效果标记为失效,下回合开始时不再触发选牌请求。 | +| **暂时退出后手牌锁定范围** | 手牌将被锁定,不再受到移动手牌的能力效果影响,但可以被质疑 | 退出玩家的手牌添加"locked"标记。效果系统处理移动手牌类效果(班长、大小姐、新闻部)时跳过这些卡牌。质疑操作不受影响,仍可正常被放置质疑牌。 | +| **质疑区MP合计为负时处理** | 质疑的合计值不会是负数。若合计值是-1,则将它当作0 | 结算阶段计算质疑区MP总和时,对每张卡牌取 `max(0, mp)`。外星人MP=-1,在质疑区贡献0点。 | +| **归宅部与犯人联动规则** | 归宅部可以将1张手牌和1张调和位置的牌交换。若犯人通过归宅部效果进入调和区,则可能出现"其实根本就没有犯人"的结局 | 允许交换。若犯人牌被换入调和区,调和判定阶段统计MP时犯人牌贡献0点(与实际MP一致)。归宅部持有者手牌中不再持有犯人。 | +| **混沌模式与基础模式差异** | 混沌模式中,玩家的最后一张手牌也可以用能力进行转移,但玩家仍然处于暂时退出的状态 | 开启混沌模式后,效果系统处理移动手牌效果时,不跳过仅剩1张手牌的玩家(允许其手牌被交换、传递、抽走)。但该玩家仍标记为已退出,不再参与回合轮转。 | +| **全灭结局触发条件** | 没有任何人能获得胜利而结束游戏的情况 | 胜利判定阶段按优先顺序1→5依次检查,若所有优先顺序下均无人满足胜利条件,则广播全灭结局。 | + +### 9.4 卡牌效果详细规格表(供程序逻辑参考) + +| 卡牌名称 | 是否需要选择目标 | 目标类型 | 是否影响他人手牌 | 是否影响调和区 | 是否影响质疑区 | 效果失效条件 | 响应超时处理 | +| :----------- | :----------------- | :----------------------------------------------------------- | :----------------------: | :----------------------: | :------------: | :----------------------------------------------------------- | :----------------------------------------------------------- | +| **学生会长** | 否 | 无 | 否 | 否 | 否 | 无(持有即生效,打出不产生效果) | 无 | +| **班长** | 是 | 先选择1名玩家,再双方各选1张自己的手牌 | 是 | 否 | 否 | 目标玩家已退出;目标玩家手牌为空 | 超时自动随机选择1张手牌完成交换 | +| **优等生** | 否(自动触发) | 无(向所有其他玩家发送闭眼指令,向持犯人牌玩家发送提示,向外星人持有者发送是否回应提示) | 否 | 否 | 否 | 其他玩家全部已退出;场上无犯人牌 | 犯人玩家超时未确认吐舌头视为不回应;外星人持有者超时未确认视为不装作犯人 | +| **风纪委员** | 是 | 选择1名玩家 | 否(仅查看,不移动) | 否 | 否 | 目标玩家已退出;目标玩家手牌为空 | 无(纯信息展示,无需超时处理) | +| **大小姐** | 是 | 系统随机选择1名其他玩家并随机抽其1张手牌,然后自己选择1张手牌返还 | 是 | 否 | 否 | 无其他玩家;被抽牌玩家手牌为空 | 超时自动将抽到的牌原路返还,或随机选择1张自己的手牌返还 | +| **新闻部** | 否(但需全员响应) | 每位未退出玩家选择自己的1张手牌 | 是 | 否 | 否 | 无 | 每位玩家超时自动随机选择1张手牌传递;已退出玩家自动跳过 | +| **归宅部** | 是 | 选择自己的1张手牌 + 选择调和区1张牌 | 否 | 是 | 否 | 调和区无卡牌;自己手牌为空 | 超时自动取消效果 | +| **图书委员** | 否 | 无 | 否 | 否(仅查看,不改变顺序) | 否 | 调和区无卡牌 | 无(纯信息展示) | +| **保健委员** | 是 | 选择其他玩家已使用区(SkillZone)中的1张非保健委员牌 | 是(从已使用区移入手牌) | 否 | 否 | 所有玩家已使用区为空;或仅有保健委员 | 超时自动随机选择1张符合条件的牌,若无则取消效果 | +| **外星人** | 否(被动触发) | 无 | 否 | 否 | 否 | 无人使用优等生特技;正面打出时无效果 | 收到优等生触发通知后,超时未响应视为不装作犯人 | +| **感染者** | 是(延迟触发) | 下回合开始时选择调和区1张牌 | 否 | 是 | 否 | 打出后手牌≤2(立即退出,无法活到下回合);下回合开始时此牌不在已使用区;调和区无卡牌 | 超时自动随机选择1张调和区卡牌加入手牌,或放弃效果 | +| **犯人** | N/A | N/A | N/A | N/A | N/A | 无法主动使用 | N/A | +| **共犯** | 是 | 先选择任意质疑区1张牌,再选择1名其他玩家 | 否 | 否 | 是 | 全房间质疑区无卡牌 | 超时自动随机选择1张质疑区卡牌和1名随机其他玩家完成转移 | + +> **字段说明** +> - **目标类型**:该效果是否需要玩家选择目标,以及目标的类型 +> - **是否影响他人手牌**:效果是否会导致其他玩家的手牌数量或内容发生变化 +> - **响应超时处理**:若需要其他玩家响应(如新闻部选牌),超时后如何处理(自动随机选择 / 跳过该玩家 / 效果取消) + +--- + +> **文档说明**:本文档为框架性指导,所有涉及具体卡牌属性(人力值、优先顺序、效果描述、卡牌张数、移除规则)的字段均留空,由开发者根据实体卡牌自行填入配置文件。文档不涉及具体代码实现与项目目录结构。 \ No newline at end of file diff --git a/Doc/Rule.md b/Doc/Rule.md new file mode 100644 index 0000000..99c4f41 --- /dev/null +++ b/Doc/Rule.md @@ -0,0 +1,114 @@ +# 《冰冷的她醒来之前》规则说明书 + +## 基本信息 +- **游戏名称**:冰冷的她醒来之前 / Embalming Girl +- **游戏设计**:ゆお +- **美术设计**:うすくち +- **规则说明书**:2020年6月30日版 +- **人数**:3~6人 +- **游戏时间**:10分钟 +- **适合人群**:10岁以上 + +--- + +## 游戏配件 +- 游戏卡牌 25张 +- 少女的尸体牌 1张 +- 信息牌 2张 + +--- + +## 游戏概述 + +使感染者突然狂暴化的未知病毒席卷全球,世界随之产生巨变。而在这里的圣莉莉女子学院中也有暴徒汹涌而至,因此学生们纷纷逃往日校舍的方向。"那些家伙"似乎并没有发现旧校舍的位置,但少女们只能在这里屏住呼吸,不发出一点声响,并静待着救援的到来。她们饱含着不安与恐怖的情绪度过了一天,而当第二天天亮后,众人赫然发现同伴中的一个人死去了……。因为不知道救援何时会到来,所以她们觉得不能将尸体就这样放着不管。因此少女们根据自己所在班级分成好几组,并试着使用校内有限的资源来对尸体进行防腐处理。而她们同时也在寻找着那可能就混在她们之中的杀人犯的线索……。 + +在这个游戏中,玩家将成为各自班级的领队,并对班里的成员(手牌)发出指令。而剩下的最后那张手牌便是领队,也就是你自己。最终,你们又是否能找到事件的真相,并在这场惨剧中存活下去吗? + +--- + +## 游戏卡牌说明 + +卡牌各栏位含义: +1. **卡牌强度**(MP=人力) +2. **卡牌名称** +3. 将卡牌正面朝上使用时所发动的**卡牌能力**。但有的能力仅需持有那张卡牌就可以直接发动 +4. **背景叙述文本**,与游戏本身并无直接关系 +5. 卡牌的**胜利条件**和它的**优先顺序** +6. **相同卡牌的总数**(人形标记的数量就代表卡牌的总数) + +--- + +## 游戏设置 + +1. **设定调和目标值**:根据玩家人数设定调和的目标值。3人游戏时的目标值为"9",4人游戏时为"8",在这之后每多加1名玩家便减少1点目标值(也就是说**目标值 = 12 - 玩家人数**)。将少女的尸体牌放在桌子的中央位置,并将信息牌的正面(蓝色那面)重叠放在尸体牌右侧的数字上,让人能更直观地知道这次的目标值。另外1张信息牌可供大家查看。 + +2. **调整卡牌数量**:根据玩家人数来调整需要使用的游戏卡牌数量。 + - **4人或6人游戏**:从卡牌中去掉1张"图书委员",使用总计24张卡牌 + - **3人游戏**:在4人基础上再去掉"共犯"、"优等生"、"风纪委员"、"大小姐"、"新闻部"、"归宅部"各1张,使用总计18张卡牌 + - **5人游戏**:使用全部的25张卡牌 + +3. **分发手牌**:将游戏卡牌反面朝上进行洗牌,然后将全部卡牌平均分发给各位玩家。 + - 3~4人时:每人6张 + - 5人时:每人5张 + - 6人时:每人4张 + +4. **确定起始玩家**:持有"学生会长"的玩家将成为起始玩家(玩家只需宣称自己持有这张牌即可,并不需要第一张就打出)。 + +--- + +## 回合流程 + +1. **玩家行动**:玩家从手牌中选出1张牌,并从以下三种使用方式中选择一种来执行: + - **A. 特技**:将卡牌**正面朝上**放在自己面前,并使用能力栏中的效果 + - **B. 调和**:将卡牌**反面朝上**放在少女的尸体旁边。放置时请注意能让人清楚地识别出放置卡牌的先后顺序 + - **C. 质疑**:将卡牌**反面朝上**放在一名**其他玩家**面前,表示质疑。玩家不能质疑自己 + +2. **暂时退出**:在自己的回合结束时,手牌只剩下最后1张的玩家将从游戏中暂时退出。在这之后都不会再轮到他的回合,并且也不会受到移动手牌的能力效果影响(他的手牌将被锁定)。但是,可以受到质疑。 + +3. **轮到下一位**:之后,便轮到**左边玩家**的回合。 + +--- + +## 游戏中的示例 + +(图例说明:包含调和位置的卡牌、调和目标值、玩家A的质疑牌、你的质疑牌、你的已使用卡牌、手牌、玩家B的已使用卡牌等布局示意) + +--- + +## 游戏结束 + +当所有人都暂时退出游戏时,便进入胜利判定阶段: + +1. **调和判定**:将放在调和位置的卡牌全部翻开,并统计MP的合计值。若合计值达到或超出目标值,则视为**调和成功**。 + +2. **质疑判定**:将放在质疑位置的卡牌全部改为正面朝上,并分别计算出各自的MP合计值。**合计值最高的玩家将被当作犯罪嫌疑人而受到监禁**。若合计值最高的玩家有多个,则他们都将受到监禁。但是,若所有人的合计值都一样,则不会有任何人受到监禁。(质疑的合计值不会是负数。若合计值是-1,则将它当作0。) + +3. **胜利判定**:玩家根据胜利条件的**优先顺序**(按照从1到5的顺序),依次公开自己的手牌,并判定玩家是否满足胜利条件。若某位玩家满足了胜利条件,则他将立刻直接获得胜利。而排在他后面的人将不再进行胜负判定("优先顺序第一的玩家是谁?""是我"→判定,"优先顺序第二的玩家是谁?""是我"→判定,就像这样进行判定。若此时有胜者出现,则判定结束。也就是说即使你已经满足了胜利条件,若被别人先你一步获胜,那么你将无法赢过他)。而优先顺序相同的人当然可以一起获得胜利(亦或是一起败北)。还有,即使是受到了监禁的玩家也可以参加胜负判定。 + +--- + +## 其他、游戏补充说明 + +- 若无论如何都无法使用卡牌的能力效果,则该能力的使用将立刻被中止。(例如,在其他玩家全都暂时退出游戏的状态下某个玩家使用了大小姐、又或者是在调和位置上没有卡牌的状态下使用了归宅部等卡牌时,会被立刻中止使用卡牌的效果。) +- 即使你使用了感染者,若你无法轮到自己的下一个回合,那么你所使用的感染者的效果自然也不会生效。也就是说,若你在剩余手牌不到2张时使用这张牌,它的能力不会生效。 +- 若是在有暂时退出游戏(手牌只剩1张)的玩家存在的情况下使用了新闻部的能力,则请直接跳过那名暂时退出的玩家,并将卡牌交给该玩家左边的玩家。 +- 由于犯人牌无法使用,所以基本上可以肯定有某个玩家一定会成为犯人。但是,由于归宅部的能力效果,使得"犯人去调和药剂了"这唯一的可能性发生。(此时,就会变成"其实根本就没有犯人,死去的她其实只是病死的。"这一结局。) +- 有时也可能发生没有任何人能获得胜利而结束游戏的情况。此时则被视为**玩家全灭结局**。 + +--- + +## 变体规则 + +觉得"最后的那张手牌居然会受到保护,这也太天真了点吧。"的人,可以在征得所有玩家的同意后,使用游戏的变体规则——**混沌模式**。 + +在该模式中,玩家的最后一张手牌也可以用能力进行转移,但玩家仍然处于从游戏中暂时退出的状态,这点与基础规则一致。 + +--- + +## 附录:卡牌文字(右侧可见) + +| 卡牌名称 | 内容 | +| ------------ | ----------------- | +| **调和失败** | Embalming failure | +| **优先顺序** | 3 | +| **胜利条件** | 犯人获胜即可获得 | \ No newline at end of file diff --git a/Res/Character/filenames.txt b/Res/Character/filenames.txt new file mode 100644 index 0000000..bb20a6b --- /dev/null +++ b/Res/Character/filenames.txt @@ -0,0 +1,14 @@ +ŵ.png +ίԱ.png +.png +ͼίԱ.png +.png +С.png +ѧ᳤.png +լ.png +Ⱦ.png +Ƶǰļеļ.bat +Ų.png +.png +೤.png +ίԱ.png diff --git a/Res/Character/优等生.png b/Res/Character/优等生.png new file mode 100644 index 0000000..0db8864 Binary files /dev/null and b/Res/Character/优等生.png differ diff --git a/Res/Character/保健委员.png b/Res/Character/保健委员.png new file mode 100644 index 0000000..4a0da48 Binary files /dev/null and b/Res/Character/保健委员.png differ diff --git a/Res/Character/共犯.png b/Res/Character/共犯.png new file mode 100644 index 0000000..b8daff4 Binary files /dev/null and b/Res/Character/共犯.png differ diff --git a/Res/Character/图书委员.png b/Res/Character/图书委员.png new file mode 100644 index 0000000..e8c9068 Binary files /dev/null and b/Res/Character/图书委员.png differ diff --git a/Res/Character/外星人.png b/Res/Character/外星人.png new file mode 100644 index 0000000..861a869 Binary files /dev/null and b/Res/Character/外星人.png differ diff --git a/Res/Character/大小姐.png b/Res/Character/大小姐.png new file mode 100644 index 0000000..2a4357c Binary files /dev/null and b/Res/Character/大小姐.png differ diff --git a/Res/Character/学生会长.png b/Res/Character/学生会长.png new file mode 100644 index 0000000..eaf0c8d Binary files /dev/null and b/Res/Character/学生会长.png differ diff --git a/Res/Character/归宅部.png b/Res/Character/归宅部.png new file mode 100644 index 0000000..c5f7ad6 Binary files /dev/null and b/Res/Character/归宅部.png differ diff --git a/Res/Character/感染者.png b/Res/Character/感染者.png new file mode 100644 index 0000000..53a1003 Binary files /dev/null and b/Res/Character/感染者.png differ diff --git a/Res/Character/新闻部.png b/Res/Character/新闻部.png new file mode 100644 index 0000000..145069d Binary files /dev/null and b/Res/Character/新闻部.png differ diff --git a/Res/Character/犯人.png b/Res/Character/犯人.png new file mode 100644 index 0000000..db1bdb4 Binary files /dev/null and b/Res/Character/犯人.png differ diff --git a/Res/Character/班长.png b/Res/Character/班长.png new file mode 100644 index 0000000..8e7674d Binary files /dev/null and b/Res/Character/班长.png differ diff --git a/Res/Character/风纪委员.png b/Res/Character/风纪委员.png new file mode 100644 index 0000000..b14f75f Binary files /dev/null and b/Res/Character/风纪委员.png differ diff --git a/Res/Font/ElaAwesome.ttf b/Res/Font/ElaAwesome.ttf new file mode 100644 index 0000000..a2d3c33 Binary files /dev/null and b/Res/Font/ElaAwesome.ttf differ diff --git a/Res/Souce/IMG_20260616_101957.jpg b/Res/Souce/IMG_20260616_101957.jpg new file mode 100644 index 0000000..342c2f5 Binary files /dev/null and b/Res/Souce/IMG_20260616_101957.jpg differ diff --git a/Res/Souce/IMG_20260616_102014.jpg b/Res/Souce/IMG_20260616_102014.jpg new file mode 100644 index 0000000..2f4ac83 Binary files /dev/null and b/Res/Souce/IMG_20260616_102014.jpg differ diff --git a/Res/Souce/IMG_20260616_102043.jpg b/Res/Souce/IMG_20260616_102043.jpg new file mode 100644 index 0000000..ada9973 Binary files /dev/null and b/Res/Souce/IMG_20260616_102043.jpg differ diff --git a/Res/Souce/IMG_20260616_102111.jpg b/Res/Souce/IMG_20260616_102111.jpg new file mode 100644 index 0000000..09b06aa Binary files /dev/null and b/Res/Souce/IMG_20260616_102111.jpg differ diff --git a/Res/Souce/IMG_20260616_102127.jpg b/Res/Souce/IMG_20260616_102127.jpg new file mode 100644 index 0000000..98a651b Binary files /dev/null and b/Res/Souce/IMG_20260616_102127.jpg differ diff --git a/Res/Souce/IMG_20260616_102204.jpg b/Res/Souce/IMG_20260616_102204.jpg new file mode 100644 index 0000000..97704da Binary files /dev/null and b/Res/Souce/IMG_20260616_102204.jpg differ diff --git a/Res/Souce/IMG_20260616_102214.jpg b/Res/Souce/IMG_20260616_102214.jpg new file mode 100644 index 0000000..c4cbeb7 Binary files /dev/null and b/Res/Souce/IMG_20260616_102214.jpg differ diff --git a/Res/Souce/IMG_20260616_102249.jpg b/Res/Souce/IMG_20260616_102249.jpg new file mode 100644 index 0000000..e6be3ba Binary files /dev/null and b/Res/Souce/IMG_20260616_102249.jpg differ diff --git a/Res/Souce/IMG_20260616_102308.jpg b/Res/Souce/IMG_20260616_102308.jpg new file mode 100644 index 0000000..5e709db Binary files /dev/null and b/Res/Souce/IMG_20260616_102308.jpg differ diff --git a/Res/control/Acrylic.png b/Res/control/Acrylic.png new file mode 100644 index 0000000..73c1328 Binary files /dev/null and b/Res/control/Acrylic.png differ diff --git a/Res/control/AnimatedIcon.png b/Res/control/AnimatedIcon.png new file mode 100644 index 0000000..9311db4 Binary files /dev/null and b/Res/control/AnimatedIcon.png differ diff --git a/Res/control/AnimatedVisualPlayer.png b/Res/control/AnimatedVisualPlayer.png new file mode 100644 index 0000000..8035660 Binary files /dev/null and b/Res/control/AnimatedVisualPlayer.png differ diff --git a/Res/control/AnimationInterop.png b/Res/control/AnimationInterop.png new file mode 100644 index 0000000..6b385c5 Binary files /dev/null and b/Res/control/AnimationInterop.png differ diff --git a/Res/control/AppBarButton.png b/Res/control/AppBarButton.png new file mode 100644 index 0000000..1bd23eb Binary files /dev/null and b/Res/control/AppBarButton.png differ diff --git a/Res/control/AppBarSeparator.png b/Res/control/AppBarSeparator.png new file mode 100644 index 0000000..b14b397 Binary files /dev/null and b/Res/control/AppBarSeparator.png differ diff --git a/Res/control/AppBarToggleButton.png b/Res/control/AppBarToggleButton.png new file mode 100644 index 0000000..8eaaf8d Binary files /dev/null and b/Res/control/AppBarToggleButton.png differ diff --git a/Res/control/AutoSuggestBox.png b/Res/control/AutoSuggestBox.png new file mode 100644 index 0000000..5403ced Binary files /dev/null and b/Res/control/AutoSuggestBox.png differ diff --git a/Res/control/AutomationProperties.png b/Res/control/AutomationProperties.png new file mode 100644 index 0000000..7a44af3 Binary files /dev/null and b/Res/control/AutomationProperties.png differ diff --git a/Res/control/Border.png b/Res/control/Border.png new file mode 100644 index 0000000..02717a9 Binary files /dev/null and b/Res/control/Border.png differ diff --git a/Res/control/BreadcrumbBar.png b/Res/control/BreadcrumbBar.png new file mode 100644 index 0000000..38629c3 Binary files /dev/null and b/Res/control/BreadcrumbBar.png differ diff --git a/Res/control/Button.png b/Res/control/Button.png new file mode 100644 index 0000000..7408813 Binary files /dev/null and b/Res/control/Button.png differ diff --git a/Res/control/CalendarDatePicker.png b/Res/control/CalendarDatePicker.png new file mode 100644 index 0000000..b3f5f9a Binary files /dev/null and b/Res/control/CalendarDatePicker.png differ diff --git a/Res/control/CalendarView.png b/Res/control/CalendarView.png new file mode 100644 index 0000000..fe34b82 Binary files /dev/null and b/Res/control/CalendarView.png differ diff --git a/Res/control/Canvas.png b/Res/control/Canvas.png new file mode 100644 index 0000000..9c857db Binary files /dev/null and b/Res/control/Canvas.png differ diff --git a/Res/control/Checkbox.png b/Res/control/Checkbox.png new file mode 100644 index 0000000..6616d87 Binary files /dev/null and b/Res/control/Checkbox.png differ diff --git a/Res/control/Clipboard.png b/Res/control/Clipboard.png new file mode 100644 index 0000000..0e24c27 Binary files /dev/null and b/Res/control/Clipboard.png differ diff --git a/Res/control/ColorPaletteResources.png b/Res/control/ColorPaletteResources.png new file mode 100644 index 0000000..8f4e31c Binary files /dev/null and b/Res/control/ColorPaletteResources.png differ diff --git a/Res/control/ColorPicker.png b/Res/control/ColorPicker.png new file mode 100644 index 0000000..b4e7a96 Binary files /dev/null and b/Res/control/ColorPicker.png differ diff --git a/Res/control/ComboBox.png b/Res/control/ComboBox.png new file mode 100644 index 0000000..dee1fd9 Binary files /dev/null and b/Res/control/ComboBox.png differ diff --git a/Res/control/CommandBar.png b/Res/control/CommandBar.png new file mode 100644 index 0000000..e265666 Binary files /dev/null and b/Res/control/CommandBar.png differ diff --git a/Res/control/CommandBarFlyout.png b/Res/control/CommandBarFlyout.png new file mode 100644 index 0000000..415c429 Binary files /dev/null and b/Res/control/CommandBarFlyout.png differ diff --git a/Res/control/CompactSizing.png b/Res/control/CompactSizing.png new file mode 100644 index 0000000..4ae95db Binary files /dev/null and b/Res/control/CompactSizing.png differ diff --git a/Res/control/ConnectedAnimation.png b/Res/control/ConnectedAnimation.png new file mode 100644 index 0000000..ff4e159 Binary files /dev/null and b/Res/control/ConnectedAnimation.png differ diff --git a/Res/control/ContentDialog.png b/Res/control/ContentDialog.png new file mode 100644 index 0000000..0ade66e Binary files /dev/null and b/Res/control/ContentDialog.png differ diff --git a/Res/control/CreateMultipleWindows.png b/Res/control/CreateMultipleWindows.png new file mode 100644 index 0000000..d70a55c Binary files /dev/null and b/Res/control/CreateMultipleWindows.png differ diff --git a/Res/control/DataGrid.png b/Res/control/DataGrid.png new file mode 100644 index 0000000..3b105a3 Binary files /dev/null and b/Res/control/DataGrid.png differ diff --git a/Res/control/DatePicker.png b/Res/control/DatePicker.png new file mode 100644 index 0000000..23d35b3 Binary files /dev/null and b/Res/control/DatePicker.png differ diff --git a/Res/control/DropDownButton.png b/Res/control/DropDownButton.png new file mode 100644 index 0000000..dc5ff6d Binary files /dev/null and b/Res/control/DropDownButton.png differ diff --git a/Res/control/EasingFunction.png b/Res/control/EasingFunction.png new file mode 100644 index 0000000..caeee14 Binary files /dev/null and b/Res/control/EasingFunction.png differ diff --git a/Res/control/Expander.png b/Res/control/Expander.png new file mode 100644 index 0000000..531a7f4 Binary files /dev/null and b/Res/control/Expander.png differ diff --git a/Res/control/FilePicker.png b/Res/control/FilePicker.png new file mode 100644 index 0000000..7bcbbc1 Binary files /dev/null and b/Res/control/FilePicker.png differ diff --git a/Res/control/FlipView.png b/Res/control/FlipView.png new file mode 100644 index 0000000..ee466c0 Binary files /dev/null and b/Res/control/FlipView.png differ diff --git a/Res/control/Flyout.png b/Res/control/Flyout.png new file mode 100644 index 0000000..28fd032 Binary files /dev/null and b/Res/control/Flyout.png differ diff --git a/Res/control/Grid.png b/Res/control/Grid.png new file mode 100644 index 0000000..6019cee Binary files /dev/null and b/Res/control/Grid.png differ diff --git a/Res/control/GridView.png b/Res/control/GridView.png new file mode 100644 index 0000000..3829acb Binary files /dev/null and b/Res/control/GridView.png differ diff --git a/Res/control/HyperlinkButton.png b/Res/control/HyperlinkButton.png new file mode 100644 index 0000000..51549ec Binary files /dev/null and b/Res/control/HyperlinkButton.png differ diff --git a/Res/control/IconElement.png b/Res/control/IconElement.png new file mode 100644 index 0000000..9164bff Binary files /dev/null and b/Res/control/IconElement.png differ diff --git a/Res/control/Image.png b/Res/control/Image.png new file mode 100644 index 0000000..96df4be Binary files /dev/null and b/Res/control/Image.png differ diff --git a/Res/control/ImplicitTransition.png b/Res/control/ImplicitTransition.png new file mode 100644 index 0000000..fe595e1 Binary files /dev/null and b/Res/control/ImplicitTransition.png differ diff --git a/Res/control/InfoBadge.png b/Res/control/InfoBadge.png new file mode 100644 index 0000000..21263ac Binary files /dev/null and b/Res/control/InfoBadge.png differ diff --git a/Res/control/InfoBar.png b/Res/control/InfoBar.png new file mode 100644 index 0000000..3d9f9d7 Binary files /dev/null and b/Res/control/InfoBar.png differ diff --git a/Res/control/InkCanvas.png b/Res/control/InkCanvas.png new file mode 100644 index 0000000..897b82c Binary files /dev/null and b/Res/control/InkCanvas.png differ diff --git a/Res/control/InkToolbar.png b/Res/control/InkToolbar.png new file mode 100644 index 0000000..2134e8c Binary files /dev/null and b/Res/control/InkToolbar.png differ diff --git a/Res/control/InputValidation.png b/Res/control/InputValidation.png new file mode 100644 index 0000000..d090df4 Binary files /dev/null and b/Res/control/InputValidation.png differ diff --git a/Res/control/ItemsRepeater.png b/Res/control/ItemsRepeater.png new file mode 100644 index 0000000..18dc1cd Binary files /dev/null and b/Res/control/ItemsRepeater.png differ diff --git a/Res/control/Line.png b/Res/control/Line.png new file mode 100644 index 0000000..ed3e408 Binary files /dev/null and b/Res/control/Line.png differ diff --git a/Res/control/ListBox.png b/Res/control/ListBox.png new file mode 100644 index 0000000..2d8d1a7 Binary files /dev/null and b/Res/control/ListBox.png differ diff --git a/Res/control/ListView.png b/Res/control/ListView.png new file mode 100644 index 0000000..8fc7cec Binary files /dev/null and b/Res/control/ListView.png differ diff --git a/Res/control/MediaPlayerElement.png b/Res/control/MediaPlayerElement.png new file mode 100644 index 0000000..b517c7e Binary files /dev/null and b/Res/control/MediaPlayerElement.png differ diff --git a/Res/control/MenuBar.png b/Res/control/MenuBar.png new file mode 100644 index 0000000..35553ad Binary files /dev/null and b/Res/control/MenuBar.png differ diff --git a/Res/control/MenuFlyout.png b/Res/control/MenuFlyout.png new file mode 100644 index 0000000..a501970 Binary files /dev/null and b/Res/control/MenuFlyout.png differ diff --git a/Res/control/NavigationView.png b/Res/control/NavigationView.png new file mode 100644 index 0000000..a92e069 Binary files /dev/null and b/Res/control/NavigationView.png differ diff --git a/Res/control/NumberBox.png b/Res/control/NumberBox.png new file mode 100644 index 0000000..674791c Binary files /dev/null and b/Res/control/NumberBox.png differ diff --git a/Res/control/PageTransition.png b/Res/control/PageTransition.png new file mode 100644 index 0000000..25b18ab Binary files /dev/null and b/Res/control/PageTransition.png differ diff --git a/Res/control/ParallaxView.png b/Res/control/ParallaxView.png new file mode 100644 index 0000000..07160cf Binary files /dev/null and b/Res/control/ParallaxView.png differ diff --git a/Res/control/PasswordBox.png b/Res/control/PasswordBox.png new file mode 100644 index 0000000..9b613c7 Binary files /dev/null and b/Res/control/PasswordBox.png differ diff --git a/Res/control/PersonPicture.png b/Res/control/PersonPicture.png new file mode 100644 index 0000000..23d2e64 Binary files /dev/null and b/Res/control/PersonPicture.png differ diff --git a/Res/control/PipsPager.png b/Res/control/PipsPager.png new file mode 100644 index 0000000..c07faf1 Binary files /dev/null and b/Res/control/PipsPager.png differ diff --git a/Res/control/Pivot.png b/Res/control/Pivot.png new file mode 100644 index 0000000..675ee0e Binary files /dev/null and b/Res/control/Pivot.png differ diff --git a/Res/control/ProgressBar.png b/Res/control/ProgressBar.png new file mode 100644 index 0000000..9e0e003 Binary files /dev/null and b/Res/control/ProgressBar.png differ diff --git a/Res/control/ProgressRing.png b/Res/control/ProgressRing.png new file mode 100644 index 0000000..fc365f6 Binary files /dev/null and b/Res/control/ProgressRing.png differ diff --git a/Res/control/PullToRefresh.png b/Res/control/PullToRefresh.png new file mode 100644 index 0000000..0ea571a Binary files /dev/null and b/Res/control/PullToRefresh.png differ diff --git a/Res/control/RadialGradientBrush.png b/Res/control/RadialGradientBrush.png new file mode 100644 index 0000000..047ef76 Binary files /dev/null and b/Res/control/RadialGradientBrush.png differ diff --git a/Res/control/RadioButton.png b/Res/control/RadioButton.png new file mode 100644 index 0000000..9fdaafa Binary files /dev/null and b/Res/control/RadioButton.png differ diff --git a/Res/control/RadioButtons.png b/Res/control/RadioButtons.png new file mode 100644 index 0000000..cab12c1 Binary files /dev/null and b/Res/control/RadioButtons.png differ diff --git a/Res/control/RatingControl.png b/Res/control/RatingControl.png new file mode 100644 index 0000000..30efd43 Binary files /dev/null and b/Res/control/RatingControl.png differ diff --git a/Res/control/RelativePanel.png b/Res/control/RelativePanel.png new file mode 100644 index 0000000..df1ae73 Binary files /dev/null and b/Res/control/RelativePanel.png differ diff --git a/Res/control/RepeatButton.png b/Res/control/RepeatButton.png new file mode 100644 index 0000000..eb1ad5b Binary files /dev/null and b/Res/control/RepeatButton.png differ diff --git a/Res/control/RevealFocus.png b/Res/control/RevealFocus.png new file mode 100644 index 0000000..d912778 Binary files /dev/null and b/Res/control/RevealFocus.png differ diff --git a/Res/control/RichEditBox.png b/Res/control/RichEditBox.png new file mode 100644 index 0000000..3ffe648 Binary files /dev/null and b/Res/control/RichEditBox.png differ diff --git a/Res/control/RichTextBlock.png b/Res/control/RichTextBlock.png new file mode 100644 index 0000000..34cd05e Binary files /dev/null and b/Res/control/RichTextBlock.png differ diff --git a/Res/control/ScrollViewer.png b/Res/control/ScrollViewer.png new file mode 100644 index 0000000..d54541a Binary files /dev/null and b/Res/control/ScrollViewer.png differ diff --git a/Res/control/SemanticZoom.png b/Res/control/SemanticZoom.png new file mode 100644 index 0000000..bbdbf45 Binary files /dev/null and b/Res/control/SemanticZoom.png differ diff --git a/Res/control/Shape.png b/Res/control/Shape.png new file mode 100644 index 0000000..c37f49e Binary files /dev/null and b/Res/control/Shape.png differ diff --git a/Res/control/Slider.png b/Res/control/Slider.png new file mode 100644 index 0000000..cdc6175 Binary files /dev/null and b/Res/control/Slider.png differ diff --git a/Res/control/Sound.png b/Res/control/Sound.png new file mode 100644 index 0000000..bcec53a Binary files /dev/null and b/Res/control/Sound.png differ diff --git a/Res/control/SplitButton.png b/Res/control/SplitButton.png new file mode 100644 index 0000000..1f0e141 Binary files /dev/null and b/Res/control/SplitButton.png differ diff --git a/Res/control/SplitView.png b/Res/control/SplitView.png new file mode 100644 index 0000000..73d8119 Binary files /dev/null and b/Res/control/SplitView.png differ diff --git a/Res/control/StackPanel.png b/Res/control/StackPanel.png new file mode 100644 index 0000000..f7e46a0 Binary files /dev/null and b/Res/control/StackPanel.png differ diff --git a/Res/control/StandardUICommand.png b/Res/control/StandardUICommand.png new file mode 100644 index 0000000..a22ee81 Binary files /dev/null and b/Res/control/StandardUICommand.png differ diff --git a/Res/control/SwipeControl.png b/Res/control/SwipeControl.png new file mode 100644 index 0000000..796eb65 Binary files /dev/null and b/Res/control/SwipeControl.png differ diff --git a/Res/control/TabView.png b/Res/control/TabView.png new file mode 100644 index 0000000..0364e3a Binary files /dev/null and b/Res/control/TabView.png differ diff --git a/Res/control/TeachingTip.png b/Res/control/TeachingTip.png new file mode 100644 index 0000000..2428e2a Binary files /dev/null and b/Res/control/TeachingTip.png differ diff --git a/Res/control/TextBlock.png b/Res/control/TextBlock.png new file mode 100644 index 0000000..a97c489 Binary files /dev/null and b/Res/control/TextBlock.png differ diff --git a/Res/control/TextBox.png b/Res/control/TextBox.png new file mode 100644 index 0000000..281645b Binary files /dev/null and b/Res/control/TextBox.png differ diff --git a/Res/control/ThemeTransition.png b/Res/control/ThemeTransition.png new file mode 100644 index 0000000..91a74c0 Binary files /dev/null and b/Res/control/ThemeTransition.png differ diff --git a/Res/control/TimePicker.png b/Res/control/TimePicker.png new file mode 100644 index 0000000..c63aca3 Binary files /dev/null and b/Res/control/TimePicker.png differ diff --git a/Res/control/TitleBar.png b/Res/control/TitleBar.png new file mode 100644 index 0000000..2c76819 Binary files /dev/null and b/Res/control/TitleBar.png differ diff --git a/Res/control/ToggleButton.png b/Res/control/ToggleButton.png new file mode 100644 index 0000000..52682aa Binary files /dev/null and b/Res/control/ToggleButton.png differ diff --git a/Res/control/ToggleSplitButton.png b/Res/control/ToggleSplitButton.png new file mode 100644 index 0000000..3582c10 Binary files /dev/null and b/Res/control/ToggleSplitButton.png differ diff --git a/Res/control/ToggleSwitch.png b/Res/control/ToggleSwitch.png new file mode 100644 index 0000000..a8537a2 Binary files /dev/null and b/Res/control/ToggleSwitch.png differ diff --git a/Res/control/ToolTip.png b/Res/control/ToolTip.png new file mode 100644 index 0000000..83eee5a Binary files /dev/null and b/Res/control/ToolTip.png differ diff --git a/Res/control/TreeView.png b/Res/control/TreeView.png new file mode 100644 index 0000000..0a652ba Binary files /dev/null and b/Res/control/TreeView.png differ diff --git a/Res/control/VariableSizedWrapGrid.png b/Res/control/VariableSizedWrapGrid.png new file mode 100644 index 0000000..f2f0c78 Binary files /dev/null and b/Res/control/VariableSizedWrapGrid.png differ diff --git a/Res/control/Viewbox.png b/Res/control/Viewbox.png new file mode 100644 index 0000000..8940cdf Binary files /dev/null and b/Res/control/Viewbox.png differ diff --git a/Res/control/WebView.png b/Res/control/WebView.png new file mode 100644 index 0000000..c6742a1 Binary files /dev/null and b/Res/control/WebView.png differ diff --git a/Res/control/XamlUICommand.png b/Res/control/XamlUICommand.png new file mode 100644 index 0000000..9c03d5b Binary files /dev/null and b/Res/control/XamlUICommand.png differ diff --git a/Res/卡牌背景.png b/Res/卡牌背景.png new file mode 100644 index 0000000..afdb85b Binary files /dev/null and b/Res/卡牌背景.png differ diff --git a/Res/少女的尸体.png b/Res/少女的尸体.png new file mode 100644 index 0000000..83a5cf1 Binary files /dev/null and b/Res/少女的尸体.png differ diff --git a/Res/能力一览.png b/Res/能力一览.png new file mode 100644 index 0000000..4521845 Binary files /dev/null and b/Res/能力一览.png differ diff --git a/Res/调和失败.png b/Res/调和失败.png new file mode 100644 index 0000000..a8cadce Binary files /dev/null and b/Res/调和失败.png differ diff --git a/Res/调和目标.png b/Res/调和目标.png new file mode 100644 index 0000000..debb063 Binary files /dev/null and b/Res/调和目标.png differ diff --git a/config/cards.json b/config/cards.json new file mode 100644 index 0000000..48d8e21 --- /dev/null +++ b/config/cards.json @@ -0,0 +1,203 @@ +{ + "cards": [ + { + "id": "honor_student", + "name": "优等生", + "mp": 2, + "priority": 4, + "count": 3, + "win_condition": "调和成功即可获胜", + "effect_description": "除你之外的所有玩家闭上眼睛,然后持犯人牌的玩家吐出舌头。等他们将舌头收回后所有玩家睁开眼睛。", + "effect_trigger": "play", + "needs_target": false, + "target_type": "none" + }, + { + "id": "health_committee_member", + "name": "保健委员", + "mp": 1, + "priority": 4, + "count": 2, + "win_condition": "调和成功即可获胜", + "effect_description": "你可以选择1张其他玩家已使用(正面朝上)的非保健委员的牌,将它加入自己的手牌中。", + "effect_trigger": "play", + "needs_target": true, + "target_type": "skill_zone_card" + }, + { + "id": "accomplice", + "name": "共犯", + "mp": 0, + "priority": 3, + "count": 1, + "win_condition": "犯人获胜即可获胜", + "effect_description": "将1张放在任意玩家面前质疑位置的牌转移到1名其他玩家面前。", + "effect_trigger": "play", + "needs_target": true, + "target_type": "challenge_zone_card_and_player" + }, + { + "id": "library_committee_member", + "name": "图书委员", + "mp": 1, + "priority": 4, + "count": 3, + "win_condition": "调和成功即可获胜", + "effect_description": "你可以查看放在调和位置的所有卡牌。", + "effect_trigger": "play", + "needs_target": false, + "target_type": "none" + }, + { + "id": "alien", + "name": "外星人", + "mp": -1, + "priority": 1, + "count": 1, + "win_condition": "被监禁即可获胜", + "effect_description": "当你持有这张牌时,若有人使用了优等生的特技,你可以伸出舌头,装作是犯人来回应他。正面打出时不会产生任何效果。", + "effect_trigger": "passive", + "needs_target": false, + "target_type": "none" + }, + { + "id": "young_lady", + "name": "大小姐", + "mp": 1, + "priority": 4, + "count": 3, + "win_condition": "调和成功即可获胜", + "effect_description": "随机抽取1名其他玩家的1张手牌,然后选择1张自己的手牌保持反面朝上的状态返还给他。", + "effect_trigger": "play", + "needs_target": true, + "target_type": "random_player_draw" + }, + { + "id": "student_council_president", + "name": "学生会长", + "mp": 3, + "priority": 4, + "count": 1, + "win_condition": "调和成功即可获胜", + "effect_description": "持有者成为起始玩家。正面打出时不会产生任何效果。", + "effect_trigger": "passive", + "needs_target": false, + "target_type": "none" + }, + { + "id": "go_home_club", + "name": "归宅部", + "mp": 0, + "priority": 5, + "count": 3, + "win_condition": "没有任何人获胜即可获胜", + "effect_description": "你可以将1张自己的手牌和1张放在调和位置上的牌交换。", + "effect_trigger": "play", + "needs_target": true, + "target_type": "hand_and_harmony_swap" + }, + { + "id": "infected_person", + "name": "感染者", + "mp": 0, + "priority": 2, + "count": 1, + "win_condition": "调和失败即可获胜", + "effect_description": "在你的下一个回合开始时,若这张牌仍然正面朝上摆在你面前,则可以将调和位置的1张牌加入自己的手牌中。", + "effect_trigger": "next_turn_start", + "needs_target": true, + "target_type": "harmony_zone_card" + }, + { + "id": "newspaper_club", + "name": "新闻部", + "mp": 1, + "priority": 4, + "count": 3, + "win_condition": "调和成功即可获胜", + "effect_description": "每位玩家选择1张自己的手牌,将它保持反面朝上的状态传给自己左手边的玩家。", + "effect_trigger": "play", + "needs_target": false, + "target_type": "all_players_choose" + }, + { + "id": "culprit", + "name": "犯人", + "mp": 0, + "priority": 3, + "count": 1, + "win_condition": "不被监禁即可获胜", + "effect_description": "这张牌无论是正面朝上还是反面朝上都无法被使用。但可因其他卡牌的能力效果而进行移动。", + "effect_trigger": "none", + "needs_target": false, + "target_type": "none", + "unusable": true + }, + { + "id": "class_monitor", + "name": "班长", + "mp": 2, + "priority": 4, + "count": 2, + "win_condition": "调和成功即可获胜", + "effect_description": "选择1名玩家,双方各选择1张自己的手牌,然后保持反面朝上的状态转交给对方。", + "effect_trigger": "play", + "needs_target": true, + "target_type": "player_and_mutual_exchange" + }, + { + "id": "discipline_committee_member", + "name": "风纪委员", + "mp": 1, + "priority": 4, + "count": 2, + "win_condition": "调和成功即可获胜", + "effect_description": "你可以查看一名玩家的所有手牌。", + "effect_trigger": "play", + "needs_target": true, + "target_type": "player_view_hand" + } + ], + "game_settings": [ + { + "player_count": 3, + "harmony_target": 9, + "remove_cards": [ + {"id": "library_committee_member", "count": 1}, + {"id": "accomplice", "count": 1}, + {"id": "honor_student", "count": 1}, + {"id": "discipline_committee_member", "count": 1}, + {"id": "young_lady", "count": 1}, + {"id": "newspaper_club", "count": 1}, + {"id": "go_home_club", "count": 1} + ], + "total_cards": 18, + "hand_size": 6 + }, + { + "player_count": 4, + "harmony_target": 8, + "remove_cards": [ + {"id": "library_committee_member", "count": 1} + ], + "total_cards": 24, + "hand_size": 6 + }, + { + "player_count": 5, + "harmony_target": 7, + "remove_cards": [], + "total_cards": 25, + "hand_size": 5 + }, + { + "player_count": 6, + "harmony_target": 6, + "remove_cards": [ + {"id": "library_committee_member", "count": 1} + ], + "total_cards": 24, + "hand_size": 4 + } + ] +} diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..5de4c34 --- /dev/null +++ b/main.cpp @@ -0,0 +1,21 @@ +#include + +#include "ElaApplication.h" +#include "ElaTheme.h" +#include "MainWindow.h" +#include "CardData.h" + +int main(int argc, char* argv[]) +{ + QApplication a(argc, argv); + eApp->init(); + eTheme->setThemeMode(ElaThemeType::Dark); + + CardDatabase::instance().loadFromFile( + QCoreApplication::applicationDirPath() + "/config/cards.json"); + + MainWindow w; + w.show(); + + return QApplication::exec(); +} diff --git a/resources.qrc b/resources.qrc new file mode 100644 index 0000000..e979afd --- /dev/null +++ b/resources.qrc @@ -0,0 +1,22 @@ + + + Res/卡牌背景.png + Res/少女的尸体.png + Res/调和目标.png + Res/调和失败.png + Res/能力一览.png + Res/Character/优等生.png + Res/Character/保健委员.png + Res/Character/共犯.png + Res/Character/图书委员.png + Res/Character/外星人.png + Res/Character/大小姐.png + Res/Character/学生会长.png + Res/Character/归宅部.png + Res/Character/感染者.png + Res/Character/新闻部.png + Res/Character/犯人.png + Res/Character/班长.png + Res/Character/风纪委员.png + + diff --git a/src/model/CardData.h b/src/model/CardData.h new file mode 100644 index 0000000..b568816 --- /dev/null +++ b/src/model/CardData.h @@ -0,0 +1,124 @@ +#ifndef CARDDATA_H +#define CARDDATA_H + +#include +#include +#include +#include +#include +#include +#include +#include + +struct CardDef { + QString id; + QString name; + int mp = 0; + int priority = 0; + int count = 0; + QString winCondition; + QString effectDescription; + QString effectTrigger; + bool needsTarget = false; + QString targetType; + bool unusable = false; + + static CardDef fromJson(const QJsonObject& obj) { + CardDef def; + def.id = obj["id"].toString(); + def.name = obj["name"].toString(); + def.mp = obj["mp"].toInt(); + def.priority = obj["priority"].toInt(); + def.count = obj["count"].toInt(); + def.winCondition = obj["win_condition"].toString(); + def.effectDescription = obj["effect_description"].toString(); + def.effectTrigger = obj["effect_trigger"].toString(); + def.needsTarget = obj["needs_target"].toBool(); + def.targetType = obj["target_type"].toString(); + def.unusable = obj["unusable"].toBool(false); + return def; + } +}; + +struct GameSettingDef { + int playerCount = 0; + int harmonyTarget = 0; + QVector> removeCards; + int totalCards = 0; + int handSize = 0; + + static GameSettingDef fromJson(const QJsonObject& obj) { + GameSettingDef def; + def.playerCount = obj["player_count"].toInt(); + def.harmonyTarget = obj["harmony_target"].toInt(); + def.totalCards = obj["total_cards"].toInt(); + def.handSize = obj["hand_size"].toInt(); + for (const auto& v : obj["remove_cards"].toArray()) { + auto o = v.toObject(); + def.removeCards.append({o["id"].toString(), o["count"].toInt()}); + } + return def; + } +}; + +struct CardInstance { + QString uid; + QString typeId; + bool faceUp = false; + bool selected = false; + bool enabled = true; + bool locked = false; +}; + +class CardDatabase { +public: + static CardDatabase& instance() { + static CardDatabase db; + return db; + } + + bool loadFromFile(const QString& path) { + QFile file(path); + if (!file.open(QIODevice::ReadOnly)) + return false; + auto doc = QJsonDocument::fromJson(file.readAll()); + if (doc.isNull()) + return false; + auto root = doc.object(); + for (const auto& v : root["cards"].toArray()) { + auto def = CardDef::fromJson(v.toObject()); + _cardDefs[def.id] = def; + } + for (const auto& v : root["game_settings"].toArray()) { + auto setting = GameSettingDef::fromJson(v.toObject()); + _gameSettings[setting.playerCount] = setting; + } + return true; + } + + const CardDef* getCardDef(const QString& id) const { + auto it = _cardDefs.find(id); + return it != _cardDefs.end() ? &it.value() : nullptr; + } + + QMap allCardDefs() const { return _cardDefs; } + const GameSettingDef* getGameSetting(int playerCount) const { + auto it = _gameSettings.find(playerCount); + return it != _gameSettings.end() ? &it.value() : nullptr; + } + + QPixmap getCardFrontImage(const QString& typeId) const { + return QPixmap(QString(":/images/%1").arg(typeId)); + } + + QPixmap getCardBackImage() const { + return QPixmap(":/images/card_back"); + } + +private: + CardDatabase() = default; + QMap _cardDefs; + QMap _gameSettings; +}; + +#endif // CARDDATA_H diff --git a/src/model/PlayerData.h b/src/model/PlayerData.h new file mode 100644 index 0000000..aaf211b --- /dev/null +++ b/src/model/PlayerData.h @@ -0,0 +1,20 @@ +#ifndef PLAYERDATA_H +#define PLAYERDATA_H + +#include +#include + +struct PlayerData { + QString playerId; + QString nickname; + bool isReady = false; + bool isHost = false; + bool isExited = false; + int handCardCount = 0; + QVector handCardIds; + QVector skillZoneCardIds; + QVector challengeZoneCardIds; + QString lastCardId; +}; + +#endif // PLAYERDATA_H diff --git a/src/model/RoomData.h b/src/model/RoomData.h new file mode 100644 index 0000000..cbe049d --- /dev/null +++ b/src/model/RoomData.h @@ -0,0 +1,44 @@ +#ifndef ROOMDATA_H +#define ROOMDATA_H + +#include +#include +#include "PlayerData.h" + +enum class RoomState { + Waiting, + Setting, + Playing, + Settling, + Finished +}; + +enum class GamePhase { + None, + Dealing, + WaitingForCardSelect, + WaitingForAction, + WaitingForTarget, + EffectResolving, + WaitingForResponse, + TurnEndCheck, + HarmonyJudge, + ChallengeJudge, + VictoryJudge +}; + +struct RoomData { + QString roomId; + QString roomName; + RoomState state = RoomState::Waiting; + GamePhase phase = GamePhase::None; + int maxPlayers = 6; + int harmonyTarget = 0; + bool chaosMode = false; + QString currentTurnPlayerId; + QVector players; + QVector harmonyZoneCardIds; + int turnTimeLimit = 60; +}; + +#endif // ROOMDATA_H diff --git a/src/network/NetworkManager.cpp b/src/network/NetworkManager.cpp new file mode 100644 index 0000000..9f16c97 --- /dev/null +++ b/src/network/NetworkManager.cpp @@ -0,0 +1,169 @@ +#include "NetworkManager.h" +#include +#include + +NetworkManager::NetworkManager(QObject* parent) + : QObject(parent) +{ + connect(&_socket, &QWebSocket::connected, this, &NetworkManager::onConnected); + connect(&_socket, &QWebSocket::disconnected, this, &NetworkManager::onDisconnected); + connect(&_socket, &QWebSocket::textMessageReceived, this, &NetworkManager::onTextMessageReceived); + connect(&_socket, &QWebSocket::errorOccurred, this, &NetworkManager::onError); + connect(&_socket, &QWebSocket::binaryMessageReceived, this, [this](const QByteArray& msg) { + emit binaryFrameReceived(msg); + }); + connect(&_heartbeatTimer, &QTimer::timeout, this, &NetworkManager::onHeartbeat); +} + +void NetworkManager::connectToServer(const QString& url) { + _serverUrl = url; + _socket.open(QUrl(url)); +} + +void NetworkManager::disconnect() { + _heartbeatTimer.stop(); + _socket.close(); +} + +bool NetworkManager::isConnected() const { + return _socket.state() == QAbstractSocket::ConnectedState; +} + +void NetworkManager::sendMessage(const QString& type, const QJsonObject& payload) { + QJsonObject msg; + msg["type"] = type; + msg["seq"] = ++_seqNum; + msg["timestamp"] = QDateTime::currentMSecsSinceEpoch(); + if (!payload.isEmpty()) + msg["payload"] = payload; + _socket.sendTextMessage(QJsonDocument(msg).toJson(QJsonDocument::Compact)); +} + +void NetworkManager::joinRoom(const QString& roomId, const QString& nickname) { + QJsonObject payload; + payload["room_id"] = roomId; + payload["nickname"] = nickname; + sendMessage("join_room", payload); +} + +void NetworkManager::createRoom(const QString& nickname, int maxPlayers) { + QJsonObject payload; + payload["nickname"] = nickname; + payload["max_players"] = maxPlayers; + sendMessage("create_room", payload); +} + +void NetworkManager::leaveRoom() { + sendMessage("leave_room"); +} + +void NetworkManager::setReady(bool ready) { + QJsonObject payload; + payload["ready"] = ready; + sendMessage("set_ready", payload); +} + +void NetworkManager::startGame() { + sendMessage("start_game"); +} + +void NetworkManager::selectCard(const QString& cardUid) { + QJsonObject payload; + payload["card_uid"] = cardUid; + sendMessage("select_card", payload); +} + +void NetworkManager::confirmAction(const QString& action, const QJsonObject& extra) { + QJsonObject payload; + payload["action"] = action; + if (!extra.isEmpty()) { + for (auto it = extra.begin(); it != extra.end(); ++it) + payload[it.key()] = it.value(); + } + sendMessage("confirm_action", payload); +} + +void NetworkManager::effectResponse(const QJsonObject& response) { + sendMessage("effect_response", response); +} + +void NetworkManager::listRooms() { + sendMessage("list_rooms"); +} + +void NetworkManager::sendChat(const QString& text) { + QJsonObject payload; + payload["text"] = text; + sendMessage("send_chat", payload); +} + +void NetworkManager::sendBinaryFrame(const QByteArray& data) { + if (_socket.state() == QAbstractSocket::ConnectedState) + _socket.sendBinaryMessage(data); +} + +void NetworkManager::onConnected() { + _heartbeatTimer.start(15000); + emit connected(); +} + +void NetworkManager::onDisconnected() { + _heartbeatTimer.stop(); + emit disconnected(); +} + +void NetworkManager::onTextMessageReceived(const QString& message) { + auto doc = QJsonDocument::fromJson(message.toUtf8()); + if (doc.isNull()) return; + handleServerMessage(doc.object()); +} + +void NetworkManager::onError(QAbstractSocket::SocketError error) { + Q_UNUSED(error) + emit connectionError(_socket.errorString()); +} + +void NetworkManager::onHeartbeat() { + sendMessage("heartbeat"); +} + +void NetworkManager::handleServerMessage(const QJsonObject& msg) { + auto type = msg["type"].toString(); + auto payload = msg["payload"].toObject(); + + if (type == "room_state") { + emit roomStateUpdated(payload); + } else if (type == "game_start") { + emit gameStarted(payload); + } else if (type == "state_snapshot") { + emit gameStateSnapshot(payload); + } else if (type == "card_move") { + emit cardMoveEvent(payload); + } else if (type == "card_flip") { + emit cardFlipEvent(payload); + } else if (type == "effect_trigger") { + emit effectTriggered(payload); + } else if (type == "request_choice") { + emit requestChoice(payload); + } else if (type == "player_exit") { + emit playerExited(payload); + } else if (type == "settlement") { + emit settlementNotify(payload); + } else if (type == "game_end") { + emit gameEnded(payload); + } else if (type == "error") { + emit errorNotify(payload); + } else if (type == "chat") { + emit chatMessage(payload); + } else if (type == "room_created") { + emit roomCreated(payload["room_id"].toString()); + } else if (type == "room_joined") { + emit roomJoined(payload); + } else if (type == "room_left") { + emit roomLeft(); + } else if (type == "room_list") { + emit roomListReceived(payload["rooms"].toArray()); + } else if (type == "settlement_result") { + emit settlementResult(payload); + } +} diff --git a/src/network/NetworkManager.h b/src/network/NetworkManager.h new file mode 100644 index 0000000..dec4a7c --- /dev/null +++ b/src/network/NetworkManager.h @@ -0,0 +1,81 @@ +#ifndef NETWORKMANAGER_H +#define NETWORKMANAGER_H + +#include +#include +#include +#include +#include +#include + +class NetworkManager : public QObject { + Q_OBJECT +public: + static NetworkManager& instance() { + static NetworkManager mgr; + return mgr; + } + + void connectToServer(const QString& url); + void disconnect(); + bool isConnected() const; + + void sendMessage(const QString& type, const QJsonObject& payload = {}); + + void joinRoom(const QString& roomId, const QString& nickname); + void createRoom(const QString& nickname, int maxPlayers); + void leaveRoom(); + void setReady(bool ready); + void startGame(); + void selectCard(const QString& cardUid); + void confirmAction(const QString& action, const QJsonObject& extra = {}); + void effectResponse(const QJsonObject& response); + void listRooms(); + void sendChat(const QString& text); + void sendBinaryFrame(const QByteArray& data); + +signals: + void connected(); + void disconnected(); + void connectionError(const QString& error); + + void roomStateUpdated(const QJsonObject& roomState); + void gameStarted(const QJsonObject& initConfig); + void gameStateSnapshot(const QJsonObject& snapshot); + void cardMoveEvent(const QJsonObject& event); + void cardFlipEvent(const QJsonObject& event); + void effectTriggered(const QJsonObject& event); + void requestChoice(const QJsonObject& request); + void playerExited(const QJsonObject& event); + void settlementNotify(const QJsonObject& event); + void gameEnded(const QJsonObject& result); + void errorNotify(const QJsonObject& error); + void chatMessage(const QJsonObject& msg); + + void roomCreated(const QString& roomId); + void roomJoined(const QJsonObject& roomInfo); + void roomLeft(); + void roomListReceived(const QJsonArray& rooms); + void settlementResult(const QJsonObject& data); + void binaryFrameReceived(const QByteArray& data); + +private slots: + void onConnected(); + void onDisconnected(); + void onTextMessageReceived(const QString& message); + void onError(QAbstractSocket::SocketError error); + void onHeartbeat(); + +private: + explicit NetworkManager(QObject* parent = nullptr); + ~NetworkManager() override = default; + + void handleServerMessage(const QJsonObject& msg); + + QWebSocket _socket; + QTimer _heartbeatTimer; + int _seqNum = 0; + QString _serverUrl; +}; + +#endif // NETWORKMANAGER_H diff --git a/src/ui/CardInfoPopup.cpp b/src/ui/CardInfoPopup.cpp new file mode 100644 index 0000000..6874ce9 --- /dev/null +++ b/src/ui/CardInfoPopup.cpp @@ -0,0 +1,113 @@ +#include "CardInfoPopup.h" +#include "CardData.h" +#include +#include +#include +#include +#include +#include + +CardInfoPopup::CardInfoPopup(QWidget* parent) : QWidget(parent) +{ + setWindowFlags(Qt::ToolTip | Qt::FramelessWindowHint); + setAttribute(Qt::WA_TranslucentBackground); + setAttribute(Qt::WA_ShowWithoutActivating); + setMinimumWidth(220); + setMaximumWidth(280); + + auto* lay = new QVBoxLayout(this); + lay->setContentsMargins(14, 10, 14, 10); + lay->setSpacing(4); + + _nameLabel = new QLabel(this); + _nameLabel->setStyleSheet("color: #ffd700; font-size: 15px; font-weight: bold; background:transparent;"); + lay->addWidget(_nameLabel); + + _mpLabel = new QLabel(this); + _mpLabel->setStyleSheet("color: #88bbff; font-size: 12px; background:transparent;"); + lay->addWidget(_mpLabel); + + _priorityLabel = new QLabel(this); + _priorityLabel->setStyleSheet("color: #aaa; font-size: 11px; background:transparent;"); + lay->addWidget(_priorityLabel); + + _winLabel = new QLabel(this); + _winLabel->setStyleSheet("color: #4ecdc4; font-size: 11px; background:transparent;"); + _winLabel->setWordWrap(true); + lay->addWidget(_winLabel); + + _effectLabel = new QLabel(this); + _effectLabel->setStyleSheet("color: #ccc; font-size: 11px; background:transparent;"); + _effectLabel->setWordWrap(true); + lay->addWidget(_effectLabel); + + _fadeAnim = new QPropertyAnimation(this, "popupOpacity", this); + _fadeAnim->setDuration(120); + connect(_fadeAnim, &QPropertyAnimation::finished, this, [this]() { + if (_opacity <= 0.01 && !_showRequested) hide(); + }); + + _hideTimer = new QTimer(this); + _hideTimer->setSingleShot(true); + _hideTimer->setInterval(80); + connect(_hideTimer, &QTimer::timeout, this, &CardInfoPopup::doFadeOut); +} + +void CardInfoPopup::showForCard(const QString& typeId, const QPoint& globalPos) { + const CardDef* def = CardDatabase::instance().getCardDef(typeId); + if (!def) { hidePopup(); return; } + + _showRequested = true; + _hideTimer->stop(); + _fadeAnim->stop(); + _currentTypeId = typeId; + + _nameLabel->setText(def->name); + _mpLabel->setText(QStringLiteral("人力值 (MP): %1").arg(def->mp)); + _priorityLabel->setText(QStringLiteral("优先顺序: %1").arg(def->priority)); + _winLabel->setText(QStringLiteral("胜利条件: %1").arg(def->winCondition)); + _effectLabel->setText(def->effectDescription); + + adjustSize(); + + QPoint pos = globalPos + QPoint(15, -height() / 2); + QScreen* screen = QApplication::screenAt(globalPos); + if (screen) { + QRect sr = screen->availableGeometry(); + if (pos.x() + width() > sr.right()) pos.setX(globalPos.x() - width() - 15); + if (pos.y() < sr.top()) pos.setY(sr.top()); + if (pos.y() + height() > sr.bottom()) pos.setY(sr.bottom() - height()); + } + move(pos); + + _opacity = 1.0; + show(); + update(); + _showRequested = false; +} + +void CardInfoPopup::hidePopup() { + _currentTypeId.clear(); + _hideTimer->start(); +} + +void CardInfoPopup::doFadeOut() { + if (!_currentTypeId.isEmpty()) return; + if (!isVisible()) return; + _fadeAnim->stop(); + _fadeAnim->setStartValue(_opacity); + _fadeAnim->setEndValue(0.0); + _fadeAnim->start(); +} + +void CardInfoPopup::paintEvent(QPaintEvent*) { + QPainter p(this); + p.setRenderHint(QPainter::Antialiasing); + p.setOpacity(_opacity); + + QPainterPath path; + path.addRoundedRect(rect().adjusted(1, 1, -1, -1), 10, 10); + p.fillPath(path, QColor(15, 15, 25, 235)); + p.setPen(QPen(QColor(255, 215, 0, 60), 1)); + p.drawPath(path); +} diff --git a/src/ui/CardInfoPopup.h b/src/ui/CardInfoPopup.h new file mode 100644 index 0000000..6a9ae21 --- /dev/null +++ b/src/ui/CardInfoPopup.h @@ -0,0 +1,39 @@ +#ifndef CARDINFOPOPUP_H +#define CARDINFOPOPUP_H + +#include +#include +#include + +class QLabel; + +class CardInfoPopup : public QWidget { + Q_OBJECT + Q_PROPERTY(qreal popupOpacity READ popupOpacity WRITE setPopupOpacity) +public: + explicit CardInfoPopup(QWidget* parent = nullptr); + void showForCard(const QString& typeId, const QPoint& globalPos); + void hidePopup(); + + qreal popupOpacity() const { return _opacity; } + void setPopupOpacity(qreal o) { _opacity = o; update(); } + +protected: + void paintEvent(QPaintEvent* event) override; + +private: + void doFadeOut(); + + QLabel* _nameLabel = nullptr; + QLabel* _mpLabel = nullptr; + QLabel* _priorityLabel = nullptr; + QLabel* _winLabel = nullptr; + QLabel* _effectLabel = nullptr; + QPropertyAnimation* _fadeAnim = nullptr; + QTimer* _hideTimer = nullptr; + qreal _opacity = 0.0; + QString _currentTypeId; + bool _showRequested = false; +}; + +#endif diff --git a/src/ui/CardWidget.cpp b/src/ui/CardWidget.cpp new file mode 100644 index 0000000..36237cb --- /dev/null +++ b/src/ui/CardWidget.cpp @@ -0,0 +1,165 @@ +#include "CardWidget.h" +#include "CardData.h" +#include +#include +#include +#include + +CardWidget::CardWidget(QWidget* parent) : QWidget(parent) +{ + setFixedSize(normalSize()); + setCursor(Qt::PointingHandCursor); + _backRaw = CardDatabase::instance().getCardBackImage(); + rebuildCache(); +} + +void CardWidget::setCardType(const QString& typeId) { + _typeId = typeId; + loadPixmap(); + rebuildCache(); + update(); +} + +void CardWidget::setFaceUp(bool faceUp) { _faceUp = faceUp; update(); } +void CardWidget::setCardSelected(bool s) { _selected = s; update(); } + +void CardWidget::setCardEnabled(bool e) { + _enabled = e; + setCursor(e ? Qt::PointingHandCursor : Qt::ArrowCursor); + update(); +} + +void CardWidget::setMini(bool m) { + _mini = m; + setFixedSize(m ? miniSize() : normalSize()); + rebuildCache(); + update(); +} + +void CardWidget::loadPixmap() { + if (!_typeId.isEmpty()) + _faceRaw = CardDatabase::instance().getCardFrontImage(_typeId); + else + _faceRaw = QPixmap(); +} + +void CardWidget::rebuildCache() { + QSize target = size(); + qreal dpr = devicePixelRatioF(); + QSize pxSize(qRound(target.width() * dpr), qRound(target.height() * dpr)); + + if (!_faceRaw.isNull()) { + _faceScaled = _faceRaw.scaled(pxSize, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); + if (_faceScaled.width() > pxSize.width() || _faceScaled.height() > pxSize.height()) { + int x = (_faceScaled.width() - pxSize.width()) / 2; + int y = (_faceScaled.height() - pxSize.height()) / 2; + _faceScaled = _faceScaled.copy(x, y, pxSize.width(), pxSize.height()); + } + _faceScaled.setDevicePixelRatio(dpr); + } else { + _faceScaled = QPixmap(); + } + + if (!_backRaw.isNull()) { + _backScaled = _backRaw.scaled(pxSize, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); + if (_backScaled.width() > pxSize.width() || _backScaled.height() > pxSize.height()) { + int x = (_backScaled.width() - pxSize.width()) / 2; + int y = (_backScaled.height() - pxSize.height()) / 2; + _backScaled = _backScaled.copy(x, y, pxSize.width(), pxSize.height()); + } + _backScaled.setDevicePixelRatio(dpr); + } else { + _backScaled = QPixmap(); + } + + _cachedSize = target; +} + +void CardWidget::paintEvent(QPaintEvent*) { + QPainter p(this); + p.setRenderHint(QPainter::Antialiasing); + p.setRenderHint(QPainter::SmoothPixmapTransform); + + QRectF cr = rect(); + if (_selected) cr.adjust(0, 0, 0, -12); + int r = _mini ? 4 : 6; + + if (_hovered && _enabled && !_mini) { + QRadialGradient glow(cr.center(), cr.width() * 0.8); + glow.setColorAt(0, QColor(120, 200, 230, 50)); + glow.setColorAt(1, Qt::transparent); + p.setPen(Qt::NoPen); + p.setBrush(glow); + p.drawRoundedRect(cr.adjusted(-6, -6, 6, 6), r + 4, r + 4); + } + + QPainterPath clip; + clip.addRoundedRect(cr, r, r); + p.setClipPath(clip); + + QPixmap& px = (_faceUp && !_faceScaled.isNull()) ? _faceScaled : _backScaled; + if (!px.isNull()) { + p.drawPixmap(cr.topLeft(), px); + } else { + QLinearGradient bg(0, 0, 0, cr.height()); + if (_faceUp) { bg.setColorAt(0, QColor("#d4c5a0")); bg.setColorAt(1, QColor("#b8a67a")); } + else { bg.setColorAt(0, QColor("#2a4a6b")); bg.setColorAt(1, QColor("#1a2f4a")); } + p.fillRect(cr, bg); + if (!_faceUp) { + p.setPen(QPen(QColor(255,255,255,20), 1)); + for (int i = -200; i < 400; i += 12) + p.drawLine(QPointF(cr.left()+i, cr.top()), QPointF(cr.left()+i+100, cr.bottom())); + } + } + p.setClipping(false); + + if (_faceUp && !_mini) { + const CardDef* def = CardDatabase::instance().getCardDef(_typeId); + if (def) { + p.setPen(Qt::NoPen); + p.setBrush(QColor(0,0,0,140)); + QRectF badge(cr.left()+3, cr.top()+3, 22, 18); + p.drawRoundedRect(badge, 3, 3); + p.setFont(QFont("Microsoft YaHei", 9, QFont::Bold)); + p.setPen(def->mp >= 0 ? QColor("#ffd700") : QColor("#ff4444")); + p.drawText(badge, Qt::AlignCenter, QString::number(def->mp)); + } + } + + QPen border; + if (_selected) border = QPen(QColor("#ffd700"), 2.5); + else if (_hovered && _enabled) border = QPen(QColor("#7ec8e3"), 2); + else border = QPen(QColor(0,0,0,60), 1); + p.setPen(border); + p.setBrush(Qt::NoBrush); + p.drawRoundedRect(cr.adjusted(0.5,0.5,-0.5,-0.5), r, r); + + if (!_enabled && !_mini) { + QPainterPath dimClip; + dimClip.addRoundedRect(cr, r, r); + p.setClipPath(dimClip); + p.fillRect(cr, QColor(0,0,0,90)); + } +} + +void CardWidget::enterEvent(QEnterEvent*) { + _hovered = true; + update(); + if (_faceUp && !_typeId.isEmpty()) + emit hovered(_typeId, true); +} + +void CardWidget::leaveEvent(QEvent*) { + _hovered = false; + update(); + emit hovered(_typeId, false); +} + +void CardWidget::mousePressEvent(QMouseEvent* e) { + if (_enabled && e->button() == Qt::LeftButton) emit clicked(_uid); + if (_faceUp && e->button() == Qt::RightButton) emit rightClicked(_typeId); +} + +void CardWidget::mouseDoubleClickEvent(QMouseEvent* e) { + if (_enabled && e->button() == Qt::LeftButton) emit doubleClicked(_uid); +} diff --git a/src/ui/CardWidget.h b/src/ui/CardWidget.h new file mode 100644 index 0000000..12318f6 --- /dev/null +++ b/src/ui/CardWidget.h @@ -0,0 +1,58 @@ +#ifndef CARDWIDGET_H +#define CARDWIDGET_H + +#include +#include + +class CardWidget : public QWidget { + Q_OBJECT +public: + explicit CardWidget(QWidget* parent = nullptr); + + void setCardType(const QString& typeId); + void setFaceUp(bool faceUp); + void setCardSelected(bool selected); + void setCardEnabled(bool enabled); + void setMini(bool mini); + void rebuildCache(); + + QString cardUid() const { return _uid; } + void setCardUid(const QString& uid) { _uid = uid; } + QString cardTypeId() const { return _typeId; } + bool isFaceUp() const { return _faceUp; } + bool isCardSelected() const { return _selected; } + + static QSize normalSize() { return {90, 130}; } + static QSize miniSize() { return {45, 65}; } + +signals: + void clicked(const QString& uid); + void rightClicked(const QString& typeId); + void doubleClicked(const QString& uid); + void hovered(const QString& typeId, bool entered); + +protected: + void paintEvent(QPaintEvent* event) override; + void enterEvent(QEnterEvent* event) override; + void leaveEvent(QEvent* event) override; + void mousePressEvent(QMouseEvent* event) override; + void mouseDoubleClickEvent(QMouseEvent* event) override; + +private: + void loadPixmap(); + + QString _uid; + QString _typeId; + bool _faceUp = false; + bool _selected = false; + bool _enabled = true; + bool _hovered = false; + bool _mini = false; + QPixmap _faceRaw; + QPixmap _backRaw; + QPixmap _faceScaled; + QPixmap _backScaled; + QSize _cachedSize; +}; + +#endif diff --git a/src/ui/ChoiceOverlay.cpp b/src/ui/ChoiceOverlay.cpp new file mode 100644 index 0000000..d7afa02 --- /dev/null +++ b/src/ui/ChoiceOverlay.cpp @@ -0,0 +1,297 @@ +#include "ChoiceOverlay.h" +#include "CardWidget.h" +#include "CardData.h" +#include "ElaPushButton.h" +#include +#include +#include +#include +#include +#include +#include + +ChoiceOverlay::ChoiceOverlay(QWidget* parent) : QWidget(parent) +{ + hide(); + _panel = new QWidget(this); + _panel->setFixedWidth(660); + _panel->setStyleSheet("QWidget#choicePanel { background: rgba(12,12,22,245); border-radius: 16px; border: 1px solid rgba(255,215,0,50); }"); + _panel->setObjectName("choicePanel"); + + auto* panelLayout = new QVBoxLayout(_panel); + panelLayout->setContentsMargins(24, 18, 24, 18); + panelLayout->setSpacing(12); + + _titleLabel = new QLabel(_panel); + _titleLabel->setStyleSheet("color: #ffd700; font-size: 16px; font-weight: bold; background:transparent;"); + _titleLabel->setWordWrap(true); + _titleLabel->setAlignment(Qt::AlignCenter); + panelLayout->addWidget(_titleLabel); + + _contentLayout = new QVBoxLayout(); + _contentLayout->setSpacing(10); + panelLayout->addLayout(_contentLayout); +} + +void ChoiceOverlay::showChoice(const QString& choiceType, const QJsonObject& data) { + _choiceType = choiceType; + clearContent(); + _titleLabel->setText(data["message"].toString()); + + if (choiceType == "view_cards") + buildViewCards(data); + else if (choiceType == "select_player") + buildSelectPlayer(data); + else if (choiceType == "select_card_from_hand" || choiceType == "all_select_card") + buildSelectCard(data); + else if (choiceType == "select_skill_card" || choiceType == "select_challenge_card" || choiceType == "select_harmony_card") + buildSelectZoneCard(data, choiceType); + else if (choiceType == "yes_no") + buildYesNo(data); + else if (choiceType == "select_option") + buildSelectOption(data); + + _panel->setMaximumHeight(height() - 40); + _panel->adjustSize(); + show(); raise(); + _panel->move((width() - _panel->width()) / 2, qMax(20, (height() - _panel->height()) / 2)); +} + +void ChoiceOverlay::hideChoice() { clearContent(); hide(); } + +void ChoiceOverlay::clearContent() { + for (auto* w : _dynamicWidgets) { _contentLayout->removeWidget(w); w->deleteLater(); } + _dynamicWidgets.clear(); +} + +void ChoiceOverlay::paintEvent(QPaintEvent*) { + QPainter p(this); + p.fillRect(rect(), QColor(0, 0, 0, 170)); +} + +void ChoiceOverlay::mousePressEvent(QMouseEvent* e) { e->accept(); } + +QWidget* ChoiceOverlay::makeCardColumn(const QString& uid, const QString& typeId, const QString& name, bool faceUp, bool clickable) { + auto* col = new QWidget(); + col->setStyleSheet("background:transparent;"); + auto* vlay = new QVBoxLayout(col); + vlay->setContentsMargins(0, 0, 0, 0); + vlay->setSpacing(4); + vlay->setAlignment(Qt::AlignCenter); + + auto* cw = new CardWidget(col); + cw->setCardUid(uid); + if (!typeId.isEmpty()) cw->setCardType(typeId); + cw->setFaceUp(faceUp); + cw->setCardEnabled(clickable); + vlay->addWidget(cw, 0, Qt::AlignCenter); + + if (!name.isEmpty()) { + auto* lbl = new QLabel(name, col); + lbl->setStyleSheet("color:#ccc; font-size:11px; background:transparent;"); + lbl->setAlignment(Qt::AlignCenter); + lbl->setFixedWidth(CardWidget::normalSize().width()); + lbl->setWordWrap(true); + vlay->addWidget(lbl, 0, Qt::AlignCenter); + } + + if (clickable) { + connect(cw, &CardWidget::clicked, this, [this, uid, typeId]() { + QJsonObject resp; + resp["card_uid"] = uid; + emit responded(resp); + hideChoice(); + }); + } + return col; +} + +void ChoiceOverlay::buildViewCards(const QJsonObject& data) { + auto cards = data["cards"].toArray(); + + auto* scroll = new QScrollArea(); + scroll->setWidgetResizable(true); + scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + scroll->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + scroll->setMinimumHeight(180); + scroll->setMaximumHeight(350); + scroll->setStyleSheet("QScrollArea{background:transparent;border:none;}" + "QScrollBar{width:4px;height:4px;background:transparent;}" + "QScrollBar::handle{background:rgba(255,255,255,30);border-radius:2px;}"); + + auto* strip = new QWidget(); + strip->setStyleSheet("background:transparent;"); + auto* hlay = new QHBoxLayout(strip); + hlay->setAlignment(Qt::AlignCenter); + hlay->setSpacing(12); + + for (const auto& cv : cards) { + auto c = cv.toObject(); + auto name = c["name"].toString(); + if (name.isEmpty()) { + const CardDef* def = CardDatabase::instance().getCardDef(c["type_id"].toString()); + if (def) name = def->name + QStringLiteral(" (MP:%1)").arg(def->mp); + } else { + name += QStringLiteral(" (MP:%1)").arg(c["mp"].toInt()); + } + hlay->addWidget(makeCardColumn(c["uid"].toString(), c["type_id"].toString(), name, true, false)); + } + scroll->setWidget(strip); + _contentLayout->addWidget(scroll); + _dynamicWidgets.append(scroll); + + auto* btn = new ElaPushButton(QStringLiteral("确认"), _panel); + btn->setFixedSize(120, 36); + connect(btn, &ElaPushButton::clicked, this, [this]() { + QJsonObject r; r["ok"] = true; emit responded(r); hideChoice(); + }); + _contentLayout->addWidget(btn, 0, Qt::AlignCenter); + _dynamicWidgets.append(btn); +} + +void ChoiceOverlay::buildSelectPlayer(const QJsonObject& data) { + auto targets = data["targets"].toArray(); + auto* row = new QWidget(); + row->setStyleSheet("background:transparent;"); + auto* hlay = new QHBoxLayout(row); + hlay->setAlignment(Qt::AlignCenter); + hlay->setSpacing(14); + + for (const auto& tv : targets) { + auto t = tv.toObject(); + auto pid = t["player_id"].toString(); + auto nick = t["nickname"].toString(); + auto* btn = new ElaPushButton(nick, row); + btn->setFixedSize(110, 40); + connect(btn, &ElaPushButton::clicked, this, [this, pid]() { + QJsonObject r; r["player_id"] = pid; emit responded(r); hideChoice(); + }); + hlay->addWidget(btn); + } + _contentLayout->addWidget(row); + _dynamicWidgets.append(row); +} + +void ChoiceOverlay::buildSelectCard(const QJsonObject& data) { + auto cards = data["cards"].toArray(); + + auto* scroll = new QScrollArea(); + scroll->setWidgetResizable(true); + scroll->setMinimumHeight(180); + scroll->setMaximumHeight(350); + scroll->setStyleSheet("QScrollArea{background:transparent;border:none;}" + "QScrollBar{width:4px;height:4px;background:transparent;}" + "QScrollBar::handle{background:rgba(255,255,255,30);border-radius:2px;}"); + + auto* strip = new QWidget(); + strip->setStyleSheet("background:transparent;"); + auto* hlay = new QHBoxLayout(strip); + hlay->setAlignment(Qt::AlignCenter); + hlay->setSpacing(10); + + for (const auto& cv : cards) { + auto c = cv.toObject(); + const CardDef* def = CardDatabase::instance().getCardDef(c["type_id"].toString()); + QString name = def ? def->name : ""; + hlay->addWidget(makeCardColumn(c["uid"].toString(), c["type_id"].toString(), name, true, true)); + } + scroll->setWidget(strip); + _contentLayout->addWidget(scroll); + _dynamicWidgets.append(scroll); +} + +void ChoiceOverlay::buildYesNo(const QJsonObject&) { + auto* row = new QWidget(); + row->setStyleSheet("background:transparent;"); + auto* hlay = new QHBoxLayout(row); + hlay->setAlignment(Qt::AlignCenter); + hlay->setSpacing(24); + + auto* yBtn = new ElaPushButton(QStringLiteral("是"), row); + yBtn->setFixedSize(100, 40); + connect(yBtn, &ElaPushButton::clicked, this, [this]() { + QJsonObject r; r["yes"] = true; emit responded(r); hideChoice(); + }); + auto* nBtn = new ElaPushButton(QStringLiteral("否"), row); + nBtn->setFixedSize(100, 40); + connect(nBtn, &ElaPushButton::clicked, this, [this]() { + QJsonObject r; r["yes"] = false; emit responded(r); hideChoice(); + }); + hlay->addWidget(yBtn); + hlay->addWidget(nBtn); + _contentLayout->addWidget(row); + _dynamicWidgets.append(row); +} + +void ChoiceOverlay::buildSelectOption(const QJsonObject& data) { + auto options = data["options"].toArray(); + auto* row = new QWidget(); + row->setStyleSheet("background:transparent;"); + auto* hlay = new QHBoxLayout(row); + hlay->setAlignment(Qt::AlignCenter); + hlay->setSpacing(14); + + for (const auto& ov : options) { + auto o = ov.toObject(); + auto val = o["value"].toString(); + auto label = o["label"].toString(); + auto* btn = new ElaPushButton(label, row); + btn->setFixedHeight(40); + btn->setMinimumWidth(100); + connect(btn, &ElaPushButton::clicked, this, [this, val]() { + QJsonObject r; + r["value"] = val; + emit responded(r); + hideChoice(); + }); + hlay->addWidget(btn); + } + _contentLayout->addWidget(row); + _dynamicWidgets.append(row); +} + +void ChoiceOverlay::buildSelectZoneCard(const QJsonObject& data, const QString& type) { + auto cards = data["cards"].toArray(); + + auto* scroll = new QScrollArea(); + scroll->setWidgetResizable(true); + scroll->setMinimumHeight(180); + scroll->setMaximumHeight(350); + scroll->setStyleSheet("QScrollArea{background:transparent;border:none;}" + "QScrollBar{width:4px;height:4px;background:transparent;}" + "QScrollBar::handle{background:rgba(255,255,255,30);border-radius:2px;}"); + + auto* strip = new QWidget(); + strip->setStyleSheet("background:transparent;"); + auto* hlay = new QHBoxLayout(strip); + hlay->setAlignment(Qt::AlignCenter); + hlay->setSpacing(10); + + for (const auto& cv : cards) { + auto c = cv.toObject(); + auto uid = c["uid"].toString(); + auto ownerId = c["owner_id"].toString(); + auto typeId = c.contains("type_id") ? c["type_id"].toString() : QString(); + bool faceUp = !typeId.isEmpty(); + const CardDef* def = faceUp ? CardDatabase::instance().getCardDef(typeId) : nullptr; + QString name = def ? def->name : ""; + + auto* col = makeCardColumn(uid, typeId, name, faceUp, false); + auto* cw = col->findChild(); + if (cw) { + cw->setCardEnabled(true); + disconnect(cw, nullptr, this, nullptr); + connect(cw, &CardWidget::clicked, this, [this, uid, ownerId, type]() { + QJsonObject r; + r["card_uid"] = uid; + if (type == "select_challenge_card") r["owner_id"] = ownerId; + emit responded(r); + hideChoice(); + }); + } + hlay->addWidget(col); + } + scroll->setWidget(strip); + _contentLayout->addWidget(scroll); + _dynamicWidgets.append(scroll); +} diff --git a/src/ui/ChoiceOverlay.h b/src/ui/ChoiceOverlay.h new file mode 100644 index 0000000..d0900ff --- /dev/null +++ b/src/ui/ChoiceOverlay.h @@ -0,0 +1,44 @@ +#ifndef CHOICEOVERLAY_H +#define CHOICEOVERLAY_H + +#include +#include +#include + +class ElaPushButton; +class CardWidget; +class QVBoxLayout; +class QLabel; + +class ChoiceOverlay : public QWidget { + Q_OBJECT +public: + explicit ChoiceOverlay(QWidget* parent = nullptr); + void showChoice(const QString& choiceType, const QJsonObject& choiceData); + void hideChoice(); + +signals: + void responded(const QJsonObject& response); + +protected: + void paintEvent(QPaintEvent* event) override; + void mousePressEvent(QMouseEvent* event) override; + +private: + void clearContent(); + QWidget* makeCardColumn(const QString& uid, const QString& typeId, const QString& name, bool faceUp, bool clickable); + void buildViewCards(const QJsonObject& data); + void buildSelectPlayer(const QJsonObject& data); + void buildSelectCard(const QJsonObject& data); + void buildYesNo(const QJsonObject& data); + void buildSelectOption(const QJsonObject& data); + void buildSelectZoneCard(const QJsonObject& data, const QString& type); + + QWidget* _panel = nullptr; + QVBoxLayout* _contentLayout = nullptr; + QLabel* _titleLabel = nullptr; + QVector _dynamicWidgets; + QString _choiceType; +}; + +#endif diff --git a/src/ui/GameWidget.cpp b/src/ui/GameWidget.cpp new file mode 100644 index 0000000..bc5d8bb --- /dev/null +++ b/src/ui/GameWidget.cpp @@ -0,0 +1,755 @@ +#include "GameWidget.h" +#include "SceneCardItem.h" +#include "CardWidget.h" +#include "PlayerSeatWidget.h" +#include "CardInfoPopup.h" +#include "ChoiceOverlay.h" +#include "SettlementOverlay.h" +#include "CardData.h" +#include "ElaPushButton.h" +#include "ElaLineEdit.h" +#include "ElaMessageBar.h" +#include "NetworkManager.h" + +#include +#include "VoiceManager.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "VoiceSettingsDialog.h" +#include +#include +#include + +static const QColor BG_MAIN("#1a1f2e"); +static const QColor BG_PANEL("#252b3d"); +static const QColor ACCENT("#5eb3e6"); +static const QColor WARNING("#d94f5c"); +static const QColor SUCCESS("#5cb85c"); +static const QColor TEXT_PRI("#e0e0e0"); +static const QColor TEXT_SEC("#8a8f9d"); +static const QColor CARD_BORDER("#8b2635"); + +GameWidget::GameWidget(QWidget* parent) : QWidget(parent) +{ + setStyleSheet(QStringLiteral("background:%1;").arg(BG_MAIN.name())); + setFocusPolicy(Qt::StrongFocus); + initScene(); + initFloatingUI(); + + auto& net = NetworkManager::instance(); + connect(&net, &NetworkManager::gameStarted, this, &GameWidget::onGameStart); + connect(&net, &NetworkManager::gameStateSnapshot, this, &GameWidget::onSnapshot); + connect(&net, &NetworkManager::settlementResult, this, &GameWidget::showSettlement); + connect(&net, &NetworkManager::gameEnded, this, [this](const QJsonObject& r) { + addLog(QStringLiteral("游戏结束: ") + r["message"].toString()); + ElaMessageBar::information(ElaMessageBarType::Top, QStringLiteral("游戏结束"), r["message"].toString(), 5000, this); + _turnLabel->setText(QStringLiteral("游戏结束")); + _phase = Phase::Watching; + setActionsEnabled(false, false, false); + }); + connect(&net, &NetworkManager::errorNotify, this, [this](const QJsonObject& e) { + ElaMessageBar::error(ElaMessageBarType::TopRight, QStringLiteral("错误"), e["message"].toString(), 3000, this); + }); + connect(&net, &NetworkManager::chatMessage, this, [this](const QJsonObject& m) { + auto nick = m["nickname"].toString(); + auto text = m["text"].toString(); + auto* item = new QListWidgetItem(nick + ": " + text); + item->setForeground(QColor("#e0e0e0")); + _chatList->addItem(item); + _chatList->scrollToBottom(); + }); + + auto& voice = VoiceManager::instance(); + connect(&voice, &VoiceManager::audioFrame, this, [](const QByteArray& pcm) { + NetworkManager::instance().sendBinaryFrame(pcm); + }); + connect(&net, &NetworkManager::binaryFrameReceived, this, [](const QByteArray& data) { + VoiceManager::instance().processRemoteAudio(data); + }); + connect(&voice, &VoiceManager::remoteSpeaking, this, [this](const QString& pid, bool speaking) { + if (_seats.contains(pid)) + _seats[pid]->setIsCurrentTurn(speaking); + }); +} + +void GameWidget::initScene() { + _scene = new QGraphicsScene(this); + _view = new QGraphicsView(_scene, this); + _view->setRenderHint(QPainter::Antialiasing); + _view->setRenderHint(QPainter::SmoothPixmapTransform); + _view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + _view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + _view->setFrameShape(QFrame::NoFrame); + _view->setStyleSheet(QStringLiteral("background:%1;").arg(BG_MAIN.name())); + _view->setDragMode(QGraphicsView::NoDrag); + _view->setAlignment(Qt::AlignLeft | Qt::AlignTop); + + auto* layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(_view); + + QPixmap corpsePix(":/images/corpse"); + if (!corpsePix.isNull()) + _corpseItem = _scene->addPixmap(corpsePix.scaled(140, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + + _harmonyLabel = _scene->addText("", QFont("Consolas", 12, QFont::Bold)); + _harmonyLabel->setDefaultTextColor(TEXT_SEC); +} + +void GameWidget::initFloatingUI() { + _topBar = new QWidget(this); + _topBar->setStyleSheet(QStringLiteral("background:rgba(0,0,0,0.6);border-bottom:1px solid rgba(94,179,230,0.2);")); + _topBar->setFixedHeight(44); + auto* tbLay = new QHBoxLayout(_topBar); + tbLay->setContentsMargins(16, 0, 16, 0); + _turnLabel = new QLabel(QStringLiteral("等待游戏开始"), _topBar); + _turnLabel->setStyleSheet(QStringLiteral("color:%1;font-size:14px;font-weight:bold;").arg(ACCENT.name())); + _targetLabel = new QLabel(_topBar); + _targetLabel->setStyleSheet(QStringLiteral("color:%1;font-size:16px;font-weight:bold;font-family:Consolas;").arg(QColor("#d4c5a3").name())); + _targetLabel->setAlignment(Qt::AlignCenter); + tbLay->addWidget(_turnLabel); + tbLay->addStretch(); + tbLay->addWidget(_targetLabel); + tbLay->addStretch(); + auto* voiceStatus = new QLabel(QStringLiteral("🔇"), _topBar); + voiceStatus->setStyleSheet("color:#8a8f9d;font-size:16px;background:transparent;"); + voiceStatus->setToolTip(QStringLiteral("V键: 按住说话 / 按下切换")); + tbLay->addWidget(voiceStatus); + auto* voiceSettingsBtn = new ElaPushButton(QStringLiteral("⚙"), _topBar); + voiceSettingsBtn->setFixedSize(30, 30); + voiceSettingsBtn->setToolTip(QStringLiteral("语音设置")); + connect(voiceSettingsBtn, &ElaPushButton::clicked, this, [this]() { + VoiceSettingsDialog dlg(this); + dlg.exec(); + }); + tbLay->addWidget(voiceSettingsBtn); + + connect(&VoiceManager::instance(), &VoiceManager::mutedChanged, voiceStatus, [voiceStatus](bool muted) { + voiceStatus->setText(muted ? QStringLiteral("🔇") : QStringLiteral("🎤")); + voiceStatus->setStyleSheet(muted ? "color:#8a8f9d;font-size:16px;background:transparent;" : "color:#5cb85c;font-size:16px;background:transparent;"); + }); + + _actionBar = new QWidget(this); + _actionBar->setStyleSheet("background:transparent;"); + _actionBar->setFixedHeight(50); + auto* abLay = new QHBoxLayout(_actionBar); + abLay->setContentsMargins(0, 4, 0, 4); + abLay->setSpacing(14); + abLay->addStretch(); + _skillBtn = new ElaPushButton(QStringLiteral("特技"), _actionBar); + _skillBtn->setFixedSize(100, 38); + _skillBtn->setEnabled(false); + connect(_skillBtn, &ElaPushButton::clicked, this, &GameWidget::onSkill); + _harmonyBtn = new ElaPushButton(QStringLiteral("调和"), _actionBar); + _harmonyBtn->setFixedSize(100, 38); + _harmonyBtn->setEnabled(false); + connect(_harmonyBtn, &ElaPushButton::clicked, this, &GameWidget::onHarmony); + _challengeBtn = new ElaPushButton(QStringLiteral("质疑"), _actionBar); + _challengeBtn->setFixedSize(100, 38); + _challengeBtn->setEnabled(false); + connect(_challengeBtn, &ElaPushButton::clicked, this, &GameWidget::onChallenge); + abLay->addWidget(_skillBtn); + abLay->addWidget(_harmonyBtn); + abLay->addWidget(_challengeBtn); + abLay->addStretch(); + + _logPanel = new QWidget(this); + _logPanel->setStyleSheet(QStringLiteral("background:rgba(0,0,0,0.7);border-left:1px solid rgba(94,179,230,0.15);border-radius:0;")); + auto* logLay = new QVBoxLayout(_logPanel); + logLay->setContentsMargins(8, 8, 8, 8); + logLay->setSpacing(4); + auto* logT = new QLabel(QStringLiteral("游戏日志"), _logPanel); + logT->setStyleSheet(QStringLiteral("color:%1;font-size:12px;font-weight:bold;").arg(TEXT_SEC.name())); + logLay->addWidget(logT); + _logList = new QListWidget(_logPanel); + _logList->setStyleSheet( + QStringLiteral("QListWidget{background:transparent;border:none;color:%1;font-size:12px;font-family:'Consolas';}" + "QListWidget::item{padding:2px 0;}").arg(TEXT_SEC.name())); + logLay->addWidget(_logList, 1); + + _cardInfoPopup = new CardInfoPopup(this); + _choiceOverlay = new ChoiceOverlay(this); + _choiceOverlay->hide(); + connect(_choiceOverlay, &ChoiceOverlay::responded, this, &GameWidget::onEffectResponse); + + _settlementOverlay = new SettlementOverlay(this); + _settlementOverlay->hide(); + connect(_settlementOverlay, &SettlementOverlay::finished, this, [this]() { emit gameFinished(); }); + + _zoomOverlay = new QWidget(this); + _zoomOverlay->hide(); + _zoomOverlay->setStyleSheet("background:rgba(0,0,0,180);"); + _zoomCard = new CardWidget(_zoomOverlay); + _zoomCard->setFixedSize(270, 390); + _zoomCard->setCardEnabled(false); + _zoomOverlay->installEventFilter(this); + + _playAnimWidget = new QWidget(this); + _playAnimWidget->hide(); + _playAnimWidget->setStyleSheet("background:rgba(0,0,0,140);"); + auto* paLay = new QVBoxLayout(_playAnimWidget); + paLay->setAlignment(Qt::AlignCenter); + paLay->setSpacing(10); + _playAnimCard = new CardWidget(_playAnimWidget); + _playAnimCard->setFixedSize(200, 290); + _playAnimCard->setCardEnabled(false); + paLay->addWidget(_playAnimCard, 0, Qt::AlignCenter); + _playAnimLabel = new QLabel(_playAnimWidget); + _playAnimLabel->setStyleSheet(QStringLiteral("color:%1;font-size:18px;font-weight:bold;background:transparent;").arg(QColor("#d4c5a3").name())); + _playAnimLabel->setAlignment(Qt::AlignCenter); + paLay->addWidget(_playAnimLabel, 0, Qt::AlignCenter); + + _chatPanel = new QWidget(this); + _chatPanel->setStyleSheet(QStringLiteral("background:rgba(0,0,0,0.7);border-right:1px solid rgba(94,179,230,0.15);")); + auto* chatLay = new QVBoxLayout(_chatPanel); + chatLay->setContentsMargins(8, 8, 8, 8); + chatLay->setSpacing(4); + auto* chatTitle = new QLabel(QStringLiteral("聊天"), _chatPanel); + chatTitle->setStyleSheet(QStringLiteral("color:%1;font-size:12px;font-weight:bold;").arg(TEXT_SEC.name())); + chatLay->addWidget(chatTitle); + _chatList = new QListWidget(_chatPanel); + _chatList->setStyleSheet(QStringLiteral("QListWidget{background:transparent;border:none;color:%1;font-size:12px;}" + "QListWidget::item{padding:2px 0;}").arg(TEXT_PRI.name())); + _chatList->setWordWrap(true); + chatLay->addWidget(_chatList, 1); + auto* chatRow = new QHBoxLayout(); + chatRow->setSpacing(4); + _chatInput = new ElaLineEdit(_chatPanel); + _chatInput->setPlaceholderText(QStringLiteral("输入消息...")); + _chatInput->setFixedHeight(28); + connect(_chatInput, &ElaLineEdit::returnPressed, this, &GameWidget::onSendChat); + auto* sendBtn = new ElaPushButton(QStringLiteral("发送"), _chatPanel); + sendBtn->setFixedSize(50, 28); + connect(sendBtn, &ElaPushButton::clicked, this, &GameWidget::onSendChat); + chatRow->addWidget(_chatInput, 1); + chatRow->addWidget(sendBtn); + chatLay->addLayout(chatRow); + + _chatToggle = new ElaPushButton(QStringLiteral("💬"), this); + _chatToggle->setFixedSize(36, 36); + _chatToggle->setToolTip(QStringLiteral("聊天")); + connect(_chatToggle, &ElaPushButton::clicked, this, &GameWidget::toggleChat); + + _chatPanel->hide(); +} + +void GameWidget::resizeEvent(QResizeEvent* e) { + QWidget::resizeEvent(e); + layoutFloatingUI(); + layoutPlayerSeats(); + + QRect ga = gameArea(); + int cx = ga.center().x(); + + _scene->setSceneRect(0, 0, width(), height()); + + if (_corpseItem) _corpseItem->setPos(cx - 70, ga.y() + ga.height() * 0.08); + if (_harmonyLabel) _harmonyLabel->setPos(cx - 80, ga.y() + ga.height() * 0.25); + + qreal cardW = SceneCardItem::W; + int hCount = _harmonyCards.size(); + if (hCount > 0) { + qreal hSpacing = qMin(cardW + 10, (ga.width() * 0.6) / qMax(1, hCount)); + qreal hTotalW = (hCount - 1) * hSpacing + cardW; + qreal hStartX = cx - hTotalW / 2.0; + qreal hY = ga.y() + ga.height() * 0.32; + for (int i = 0; i < hCount; ++i) + _harmonyCards[i]->setPos(hStartX + i * hSpacing, hY); + } + + int handCount = _handCards.size(); + if (handCount > 0) { + qreal spacing = qMin(cardW + 10, (ga.width() * 0.8) / qMax(1, handCount)); + qreal totalW = (handCount - 1) * spacing + cardW; + qreal startX = cx - totalW / 2.0; + qreal handY = ga.y() + ga.height() * 0.72; + for (int i = 0; i < handCount; ++i) { + _handCards[i]->setPos(startX + i * spacing, handY); + _handCards[i]->setZValue(10 + i); + } + } +} + +void GameWidget::layoutFloatingUI() { + int w = width(), h = height(); + int chatW = _chatVisible ? 200 : 0; + + if (_chatVisible) { + _chatPanel->setGeometry(0, 44, 200, h - 44); + _chatPanel->show(); + _chatPanel->raise(); + } else { + _chatPanel->hide(); + } + _chatToggle->move(chatW + 4, 48); + _chatToggle->raise(); + + _topBar->setGeometry(0, 0, w, 44); + _topBar->raise(); + _actionBar->setGeometry(chatW, h - 54, w - chatW - 220, 50); + _actionBar->raise(); + _logPanel->setGeometry(w - 220, 44, 220, h - 44); + _logPanel->raise(); + _choiceOverlay->setGeometry(0, 0, w, h); + _settlementOverlay->setGeometry(0, 0, w, h); + _zoomOverlay->setGeometry(0, 0, w, h); + _playAnimWidget->setGeometry(0, 0, w, h); + _zoomCard->move((w - _zoomCard->width()) / 2, (h - _zoomCard->height()) / 2); +} + +bool GameWidget::eventFilter(QObject* obj, QEvent* event) { + if (obj == _zoomOverlay && event->type() == QEvent::MouseButtonPress) { + _zoomOverlay->hide(); + return true; + } + return QWidget::eventFilter(obj, event); +} + +// ---- Game lifecycle ---- + +void GameWidget::onGameStart(const QJsonObject& config) { + resetGame(); + _chatList->clear(); + _localPlayerId = config["your_player_id"].toString(); + _harmonyTarget = config["harmony_target"].toInt(); + _targetLabel->setText(QStringLiteral("目标值: %1").arg(_harmonyTarget)); + _turnLabel->setText(QStringLiteral("游戏开始!")); + _harmonyLabel->setPlainText(QStringLiteral("调和区 (目标: %1)").arg(_harmonyTarget)); + addLog(QStringLiteral("游戏开始 - 调和目标值 %1").arg(_harmonyTarget)); +} + +void GameWidget::onSnapshot(const QJsonObject& snap) { + _localPlayerId = snap["your_player_id"].toString(); + auto phase = snap["phase"].toString(); + auto turnPhase = snap["turn_phase"].toString(); + auto turnPlayer = snap["current_turn"].toString(); + auto players = snap["players"].toArray(); + + _playerOrder.clear(); + for (const auto& pv : players) + _playerOrder.append(pv.toObject()["player_id"].toString()); + + for (const auto& pv : players) { + auto po = pv.toObject(); + auto pid = po["player_id"].toString(); + if (pid == _localPlayerId) continue; + if (!_seats.contains(pid)) { + auto* seat = new PlayerSeatWidget(this); + connect(seat, &PlayerSeatWidget::playerClicked, this, &GameWidget::onPlayerSeatClicked); + _seats[pid] = seat; + } + _seats[pid]->setPlayerData(po); + _seats[pid]->setIsCurrentTurn(pid == turnPlayer); + } + QStringList gone; + for (auto it = _seats.begin(); it != _seats.end(); ++it) { + bool found = false; + for (const auto& pv : players) + if (pv.toObject()["player_id"].toString() == it.key() && it.key() != _localPlayerId) + found = true; + if (!found) gone.append(it.key()); + } + for (const auto& k : gone) { _seats[k]->deleteLater(); _seats.remove(k); } + layoutPlayerSeats(); + updateHandCards(snap["hand_cards"].toArray()); + updateHarmonyZone(snap["harmony_zone"].toArray()); + + if (snap.contains("effect")) { + auto eInfo = snap["effect"].toObject(); + if (eInfo["needs_response"].toBool()) { + auto ct = eInfo["choice_type"].toString(); + if (ct == "all_select_card" || ct == "select_card_from_hand") { + auto cd = eInfo["choice_data"].toObject(); + if (!cd.contains("cards")) { cd["cards"] = snap["hand_cards"]; eInfo["choice_data"] = cd; } + } + } + handleEffectState(eInfo); + return; + } + _choiceOverlay->hideChoice(); + + bool myTurn = (turnPlayer == _localPlayerId); + if (phase == "all_exited") { + _turnLabel->setText(QStringLiteral("所有人已退出 — 等待结算")); + _phase = Phase::Watching; + setActionsEnabled(false, false, false); + return; + } + if (!myTurn) { + QString nick; + for (const auto& pv : players) + if (pv.toObject()["player_id"].toString() == turnPlayer) + nick = pv.toObject()["nickname"].toString(); + _turnLabel->setText(QStringLiteral("等待 %1 行动...").arg(nick)); + _turnLabel->setStyleSheet(QStringLiteral("color:%1;font-size:14px;").arg(TEXT_SEC.name())); + _phase = Phase::Idle; + setActionsEnabled(false, false, false); + return; + } + + _turnLabel->setStyleSheet(QStringLiteral("color:%1;font-size:14px;font-weight:bold;").arg(ACCENT.name())); + if (turnPhase == "select_card") { + _turnLabel->setText(QStringLiteral("你的回合 — 选择手牌")); + _phase = Phase::SelectCard; + for (auto* c : _handCards) { + const CardDef* def = CardDatabase::instance().getCardDef(c->typeId()); + c->setCardEnabled(!(def && def->unusable)); + } + setActionsEnabled(false, false, false); + } else if (turnPhase == "select_action") { + _turnLabel->setText(QStringLiteral("选择行动方式")); + _phase = Phase::SelectAction; + } else if (turnPhase == "select_target") { + _turnLabel->setText(QStringLiteral("选择质疑目标")); + _phase = Phase::SelectTarget; + } + + for (const auto& pv : players) { + if (pv.toObject()["player_id"].toString() == _localPlayerId && pv.toObject()["is_exited"].toBool()) { + _turnLabel->setText(QStringLiteral("你已暂时退出")); + _turnLabel->setStyleSheet(QStringLiteral("color:%1;font-size:14px;").arg(TEXT_SEC.name())); + _phase = Phase::Watching; + setActionsEnabled(false, false, false); + } + } +} + +void GameWidget::resetGame() { + VoiceManager::instance().stop(); + for (auto* c : _handCards) { _scene->removeItem(c); delete c; } + _handCards.clear(); + for (auto* c : _harmonyCards) { _scene->removeItem(c); delete c; } + _harmonyCards.clear(); + for (auto* s : _seats) s->deleteLater(); + _seats.clear(); + _playerOrder.clear(); + _selectedCardUid.clear(); + _phase = Phase::Idle; + _logList->clear(); + setActionsEnabled(false, false, false); + _turnLabel->setText(QStringLiteral("等待游戏开始")); + _targetLabel->clear(); + _harmonyLabel->setPlainText(""); + _choiceOverlay->hideChoice(); +} + +// ---- Scene layout ---- + +void GameWidget::layoutPlayerSeats() { + QVector others; + for (const auto& pid : _playerOrder) + if (pid != _localPlayerId && _seats.contains(pid)) + others.append(pid); + int n = others.size(); + if (n == 0) return; + + QRect ga = gameArea(); + int gx = ga.x(), gy = ga.y(), gw = ga.width(), gh = ga.height(); + int pw = 180, ph = 130; + + struct Pos { int x, y; }; + QVector positions; + + if (n == 1) { + positions = {{gx + gw / 2 - pw / 2, gy}}; + } else if (n == 2) { + positions = {{gx + gw / 4 - pw / 2, gy}, {gx + 3 * gw / 4 - pw / 2, gy}}; + } else if (n == 3) { + positions = {{gx, gy + gh / 2 - ph / 2}, {gx + gw / 2 - pw / 2, gy}, {gx + gw - pw, gy + gh / 2 - ph / 2}}; + } else if (n == 4) { + positions = {{gx, gy + gh / 2 - ph / 2}, {gx + gw / 3 - pw / 2, gy}, {gx + 2 * gw / 3 - pw / 2, gy}, {gx + gw - pw, gy + gh / 2 - ph / 2}}; + } else { + positions.append({gx, gy + gh / 2 - ph / 2}); + int topCount = n - 2; + for (int i = 0; i < topCount; ++i) + positions.append({gx + int((i + 1) * double(gw) / (topCount + 1) - pw / 2), gy}); + positions.append({gx + gw - pw, gy + gh / 2 - ph / 2}); + } + + for (int i = 0; i < n && i < positions.size(); ++i) { + _seats[others[i]]->setGeometry(positions[i].x, positions[i].y, pw, ph); + _seats[others[i]]->show(); + _seats[others[i]]->raise(); + } +} + +void GameWidget::updateHandCards(const QJsonArray& cards) { + for (auto* c : _handCards) { _scene->removeItem(c); delete c; } + _handCards.clear(); + _selectedCardUid.clear(); + setActionsEnabled(false, false, false); + + QRect ga = gameArea(); + int cx = ga.center().x(); + int count = cards.size(); + qreal cardW = SceneCardItem::W; + qreal spacing = qMin(cardW + 10, (ga.width() * 0.8) / qMax(1, count)); + qreal totalW = (count - 1) * spacing + cardW; + qreal startX = cx - totalW / 2.0; + qreal handY = ga.y() + ga.height() * 0.72; + + for (int i = 0; i < count; ++i) { + auto c = cards[i].toObject(); + auto* item = new SceneCardItem(); + item->setCardData(c["uid"].toString(), c["type_id"].toString(), true); + item->setPos(startX + i * spacing, handY); + item->setZValue(10 + i); + + const CardDef* def = CardDatabase::instance().getCardDef(c["type_id"].toString()); + item->setCardEnabled(_phase == Phase::SelectCard && !(def && def->unusable)); + + connect(item, &SceneCardItem::clicked, this, &GameWidget::onSceneCardClicked); + connect(item, &SceneCardItem::hoverIn, this, &GameWidget::onSceneCardHover); + connect(item, &SceneCardItem::hoverOut, this, &GameWidget::onSceneCardHoverOut); + connect(item, &SceneCardItem::rightClicked, this, &GameWidget::showCardZoom); + _scene->addItem(item); + _handCards.append(item); + } +} + +void GameWidget::updateHarmonyZone(const QJsonArray& cards) { + for (auto* c : _harmonyCards) { _scene->removeItem(c); delete c; } + _harmonyCards.clear(); + _harmonyLabel->setPlainText(QStringLiteral("调和区 (%1张 / 目标: %2)").arg(cards.size()).arg(_harmonyTarget)); + + QRect ga = gameArea(); + int cx = ga.center().x(); + int count = cards.size(); + qreal cardW = SceneCardItem::W; + qreal spacing = qMin(cardW + 10, (ga.width() * 0.6) / qMax(1, count)); + qreal totalW = (count - 1) * spacing + cardW; + qreal startX = cx - totalW / 2.0; + qreal hY = ga.y() + ga.height() * 0.32; + + for (int i = 0; i < count; ++i) { + auto c = cards[i].toObject(); + auto* item = new SceneCardItem(); + item->setCardData(c["uid"].toString(), c.contains("type_id") ? c["type_id"].toString() : "", c["face_up"].toBool(false)); + item->setPos(startX + i * spacing, hY); + item->setScale(0.8); + item->setCardEnabled(false); + item->setZValue(5); + _scene->addItem(item); + _harmonyCards.append(item); + } +} + +// ---- Interaction handlers ---- + +void GameWidget::onSceneCardClicked(const QString& uid) { + if (_phase != Phase::SelectCard) return; + bool wasSelected = false; + for (auto* c : _handCards) { + if (c->uid() == uid) { + wasSelected = c->isCardSelected(); + c->setCardSelected(!wasSelected); + } else { + c->setCardSelected(false); + } + } + if (wasSelected) { _selectedCardUid.clear(); setActionsEnabled(false, false, false); } + else { _selectedCardUid = uid; setActionsEnabled(true, true, true); } +} + +void GameWidget::onSceneCardHover(const QString& typeId) { + auto* item = qobject_cast(sender()); + if (item) { + QPointF scenePos = item->mapToScene(SceneCardItem::W, 0); + QPoint viewPos = _view->mapFromScene(scenePos); + QPoint globalPos = _view->viewport()->mapToGlobal(viewPos); + _cardInfoPopup->showForCard(typeId, globalPos); + } +} + +void GameWidget::onSceneCardHoverOut() { + _cardInfoPopup->hidePopup(); +} + +void GameWidget::onSkill() { + if (_selectedCardUid.isEmpty()) return; + QString typeId; + for (auto* c : _handCards) if (c->uid() == _selectedCardUid) typeId = c->typeId(); + const CardDef* def = CardDatabase::instance().getCardDef(typeId); + showPlayAnimation(typeId, QStringLiteral("特技: ") + (def ? def->name : typeId)); + NetworkManager::instance().selectCard(_selectedCardUid); + NetworkManager::instance().confirmAction("skill"); + _phase = Phase::WaitResponse; + setActionsEnabled(false, false, false); + addLog(QStringLiteral("使用特技: ") + (def ? def->name : typeId)); +} + +void GameWidget::onHarmony() { + if (_selectedCardUid.isEmpty()) return; + QString typeId; + for (auto* c : _handCards) if (c->uid() == _selectedCardUid) typeId = c->typeId(); + showPlayAnimation(typeId, QStringLiteral("调和")); + NetworkManager::instance().selectCard(_selectedCardUid); + NetworkManager::instance().confirmAction("harmony"); + _phase = Phase::WaitResponse; + setActionsEnabled(false, false, false); + addLog(QStringLiteral("放入调和区")); +} + +void GameWidget::onChallenge() { + if (_selectedCardUid.isEmpty()) return; + _phase = Phase::SelectTarget; + _turnLabel->setText(QStringLiteral("点击目标玩家")); + _turnLabel->setStyleSheet(QStringLiteral("color:%1;font-size:14px;font-weight:bold;").arg(WARNING.name())); + setActionsEnabled(false, false, false); + for (auto* s : _seats) s->setCursor(Qt::PointingHandCursor); + addLog(QStringLiteral("选择质疑目标...")); +} + +void GameWidget::onPlayerSeatClicked(const QString& playerId) { + if (_phase != Phase::SelectTarget || _selectedCardUid.isEmpty()) return; + QString typeId; + for (auto* c : _handCards) if (c->uid() == _selectedCardUid) typeId = c->typeId(); + showPlayAnimation(typeId, QStringLiteral("质疑")); + NetworkManager::instance().selectCard(_selectedCardUid); + QJsonObject extra; + extra["target_player_id"] = playerId; + NetworkManager::instance().confirmAction("challenge", extra); + _phase = Phase::WaitResponse; + for (auto* s : _seats) s->setCursor(Qt::ArrowCursor); + addLog(QStringLiteral("质疑 ") + playerId); +} + +// ---- Effect / Settlement / Overlays ---- + +void GameWidget::handleEffectState(const QJsonObject& info) { + if (info["needs_response"].toBool()) { + auto ct = info["choice_type"].toString(); + auto cd = info["choice_data"].toObject(); + addLog(QStringLiteral("效果: ") + info["card_type"].toString() + " - " + cd["message"].toString()); + _choiceOverlay->showChoice(ct, cd); + _turnLabel->setText(QStringLiteral("效果结算 — 请做出选择")); + _turnLabel->setStyleSheet(QStringLiteral("color:%1;font-size:14px;font-weight:bold;").arg(QColor("#d4c5a3").name())); + } else { + _turnLabel->setText(info["waiting_message"].toString()); + _turnLabel->setStyleSheet(QStringLiteral("color:%1;font-size:14px;").arg(TEXT_SEC.name())); + addLog(QStringLiteral("效果结算中: ") + info["card_type"].toString()); + } +} + +void GameWidget::onEffectResponse(const QJsonObject& resp) { + NetworkManager::instance().effectResponse(resp); + _turnLabel->setText(QStringLiteral("等待效果结算...")); +} + +void GameWidget::showSettlement(const QJsonObject& data) { + _phase = Phase::Watching; + setActionsEnabled(false, false, false); + _turnLabel->setText(QStringLiteral("结算中...")); + addLog(QStringLiteral("进入结算阶段")); + _settlementOverlay->showSettlement(data); +} + +void GameWidget::showCardZoom(const QString& typeId) { + if (typeId.isEmpty()) return; + _zoomCard->setCardType(typeId); + _zoomCard->setFaceUp(true); + _zoomCard->rebuildCache(); + _zoomOverlay->setGeometry(rect()); + _zoomCard->move((width() - _zoomCard->width()) / 2, (height() - _zoomCard->height()) / 2); + _zoomOverlay->show(); + _zoomOverlay->raise(); +} + +void GameWidget::showPlayAnimation(const QString& typeId, const QString& actionName) { + _playAnimCard->setCardType(typeId); + _playAnimCard->setFaceUp(true); + _playAnimCard->rebuildCache(); + _playAnimLabel->setText(actionName); + _playAnimWidget->setGeometry(rect()); + _playAnimWidget->show(); + _playAnimWidget->raise(); + QTimer::singleShot(1200, this, [this]() { _playAnimWidget->hide(); }); +} + +void GameWidget::animateCardToPos(SceneCardItem* card, const QPointF& target, int duration) { + auto* anim = new QPropertyAnimation(card, "pos", this); + anim->setDuration(duration); + anim->setStartValue(card->pos()); + anim->setEndValue(target); + anim->setEasingCurve(QEasingCurve::OutCubic); + anim->start(QAbstractAnimation::DeleteWhenStopped); +} + +// ---- Helpers ---- + +void GameWidget::setActionsEnabled(bool s, bool h, bool c) { + _skillBtn->setEnabled(s); + _harmonyBtn->setEnabled(h); + _challengeBtn->setEnabled(c); +} + +void GameWidget::addLog(const QString& text) { + auto* item = new QListWidgetItem("[" + QDateTime::currentDateTime().toString("hh:mm:ss") + "] " + text); + item->setForeground(text.contains(QStringLiteral("效果")) ? ACCENT : TEXT_SEC); + _logList->addItem(item); + _logList->scrollToBottom(); +} + +QRect GameWidget::gameArea() const { + int chatW = _chatVisible ? 200 : 0; + return {chatW, 44, width() - chatW - 220, height() - 44 - 54}; +} + +void GameWidget::onSendChat() { + auto text = _chatInput->text().trimmed(); + if (text.isEmpty()) return; + NetworkManager::instance().sendChat(text); + _chatInput->clear(); +} + +void GameWidget::toggleChat() { + _chatVisible = !_chatVisible; + layoutFloatingUI(); + layoutPlayerSeats(); + + QRect ga = gameArea(); + int cx = ga.center().x(); + _scene->setSceneRect(0, 0, width(), height()); + if (_corpseItem) _corpseItem->setPos(cx - 70, ga.y() + ga.height() * 0.08); + if (_harmonyLabel) _harmonyLabel->setPos(cx - 80, ga.y() + ga.height() * 0.25); + + qreal cardW = SceneCardItem::W; + int hc = _harmonyCards.size(); + if (hc > 0) { + qreal sp = qMin(cardW + 10, (ga.width() * 0.6) / qMax(1, hc)); + qreal tw = (hc - 1) * sp + cardW; + for (int i = 0; i < hc; ++i) _harmonyCards[i]->setPos(cx - tw / 2.0 + i * sp, ga.y() + ga.height() * 0.32); + } + int nc = _handCards.size(); + if (nc > 0) { + qreal sp = qMin(cardW + 10, (ga.width() * 0.8) / qMax(1, nc)); + qreal tw = (nc - 1) * sp + cardW; + for (int i = 0; i < nc; ++i) { _handCards[i]->setPos(cx - tw / 2.0 + i * sp, ga.y() + ga.height() * 0.72); _handCards[i]->setZValue(10 + i); } + } +} + +void GameWidget::keyPressEvent(QKeyEvent* e) { + if (e->key() == Qt::Key_V && !e->isAutoRepeat()) { + if (_chatInput && _chatInput->hasFocus()) { QWidget::keyPressEvent(e); return; } + VoiceManager::instance().keyDown(); + return; + } + QWidget::keyPressEvent(e); +} + +void GameWidget::keyReleaseEvent(QKeyEvent* e) { + if (e->key() == Qt::Key_V && !e->isAutoRepeat()) { + if (_chatInput && _chatInput->hasFocus()) { QWidget::keyReleaseEvent(e); return; } + VoiceManager::instance().keyUp(); + return; + } + QWidget::keyReleaseEvent(e); +} diff --git a/src/ui/GameWidget.h b/src/ui/GameWidget.h new file mode 100644 index 0000000..5b41855 --- /dev/null +++ b/src/ui/GameWidget.h @@ -0,0 +1,113 @@ +#ifndef GAMEWIDGET_H +#define GAMEWIDGET_H + +#include +#include +#include +#include +#include +#include + +class SceneCardItem; +class CardWidget; +class CardInfoPopup; +class ChoiceOverlay; +class SettlementOverlay; +class PlayerSeatWidget; +class ElaPushButton; +class QLabel; +class QListWidget; +class QGraphicsPixmapItem; +class QGraphicsTextItem; +class ElaLineEdit; + +class GameWidget : public QWidget { + Q_OBJECT +public: + explicit GameWidget(QWidget* parent = nullptr); + + void onGameStart(const QJsonObject& config); + void onSnapshot(const QJsonObject& snapshot); + void resetGame(); + +signals: + void gameFinished(); + +protected: + void resizeEvent(QResizeEvent* event) override; + bool eventFilter(QObject* obj, QEvent* event) override; + void keyPressEvent(QKeyEvent* event) override; + void keyReleaseEvent(QKeyEvent* event) override; + +private: + void initScene(); + void initFloatingUI(); + void layoutFloatingUI(); + void layoutPlayerSeats(); + void updateHandCards(const QJsonArray& cards); + void updateHarmonyZone(const QJsonArray& cards); + void addLog(const QString& text); + void setActionsEnabled(bool s, bool h, bool c); + + void onSceneCardClicked(const QString& uid); + void onSceneCardHover(const QString& typeId); + void onSceneCardHoverOut(); + void onSkill(); + void onHarmony(); + void onChallenge(); + void onPlayerSeatClicked(const QString& playerId); + void onEffectResponse(const QJsonObject& resp); + void handleEffectState(const QJsonObject& info); + void showSettlement(const QJsonObject& data); + void showCardZoom(const QString& typeId); + void showPlayAnimation(const QString& typeId, const QString& actionName); + void animateCardToPos(SceneCardItem* card, const QPointF& target, int duration = 400); + void onSendChat(); + void toggleChat(); + QRect gameArea() const; + + enum class Phase { Idle, SelectCard, SelectAction, SelectTarget, WaitResponse, Watching }; + Phase _phase = Phase::Idle; + QString _localPlayerId; + QString _selectedCardUid; + int _harmonyTarget = 0; + + QGraphicsScene* _scene = nullptr; + QGraphicsView* _view = nullptr; + QGraphicsPixmapItem* _corpseItem = nullptr; + QGraphicsTextItem* _harmonyLabel = nullptr; + + QVector _handCards; + QVector _harmonyCards; + QMap _seats; + QVector _playerOrder; + + QWidget* _topBar = nullptr; + QLabel* _turnLabel = nullptr; + QLabel* _targetLabel = nullptr; + + QWidget* _actionBar = nullptr; + ElaPushButton* _skillBtn = nullptr; + ElaPushButton* _harmonyBtn = nullptr; + ElaPushButton* _challengeBtn = nullptr; + + QWidget* _logPanel = nullptr; + QListWidget* _logList = nullptr; + + QWidget* _chatPanel = nullptr; + QListWidget* _chatList = nullptr; + ElaLineEdit* _chatInput = nullptr; + ElaPushButton* _chatToggle = nullptr; + bool _chatVisible = false; + + CardInfoPopup* _cardInfoPopup = nullptr; + ChoiceOverlay* _choiceOverlay = nullptr; + SettlementOverlay* _settlementOverlay = nullptr; + QWidget* _zoomOverlay = nullptr; + CardWidget* _zoomCard = nullptr; + QWidget* _playAnimWidget = nullptr; + CardWidget* _playAnimCard = nullptr; + QLabel* _playAnimLabel = nullptr; +}; + +#endif diff --git a/src/ui/LobbyWidget.cpp b/src/ui/LobbyWidget.cpp new file mode 100644 index 0000000..bc78348 --- /dev/null +++ b/src/ui/LobbyWidget.cpp @@ -0,0 +1,297 @@ +#include "LobbyWidget.h" +#include "CardWidget.h" +#include "CardData.h" +#include "ElaLineEdit.h" +#include "ElaPushButton.h" +#include "ElaSpinBox.h" +#include "ElaMessageBar.h" +#include "NetworkManager.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +LobbyWidget::LobbyWidget(QWidget* parent) : QWidget(parent) +{ + initUI(); + + auto& net = NetworkManager::instance(); + connect(&net, &NetworkManager::connected, this, [this]() { + _statusLabel->setText(QStringLiteral("● 已连接到服务器")); + _statusLabel->setStyleSheet("color: #4ecdc4; font-size: 13px; background:transparent;"); + _connectBtn->setText(QStringLiteral("断开")); + _connectBtn->setEnabled(true); + _createBtn->setEnabled(true); + _joinBtn->setEnabled(true); + ElaMessageBar::success(ElaMessageBarType::TopRight, QStringLiteral("连接成功"), QStringLiteral("已连接,可以创建或加入房间"), 2000, this); + }); + connect(&net, &NetworkManager::disconnected, this, [this]() { + _statusLabel->setText(QStringLiteral("○ 未连接")); + _statusLabel->setStyleSheet("color: #888; font-size: 13px; background:transparent;"); + _connectBtn->setText(QStringLiteral("连接")); + _connectBtn->setEnabled(true); + _createBtn->setEnabled(false); + _joinBtn->setEnabled(false); + }); + connect(&net, &NetworkManager::connectionError, this, [this](const QString& err) { + _connectBtn->setEnabled(true); + _connectBtn->setText(QStringLiteral("连接")); + ElaMessageBar::error(ElaMessageBarType::TopRight, QStringLiteral("连接失败"), err, 3000, this); + }); + connect(&net, &NetworkManager::roomCreated, this, [this](const QString& roomId) { + ElaMessageBar::success(ElaMessageBarType::Top, QStringLiteral("房间已创建"), QStringLiteral("房间号: ") + roomId, 3000, this); + emit createdRoom(); + }); + connect(&net, &NetworkManager::roomJoined, this, [this](const QJsonObject& info) { + auto rid = info["room_id"].toString(); + ElaMessageBar::success(ElaMessageBarType::Top, QStringLiteral("已加入房间"), QStringLiteral("房间号: ") + rid + QStringLiteral(" 正在进入等待室..."), 2000, this); + emit joinedRoom(); + }); + connect(&net, &NetworkManager::errorNotify, this, [this](const QJsonObject& err) { + ElaMessageBar::error(ElaMessageBarType::TopRight, QStringLiteral("错误"), err["message"].toString(), 3000, this); + }); +} + +void LobbyWidget::initUI() { + auto* root = new QVBoxLayout(this); + root->setContentsMargins(0, 0, 0, 0); + root->setSpacing(0); + + auto* topSection = new QWidget(this); + auto* topLayout = new QVBoxLayout(topSection); + topLayout->setAlignment(Qt::AlignCenter); + topLayout->setContentsMargins(0, 20, 0, 10); + + auto* card = new QWidget(topSection); + card->setFixedSize(440, 500); + card->setStyleSheet("QWidget#lobbyCard { background: rgba(37,43,61,220); border-radius: 16px; border: 1px solid rgba(94,179,230,30); }"); + card->setObjectName("lobbyCard"); + auto* layout = new QVBoxLayout(card); + layout->setContentsMargins(30, 20, 30, 18); + layout->setSpacing(10); + + auto* titleIcon = new QLabel(card); + titleIcon->setPixmap(QPixmap(":/images/corpse").scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + titleIcon->setAlignment(Qt::AlignCenter); + titleIcon->setStyleSheet("background:transparent;"); + layout->addWidget(titleIcon); + + auto* title = new QLabel(QStringLiteral("冰冷的她醒来之前"), card); + title->setStyleSheet("color: white; font-size: 20px; font-weight: bold; background:transparent;"); + title->setAlignment(Qt::AlignCenter); + layout->addWidget(title); + + layout->addSpacing(6); + + auto makeRow = [&](const QString& labelText, QWidget* input) { + auto* row = new QHBoxLayout(); + auto* lbl = new QLabel(labelText, card); + lbl->setStyleSheet("color:#bbb; font-size:13px; background:transparent; min-width:55px;"); + row->addWidget(lbl); + row->addWidget(input, 1); + layout->addLayout(row); + }; + + _serverEdit = new ElaLineEdit(card); + _serverEdit->setText("ws://127.0.0.1:8080/ws"); + _serverEdit->setFixedHeight(30); + makeRow(QStringLiteral("服务器"), _serverEdit); + + _nicknameEdit = new ElaLineEdit(card); + _nicknameEdit->setText(QStringLiteral("玩家")); + _nicknameEdit->setFixedHeight(30); + makeRow(QStringLiteral("昵称"), _nicknameEdit); + + auto* connRow = new QHBoxLayout(); + _statusLabel = new QLabel(QStringLiteral("○ 未连接 (请先启动服务端)"), card); + _statusLabel->setStyleSheet("color: #888; font-size: 12px; background:transparent;"); + _connectBtn = new ElaPushButton(QStringLiteral("连接"), card); + _connectBtn->setFixedSize(80, 30); + connect(_connectBtn, &ElaPushButton::clicked, this, &LobbyWidget::onConnect); + connRow->addWidget(_statusLabel); + connRow->addStretch(); + connRow->addWidget(_connectBtn); + layout->addLayout(connRow); + + auto* sep1 = new QFrame(card); + sep1->setFrameShape(QFrame::HLine); + sep1->setStyleSheet("color:rgba(255,255,255,15); background:transparent;"); + layout->addWidget(sep1); + + auto* createRow = new QHBoxLayout(); + auto* maxLbl = new QLabel(QStringLiteral("人数"), card); + maxLbl->setStyleSheet("color:#bbb; font-size:13px; background:transparent;"); + _maxPlayersSpin = new ElaSpinBox(card); + _maxPlayersSpin->setRange(3, 6); + _maxPlayersSpin->setValue(4); + _maxPlayersSpin->setFixedWidth(70); + _createBtn = new ElaPushButton(QStringLiteral("创建房间"), card); + _createBtn->setFixedHeight(32); + _createBtn->setEnabled(false); + connect(_createBtn, &ElaPushButton::clicked, this, &LobbyWidget::onCreateRoom); + createRow->addWidget(maxLbl); + createRow->addWidget(_maxPlayersSpin); + createRow->addStretch(); + createRow->addWidget(_createBtn); + layout->addLayout(createRow); + + auto* joinRow = new QHBoxLayout(); + _roomIdEdit = new ElaLineEdit(card); + _roomIdEdit->setPlaceholderText(QStringLiteral("输入房间号")); + _roomIdEdit->setFixedHeight(30); + _joinBtn = new ElaPushButton(QStringLiteral("加入"), card); + _joinBtn->setFixedSize(80, 32); + _joinBtn->setEnabled(false); + connect(_joinBtn, &ElaPushButton::clicked, this, &LobbyWidget::onJoinRoom); + joinRow->addWidget(_roomIdEdit, 1); + joinRow->addWidget(_joinBtn); + layout->addLayout(joinRow); + + auto* sep2 = new QFrame(card); + sep2->setFrameShape(QFrame::HLine); + sep2->setStyleSheet("color:rgba(255,255,255,15); background:transparent;"); + layout->addWidget(sep2); + + auto* testTitle = new QLabel(QStringLiteral("快速测试(含人机占位,连接后可用)"), card); + testTitle->setStyleSheet("color:#777; font-size:11px; background:transparent;"); + testTitle->setAlignment(Qt::AlignCenter); + layout->addWidget(testTitle); + + auto* testRow = new QHBoxLayout(); + testRow->setSpacing(8); + for (int n = 3; n <= 6; ++n) { + auto* btn = new ElaPushButton(QStringLiteral("%1人局").arg(n), card); + btn->setFixedHeight(30); + btn->setEnabled(false); + connect(btn, &ElaPushButton::clicked, this, [this, n]() { onJoinTestRoom(n); }); + connect(&NetworkManager::instance(), &NetworkManager::connected, btn, [btn]() { btn->setEnabled(true); }); + connect(&NetworkManager::instance(), &NetworkManager::disconnected, btn, [btn]() { btn->setEnabled(false); }); + testRow->addWidget(btn); + } + layout->addLayout(testRow); + + topLayout->addWidget(card); + root->addWidget(topSection, 1); + + buildCardGallery(this, root); +} + +void LobbyWidget::buildCardGallery(QWidget* parent, QLayout* parentLayout) { + auto* section = new QWidget(parent); + section->setFixedHeight(180); + section->setStyleSheet("background: rgba(0,0,0,80); border-top: 1px solid rgba(255,255,255,10);"); + auto* sLayout = new QVBoxLayout(section); + sLayout->setContentsMargins(20, 8, 20, 8); + sLayout->setSpacing(4); + + auto* label = new QLabel(QStringLiteral("卡牌一览"), section); + label->setStyleSheet("color: #999; font-size: 13px; font-weight: bold; background:transparent;"); + sLayout->addWidget(label); + + auto* scroll = new QScrollArea(section); + scroll->setWidgetResizable(true); + scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + scroll->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + scroll->setStyleSheet( + "QScrollArea { background:transparent; border:none; }" + "QScrollBar:horizontal { height:6px; background:transparent; }" + "QScrollBar::handle:horizontal { background:rgba(255,255,255,40); border-radius:3px; min-width:30px; }" + "QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal { width:0; }" + ); + + auto* strip = new QWidget(); + strip->setStyleSheet("background:transparent;"); + auto* hlay = new QHBoxLayout(strip); + hlay->setContentsMargins(0, 0, 0, 0); + hlay->setSpacing(12); + + auto allDefs = CardDatabase::instance().allCardDefs(); + QStringList order = { + "student_council_president", "class_monitor", "honor_student", + "discipline_committee_member", "library_committee_member", + "health_committee_member", "young_lady", "newspaper_club", + "go_home_club", "infected_person", "accomplice", "culprit", "alien" + }; + + for (const auto& id : order) { + auto* col = new QWidget(strip); + auto* colLayout = new QVBoxLayout(col); + colLayout->setContentsMargins(0, 0, 0, 0); + colLayout->setSpacing(3); + colLayout->setAlignment(Qt::AlignCenter); + + auto* cw = new CardWidget(col); + cw->setCardType(id); + cw->setFaceUp(true); + cw->setCardEnabled(false); + colLayout->addWidget(cw, 0, Qt::AlignCenter); + + const CardDef* def = CardDatabase::instance().getCardDef(id); + QString name = def ? def->name : id; + auto* nameLabel = new QLabel(name, col); + nameLabel->setStyleSheet("color:#aaa; font-size:10px; background:transparent;"); + nameLabel->setAlignment(Qt::AlignCenter); + colLayout->addWidget(nameLabel); + + hlay->addWidget(col); + } + hlay->addStretch(); + + scroll->setWidget(strip); + QScroller::grabGesture(scroll->viewport(), QScroller::LeftMouseButtonGesture); + sLayout->addWidget(scroll, 1); + + parentLayout->addWidget(section); +} + +void LobbyWidget::paintEvent(QPaintEvent*) { + QPainter p(this); + QLinearGradient grad(0, 0, width(), height()); + grad.setColorAt(0, QColor("#1a1f2e")); + grad.setColorAt(0.5, QColor("#15192a")); + grad.setColorAt(1, QColor("#1a1f2e")); + p.fillRect(rect(), grad); +} + +void LobbyWidget::onConnect() { + auto& net = NetworkManager::instance(); + if (net.isConnected()) { + net.disconnect(); + } else { + auto url = _serverEdit->text().trimmed(); + if (url.isEmpty()) return; + _connectBtn->setEnabled(false); + _connectBtn->setText(QStringLiteral("连接中...")); + net.connectToServer(url); + } +} + +void LobbyWidget::onCreateRoom() { + auto nick = _nicknameEdit->text().trimmed(); + if (nick.isEmpty()) nick = QStringLiteral("玩家"); + NetworkManager::instance().createRoom(nick, _maxPlayersSpin->value()); +} + +void LobbyWidget::onJoinRoom() { + auto nick = _nicknameEdit->text().trimmed(); + auto rid = _roomIdEdit->text().trimmed(); + if (rid.isEmpty()) { + ElaMessageBar::warning(ElaMessageBarType::TopRight, QStringLiteral("提示"), QStringLiteral("请输入房间号"), 2000, this); + return; + } + if (nick.isEmpty()) nick = QStringLiteral("玩家"); + NetworkManager::instance().joinRoom(rid, nick); +} + +void LobbyWidget::onJoinTestRoom(int playerCount) { + auto nick = _nicknameEdit->text().trimmed(); + if (nick.isEmpty()) nick = QStringLiteral("玩家"); + QString roomId = QStringLiteral("TEST%1").arg(playerCount); + ElaMessageBar::information(ElaMessageBarType::Top, QStringLiteral("加入房间"), QStringLiteral("正在加入 ") + roomId + QStringLiteral(" ..."), 1500, this); + NetworkManager::instance().joinRoom(roomId, nick); +} diff --git a/src/ui/LobbyWidget.h b/src/ui/LobbyWidget.h new file mode 100644 index 0000000..77749a1 --- /dev/null +++ b/src/ui/LobbyWidget.h @@ -0,0 +1,42 @@ +#ifndef LOBBYWIDGET_H +#define LOBBYWIDGET_H + +#include + +class ElaLineEdit; +class ElaPushButton; +class ElaSpinBox; +class QLabel; +class QScrollArea; + +class LobbyWidget : public QWidget { + Q_OBJECT +public: + explicit LobbyWidget(QWidget* parent = nullptr); + +signals: + void joinedRoom(); + void createdRoom(); + +protected: + void paintEvent(QPaintEvent* event) override; + +private: + void initUI(); + void buildCardGallery(QWidget* parent, QLayout* parentLayout); + void onConnect(); + void onCreateRoom(); + void onJoinRoom(); + void onJoinTestRoom(int playerCount); + + ElaLineEdit* _serverEdit = nullptr; + ElaLineEdit* _nicknameEdit = nullptr; + ElaLineEdit* _roomIdEdit = nullptr; + ElaSpinBox* _maxPlayersSpin = nullptr; + ElaPushButton* _connectBtn = nullptr; + ElaPushButton* _createBtn = nullptr; + ElaPushButton* _joinBtn = nullptr; + QLabel* _statusLabel = nullptr; +}; + +#endif diff --git a/src/ui/MainWindow.cpp b/src/ui/MainWindow.cpp new file mode 100644 index 0000000..1676d8c --- /dev/null +++ b/src/ui/MainWindow.cpp @@ -0,0 +1,43 @@ +#include "MainWindow.h" +#include "LobbyWidget.h" +#include "RoomWidget.h" +#include "GameWidget.h" + +#include +#include + +MainWindow::MainWindow(QWidget* parent) : QWidget(parent) +{ + initUI(); +} + +void MainWindow::initUI() { + setWindowTitle(QStringLiteral("冰冷的她醒来之前")); + setWindowIcon(QIcon(":/images/corpse")); + resize(1200, 800); + setMinimumSize(900, 600); + + auto* layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + + _stack = new QStackedWidget(this); + _lobby = new LobbyWidget(this); + _room = new RoomWidget(this); + _game = new GameWidget(this); + + _stack->addWidget(_lobby); + _stack->addWidget(_room); + _stack->addWidget(_game); + _stack->setCurrentWidget(_lobby); + layout->addWidget(_stack); + + connect(_lobby, &LobbyWidget::createdRoom, this, &MainWindow::switchToRoom); + connect(_lobby, &LobbyWidget::joinedRoom, this, &MainWindow::switchToRoom); + connect(_room, &RoomWidget::gameStarted, this, &MainWindow::switchToGame); + connect(_room, &RoomWidget::leftRoom, this, &MainWindow::switchToLobby); + connect(_game, &GameWidget::gameFinished, this, &MainWindow::switchToRoom); +} + +void MainWindow::switchToLobby() { _stack->setCurrentWidget(_lobby); } +void MainWindow::switchToRoom() { _stack->setCurrentWidget(_room); } +void MainWindow::switchToGame() { _stack->setCurrentWidget(_game); } diff --git a/src/ui/MainWindow.h b/src/ui/MainWindow.h new file mode 100644 index 0000000..5198550 --- /dev/null +++ b/src/ui/MainWindow.h @@ -0,0 +1,29 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include + +class QStackedWidget; +class LobbyWidget; +class RoomWidget; +class GameWidget; + +class MainWindow : public QWidget { + Q_OBJECT +public: + explicit MainWindow(QWidget* parent = nullptr); + + void switchToLobby(); + void switchToRoom(); + void switchToGame(); + +private: + void initUI(); + + QStackedWidget* _stack = nullptr; + LobbyWidget* _lobby = nullptr; + RoomWidget* _room = nullptr; + GameWidget* _game = nullptr; +}; + +#endif diff --git a/src/ui/PlayerSeatWidget.cpp b/src/ui/PlayerSeatWidget.cpp new file mode 100644 index 0000000..555399e --- /dev/null +++ b/src/ui/PlayerSeatWidget.cpp @@ -0,0 +1,126 @@ +#include "PlayerSeatWidget.h" +#include "CardWidget.h" +#include +#include +#include +#include +#include +#include +#include + +PlayerSeatWidget::PlayerSeatWidget(QWidget* parent) + : QWidget(parent) +{ + initUI(); + setFixedSize(180, 130); +} + +void PlayerSeatWidget::initUI() { + auto* mainLayout = new QVBoxLayout(this); + mainLayout->setContentsMargins(10, 8, 10, 8); + mainLayout->setSpacing(4); + + _nameLabel = new QLabel(this); + _nameLabel->setStyleSheet("color: white; font-size: 14px; font-weight: bold;"); + _nameLabel->setAlignment(Qt::AlignCenter); + mainLayout->addWidget(_nameLabel); + + auto* infoRow = new QHBoxLayout(); + infoRow->setSpacing(8); + _handCountLabel = new QLabel(this); + _handCountLabel->setStyleSheet("color: #aaa; font-size: 11px;"); + _challengeLabel = new QLabel(this); + _challengeLabel->setStyleSheet("color: #ff6b6b; font-size: 11px;"); + _statusLabel = new QLabel(this); + _statusLabel->setStyleSheet("color: #4ecdc4; font-size: 11px;"); + infoRow->addWidget(_handCountLabel); + infoRow->addWidget(_challengeLabel); + infoRow->addStretch(); + infoRow->addWidget(_statusLabel); + mainLayout->addLayout(infoRow); + + _skillZoneWidget = new QWidget(this); + _skillLayout = new QHBoxLayout(_skillZoneWidget); + _skillLayout->setContentsMargins(0, 0, 0, 0); + _skillLayout->setSpacing(2); + _skillLayout->addStretch(); + mainLayout->addWidget(_skillZoneWidget); + + mainLayout->addStretch(); +} + +void PlayerSeatWidget::setPlayerData(const QJsonObject& data) { + _playerId = data["player_id"].toString(); + auto nick = data["nickname"].toString(); + auto handCount = data["hand_card_count"].toInt(); + auto challengeCount = data["challenge_zone_count"].toInt(); + _isExited = data["is_exited"].toBool(); + + _nameLabel->setText(nick); + _handCountLabel->setText(QStringLiteral("手牌: %1").arg(handCount)); + + if (challengeCount > 0) { + _challengeLabel->setText(QStringLiteral("质疑: %1").arg(challengeCount)); + _challengeLabel->show(); + } else { + _challengeLabel->hide(); + } + + if (_isExited) { + _statusLabel->setText(QStringLiteral("已退出")); + _statusLabel->show(); + } else { + _statusLabel->hide(); + } + + updateSkillZone(data["skill_zone"].toArray()); + update(); +} + +void PlayerSeatWidget::setIsCurrentTurn(bool isTurn) { + _isTurn = isTurn; + update(); +} + +void PlayerSeatWidget::setPosition(int position) { + _position = position; +} + +void PlayerSeatWidget::updateSkillZone(const QJsonArray& cards) { + for (auto* c : _skillCards) { + _skillLayout->removeWidget(c); + c->deleteLater(); + } + _skillCards.clear(); + + for (const auto& cv : cards) { + auto c = cv.toObject(); + auto* card = new CardWidget(_skillZoneWidget); + card->setMini(true); + card->setCardType(c["type_id"].toString()); + card->setFaceUp(true); + card->setCardEnabled(false); + _skillLayout->insertWidget(_skillLayout->count() - 1, card); + _skillCards.append(card); + } +} + +void PlayerSeatWidget::paintEvent(QPaintEvent*) { + QPainter p(this); + p.setRenderHint(QPainter::Antialiasing); + + QColor bgColor = _isTurn ? QColor(94, 179, 230, 30) : QColor(37, 43, 61, 180); + QColor borderColor = _isTurn ? QColor("#5eb3e6") : QColor(255, 255, 255, 25); + + QPainterPath path; + path.addRoundedRect(rect().adjusted(1, 1, -1, -1), 10, 10); + p.fillPath(path, bgColor); + p.setPen(QPen(borderColor, _isTurn ? 2.0 : 1.0)); + p.drawPath(path); +} + +void PlayerSeatWidget::mousePressEvent(QMouseEvent* e) { + if (e->button() == Qt::LeftButton) { + emit playerClicked(_playerId); + } +} diff --git a/src/ui/PlayerSeatWidget.h b/src/ui/PlayerSeatWidget.h new file mode 100644 index 0000000..e2f916a --- /dev/null +++ b/src/ui/PlayerSeatWidget.h @@ -0,0 +1,46 @@ +#ifndef PLAYERSEATWIDGET_H +#define PLAYERSEATWIDGET_H + +#include +#include + +class QLabel; +class QHBoxLayout; +class CardWidget; + +class PlayerSeatWidget : public QWidget { + Q_OBJECT +public: + explicit PlayerSeatWidget(QWidget* parent = nullptr); + + void setPlayerData(const QJsonObject& data); + void setIsCurrentTurn(bool isTurn); + void setPosition(int position); + QString playerId() const { return _playerId; } + +signals: + void playerClicked(const QString& playerId); + +protected: + void paintEvent(QPaintEvent* event) override; + void mousePressEvent(QMouseEvent* event) override; + +private: + void initUI(); + void updateSkillZone(const QJsonArray& cards); + + QString _playerId; + int _position = 0; + bool _isTurn = false; + bool _isExited = false; + + QLabel* _nameLabel = nullptr; + QLabel* _handCountLabel = nullptr; + QLabel* _challengeLabel = nullptr; + QLabel* _statusLabel = nullptr; + QHBoxLayout* _skillLayout = nullptr; + QWidget* _skillZoneWidget = nullptr; + QVector _skillCards; +}; + +#endif diff --git a/src/ui/RoomWidget.cpp b/src/ui/RoomWidget.cpp new file mode 100644 index 0000000..e00308e --- /dev/null +++ b/src/ui/RoomWidget.cpp @@ -0,0 +1,167 @@ +#include "RoomWidget.h" +#include "ElaPushButton.h" +#include "ElaToggleSwitch.h" +#include "ElaMessageBar.h" +#include "NetworkManager.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +RoomWidget::RoomWidget(QWidget* parent) : QWidget(parent) +{ + initUI(); + auto& net = NetworkManager::instance(); + connect(&net, &NetworkManager::roomStateUpdated, this, &RoomWidget::onRoomState); + connect(&net, &NetworkManager::gameStarted, this, [this](const QJsonObject&) { emit gameStarted(); }); + connect(&net, &NetworkManager::roomLeft, this, [this]() { emit leftRoom(); }); +} + +void RoomWidget::initUI() { + auto* root = new QVBoxLayout(this); + root->setContentsMargins(0, 0, 0, 0); + root->setAlignment(Qt::AlignCenter); + + auto* card = new QWidget(this); + card->setFixedSize(520, 580); + card->setObjectName("roomCard"); + card->setStyleSheet("QWidget#roomCard { background: rgba(37,43,61,230); border-radius: 16px; border: 1px solid rgba(94,179,230,40); }"); + auto* layout = new QVBoxLayout(card); + layout->setContentsMargins(30, 20, 30, 20); + layout->setSpacing(10); + + auto* headerLabel = new QLabel(QStringLiteral("— 等待室 —"), card); + headerLabel->setStyleSheet("color: #4ecdc4; font-size: 12px; letter-spacing: 6px; background:transparent;"); + headerLabel->setAlignment(Qt::AlignCenter); + layout->addWidget(headerLabel); + + _roomIdLabel = new QLabel(card); + _roomIdLabel->setStyleSheet("color: #ffd700; font-size: 28px; font-weight: bold; background:transparent;"); + _roomIdLabel->setAlignment(Qt::AlignCenter); + _roomIdLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); + _roomIdLabel->setCursor(Qt::IBeamCursor); + _roomIdLabel->setToolTip(QStringLiteral("点击可选中复制")); + layout->addWidget(_roomIdLabel); + + _infoLabel = new QLabel(card); + _infoLabel->setStyleSheet("color: #888; font-size: 13px; background:transparent;"); + _infoLabel->setAlignment(Qt::AlignCenter); + layout->addWidget(_infoLabel); + + layout->addSpacing(5); + + auto* listHeader = new QLabel(QStringLiteral("玩家"), card); + listHeader->setStyleSheet("color: #bbb; font-size: 13px; font-weight: bold; background:transparent;"); + layout->addWidget(listHeader); + + _playerList = new QListWidget(card); + _playerList->setMinimumHeight(220); + _playerList->setStyleSheet( + "QListWidget { background: rgba(255,255,255,5); border: 1px solid rgba(255,255,255,12); border-radius: 8px; color: white; font-size: 14px; }" + "QListWidget::item { padding: 10px 15px; border-bottom: 1px solid rgba(255,255,255,6); }" + ); + layout->addWidget(_playerList, 1); + + auto* chaosRow = new QHBoxLayout(); + auto* chaosLbl = new QLabel(QStringLiteral("混沌模式"), card); + chaosLbl->setStyleSheet("color: #999; font-size: 12px; background:transparent;"); + _chaosSwitch = new ElaToggleSwitch(card); + chaosRow->addWidget(chaosLbl); + chaosRow->addStretch(); + chaosRow->addWidget(_chaosSwitch); + layout->addLayout(chaosRow); + + auto* btnRow = new QHBoxLayout(); + btnRow->setSpacing(10); + _leaveBtn = new ElaPushButton(QStringLiteral("离开房间"), card); + _leaveBtn->setFixedHeight(38); + connect(_leaveBtn, &ElaPushButton::clicked, this, [this]() { NetworkManager::instance().leaveRoom(); }); + + _readyBtn = new ElaPushButton(QStringLiteral("准备"), card); + _readyBtn->setFixedHeight(38); + connect(_readyBtn, &ElaPushButton::clicked, this, [this]() { + _isReady = !_isReady; + NetworkManager::instance().setReady(_isReady); + }); + + _startBtn = new ElaPushButton(QStringLiteral("开始游戏"), card); + _startBtn->setFixedHeight(38); + _startBtn->setEnabled(false); + _startBtn->hide(); + connect(_startBtn, &ElaPushButton::clicked, this, [this]() { NetworkManager::instance().startGame(); }); + + btnRow->addWidget(_leaveBtn); + btnRow->addStretch(); + btnRow->addWidget(_readyBtn); + btnRow->addWidget(_startBtn); + layout->addLayout(btnRow); + + root->addWidget(card); +} + +void RoomWidget::paintEvent(QPaintEvent*) { + QPainter p(this); + QLinearGradient grad(0, 0, width(), height()); + grad.setColorAt(0, QColor("#1a1f2e")); + grad.setColorAt(0.5, QColor("#1d2233")); + grad.setColorAt(1, QColor("#1a1f2e")); + p.fillRect(rect(), grad); +} + +void RoomWidget::onRoomState(const QJsonObject& state) { + auto roomId = state["room_id"].toString(); + _roomIdLabel->setText(roomId); + + auto ht = state["harmony_target"].toInt(); + int total = state["players"].toArray().size(); + QString info = QStringLiteral("%1 / %2 人").arg(total).arg(total); + if (ht > 0) info += QStringLiteral(" | 调和目标: %1").arg(ht); + _infoLabel->setText(info); + + _localPlayerId = state["your_player_id"].toString(); + _playerList->clear(); + auto players = state["players"].toArray(); + bool allReady = !players.isEmpty(); + _isHost = false; + + for (const auto& pv : players) { + auto p = pv.toObject(); + auto pid = p["player_id"].toString(); + auto nick = p["nickname"].toString(); + auto ready = p["is_ready"].toBool(); + auto host = p["is_host"].toBool(); + auto bot = p["is_bot"].toBool(); + + QString text; + if (bot) { + text = QStringLiteral("🤖 %1").arg(nick); + } else { + text = QStringLiteral("👤 %1").arg(nick); + } + if (host) text += QStringLiteral(" 👑 房主"); + if (ready) text += QStringLiteral(" ✔ 已准备"); + else text += QStringLiteral(" ⏳ 未准备"); + if (pid == _localPlayerId) text += QStringLiteral(" ← 你"); + if (!ready && !bot) allReady = false; + if (pid == _localPlayerId && host) _isHost = true; + if (pid == _localPlayerId) _isReady = ready; + + auto* item = new QListWidgetItem(text); + if (pid == _localPlayerId) + item->setForeground(QColor("#4ecdc4")); + else if (bot) + item->setForeground(QColor("#888")); + _playerList->addItem(item); + } + + _readyBtn->setText(_isReady ? QStringLiteral("取消准备") : QStringLiteral("准备")); + _startBtn->setVisible(_isHost); + _startBtn->setEnabled(_isHost && allReady && players.size() >= 3); +} diff --git a/src/ui/RoomWidget.h b/src/ui/RoomWidget.h new file mode 100644 index 0000000..8fa3c82 --- /dev/null +++ b/src/ui/RoomWidget.h @@ -0,0 +1,42 @@ +#ifndef ROOMWIDGET_H +#define ROOMWIDGET_H + +#include +#include + +class ElaPushButton; +class QLabel; +class QListWidget; +class ElaToggleSwitch; + +class RoomWidget : public QWidget { + Q_OBJECT +public: + explicit RoomWidget(QWidget* parent = nullptr); + +signals: + void gameStarted(); + void leftRoom(); + +protected: + void paintEvent(QPaintEvent* event) override; + +private: + void initUI(); + void onRoomState(const QJsonObject& state); + + QLabel* _titleLabel = nullptr; + QLabel* _roomIdLabel = nullptr; + QLabel* _infoLabel = nullptr; + QListWidget* _playerList = nullptr; + ElaPushButton* _readyBtn = nullptr; + ElaPushButton* _startBtn = nullptr; + ElaPushButton* _leaveBtn = nullptr; + ElaToggleSwitch* _chaosSwitch = nullptr; + + bool _isReady = false; + bool _isHost = false; + QString _localPlayerId; +}; + +#endif diff --git a/src/ui/SceneCardItem.cpp b/src/ui/SceneCardItem.cpp new file mode 100644 index 0000000..d08dd23 --- /dev/null +++ b/src/ui/SceneCardItem.cpp @@ -0,0 +1,172 @@ +#include "SceneCardItem.h" +#include "CardData.h" +#include +#include +#include +#include +#include +#include +#include + +SceneCardItem::SceneCardItem(QGraphicsItem* parent) + : QGraphicsObject(parent) +{ + setAcceptHoverEvents(true); + setCacheMode(DeviceCoordinateCache); + auto backRaw = CardDatabase::instance().getCardBackImage(); + if (!backRaw.isNull()) + _scaledBack = backRaw.scaled(int(W), int(H), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation) + .copy(0, 0, int(W), int(H)); +} + +void SceneCardItem::setCardData(const QString& uid, const QString& typeId, bool faceUp) { + _uid = uid; + _typeId = typeId; + _faceUp = faceUp; + rebuildPixmap(); + update(); +} + +void SceneCardItem::setFaceUp(bool f) { _faceUp = f; update(); } +void SceneCardItem::setCardSelected(bool s) { + if (_selected == s) return; + bool was = _selected; + _selected = s; + if (s && !was) setY(y() - 20); + else if (!s && was) setY(y() + 20); + update(); +} +void SceneCardItem::setCardEnabled(bool e) { + _enabled = e; + setCursor(e ? Qt::PointingHandCursor : Qt::ArrowCursor); + update(); +} + +QRectF SceneCardItem::boundingRect() const { + return {-8, -8, W + 16, H + 16}; +} + +void SceneCardItem::paint(QPainter* p, const QStyleOptionGraphicsItem*, QWidget*) { + p->setRenderHint(QPainter::Antialiasing); + p->setRenderHint(QPainter::SmoothPixmapTransform); + + QRectF cr(0, 0, W, H); + constexpr qreal r = 8; + + if (_hovered && _enabled) { + QRadialGradient glow(cr.center(), W * 0.7); + glow.setColorAt(0, QColor(94, 179, 230, 60)); + glow.setColorAt(1, Qt::transparent); + p->setPen(Qt::NoPen); + p->setBrush(glow); + p->drawRoundedRect(cr.adjusted(-6, -6, 6, 6), r + 4, r + 4); + } + + QPainterPath clip; + clip.addRoundedRect(cr, r, r); + p->setClipPath(clip); + + bool showFront = _showFlippedFace ? !_faceUp : _faceUp; + QPixmap& px = (showFront && !_scaledFace.isNull()) ? _scaledFace : _scaledBack; + if (!px.isNull()) { + p->drawPixmap(cr.toRect(), px); + } else { + QLinearGradient bg(0, 0, 0, H); + if (showFront) { bg.setColorAt(0, QColor("#d4c5a3")); bg.setColorAt(1, QColor("#b8a67a")); } + else { bg.setColorAt(0, QColor("#2a3a5a")); bg.setColorAt(1, QColor("#1a2a44")); } + p->fillRect(cr, bg); + } + p->setClipping(false); + + if (showFront) { + const CardDef* def = CardDatabase::instance().getCardDef(_typeId); + if (def) { + p->setPen(Qt::NoPen); + p->setBrush(QColor(0, 0, 0, 160)); + QRectF badge(3, 3, 26, 20); + p->drawRoundedRect(badge, 4, 4); + p->setFont(QFont("Consolas", 11, QFont::Bold)); + p->setPen(def->mp >= 0 ? QColor("#d4c5a3") : QColor("#d94f5c")); + p->drawText(badge, Qt::AlignCenter, QString::number(def->mp)); + } + } + + QPen border; + if (_selected) border = QPen(QColor("#5eb3e6"), 2.5); + else if (_hovered && _enabled) border = QPen(QColor(94, 179, 230, 120), 1.5); + else border = QPen(QColor("#8b2635"), 1); + p->setPen(border); + p->setBrush(Qt::NoBrush); + p->drawRoundedRect(cr.adjusted(0.5, 0.5, -0.5, -0.5), r, r); + + if (!_enabled) { + QPainterPath dc; + dc.addRoundedRect(cr, r, r); + p->setClipPath(dc); + p->fillRect(cr, QColor(0, 0, 0, 100)); + } + + if (_hlOpacity > 0.01) { + QPainterPath hc; + hc.addRoundedRect(cr, r, r); + p->setClipPath(hc); + p->fillRect(cr, QColor(94, 179, 230, int(40 * _hlOpacity))); + } +} + +void SceneCardItem::hoverEnterEvent(QGraphicsSceneHoverEvent*) { + if (!_enabled) return; + _hovered = true; + update(); + emit hoverIn(_typeId); +} + +void SceneCardItem::hoverLeaveEvent(QGraphicsSceneHoverEvent*) { + _hovered = false; + update(); + emit hoverOut(); +} + +void SceneCardItem::mousePressEvent(QGraphicsSceneMouseEvent* e) { + if (e->button() == Qt::LeftButton && _enabled) + emit clicked(_uid); + if (e->button() == Qt::RightButton && _faceUp) + emit rightClicked(_typeId); +} + +void SceneCardItem::setFlipProgress(qreal p) { + _flipProg = p; + qreal scaleX = qMax(0.02, qAbs(qCos(p * M_PI))); + QTransform t; + t.translate(W / 2.0, H / 2.0); + t.scale(scaleX, 1.0); + t.translate(-W / 2.0, -H / 2.0); + setTransform(t); + _showFlippedFace = (p >= 0.5); + update(); +} + +void SceneCardItem::animateFlip(bool toFaceUp, int duration) { + auto* anim = new QPropertyAnimation(this, "flipProgress"); + anim->setDuration(duration); + anim->setStartValue(0.0); + anim->setEndValue(1.0); + anim->setEasingCurve(QEasingCurve::InOutQuad); + connect(anim, &QPropertyAnimation::finished, this, [this, toFaceUp]() { + _faceUp = toFaceUp; + _flipProg = 0; + _showFlippedFace = false; + setTransform(QTransform()); + update(); + }); + anim->start(QAbstractAnimation::DeleteWhenStopped); +} + +void SceneCardItem::rebuildPixmap() { + if (!_typeId.isEmpty()) { + auto raw = CardDatabase::instance().getCardFrontImage(_typeId); + if (!raw.isNull()) + _scaledFace = raw.scaled(int(W), int(H), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation) + .copy(0, 0, int(W), int(H)); + } +} diff --git a/src/ui/SceneCardItem.h b/src/ui/SceneCardItem.h new file mode 100644 index 0000000..1b35ac7 --- /dev/null +++ b/src/ui/SceneCardItem.h @@ -0,0 +1,62 @@ +#ifndef SCENECARDITEM_H +#define SCENECARDITEM_H + +#include +#include + +class SceneCardItem : public QGraphicsObject { + Q_OBJECT + Q_PROPERTY(qreal highlightOpacity READ highlightOpacity WRITE setHighlightOpacity) + Q_PROPERTY(qreal flipProgress READ flipProgress WRITE setFlipProgress) +public: + explicit SceneCardItem(QGraphicsItem* parent = nullptr); + + void setCardData(const QString& uid, const QString& typeId, bool faceUp); + void setFaceUp(bool f); + void setCardSelected(bool s); + void setCardEnabled(bool e); + + QString uid() const { return _uid; } + QString typeId() const { return _typeId; } + bool isFaceUp() const { return _faceUp; } + bool isCardSelected() const { return _selected; } + + qreal highlightOpacity() const { return _hlOpacity; } + void setHighlightOpacity(qreal o) { _hlOpacity = o; update(); } + + qreal flipProgress() const { return _flipProg; } + void setFlipProgress(qreal p); + void animateFlip(bool toFaceUp, int duration = 500); + + static constexpr qreal W = 100; + static constexpr qreal H = 140; + + QRectF boundingRect() const override; + void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override; + +signals: + void clicked(const QString& uid); + void rightClicked(const QString& typeId); + void hoverIn(const QString& typeId); + void hoverOut(); + +protected: + void hoverEnterEvent(QGraphicsSceneHoverEvent* e) override; + void hoverLeaveEvent(QGraphicsSceneHoverEvent* e) override; + void mousePressEvent(QGraphicsSceneMouseEvent* e) override; + +private: + void rebuildPixmap(); + + QString _uid, _typeId; + bool _faceUp = false; + bool _selected = false; + bool _enabled = true; + bool _hovered = false; + qreal _hlOpacity = 0; + qreal _flipProg = 0; + bool _showFlippedFace = false; + QPixmap _scaledFace, _scaledBack; +}; + +#endif diff --git a/src/ui/SettlementOverlay.cpp b/src/ui/SettlementOverlay.cpp new file mode 100644 index 0000000..37bf799 --- /dev/null +++ b/src/ui/SettlementOverlay.cpp @@ -0,0 +1,322 @@ +#include "SettlementOverlay.h" +#include "CardWidget.h" +#include "CardData.h" +#include "ElaPushButton.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +static const QColor S_ACCENT("#5eb3e6"); +static const QColor S_SUCCESS("#5cb85c"); +static const QColor S_DANGER("#d94f5c"); +static const QColor S_GOLD("#d4c5a3"); +static const QColor S_TEXT("#e0e0e0"); +static const QColor S_DIM("#8a8f9d"); + +SettlementOverlay::SettlementOverlay(QWidget* parent) : QWidget(parent) +{ + hide(); + _timer = new QTimer(this); + _timer->setInterval(500); + connect(_timer, &QTimer::timeout, this, &SettlementOverlay::revealNextCard); + + _panel = new QWidget(this); + _panel->setFixedSize(800, 600); + _panel->setObjectName("stPanel"); + _panel->setStyleSheet("QWidget#stPanel{background:rgba(20,24,38,245);border-radius:16px;border:1px solid rgba(94,179,230,40);}"); + + auto* pLay = new QVBoxLayout(_panel); + pLay->setContentsMargins(30, 20, 30, 20); + pLay->setSpacing(8); + + _phaseTitle = new QLabel(_panel); + _phaseTitle->setStyleSheet(QStringLiteral("color:%1;font-size:22px;font-weight:bold;background:transparent;").arg(S_GOLD.name())); + _phaseTitle->setAlignment(Qt::AlignCenter); + pLay->addWidget(_phaseTitle); + + _scroll = new QScrollArea(_panel); + _scroll->setWidgetResizable(true); + _scroll->setStyleSheet("QScrollArea{background:transparent;border:none;}QScrollBar{width:4px;background:transparent;}QScrollBar::handle{background:rgba(255,255,255,30);border-radius:2px;}"); + _content = new QWidget(); + _content->setStyleSheet("background:transparent;"); + _contentLayout = new QVBoxLayout(_content); + _contentLayout->setContentsMargins(0, 0, 0, 0); + _contentLayout->setSpacing(8); + _contentLayout->setAlignment(Qt::AlignTop); + _scroll->setWidget(_content); + pLay->addWidget(_scroll, 1); + + _totalLabel = new QLabel(_panel); + _totalLabel->setStyleSheet(QStringLiteral("color:%1;font-size:16px;font-weight:bold;background:transparent;").arg(S_ACCENT.name())); + _totalLabel->setAlignment(Qt::AlignCenter); + pLay->addWidget(_totalLabel); + + _resultLabel = new QLabel(_panel); + _resultLabel->setStyleSheet(QStringLiteral("color:%1;font-size:20px;font-weight:bold;background:transparent;").arg(S_GOLD.name())); + _resultLabel->setAlignment(Qt::AlignCenter); + _resultLabel->hide(); + pLay->addWidget(_resultLabel); + + _nextBtn = new ElaPushButton(QStringLiteral("继续"), _panel); + _nextBtn->setFixedSize(120, 38); + _nextBtn->hide(); + connect(_nextBtn, &ElaPushButton::clicked, this, [this]() { startPhase(_phase + 1); }); + pLay->addWidget(_nextBtn, 0, Qt::AlignCenter); +} + +void SettlementOverlay::showSettlement(const QJsonObject& data) { + _data = data; + show(); + raise(); + _panel->move((width() - 800) / 2, (height() - 600) / 2); + startPhase(0); +} + +void SettlementOverlay::paintEvent(QPaintEvent*) { + QPainter p(this); + p.fillRect(rect(), QColor(0, 0, 0, 190)); +} + +void SettlementOverlay::startPhase(int phase) { + _phase = phase; + _revealIdx = 0; + _runningTotal = 0; + _currentPlayerIdx = 0; + _timer->stop(); + _nextBtn->hide(); + _resultLabel->hide(); + _totalLabel->clear(); + + while (_contentLayout->count()) { + auto* item = _contentLayout->takeAt(0); + if (item->widget()) item->widget()->deleteLater(); + delete item; + } + _cardRow = nullptr; + _cardRowWidget = nullptr; + + if (phase == 0) { + _phaseTitle->setText(QStringLiteral("— 调和判定 —")); + _phaseTitle->setStyleSheet(QStringLiteral("color:%1;font-size:22px;font-weight:bold;background:transparent;").arg(S_ACCENT.name())); + auto h = _data["harmony"].toObject(); + _totalLabel->setText(QStringLiteral("目标: %1 | 合计: 0").arg(h["target"].toInt())); + _cardRowWidget = new QWidget(_content); + _cardRowWidget->setStyleSheet("background:transparent;"); + _cardRow = new QHBoxLayout(_cardRowWidget); + _cardRow->setAlignment(Qt::AlignCenter); + _cardRow->setSpacing(8); + _contentLayout->addWidget(_cardRowWidget); + _timer->start(); + } else if (phase == 1) { + _phaseTitle->setText(QStringLiteral("— 质疑判定 —")); + _phaseTitle->setStyleSheet(QStringLiteral("color:%1;font-size:22px;font-weight:bold;background:transparent;").arg(QColor("#ff8c42").name())); + _totalLabel->clear(); + _timer->start(); + } else if (phase == 2) { + _phaseTitle->setText(QStringLiteral("— 胜利判定 —")); + _phaseTitle->setStyleSheet(QStringLiteral("color:%1;font-size:22px;font-weight:bold;background:transparent;").arg(S_GOLD.name())); + _totalLabel->clear(); + _timer->setInterval(800); + _timer->start(); + } else { + buildFinalPanel(); + } +} + +void SettlementOverlay::revealNextCard() { + if (_phase == 0) { + auto cards = _data["harmony"].toObject()["cards"].toArray(); + if (_revealIdx >= cards.size()) { + _timer->stop(); + showPhaseResult(); + return; + } + auto c = cards[_revealIdx].toObject(); + auto* cw = makeFlipCard(c); + _cardRow->addWidget(cw); + + _runningTotal += c["mp"].toInt(); + int target = _data["harmony"].toObject()["target"].toInt(); + _totalLabel->setText(QStringLiteral("目标: %1 | 合计: %2").arg(target).arg(_runningTotal)); + _revealIdx++; + } else if (_phase == 1) { + auto perPlayer = _data["challenge"].toObject()["per_player"].toArray(); + if (_currentPlayerIdx >= perPlayer.size()) { + _timer->stop(); + showPhaseResult(); + return; + } + auto pp = perPlayer[_currentPlayerIdx].toObject(); + auto cards = pp["cards"].toArray(); + + if (_revealIdx == 0) { + auto* nameLabel = new QLabel(QStringLiteral("%1 (质疑合计: %2)").arg(pp["nickname"].toString()).arg(pp["total_mp"].toInt()), _content); + nameLabel->setStyleSheet(QStringLiteral("color:%1;font-size:14px;font-weight:bold;background:transparent;").arg(S_TEXT.name())); + _contentLayout->addWidget(nameLabel); + + _cardRowWidget = new QWidget(_content); + _cardRowWidget->setStyleSheet("background:transparent;"); + _cardRow = new QHBoxLayout(_cardRowWidget); + _cardRow->setAlignment(Qt::AlignLeft); + _cardRow->setSpacing(6); + _contentLayout->addWidget(_cardRowWidget); + } + + if (_revealIdx < cards.size()) { + auto* cw = makeFlipCard(cards[_revealIdx].toObject()); + _cardRow->addWidget(cw); + _revealIdx++; + } else { + if (cards.isEmpty()) { + auto* empty = new QLabel(QStringLiteral(" (无质疑牌)"), _content); + empty->setStyleSheet(QStringLiteral("color:%1;font-size:12px;background:transparent;").arg(S_DIM.name())); + _contentLayout->addWidget(empty); + } + _currentPlayerIdx++; + _revealIdx = 0; + } + } else if (_phase == 2) { + auto checks = _data["victory"].toObject()["checks"].toArray(); + if (_revealIdx >= checks.size()) { + _timer->stop(); + showPhaseResult(); + return; + } + auto vc = checks[_revealIdx].toObject(); + bool met = vc["met"].toBool(); + auto* label = new QLabel(QStringLiteral("优先%1 %2 [%3] → %4") + .arg(vc["priority"].toInt()) + .arg(vc["nickname"].toString()) + .arg(vc["card_name"].toString()) + .arg(met ? QStringLiteral("✓ 达成") : QStringLiteral("✗ 未达成")), + _content); + label->setStyleSheet(met + ? QStringLiteral("color:%1;font-size:14px;font-weight:bold;background:transparent;").arg(S_SUCCESS.name()) + : QStringLiteral("color:%1;font-size:14px;background:transparent;").arg(S_DIM.name())); + _contentLayout->addWidget(label); + _revealIdx++; + } +} + +void SettlementOverlay::showPhaseResult() { + _resultLabel->show(); + _nextBtn->show(); + + if (_phase == 0) { + bool ok = _data["harmony"].toObject()["success"].toBool(); + _resultLabel->setText(ok ? QStringLiteral("调和成功 ✓") : QStringLiteral("调和失败 ✗")); + _resultLabel->setStyleSheet(QStringLiteral("color:%1;font-size:24px;font-weight:bold;background:transparent;") + .arg(ok ? S_SUCCESS.name() : S_DANGER.name())); + } else if (_phase == 1) { + auto imprisoned = _data["challenge"].toObject()["imprisoned"].toArray(); + if (imprisoned.isEmpty()) { + _resultLabel->setText(QStringLiteral("无人被监禁")); + _resultLabel->setStyleSheet(QStringLiteral("color:%1;font-size:20px;font-weight:bold;background:transparent;").arg(S_DIM.name())); + } else { + QStringList names; + auto pp = _data["challenge"].toObject()["per_player"].toArray(); + QSet jailed; + for (const auto& v : imprisoned) jailed.insert(v.toString()); + for (const auto& pv : pp) + if (jailed.contains(pv.toObject()["player_id"].toString())) + names.append(pv.toObject()["nickname"].toString()); + _resultLabel->setText(QStringLiteral("🔒 被监禁: ") + names.join(QStringLiteral(", "))); + _resultLabel->setStyleSheet(QStringLiteral("color:%1;font-size:20px;font-weight:bold;background:transparent;").arg(S_DANGER.name())); + } + } else if (_phase == 2) { + auto v = _data["victory"].toObject(); + if (v["end_type"].toString() == "all_dead") { + _resultLabel->setText(QStringLiteral("全灭结局 — 没有任何人获胜")); + _resultLabel->setStyleSheet(QStringLiteral("color:%1;font-size:22px;font-weight:bold;background:transparent;").arg(S_DANGER.name())); + } else { + QStringList names; + for (const auto& wv : v["winners"].toArray()) + names.append(wv.toObject()["nickname"].toString()); + _resultLabel->setText(QStringLiteral("🏆 获胜: ") + names.join(QStringLiteral(", "))); + _resultLabel->setStyleSheet(QStringLiteral("color:%1;font-size:22px;font-weight:bold;background:transparent;").arg(S_GOLD.name())); + } + _nextBtn->setText(QStringLiteral("查看详情")); + } +} + +void SettlementOverlay::buildFinalPanel() { + _phaseTitle->setText(QStringLiteral("— 结算完毕 —")); + _phaseTitle->setStyleSheet(QStringLiteral("color:%1;font-size:22px;font-weight:bold;background:transparent;").arg(S_GOLD.name())); + _resultLabel->hide(); + _totalLabel->clear(); + + auto v = _data["victory"].toObject(); + QString endText; + if (v["end_type"].toString() == "all_dead") + endText = QStringLiteral("全灭结局"); + else { + QStringList n; + for (const auto& w : v["winners"].toArray()) n.append(w.toObject()["nickname"].toString()); + endText = QStringLiteral("获胜者: ") + n.join(", "); + } + auto* endLabel = new QLabel(endText, _content); + endLabel->setStyleSheet(QStringLiteral("color:%1;font-size:18px;font-weight:bold;background:transparent;").arg(S_GOLD.name())); + endLabel->setAlignment(Qt::AlignCenter); + _contentLayout->addWidget(endLabel); + _contentLayout->addSpacing(10); + + auto checks = v["checks"].toArray(); + for (const auto& cv : checks) { + auto c = cv.toObject(); + auto* row = new QLabel(QStringLiteral("%1 [%2] 优先%3 — %4") + .arg(c["nickname"].toString(), c["card_name"].toString()) + .arg(c["priority"].toInt()) + .arg(c["met"].toBool() ? QStringLiteral("✓") : QStringLiteral("✗")), + _content); + row->setStyleSheet(c["met"].toBool() + ? QStringLiteral("color:%1;font-size:13px;background:transparent;").arg(S_SUCCESS.name()) + : QStringLiteral("color:%1;font-size:13px;background:transparent;").arg(S_DIM.name())); + _contentLayout->addWidget(row); + } + _contentLayout->addStretch(); + + auto* btnRow = new QWidget(_content); + btnRow->setStyleSheet("background:transparent;"); + auto* bLay = new QHBoxLayout(btnRow); + bLay->setAlignment(Qt::AlignCenter); + bLay->setSpacing(16); + auto* backBtn = new ElaPushButton(QStringLiteral("返回等待室"), btnRow); + backBtn->setFixedSize(140, 38); + connect(backBtn, &ElaPushButton::clicked, this, [this]() { hide(); emit finished(); }); + bLay->addWidget(backBtn); + _contentLayout->addWidget(btnRow); + + _nextBtn->hide(); +} + +QWidget* SettlementOverlay::makeFlipCard(const QJsonObject& card) { + auto* col = new QWidget(); + col->setStyleSheet("background:transparent;"); + auto* vlay = new QVBoxLayout(col); + vlay->setContentsMargins(0, 0, 0, 0); + vlay->setSpacing(2); + vlay->setAlignment(Qt::AlignCenter); + + auto* cw = new CardWidget(col); + cw->setMini(true); + cw->setCardType(card["type_id"].toString()); + cw->setFaceUp(true); + cw->setCardEnabled(false); + vlay->addWidget(cw, 0, Qt::AlignCenter); + + const CardDef* def = CardDatabase::instance().getCardDef(card["type_id"].toString()); + QString name = def ? def->name : card["name"].toString(); + int mp = card["mp"].toInt(); + auto* lbl = new QLabel(QStringLiteral("%1 (%2)").arg(name).arg(mp), col); + lbl->setStyleSheet(QStringLiteral("color:%1;font-size:10px;background:transparent;").arg(S_TEXT.name())); + lbl->setAlignment(Qt::AlignCenter); + vlay->addWidget(lbl); + + return col; +} diff --git a/src/ui/SettlementOverlay.h b/src/ui/SettlementOverlay.h new file mode 100644 index 0000000..d574bc7 --- /dev/null +++ b/src/ui/SettlementOverlay.h @@ -0,0 +1,55 @@ +#ifndef SETTLEMENTOVERLAY_H +#define SETTLEMENTOVERLAY_H + +#include +#include +#include +#include + +class QVBoxLayout; +class QHBoxLayout; +class QLabel; +class ElaPushButton; +class CardWidget; +class QScrollArea; + +class SettlementOverlay : public QWidget { + Q_OBJECT +public: + explicit SettlementOverlay(QWidget* parent = nullptr); + void showSettlement(const QJsonObject& data); + +signals: + void finished(); + +protected: + void paintEvent(QPaintEvent* event) override; + +private: + void startPhase(int phase); + void revealNextCard(); + void showPhaseResult(); + void buildFinalPanel(); + QWidget* makeFlipCard(const QJsonObject& card); + + QJsonObject _data; + int _phase = 0; + int _revealIdx = 0; + int _runningTotal = 0; + int _currentPlayerIdx = 0; + QTimer* _timer = nullptr; + + QWidget* _panel = nullptr; + QScrollArea* _scroll = nullptr; + QWidget* _content = nullptr; + QVBoxLayout* _contentLayout = nullptr; + QLabel* _phaseTitle = nullptr; + QLabel* _totalLabel = nullptr; + QLabel* _resultLabel = nullptr; + QHBoxLayout* _cardRow = nullptr; + QWidget* _cardRowWidget = nullptr; + ElaPushButton* _nextBtn = nullptr; + QVector _tempWidgets; +}; + +#endif diff --git a/src/ui/VoiceSettingsDialog.cpp b/src/ui/VoiceSettingsDialog.cpp new file mode 100644 index 0000000..d8981e3 --- /dev/null +++ b/src/ui/VoiceSettingsDialog.cpp @@ -0,0 +1,97 @@ +#include "VoiceSettingsDialog.h" +#include "VoiceManager.h" +#include "ElaPushButton.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +VoiceSettingsDialog::VoiceSettingsDialog(QWidget* parent) + : QDialog(parent) +{ + setWindowTitle(QStringLiteral("语音设置")); + setFixedSize(400, 340); + setStyleSheet("QDialog{background:#1a1f2e;} QLabel{color:#e0e0e0;} QGroupBox{color:#8a8f9d;border:1px solid #3a3f4e;border-radius:6px;margin-top:8px;padding-top:14px;} QGroupBox::title{subcontrol-origin:margin;left:10px;}"); + + auto* lay = new QVBoxLayout(this); + lay->setSpacing(12); + + auto* inputGroup = new QGroupBox(QStringLiteral("输入设备 (麦克风)"), this); + auto* igLay = new QVBoxLayout(inputGroup); + _inputCombo = new QComboBox(inputGroup); + igLay->addWidget(_inputCombo); + lay->addWidget(inputGroup); + + auto* outputGroup = new QGroupBox(QStringLiteral("输出设备 (扬声器)"), this); + auto* ogLay = new QVBoxLayout(outputGroup); + _outputCombo = new QComboBox(outputGroup); + ogLay->addWidget(_outputCombo); + lay->addWidget(outputGroup); + + auto* modeGroup = new QGroupBox(QStringLiteral("说话模式"), this); + auto* mgLay = new QVBoxLayout(modeGroup); + _pttRadio = new QRadioButton(QStringLiteral("按住说话 (按住 V 键说话,松开停止)"), modeGroup); + _toggleRadio = new QRadioButton(QStringLiteral("按键切换 (按 V 键开始/停止说话)"), modeGroup); + _pttRadio->setStyleSheet("color:#e0e0e0;"); + _toggleRadio->setStyleSheet("color:#e0e0e0;"); + mgLay->addWidget(_pttRadio); + mgLay->addWidget(_toggleRadio); + lay->addWidget(modeGroup); + + _statusLabel = new QLabel(this); + _statusLabel->setStyleSheet("color:#5eb3e6;font-size:12px;"); + _statusLabel->setAlignment(Qt::AlignCenter); + lay->addWidget(_statusLabel); + + auto* btnRow = new QHBoxLayout(); + btnRow->addStretch(); + auto* applyBtn = new ElaPushButton(QStringLiteral("应用"), this); + applyBtn->setFixedSize(80, 34); + connect(applyBtn, &ElaPushButton::clicked, this, &VoiceSettingsDialog::apply); + auto* closeBtn = new ElaPushButton(QStringLiteral("关闭"), this); + closeBtn->setFixedSize(80, 34); + connect(closeBtn, &ElaPushButton::clicked, this, &QDialog::close); + btnRow->addWidget(applyBtn); + btnRow->addWidget(closeBtn); + lay->addLayout(btnRow); + + populate(); +} + +void VoiceSettingsDialog::populate() { + _inputCombo->clear(); + for (const auto& dev : QMediaDevices::audioInputs()) + _inputCombo->addItem(dev.description(), QVariant::fromValue(dev)); + if (_inputCombo->count() == 0) + _inputCombo->addItem(QStringLiteral("(无可用设备)")); + + _outputCombo->clear(); + for (const auto& dev : QMediaDevices::audioOutputs()) + _outputCombo->addItem(dev.description(), QVariant::fromValue(dev)); + if (_outputCombo->count() == 0) + _outputCombo->addItem(QStringLiteral("(无可用设备)")); + + auto& v = VoiceManager::instance(); + _pttRadio->setChecked(v.mode() == VoiceManager::PushToTalk); + _toggleRadio->setChecked(v.mode() == VoiceManager::Toggle); + + _statusLabel->setText(v.isActive() ? QStringLiteral("语音已启动") : QStringLiteral("语音未启动")); +} + +void VoiceSettingsDialog::apply() { + auto& v = VoiceManager::instance(); + v.setMode(_pttRadio->isChecked() ? VoiceManager::PushToTalk : VoiceManager::Toggle); + + auto inDev = _inputCombo->currentData().value(); + auto outDev = _outputCombo->currentData().value(); + if (!inDev.isNull()) v.setInputDevice(inDev); + if (!outDev.isNull()) v.setOutputDevice(outDev); + + if (!v.isActive()) v.start(); + _statusLabel->setText(QStringLiteral("已应用,语音已启动")); +} diff --git a/src/ui/VoiceSettingsDialog.h b/src/ui/VoiceSettingsDialog.h new file mode 100644 index 0000000..df5406f --- /dev/null +++ b/src/ui/VoiceSettingsDialog.h @@ -0,0 +1,26 @@ +#ifndef VOICESETTINGSDIALOG_H +#define VOICESETTINGSDIALOG_H + +#include + +class QComboBox; +class QRadioButton; +class QLabel; + +class VoiceSettingsDialog : public QDialog { + Q_OBJECT +public: + explicit VoiceSettingsDialog(QWidget* parent = nullptr); + +private: + void populate(); + void apply(); + + QComboBox* _inputCombo = nullptr; + QComboBox* _outputCombo = nullptr; + QRadioButton* _pttRadio = nullptr; + QRadioButton* _toggleRadio = nullptr; + QLabel* _statusLabel = nullptr; +}; + +#endif diff --git a/src/voice/AudioRingBuffer.h b/src/voice/AudioRingBuffer.h new file mode 100644 index 0000000..6b365c1 --- /dev/null +++ b/src/voice/AudioRingBuffer.h @@ -0,0 +1,51 @@ +#ifndef AUDIORINGBUFFER_H +#define AUDIORINGBUFFER_H + +#include +#include +#include + +class AudioRingBuffer : public QIODevice { + Q_OBJECT +public: + explicit AudioRingBuffer(int capacity = 32000, QObject* parent = nullptr) + : QIODevice(parent), _buf(capacity, 0), _capacity(capacity) { open(ReadWrite); } + + void feed(const QByteArray& data) { + QMutexLocker lock(&_mutex); + for (int i = 0; i < data.size(); ++i) { + _buf[_writePos % _capacity] = data[i]; + _writePos++; + if (_writePos - _readPos > _capacity) + _readPos = _writePos - _capacity; + } + } + + qint64 readData(char* data, qint64 maxlen) override { + QMutexLocker lock(&_mutex); + qint64 avail = _writePos - _readPos; + qint64 toRead = qMin(maxlen, avail); + for (qint64 i = 0; i < toRead; ++i) { + data[i] = _buf[_readPos % _capacity]; + _readPos++; + } + if (toRead < maxlen) + memset(data + toRead, 0, maxlen - toRead); + return maxlen; + } + + qint64 writeData(const char*, qint64) override { return 0; } + qint64 bytesAvailable() const override { + QMutexLocker lock(&_mutex); + return (_writePos - _readPos) + QIODevice::bytesAvailable(); + } + +private: + QByteArray _buf; + int _capacity; + qint64 _readPos = 0; + qint64 _writePos = 0; + mutable QMutex _mutex; +}; + +#endif diff --git a/src/voice/VoiceManager.cpp b/src/voice/VoiceManager.cpp new file mode 100644 index 0000000..be97199 --- /dev/null +++ b/src/voice/VoiceManager.cpp @@ -0,0 +1,152 @@ +#include "VoiceManager.h" +#include "AudioRingBuffer.h" +#include +#include +#include +#include +#include +#include +#include +#include + +static const int SAMPLE_RATE = 16000; +static const int FRAME_MS = 20; +static const int FRAME_BYTES = SAMPLE_RATE * 2 * FRAME_MS / 1000; + +VoiceManager& VoiceManager::instance() { + static VoiceManager mgr; + return mgr; +} + +VoiceManager::VoiceManager(QObject* parent) : QObject(parent) { + _inputDev = QMediaDevices::defaultAudioInput(); + _outputDev = QMediaDevices::defaultAudioOutput(); +} + +QAudioFormat VoiceManager::audioFormat() { + QAudioFormat fmt; + fmt.setSampleRate(SAMPLE_RATE); + fmt.setChannelCount(1); + fmt.setSampleFormat(QAudioFormat::Int16); + return fmt; +} + +void VoiceManager::start() { + if (_active) return; + restartCapture(); + _active = true; + _muted = true; + emit mutedChanged(true); +} + +void VoiceManager::stop() { + if (_capture) { + _capture->stop(); + _capture->deleteLater(); + _capture = nullptr; + _captureDev = nullptr; + } + for (auto& rs : _remotes) { + if (rs.sink) { rs.sink->stop(); rs.sink->deleteLater(); } + if (rs.buffer) rs.buffer->deleteLater(); + } + _remotes.clear(); + _active = false; + _muted = true; +} + +void VoiceManager::restartCapture() { + if (_capture) { + _capture->stop(); + _capture->deleteLater(); + _capture = nullptr; + } + if (_inputDev.isNull()) return; + auto fmt = audioFormat(); + _capture = new QAudioSource(_inputDev, fmt, this); + _capture->setBufferSize(FRAME_BYTES * 4); + _captureDev = _capture->start(); + if (_captureDev) + connect(_captureDev, &QIODevice::readyRead, this, &VoiceManager::onCaptureReady); +} + +void VoiceManager::setMuted(bool m) { + if (_muted == m) return; + _muted = m; + emit mutedChanged(m); +} + +void VoiceManager::setInputDevice(const QAudioDevice& dev) { + _inputDev = dev; + if (_active) restartCapture(); +} + +void VoiceManager::setOutputDevice(const QAudioDevice& dev) { + _outputDev = dev; +} + +void VoiceManager::keyDown() { + if (!_active) return; + if (_mode == PushToTalk) { + setMuted(false); + } else { + setMuted(!_muted); + } +} + +void VoiceManager::keyUp() { + if (!_active) return; + if (_mode == PushToTalk) { + setMuted(true); + } +} + +void VoiceManager::onCaptureReady() { + if (!_captureDev) return; + QByteArray data = _captureDev->readAll(); + if (data.isEmpty()) return; + + double energy = 0; + int samples = data.size() / 2; + const qint16* pcm = reinterpret_cast(data.constData()); + for (int i = 0; i < samples; ++i) + energy += double(pcm[i]) * pcm[i]; + energy = (samples > 0) ? std::sqrt(energy / samples) : 0; + + bool speaking = energy > 500; + if (speaking != _wasSpeaking) { + _wasSpeaking = speaking; + emit localSpeaking(speaking); + } + + if (!_muted && speaking) + emit audioFrame(data); +} + +void VoiceManager::processRemoteAudio(const QByteArray& frame) { + if (frame.size() < 3) return; + quint16 idLen = qFromBigEndian(reinterpret_cast(frame.constData())); + if (frame.size() < 2 + idLen) return; + QString senderId = QString::fromUtf8(frame.mid(2, idLen)); + QByteArray pcm = frame.mid(2 + idLen); + + if (!_remotes.contains(senderId)) { + auto fmt = audioFormat(); + auto* sink = new QAudioSink(_outputDev, fmt, this); + auto* buf = new AudioRingBuffer(FRAME_BYTES * 50, this); + sink->start(buf); + _remotes[senderId] = {sink, buf, 0}; + } + + auto& rs = _remotes[senderId]; + rs.buffer->feed(pcm); + rs.lastRecv = QDateTime::currentMSecsSinceEpoch(); + + emit remoteSpeaking(senderId, true); + QTimer::singleShot(300, this, [this, senderId]() { + if (_remotes.contains(senderId)) { + qint64 elapsed = QDateTime::currentMSecsSinceEpoch() - _remotes[senderId].lastRecv; + if (elapsed >= 280) emit remoteSpeaking(senderId, false); + } + }); +} diff --git a/src/voice/VoiceManager.h b/src/voice/VoiceManager.h new file mode 100644 index 0000000..66e65ff --- /dev/null +++ b/src/voice/VoiceManager.h @@ -0,0 +1,68 @@ +#ifndef VOICEMANAGER_H +#define VOICEMANAGER_H + +#include +#include +#include +#include + +class QAudioSource; +class QAudioSink; +class QIODevice; +class AudioRingBuffer; + +class VoiceManager : public QObject { + Q_OBJECT +public: + enum VoiceMode { PushToTalk, Toggle }; + + static VoiceManager& instance(); + + void start(); + void stop(); + + void setMuted(bool m); + bool isMuted() const { return _muted; } + bool isActive() const { return _active; } + + void setMode(VoiceMode m) { _mode = m; } + VoiceMode mode() const { return _mode; } + + void setInputDevice(const QAudioDevice& dev); + void setOutputDevice(const QAudioDevice& dev); + + void keyDown(); + void keyUp(); + + void processRemoteAudio(const QByteArray& frame); + static QAudioFormat audioFormat(); + +signals: + void audioFrame(const QByteArray& pcm); + void localSpeaking(bool speaking); + void remoteSpeaking(const QString& playerId, bool speaking); + void mutedChanged(bool muted); + +private: + explicit VoiceManager(QObject* parent = nullptr); + void onCaptureReady(); + void restartCapture(); + + QAudioSource* _capture = nullptr; + QIODevice* _captureDev = nullptr; + QAudioDevice _inputDev; + QAudioDevice _outputDev; + bool _muted = true; + bool _active = false; + bool _wasSpeaking = false; + VoiceMode _mode = PushToTalk; + + struct RemoteStream { + QAudioSink* sink = nullptr; + AudioRingBuffer* buffer = nullptr; + qint64 lastRecv = 0; + }; + QMap _remotes; +}; + +#endif