From d6f6fb6cdff8477b0971730333a77988194cf3c2 Mon Sep 17 00:00:00 2001 From: huxingyi Date: Sun, 18 Oct 2020 14:33:15 +0930 Subject: [PATCH] Add metallic and roughness settings for part Part deform painter got removed. Experiment color painter introduced, but not enabled yet. --- ACKNOWLEDGEMENTS.html | 20 +- dust3d.pro | 8 +- shaders/default.core.frag | 12 +- src/document.cpp | 132 ++- src/document.h | 21 +- src/documentwindow.cpp | 119 ++- src/documentwindow.h | 2 + src/floatnumberwidget.cpp | 5 + src/floatnumberwidget.h | 3 +- src/meshgenerator.cpp | 12 + src/model.cpp | 2 - src/modelmeshbinder.cpp | 24 +- src/mousepicker.cpp | 217 ----- src/mousepicker.h | 52 -- src/outcome.h | 2 + src/partpreviewimagesgenerator.cpp | 1 + src/parttreewidget.cpp | 22 + src/parttreewidget.h | 2 + src/partwidget.cpp | 59 +- src/partwidget.h | 2 + src/shortcuts.cpp | 2 +- src/skeletondocument.h | 12 + src/texturegenerator.cpp | 60 +- src/texturetype.h | 2 +- src/util.cpp | 84 ++ src/util.h | 15 +- src/vertexcolorpainter.cpp | 227 +++++ src/vertexcolorpainter.h | 81 ++ src/voxelgrid.cpp | 1 + src/voxelgrid.h | 85 ++ thirdparty/Qt-Color-Widgets/.gitignore | 1 + thirdparty/Qt-Color-Widgets/CMakeLists.txt | 171 ++++ thirdparty/Qt-Color-Widgets/COPYING | 165 ++++ thirdparty/Qt-Color-Widgets/LICENSE-EXCEPTION | 14 + .../Qt-Color-Widgets/QtColorWidgets.pc.in | 10 + thirdparty/Qt-Color-Widgets/README.md | 66 ++ .../cmake/QtColorWidgets-config.cmake | 1 + .../Qt-Color-Widgets/cmake/install.cmake | 88 ++ .../cmake/modules/ProjectVersioning.cmake | 80 ++ .../modules/ProjectVersioning/version.c.in | 34 + .../modules/ProjectVersioning/version.h.in | 23 + .../cmake/modules/VersionInfo.in | 113 +++ .../cmake/modules/VersionResource.rc | 66 ++ .../modules/generate_product_version.cmake | 136 +++ .../cmake/qt-configuration.cmake | 58 ++ .../Qt-Color-Widgets/cmake/versioning.cmake | 43 + thirdparty/Qt-Color-Widgets/color_widgets.pri | 77 ++ thirdparty/Qt-Color-Widgets/color_widgets.pro | 53 ++ .../CMakeLists.txt | 178 ++++ .../color_2d_slider_plugin.cpp | 90 ++ .../color_2d_slider_plugin.hpp | 57 ++ .../color_line_edit_plugin.cpp | 87 ++ .../color_line_edit_plugin.hpp | 58 ++ .../color_list_plugin.cpp | 88 ++ .../color_list_plugin.hpp | 56 ++ .../color_palette_widget_plugin.cpp | 120 +++ .../color_palette_widget_plugin.hpp | 57 ++ .../color_preview_plugin.cpp | 95 +++ .../color_preview_plugin.hpp | 55 ++ .../color_selector_plugin.cpp | 92 ++ .../color_selector_plugin.hpp | 55 ++ .../color_wheel_plugin.cpp | 97 +++ .../color_wheel_plugin.hpp | 57 ++ .../color_widget_plugin_collection.cpp | 56 ++ .../color_widget_plugin_collection.hpp | 43 + .../gradient_editor_plugin.cpp | 98 +++ .../gradient_editor_plugin.hpp | 58 ++ .../gradient_slider_plugin.cpp | 102 +++ .../gradient_slider_plugin.hpp | 34 + .../hue_slider_plugin.cpp | 98 +++ .../hue_slider_plugin.hpp | 34 + .../color_widgets_designer_plugin/new_plugin | 236 ++++++ .../swatch_plugin.cpp | 106 +++ .../swatch_plugin.hpp | 57 ++ .../Qt-Color-Widgets/gallery/CMakeLists.txt | 48 ++ .../gallery/Color2DSlider.png | Bin 0 -> 5904 bytes .../Qt-Color-Widgets/gallery/ColorDialog.png | Bin 0 -> 45295 bytes .../gallery/ColorLineEdit.png | Bin 0 -> 1434 bytes .../gallery/ColorLineEdit_with_color.png | Bin 0 -> 1400 bytes .../gallery/ColorListWidget.png | Bin 0 -> 6095 bytes .../gallery/ColorPaletteWidget.png | Bin 0 -> 7246 bytes .../gallery/ColorPaletteWidget_readonly.png | Bin 0 -> 3023 bytes .../Qt-Color-Widgets/gallery/ColorPreview.png | Bin 0 -> 243 bytes .../Qt-Color-Widgets/gallery/ColorWheel.png | Bin 0 -> 22791 bytes .../gallery/GradientEditor.png | Bin 0 -> 230 bytes .../gallery/GradientListModel_combo.png | Bin 0 -> 1749 bytes .../gallery/GradientListModel_view.png | Bin 0 -> 4232 bytes .../Qt-Color-Widgets/gallery/HueSlider.png | Bin 0 -> 249 bytes thirdparty/Qt-Color-Widgets/gallery/README.md | 91 ++ .../Qt-Color-Widgets/gallery/Swatch.png | Bin 0 -> 951 bytes .../Qt-Color-Widgets/gallery/screenshot.cpp | 213 +++++ .../include/QtColorWidgets/AbstractWidgetList | 1 + .../include/QtColorWidgets/BoundColorSelector | 1 + .../include/QtColorWidgets/CMakeLists.txt | 37 + .../include/QtColorWidgets/ColorDelegate | 1 + .../include/QtColorWidgets/ColorDialog | 1 + .../include/QtColorWidgets/ColorListWidget | 1 + .../include/QtColorWidgets/ColorPreview | 1 + .../include/QtColorWidgets/ColorSelector | 1 + .../include/QtColorWidgets/ColorWheel | 1 + .../include/QtColorWidgets/GradientEditor | 1 + .../include/QtColorWidgets/GradientListModel | 1 + .../include/QtColorWidgets/GradientSlider | 1 + .../include/QtColorWidgets/HarmonyColorWheel | 1 + .../include/QtColorWidgets/HueSlider | 1 + .../QtColorWidgets/abstract_widget_list.hpp | 110 +++ .../QtColorWidgets/bound_color_selector.hpp | 44 + .../QtColorWidgets/color_2d_slider.hpp | 128 +++ .../include/QtColorWidgets/color_delegate.hpp | 54 ++ .../include/QtColorWidgets/color_dialog.hpp | 162 ++++ .../QtColorWidgets/color_line_edit.hpp | 98 +++ .../QtColorWidgets/color_list_widget.hpp | 78 ++ .../include/QtColorWidgets/color_names.hpp | 57 ++ .../include/QtColorWidgets/color_palette.hpp | 229 +++++ .../QtColorWidgets/color_palette_model.hpp | 140 ++++ .../QtColorWidgets/color_palette_widget.hpp | 187 +++++ .../include/QtColorWidgets/color_preview.hpp | 110 +++ .../include/QtColorWidgets/color_selector.hpp | 97 +++ .../include/QtColorWidgets/color_utils.hpp | 77 ++ .../include/QtColorWidgets/color_wheel.hpp | 176 ++++ .../QtColorWidgets/color_wheel_private.hpp | 284 +++++++ .../QtColorWidgets/colorwidgets_global.hpp | 16 + .../QtColorWidgets/gradient_delegate.hpp | 103 +++ .../QtColorWidgets/gradient_editor.hpp | 123 +++ .../QtColorWidgets/gradient_helper.hpp | 98 +++ .../QtColorWidgets/gradient_list_model.hpp | 173 ++++ .../QtColorWidgets/gradient_slider.hpp | 117 +++ .../QtColorWidgets/harmony_color_wheel.hpp | 95 +++ .../include/QtColorWidgets/hue_slider.hpp | 100 +++ .../include/QtColorWidgets/swatch.hpp | 194 +++++ thirdparty/Qt-Color-Widgets/refactor.sh | 99 +++ .../resources/QtColorWidgets/CMakeLists.txt | 16 + .../resources/QtColorWidgets/alphaback.png | Bin 0 -> 1079 bytes .../QtColorWidgets/color_widgets.qrc | 5 + .../src/QtColorWidgets/CMakeLists.txt | 37 + .../QtColorWidgets/abstract_widget_list.cpp | 175 ++++ .../QtColorWidgets/bound_color_selector.cpp | 38 + .../src/QtColorWidgets/color_2d_slider.cpp | 267 ++++++ .../src/QtColorWidgets/color_delegate.cpp | 94 +++ .../src/QtColorWidgets/color_dialog.cpp | 378 +++++++++ .../src/QtColorWidgets/color_dialog.ui | 700 ++++++++++++++++ .../src/QtColorWidgets/color_line_edit.cpp | 211 +++++ .../src/QtColorWidgets/color_list_widget.cpp | 149 ++++ .../src/QtColorWidgets/color_names.cpp | 89 ++ .../src/QtColorWidgets/color_palette.cpp | 498 +++++++++++ .../QtColorWidgets/color_palette_model.cpp | 329 ++++++++ .../QtColorWidgets/color_palette_widget.cpp | 416 +++++++++ .../QtColorWidgets/color_palette_widget.ui | 205 +++++ .../src/QtColorWidgets/color_preview.cpp | 185 ++++ .../src/QtColorWidgets/color_selector.cpp | 183 ++++ .../src/QtColorWidgets/color_utils.cpp | 83 ++ .../src/QtColorWidgets/color_wheel.cpp | 364 ++++++++ .../src/QtColorWidgets/gradient_editor.cpp | 575 +++++++++++++ .../QtColorWidgets/gradient_list_model.cpp | 319 +++++++ .../src/QtColorWidgets/gradient_slider.cpp | 276 ++++++ .../QtColorWidgets/harmony_color_wheel.cpp | 207 +++++ .../src/QtColorWidgets/hue_slider.cpp | 153 ++++ .../src/QtColorWidgets/swatch.cpp | 792 ++++++++++++++++++ 158 files changed, 14571 insertions(+), 373 deletions(-) delete mode 100644 src/mousepicker.cpp delete mode 100644 src/mousepicker.h create mode 100644 src/vertexcolorpainter.cpp create mode 100644 src/vertexcolorpainter.h create mode 100644 src/voxelgrid.cpp create mode 100644 src/voxelgrid.h create mode 100644 thirdparty/Qt-Color-Widgets/.gitignore create mode 100644 thirdparty/Qt-Color-Widgets/CMakeLists.txt create mode 100644 thirdparty/Qt-Color-Widgets/COPYING create mode 100644 thirdparty/Qt-Color-Widgets/LICENSE-EXCEPTION create mode 100644 thirdparty/Qt-Color-Widgets/QtColorWidgets.pc.in create mode 100644 thirdparty/Qt-Color-Widgets/README.md create mode 100644 thirdparty/Qt-Color-Widgets/cmake/QtColorWidgets-config.cmake create mode 100644 thirdparty/Qt-Color-Widgets/cmake/install.cmake create mode 100644 thirdparty/Qt-Color-Widgets/cmake/modules/ProjectVersioning.cmake create mode 100644 thirdparty/Qt-Color-Widgets/cmake/modules/ProjectVersioning/version.c.in create mode 100644 thirdparty/Qt-Color-Widgets/cmake/modules/ProjectVersioning/version.h.in create mode 100644 thirdparty/Qt-Color-Widgets/cmake/modules/VersionInfo.in create mode 100644 thirdparty/Qt-Color-Widgets/cmake/modules/VersionResource.rc create mode 100644 thirdparty/Qt-Color-Widgets/cmake/modules/generate_product_version.cmake create mode 100644 thirdparty/Qt-Color-Widgets/cmake/qt-configuration.cmake create mode 100644 thirdparty/Qt-Color-Widgets/cmake/versioning.cmake create mode 100644 thirdparty/Qt-Color-Widgets/color_widgets.pri create mode 100644 thirdparty/Qt-Color-Widgets/color_widgets.pro create mode 100644 thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/CMakeLists.txt create mode 100644 thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_2d_slider_plugin.cpp create mode 100644 thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_2d_slider_plugin.hpp create mode 100644 thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_line_edit_plugin.cpp create mode 100644 thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_line_edit_plugin.hpp create mode 100644 thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_list_plugin.cpp create mode 100644 thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_list_plugin.hpp create mode 100644 thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_palette_widget_plugin.cpp create mode 100644 thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_palette_widget_plugin.hpp create mode 100644 thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_preview_plugin.cpp create mode 100644 thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_preview_plugin.hpp create mode 100644 thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_selector_plugin.cpp create mode 100644 thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_selector_plugin.hpp create mode 100644 thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_wheel_plugin.cpp create mode 100644 thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_wheel_plugin.hpp create mode 100644 thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_widget_plugin_collection.cpp create mode 100644 thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_widget_plugin_collection.hpp create mode 100644 thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/gradient_editor_plugin.cpp create mode 100644 thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/gradient_editor_plugin.hpp create mode 100644 thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/gradient_slider_plugin.cpp create mode 100644 thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/gradient_slider_plugin.hpp create mode 100644 thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/hue_slider_plugin.cpp create mode 100644 thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/hue_slider_plugin.hpp create mode 100644 thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/new_plugin create mode 100644 thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/swatch_plugin.cpp create mode 100644 thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/swatch_plugin.hpp create mode 100644 thirdparty/Qt-Color-Widgets/gallery/CMakeLists.txt create mode 100644 thirdparty/Qt-Color-Widgets/gallery/Color2DSlider.png create mode 100644 thirdparty/Qt-Color-Widgets/gallery/ColorDialog.png create mode 100644 thirdparty/Qt-Color-Widgets/gallery/ColorLineEdit.png create mode 100644 thirdparty/Qt-Color-Widgets/gallery/ColorLineEdit_with_color.png create mode 100644 thirdparty/Qt-Color-Widgets/gallery/ColorListWidget.png create mode 100644 thirdparty/Qt-Color-Widgets/gallery/ColorPaletteWidget.png create mode 100644 thirdparty/Qt-Color-Widgets/gallery/ColorPaletteWidget_readonly.png create mode 100644 thirdparty/Qt-Color-Widgets/gallery/ColorPreview.png create mode 100644 thirdparty/Qt-Color-Widgets/gallery/ColorWheel.png create mode 100644 thirdparty/Qt-Color-Widgets/gallery/GradientEditor.png create mode 100644 thirdparty/Qt-Color-Widgets/gallery/GradientListModel_combo.png create mode 100644 thirdparty/Qt-Color-Widgets/gallery/GradientListModel_view.png create mode 100644 thirdparty/Qt-Color-Widgets/gallery/HueSlider.png create mode 100644 thirdparty/Qt-Color-Widgets/gallery/README.md create mode 100644 thirdparty/Qt-Color-Widgets/gallery/Swatch.png create mode 100644 thirdparty/Qt-Color-Widgets/gallery/screenshot.cpp create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/AbstractWidgetList create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/BoundColorSelector create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/CMakeLists.txt create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/ColorDelegate create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/ColorDialog create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/ColorListWidget create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/ColorPreview create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/ColorSelector create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/ColorWheel create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/GradientEditor create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/GradientListModel create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/GradientSlider create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/HarmonyColorWheel create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/HueSlider create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/abstract_widget_list.hpp create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/bound_color_selector.hpp create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_2d_slider.hpp create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_delegate.hpp create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_dialog.hpp create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_line_edit.hpp create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_list_widget.hpp create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_names.hpp create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_palette.hpp create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_palette_model.hpp create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_palette_widget.hpp create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_preview.hpp create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_selector.hpp create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_utils.hpp create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_wheel.hpp create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_wheel_private.hpp create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/colorwidgets_global.hpp create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/gradient_delegate.hpp create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/gradient_editor.hpp create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/gradient_helper.hpp create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/gradient_list_model.hpp create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/gradient_slider.hpp create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/harmony_color_wheel.hpp create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/hue_slider.hpp create mode 100644 thirdparty/Qt-Color-Widgets/include/QtColorWidgets/swatch.hpp create mode 100644 thirdparty/Qt-Color-Widgets/refactor.sh create mode 100644 thirdparty/Qt-Color-Widgets/resources/QtColorWidgets/CMakeLists.txt create mode 100644 thirdparty/Qt-Color-Widgets/resources/QtColorWidgets/alphaback.png create mode 100644 thirdparty/Qt-Color-Widgets/resources/QtColorWidgets/color_widgets.qrc create mode 100644 thirdparty/Qt-Color-Widgets/src/QtColorWidgets/CMakeLists.txt create mode 100644 thirdparty/Qt-Color-Widgets/src/QtColorWidgets/abstract_widget_list.cpp create mode 100644 thirdparty/Qt-Color-Widgets/src/QtColorWidgets/bound_color_selector.cpp create mode 100644 thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_2d_slider.cpp create mode 100644 thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_delegate.cpp create mode 100644 thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_dialog.cpp create mode 100644 thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_dialog.ui create mode 100644 thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_line_edit.cpp create mode 100644 thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_list_widget.cpp create mode 100644 thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_names.cpp create mode 100644 thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_palette.cpp create mode 100644 thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_palette_model.cpp create mode 100644 thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_palette_widget.cpp create mode 100644 thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_palette_widget.ui create mode 100644 thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_preview.cpp create mode 100644 thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_selector.cpp create mode 100644 thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_utils.cpp create mode 100644 thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_wheel.cpp create mode 100644 thirdparty/Qt-Color-Widgets/src/QtColorWidgets/gradient_editor.cpp create mode 100644 thirdparty/Qt-Color-Widgets/src/QtColorWidgets/gradient_list_model.cpp create mode 100644 thirdparty/Qt-Color-Widgets/src/QtColorWidgets/gradient_slider.cpp create mode 100644 thirdparty/Qt-Color-Widgets/src/QtColorWidgets/harmony_color_wheel.cpp create mode 100644 thirdparty/Qt-Color-Widgets/src/QtColorWidgets/hue_slider.cpp create mode 100644 thirdparty/Qt-Color-Widgets/src/QtColorWidgets/swatch.cpp diff --git a/ACKNOWLEDGEMENTS.html b/ACKNOWLEDGEMENTS.html index 28ebb99e..66518d47 100644 --- a/ACKNOWLEDGEMENTS.html +++ b/ACKNOWLEDGEMENTS.html @@ -1345,4 +1345,22 @@ https://www.reddit.com/r/gamedev/comments/5iuf3h/i_am_writting_a_3d_monster_mode ** $QT_END_LICENSE$ ** ****************************************************************************/ - \ No newline at end of file + + +

Qt-Color-Widgets

+
+    Linking this library statically or dynamically with other modules is making a
+    combined work based on this library. Thus, the terms and conditions of the
+    GNU Lesser General Public License version 3 cover the whole combination.
+
+    As a special exception, the copyright holders of this library give you
+    permission to combine this library with independent
+    modules to produce an executable, and to copy and distribute the resulting
+    executable under terms of any of the GNU General Public licenses, as published
+    by the Free Software Foundation, provided that you also meet,
+    for each linked independent module, the terms and conditions of the license of
+    that module. An independent module is a module which is not derived from or
+    based on this library. If you modify this library, you may extend this
+    exception to your version of the library, but you are not obliged to do so.
+    If you do not wish to do so, delete this exception statement from your version.
+
diff --git a/dust3d.pro b/dust3d.pro index f79619a6..a0b23060 100644 --- a/dust3d.pro +++ b/dust3d.pro @@ -132,6 +132,7 @@ DEFINES += NOMINMAX include(thirdparty/QtAwesome/QtAwesome/QtAwesome.pri) include(thirdparty/qtsingleapplication/src/qtsingleapplication.pri) +include(thirdparty/Qt-Color-Widgets/color_widgets.pri) INCLUDEPATH += src @@ -443,8 +444,11 @@ HEADERS += src/intnumberwidget.h SOURCES += src/imagepreviewwidget.cpp HEADERS += src/imagepreviewwidget.h -SOURCES += src/mousepicker.cpp -HEADERS += src/mousepicker.h +SOURCES += src/vertexcolorpainter.cpp +HEADERS += src/vertexcolorpainter.h + +SOURCES += src/voxelgrid.cpp +HEADERS += src/voxelgrid.h SOURCES += src/paintmode.cpp HEADERS += src/paintmode.h diff --git a/shaders/default.core.frag b/shaders/default.core.frag index a4c09eae..5014ea7b 100644 --- a/shaders/default.core.frag +++ b/shaders/default.core.frag @@ -573,16 +573,16 @@ void main() lights[0].type = TYPE_POINT; lights[0].position = firstLightPos; lights[0].color = vec3(1.0, 1.0, 1.0); - lights[0].intensity = 3.0; - lights[0].constantAttenuation = 0.0; + lights[0].intensity = 1.0; + lights[0].constantAttenuation = 1.0; lights[0].linearAttenuation = 0.0; - lights[0].quadraticAttenuation = 0.0; + lights[0].quadraticAttenuation = 0.0025; // Fill light lights[1].type = TYPE_POINT; lights[1].position = secondLightPos; lights[1].color = vec3(1.0, 1.0, 1.0); - lights[1].intensity = 1.0; + lights[1].intensity = 0.1; lights[1].constantAttenuation = 0.0; lights[1].linearAttenuation = 0.0; lights[1].quadraticAttenuation = 0.0; @@ -591,7 +591,7 @@ void main() lights[2].type = TYPE_POINT; lights[2].position = thirdLightPos; lights[2].color = vec3(1.0, 1.0, 1.0); - lights[2].intensity = 0.5; + lights[2].intensity = 0.05; lights[2].constantAttenuation = 0.0; lights[2].linearAttenuation = 0.0; lights[2].quadraticAttenuation = 0.0; @@ -635,8 +635,8 @@ void main() ambientOcclusion = texture(metalnessRoughnessAmbientOcclusionMapId, vertTexCoord).r; } + roughness = min(0.99, roughness); if (environmentIrradianceMapEnabled != 1) { - roughness = min(0.99, roughness); metalness = min(0.99, metalness); } diff --git a/src/document.cpp b/src/document.cpp index 917481bc..32c1c62b 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -17,7 +17,6 @@ #include "motionsgenerator.h" #include "skeletonside.h" #include "scriptrunner.h" -#include "mousepicker.h" #include "imageforever.h" #include "contourtopartconverter.h" @@ -41,10 +40,12 @@ Document::Document() : rigType(RigType::None), weldEnabled(true), polyCount(PolyCount::Original), + brushColor(Qt::white), // private m_isResultMeshObsolete(false), m_meshGenerator(nullptr), m_resultMesh(nullptr), + m_paintedMesh(nullptr), m_resultMeshCutFaceTransforms(nullptr), m_resultMeshNodesCutFaces(nullptr), m_isMeshGenerationSucceed(true), @@ -73,11 +74,12 @@ Document::Document() : m_nextMeshGenerationId(1), m_scriptRunner(nullptr), m_isScriptResultObsolete(false), - m_mousePicker(nullptr), + m_vertexColorPainter(nullptr), m_isMouseTargetResultObsolete(false), m_paintMode(PaintMode::None), - m_mousePickRadius(0.2), - m_saveNextPaintSnapshot(false) + m_mousePickRadius(0.05), + m_saveNextPaintSnapshot(false), + m_vertexColorVoxelGrid(nullptr) { connect(&Preferences::instance(), &Preferences::partColorChanged, this, &Document::applyPreferencePartColorChange); connect(&Preferences::instance(), &Preferences::flatShadingChanged, this, &Document::applyPreferenceFlatShadingChange); @@ -103,6 +105,7 @@ void Document::applyPreferenceTextureSizeChange() Document::~Document() { delete m_resultMesh; + delete m_paintedMesh; delete m_resultMeshCutFaceTransforms; delete m_resultMeshNodesCutFaces; delete m_postProcessedOutcome; @@ -1029,7 +1032,7 @@ void Document::setPaintMode(PaintMode mode) m_paintMode = mode; emit paintModeChanged(); - doPickMouseTarget(); + paintVertexColors(); } void Document::joinNodeAndNeiborsToGroup(std::vector *group, QUuid nodeId, std::set *visitMap, QUuid noUseEdgeId) @@ -1170,6 +1173,10 @@ void Document::toSnapshot(Snapshot *snapshot, const std::set &limitNodeId part["color"] = partIt.second.color.name(QColor::HexArgb); if (partIt.second.colorSolubilityAdjusted()) part["colorSolubility"] = QString::number(partIt.second.colorSolubility); + if (partIt.second.metalnessAdjusted()) + part["metalness"] = QString::number(partIt.second.metalness); + if (partIt.second.roughnessAdjusted()) + part["roughness"] = QString::number(partIt.second.roughness); if (partIt.second.deformThicknessAdjusted()) part["deformThickness"] = QString::number(partIt.second.deformThickness); if (partIt.second.deformWidthAdjusted()) @@ -1590,6 +1597,12 @@ void Document::addFromSnapshot(const Snapshot &snapshot, enum SnapshotSource sou const auto &colorSolubilityIt = partKv.second.find("colorSolubility"); if (colorSolubilityIt != partKv.second.end()) part.colorSolubility = colorSolubilityIt->second.toFloat(); + const auto &metalnessIt = partKv.second.find("metalness"); + if (metalnessIt != partKv.second.end()) + part.metalness = metalnessIt->second.toFloat(); + const auto &roughnessIt = partKv.second.find("roughness"); + if (roughnessIt != partKv.second.end()) + part.roughness = roughnessIt->second.toFloat(); const auto &deformThicknessIt = partKv.second.find("deformThickness"); if (deformThicknessIt != partKv.second.end()) part.setDeformThickness(deformThicknessIt->second.toFloat()); @@ -1905,6 +1918,14 @@ Model *Document::takeResultMesh() return resultMesh; } +Model *Document::takePaintedMesh() +{ + if (nullptr == m_paintedMesh) + return nullptr; + Model *paintedMesh = new Model(*m_paintedMesh); + return paintedMesh; +} + bool Document::isMeshGenerationSucceed() { return m_isMeshGenerationSucceed; @@ -2205,12 +2226,12 @@ void Document::pickMouseTarget(const QVector3D &nearPosition, const QVector3D &f m_mouseRayNear = nearPosition; m_mouseRayFar = farPosition; - doPickMouseTarget(); + paintVertexColors(); } -void Document::doPickMouseTarget() +void Document::paintVertexColors() { - if (nullptr != m_mousePicker) { + if (nullptr != m_vertexColorPainter) { m_isMouseTargetResultObsolete = true; return; } @@ -2225,44 +2246,41 @@ void Document::doPickMouseTarget() //qDebug() << "Mouse picking.."; QThread *thread = new QThread; - m_mousePicker = new MousePicker(*m_currentOutcome, m_mouseRayNear, m_mouseRayFar); - - std::map paintImages; - for (const auto &it: partMap) { - if (!it.second.deformMapImageId.isNull()) { - paintImages[it.first] = it.second.deformMapImageId; - } - } + m_vertexColorPainter = new VertexColorPainter(*m_currentOutcome, m_mouseRayNear, m_mouseRayFar); + m_vertexColorPainter->setBrushColor(brushColor); + m_vertexColorPainter->setBrushMetalness(brushMetalness); + m_vertexColorPainter->setBrushRoughness(brushRoughness); if (SkeletonDocumentEditMode::Paint == editMode) { - m_mousePicker->setPaintImages(paintImages); - m_mousePicker->setPaintMode(m_paintMode); - m_mousePicker->setRadius(m_mousePickRadius); - m_mousePicker->setMaskNodeIds(m_mousePickMaskNodeIds); + if (nullptr == m_vertexColorVoxelGrid) { + m_vertexColorVoxelGrid = new VoxelGrid(); + } + m_vertexColorPainter->setVoxelGrid(m_vertexColorVoxelGrid); + m_vertexColorPainter->setPaintMode(m_paintMode); + m_vertexColorPainter->setRadius(m_mousePickRadius); + m_vertexColorPainter->setMaskNodeIds(m_mousePickMaskNodeIds); } - m_mousePicker->moveToThread(thread); - connect(thread, &QThread::started, m_mousePicker, &MousePicker::process); - connect(m_mousePicker, &MousePicker::finished, this, &Document::mouseTargetReady); - connect(m_mousePicker, &MousePicker::finished, thread, &QThread::quit); + m_vertexColorPainter->moveToThread(thread); + connect(thread, &QThread::started, m_vertexColorPainter, &VertexColorPainter::process); + connect(m_vertexColorPainter, &VertexColorPainter::finished, this, &Document::vertexColorsReady); + connect(m_vertexColorPainter, &VertexColorPainter::finished, thread, &QThread::quit); connect(thread, &QThread::finished, thread, &QThread::deleteLater); thread->start(); } -void Document::mouseTargetReady() +void Document::vertexColorsReady() { - m_mouseTargetPosition = m_mousePicker->targetPosition(); - const auto &changedPartIds = m_mousePicker->changedPartIds(); - for (const auto &it: m_mousePicker->resultPaintImages()) { - const auto &partId = it.first; - if (changedPartIds.find(partId) == changedPartIds.end()) - continue; - const auto &imageId = it.second; - m_intermediatePaintImageIds.insert(imageId); - setPartDeformMapImageId(partId, imageId); + m_mouseTargetPosition = m_vertexColorPainter->targetPosition(); + + Model *model = m_vertexColorPainter->takePaintedModel(); + if (nullptr != model) { + delete m_paintedMesh; + m_paintedMesh = model; + emit paintedMeshChanged(); } - delete m_mousePicker; - m_mousePicker = nullptr; + delete m_vertexColorPainter; + m_vertexColorPainter = nullptr; if (!m_isMouseTargetResultObsolete && m_saveNextPaintSnapshot) { m_saveNextPaintSnapshot = false; @@ -3064,6 +3082,36 @@ void Document::setPartColorSolubility(QUuid partId, float solubility) emit skeletonChanged(); } +void Document::setPartMetalness(QUuid partId, float metalness) +{ + auto part = partMap.find(partId); + if (part == partMap.end()) { + qDebug() << "Part not found:" << partId; + return; + } + if (qFuzzyCompare(part->second.metalness, metalness)) + return; + part->second.metalness = metalness; + part->second.dirty = true; + emit partMetalnessChanged(partId); + emit skeletonChanged(); +} + +void Document::setPartRoughness(QUuid partId, float roughness) +{ + auto part = partMap.find(partId); + if (part == partMap.end()) { + qDebug() << "Part not found:" << partId; + return; + } + if (qFuzzyCompare(part->second.roughness, roughness)) + return; + part->second.roughness = roughness; + part->second.dirty = true; + emit partRoughnessChanged(partId); + emit skeletonChanged(); +} + void Document::setPartHollowThickness(QUuid partId, float hollowThickness) { auto part = partMap.find(partId); @@ -4086,19 +4134,11 @@ void Document::startPaint(void) void Document::stopPaint(void) { - if (m_mousePicker || m_isMouseTargetResultObsolete) { + if (m_vertexColorPainter || m_isMouseTargetResultObsolete) { m_saveNextPaintSnapshot = true; return; } - saveSnapshot(); - for (const auto &it: partMap) { - m_intermediatePaintImageIds.erase(it.second.deformMapImageId); - } - for (const auto &it: m_intermediatePaintImageIds) { - //qDebug() << "Remove intermediate image:" << it; - ImageForever::remove(it); - } - m_intermediatePaintImageIds.clear(); + //saveSnapshot(); } void Document::setMousePickMaskNodeIds(const std::set &nodeIds) diff --git a/src/document.h b/src/document.h index 2efd8bc5..30dd674d 100644 --- a/src/document.h +++ b/src/document.h @@ -31,11 +31,12 @@ #include "proceduralanimation.h" #include "componentlayer.h" #include "clothforce.h" +#include "voxelgrid.h" +#include "vertexcolorpainter.h" class MaterialPreviewsGenerator; class MotionsGenerator; class ScriptRunner; -class MousePicker; class HistoryItem { @@ -437,6 +438,7 @@ signals: void edgeReversed(QUuid edgeId); void partPreviewChanged(QUuid partId); void resultMeshChanged(); + void paintedMeshChanged(); void turnaroundChanged(); void editModeChanged(); void paintModeChanged(); @@ -466,6 +468,8 @@ signals: void partChamferStateChanged(QUuid partId); void partTargetChanged(QUuid partId); void partColorSolubilityChanged(QUuid partId); + void partMetalnessChanged(QUuid partId); + void partRoughnessChanged(QUuid partId); void partHollowThicknessChanged(QUuid partId); void partCountershadeStateChanged(QUuid partId); void partGridStateChanged(QUuid partId); @@ -537,6 +541,9 @@ public: // need initialize RigType rigType; bool weldEnabled; PolyCount polyCount; + QColor brushColor; + float brushMetalness = Model::m_defaultMetalness; + float brushRoughness = Model::m_defaultRoughness; public: Document(); ~Document(); @@ -576,6 +583,7 @@ public: const Pose *findPose(QUuid poseId) const; const Motion *findMotion(QUuid motionId) const; Model *takeResultMesh(); + Model *takePaintedMesh(); bool isMeshGenerationSucceed(); Model *takeResultTextureMesh(); Model *takeResultRigWeightMesh(); @@ -646,8 +654,8 @@ public slots: void generateMotions(); void motionsReady(); void pickMouseTarget(const QVector3D &nearPosition, const QVector3D &farPosition); - void doPickMouseTarget(); - void mouseTargetReady(); + void paintVertexColors(); + void vertexColorsReady(); void setPartLockState(QUuid partId, bool locked); void setPartVisibleState(QUuid partId, bool visible); void setPartSubdivState(QUuid partId, bool subdived); @@ -668,6 +676,8 @@ public slots: void setPartChamferState(QUuid partId, bool chamfered); void setPartTarget(QUuid partId, PartTarget target); void setPartColorSolubility(QUuid partId, float solubility); + void setPartMetalness(QUuid partId, float metalness); + void setPartRoughness(QUuid partId, float roughness); void setPartHollowThickness(QUuid partId, float hollowThickness); void setPartCountershaded(QUuid partId, bool countershaded); void setComponentCombineMode(QUuid componentId, CombineMode combineMode); @@ -774,6 +784,7 @@ private: // need initialize bool m_isResultMeshObsolete; MeshGenerator *m_meshGenerator; Model *m_resultMesh; + Model *m_paintedMesh; std::map *m_resultMeshCutFaceTransforms; std::map> *m_resultMeshNodesCutFaces; bool m_isMeshGenerationSucceed; @@ -805,11 +816,12 @@ private: // need initialize std::map> m_mergedVariables; ScriptRunner *m_scriptRunner; bool m_isScriptResultObsolete; - MousePicker *m_mousePicker; + VertexColorPainter *m_vertexColorPainter; bool m_isMouseTargetResultObsolete; PaintMode m_paintMode; float m_mousePickRadius; bool m_saveNextPaintSnapshot; + VoxelGrid *m_vertexColorVoxelGrid; private: static unsigned long m_maxSnapshot; std::deque m_undoItems; @@ -823,7 +835,6 @@ private: QString m_scriptConsoleLog; QString m_script; std::set m_mousePickMaskNodeIds; - std::set m_intermediatePaintImageIds; }; #endif diff --git a/src/documentwindow.cpp b/src/documentwindow.cpp index b80ec8f7..b6e53c40 100644 --- a/src/documentwindow.cpp +++ b/src/documentwindow.cpp @@ -162,7 +162,8 @@ DocumentWindow::DocumentWindow() : m_exportPreviewWidget(nullptr), m_preferencesWidget(nullptr), m_isLastMeshGenerationSucceed(true), - m_currentUpdatedMeshId(0) + m_currentUpdatedMeshId(0), + m_colorWheelWidget(nullptr) { QObject::connect((QtSingleApplication *)QGuiApplication::instance(), &QtSingleApplication::messageReceived, this, [this](const QString &message) { @@ -201,9 +202,9 @@ DocumentWindow::DocumentWindow() : //markerButton->setToolTip(tr("Marker pen")); //Theme::initAwesomeButton(markerButton); - QPushButton *paintButton = new QPushButton(QChar(fa::paintbrush)); - paintButton->setToolTip(tr("Paint brush")); - Theme::initAwesomeButton(paintButton); + //QPushButton *paintButton = new QPushButton(QChar(fa::paintbrush)); + //paintButton->setToolTip(tr("Paint brush")); + //Theme::initAwesomeButton(paintButton); //QPushButton *dragButton = new QPushButton(QChar(fa::handrocko)); //dragButton->setToolTip(tr("Enter drag mode")); @@ -269,6 +270,10 @@ DocumentWindow::DocumentWindow() : updateRegenerateIconAndTips(regenerateButton, m_document->isMeshGenerationSucceed()); generatePartPreviewImages(); }); + connect(m_document, &Document::paintedMeshChanged, [=]() { + auto paintedMesh = m_document->takePaintedMesh(); + m_modelRenderWidget->updateMesh(paintedMesh); + }); connect(m_document, &Document::postProcessing, this, [=]() { regenerateButton->showSpinner(true); }); @@ -287,7 +292,7 @@ DocumentWindow::DocumentWindow() : toolButtonLayout->addWidget(addButton); toolButtonLayout->addWidget(selectButton); //toolButtonLayout->addWidget(markerButton); - toolButtonLayout->addWidget(paintButton); + //toolButtonLayout->addWidget(paintButton); //toolButtonLayout->addWidget(dragButton); toolButtonLayout->addWidget(zoomInButton); toolButtonLayout->addWidget(zoomOutButton); @@ -392,16 +397,80 @@ DocumentWindow::DocumentWindow() : setTabPosition(Qt::RightDockWidgetArea, QTabWidget::East); - QDockWidget *partTreeDocker = new QDockWidget(tr("Parts"), this); - partTreeDocker->setAllowedAreas(Qt::RightDockWidgetArea); - m_partTreeWidget = new PartTreeWidget(m_document, partTreeDocker); - partTreeDocker->setWidget(m_partTreeWidget); - addDockWidget(Qt::RightDockWidgetArea, partTreeDocker); - //connect(partTreeDocker, &QDockWidget::topLevelChanged, [=](bool topLevel) { - // Q_UNUSED(topLevel); - // for (const auto &part: m_document->partMap) - // m_partTreeWidget->partPreviewChanged(part.first); - //}); + QDockWidget *partsDocker = new QDockWidget(tr("Parts"), this); + partsDocker->setAllowedAreas(Qt::RightDockWidgetArea); + m_colorWheelWidget = new color_widgets::ColorWheel(nullptr); + m_colorWheelWidget->setContentsMargins(0, 5, 0, 5); + m_colorWheelWidget->hide(); + m_document->brushColor = m_colorWheelWidget->color(); + connect(m_colorWheelWidget, &color_widgets::ColorWheel::colorChanged, this, [=](QColor color) { + m_document->brushColor = color; + }); + + FloatNumberWidget *metalnessWidget = new FloatNumberWidget; + metalnessWidget->setSliderFixedWidth(Theme::sidebarPreferredWidth * 0.4); + metalnessWidget->setItemName(tr("Metallic")); + metalnessWidget->setRange(0.0, 1.0); + metalnessWidget->setValue(m_document->brushMetalness); + + connect(metalnessWidget, &FloatNumberWidget::valueChanged, [=](float value) { + m_document->brushMetalness = value; + }); + + QPushButton *metalnessEraser = new QPushButton(QChar(fa::eraser)); + Theme::initAwesomeToolButtonWithoutFont(metalnessEraser); + + connect(metalnessEraser, &QPushButton::clicked, [=]() { + metalnessWidget->setValue(Model::m_defaultMetalness); + }); + + QHBoxLayout *metalnessLayout = new QHBoxLayout; + metalnessLayout->addWidget(metalnessEraser); + metalnessLayout->addWidget(metalnessWidget); + + FloatNumberWidget *roughnessWidget = new FloatNumberWidget; + roughnessWidget->setSliderFixedWidth(Theme::sidebarPreferredWidth * 0.35); + roughnessWidget->setItemName(tr("Roughness")); + roughnessWidget->setRange(0.0, 1.0); + roughnessWidget->setValue(m_document->brushRoughness); + + connect(roughnessWidget, &FloatNumberWidget::valueChanged, [=](float value) { + m_document->brushRoughness = value; + }); + + QPushButton *roughnessEraser = new QPushButton(QChar(fa::eraser)); + Theme::initAwesomeToolButtonWithoutFont(roughnessEraser); + + connect(roughnessEraser, &QPushButton::clicked, [=]() { + roughnessWidget->setValue(Model::m_defaultRoughness); + }); + + QHBoxLayout *roughnessLayout = new QHBoxLayout; + roughnessLayout->addWidget(roughnessEraser); + roughnessLayout->addWidget(roughnessWidget); + + QWidget *metalnessAndRoughnessWidget = new QWidget; + QVBoxLayout *metalnessAndRoughnessLayout = new QVBoxLayout; + metalnessAndRoughnessLayout->addLayout(metalnessLayout); + metalnessAndRoughnessLayout->addLayout(roughnessLayout); + metalnessAndRoughnessWidget->setLayout(metalnessAndRoughnessLayout); + metalnessAndRoughnessWidget->hide(); + + connect(m_document, &Document::editModeChanged, this, [=]() { + m_colorWheelWidget->setVisible(SkeletonDocumentEditMode::Paint == m_document->editMode); + metalnessAndRoughnessWidget->setVisible(SkeletonDocumentEditMode::Paint == m_document->editMode); + }); + + m_partTreeWidget = new PartTreeWidget(m_document, nullptr); + QWidget *partsWidget = new QWidget(partsDocker); + QVBoxLayout *partsLayout = new QVBoxLayout; + partsLayout->setContentsMargins(0, 0, 0, 0); + partsLayout->addWidget(m_colorWheelWidget); + partsLayout->addWidget(metalnessAndRoughnessWidget); + partsLayout->addWidget(m_partTreeWidget); + partsWidget->setLayout(partsLayout); + partsDocker->setWidget(partsWidget); + addDockWidget(Qt::RightDockWidgetArea, partsDocker); QDockWidget *materialDocker = new QDockWidget(tr("Materials"), this); materialDocker->setAllowedAreas(Qt::RightDockWidgetArea); @@ -453,13 +522,13 @@ DocumentWindow::DocumentWindow() : scriptDocker->setWidget(scriptWidget); addDockWidget(Qt::RightDockWidgetArea, scriptDocker); - tabifyDockWidget(partTreeDocker, materialDocker); + tabifyDockWidget(partsDocker, materialDocker); tabifyDockWidget(materialDocker, rigDocker); tabifyDockWidget(rigDocker, poseDocker); tabifyDockWidget(poseDocker, motionDocker); tabifyDockWidget(motionDocker, scriptDocker); - partTreeDocker->raise(); + partsDocker->raise(); QHBoxLayout *mainLayout = new QHBoxLayout; mainLayout->setSpacing(0); @@ -829,8 +898,8 @@ DocumentWindow::DocumentWindow() : m_showPartsListAction = new QAction(tr("Parts"), this); connect(m_showPartsListAction, &QAction::triggered, [=]() { - partTreeDocker->show(); - partTreeDocker->raise(); + partsDocker->show(); + partsDocker->raise(); }); m_windowMenu->addAction(m_showPartsListAction); @@ -955,9 +1024,9 @@ DocumentWindow::DocumentWindow() : // m_document->setEditMode(SkeletonDocumentEditMode::Mark); //}); - connect(paintButton, &QPushButton::clicked, [=]() { - m_document->setEditMode(SkeletonDocumentEditMode::Paint); - }); + //connect(paintButton, &QPushButton::clicked, [=]() { + // m_document->setEditMode(SkeletonDocumentEditMode::Paint); + //}); //connect(dragButton, &QPushButton::clicked, [=]() { // m_document->setEditMode(SkeletonDocumentEditMode::Drag); @@ -992,8 +1061,8 @@ DocumentWindow::DocumentWindow() : m_partListDockerVisibleSwitchConnection = connect(m_document, &Document::skeletonChanged, [=]() { if (m_graphicsWidget->hasItems()) { - if (partTreeDocker->isHidden()) - partTreeDocker->show(); + if (partsDocker->isHidden()) + partsDocker->show(); disconnect(m_partListDockerVisibleSwitchConnection); } }); @@ -1147,6 +1216,8 @@ DocumentWindow::DocumentWindow() : connect(m_document, &Document::partHollowThicknessChanged, m_partTreeWidget, &PartTreeWidget::partHollowThicknessChanged); connect(m_document, &Document::partMaterialIdChanged, m_partTreeWidget, &PartTreeWidget::partMaterialIdChanged); connect(m_document, &Document::partColorSolubilityChanged, m_partTreeWidget, &PartTreeWidget::partColorSolubilityChanged); + connect(m_document, &Document::partMetalnessChanged, m_partTreeWidget, &PartTreeWidget::partMetalnessChanged); + connect(m_document, &Document::partRoughnessChanged, m_partTreeWidget, &PartTreeWidget::partRoughnessChanged); connect(m_document, &Document::partCountershadeStateChanged, m_partTreeWidget, &PartTreeWidget::partCountershadeStateChanged); connect(m_document, &Document::partTargetChanged, m_partTreeWidget, &PartTreeWidget::partXmirrorStateChanged); diff --git a/src/documentwindow.h b/src/documentwindow.h index dc045e73..29c63a7d 100644 --- a/src/documentwindow.h +++ b/src/documentwindow.h @@ -21,6 +21,7 @@ #include "normalanddepthmapsgenerator.h" #include "autosaver.h" #include "partpreviewimagesgenerator.h" +#include "QtColorWidgets/ColorWheel" class SkeletonGraphicsWidget; class PartTreeWidget; @@ -116,6 +117,7 @@ private: bool m_isLastMeshGenerationSucceed; quint64 m_currentUpdatedMeshId; QStringList m_waitingForExportToFilenames; + color_widgets::ColorWheel *m_colorWheelWidget; private: QString m_currentFilename; diff --git a/src/floatnumberwidget.cpp b/src/floatnumberwidget.cpp index 0988fdde..c7284e9c 100644 --- a/src/floatnumberwidget.cpp +++ b/src/floatnumberwidget.cpp @@ -34,6 +34,11 @@ FloatNumberWidget::FloatNumberWidget(QWidget *parent, bool singleLine) : } } +void FloatNumberWidget::setSliderFixedWidth(float width) +{ + m_slider->setFixedWidth(width); +} + void FloatNumberWidget::updateValueLabel(float value) { QString valueString = QString().sprintf("%.2f", value); diff --git a/src/floatnumberwidget.h b/src/floatnumberwidget.h index 1913bc26..7d841a91 100644 --- a/src/floatnumberwidget.h +++ b/src/floatnumberwidget.h @@ -9,10 +9,11 @@ class FloatNumberWidget : public QWidget { Q_OBJECT public: - explicit FloatNumberWidget(QWidget *parent = nullptr, bool singleLine=true); + explicit FloatNumberWidget(QWidget *parent=nullptr, bool singleLine=true); void setRange(float min, float max); float value() const; void setItemName(const QString &name); + void setSliderFixedWidth(float width); public slots: void increaseValue(); diff --git a/src/meshgenerator.cpp b/src/meshgenerator.cpp index 85cda07c..779e8953 100644 --- a/src/meshgenerator.cpp +++ b/src/meshgenerator.cpp @@ -416,6 +416,16 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString, if (!colorSolubilityString.isEmpty()) colorSolubility = colorSolubilityString.toFloat(); + float metalness = 0; + QString metalnessString = valueOfKeyInMapOrEmpty(part, "metalness"); + if (!metalnessString.isEmpty()) + metalness = metalnessString.toFloat(); + + float roughness = 1.0; + QString roughnessString = valueOfKeyInMapOrEmpty(part, "roughness"); + if (!roughnessString.isEmpty()) + roughness = roughnessString.toFloat(); + QUuid fillMeshFileId; QString fillMeshString = valueOfKeyInMapOrEmpty(part, "fillMesh"); if (!fillMeshString.isEmpty()) { @@ -538,6 +548,8 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString, outcomeNode.materialId = materialId; outcomeNode.countershaded = countershaded; outcomeNode.colorSolubility = colorSolubility; + outcomeNode.metalness = metalness; + outcomeNode.roughness = roughness; outcomeNode.boneMark = nodeInfo.boneMark; if (!__mirroredByPartId.isEmpty()) outcomeNode.mirroredByPartId = QUuid(__mirroredByPartId); diff --git a/src/model.cpp b/src/model.cpp index cd88b9b6..3a4cc226 100644 --- a/src/model.cpp +++ b/src/model.cpp @@ -5,8 +5,6 @@ #include "model.h" #include "version.h" -#define MAX_VERTICES_PER_FACE 100 - float Model::m_defaultMetalness = 0.0; float Model::m_defaultRoughness = 1.0; diff --git a/src/modelmeshbinder.cpp b/src/modelmeshbinder.cpp index 2588bfe5..4eef98f3 100644 --- a/src/modelmeshbinder.cpp +++ b/src/modelmeshbinder.cpp @@ -156,18 +156,22 @@ void ModelMeshBinder::paint(ModelShaderProgram *program) (m_hasMetalnessMap || m_hasRoughnessMap || m_hasAmbientOcclusionMap)) m_metalnessRoughnessAmbientOcclusionMap = new QOpenGLTexture(*m_mesh->metalnessRoughnessAmbientOcclusionImage()); - delete m_environmentIrradianceMap; - m_environmentIrradianceMap = nullptr; - delete m_environmentSpecularMap; - m_environmentSpecularMap = nullptr; + //delete m_environmentIrradianceMap; + //m_environmentIrradianceMap = nullptr; + //delete m_environmentSpecularMap; + //m_environmentSpecularMap = nullptr; if (program->isCoreProfile() && - m_environmentLightEnabled && - (m_hasMetalnessMap || m_hasRoughnessMap)) { - DdsFileReader irradianceFile(":/resources/cedar_bridge_irradiance.dds"); - m_environmentIrradianceMap = irradianceFile.createOpenGLTexture(); + m_environmentLightEnabled/* && + (m_hasMetalnessMap || m_hasRoughnessMap)*/) { + if (nullptr == m_environmentIrradianceMap) { + DdsFileReader irradianceFile(":/resources/cedar_bridge_irradiance.dds"); + m_environmentIrradianceMap = irradianceFile.createOpenGLTexture(); + } - DdsFileReader specularFile(":/resources/cedar_bridge_specular.dds"); - m_environmentSpecularMap = specularFile.createOpenGLTexture(); + if (nullptr == m_environmentSpecularMap) { + DdsFileReader specularFile(":/resources/cedar_bridge_specular.dds"); + m_environmentSpecularMap = specularFile.createOpenGLTexture(); + } } { diff --git a/src/mousepicker.cpp b/src/mousepicker.cpp deleted file mode 100644 index c6ac617b..00000000 --- a/src/mousepicker.cpp +++ /dev/null @@ -1,217 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "mousepicker.h" -#include "util.h" -#include "imageforever.h" - -MousePicker::MousePicker(const Outcome &outcome, const QVector3D &mouseRayNear, const QVector3D &mouseRayFar) : - m_outcome(outcome), - m_mouseRayNear(mouseRayNear), - m_mouseRayFar(mouseRayFar) -{ -} - -const std::set &MousePicker::changedPartIds() -{ - return m_changedPartIds; -} - -void MousePicker::setPaintMode(PaintMode paintMode) -{ - m_paintMode = paintMode; -} - -void MousePicker::setMaskNodeIds(const std::set &nodeIds) -{ - m_mousePickMaskNodeIds = nodeIds; -} - -void MousePicker::setRadius(float radius) -{ - m_radius = radius; -} - -MousePicker::~MousePicker() -{ -} - -bool MousePicker::calculateMouseModelPosition(QVector3D &mouseModelPosition) -{ - bool foundPosition = false; - auto ray = (m_mouseRayNear - m_mouseRayFar).normalized(); - float minDistance2 = std::numeric_limits::max(); - for (size_t i = 0; i < m_outcome.triangles.size(); ++i) { - const auto &triangleIndices = m_outcome.triangles[i]; - std::vector triangle = { - m_outcome.vertices[triangleIndices[0]], - m_outcome.vertices[triangleIndices[1]], - m_outcome.vertices[triangleIndices[2]], - }; - const auto &triangleNormal = m_outcome.triangleNormals[i]; - if (QVector3D::dotProduct(triangleNormal, ray) <= 0) - continue; - QVector3D intersection; - if (intersectSegmentAndTriangle(m_mouseRayNear, m_mouseRayFar, - triangle, - triangleNormal, - &intersection)) { - float distance2 = (intersection - m_mouseRayNear).lengthSquared(); - if (distance2 < minDistance2) { - mouseModelPosition = intersection; - minDistance2 = distance2; - foundPosition = true; - } - } - } - return foundPosition; -} - -void MousePicker::pick() -{ - if (!calculateMouseModelPosition(m_targetPosition)) - return; - - if (PaintMode::None == m_paintMode) - return; - - float distance2 = m_radius * m_radius; - - for (const auto &map: m_outcome.paintMaps) { - for (const auto &node: map.paintNodes) { - if (!m_mousePickMaskNodeIds.empty() && m_mousePickMaskNodeIds.find(node.originNodeId) == m_mousePickMaskNodeIds.end()) - continue; - size_t intersectedNum = 0; - QVector3D sumOfDirection; - QVector3D referenceDirection = (m_targetPosition - node.origin).normalized(); - float sumOfRadius = 0; - for (const auto &vertexPosition: node.vertices) { - // >0.866 = <30 degrees - auto direction = (vertexPosition - node.origin).normalized(); - if (QVector3D::dotProduct(referenceDirection, direction) > 0.866 && - (vertexPosition - m_targetPosition).lengthSquared() <= distance2) { - float distance = vertexPosition.distanceToPoint(m_targetPosition); - float radius = (m_radius - distance) / node.radius; - sumOfRadius += radius; - sumOfDirection += direction * radius; - ++intersectedNum; - } - } - if (intersectedNum > 0) { - float paintRadius = sumOfRadius / intersectedNum; - QVector3D paintDirection = sumOfDirection.normalized(); - float degrees = angleInRangle360BetweenTwoVectors(node.baseNormal, paintDirection, node.direction); - float offset = (float)node.order / map.paintNodes.size(); - m_changedPartIds.insert(map.partId); - paintToImage(map.partId, offset, degrees / 360.0, paintRadius, PaintMode::Push == m_paintMode); - } - } - } -} - -void MousePicker::process() -{ - pick(); - emit finished(); -} - -void MousePicker::paintToImage(const QUuid &partId, float x, float y, float radius, bool inverted) -{ - QUuid oldImageId; - QImage image(72, 36, QImage::Format_Grayscale8); - image.fill(QColor(127, 127, 127)); - const auto &findImageId = m_paintImages.find(partId); - if (findImageId != m_paintImages.end()) { - const QImage *oldImage = ImageForever::get(findImageId->second); - if (nullptr != oldImage) { - if (oldImage->size() == image.size() && - oldImage->format() == image.format()) { - image = *oldImage; - } - } - } - float destX = image.width() * x; - float destY = image.height() * y; - float destRadius = image.height() * radius; - { - QRadialGradient gradient(destX, destY, destRadius / 2); - if (inverted) { - gradient.setColorAt(0, QColor(0, 0, 0, 3)); - gradient.setColorAt(1, Qt::transparent); - } else { - gradient.setColorAt(0, QColor(255, 255, 255, 3)); - gradient.setColorAt(1, Qt::transparent); - } - QBrush brush(gradient); - QPainter paint(&image); - paint.setRenderHint(QPainter::HighQualityAntialiasing); - paint.setBrush(brush); - paint.setPen(Qt::NoPen); - paint.drawEllipse(destX - destRadius / 2, destY - destRadius / 2, destRadius, destRadius); - } - QUuid imageId = ImageForever::add(&image); - m_paintImages[partId] = imageId; -} - -const QVector3D &MousePicker::targetPosition() -{ - return m_targetPosition; -} - -bool MousePicker::intersectSegmentAndPlane(const QVector3D &segmentPoint0, const QVector3D &segmentPoint1, - const QVector3D &pointOnPlane, const QVector3D &planeNormal, - QVector3D *intersection) -{ - auto u = segmentPoint1 - segmentPoint0; - auto w = segmentPoint0 - pointOnPlane; - auto d = QVector3D::dotProduct(planeNormal, u); - auto n = QVector3D::dotProduct(-planeNormal, w); - if (qAbs(d) < 0.00000001) - return false; - auto s = n / d; - if (s < 0 || s > 1 || qIsNaN(s) || qIsInf(s)) - return false; - if (nullptr != intersection) - *intersection = segmentPoint0 + s * u; - return true; -} - -bool MousePicker::intersectSegmentAndTriangle(const QVector3D &segmentPoint0, const QVector3D &segmentPoint1, - const std::vector &triangle, - const QVector3D &triangleNormal, - QVector3D *intersection) -{ - QVector3D possibleIntersection; - if (!intersectSegmentAndPlane(segmentPoint0, segmentPoint1, - triangle[0], triangleNormal, &possibleIntersection)) { - return false; - } - auto ray = (segmentPoint0 - segmentPoint1).normalized(); - std::vector normals; - for (size_t i = 0; i < 3; ++i) { - size_t j = (i + 1) % 3; - normals.push_back(QVector3D::normal(possibleIntersection, triangle[i], triangle[j])); - } - if (QVector3D::dotProduct(normals[0], ray) <= 0) - return false; - if (QVector3D::dotProduct(normals[0], normals[1]) <= 0) - return false; - if (QVector3D::dotProduct(normals[0], normals[2]) <= 0) - return false; - if (nullptr != intersection) - *intersection = possibleIntersection; - return true; -} - -void MousePicker::setPaintImages(const std::map &paintImages) -{ - m_paintImages = paintImages; -} - -const std::map &MousePicker::resultPaintImages() -{ - return m_paintImages; -} diff --git a/src/mousepicker.h b/src/mousepicker.h deleted file mode 100644 index cda52148..00000000 --- a/src/mousepicker.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef DUST3D_MOUSE_PICKER_H -#define DUST3D_MOUSE_PICKER_H -#include -#include -#include -#include -#include -#include "outcome.h" -#include "paintmode.h" - -class MousePicker : public QObject -{ - Q_OBJECT -public: - MousePicker(const Outcome &outcome, const QVector3D &mouseRayNear, const QVector3D &mouseRayFar); - void setRadius(float radius); - void setPaintImages(const std::map &paintImages); - void setPaintMode(PaintMode paintMode); - void setMaskNodeIds(const std::set &nodeIds); - const std::map &resultPaintImages(); - const std::set &changedPartIds(); - - ~MousePicker(); - const QVector3D &targetPosition(); -signals: - void finished(); -public slots: - void process(); - void pick(); -private: - float m_radius = 0.0; - std::map m_paintImages; - PaintMode m_paintMode = PaintMode::None; - std::set m_changedPartIds; - std::set m_mousePickMaskNodeIds; - bool m_enablePaint = false; - Outcome m_outcome; - QVector3D m_mouseRayNear; - QVector3D m_mouseRayFar; - QVector3D m_targetPosition; - static bool intersectSegmentAndPlane(const QVector3D &segmentPoint0, const QVector3D &segmentPoint1, - const QVector3D &pointOnPlane, const QVector3D &planeNormal, - QVector3D *intersection=nullptr); - static bool intersectSegmentAndTriangle(const QVector3D &segmentPoint0, const QVector3D &segmentPoint1, - const std::vector &triangle, - const QVector3D &triangleNormal, - QVector3D *intersection=nullptr); - bool calculateMouseModelPosition(QVector3D &mouseModelPosition); - void paintToImage(const QUuid &partId, float x, float y, float radius, bool inverted=false); -}; - -#endif diff --git a/src/outcome.h b/src/outcome.h index d50222e0..59efb178 100644 --- a/src/outcome.h +++ b/src/outcome.h @@ -19,6 +19,8 @@ struct OutcomeNode float radius = 0; QColor color; float colorSolubility = 0; + float metalness = 0; + float roughness = 1.0; QUuid materialId; bool countershaded = false; QUuid mirrorFromPartId; diff --git a/src/partpreviewimagesgenerator.cpp b/src/partpreviewimagesgenerator.cpp index 3ffa2db5..cc8dcbd4 100644 --- a/src/partpreviewimagesgenerator.cpp +++ b/src/partpreviewimagesgenerator.cpp @@ -28,6 +28,7 @@ void PartPreviewImagesGenerator::generate() m_offscreenRender->setZRotation(0); m_offscreenRender->setEyePosition(QVector3D(0, 0, -4.0)); + m_offscreenRender->enableEnvironmentLight(); m_offscreenRender->setRenderPurpose(0); for (auto &it: m_partPreviews) { if (it.second.isCutFace) { diff --git a/src/parttreewidget.cpp b/src/parttreewidget.cpp index 32f3570f..9c4c0511 100644 --- a/src/parttreewidget.cpp +++ b/src/parttreewidget.cpp @@ -1442,6 +1442,28 @@ void PartTreeWidget::partColorSolubilityChanged(QUuid partId) widget->updateColorButton(); } +void PartTreeWidget::partMetalnessChanged(QUuid partId) +{ + auto item = m_partItemMap.find(partId); + if (item == m_partItemMap.end()) { + qDebug() << "Part item not found:" << partId; + return; + } + PartWidget *widget = (PartWidget *)itemWidget(item->second, 0); + widget->updateColorButton(); +} + +void PartTreeWidget::partRoughnessChanged(QUuid partId) +{ + auto item = m_partItemMap.find(partId); + if (item == m_partItemMap.end()) { + qDebug() << "Part item not found:" << partId; + return; + } + PartWidget *widget = (PartWidget *)itemWidget(item->second, 0); + widget->updateColorButton(); +} + void PartTreeWidget::partCountershadeStateChanged(QUuid partId) { auto item = m_partItemMap.find(partId); diff --git a/src/parttreewidget.h b/src/parttreewidget.h index 3fbcf027..9f0d5b7e 100644 --- a/src/parttreewidget.h +++ b/src/parttreewidget.h @@ -79,6 +79,8 @@ public slots: void partHollowThicknessChanged(QUuid partId); void partMaterialIdChanged(QUuid partId); void partColorSolubilityChanged(QUuid partId); + void partMetalnessChanged(QUuid partId); + void partRoughnessChanged(QUuid partId); void partCountershadeStateChanged(QUuid partId); void partChecked(QUuid partId); void partUnchecked(QUuid partId); diff --git a/src/partwidget.cpp b/src/partwidget.cpp index 6e7c0f4d..c2214f61 100644 --- a/src/partwidget.cpp +++ b/src/partwidget.cpp @@ -177,6 +177,8 @@ PartWidget::PartWidget(const Document *document, QUuid partId) : connect(this, &PartWidget::setPartColorState, m_document, &Document::setPartColorState); connect(this, &PartWidget::setPartMaterialId, m_document, &Document::setPartMaterialId); connect(this, &PartWidget::setPartColorSolubility, m_document, &Document::setPartColorSolubility); + connect(this, &PartWidget::setPartMetalness, m_document, &Document::setPartMetalness); + connect(this, &PartWidget::setPartRoughness, m_document, &Document::setPartRoughness); connect(this, &PartWidget::setPartHollowThickness, m_document, &Document::setPartHollowThickness); connect(this, &PartWidget::setPartCountershaded, m_document, &Document::setPartCountershaded); connect(this, &PartWidget::checkPart, m_document, &Document::checkPart); @@ -469,10 +471,57 @@ void PartWidget::showColorSettingPopup(const QPoint &pos) colorSolubilityLayout->addWidget(colorSolubilityEraser); colorSolubilityLayout->addWidget(colorSolubilityWidget); + FloatNumberWidget *metalnessWidget = new FloatNumberWidget; + metalnessWidget->setItemName(tr("Metallic")); + metalnessWidget->setRange(0.0, 1.0); + metalnessWidget->setValue(part->metalness); + + connect(metalnessWidget, &FloatNumberWidget::valueChanged, [=](float value) { + emit setPartMetalness(m_partId, value); + emit groupOperationAdded(); + }); + + QPushButton *metalnessEraser = new QPushButton(QChar(fa::eraser)); + initToolButton(metalnessEraser); + + connect(metalnessEraser, &QPushButton::clicked, [=]() { + metalnessWidget->setValue(0.0); + emit groupOperationAdded(); + }); + + QHBoxLayout *metalnessLayout = new QHBoxLayout; + metalnessLayout->addWidget(metalnessEraser); + metalnessLayout->addWidget(metalnessWidget); + + FloatNumberWidget *roughnessWidget = new FloatNumberWidget; + roughnessWidget->setItemName(tr("Roughness")); + roughnessWidget->setRange(0.0, 1.0); + roughnessWidget->setValue(part->roughness); + + connect(roughnessWidget, &FloatNumberWidget::valueChanged, [=](float value) { + emit setPartRoughness(m_partId, value); + emit groupOperationAdded(); + }); + + QPushButton *roughnessEraser = new QPushButton(QChar(fa::eraser)); + initToolButton(roughnessEraser); + + connect(roughnessEraser, &QPushButton::clicked, [=]() { + roughnessWidget->setValue(1.0); + emit groupOperationAdded(); + }); + + QHBoxLayout *roughnessLayout = new QHBoxLayout; + roughnessLayout->addWidget(roughnessEraser); + roughnessLayout->addWidget(roughnessWidget); + QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addLayout(colorLayout); mainLayout->addLayout(colorTransparencyLayout); mainLayout->addLayout(colorSolubilityLayout); + mainLayout->addWidget(Theme::createHorizontalLineWidget()); + mainLayout->addLayout(metalnessLayout); + mainLayout->addLayout(roughnessLayout); if (m_document->materialIdList.empty()) { InfoLabel *infoLabel = new InfoLabel; @@ -861,10 +910,16 @@ void PartWidget::updateColorButton() qDebug() << "Part not found:" << m_partId; return; } - if (part->hasColor || part->materialAdjusted() || part->colorSolubilityAdjusted() || part->countershaded) + if (part->hasColor || + part->materialAdjusted() || + part->colorSolubilityAdjusted() || + part->countershaded || + part->metalnessAdjusted() || + part->roughnessAdjusted()) { updateButton(m_colorButton, QChar(fa::eyedropper), true, part->hasColorFunction()); - else + } else { updateButton(m_colorButton, QChar(fa::eyedropper), false, part->hasColorFunction()); + } } void PartWidget::updateCutRotationButton() diff --git a/src/partwidget.h b/src/partwidget.h index dda750f7..1d703e24 100644 --- a/src/partwidget.h +++ b/src/partwidget.h @@ -28,6 +28,8 @@ signals: void setPartCutFaceLinkedId(QUuid partId, QUuid linkedId); void setPartMaterialId(QUuid partId, QUuid materialId); void setPartColorSolubility(QUuid partId, float colorSolubility); + void setPartMetalness(QUuid partId, float metalness); + void setPartRoughness(QUuid partId, float roughness); void setPartHollowThickness(QUuid partId, float hollowThickness); void setPartCountershaded(QUuid partId, bool countershaded); void movePartUp(QUuid partId); diff --git a/src/shortcuts.cpp b/src/shortcuts.cpp index 5c08cb47..6c60d3b9 100644 --- a/src/shortcuts.cpp +++ b/src/shortcuts.cpp @@ -27,7 +27,7 @@ void initShortCuts(QWidget *widget, SkeletonGraphicsWidget *graphicsWidget) defineKey(Qt::CTRL + Qt::Key_C, &SkeletonGraphicsWidget::shortcutCopy); defineKey(Qt::CTRL + Qt::Key_V, &SkeletonGraphicsWidget::shortcutPaste); defineKey(Qt::Key_S, &SkeletonGraphicsWidget::shortcutSelectMode); - defineKey(Qt::Key_D, &SkeletonGraphicsWidget::shortcutPaintMode); + //defineKey(Qt::Key_D, &SkeletonGraphicsWidget::shortcutPaintMode); defineKey(Qt::ALT + Qt::Key_Minus, &SkeletonGraphicsWidget::shortcutZoomRenderedModelByMinus10); defineKey(Qt::Key_Minus, &SkeletonGraphicsWidget::shortcutZoomSelectedByMinus1); defineKey(Qt::ALT + Qt::Key_Equal, &SkeletonGraphicsWidget::shortcutZoomRenderedModelBy10); diff --git a/src/skeletondocument.h b/src/skeletondocument.h index b269a707..d4be3b16 100644 --- a/src/skeletondocument.h +++ b/src/skeletondocument.h @@ -177,6 +177,8 @@ public: QUuid materialId; PartTarget target; float colorSolubility; + float metalness; + float roughness; float deformMapScale; QUuid deformMapImageId; float hollowThickness; @@ -204,6 +206,8 @@ public: cutFace(CutFace::Quad), target(PartTarget::Model), colorSolubility(0.0), + metalness(0.0), + roughness(1.0), deformMapScale(1.0), hollowThickness(0.0), countershaded(false), @@ -334,6 +338,14 @@ public: { return fabs(colorSolubility - 0.0) >= 0.01; } + bool metalnessAdjusted() const + { + return fabs(metalness - 0.0) >= 0.01; + } + bool roughnessAdjusted() const + { + return fabs(roughness - 1.0) >= 0.01; + } bool cutRotationAdjusted() const { return fabs(cutRotation - 0.0) >= 0.01; diff --git a/src/texturegenerator.cpp b/src/texturegenerator.cpp index e10c6cf9..e79e7562 100644 --- a/src/texturegenerator.cpp +++ b/src/texturegenerator.cpp @@ -248,6 +248,8 @@ void TextureGenerator::generate() std::map partColorMap; std::map, const OutcomeNode *> nodeMap; std::map partColorSolubilityMap; + std::map partMetalnessMap; + std::map partRoughnessMap; for (const auto &item: m_outcome->nodes) { if (!m_hasTransparencySettings) { if (!qFuzzyCompare(1.0, item.color.alphaF())) @@ -256,6 +258,8 @@ void TextureGenerator::generate() nodeMap.insert({{item.partId, item.nodeId}, &item}); partColorMap.insert({item.partId, item.color}); partColorSolubilityMap.insert({item.partId, item.colorSolubility}); + partMetalnessMap.insert({item.partId, item.metalness}); + partRoughnessMap.insert({item.partId, item.roughness}); } auto createImageBeginTime = countTimeConsumed.elapsed(); @@ -339,6 +343,56 @@ void TextureGenerator::generate() } } + for (const auto &it: partUvRects) { + const auto &partId = it.first; + const auto &rects = it.second; + auto findMetalnessResult = partMetalnessMap.find(partId); + if (findMetalnessResult != partMetalnessMap.end()) { + if (qFuzzyCompare(findMetalnessResult->second, (float)0.0)) + continue; + const auto &color = QColor(findMetalnessResult->second * 255, + findMetalnessResult->second * 255, + findMetalnessResult->second * 255); + QBrush brush(color); + float fillExpandSize = 2; + for (const auto &rect: rects) { + QRectF translatedRect = { + rect.left() * TextureGenerator::m_textureSize - fillExpandSize, + rect.top() * TextureGenerator::m_textureSize - fillExpandSize, + rect.width() * TextureGenerator::m_textureSize + fillExpandSize * 2, + rect.height() * TextureGenerator::m_textureSize + fillExpandSize * 2 + }; + textureMetalnessPainter.fillRect(translatedRect, brush); + hasMetalnessMap = true; + } + } + } + + for (const auto &it: partUvRects) { + const auto &partId = it.first; + const auto &rects = it.second; + auto findRoughnessResult = partRoughnessMap.find(partId); + if (findRoughnessResult != partRoughnessMap.end()) { + if (qFuzzyCompare(findRoughnessResult->second, (float)1.0)) + continue; + const auto &color = QColor(findRoughnessResult->second * 255, + findRoughnessResult->second * 255, + findRoughnessResult->second * 255); + QBrush brush(color); + float fillExpandSize = 2; + for (const auto &rect: rects) { + QRectF translatedRect = { + rect.left() * TextureGenerator::m_textureSize - fillExpandSize, + rect.top() * TextureGenerator::m_textureSize - fillExpandSize, + rect.width() * TextureGenerator::m_textureSize + fillExpandSize * 2, + rect.height() * TextureGenerator::m_textureSize + fillExpandSize * 2 + }; + textureRoughnessPainter.fillRect(translatedRect, brush); + hasRoughnessMap = true; + } + } + } + auto drawTexture = [&](const std::map> &map, QPainter &painter, bool useAlpha) { for (const auto &it: partUvRects) { const auto &partId = it.first; @@ -629,8 +683,10 @@ void TextureGenerator::generate() } hasNormalMap = !m_partNormalTextureMap.empty(); - hasMetalnessMap = !m_partMetalnessTextureMap.empty(); - hasRoughnessMap = !m_partRoughnessTextureMap.empty(); + if (!m_partMetalnessTextureMap.empty()) + hasMetalnessMap = true; + if (!m_partRoughnessTextureMap.empty()) + hasRoughnessMap = true; hasAmbientOcclusionMap = !m_partAmbientOcclusionTextureMap.empty(); auto paintTextureEndTime = countTimeConsumed.elapsed(); diff --git a/src/texturetype.h b/src/texturetype.h index 75ae2139..b6b3df14 100644 --- a/src/texturetype.h +++ b/src/texturetype.h @@ -25,7 +25,7 @@ QString TextureTypeToDispName(TextureType type) \ case TextureType::Normal: \ return QObject::tr("Normal Map"); \ case TextureType::Metalness: \ - return QObject::tr("Metalness"); \ + return QObject::tr("Metallic"); \ case TextureType::Roughness: \ return QObject::tr("Roughness"); \ case TextureType::AmbientOcclusion: \ diff --git a/src/util.cpp b/src/util.cpp index b150bcf1..58127c59 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -510,3 +510,87 @@ void saveAsObj(const char *filename, const std::vector &vertices, stream << endl; } } + +bool intersectSegmentAndPlane(const QVector3D &segmentPoint0, const QVector3D &segmentPoint1, + const QVector3D &pointOnPlane, const QVector3D &planeNormal, + QVector3D *intersection) +{ + auto u = segmentPoint1 - segmentPoint0; + auto w = segmentPoint0 - pointOnPlane; + auto d = QVector3D::dotProduct(planeNormal, u); + auto n = QVector3D::dotProduct(-planeNormal, w); + if (qAbs(d) < 0.00000001) + return false; + auto s = n / d; + if (s < 0 || s > 1 || qIsNaN(s) || qIsInf(s)) + return false; + if (nullptr != intersection) + *intersection = segmentPoint0 + s * u; + return true; +} + +bool intersectSegmentAndTriangle(const QVector3D &segmentPoint0, const QVector3D &segmentPoint1, + const std::vector &triangle, + const QVector3D &triangleNormal, + QVector3D *intersection) +{ + QVector3D possibleIntersection; + if (!intersectSegmentAndPlane(segmentPoint0, segmentPoint1, + triangle[0], triangleNormal, &possibleIntersection)) { + return false; + } + auto ray = (segmentPoint0 - segmentPoint1).normalized(); + std::vector normals; + for (size_t i = 0; i < 3; ++i) { + size_t j = (i + 1) % 3; + normals.push_back(QVector3D::normal(possibleIntersection, triangle[i], triangle[j])); + } + if (QVector3D::dotProduct(normals[0], ray) <= 0) + return false; + if (QVector3D::dotProduct(normals[0], normals[1]) <= 0) + return false; + if (QVector3D::dotProduct(normals[0], normals[2]) <= 0) + return false; + if (nullptr != intersection) + *intersection = possibleIntersection; + return true; +} + +bool intersectRayAndPolyhedron(const QVector3D &rayNear, + const QVector3D &rayFar, + const std::vector &vertices, + const std::vector> &triangles, + const std::vector &triangleNormals, + QVector3D *intersection) +{ + bool foundPosition = false; + auto ray = (rayNear - rayFar).normalized(); + float minDistance2 = std::numeric_limits::max(); + for (size_t i = 0; i < triangles.size(); ++i) { + const auto &triangleIndices = triangles[i]; + std::vector triangle = { + vertices[triangleIndices[0]], + vertices[triangleIndices[1]], + vertices[triangleIndices[2]], + }; + const auto &triangleNormal = triangleNormals[i]; + if (QVector3D::dotProduct(triangleNormal, ray) <= 0) + continue; + QVector3D point; + if (intersectSegmentAndTriangle(rayNear, rayFar, + triangle, + triangleNormal, + &point)) { + float distance2 = (point - rayNear).lengthSquared(); + if (distance2 < minDistance2) { + if (nullptr != intersection) + *intersection = point; + minDistance2 = distance2; + foundPosition = true; + } + } + } + return foundPosition; +} + + diff --git a/src/util.h b/src/util.h index 2b3f0fa3..c511525c 100644 --- a/src/util.h +++ b/src/util.h @@ -47,5 +47,18 @@ void subdivideFace2D(std::vector *face); QVector3D choosenBaseAxis(const QVector3D &layoutDirection); void saveAsObj(const char *filename, const std::vector &vertices, const std::vector> &faces); - +bool intersectSegmentAndPlane(const QVector3D &segmentPoint0, const QVector3D &segmentPoint1, + const QVector3D &pointOnPlane, const QVector3D &planeNormal, + QVector3D *intersection=nullptr); +bool intersectSegmentAndTriangle(const QVector3D &segmentPoint0, const QVector3D &segmentPoint1, + const std::vector &triangle, + const QVector3D &triangleNormal, + QVector3D *intersection=nullptr); +bool intersectRayAndPolyhedron(const QVector3D &rayNear, + const QVector3D &rayFar, + const std::vector &vertices, + const std::vector> &triangles, + const std::vector &triangleNormals, + QVector3D *intersection=nullptr); + #endif diff --git a/src/vertexcolorpainter.cpp b/src/vertexcolorpainter.cpp new file mode 100644 index 00000000..e5842ca1 --- /dev/null +++ b/src/vertexcolorpainter.cpp @@ -0,0 +1,227 @@ +#include +#include +#include +#include +#include +#include +#include "vertexcolorpainter.h" +#include "util.h" +#include "imageforever.h" + +const int VertexColorPainter::m_gridSize = 4096; + +PaintColor operator+(const PaintColor &first, const PaintColor &second) +{ + float total = first.alphaF() + second.alphaF(); + if (qFuzzyIsNull(total)) + return PaintColor(255, 255, 255, 255); + float remaining = second.alphaF() / total; + float rate = 1.0 - remaining; + PaintColor color(first.red() * rate + second.red() * remaining, + first.green() * rate + second.green() * remaining, + first.blue() * rate + second.blue() * remaining); + color.metalness = first.metalness * rate + second.metalness * remaining; + color.roughness = first.roughness * rate + second.roughness * remaining; + return color; +} + +PaintColor operator-(const PaintColor &first, const PaintColor &second) +{ + PaintColor color = first; + color.setAlphaF(std::max(color.alphaF() - second.alphaF(), 0.0)); + return color; +} + +VertexColorPainter::VertexColorPainter(const Outcome &m_outcome, const QVector3D &mouseRayNear, const QVector3D &mouseRayFar) : + m_outcome(m_outcome), + m_mouseRayNear(mouseRayNear), + m_mouseRayFar(mouseRayFar) +{ +} + +Model *VertexColorPainter::takePaintedModel() +{ + Model *paintedModel = m_model; + m_model = nullptr; + return paintedModel; +} + +void VertexColorPainter::setVoxelGrid(VoxelGrid *voxelGrid) +{ + m_voxelGrid = voxelGrid; + m_voxelGrid->setNullValue(PaintColor(255, 255, 255, 255)); +} + +void VertexColorPainter::setPaintMode(PaintMode paintMode) +{ + m_paintMode = paintMode; +} + +void VertexColorPainter::setMaskNodeIds(const std::set &nodeIds) +{ + m_mousePickMaskNodeIds = nodeIds; +} + +void VertexColorPainter::setRadius(float radius) +{ + m_radius = radius; +} + +void VertexColorPainter::setBrushColor(const QColor &color) +{ + m_brushColor = color; +} + +void VertexColorPainter::setBrushMetalness(float value) +{ + m_brushMetalness = value; +} + +void VertexColorPainter::setBrushRoughness(float value) +{ + m_brushRoughness = value; +} + +VertexColorPainter::~VertexColorPainter() +{ + delete m_model; +} + +bool VertexColorPainter::calculateMouseModelPosition(QVector3D &mouseModelPosition) +{ + return intersectRayAndPolyhedron(m_mouseRayNear, + m_mouseRayFar, + m_outcome.vertices, + m_outcome.triangles, + m_outcome.triangleNormals, + &mouseModelPosition); +} + +void VertexColorPainter::paintToVoxelGrid() +{ + int voxelX = toVoxelLength(m_targetPosition.x()); + int voxelY = toVoxelLength(m_targetPosition.y()); + int voxelZ = toVoxelLength(m_targetPosition.z()); + int voxelRadius = toVoxelLength(m_radius); + int range2 = voxelRadius * voxelRadius; + PaintColor paintColor(m_brushColor); + paintColor.metalness = m_brushMetalness; + paintColor.roughness = m_brushRoughness; + m_voxelGrid->add(voxelX, voxelY, voxelZ, paintColor); + for (int i = -voxelRadius; i <= voxelRadius; ++i) { + qint8 x = voxelX + i; + int i2 = i * i; + for (int j = -voxelRadius; j <= voxelRadius; ++j) { + qint8 y = voxelY + j; + int j2 = j * j; + for (int k = -voxelRadius; k <= voxelRadius; ++k) { + qint8 z = voxelZ + k; + int k2 = k * k; + int dist2 = i2 + j2 + k2; + if (dist2 <= range2) { + int dist = std::sqrt(dist2); + float alpha = 1.0 - (float)dist / voxelRadius; + qDebug() << "alpha:" << alpha; + PaintColor color = paintColor; + color.setAlphaF(alpha); + m_voxelGrid->add(x, y, z, color); + } + } + } + } +} + +void VertexColorPainter::createPaintedModel() +{ + std::vector vertexColors(m_outcome.vertices.size()); + for (size_t i = 0; i < m_outcome.vertices.size(); ++i) { + const auto &position = m_outcome.vertices[i]; + int voxelX = toVoxelLength(position.x()); + int voxelY = toVoxelLength(position.y()); + int voxelZ = toVoxelLength(position.z()); + vertexColors[i] = m_voxelGrid->query(voxelX, voxelY, voxelZ); + } + + int triangleVertexCount = m_outcome.triangles.size() * 3; + ShaderVertex *triangleVertices = new ShaderVertex[triangleVertexCount]; + int destIndex = 0; + const auto triangleVertexNormals = m_outcome.triangleVertexNormals(); + const auto triangleVertexUvs = m_outcome.triangleVertexUvs(); + const auto triangleTangents = m_outcome.triangleTangents(); + const QVector3D defaultNormal = QVector3D(0, 0, 0); + const QVector2D defaultUv = QVector2D(0, 0); + const QVector3D defaultTangent = QVector3D(0, 0, 0); + for (size_t i = 0; i < m_outcome.triangles.size(); ++i) { + for (auto j = 0; j < 3; j++) { + int vertexIndex = m_outcome.triangles[i][j]; + const auto &vertexColor = &vertexColors[vertexIndex]; + const QVector3D *srcVert = &m_outcome.vertices[vertexIndex]; + const QVector3D *srcNormal = &defaultNormal; + if (triangleVertexNormals) + srcNormal = &(*triangleVertexNormals)[i][j]; + const QVector2D *srcUv = &defaultUv; + if (triangleVertexUvs) + srcUv = &(*triangleVertexUvs)[i][j]; + const QVector3D *srcTangent = &defaultTangent; + if (triangleTangents) + srcTangent = &(*triangleTangents)[i]; + ShaderVertex *dest = &triangleVertices[destIndex]; + dest->colorR = vertexColor->redF(); + dest->colorG = vertexColor->greenF(); + dest->colorB = vertexColor->blueF(); + dest->alpha = vertexColor->alphaF(); + dest->posX = srcVert->x(); + dest->posY = srcVert->y(); + dest->posZ = srcVert->z(); + dest->texU = srcUv->x(); + dest->texV = srcUv->y(); + dest->normX = srcNormal->x(); + dest->normY = srcNormal->y(); + dest->normZ = srcNormal->z(); + dest->metalness = vertexColor->metalness; + dest->roughness = vertexColor->roughness; + dest->tangentX = srcTangent->x(); + dest->tangentY = srcTangent->y(); + dest->tangentZ = srcTangent->z(); + destIndex++; + } + } + m_model = new Model(triangleVertices, triangleVertexCount); +} + +int VertexColorPainter::toVoxelLength(float length) +{ + int voxelLength = length * 100; + if (voxelLength > m_gridSize) + voxelLength = m_gridSize; + else if (voxelLength < -m_gridSize) + voxelLength = -m_gridSize; + return voxelLength; +} + +void VertexColorPainter::paint() +{ + if (!calculateMouseModelPosition(m_targetPosition)) + return; + + if (PaintMode::None == m_paintMode) + return; + + if (nullptr == m_voxelGrid) + return; + + paintToVoxelGrid(); + createPaintedModel(); +} + +void VertexColorPainter::process() +{ + paint(); + + emit finished(); +} + +const QVector3D &VertexColorPainter::targetPosition() +{ + return m_targetPosition; +} diff --git a/src/vertexcolorpainter.h b/src/vertexcolorpainter.h new file mode 100644 index 00000000..2ead83ea --- /dev/null +++ b/src/vertexcolorpainter.h @@ -0,0 +1,81 @@ +#ifndef DUST3D_VERTEX_COLOR_PAINTER_H +#define DUST3D_VERTEX_COLOR_PAINTER_H +#include +#include +#include +#include +#include +#include +#include "outcome.h" +#include "paintmode.h" +#include "voxelgrid.h" +#include "model.h" + +class PaintColor : public QColor +{ +public: + float metalness = Model::m_defaultMetalness; + float roughness = Model::m_defaultRoughness; + + PaintColor() : + QColor() + { + } + + PaintColor(int r, int g, int b, int a = 255) : + QColor(r, g, b, a) + { + } + + PaintColor(const QColor &color) : + QColor(color) + { + } +}; + +PaintColor operator+(const PaintColor &first, const PaintColor &second); +PaintColor operator-(const PaintColor &first, const PaintColor &second); + +class VertexColorPainter : public QObject +{ + Q_OBJECT +public: + VertexColorPainter(const Outcome &outcome, const QVector3D &mouseRayNear, const QVector3D &mouseRayFar); + void setRadius(float radius); + void setBrushColor(const QColor &color); + void setBrushMetalness(float value); + void setBrushRoughness(float value); + void setPaintMode(PaintMode paintMode); + void setMaskNodeIds(const std::set &nodeIds); + void setVoxelGrid(VoxelGrid *voxelGrid); + + ~VertexColorPainter(); + Model *takePaintedModel(); + const QVector3D &targetPosition(); +signals: + void finished(); +public slots: + void process(); + void paint(); +private: + float m_radius = 0.0; + PaintMode m_paintMode = PaintMode::None; + std::set m_mousePickMaskNodeIds; + Outcome m_outcome; + QVector3D m_mouseRayNear; + QVector3D m_mouseRayFar; + QVector3D m_targetPosition; + QColor m_brushColor; + float m_brushMetalness = Model::m_defaultMetalness; + float m_brushRoughness = Model::m_defaultRoughness; + VoxelGrid *m_voxelGrid = nullptr; + Model *m_model = nullptr; + bool calculateMouseModelPosition(QVector3D &mouseModelPosition); + void paintToVoxelGrid(); + int toVoxelLength(float length); + void createPaintedModel(); +public: + static const int m_gridSize; +}; + +#endif diff --git a/src/voxelgrid.cpp b/src/voxelgrid.cpp new file mode 100644 index 00000000..35bc5721 --- /dev/null +++ b/src/voxelgrid.cpp @@ -0,0 +1 @@ +#include "voxelgrid.h" diff --git a/src/voxelgrid.h b/src/voxelgrid.h new file mode 100644 index 00000000..e5e3012c --- /dev/null +++ b/src/voxelgrid.h @@ -0,0 +1,85 @@ +#ifndef DUST3D_VOXEL_GRID_H +#define DUST3D_VOXEL_GRID_H +#include +#include + +template +class VoxelGrid +{ +public: + struct Voxel + { + qint16 x; + qint16 y; + qint16 z; + }; + + struct VoxelHash + { + size_t operator()(const Voxel &voxel) const + { + return ((size_t)voxel.x ^ ((size_t)voxel.y << 1)) ^ (size_t)voxel.z; + } + }; + + struct VoxelEqual + { + bool operator()(const Voxel &left, const Voxel &right) const + { + return (left.x == right.x) && + (left.y == right.y) && + (left.z == right.z); + } + }; + + T query(qint16 x, qint16 y, qint16 z) + { + auto findResult = m_grid.find({x, y, z}); + if (findResult == m_grid.end()) + return m_nullValue; + return findResult->second; + } + + T add(qint16 x, qint16 y, qint16 z, T value) + { + auto insertResult = m_grid.insert(std::make_pair(Voxel {x, y, z}, value)); + if (insertResult.second) { + insertResult.first->second = m_nullValue + value; + return insertResult.first->second; + } + insertResult.first->second = insertResult.first->second + value; + return insertResult.first->second; + } + + T sub(qint16 x, qint16 y, qint16 z, T value) + { + auto findResult = m_grid.find({x, y, z}); + if (findResult == m_grid.end()) + return m_nullValue; + findResult->second = findResult->second - value; + if (findResult->second == m_nullValue) { + m_grid.erase(findResult); + return m_nullValue; + } + return findResult->second; + } + + void reset(qint16 x, qint16 y, qint16 z) + { + auto findResult = m_grid.find({x, y, z}); + if (findResult == m_grid.end()) + return; + m_grid.erase(findResult); + } + + void setNullValue(const T &nullValue) + { + m_nullValue = nullValue; + } + +private: + std::unordered_map m_grid; + T m_nullValue = T(); +}; + +#endif diff --git a/thirdparty/Qt-Color-Widgets/.gitignore b/thirdparty/Qt-Color-Widgets/.gitignore new file mode 100644 index 00000000..567609b1 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/thirdparty/Qt-Color-Widgets/CMakeLists.txt b/thirdparty/Qt-Color-Widgets/CMakeLists.txt new file mode 100644 index 00000000..25f65ee6 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/CMakeLists.txt @@ -0,0 +1,171 @@ +# +# Copyright (C) 2013-2020 Mattia Basaglia +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# + + +cmake_minimum_required (VERSION 3.1 FATAL_ERROR) + +set(COLORWIDGET_PROJECT_NAME QtColorWidgets) +project(${COLORWIDGET_PROJECT_NAME} CXX) + + +set (CMAKE_MODULE_PATH + ${CMAKE_MODULE_PATH} + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules + ${CMAKE_CURRENT_LIST_DIR}) + + +include (CheckCXXCompilerFlag) + +include (cmake/install.cmake) +include (cmake/qt-configuration.cmake) +include (cmake/versioning.cmake) + + +SET (${COLORWIDGET_PROJECT_NAME}_VERSION_MAJOR 2) +SET (${COLORWIDGET_PROJECT_NAME}_VERSION_MINOR 1) +SET (${COLORWIDGET_PROJECT_NAME}_VERSION_PATCH 0) +SET (${COLORWIDGET_PROJECT_NAME}_VERSION "${${COLORWIDGET_PROJECT_NAME}_VERSION_MAJOR}.${${COLORWIDGET_PROJECT_NAME}_VERSION_MINOR}.${${COLORWIDGET_PROJECT_NAME}_VERSION_PATCH}") + + +set (QT_SUPPORTED_VERSIONS 5) + +select_qt ( + QT_SUPPORTED_VERSIONS "${QT_SUPPORTED_VERSIONS}" + QT_DEFAULT_VERSION 5) + +set (REQUIRED_QT_COMPONENTS + Widgets + ) +find_qt ( + QT_SUPPORTED_VERSIONS "${QT_SUPPORTED_VERSIONS}" + REQUIRED_COMPONENTS "${REQUIRED_QT_COMPONENTS}" + OPTIONAL_COMPONENTS "") + +# Auto generate moc files +set(CMAKE_AUTOMOC ON) +# Auto generate moc files +set(CMAKE_AUTOUIC ON) +# Auto generate moc files +set(CMAKE_AUTORCC ON) + + +option(BUILD_SHARED_LIBS "Build the shared library" ON) +option(BUILD_STATIC_LIBS "Build the static library" OFF) + +set (TARGET_NAME ${COLORWIDGET_PROJECT_NAME}) +set (TARGET_OUTPUT_SUFFIX "-Qt${QT_VERSION}${${COLORWIDGET_PROJECT_NAME}_VERSION_MAJOR}") +set (INCLUDE_PREFIX "QtColorWidgets") +set (COLOR_WIDGETS_LIBRARY "${TARGET_NAME}") + +if ( ${BUILD_STATIC_LIBS} ) + add_definitions(-DQTCOLORWIDGETS_STATICALLY_LINKED) +endif() + + +add_library (${TARGET_NAME} "") +set_target_properties(${TARGET_NAME} + PROPERTIES + EXPORT_NAME "${TARGET_NAME}${TARGET_OUTPUT_SUFFIX}") + + +set_target_properties (${TARGET_NAME} + PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED ON + VERSION ${${COLORWIDGET_PROJECT_NAME}_VERSION} + SOVERSION ${${COLORWIDGET_PROJECT_NAME}_VERSION_MAJOR} + "INTERFACE_${COLORWIDGET_PROJECT_NAME}_MAJOR_VERSION" ${${COLORWIDGET_PROJECT_NAME}_VERSION_MAJOR} + COMPATIBLE_INTERFACE_STRING "${COLORWIDGET_PROJECT_NAME}_MAJOR_VERSION" + COMPILE_DEFINITIONS QTCOLORWIDGETS_LIBRARY + OUTPUT_NAME "${TARGET_NAME}${TARGET_OUTPUT_SUFFIX}") + +check_cxx_compiler_flag ("-Wall" Wall_FLAG_SUPPORTED) + +if (Wall_FLAG_SUPPORTED) + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall") +endif (Wall_FLAG_SUPPORTED) + +check_cxx_compiler_flag ("-pedantic" pedantic_FLAG_SUPPORTED) + +if (pedantic_FLAG_SUPPORTED) + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -pedantic") +endif (pedantic_FLAG_SUPPORTED) + +check_cxx_compiler_flag ("-Wextra" Wextra_FLAG_SUPPORTED) + +if (Wextra_FLAG_SUPPORTED) + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wextra") +endif (Wextra_FLAG_SUPPORTED) + + +set (EXPORT_HEADER "${INCLUDE_PREFIX}/colorwidgets_global.hpp") + + +generate_versioning_information ( + TARGET_NAME ${TARGET_NAME} + EXPORT_HEADER ${EXPORT_HEADER} + EXPORT_MACRO QCP_EXPORT + VERSIONED_ENTITY ${COLORWIDGET_PROJECT_NAME} + INCLUDE_PREFIX ${INCLUDE_PREFIX} + COMPANY_NAME "Mattia Basaglia" + COMPANY_COPYRIGHT "Mattia Basaglia Copyright (C) 2013-2017" + FILE_DESCRIPTION "Color wheel widget and dialog for Qt${QT_VERSION}" +) + + +target_include_directories(${TARGET_NAME} + PUBLIC + $ + $ + PUBLIC + $ + $ + ) + + +add_subdirectory (${CMAKE_CURRENT_SOURCE_DIR}/include/${INCLUDE_PREFIX}) +add_subdirectory (${CMAKE_CURRENT_SOURCE_DIR}/resources/${INCLUDE_PREFIX}) +add_subdirectory (${CMAKE_CURRENT_SOURCE_DIR}/src/${INCLUDE_PREFIX}) + + +use_qt ( + TARGET_NAME ${TARGET_NAME} + QT_SUPPORTED_VERSIONS "${QT_SUPPORTED_VERSIONS}" + REQUIRED_COMPONENTS "${REQUIRED_QT_COMPONENTS}" + OPTIONAL_COMPONENTS "") + + +install_project ( + PROJECT_NAME ${COLORWIDGET_PROJECT_NAME} + TARGET_NAME ${TARGET_NAME} + TARGET_OUTPUT_SUFFIX ${TARGET_OUTPUT_SUFFIX} + EXPORT_HEADER ${EXPORT_HEADER} + INCLUDE_PREFIX ${INCLUDE_PREFIX} + HEADER_MATCHING_REGEX "^.*\.h$|^.*\.hpp$|^.*$" + VERSION_HEADER ${CMAKE_CURRENT_BINARY_DIR}/${COLORWIDGET_PROJECT_NAME}_version.h + NAMESPACE "") + + +add_subdirectory (gallery) + +option(QTCOLORWIDGETS_DESIGNER_PLUGIN "Build QtDesigner plugin" ON) +if (${QTCOLORWIDGETS_DESIGNER_PLUGIN}) + find_package (Qt5Designer QUIET) + if (Qt5Designer_FOUND) + add_subdirectory (color_widgets_designer_plugin) + endif(Qt5Designer_FOUND) +endif() diff --git a/thirdparty/Qt-Color-Widgets/COPYING b/thirdparty/Qt-Color-Widgets/COPYING new file mode 100644 index 00000000..65c5ca88 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/COPYING @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/thirdparty/Qt-Color-Widgets/LICENSE-EXCEPTION b/thirdparty/Qt-Color-Widgets/LICENSE-EXCEPTION new file mode 100644 index 00000000..06698128 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/LICENSE-EXCEPTION @@ -0,0 +1,14 @@ +Linking this library statically or dynamically with other modules is making a +combined work based on this library. Thus, the terms and conditions of the +GNU Lesser General Public License version 3 cover the whole combination. + +As a special exception, the copyright holders of this library give you +permission to combine this library with independent +modules to produce an executable, and to copy and distribute the resulting +executable under terms of any of the GNU General Public licenses, as published +by the Free Software Foundation, provided that you also meet, +for each linked independent module, the terms and conditions of the license of +that module. An independent module is a module which is not derived from or +based on this library. If you modify this library, you may extend this +exception to your version of the library, but you are not obliged to do so. +If you do not wish to do so, delete this exception statement from your version. diff --git a/thirdparty/Qt-Color-Widgets/QtColorWidgets.pc.in b/thirdparty/Qt-Color-Widgets/QtColorWidgets.pc.in new file mode 100644 index 00000000..701b124d --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/QtColorWidgets.pc.in @@ -0,0 +1,10 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=@CMAKE_INSTALL_LIBDIR@ +includedir=${prefix}/include + +Name: QtColorWidgets +Description: Color wheel widget and dialog for Qt +Version: @QtColorWidgets_VERSION@ +Libs: -L${libdir} -l@i_target_name@@i_target_output_suffix@ +Cflags: -I${includedir}/@i_project_name@ -I${includedir} diff --git a/thirdparty/Qt-Color-Widgets/README.md b/thirdparty/Qt-Color-Widgets/README.md new file mode 100644 index 00000000..2c6815ef --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/README.md @@ -0,0 +1,66 @@ +Color Widgets +============= + +Here is a color dialog that is more user-friendly than the default QColorDialog +and several other color-related widgets + +The provided widgets are: + +* ColorWheel, An analog widget used to select a color +* ColorPreview, A simple widget that displays a color +* GradientSlider, A slider that has a gradient background +* HueSlider, A variant of GradientSlider that has a rainbow background +* ColorSelector, A ColorPreview that shows a ColorDialog when clicked +* ColorDialog, A dialog that uses the above widgets to provide a better user experience than QColorDialog +* ColorListWidget, A widget to edit a list of colors +* Swatch, A widget to display a color palette +* ColorPaletteWidget, A widget to use and manage a list of palettes +* Color2DSlider, An analog widget used to select 2 color components +* ColorLineEdit, A widget to manipulate a string representing a color +* HarmonyColorWheel, A ColorWheel which allows defining multiple colors, separated by hue +* GradientListModel, A QAbstractListModel used to list gradients (useful for combo boxes, list views and the like) + +they are all in the color_widgets namespace. + +See [the gallery](gallery/README.md) for more information and screenshots. + + +Using it in a project +--------------------- + +For QMake-based projects, include color_widgets.pri in the QMake project file. +For CMake-based projects, add this as subdirectory, it will be compiled as a +library and you can link the required targets to ColorWidgets. +All the required files are in ./src and ./include. + + +Installing as a Qt Designer/Creator Plugin +------------------------------------------ + +The sources for the designer plugin are in ./color_widgets_designer_plugin + +Compile the library and install in +(Qt SDK)/Tools/QtCreator/bin/designer/ +(Qt SDK)/(Qt Version)/(Toolchain)/plugins/designer + + mkdir build && cd build && cmake .. && make QtColorWidgetsPlugin && make install + + +Latest Version +-------------- + +The latest version of the sources can be found at the following locations: + +* https://gitlab.com/mattia.basaglia/Qt-Color-Widgets +* git://gitlab.com/mattia.basaglia/Qt-Color-Widgets.git + + +License +------- + +LGPLv3+, See COPYING. +As a special exception, this library can be included in any project under the +terms of any of the GNU liceses, distributing the whole project under a +different GNU license, see LICENSE-EXCEPTION for details. + +Copyright (C) 2013-2020 Mattia Basaglia diff --git a/thirdparty/Qt-Color-Widgets/cmake/QtColorWidgets-config.cmake b/thirdparty/Qt-Color-Widgets/cmake/QtColorWidgets-config.cmake new file mode 100644 index 00000000..9072b088 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/cmake/QtColorWidgets-config.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_LIST_DIR}/@CMAKE_BASE_FILE_NAME@@CMAKE_FILE_OUTPUT_SUFFIX@-targets.cmake") diff --git a/thirdparty/Qt-Color-Widgets/cmake/install.cmake b/thirdparty/Qt-Color-Widgets/cmake/install.cmake new file mode 100644 index 00000000..cd35415a --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/cmake/install.cmake @@ -0,0 +1,88 @@ +function (install_project + PROJECT_NAME i_project_name + TARGET_NAME i_target_name + TARGET_OUTPUT_SUFFIX i_target_output_suffix + EXPORT_HEADER i_export_header + INCLUDE_PREFIX i_include_prefix + HEADER_MATCHING_REGEX i_header_matching_regex + VERSION_HEADER i_version_header + NAMESPACE i_namespace) + install (TARGETS ${i_target_name} + EXPORT ${i_target_name} + RUNTIME DESTINATION bin + INCLUDES DESTINATION include + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib) + + install (DIRECTORY include/ + DESTINATION include + FILES_MATCHING + REGEX ${i_header_matching_regex} + REGEX "CMakeLists\.txt" EXCLUDE) + + install (FILES ${i_version_header} + DESTINATION include/${i_include_prefix} + COMPONENT Devel) + + install (FILES include/${i_export_header} + DESTINATION include/${i_include_prefix} + COMPONENT Devel) + + install( + EXPORT ${i_target_name} + DESTINATION lib/cmake/${i_target_name}${i_target_output_suffix} + FILE "${i_target_name}${i_target_output_suffix}.cmake" + COMPONENT Devel) + + + include(CMakePackageConfigHelpers) + + string (TOLOWER ${i_target_name} CMAKE_BASE_FILE_NAME) + string (TOLOWER ${i_target_output_suffix} CMAKE_FILE_OUTPUT_SUFFIX) + + write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/${i_target_name}/${CMAKE_BASE_FILE_NAME}${CMAKE_FILE_OUTPUT_SUFFIX}-config-version.cmake" + VERSION ${${i_project_name}_VERSION} + COMPATIBILITY SameMajorVersion + ) + + export(EXPORT ${i_target_name} + FILE "${CMAKE_CURRENT_BINARY_DIR}/${i_target_name}/${CMAKE_BASE_FILE_NAME}${CMAKE_FILE_OUTPUT_SUFFIX}-targets.cmake" + ) + + configure_file("cmake/${i_project_name}-config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/${i_target_name}/${CMAKE_BASE_FILE_NAME}${CMAKE_FILE_OUTPUT_SUFFIX}-config.cmake" + @ONLY + ) + + set(ConfigPackageLocation "lib/cmake/${i_target_name}${i_target_output_suffix}") + install(EXPORT ${i_target_name} + FILE + "${CMAKE_BASE_FILE_NAME}${CMAKE_FILE_OUTPUT_SUFFIX}-targets.cmake" + NAMESPACE + "${i_namespace}" + DESTINATION + ${ConfigPackageLocation} + ) + install( + FILES + "${CMAKE_CURRENT_BINARY_DIR}/${i_target_name}/${CMAKE_BASE_FILE_NAME}${CMAKE_FILE_OUTPUT_SUFFIX}-config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/${i_target_name}/${CMAKE_BASE_FILE_NAME}${CMAKE_FILE_OUTPUT_SUFFIX}-config-version.cmake" + DESTINATION + ${ConfigPackageLocation} + COMPONENT + Devel + ) + + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/${i_project_name}.pc.in + ${CMAKE_CURRENT_BINARY_DIR}/${i_target_name}${i_target_output_suffix}.pc + @ONLY + ) + + install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/${i_target_name}${i_target_output_suffix}.pc + DESTINATION + lib/pkgconfig + ) +endfunction (install_project) diff --git a/thirdparty/Qt-Color-Widgets/cmake/modules/ProjectVersioning.cmake b/thirdparty/Qt-Color-Widgets/cmake/modules/ProjectVersioning.cmake new file mode 100644 index 00000000..6975a968 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/cmake/modules/ProjectVersioning.cmake @@ -0,0 +1,80 @@ +define_property (TARGET + PROPERTY GIT_DESCRIBE + BRIEF_DOCS "Advanced version info for developers" + FULL_DOCS "String with information that is important for developers during + development process. This information includes git commit hash, durty status + of repo, distance from the last tag.") + +define_property (TARGET + PROPERTY GIT_UNTRACKED_FILES + BRIEF_DOCS "Information about presence of untracked files" + FULL_DOCS "Used in helper functions generation to add .with-untracked suffix + to version string. Suffix is only added if there are some untracked not + ignored files in repository.") + +set(HERE_DIR ${CMAKE_CURRENT_LIST_DIR}) + + +function (target_version_information + TARGET_NAME i_target_name + EXPORT_HEADER i_export_header + EXPORT_MACRO i_export_macro + VERSIONED_ENTITY i_versioned_entity) + + find_file ( + headerFileTemplate + "ProjectVersioning/version.h.in" + PATHS ${CMAKE_MODULE_PATH}) + + if ( NOT ${headerFileTemplate} ) + set(headerFileTemplate "${HERE_DIR}/ProjectVersioning/version.h.in") + endif() + + find_file ( + sourceFileTemplate + "ProjectVersioning/version.c.in" + PATHS ${CMAKE_MODULE_PATH}) + + if ( NOT ${sourceFileTemplate} ) + set(sourceFileTemplate "${HERE_DIR}/ProjectVersioning/version.c.in") + endif() + + exec_program ( + "git" + ${CMAKE_SOURCE_DIR} + ARGS "describe --always --dirty --long --tags" + OUTPUT_VARIABLE gitDescribe) + + exec_program ( + "git" + ${CMAKE_SOURCE_DIR} + ARGS "ls-files --others --exclude-standard" + OUTPUT_VARIABLE gitUntracked) + + if (gitUntracked) + set (gitUntracked ".with-untracked") + endif (gitUntracked) + + configure_file ( + "${headerFileTemplate}" + "${CMAKE_CURRENT_BINARY_DIR}/${i_versioned_entity}_version.h") + + configure_file( + "${sourceFileTemplate}" + "${CMAKE_BINARY_DIR}/${i_versioned_entity}_version.c") + + target_sources ("${i_target_name}" + PRIVATE + $ + $ + PRIVATE + "${CMAKE_BINARY_DIR}/${i_versioned_entity}_version.c") + + set_target_properties (${i_target_name} + PROPERTIES + GIT_DESCRIBE "${gitDescribe}" + GIT_UNTRACKED_FILES "${gitUntracked}") + + unset (headerFileTemplate PARENT_SCOPE) + unset (sourceFileTemplate PARENT_SCOPE) +endfunction (target_version_information) diff --git a/thirdparty/Qt-Color-Widgets/cmake/modules/ProjectVersioning/version.c.in b/thirdparty/Qt-Color-Widgets/cmake/modules/ProjectVersioning/version.c.in new file mode 100644 index 00000000..5786e0db --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/cmake/modules/ProjectVersioning/version.c.in @@ -0,0 +1,34 @@ +#include "${i_versioned_entity}_version.h" + + +unsigned int @i_versioned_entity@_versionMajor () { + return ${${i_versioned_entity}_VERSION_MAJOR}; +} + + +unsigned int @i_versioned_entity@_versionMinor () { + return ${${i_versioned_entity}_VERSION_MINOR}; +} + + +unsigned int @i_versioned_entity@_versionPatch () { + return ${${i_versioned_entity}_VERSION_PATCH}; +} + + +const char* @i_versioned_entity@_versionGitInfo () { + return + "${gitDescribe}" + "${gitUntracked}"; +} + + +const char* @i_versioned_entity@_versionFullString () { + return + "${${i_versioned_entity}_VERSION_MAJOR}." + "${${i_versioned_entity}_VERSION_MINOR}." + "${${i_versioned_entity}_VERSION_PATCH}" + "${${i_versioned_entity}_VERSION_PRE_RELEASE}" + "+${gitDescribe}" + "${gitUntracked}"; +} diff --git a/thirdparty/Qt-Color-Widgets/cmake/modules/ProjectVersioning/version.h.in b/thirdparty/Qt-Color-Widgets/cmake/modules/ProjectVersioning/version.h.in new file mode 100644 index 00000000..c4c57ab0 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/cmake/modules/ProjectVersioning/version.h.in @@ -0,0 +1,23 @@ +#ifndef @CMAKE_PROJECT_NAME@_VERSION_H +#define @CMAKE_PROJECT_NAME@_VERSION_H + + +#include "@i_export_header@" + + +@i_export_macro@ unsigned int @i_versioned_entity@_versionMajor (); + + +@i_export_macro@ unsigned int @i_versioned_entity@_versionMinor (); + + +@i_export_macro@ unsigned int @i_versioned_entity@_versionPatch (); + + +@i_export_macro@ const char* @i_versioned_entity@_versionGitInfo (); + + +@i_export_macro@ const char* @i_versioned_entity@_versionFullString (); + + +#endif /* @CMAKE_PROJECT_NAME@_VERSION_H */ diff --git a/thirdparty/Qt-Color-Widgets/cmake/modules/VersionInfo.in b/thirdparty/Qt-Color-Widgets/cmake/modules/VersionInfo.in new file mode 100644 index 00000000..3dfd3510 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/cmake/modules/VersionInfo.in @@ -0,0 +1,113 @@ +/* + The MIT License (MIT) + + Copyright (c) 2015, by [halex2005](mailto:akharlov@gmail.com) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + + +#pragma once + +#ifndef PRODUCT_VERSION_MAJOR +#define PRODUCT_VERSION_MAJOR @PRODUCT_VERSION_MAJOR@ +#endif + +#ifndef PRODUCT_VERSION_MINOR +#define PRODUCT_VERSION_MINOR @PRODUCT_VERSION_MINOR@ +#endif + +#ifndef PRODUCT_VERSION_PATCH +#define PRODUCT_VERSION_PATCH @PRODUCT_VERSION_PATCH@ +#endif + +#ifndef PRODUCT_VERSION_BUILD +#define PRODUCT_VERSION_BUILD @PRODUCT_VERSION_REVISION@ +#endif + +#ifndef FILE_VERSION_MAJOR +#define FILE_VERSION_MAJOR @PRODUCT_VERSION_MAJOR@ +#endif + +#ifndef FILE_VERSION_MINOR +#define FILE_VERSION_MINOR @PRODUCT_VERSION_MINOR@ +#endif + +#ifndef FILE_VERSION_PATCH +#define FILE_VERSION_PATCH @PRODUCT_VERSION_PATCH@ +#endif + +#ifndef FILE_VERSION_BUILD +#define FILE_VERSION_BUILD @PRODUCT_VERSION_REVISION@ +#endif + +#ifndef __TO_STRING +#define __TO_STRING_IMPL(x) #x +#define __TO_STRING(x) __TO_STRING_IMPL(x) +#endif + +#define PRODUCT_VERSION_MAJOR_MINOR_STR __TO_STRING(PRODUCT_VERSION_MAJOR) "." __TO_STRING(PRODUCT_VERSION_MINOR) +#define PRODUCT_VERSION_MAJOR_MINOR_PATCH_STR PRODUCT_VERSION_MAJOR_MINOR_STR "." __TO_STRING(PRODUCT_VERSION_PATCH) +#define PRODUCT_VERSION_FULL_STR PRODUCT_VERSION_MAJOR_MINOR_PATCH_STR "." __TO_STRING(PRODUCT_VERSION_BUILD) +#define PRODUCT_VERSION_RESOURCE PRODUCT_VERSION_MAJOR,PRODUCT_VERSION_MINOR,PRODUCT_VERSION_PATCH,PRODUCT_VERSION_BUILD +#define PRODUCT_VERSION_RESOURCE_STR PRODUCT_VERSION_FULL_STR "\0" + +#define FILE_VERSION_MAJOR_MINOR_STR __TO_STRING(FILE_VERSION_MAJOR) "." __TO_STRING(FILE_VERSION_MINOR) +#define FILE_VERSION_MAJOR_MINOR_PATCH_STR FILE_VERSION_MAJOR_MINOR_STR "." __TO_STRING(FILE_VERSION_PATCH) +#define FILE_VERSION_FULL_STR FILE_VERSION_MAJOR_MINOR_PATCH_STR "." __TO_STRING(FILE_VERSION_BUILD) +#define FILE_VERSION_RESOURCE FILE_VERSION_MAJOR,FILE_VERSION_MINOR,FILE_VERSION_PATCH,FILE_VERSION_BUILD +#define FILE_VERSION_RESOURCE_STR FILE_VERSION_FULL_STR "\0" + +#ifndef USE_ICON +#define USE_ICON @USE_ICON@ +#endif + +#if USE_ICON +#ifndef PRODUCT_ICON +#define PRODUCT_ICON "@PRODUCT_ICON@" +#endif +#endif + +#ifndef PRODUCT_COMMENTS +#define PRODUCT_COMMENTS "@PRODUCT_COMMENTS@\0" +#endif + +#ifndef PRODUCT_COMPANY_NAME +#define PRODUCT_COMPANY_NAME "@PRODUCT_COMPANY_NAME@\0" +#endif + +#ifndef PRODUCT_COMPANY_COPYRIGHT +#define PRODUCT_COMPANY_COPYRIGHT "@PRODUCT_COMPANY_COPYRIGHT@\0" +#endif + +#ifndef PRODUCT_FILE_DESCRIPTION +#define PRODUCT_FILE_DESCRIPTION "@PRODUCT_FILE_DESCRIPTION@\0" +#endif + +#ifndef PRODUCT_INTERNAL_NAME +#define PRODUCT_INTERNAL_NAME "@PRODUCT_NAME@\0" +#endif + +#ifndef PRODUCT_ORIGINAL_FILENAME +#define PRODUCT_ORIGINAL_FILENAME "@PRODUCT_ORIGINAL_FILENAME@\0" +#endif + +#ifndef PRODUCT_BUNDLE +#define PRODUCT_BUNDLE "@PRODUCT_BUNDLE@\0" +#endif diff --git a/thirdparty/Qt-Color-Widgets/cmake/modules/VersionResource.rc b/thirdparty/Qt-Color-Widgets/cmake/modules/VersionResource.rc new file mode 100644 index 00000000..838db86b --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/cmake/modules/VersionResource.rc @@ -0,0 +1,66 @@ +/* + The MIT License (MIT) + + Copyright (c) 2015, by [halex2005](mailto:akharlov@gmail.com) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#include "VersionInfo.h" +#include "winresrc.h" + +#if USE_ICON +IDI_ICON1 ICON PRODUCT_ICON +#endif + +LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT + +VS_VERSION_INFO VERSIONINFO + FILEVERSION FILE_VERSION_RESOURCE + PRODUCTVERSION PRODUCT_VERSION_RESOURCE + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "041904b0" + BEGIN + VALUE "Comments", PRODUCT_COMMENTS + VALUE "CompanyName", PRODUCT_COMPANY_NAME + VALUE "FileDescription", PRODUCT_FILE_DESCRIPTION + VALUE "FileVersion", FILE_VERSION_RESOURCE_STR + VALUE "InternalName", PRODUCT_INTERNAL_NAME + VALUE "LegalCopyright", PRODUCT_COMPANY_COPYRIGHT + VALUE "OriginalFilename", PRODUCT_ORIGINAL_FILENAME + VALUE "ProductName", PRODUCT_BUNDLE + VALUE "ProductVersion", PRODUCT_VERSION_RESOURCE_STR + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x419, 1200 + END +END diff --git a/thirdparty/Qt-Color-Widgets/cmake/modules/generate_product_version.cmake b/thirdparty/Qt-Color-Widgets/cmake/modules/generate_product_version.cmake new file mode 100644 index 00000000..2880f4a0 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/cmake/modules/generate_product_version.cmake @@ -0,0 +1,136 @@ +# The MIT License (MIT) + +# Copyright (c) 2015, by [halex2005](mailto:akharlov@gmail.com) + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +include (CMakeParseArguments) + +set (GenerateProductVersionCurrentDir ${CMAKE_CURRENT_LIST_DIR}) + +# generate_product_version() function +# +# This function uses VersionInfo.in template file and VersionResource.rc file +# to generate WIN32 resource with version information and general resource strings. +# +# Usage: +# generate_product_version( +# SomeOutputResourceVariable +# NAME MyGreatProject +# ICON ${PATH_TO_APP_ICON} +# VERSION_MAJOR 2 +# VERSION_MINOR 3 +# VERSION_PATH ${BUILD_COUNTER} +# VERSION_REVISION ${BUILD_REVISION} +# ) +# where BUILD_COUNTER and BUILD_REVISION could be values from your CI server. +# +# You can use generated resource for your executable targets: +# add_executable(target-name ${target-files} ${SomeOutputResourceVariable}) +# +# You can specify resource strings in arguments: +# NAME - name of executable (no defaults, ex: Microsoft Word) +# BUNDLE - bundle (${NAME} is default, ex: Microsoft Office) +# USE_ICON - flag that shows whether icon is used or not +# ICON - path to application icon (${CMAKE_SOURCE_DIR}/product.ico by default) +# VERSION_MAJOR - 1 is default +# VERSION_MINOR - 0 is default +# VERSION_PATCH - 0 is default +# VERSION_REVISION - 0 is default +# COMPANY_NAME - your company name (no defaults) +# COMPANY_COPYRIGHT - ${COMPANY_NAME} (C) Copyright ${CURRENT_YEAR} is default +# COMMENTS - ${NAME} v${VERSION_MAJOR}.${VERSION_MINOR} is default +# ORIGINAL_FILENAME - ${NAME} is default +# INTERNAL_NAME - ${NAME} is default +# FILE_DESCRIPTION - ${NAME} is default +function(generate_product_version outfiles) + set (options) + set (oneValueArgs + NAME + BUNDLE + USE_ICON + ICON + VERSION_MAJOR + VERSION_MINOR + VERSION_PATCH + VERSION_REVISION + COMPANY_NAME + COMPANY_COPYRIGHT + COMMENTS + ORIGINAL_FILENAME + INTERNAL_NAME + FILE_DESCRIPTION) + set (multiValueArgs) + cmake_parse_arguments(PRODUCT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if (NOT PRODUCT_BUNDLE OR "${PRODUCT_BUNDLE}" STREQUAL "") + set(PRODUCT_BUNDLE "${PRODUCT_NAME}") + endif() + + if (USE_ICON) + if (NOT PRODUCT_ICON OR "${PRODUCT_ICON}" STREQUAL "") + set(PRODUCT_ICON "${CMAKE_SOURCE_DIR}/product.ico") + endif() + else () + set (USE_ICON 0) + endif () + + if (NOT PRODUCT_VERSION_MAJOR EQUAL 0 AND (NOT PRODUCT_VERSION_MAJOR OR "${PRODUCT_VERSION_MAJOR}" STREQUAL "")) + set(PRODUCT_VERSION_MAJOR 1) + endif() + if (NOT PRODUCT_VERSION_MINOR EQUAL 0 AND (NOT PRODUCT_VERSION_MINOR OR "${PRODUCT_VERSION_MINOR}" STREQUAL "")) + set(PRODUCT_VERSION_MINOR 0) + endif() + if (NOT PRODUCT_VERSION_PATCH EQUAL 0 AND (NOT PRODUCT_VERSION_PATCH OR "${PRODUCT_VERSION_PATCH}" STREQUAL "")) + set(PRODUCT_VERSION_PATCH 0) + endif() + if (NOT PRODUCT_VERSION_REVISION EQUAL 0 AND (NOT PRODUCT_VERSION_REVISION OR "${PRODUCT_VERSION_REVISION}" STREQUAL "")) + set(PRODUCT_VERSION_REVISION 0) + endif() + + if (NOT PRODUCT_COMPANY_COPYRIGHT OR "${PRODUCT_COMPANY_COPYRIGHT}" STREQUAL "") + string(TIMESTAMP PRODUCT_CURRENT_YEAR "%Y") + set(PRODUCT_COMPANY_COPYRIGHT "${PRODUCT_COMPANY_NAME} (C) Copyright ${PRODUCT_CURRENT_YEAR}") + endif() + if (NOT PRODUCT_COMMENTS OR "${PRODUCT_COMMENTS}" STREQUAL "") + set(PRODUCT_COMMENTS "${PRODUCT_NAME} v${PRODUCT_VERSION_MAJOR}.${PRODUCT_VERSION_MINOR}") + endif() + if (NOT PRODUCT_ORIGINAL_FILENAME OR "${PRODUCT_ORIGINAL_FILENAME}" STREQUAL "") + set(PRODUCT_ORIGINAL_FILENAME "${PRODUCT_NAME}") + endif() + if (NOT PRODUCT_INTERNAL_NAME OR "${PRODUCT_INTERNAL_NAME}" STREQUAL "") + set(PRODUCT_INTERNAL_NAME "${PRODUCT_NAME}") + endif() + if (NOT PRODUCT_FILE_DESCRIPTION OR "${PRODUCT_FILE_DESCRIPTION}" STREQUAL "") + set(PRODUCT_FILE_DESCRIPTION "${PRODUCT_NAME}") + endif() + + set (_VersionInfoFile ${CMAKE_CURRENT_BINARY_DIR}/VersionInfo.h) + set (_VersionResourceFile ${CMAKE_CURRENT_BINARY_DIR}/VersionResource.rc) + configure_file( + ${GenerateProductVersionCurrentDir}/VersionInfo.in + ${_VersionInfoFile} + @ONLY) + configure_file( + ${GenerateProductVersionCurrentDir}/VersionResource.rc + ${_VersionResourceFile} + COPYONLY) + list(APPEND ${outfiles} ${_VersionInfoFile} ${_VersionResourceFile}) + set (${outfiles} ${${outfiles}} PARENT_SCOPE) +endfunction() diff --git a/thirdparty/Qt-Color-Widgets/cmake/qt-configuration.cmake b/thirdparty/Qt-Color-Widgets/cmake/qt-configuration.cmake new file mode 100644 index 00000000..49c345c2 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/cmake/qt-configuration.cmake @@ -0,0 +1,58 @@ +function (select_qt + QT_SUPPORTED_VERSIONS i_qt_supported_versions + QT_DEFAULT_VERSION i_default_qt_version) + set (QT_VERSION ${i_default_qt_version} CACHE STRING + "Qt version to build against") + + set_property ( + CACHE QT_VERSION + PROPERTY STRINGS ${i_qt_supported_versions}) +endfunction (select_qt) + + +macro (find_qt + QT_SUPPORTED_VERSIONS i_qt_supported_versions + REQUIRED_COMPONENTS i_required_components + OPTIONAL_COMPONENTS i_optional_components) + # Auto generate moc files + if (4 EQUAL QT_VERSION) + find_package (Qt4 4.8 REQUIRED + COMPONENTS + ${i_required_components} + OPTIONAL_COMPONENTS + ${i_optional_components}) + elseif (5 EQUAL QT_VERSION) + find_package (Qt5 5.9 REQUIRED + COMPONENTS + ${i_required_components} + OPTIONAL_COMPONENTS + ${i_optional_components}) + else (5 EQUAL QT_VERSION) + message (FATAL_ERROR "Unsupported version of Qt: ${QT_VERSION}") + endif (4 EQUAL QT_VERSION) +endmacro (find_qt) + + +function (use_qt + TARGET_NAME i_target_name + QT_SUPPORTED_VERSIONS i_qt_supported_versions + REQUIRED_COMPONENTS i_required_components + OPTIONAL_COMPONENTS i_optional_components) + if (4 EQUAL QT_VERSION) + foreach (COMPONENT IN LISTS i_required_components, i_optional_components) + target_link_libraries (${i_target_name} + PRIVATE + Qt4::${COMPONENT} + ) + endforeach (COMPONENT IN LISTS i_required_components, i_optional_components) + elseif (5 EQUAL QT_VERSION) + foreach (COMPONENT IN LISTS i_required_components) + target_link_libraries (${i_target_name} + PRIVATE + Qt5::${COMPONENT} + ) + endforeach (COMPONENT IN LISTS i_required_components, i_optional_components) + else (5 EQUAL QT_VERSION) + message (FATAL_ERROR "Unsupported version of Qt: ${QT_VERSION}") + endif (4 EQUAL QT_VERSION) +endfunction (use_qt) diff --git a/thirdparty/Qt-Color-Widgets/cmake/versioning.cmake b/thirdparty/Qt-Color-Widgets/cmake/versioning.cmake new file mode 100644 index 00000000..4a5ccd32 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/cmake/versioning.cmake @@ -0,0 +1,43 @@ +include (ProjectVersioning) + +function (generate_versioning_information + TARGET_NAME i_target_name + EXPORT_HEADER i_export_header + EXPORT_MACRO i_export_macro + VERSIONED_ENTITY i_versioned_entity + INCLUDE_PREFIX i_include_prefix + COMPANY_NAME i_company_name + COMPANY_COPYRIGHT i_company_copyright + FILE_DESCRIPTION i_file_description +) + target_version_information ( + TARGET_NAME ${i_target_name} + EXPORT_HEADER ${i_export_header} + EXPORT_MACRO ${i_export_macro} + VERSIONED_ENTITY ${i_versioned_entity} + ) + + if (WIN32) + include (generate_product_version) + + get_target_property (gitDescribe + ${i_target_name} GIT_DESCRIBE) + + get_target_property (gitUntracked + ${i_target_name} GIT_UNTRACKED_FILES) + + generate_product_version ( + win32VersionInfoFiles + NAME ${i_versioned_entity} + VERSION_MAJOR ${${i_versioned_entity}_VERSION_MAJOR} + VERSION_MINOR ${${i_versioned_entity}_VERSION_MINOR} + VERSION_PATCH ${${i_versioned_entity}_VERSION_PATCH} + COMPANY_NAME ${i_company_name} + COMPANY_COPYRIGHT ${i_company_copyright} + COMMENTS "${gitDescribe}${gitUntracked}" + FILE_DESCRIPTION ${i_file_description} + ) + + target_sources (${i_target_name} PRIVATE ${win32VersionInfoFiles}) + endif (WIN32) +endfunction (generate_versioning_information) diff --git a/thirdparty/Qt-Color-Widgets/color_widgets.pri b/thirdparty/Qt-Color-Widgets/color_widgets.pri new file mode 100644 index 00000000..4f69ec30 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/color_widgets.pri @@ -0,0 +1,77 @@ +# Copyright (C) 2013-2019 Mattia Basaglia +# +# +# This software is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This software is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Color Widgets. If not, see . + +CONFIG += c++11 + +INCLUDEPATH += $$PWD/src $$PWD/include + +SOURCES += \ + $$PWD/src/QtColorWidgets/abstract_widget_list.cpp \ + $$PWD/src/QtColorWidgets/bound_color_selector.cpp \ + $$PWD/src/QtColorWidgets/color_2d_slider.cpp \ + $$PWD/src/QtColorWidgets/color_delegate.cpp \ + $$PWD/src/QtColorWidgets/color_dialog.cpp \ + $$PWD/src/QtColorWidgets/color_line_edit.cpp \ + $$PWD/src/QtColorWidgets/color_list_widget.cpp \ + $$PWD/src/QtColorWidgets/color_names.cpp \ + $$PWD/src/QtColorWidgets/color_palette.cpp \ + $$PWD/src/QtColorWidgets/color_palette_model.cpp \ + $$PWD/src/QtColorWidgets/color_palette_widget.cpp \ + $$PWD/src/QtColorWidgets/color_preview.cpp \ + $$PWD/src/QtColorWidgets/color_selector.cpp \ + $$PWD/src/QtColorWidgets/color_utils.cpp \ + $$PWD/src/QtColorWidgets/color_wheel.cpp \ + $$PWD/src/QtColorWidgets/gradient_editor.cpp \ + $$PWD/src/QtColorWidgets/gradient_list_model.cpp \ + $$PWD/src/QtColorWidgets/gradient_slider.cpp \ + $$PWD/src/QtColorWidgets/harmony_color_wheel.cpp \ + $$PWD/src/QtColorWidgets/hue_slider.cpp \ + $$PWD/src/QtColorWidgets/swatch.cpp + +HEADERS += \ + $$PWD/include/QtColorWidgets/abstract_widget_list.hpp \ + $$PWD/include/QtColorWidgets/bound_color_selector.hpp \ + $$PWD/include/QtColorWidgets/color_2d_slider.hpp \ + $$PWD/include/QtColorWidgets/color_delegate.hpp \ + $$PWD/include/QtColorWidgets/color_dialog.hpp \ + $$PWD/include/QtColorWidgets/color_line_edit.hpp \ + $$PWD/include/QtColorWidgets/color_list_widget.hpp \ + $$PWD/include/QtColorWidgets/color_names.hpp \ + $$PWD/include/QtColorWidgets/color_palette.hpp \ + $$PWD/include/QtColorWidgets/color_palette_model.hpp \ + $$PWD/include/QtColorWidgets/color_palette_widget.hpp \ + $$PWD/include/QtColorWidgets/color_preview.hpp \ + $$PWD/include/QtColorWidgets/color_selector.hpp \ + $$PWD/include/QtColorWidgets/color_utils.hpp \ + $$PWD/include/QtColorWidgets/color_wheel.hpp \ + $$PWD/include/QtColorWidgets/color_wheel_private.hpp \ + $$PWD/include/QtColorWidgets/colorwidgets_global.hpp \ + $$PWD/include/QtColorWidgets/gradient_delegate.hpp \ + $$PWD/include/QtColorWidgets/gradient_editor.hpp \ + $$PWD/include/QtColorWidgets/gradient_helper.hpp \ + $$PWD/include/QtColorWidgets/gradient_list_model.hpp \ + $$PWD/include/QtColorWidgets/gradient_slider.hpp \ + $$PWD/include/QtColorWidgets/harmony_color_wheel.hpp \ + $$PWD/include/QtColorWidgets/hue_slider.hpp \ + $$PWD/include/QtColorWidgets/swatch.hpp + +FORMS += \ + $$PWD/src/QtColorWidgets/color_dialog.ui \ + $$PWD/src/QtColorWidgets/color_palette_widget.ui + +RESOURCES += \ + $$PWD/resources/QtColorWidgets/color_widgets.qrc + diff --git a/thirdparty/Qt-Color-Widgets/color_widgets.pro b/thirdparty/Qt-Color-Widgets/color_widgets.pro new file mode 100644 index 00000000..7969cd93 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/color_widgets.pro @@ -0,0 +1,53 @@ +# +# Copyright (C) 2013-2020 Mattia Basaglia +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +TEMPLATE=lib +CONFIG += dll +QT += core gui widgets +DEFINES += QTCOLORWIDGETS_LIBRARY + +TARGET=QtColorWidgets-Qt5 + +VERSION=2.0.0 + +OBJECTS_DIR = out/obj +MOC_DIR = out/generated +UI_DIR = out/generated +RCC_DIR = out/generated + +include(color_widgets.pri) + +build_all:!build_pass { + CONFIG -= build_all + CONFIG += release +} + +unix { + LIB_TARGET = lib$${TARGET}.so +} +win32 { + LIB_TARGET = $${TARGET}.dll +} + +isEmpty(PREFIX) { + PREFIX = /usr/local +} +target.path = $$PREFIX/lib +headers.path = $$PREFIX/include/QtColorWidgets +headers.files = $$HEADERS + +INSTALLS += target headers + diff --git a/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/CMakeLists.txt b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/CMakeLists.txt new file mode 100644 index 00000000..709eebc3 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/CMakeLists.txt @@ -0,0 +1,178 @@ +# Copyright (C) 2013-2020 Mattia Basaglia +# +# +# This software is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This software is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Color Widgets. If not, see . + +cmake_minimum_required (VERSION 3.1 FATAL_ERROR) + +project(QtColorWidgetsPlugin CXX) + + +set (CMAKE_MODULE_PATH + ${CMAKE_MODULE_PATH} + ${CMAKE_SOURCE_DIR}/cmake/modules + ${CMAKE_CURRENT_LIST_DIR}/..) + + +include (CheckCXXCompilerFlag) + +include (../cmake/qt-configuration.cmake) +include (../cmake/versioning.cmake) + + +SET (${PROJECT_NAME}_VERSION_MAJOR 2) +SET (${PROJECT_NAME}_VERSION_MINOR 0) +SET (${PROJECT_NAME}_VERSION_PATCH 0) +SET (${PROJECT_NAME}_VERSION "${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}.${${PROJECT_NAME}_VERSION_PATCH}") + + +set (QT_SUPPORTED_VERSIONS 5) + +select_qt ( + QT_SUPPORTED_VERSIONS "${QT_SUPPORTED_VERSIONS}" + QT_DEFAULT_VERSION 5) + +set (REQUIRED_QT_COMPONENTS + Designer + ) +find_qt ( + QT_SUPPORTED_VERSIONS "${QT_SUPPORTED_VERSIONS}" + REQUIRED_COMPONENTS "${REQUIRED_QT_COMPONENTS}" + OPTIONAL_COMPONENTS "") + +# Auto generate moc files +set(CMAKE_AUTOMOC ON) +# Auto generate moc files +set(CMAKE_AUTOUIC ON) +# Auto generate moc files +set(CMAKE_AUTORCC ON) + +check_cxx_compiler_flag ("-Wall" Wall_FLAG_SUPPORTED) + +if (Wall_FLAG_SUPPORTED) + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall") +endif (Wall_FLAG_SUPPORTED) + +check_cxx_compiler_flag ("-pedantic" pedantic_FLAG_SUPPORTED) + +if (pedantic_FLAG_SUPPORTED) + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -pedantic") +endif (pedantic_FLAG_SUPPORTED) + +check_cxx_compiler_flag ("-Wextra" Wextra_FLAG_SUPPORTED) + +if (Wextra_FLAG_SUPPORTED) + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wextra") +endif (Wextra_FLAG_SUPPORTED) + +# Library +set (TARGET_NAME ${PROJECT_NAME}) +set (TARGET_OUTPUT_SUFFIX "-Qt${QT_VERSION}${${PROJECT_NAME}_VERSION_MAJOR}") + +add_library (${TARGET_NAME} SHARED "") +set_target_properties(${TARGET_NAME} + PROPERTIES + EXPORT_NAME "${TARGET_NAME}${TARGET_OUTPUT_SUFFIX}") + +set_target_properties (${TARGET_NAME} + PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED ON + VERSION ${${PROJECT_NAME}_VERSION} + SOVERSION ${${PROJECT_NAME}_VERSION_MAJOR} + "INTERFACE_${PROJECT_NAME}_MAJOR_VERSION" ${${PROJECT_NAME}_VERSION_MAJOR} + COMPATIBLE_INTERFACE_STRING "${PROJECT_NAME}_MAJOR_VERSION" + OUTPUT_NAME "${TARGET_NAME}${TARGET_OUTPUT_SUFFIX}") + + +target_link_libraries(${TARGET_NAME} + PRIVATE + ${COLOR_WIDGETS_LIBRARY}) + +use_qt ( + TARGET_NAME ${TARGET_NAME} + QT_SUPPORTED_VERSIONS "${QT_SUPPORTED_VERSIONS}" + REQUIRED_COMPONENTS "${REQUIRED_QT_COMPONENTS}" + OPTIONAL_COMPONENTS "") + + +# Sources +set(SOURCES + color_preview_plugin.cpp + color_wheel_plugin.cpp + color_widget_plugin_collection.cpp + gradient_slider_plugin.cpp + hue_slider_plugin.cpp + color_selector_plugin.cpp + color_list_plugin.cpp + swatch_plugin.cpp + color_palette_widget_plugin.cpp + color_2d_slider_plugin.cpp + color_line_edit_plugin.cpp + gradient_editor_plugin.cpp + # add new sources above this line + ) + +foreach (SOURCE IN LISTS SOURCES) + target_sources (${TARGET_NAME} + PRIVATE + $) +endforeach (SOURCE IN SOURCES) + +set(HEADERS + color_preview_plugin.hpp + color_wheel_plugin.hpp + color_widget_plugin_collection.hpp + gradient_slider_plugin.hpp + hue_slider_plugin.hpp + color_selector_plugin.hpp + color_list_plugin.hpp + swatch_plugin.hpp + color_palette_widget_plugin.hpp + color_2d_slider_plugin.hpp + color_line_edit_plugin.hpp + gradient_editor_plugin.hpp + # add new headers above this line + ) + +foreach (HEADER IN LISTS HEADERS) + target_sources (${TARGET_NAME} + PRIVATE + $) +endforeach (HEADER IN HEADERS) + + +# install +get_target_property (QT_QMAKE_EXECUTABLE + Qt5::qmake LOCATION) + +execute_process ( + COMMAND ${QT_QMAKE_EXECUTABLE} -query QT_INSTALL_PLUGINS + OUTPUT_VARIABLE QT_INSTALL_PLUGINS + OUTPUT_STRIP_TRAILING_WHITESPACE) + +# install(TARGETS ${COLOR_WIDGETS_PLUGIN} DESTINATION ${QT_INSTALL_PLUGINS}/designer OPTIONAL) + +execute_process ( + COMMAND ${QT_QMAKE_EXECUTABLE} -query QT_INSTALL_LIBS + OUTPUT_VARIABLE QT_INSTALL_LIBS + OUTPUT_STRIP_TRAILING_WHITESPACE) + +# install(TARGETS ${COLOR_WIDGETS_PLUGIN} DESTINATION ${QT_INSTALL_LIBS}/qtcreator/plugins OPTIONAL) + +add_custom_target(${TARGET_NAME}_install + COMMAND cp $ ${QT_INSTALL_PLUGINS}/designer + COMMAND cp $ ${QT_INSTALL_LIBS}/qtcreator/plugins + DEPENDS ${TARGET_NAME} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_2d_slider_plugin.cpp b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_2d_slider_plugin.cpp new file mode 100644 index 00000000..b56503c5 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_2d_slider_plugin.cpp @@ -0,0 +1,90 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#include "color_2d_slider_plugin.hpp" +#include "QtColorWidgets/color_2d_slider.hpp" + +Color2DSlider_Plugin::Color2DSlider_Plugin(QObject *parent) : + QObject(parent), initialized(false) +{ +} + +void Color2DSlider_Plugin::initialize(QDesignerFormEditorInterface *) +{ + initialized = true; +} + +bool Color2DSlider_Plugin::isInitialized() const +{ + return initialized; +} + +QWidget *Color2DSlider_Plugin::createWidget(QWidget *parent) +{ + return new color_widgets::Color2DSlider(parent); +} + +QString Color2DSlider_Plugin::name() const +{ + return "color_widgets::Color2DSlider"; +} + +QString Color2DSlider_Plugin::group() const +{ + return "Color Widgets"; +} + +QIcon Color2DSlider_Plugin::icon() const +{ + color_widgets::Color2DSlider w; + w.resize(64,64); + QPixmap pix(64,64); + w.render(&pix); + return QIcon(pix); +} + +QString Color2DSlider_Plugin::toolTip() const +{ + return "An analog widget to select 2 color components at the same time"; +} + +QString Color2DSlider_Plugin::whatsThis() const +{ + return toolTip(); +} + +bool Color2DSlider_Plugin::isContainer() const +{ + return false; +} + +QString Color2DSlider_Plugin::domXml() const +{ + return "\n" + " \n" + " \n" + "\n"; +} + +QString Color2DSlider_Plugin::includeFile() const +{ + return "QtColorWidgets/color_2d_slider.hpp"; +} diff --git a/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_2d_slider_plugin.hpp b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_2d_slider_plugin.hpp new file mode 100644 index 00000000..cc360d2e --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_2d_slider_plugin.hpp @@ -0,0 +1,57 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#ifndef COLOR_WIDGETS_COLOR_2D_SLIDER_PLUGIN_HPP +#define COLOR_WIDGETS_COLOR_2D_SLIDER_PLUGIN_HPP + +#include +#include + +class Color2DSlider_Plugin : public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + Color2DSlider_Plugin(QObject *parent = 0); + + void initialize(QDesignerFormEditorInterface *core); + bool isInitialized() const; + + QWidget *createWidget(QWidget *parent); + + QString name() const; + QString group() const; + QIcon icon() const; + QString toolTip() const; + QString whatsThis() const; + bool isContainer() const; + + QString domXml() const; + + QString includeFile() const; + +private: + bool initialized; +}; + + +#endif // COLOR_WIDGETS_COLOR_2D_SLIDER_PLUGIN_HPP diff --git a/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_line_edit_plugin.cpp b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_line_edit_plugin.cpp new file mode 100644 index 00000000..56d7a5a2 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_line_edit_plugin.cpp @@ -0,0 +1,87 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#include "color_line_edit_plugin.hpp" +#include "QtColorWidgets/color_line_edit.hpp" + +QWidget* ColorLineEdit_Plugin::createWidget(QWidget *parent) +{ + color_widgets::ColorLineEdit *widget = new color_widgets::ColorLineEdit(parent); + return widget; +} + +QIcon ColorLineEdit_Plugin::icon() const +{ + return QIcon::fromTheme("edit-rename"); +} + +QString ColorLineEdit_Plugin::domXml() const +{ + return "\n" + " \n" + " \n" + "\n"; +} + +bool ColorLineEdit_Plugin::isContainer() const +{ + return false; +} + +ColorLineEdit_Plugin::ColorLineEdit_Plugin(QObject *parent) : + QObject(parent), initialized(false) +{ +} + +void ColorLineEdit_Plugin::initialize(QDesignerFormEditorInterface *) +{ + initialized = true; +} + +bool ColorLineEdit_Plugin::isInitialized() const +{ + return initialized; +} + +QString ColorLineEdit_Plugin::name() const +{ + return "color_widgets::ColorLineEdit"; +} + +QString ColorLineEdit_Plugin::group() const +{ + return "Color Widgets"; +} + +QString ColorLineEdit_Plugin::toolTip() const +{ + return "A widget to manipulate a string representing a color"; +} + +QString ColorLineEdit_Plugin::whatsThis() const +{ + return toolTip(); +} + +QString ColorLineEdit_Plugin::includeFile() const +{ + return "QtColorWidgets/color_line_edit.hpp"; +} diff --git a/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_line_edit_plugin.hpp b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_line_edit_plugin.hpp new file mode 100644 index 00000000..1388b5bd --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_line_edit_plugin.hpp @@ -0,0 +1,58 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#ifndef COLOR_WIDGETS_COLOR_LINE_EDIT_PLUGIN_HPP +#define COLOR_WIDGETS_COLOR_LINE_EDIT_PLUGIN_HPP + +#include +#include + +class ColorLineEdit_Plugin : public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + explicit ColorLineEdit_Plugin(QObject *parent = nullptr); + + void initialize(QDesignerFormEditorInterface *core) Q_DECL_OVERRIDE; + bool isInitialized() const Q_DECL_OVERRIDE; + + QWidget *createWidget(QWidget *parent) Q_DECL_OVERRIDE; + + QString name() const Q_DECL_OVERRIDE; + QString group() const Q_DECL_OVERRIDE; + QIcon icon() const Q_DECL_OVERRIDE; + QString toolTip() const Q_DECL_OVERRIDE; + QString whatsThis() const Q_DECL_OVERRIDE; + bool isContainer() const Q_DECL_OVERRIDE; + + QString domXml() const Q_DECL_OVERRIDE; + + QString includeFile() const Q_DECL_OVERRIDE; + +private: + bool initialized; +}; + + +#endif // COLOR_WIDGETS_COLOR_LINE_EDIT_PLUGIN_HPP + diff --git a/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_list_plugin.cpp b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_list_plugin.cpp new file mode 100644 index 00000000..11768811 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_list_plugin.cpp @@ -0,0 +1,88 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#include "color_list_plugin.hpp" +#include "QtColorWidgets/color_list_widget.hpp" + +ColorListWidget_Plugin::ColorListWidget_Plugin(QObject *parent) : + QObject(parent) +{ +} + + +void ColorListWidget_Plugin::initialize(QDesignerFormEditorInterface *) +{ + initialized = true; +} + +bool ColorListWidget_Plugin::isInitialized() const +{ + return initialized; +} + +QWidget *ColorListWidget_Plugin::createWidget(QWidget *parent) +{ + return new color_widgets::ColorListWidget(parent); +} + +QString ColorListWidget_Plugin::name() const +{ + return "color_widgets::ColorListWidget"; +} + +QString ColorListWidget_Plugin::group() const +{ + return "Color Widgets"; +} + +QIcon ColorListWidget_Plugin::icon() const +{ + return QIcon::fromTheme("format-stroke-color"); +} + +QString ColorListWidget_Plugin::toolTip() const +{ + return "An editable list of colors"; +} + +QString ColorListWidget_Plugin::whatsThis() const +{ + return toolTip(); +} + +bool ColorListWidget_Plugin::isContainer() const +{ + return false; +} + +QString ColorListWidget_Plugin::domXml() const +{ + + return "\n" + " \n" + " \n" + "\n"; +} + +QString ColorListWidget_Plugin::includeFile() const +{ + return "QtColorWidgets/color_list_widget.hpp"; +} diff --git a/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_list_plugin.hpp b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_list_plugin.hpp new file mode 100644 index 00000000..31508d97 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_list_plugin.hpp @@ -0,0 +1,56 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#ifndef COLOR_LIST_PLUGIN_HPP +#define COLOR_LIST_PLUGIN_HPP + +#include + +class ColorListWidget_Plugin : public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + explicit ColorListWidget_Plugin(QObject *parent = 0); + + void initialize(QDesignerFormEditorInterface *core); + bool isInitialized() const; + + QWidget *createWidget(QWidget *parent); + + QString name() const; + QString group() const; + QIcon icon() const; + QString toolTip() const; + QString whatsThis() const; + bool isContainer() const; + + QString domXml() const; + + QString includeFile() const; + +private: + bool initialized; + +}; + +#endif // COLOR_LIST_PLUGIN_HPP diff --git a/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_palette_widget_plugin.cpp b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_palette_widget_plugin.cpp new file mode 100644 index 00000000..05d80248 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_palette_widget_plugin.cpp @@ -0,0 +1,120 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#include "color_palette_widget_plugin.hpp" +#include "QtColorWidgets/color_palette_widget.hpp" + +ColorPaletteWidget_Plugin::ColorPaletteWidget_Plugin(QObject *parent) : + QObject(parent), initialized(false) +{ +} + +void ColorPaletteWidget_Plugin::initialize(QDesignerFormEditorInterface *) +{ + initialized = true; +} + +bool ColorPaletteWidget_Plugin::isInitialized() const +{ + return initialized; +} + +QWidget* ColorPaletteWidget_Plugin::createWidget(QWidget *parent) +{ + color_widgets::ColorPaletteWidget *wid = new color_widgets::ColorPaletteWidget(parent); + + color_widgets::ColorPalette palette1; + color_widgets::ColorPalette palette2; + int columns = 12; + palette1.setName("Palette 1"); + palette2.setName("Palette 2"); + palette1.setColumns(columns); + palette2.setColumns(columns); + for ( int i = 0; i < 6; i++ ) + { + for ( int j = 0; j < columns; j++ ) + { + float f = float(j)/columns; + palette1.appendColor(QColor::fromHsvF(i/8.0,1-f,0.5+f/2)); + palette2.appendColor(QColor::fromHsvF(i/8.0,1-f,1-f)); + } + } + color_widgets::ColorPaletteModel *model = new color_widgets::ColorPaletteModel; + model->setParent(wid); + model->addPalette(palette1, false); + model->addPalette(palette2, false); + wid->setModel(model); + + return wid; +} + +QString ColorPaletteWidget_Plugin::name() const +{ + return "color_widgets::ColorPaletteWidget"; +} + +QString ColorPaletteWidget_Plugin::group() const +{ + return "Color Widgets"; +} + +QIcon ColorPaletteWidget_Plugin::icon() const +{ + color_widgets::ColorPalette w; + w.setColumns(6); + for ( int i = 0; i < 4; i++ ) + { + for ( int j = 0; j < w.columns(); j++ ) + { + float f = float(j)/w.columns(); + w.appendColor(QColor::fromHsvF(i/5.0,1-f,0.5+f/2)); + } + } + return QIcon(w.preview(QSize(64,64))); +} + +QString ColorPaletteWidget_Plugin::toolTip() const +{ + return "A widget that displays a color palette"; +} + +QString ColorPaletteWidget_Plugin::whatsThis() const +{ + return toolTip(); +} + +bool ColorPaletteWidget_Plugin::isContainer() const +{ + return false; +} + +QString ColorPaletteWidget_Plugin::domXml() const +{ + return "\n" + " \n" + " \n" + "\n"; +} + +QString ColorPaletteWidget_Plugin::includeFile() const +{ + return "QtColorWidgets/color_palette_widget.hpp"; +} diff --git a/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_palette_widget_plugin.hpp b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_palette_widget_plugin.hpp new file mode 100644 index 00000000..06f6d72c --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_palette_widget_plugin.hpp @@ -0,0 +1,57 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#ifndef COLOR_WIDGETS_COLOR_PALETTE_WIDGET_PLUGIN_HPP +#define COLOR_WIDGETS_COLOR_PALETTE_WIDGET_PLUGIN_HPP + +#include +#include + +class ColorPaletteWidget_Plugin : public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + ColorPaletteWidget_Plugin(QObject *parent = 0); + + void initialize(QDesignerFormEditorInterface *core); + bool isInitialized() const; + + QWidget *createWidget(QWidget *parent); + + QString name() const; + QString group() const; + QIcon icon() const; + QString toolTip() const; + QString whatsThis() const; + bool isContainer() const; + + QString domXml() const; + + QString includeFile() const; + +private: + bool initialized; +}; + + +#endif // COLOR_WIDGETS_COLOR_PALETTE_WIDGET_PLUGIN_HPP diff --git a/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_preview_plugin.cpp b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_preview_plugin.cpp new file mode 100644 index 00000000..1b424659 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_preview_plugin.cpp @@ -0,0 +1,95 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#include "color_preview_plugin.hpp" +#include "QtColorWidgets/color_preview.hpp" +#include + + +ColorPreview_Plugin::ColorPreview_Plugin(QObject *parent) + : QObject(parent), initialized(false) +{ +} + + +void ColorPreview_Plugin::initialize(QDesignerFormEditorInterface *) +{ + if (initialized) + return; + + initialized = true; +} + +bool ColorPreview_Plugin::isInitialized() const +{ + return initialized; +} + +QWidget *ColorPreview_Plugin::createWidget(QWidget *parent) +{ + return new color_widgets::ColorPreview(parent); +} + +QString ColorPreview_Plugin::name() const +{ + return "color_widgets::ColorPreview"; +} + +QString ColorPreview_Plugin::group() const +{ + return "Color Widgets"; +} + +QIcon ColorPreview_Plugin::icon() const +{ + return QIcon(); +} + +QString ColorPreview_Plugin::toolTip() const +{ + return "Display a color"; +} + +QString ColorPreview_Plugin::whatsThis() const +{ + return toolTip(); +} + +bool ColorPreview_Plugin::isContainer() const +{ + return false; +} + +QString ColorPreview_Plugin::domXml() const +{ + + return "\n" + " \n" + " \n" + "\n"; +} + +QString ColorPreview_Plugin::includeFile() const +{ + return "QtColorWidgets/color_preview.hpp"; +} + +//Q_EXPORT_PLUGIN2(color_widgets, ColorPreview_Plugin); diff --git a/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_preview_plugin.hpp b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_preview_plugin.hpp new file mode 100644 index 00000000..e1bd2c42 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_preview_plugin.hpp @@ -0,0 +1,55 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#ifndef COLOR_PREVIEW_PLUGIN_HPP +#define COLOR_PREVIEW_PLUGIN_HPP + +#include + +class ColorPreview_Plugin : public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + ColorPreview_Plugin(QObject *parent = 0); + + void initialize(QDesignerFormEditorInterface *core); + bool isInitialized() const; + + QWidget *createWidget(QWidget *parent); + + QString name() const; + QString group() const; + QIcon icon() const; + QString toolTip() const; + QString whatsThis() const; + bool isContainer() const; + + QString domXml() const; + + QString includeFile() const; + +private: + bool initialized; +}; + +#endif // COLOR_PREVIEW_PLUGIN_HPP diff --git a/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_selector_plugin.cpp b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_selector_plugin.cpp new file mode 100644 index 00000000..24d831f8 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_selector_plugin.cpp @@ -0,0 +1,92 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#include "color_selector_plugin.hpp" +#include "QtColorWidgets/color_selector.hpp" +#include + +ColorSelector_Plugin::ColorSelector_Plugin(QObject *parent) + : QObject(parent), initialized(false) +{ +} + + +void ColorSelector_Plugin::initialize(QDesignerFormEditorInterface *) +{ + if (initialized) + return; + + initialized = true; +} + +bool ColorSelector_Plugin::isInitialized() const +{ + return initialized; +} + +QWidget *ColorSelector_Plugin::createWidget(QWidget *parent) +{ + return new color_widgets::ColorSelector(parent); +} + +QString ColorSelector_Plugin::name() const +{ + return "color_widgets::ColorSelector"; +} + +QString ColorSelector_Plugin::group() const +{ + return "Color Widgets"; +} + +QIcon ColorSelector_Plugin::icon() const +{ + return QIcon(); +} + +QString ColorSelector_Plugin::toolTip() const +{ + return "Display a color and opens a color dialog on click"; +} + +QString ColorSelector_Plugin::whatsThis() const +{ + return toolTip(); +} + +bool ColorSelector_Plugin::isContainer() const +{ + return false; +} + +QString ColorSelector_Plugin::domXml() const +{ + + return "\n" + " \n" + " \n" + "\n"; +} + +QString ColorSelector_Plugin::includeFile() const +{ + return "QtColorWidgets/color_selector.hpp"; +} diff --git a/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_selector_plugin.hpp b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_selector_plugin.hpp new file mode 100644 index 00000000..a03ab155 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_selector_plugin.hpp @@ -0,0 +1,55 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#ifndef COLOR_SELECTOR_PLUGIN_HPP +#define COLOR_SELECTOR_PLUGIN_HPP + +#include + +class ColorSelector_Plugin : public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + ColorSelector_Plugin(QObject *parent = 0); + + void initialize(QDesignerFormEditorInterface *core); + bool isInitialized() const; + + QWidget *createWidget(QWidget *parent); + + QString name() const; + QString group() const; + QIcon icon() const; + QString toolTip() const; + QString whatsThis() const; + bool isContainer() const; + + QString domXml() const; + + QString includeFile() const; + +private: + bool initialized; +}; + +#endif // COLOR_SELECTOR_PLUGIN_HPP diff --git a/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_wheel_plugin.cpp b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_wheel_plugin.cpp new file mode 100644 index 00000000..1cde3229 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_wheel_plugin.cpp @@ -0,0 +1,97 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#include "color_wheel_plugin.hpp" +#include "QtColorWidgets/color_wheel.hpp" + +ColorWheel_Plugin::ColorWheel_Plugin(QObject *parent) : + QObject(parent), initialized(false) +{ +} + +void ColorWheel_Plugin::initialize(QDesignerFormEditorInterface *) +{ + initialized = true; +} + +bool ColorWheel_Plugin::isInitialized() const +{ + return initialized; +} + +QWidget *ColorWheel_Plugin::createWidget(QWidget *parent) +{ + return new color_widgets::ColorWheel(parent); +} + +QString ColorWheel_Plugin::name() const +{ + return "color_widgets::ColorWheel"; +} + +QString ColorWheel_Plugin::group() const +{ + return "Color Widgets"; +} + +QIcon ColorWheel_Plugin::icon() const +{ + color_widgets::ColorWheel w; + w.resize(64,64); + w.setWheelWidth(8); + QPixmap pix(64,64); + w.render(&pix); + return QIcon(pix); +} + +QString ColorWheel_Plugin::toolTip() const +{ + return "A widget that allows an intuitive selection of HSL parameters for a QColor"; +} + +QString ColorWheel_Plugin::whatsThis() const +{ + return toolTip(); +} + +bool ColorWheel_Plugin::isContainer() const +{ + return false; +} + +QString ColorWheel_Plugin::domXml() const +{ + return "\n" + " \n" + " \n" + " \n" + " 0\n" + " 0\n" + " \n" + " \n" + " \n" + "\n"; +} + +QString ColorWheel_Plugin::includeFile() const +{ + return "QtColorWidgets/color_wheel.hpp"; +} diff --git a/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_wheel_plugin.hpp b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_wheel_plugin.hpp new file mode 100644 index 00000000..921323f7 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_wheel_plugin.hpp @@ -0,0 +1,57 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#ifndef COLOR_WHEEL_PLUGIN_HPP +#define COLOR_WHEEL_PLUGIN_HPP + +#include +#include + +class ColorWheel_Plugin : public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + ColorWheel_Plugin(QObject *parent = 0); + + void initialize(QDesignerFormEditorInterface *core); + bool isInitialized() const; + + QWidget *createWidget(QWidget *parent); + + QString name() const; + QString group() const; + QIcon icon() const; + QString toolTip() const; + QString whatsThis() const; + bool isContainer() const; + + QString domXml() const; + + QString includeFile() const; + +private: + bool initialized; +}; + + +#endif // COLOR_WHEEL_PLUGIN_HPP diff --git a/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_widget_plugin_collection.cpp b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_widget_plugin_collection.cpp new file mode 100644 index 00000000..10bf5e1a --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_widget_plugin_collection.cpp @@ -0,0 +1,56 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#include "color_widget_plugin_collection.hpp" +#include "color_preview_plugin.hpp" +#include "color_wheel_plugin.hpp" +#include "gradient_slider_plugin.hpp" +#include "hue_slider_plugin.hpp" +#include "color_selector_plugin.hpp" +#include "color_list_plugin.hpp" +#include "swatch_plugin.hpp" +#include "color_palette_widget_plugin.hpp" +#include "color_2d_slider_plugin.hpp" +#include "color_line_edit_plugin.hpp" +#include "gradient_editor_plugin.hpp" +// add new plugin headers above this line + +ColorWidgets_PluginCollection::ColorWidgets_PluginCollection(QObject *parent) : + QObject(parent) +{ + widgets.push_back(new ColorPreview_Plugin(this)); + widgets.push_back(new ColorWheel_Plugin(this)); + widgets.push_back(new GradientSlider_Plugin(this)); + widgets.push_back(new HueSlider_Plugin(this)); + widgets.push_back(new ColorSelector_Plugin(this)); + widgets.push_back(new ColorListWidget_Plugin(this)); + widgets.push_back(new Swatch_Plugin(this)); + widgets.push_back(new ColorPaletteWidget_Plugin(this)); + widgets.push_back(new Color2DSlider_Plugin(this)); + widgets.push_back(new ColorLineEdit_Plugin(this)); + widgets.push_back(new GradientEditor_Plugin(this)); + // add new plugins above this line +} + +QList ColorWidgets_PluginCollection::customWidgets() const +{ + return widgets; +} diff --git a/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_widget_plugin_collection.hpp b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_widget_plugin_collection.hpp new file mode 100644 index 00000000..d58ca5df --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/color_widget_plugin_collection.hpp @@ -0,0 +1,43 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#ifndef COLOR_WIDGET_PLUGIN_COLLECTION_HPP +#define COLOR_WIDGET_PLUGIN_COLLECTION_HPP + +#include + +class ColorWidgets_PluginCollection : public QObject, public QDesignerCustomWidgetCollectionInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "mattia.basaglia.ColorWidgetsPlugin") + Q_INTERFACES(QDesignerCustomWidgetCollectionInterface) + +public: + explicit ColorWidgets_PluginCollection(QObject *parent = 0); + + QList customWidgets() const; + + private: + QList widgets; + +}; + +#endif // COLOR_WIDGET_PLUGIN_COLLECTION_HPP diff --git a/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/gradient_editor_plugin.cpp b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/gradient_editor_plugin.cpp new file mode 100644 index 00000000..c679eb9c --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/gradient_editor_plugin.cpp @@ -0,0 +1,98 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#include "gradient_editor_plugin.hpp" +#include "QtColorWidgets/gradient_editor.hpp" + +QWidget* GradientEditor_Plugin::createWidget(QWidget *parent) +{ + color_widgets::GradientEditor *widget = new color_widgets::GradientEditor(parent); + return widget; +} + +QIcon GradientEditor_Plugin::icon() const +{ + color_widgets::GradientEditor w; + w.resize(64,16); + QGradientStops cols; + cols.push_back({0.2, Qt::green}); + cols.push_back({0.5, Qt::yellow}); + cols.push_back({0.8, Qt::red}); + w.setStops(cols); + QPixmap pix(64,64); + pix.fill(Qt::transparent); + w.render(&pix, QPoint(0,16)); + return QIcon(pix); +} + +QString GradientEditor_Plugin::domXml() const +{ + return "\n" + " \n" + " \n" + "\n"; +} + +bool GradientEditor_Plugin::isContainer() const +{ + return false; +} + +GradientEditor_Plugin::GradientEditor_Plugin(QObject *parent) : + QObject(parent), initialized(false) +{ +} + +void GradientEditor_Plugin::initialize(QDesignerFormEditorInterface *) +{ + initialized = true; +} + +bool GradientEditor_Plugin::isInitialized() const +{ + return initialized; +} + +QString GradientEditor_Plugin::name() const +{ + return "color_widgets::GradientEditor"; +} + +QString GradientEditor_Plugin::group() const +{ + return "Color Widgets"; +} + +QString GradientEditor_Plugin::toolTip() const +{ + return "Widget to edit gradient stops"; +} + +QString GradientEditor_Plugin::whatsThis() const +{ + return toolTip(); +} + +QString GradientEditor_Plugin::includeFile() const +{ + return "QtColorWidgets/gradient_editor.hpp"; +} + diff --git a/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/gradient_editor_plugin.hpp b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/gradient_editor_plugin.hpp new file mode 100644 index 00000000..77fb19b7 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/gradient_editor_plugin.hpp @@ -0,0 +1,58 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#ifndef COLOR_WIDGETS_GRADIENT_EDITOR_PLUGIN_HPP +#define COLOR_WIDGETS_GRADIENT_EDITOR_PLUGIN_HPP + +#include +#include + +class GradientEditor_Plugin : public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + explicit GradientEditor_Plugin(QObject *parent = nullptr); + + void initialize(QDesignerFormEditorInterface *core) Q_DECL_OVERRIDE; + bool isInitialized() const Q_DECL_OVERRIDE; + + QWidget *createWidget(QWidget *parent) Q_DECL_OVERRIDE; + + QString name() const Q_DECL_OVERRIDE; + QString group() const Q_DECL_OVERRIDE; + QIcon icon() const Q_DECL_OVERRIDE; + QString toolTip() const Q_DECL_OVERRIDE; + QString whatsThis() const Q_DECL_OVERRIDE; + bool isContainer() const Q_DECL_OVERRIDE; + + QString domXml() const Q_DECL_OVERRIDE; + + QString includeFile() const Q_DECL_OVERRIDE; + +private: + bool initialized; +}; + + +#endif // COLOR_WIDGETS_GRADIENT_EDITOR_PLUGIN_HPP + diff --git a/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/gradient_slider_plugin.cpp b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/gradient_slider_plugin.cpp new file mode 100644 index 00000000..82e43571 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/gradient_slider_plugin.cpp @@ -0,0 +1,102 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#include "gradient_slider_plugin.hpp" +#include "QtColorWidgets/gradient_slider.hpp" +#include + +GradientSlider_Plugin::GradientSlider_Plugin(QObject *parent) + : QObject(parent), initialized(false) +{ +} + + +void GradientSlider_Plugin::initialize(QDesignerFormEditorInterface *) +{ + if (initialized) + return; + + initialized = true; +} + +bool GradientSlider_Plugin::isInitialized() const +{ + return initialized; +} + +QWidget *GradientSlider_Plugin::createWidget(QWidget *parent) +{ + return new color_widgets::GradientSlider(parent); +} + +QString GradientSlider_Plugin::name() const +{ + return "color_widgets::GradientSlider"; +} + +QString GradientSlider_Plugin::group() const +{ + return "Color Widgets"; +} + +QIcon GradientSlider_Plugin::icon() const +{ + color_widgets::GradientSlider w; + w.resize(64,16); + QVector cols; + cols.push_back(Qt::green); + cols.push_back(Qt::yellow); + cols.push_back(Qt::red); + w.setColors(cols); + QPixmap pix(64,64); + pix.fill(Qt::transparent); + w.render(&pix,QPoint(0,16)); + return QIcon(pix); +} + +QString GradientSlider_Plugin::toolTip() const +{ + return "Slider over a gradient"; +} + +QString GradientSlider_Plugin::whatsThis() const +{ + return toolTip(); +} + +bool GradientSlider_Plugin::isContainer() const +{ + return false; +} + +QString GradientSlider_Plugin::domXml() const +{ + + return "\n" + " \n" + " \n" + "\n"; +} + +QString GradientSlider_Plugin::includeFile() const +{ + return "QtColorWidgets/gradient_slider.hpp"; +} diff --git a/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/gradient_slider_plugin.hpp b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/gradient_slider_plugin.hpp new file mode 100644 index 00000000..5d98035c --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/gradient_slider_plugin.hpp @@ -0,0 +1,34 @@ +#ifndef GRADIENT_SLIDER_PLUGIN_HPP +#define GRADIENT_SLIDER_PLUGIN_HPP + +#include + +class GradientSlider_Plugin : public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + GradientSlider_Plugin(QObject *parent = 0); + + void initialize(QDesignerFormEditorInterface *core); + bool isInitialized() const; + + QWidget *createWidget(QWidget *parent); + + QString name() const; + QString group() const; + QIcon icon() const; + QString toolTip() const; + QString whatsThis() const; + bool isContainer() const; + + QString domXml() const; + + QString includeFile() const; + +private: + bool initialized; +}; + +#endif // GRADIENT_SLIDER_PLUGIN_HPP diff --git a/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/hue_slider_plugin.cpp b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/hue_slider_plugin.cpp new file mode 100644 index 00000000..27cd6c6a --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/hue_slider_plugin.cpp @@ -0,0 +1,98 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * \copyright Copyright (C) 2014 Calle Laakkonen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#include "hue_slider_plugin.hpp" +#include "QtColorWidgets/hue_slider.hpp" +#include + +HueSlider_Plugin::HueSlider_Plugin(QObject *parent) + : QObject(parent), initialized(false) +{ +} + + +void HueSlider_Plugin::initialize(QDesignerFormEditorInterface *) +{ + if (initialized) + return; + + initialized = true; +} + +bool HueSlider_Plugin::isInitialized() const +{ + return initialized; +} + +QWidget *HueSlider_Plugin::createWidget(QWidget *parent) +{ + return new color_widgets::HueSlider(parent); +} + +QString HueSlider_Plugin::name() const +{ + return "color_widgets::HueSlider"; +} + +QString HueSlider_Plugin::group() const +{ + return "Color Widgets"; +} + +QIcon HueSlider_Plugin::icon() const +{ + color_widgets::HueSlider w; + w.resize(64,16); + QPixmap pix(64,64); + pix.fill(Qt::transparent); + w.render(&pix,QPoint(0,16)); + return QIcon(pix); +} + +QString HueSlider_Plugin::toolTip() const +{ + return "Slider over a hue gradient"; +} + +QString HueSlider_Plugin::whatsThis() const +{ + return toolTip(); +} + +bool HueSlider_Plugin::isContainer() const +{ + return false; +} + +QString HueSlider_Plugin::domXml() const +{ + + return "\n" + " \n" + " \n" + "\n"; +} + +QString HueSlider_Plugin::includeFile() const +{ + return "QtColorWidgets/hue_slider.hpp"; +} diff --git a/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/hue_slider_plugin.hpp b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/hue_slider_plugin.hpp new file mode 100644 index 00000000..78f08ce8 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/hue_slider_plugin.hpp @@ -0,0 +1,34 @@ +#ifndef HUE_SLIDER_PLUGIN_HPP +#define HUE_SLIDER_PLUGIN_HPP + +#include + +class HueSlider_Plugin : public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + HueSlider_Plugin(QObject *parent = 0); + + void initialize(QDesignerFormEditorInterface *core); + bool isInitialized() const; + + QWidget *createWidget(QWidget *parent); + + QString name() const; + QString group() const; + QIcon icon() const; + QString toolTip() const; + QString whatsThis() const; + bool isContainer() const; + + QString domXml() const; + + QString includeFile() const; + +private: + bool initialized; +}; + +#endif // HUE_SLIDER_PLUGIN_HPP diff --git a/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/new_plugin b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/new_plugin new file mode 100644 index 00000000..285d8062 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/new_plugin @@ -0,0 +1,236 @@ +#!/bin/bash +# +# Copyright (C) 2013-2020 Mattia Basaglia +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +SELFDIR=$(dirname $(readlink -se "${BASH_SOURCE[0]}")) + +function class_to_underscore() +{ + echo "$1" | sed -r -e 's/([a-z])([A-Z])/\1_\L\2/g' -e 's/[A-Z]/\L\0/g' +} + +function class_to_header() +{ + echo "$(class_to_underscore "$1").hpp" +} + +function header_to_source() +{ + echo "$1" | sed -r -e 's/\.h(pp)?$/.cpp/i' +} + +function read_arg() +{ + varname=$1 + prompt="$2" + default="$3" + [ "$default" ] && prompt="${prompt} [$default]" + read -p "$prompt: " -i "$default" $varname + [ -z "${!varname}" ] && eval "$varname=\"$default\"" +} + +function header_to_guard() +{ + echo "$1" | tr [:lower:] [:upper:] | tr /. _ +} + +read_arg class "Class name" +[ -z "$class" ] && exit 1 +read_arg description "Description" +read_arg header "Header" "$(class_to_header "$class")" + +read_arg plugin "Plugin Class" "${class}_Plugin" +read_arg plugin_header "Plugin Header" "$(class_to_header "$plugin")" +read_arg plugin_source "Plugin Source" "$(header_to_source "$plugin_header")" +read_arg plugin_path "Plugin Path" "$SELFDIR" +read_arg plugin_author "Author" "$(git config user.name)" +read_arg plugin_copyright "Copyright" "2013-$(date +%Y) $plugin_author" + +echo "Summary:" +echo " Class: $class" +echo " Description: $description" +echo " Header: $header" +echo " Plugin Class: $plugin" +echo " Plugin Header: $plugin_header" +echo " Plugin Source: $plugin_source" +echo " Plugin Path: $plugin_path" +echo " Author: $plugin_author" +echo " Copyright: $plugin_copyright" + +object_name="$(class_to_underscore $class)" + +cat >"$plugin_path/$plugin_source" <. + * + */ +#include "$plugin_header" +#include "QtColorWidgets/$header" + +QWidget* $plugin::createWidget(QWidget *parent) +{ + color_widgets::$class *widget = new color_widgets::$class(parent); + return widget; +} + +QIcon $plugin::icon() const +{ + return QIcon(); +} + +QString $plugin::domXml() const +{ + return "\n" + " \n" + " \n" + "\n"; +} + +bool $plugin::isContainer() const +{ + return false; +} + +$plugin::$plugin(QObject *parent) : + QObject(parent), initialized(false) +{ +} + +void $plugin::initialize(QDesignerFormEditorInterface *) +{ + initialized = true; +} + +bool $plugin::isInitialized() const +{ + return initialized; +} + +QString $plugin::name() const +{ + return "color_widgets::$class"; +} + +QString $plugin::group() const +{ + return "Color Widgets"; +} + +QString $plugin::toolTip() const +{ + return "$description"; +} + +QString $plugin::whatsThis() const +{ + return toolTip(); +} + +QString $plugin::includeFile() const +{ + return "QtColorWidgets/$header"; +} + +PLUGIN + +header_guard="COLOR_WIDGETS_$(header_to_guard "$plugin_header")" +cat >"$plugin_path/$plugin_header" <. + * + */ +#ifndef $header_guard +#define $header_guard + +#include +#include + +class $plugin : public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + explicit $plugin(QObject *parent = nullptr); + + void initialize(QDesignerFormEditorInterface *core) Q_DECL_OVERRIDE; + bool isInitialized() const Q_DECL_OVERRIDE; + + QWidget *createWidget(QWidget *parent) Q_DECL_OVERRIDE; + + QString name() const Q_DECL_OVERRIDE; + QString group() const Q_DECL_OVERRIDE; + QIcon icon() const Q_DECL_OVERRIDE; + QString toolTip() const Q_DECL_OVERRIDE; + QString whatsThis() const Q_DECL_OVERRIDE; + bool isContainer() const Q_DECL_OVERRIDE; + + QString domXml() const Q_DECL_OVERRIDE; + + QString includeFile() const Q_DECL_OVERRIDE; + +private: + bool initialized; +}; + + +#endif // $header_guard + +PLUGIN + +sed -i -r \ + -e "\\~# add new sources above this line~i\\$plugin_source" \ + -e "\\~# add new headers above this line~i\\$plugin_header" \ + "$plugin_path/CMakeLists.txt" + +sed -i -r \ + -e "\\~// add new plugin headers above this line~i#include \"$plugin_header\"" \ + -e "\\~// add new plugins above this line~i\ widgets.push_back(new $plugin(this));" \ + "$plugin_path/color_widget_plugin_collection.cpp" diff --git a/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/swatch_plugin.cpp b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/swatch_plugin.cpp new file mode 100644 index 00000000..3c9b465a --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/swatch_plugin.cpp @@ -0,0 +1,106 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#include "swatch_plugin.hpp" +#include "QtColorWidgets/swatch.hpp" + +Swatch_Plugin::Swatch_Plugin(QObject *parent) : + QObject(parent), initialized(false) +{ +} + +void Swatch_Plugin::initialize(QDesignerFormEditorInterface *) +{ + initialized = true; +} + +bool Swatch_Plugin::isInitialized() const +{ + return initialized; +} + +QWidget* Swatch_Plugin::createWidget(QWidget *parent) +{ + color_widgets::Swatch *wid = new color_widgets::Swatch(parent); + wid->palette().setColumns(12); + for ( int i = 0; i < 6; i++ ) + { + for ( int j = 0; j < wid->palette().columns(); j++ ) + { + float f = float(j)/wid->palette().columns(); + wid->palette().appendColor(QColor::fromHsvF(i/8.0,1-f,0.5+f/2)); + } + } + return wid; +} + +QString Swatch_Plugin::name() const +{ + return "color_widgets::Swatch"; +} + +QString Swatch_Plugin::group() const +{ + return "Color Widgets"; +} + +QIcon Swatch_Plugin::icon() const +{ + color_widgets::ColorPalette w; + w.setColumns(6); + for ( int i = 0; i < 4; i++ ) + { + for ( int j = 0; j < w.columns(); j++ ) + { + float f = float(j)/w.columns(); + w.appendColor(QColor::fromHsvF(i/5.0,1-f,0.5+f/2)); + } + } + return QIcon(w.preview(QSize(64,64))); +} + +QString Swatch_Plugin::toolTip() const +{ + return "A widget that displays a color palette"; +} + +QString Swatch_Plugin::whatsThis() const +{ + return toolTip(); +} + +bool Swatch_Plugin::isContainer() const +{ + return false; +} + +QString Swatch_Plugin::domXml() const +{ + return "\n" + " \n" + " \n" + "\n"; +} + +QString Swatch_Plugin::includeFile() const +{ + return "QtColorWidgets/swatch.hpp"; +} diff --git a/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/swatch_plugin.hpp b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/swatch_plugin.hpp new file mode 100644 index 00000000..4670c60d --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/color_widgets_designer_plugin/swatch_plugin.hpp @@ -0,0 +1,57 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#ifndef COLOR_WIDGETS_SWATCH_PLUGIN_HPP +#define COLOR_WIDGETS_SWATCH_PLUGIN_HPP + +#include +#include + +class Swatch_Plugin : public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + Swatch_Plugin(QObject *parent = 0); + + void initialize(QDesignerFormEditorInterface *core); + bool isInitialized() const; + + QWidget *createWidget(QWidget *parent); + + QString name() const; + QString group() const; + QIcon icon() const; + QString toolTip() const; + QString whatsThis() const; + bool isContainer() const; + + QString domXml() const; + + QString includeFile() const; + +private: + bool initialized; +}; + + +#endif // COLOR_WIDGETS_SWATCH_PLUGIN_HPP diff --git a/thirdparty/Qt-Color-Widgets/gallery/CMakeLists.txt b/thirdparty/Qt-Color-Widgets/gallery/CMakeLists.txt new file mode 100644 index 00000000..7ec618c4 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/gallery/CMakeLists.txt @@ -0,0 +1,48 @@ +# +# Copyright (C) 2013-2020 Mattia Basaglia +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +set (QT_SUPPORTED_VERSIONS 5) + +select_qt ( + QT_SUPPORTED_VERSIONS "${QT_SUPPORTED_VERSIONS}" + QT_DEFAULT_VERSION 5) + +set (REQUIRED_QT_COMPONENTS + Widgets + ) +find_qt ( + QT_SUPPORTED_VERSIONS "${QT_SUPPORTED_VERSIONS}" + REQUIRED_COMPONENTS "${REQUIRED_QT_COMPONENTS}" + OPTIONAL_COMPONENTS "") + +set(SCREENSHOT_SOURCES screenshot.cpp) +set(SCREENSHOT_BINARY screenshot_bin) + +add_executable(${SCREENSHOT_BINARY} EXCLUDE_FROM_ALL ${SCREENSHOT_SOURCES}) + +use_qt ( + TARGET_NAME ${SCREENSHOT_BINARY} + QT_SUPPORTED_VERSIONS "${QT_SUPPORTED_VERSIONS}" + REQUIRED_COMPONENTS "${REQUIRED_QT_COMPONENTS}" + OPTIONAL_COMPONENTS "") +target_link_libraries(${SCREENSHOT_BINARY} + PRIVATE + ${COLOR_WIDGETS_LIBRARY}) + +add_custom_target(gallery + COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${SCREENSHOT_BINARY} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDS ${SCREENSHOT_BINARY} +) diff --git a/thirdparty/Qt-Color-Widgets/gallery/Color2DSlider.png b/thirdparty/Qt-Color-Widgets/gallery/Color2DSlider.png new file mode 100644 index 0000000000000000000000000000000000000000..caed3b2b509108fdf0f7550f459063e5098fd947 GIT binary patch literal 5904 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 b`)@MX*a5dFsCm%b9Y9u6=}U!#ap3;~m-k=Q literal 0 HcmV?d00001 diff --git a/thirdparty/Qt-Color-Widgets/gallery/ColorLineEdit.png b/thirdparty/Qt-Color-Widgets/gallery/ColorLineEdit.png new file mode 100644 index 0000000000000000000000000000000000000000..13439a8508b4cac8c054674772b7367d5e7dcc0e GIT binary patch literal 1434 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

xGN@*pIJK?cTKlbE?Fm`bj3dh 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 literal 0 HcmV?d00001 diff --git a/thirdparty/Qt-Color-Widgets/gallery/ColorPaletteWidget_readonly.png b/thirdparty/Qt-Color-Widgets/gallery/ColorPaletteWidget_readonly.png new file mode 100644 index 0000000000000000000000000000000000000000..659e7ea8f286c3e4e651221ed86f324d0867cf17 GIT binary patch literal 3023 zcmb_eX*d*W8>T`@hOtC4_Fa}39ExO=j4^{jl3n)Y9K#SzlT=70NemiG7-O5VWnZG~ zMj6{k z1;myezp~MA^bq?T4i1)Qhhx86IH5Q=_&a}rGfj=lS9GAeudGZs0rDcSXd z!3bMrux4X8xjd1FvARc2V1<)J#cTX;`Uc~aGmn;@Q==9|&5m35O_r4{UK;j+R$YMp zJ`}KDrw}3uKKq>bT}2>?D^Q05nTv{?Sk=NR+qm- zs|_eQ)9G|;rZpHWsdPuiXZ-b_)&xDzS&O;Qm@QR#MeA&Xal(M%!g;H zi^f3i1=LVw^M=vnzM|J2i=pYZ0Z=0V@46iIUWnXPdC zif!WEFbTM~Y?uHMk_Qo$RZ77rXLnobK4K%g`;xcPSq7e~Zd;h%vcrhx9I`2SX|cR> zin=*sRltR*5pvpTiK;`yX41%!yC+38n@6Kq5*_;Z*aXO`S8lYAd0!T^sK}bd+SK2d z4(@#^UazLN5*T2z$l5(OTHh*Bj9n#3G0hh=H(#aAN!=!5r_!{5dtbgl?MXg^o610giV>Zm_ zKtse$np%w(p(d<$>Ks?hE7C*VY-VOWhw)DRPULg?>y#4P;>SoKe{^N>Y<0?eh;NX$ zePX{iIOV;CjEwg&Rr`cNl1y!%;y_09CRD=2h^moIU<9j1RqP?W`o7=6%$5Vx_nod4 zm?Fq#Z!vH7?~g3@n)r)8sjL1C8Wt+)QLNF6rI&XM6!9#CI9w(>>v}LsD$OqY^(;jW zXq(*1mqsDi@h3|02kWt#hhJno#{uT2z>3w5?TUeWl4|n`W9p@4-`1(z4<5V_r9DVb z!Wg@)(I~pXtSMBs_43lB8>JLGRww;2`sX^}A&p9MInR11kTuuVW_g zh_z~4j1^( zYNg!op8XcF381j6<`+a>otc^R3cbEfX}5JLgl-TzT_RVE=aEZ_1&C1xh&tHm@T?+#>eybv zBZ|1$8tg8uC`l2^t0bC?dhjhLl=Xc- z!t|}6Y^OL#FNkV(|9EN~RA}Z=E*_z>jXyECrsYiEgSBP{6}rk{u#x8%P#y=L-39yx zhw5=v#p_OU*?f@Z9MV1MQ*d3pGEN!iciIN{Q7r#Rz76CD#8Kcq&>UzU-kNtRJlrKX zhJPyZ=kwpt8%GB&>=5Ht9Y){sbg3)Ld-R^Em6XuKZr>;sh;EDAp7vD=m+2OxBa=rl z^FkL-HfY@o$Q#&Uh zb#2%4a)JF{;WQo0$Ty~%RAV5enn`nhgKl5iOow5EB1T;Rn!(P_+es5}<{@t9@OQ~4 zjnB&dEQ1R`$bo#*)+%Z-bG4HpBXPGgs7>^E=`ke;ouoqb zkGx9B(8}`Bb^&=rUb7PPxyO#zte-#P-m3EAS!(>6kU(P{{O#P7!iy ztG6?=;Cx$7oEst_kf5ljUKquK&GotG@U=1jJjmw;Ayn7y*v9fa=RKE;oNlj+VXU+p z^W0h20e-9?+Ho7sEB-V%4NOj7WsnjRQ^fj%gVe1oo{IGo%g?}IX=44wWg89GfpHq7 zNN03yr+c)Gjs5MGPWOjhb}k5M?TqK?j&8QH+G07ln`QYIJ+)NN4A6$OhKLLR=5DFU zf4t0DcnF{@5_N&tcG!~ZucH1I)%oIQc^rn9W8qEVt&y3iRyc zeL;awsaAsN^PwSP-zdBG<5rwj>@oc(&9+r5ci@ga^uA~7pu4XGri6lA%U+xJz+|48`gv>p^G_${>gy<(vFq84i_KhWtK{}r2e!` zWL!t8{Ew%sJ$eRs>(9b0>CM16+>eTfUNP)tZ447CvRFn;WTqP01;krAJqV1Y z@TWyoEEU=sA@NpZH3e)nHg^PgM* literal 0 HcmV?d00001 diff --git a/thirdparty/Qt-Color-Widgets/gallery/ColorPreview.png b/thirdparty/Qt-Color-Widgets/gallery/ColorPreview.png new file mode 100644 index 0000000000000000000000000000000000000000..0751b3f8d45e6a3f6bc47dd49ad939a52f6f4ded GIT binary patch literal 243 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!2~2j9iA5fDb50q$YKTtz9S&aI8~cZ8YsBb z)5S3)qw(!6N3LcA0f&n&F;ZWRzV_G#-L9^fR1&W*U@rPc@<+brW{#Q`&ROyA{e{wf zG;Lq|$v>{|zkjJF?$nidJVR zN;0G|%wSAlYXC7KepEa3)c&uzXO1iilz#Z0G4NaMe)f`6FhkBTYgTe~DWM4fNBdLs literal 0 HcmV?d00001 diff --git a/thirdparty/Qt-Color-Widgets/gallery/ColorWheel.png b/thirdparty/Qt-Color-Widgets/gallery/ColorWheel.png new file mode 100644 index 0000000000000000000000000000000000000000..49212fc0dd671c46f46a2d70a1ec5774a7c34ea3 GIT binary patch literal 22791 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 literal 0 HcmV?d00001 diff --git a/thirdparty/Qt-Color-Widgets/gallery/GradientEditor.png b/thirdparty/Qt-Color-Widgets/gallery/GradientEditor.png new file mode 100644 index 0000000000000000000000000000000000000000..9a920749c30492bb3f58f82f269987e2cad4da97 GIT binary patch literal 230 zcmeAS@N?(olHy`uVBq!ia0vp^AwbN}!2~3~I@B)$Qk(@Ik;M!Qd`Cc-ajG_-G*EDc zr;B5VM`!Y#jm6@JJy>;kczERA=YFT{S3j{Iae7$N4|qj9qm`3ODvk%&0fq@W)B~(|DpfySV+e`S?hAci>52sB_+Q8@nBs%Pkv>>mCwqeJymr}ngY-FhZrofJ#R6o bx|CtRP*ACDyzv2`8yP%Z{an^LB{Ts5rZrb~ literal 0 HcmV?d00001 diff --git a/thirdparty/Qt-Color-Widgets/gallery/GradientListModel_combo.png b/thirdparty/Qt-Color-Widgets/gallery/GradientListModel_combo.png new file mode 100644 index 0000000000000000000000000000000000000000..417aeb7ac82b737053f75d2330ed3aa81bb465a7 GIT binary patch literal 1749 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 literal 0 HcmV?d00001 diff --git a/thirdparty/Qt-Color-Widgets/gallery/HueSlider.png b/thirdparty/Qt-Color-Widgets/gallery/HueSlider.png new file mode 100644 index 0000000000000000000000000000000000000000..58261670e8f7bbfd8efa6214a428412f23481393 GIT binary patch literal 249 zcmeAS@N?(olHy`uVBq!ia0vp^2Y{HLg9%95GK3ofDb50q$YKTtz9S&aI8~cZ8YsBh z)5S3)qw(#{jl72pL|PthemrTRL{q3h5~EhuvV;bQqXAQH1UO7o>P%ke;PF3vu8wL| ztcI*`mQfUVZU?#WY&KCt|6c!!Xd`R!p+*$%5>1F oF=3&@h5&^Kn6USQd>a{k=d;FVdQ&MBb@0Ekjsi~s-t literal 0 HcmV?d00001 diff --git a/thirdparty/Qt-Color-Widgets/gallery/README.md b/thirdparty/Qt-Color-Widgets/gallery/README.md new file mode 100644 index 00000000..a3a7cb99 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/gallery/README.md @@ -0,0 +1,91 @@ +Color Widgets +============= + +Color2DSlider +------------- +![Color2DSlider](Color2DSlider.png) + +This widget allow the user to select 2 HSV color components at the same time, +by default that is Saturation and Value but they can be changed. + +ColorDialog +----------- +![ColorDialog](ColorDialog.png) + +This is a dialog analogous to QColorDialog but with a much nicer and friendlier +user interface. It uses several of the other widgets. + +ColorLineEdit +------------- +![ColorLineEdit](ColorLineEdit.png) +![ColorLineEdit with color preview](ColorLineEdit_with_color.png) + +This is a QLineEdit intended to be used to edit and display color names. +It accepts several string formats: +* #f00 (3 hexadecimal rgb digits) +* #ff0000 (6 hexadecimal rgb digits) +* rgb(255,0,0) (function-like) +* red (color name) + +It can optionally display the color it represents on its background. + +ColorListWidget +--------------- +![ColorListWidget](ColorListWidget.png) + +It allows to display and edit a list of colors. + +ColorPaletteWidget and Swatch +----------------------------- +These widgets handle color palettes. + +![Swatch](Swatch.png) + +**Swatch** only handles a single palette, can be used to just select colors from the +palette or to modify the palette via drag and drop operations. + +**ColorPaletteModel** is a list model that represents palettes with can be used +with the Qt item view widgets, it provides a name and a preview icon for +each of the stored palettes. +It also has functionality to load and save palettes from the filesystem. + +![Read-only ColorPaletteWidget](ColorPaletteWidget_readonly.png) +![ColorPaletteWidget](ColorPaletteWidget.png) + +**ColorPaletteWidget** manages a list of palettes (via **ColorPaletteModel**). +Has two modes: read-only only allows to select palettes and colors, +otherwise it can be used to modify the list of palettes and the palette itself. + +ColorPreview and ColorSelector +------------------------------ +![ColorPreview](ColorPreview.png) + +**ColorPreview** is a widget that displays a color or compares two colors. + +**ColorSelector** is like **ColorPreview** but when clicked it shows a ColorDialog. + +GradientSlider and HueSlider +---------------------------- +![HueSlider](HueSlider.png) + +**GradientSlider** is a QSlider which uses a gradient as its background. + +**HueSlider** is specifically made to select a hue and has more information +about the represented color. + + +GradientEditor +-------------- +![GradientEditor](GradientEditor.png) + +**GradientEditor** is similar in appearance to **GradientSlider** but it's for editing the gradient. + + +GradientListModel and GradientDelegate +-------------------------------------- +![GradientListModel](GradientListModel_combo.png) +![GradientListModel](GradientListModel_view.png) + +**GradientListModel** is a QAbstractListModel used to list gradients (useful for combo boxes, item views and the like). + +**GradientDelegate** is an item delegate to edit gradients in an item view. diff --git a/thirdparty/Qt-Color-Widgets/gallery/Swatch.png b/thirdparty/Qt-Color-Widgets/gallery/Swatch.png new file mode 100644 index 0000000000000000000000000000000000000000..68e8d4fd06a8f833fb9f59f2d8c30e5a4121d731 GIT binary patch literal 951 zcmeAS@N?(olHy`uVBq!ia0vp^2Y@(%g9%6;S@dcjkm4-xh%9Dc;5!1sj8nDwq!}2P zGd*1#Ln`LHy}Phmx>DlU$IO*Id`EI)c6120Ug&U0{&u8e)mcIF#!ZU?c}rI8whUi3 zA=GlCh>*Bms5;v%Q_Ym`n^>v>UQF4dmh7eQ?93m=^l#_pRNlAw_WR;{{a4({awqnd zzhBSvAcDV1VaMN>SNj2+<;tsXz90TucPrz@d-mIJ7v0@<|NFJqIy`T` zCr;B{e4_6D+_PqdIs5aMX4=W!em{Hh#XP(1_pkr{dpIj~|N1hk)If>;_515~=l{=q z@SlT2h^eWiK|xW$0~b^3n}fxz-QUB3?(k4LK08Hh$$6XV1@}*RzPZV~^!Lm4^K38Z z-#&U{GrQE<&EDtbUf4vRQ~q7M-TrpUJllX7&$Q3WtuVe-`{_+%-5Yr>p!|~Hf3ZLp zn7?UUfX7ytvodZlKg=kvUU0ng@!572j^N{R8r`SotM}Pncz^is<(0|&U(zpn{9ZTdg4THjiIKMZM46GthJPx6elBvM0NL`~COBHp9|C z^*753uEx!|`TqLe?TKZ1`_CUbo$>Qho&D>5UFPfGe$RY;)MEav_wHv8)=azY{g*crz-(Sb+pOLAarNguMDaih^$7he#&EFRF*k=3v{#j>#=B4j{fBp69!w2i` zuMF$nWFnWJ|L+M2)|c~_OtO#@^<4V$CiBz6w-eL(ORRI|RDE*@Z~itjo&U==@p;wX z9Hu9JxwM(R>S}L1kfR^zopr0LMX~O8@`> literal 0 HcmV?d00001 diff --git a/thirdparty/Qt-Color-Widgets/gallery/screenshot.cpp b/thirdparty/Qt-Color-Widgets/gallery/screenshot.cpp new file mode 100644 index 00000000..a322af0f --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/gallery/screenshot.cpp @@ -0,0 +1,213 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#include +#include + +#include +#include +#include +#include +#include + +#include "QtColorWidgets/color_2d_slider.hpp" +#include "QtColorWidgets/color_delegate.hpp" /// \todo show it +#include "QtColorWidgets/color_dialog.hpp" +#include "QtColorWidgets/color_line_edit.hpp" +#include "QtColorWidgets/color_list_widget.hpp" +#include "QtColorWidgets/color_palette_widget.hpp" +#include "QtColorWidgets/color_preview.hpp" +#include "QtColorWidgets/color_wheel.hpp" +#include "QtColorWidgets/harmony_color_wheel.hpp" +#include "QtColorWidgets/hue_slider.hpp" +#include "QtColorWidgets/gradient_editor.hpp" +#include "QtColorWidgets/gradient_list_model.hpp" +#include "QtColorWidgets/gradient_delegate.hpp" + +bool run = false; +QStringList just_these; + +void screenshot(QWidget& widget, QString name = QString()) +{ + if ( name.isEmpty() ) + { + name = widget.metaObject()->className(); + name.remove("color_widgets::"); + } + if ( !just_these.isEmpty() && !just_these.contains(name) ) + return; + + widget.setWindowTitle(name); + QPixmap pic(widget.size()); + widget.render(&pic); + name += ".png"; + pic.save(name); + if ( run ) + widget.show(); +} + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + + QCommandLineParser parser; + parser.addHelpOption(); + parser.addPositionalArgument("just_these", "Only these widgets"); + QCommandLineOption run_option("run", "Show widgets instead of saving to file"); + parser.addOption(run_option); + + parser.process(a); + run = parser.isSet(run_option); + just_these = parser.positionalArguments(); + + QColor demo_color(64,172,143,128); + + color_widgets::ColorPalette palette1; + color_widgets::ColorPalette palette2; + int palette_columns = 12; + palette1.setName("Palette 1"); + palette2.setName("Palette 2"); + palette1.setColumns(palette_columns); + palette2.setColumns(palette_columns); + for ( int i = 0; i < 6; i++ ) + { + for ( int j = 0; j < palette_columns; j++ ) + { + float f = float(j)/palette_columns; + palette1.appendColor(QColor::fromHsvF(i/8.0,1-f,0.5+f/2)); + palette2.appendColor(QColor::fromHsvF(i/8.0,1-f,1-f)); + } + } + color_widgets::ColorPaletteModel palette_model; + palette_model.addPalette(palette1, false); + palette_model.addPalette(palette2, false); + + + color_widgets::ColorPreview preview; + preview.setColor(demo_color); + preview.setDisplayMode(color_widgets::ColorPreview::SplitAlpha); + preview.resize(128,32); + screenshot(preview); + + color_widgets::ColorDialog dialog; + dialog.setColorSpace(color_widgets::ColorWheel::ColorLCH); + dialog.setColor(demo_color); + screenshot(dialog); + + color_widgets::Color2DSlider slider2d; + slider2d.setColor(demo_color); + slider2d.resize(128,192); + screenshot(slider2d); + + color_widgets::ColorLineEdit line_edit; + line_edit.setColor(demo_color); + line_edit.resize(line_edit.sizeHint()); + screenshot(line_edit); + line_edit.setPreviewColor(true); + screenshot(line_edit, "ColorLineEdit_with_color"); + + color_widgets::ColorWheel wheel; + wheel.resize(256, 256); + wheel.setColor(demo_color); + screenshot(wheel); + + color_widgets::HarmonyColorWheel harwheel; + harwheel.resize(256, 256); + harwheel.setColor(demo_color); + harwheel.addHarmony(.333, true); + harwheel.addHarmony(.667, true); + screenshot(harwheel); + + color_widgets::Swatch swatch; + swatch.setPalette(palette1); + swatch.resize(swatch.sizeHint()); + screenshot(swatch); + + color_widgets::ColorPaletteWidget palette_widget; + palette_widget.setModel(&palette_model); + screenshot(palette_widget); + palette_widget.setReadOnly(true); + screenshot(palette_widget, "ColorPaletteWidget_readonly"); + + color_widgets::HueSlider hue_slider; + hue_slider.setColor(demo_color); + hue_slider.resize(192, hue_slider.sizeHint().height()); +// hue_slider.setInvertedAppearance(true); +// hue_slider.setOrientation(Qt::Vertical); + screenshot(hue_slider); + + color_widgets::ColorListWidget list_widget; + list_widget.setColors({ + demo_color, + palette1.colorAt(palette_columns*0), + palette1.colorAt(palette_columns*1), + palette1.colorAt(palette_columns*3), + palette1.colorAt(palette_columns*5), + }); + list_widget.resize(list_widget.sizeHint()); + screenshot(list_widget); + + color_widgets::GradientEditor editor; + QGradientStops gradient_colors; + float n_colors = 4; + for ( int i = 0; i <= n_colors; ++i ) + gradient_colors.append(QGradientStop(i/n_colors, QColor::fromHsvF(i/n_colors, 0.5, 1))); + editor.setStops(gradient_colors); + screenshot(editor); + + QComboBox gradient_list; + color_widgets::GradientListModel gradient_model; + gradient_model.setGradient("Rainbow", gradient_colors); + gradient_model.setGradient("Black to Transparent", QGradientStops{{0, Qt::black}, {1, QColor(0, 0, 0, 0)}}); + gradient_list.setModel(&gradient_model); + gradient_model.setIconSize(QSize(128, 24)); + gradient_list.setIconSize(gradient_model.iconSize()); + QObject::connect(&editor, &color_widgets::GradientEditor::stopsChanged, &gradient_model, + [&gradient_model](const QGradientStops& stops){ gradient_model.setGradient("Rainbow", stops); }); + gradient_list.resize(gradient_list.sizeHint()); + screenshot(gradient_list, "GradientListModel_combo"); + + QListView gradient_view; + color_widgets::GradientDelegate gradient_delegate; + gradient_view.setItemDelegate(&gradient_delegate); + gradient_view.setModel(&gradient_model); +// gradient_model.setEditMode(color_widgets::GradientListModel::EditName); + gradient_model.setEditMode(color_widgets::GradientListModel::EditGradient); + gradient_view.resize(QSize(gradient_view.sizeHintForColumn(0) + 4, gradient_view.sizeHint().height())); + screenshot(gradient_view, "GradientListModel_view"); + + QTableWidget gradient_table; + gradient_table.setItemDelegate(&gradient_delegate); + gradient_table.setRowCount(2); + gradient_table.setColumnCount(2); + gradient_table.setItem(0, 0, new QTableWidgetItem()); + gradient_table.item(0, 0)->setData(Qt::EditRole, QVariant::fromValue(gradient_model.gradientBrush(0))); + gradient_table.setItem(0, 1, new QTableWidgetItem(gradient_model.nameFromIndex(0))); + gradient_table.setItem(1, 0, new QTableWidgetItem()); + gradient_table.item(1, 0)->setData(Qt::EditRole, QVariant::fromValue(gradient_model.gradientBrush(1))); + gradient_table.setItem(1, 1, new QTableWidgetItem(gradient_model.nameFromIndex(1))); + screenshot(gradient_table, "GradientDelegate_table"); + + if ( run ) + return a.exec(); + + return 0; +} diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/AbstractWidgetList b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/AbstractWidgetList new file mode 100644 index 00000000..32d4d0e6 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/AbstractWidgetList @@ -0,0 +1 @@ +#include "abstract_widget_list.hpp" diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/BoundColorSelector b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/BoundColorSelector new file mode 100644 index 00000000..bd4d6514 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/BoundColorSelector @@ -0,0 +1 @@ +#include "bound_color_selector.hpp" diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/CMakeLists.txt b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/CMakeLists.txt new file mode 100644 index 00000000..e182d44c --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/CMakeLists.txt @@ -0,0 +1,37 @@ +set (HEADERS +abstract_widget_list.hpp +bound_color_selector.hpp +color_2d_slider.hpp +color_delegate.hpp +color_dialog.hpp +color_line_edit.hpp +color_list_widget.hpp +color_names.hpp +color_palette.hpp +color_palette_model.hpp +color_palette_widget.hpp +color_preview.hpp +color_selector.hpp +color_wheel.hpp +colorwidgets_global.hpp +gradient_slider.hpp +hue_slider.hpp +swatch.hpp +gradient_editor.hpp +harmony_color_wheel.hpp +gradient_list_model.hpp +gradient_delegate.hpp +) + +file(RELATIVE_PATH + PREFIX + ${PROJECT_SOURCE_DIR} + ${CMAKE_CURRENT_LIST_DIR}) + + +foreach (HEADER IN LISTS HEADERS) + target_sources (${TARGET_NAME} + PRIVATE + $ + $) +endforeach (HEADER IN HEADERS) diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/ColorDelegate b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/ColorDelegate new file mode 100644 index 00000000..0068a341 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/ColorDelegate @@ -0,0 +1 @@ +#include "color_delegate.hpp" diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/ColorDialog b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/ColorDialog new file mode 100644 index 00000000..ffaa02b9 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/ColorDialog @@ -0,0 +1 @@ +#include "color_dialog.hpp" diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/ColorListWidget b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/ColorListWidget new file mode 100644 index 00000000..e0d281b4 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/ColorListWidget @@ -0,0 +1 @@ +#include "color_list_widget.hpp" diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/ColorPreview b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/ColorPreview new file mode 100644 index 00000000..bfd4165e --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/ColorPreview @@ -0,0 +1 @@ +#include "color_preview.hpp" diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/ColorSelector b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/ColorSelector new file mode 100644 index 00000000..9b09fc8a --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/ColorSelector @@ -0,0 +1 @@ +#include "color_selector.hpp" diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/ColorWheel b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/ColorWheel new file mode 100644 index 00000000..ed17f683 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/ColorWheel @@ -0,0 +1 @@ +#include "color_wheel.hpp" diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/GradientEditor b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/GradientEditor new file mode 100644 index 00000000..b90265eb --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/GradientEditor @@ -0,0 +1 @@ +#include "gradient_editor.hpp" diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/GradientListModel b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/GradientListModel new file mode 100644 index 00000000..b6a5ef7b --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/GradientListModel @@ -0,0 +1 @@ +#include "gradient_list_model.hpp" diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/GradientSlider b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/GradientSlider new file mode 100644 index 00000000..b8035fda --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/GradientSlider @@ -0,0 +1 @@ +#include "gradient_slider.hpp" diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/HarmonyColorWheel b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/HarmonyColorWheel new file mode 100644 index 00000000..693e6478 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/HarmonyColorWheel @@ -0,0 +1 @@ +#include "harmony_color_wheel.hpp" diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/HueSlider b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/HueSlider new file mode 100644 index 00000000..df6786e3 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/HueSlider @@ -0,0 +1 @@ +#include "hue_slider.hpp" diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/abstract_widget_list.hpp b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/abstract_widget_list.hpp new file mode 100644 index 00000000..cd8d6f88 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/abstract_widget_list.hpp @@ -0,0 +1,110 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#ifndef ABSTRACT_WIDGET_LIST_HPP +#define ABSTRACT_WIDGET_LIST_HPP + +#include "colorwidgets_global.hpp" + +#include +#include + +class QCP_EXPORT AbstractWidgetList : public QWidget +{ + Q_OBJECT +public: + explicit AbstractWidgetList(QWidget *parent = 0); + ~AbstractWidgetList(); + + /** + * \brief Get the number of items + */ + int count() const; + + /** + * \brief Swap row a and row b + */ + virtual void swap(int a, int b) = 0; + + + /// Whether the given row index is valid + bool isValidRow(int i) const { return i >= 0 && i < count(); } + + void setRowHeight(int row, int height); + + +public Q_SLOTS: + /** + * \brief Remove row i + */ + void remove(int i); + + /** + * \brief append a default row + */ + virtual void append() = 0; + +Q_SIGNALS: + void removed(int i); + +protected: + + /** + * \brief Create a new row with the given widget + * + * Must be caled by implementations of append() + */ + void appendWidget(QWidget* w); + + /** + * \brief get the widget found at the given row + */ + QWidget* widget(int i); + + + /** + * \brief get the widget found at the given row + */ + template + T* widget_cast(int i) { return qobject_cast(widget(i)); } + + /** + * \brief clear all rows without emitting signals + * + * May be useful when implementation want to set all values at once + */ + void clear(); + +private Q_SLOTS: + void remove_clicked(QWidget* w); + void up_clicked(QWidget* w); + void down_clicked(QWidget* w); + +private: + class Private; + Private * const p; + + QWidget* create_button(QWidget* data, QSignalMapper*mapper, + QString icon_name, QString text, + QString tooltip = QString()) const; +}; + +#endif // ABSTRACT_WIDGET_LIST_HPP diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/bound_color_selector.hpp b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/bound_color_selector.hpp new file mode 100644 index 00000000..5d782939 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/bound_color_selector.hpp @@ -0,0 +1,44 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#ifndef BOUND_COLOR_SELECTOR_HPP +#define BOUND_COLOR_SELECTOR_HPP + +#include "color_selector.hpp" + +namespace color_widgets { +/** + * \brief A color selector bound to a color reference + * \todo Maybe this can be removed + */ +class QCP_EXPORT BoundColorSelector : public ColorSelector +{ + Q_OBJECT +private: + QColor* ref; +public: + explicit BoundColorSelector(QColor* reference, QWidget *parent = 0); + +private Q_SLOTS: + void update_reference(QColor); +}; +} // namespace color_widgets +#endif // BOUND_COLOR_SELECTOR_HPP diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_2d_slider.hpp b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_2d_slider.hpp new file mode 100644 index 00000000..51c67d00 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_2d_slider.hpp @@ -0,0 +1,128 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#ifndef COLOR_WIDGETS_COLOR_2D_SLIDER_HPP +#define COLOR_WIDGETS_COLOR_2D_SLIDER_HPP + +#include "colorwidgets_global.hpp" +#include + +namespace color_widgets { + +/** + * \brief A 2D slider that edits 2 color components + */ +class QCP_EXPORT Color2DSlider : public QWidget +{ + Q_OBJECT + + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged DESIGNABLE true STORED false ) + Q_PROPERTY(qreal hue READ hue WRITE setHue DESIGNABLE false ) + Q_PROPERTY(qreal saturation READ saturation WRITE setSaturation DESIGNABLE false ) + Q_PROPERTY(qreal value READ value WRITE setValue DESIGNABLE false ) + /** + * \brief Which color component is used on the x axis + */ + Q_PROPERTY(Component componentX READ componentX WRITE setComponentX NOTIFY componentXChanged) + /** + * \brief Which color component is used on the y axis + */ + Q_PROPERTY(Component componentY READ componentY WRITE setComponentY NOTIFY componentYChanged) + + +public: + enum Component { + Hue, Saturation, Value + }; + Q_ENUMS(Component) + + explicit Color2DSlider(QWidget *parent = nullptr); + ~Color2DSlider(); + + /// Get current color + QColor color() const; + + QSize sizeHint() const Q_DECL_OVERRIDE; + + /// Get current hue in the range [0-1] + qreal hue() const; + + /// Get current saturation in the range [0-1] + qreal saturation() const; + + /// Get current value in the range [0-1] + qreal value() const; + + Component componentX() const; + Component componentY() const; + +public Q_SLOTS: + + /// Set current color + void setColor(const QColor& c); + + /** + * @param h Hue [0-1] + */ + void setHue(qreal h); + + /** + * @param s Saturation [0-1] + */ + void setSaturation(qreal s); + + /** + * @param v Value [0-1] + */ + void setValue(qreal v); + + void setComponentX(Component componentX); + void setComponentY(Component componentY); + +Q_SIGNALS: + /** + * Emitted when the user selects a color or setColor is called + */ + void colorChanged(QColor); + + /** + * Emitted when the user selects a color + */ + void colorSelected(QColor); + + void componentXChanged(Component componentX); + void componentYChanged(Component componentY); + +protected: + void paintEvent(QPaintEvent* event) Q_DECL_OVERRIDE; + void mousePressEvent(QMouseEvent* event) Q_DECL_OVERRIDE; + void mouseMoveEvent(QMouseEvent* event) Q_DECL_OVERRIDE; + void mouseReleaseEvent(QMouseEvent* event) Q_DECL_OVERRIDE; + void resizeEvent(QResizeEvent* event) Q_DECL_OVERRIDE; + +private: + class Private; + Private * const p; +}; + +} // namespace color_widgets + +#endif // COLOR_WIDGETS_COLOR_2D_SLIDER_HPP diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_delegate.hpp b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_delegate.hpp new file mode 100644 index 00000000..7be2ab78 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_delegate.hpp @@ -0,0 +1,54 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#ifndef COLOR_DELEGATE_HPP +#define COLOR_DELEGATE_HPP + +#include "colorwidgets_global.hpp" + +#include + +namespace color_widgets { + +/** + Delegate to use a ColorSelector in a color list +*/ +class QCP_EXPORT ColorDelegate : public QAbstractItemDelegate +{ + Q_OBJECT +public: + explicit ColorDelegate(QWidget *parent = 0); + + virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const Q_DECL_OVERRIDE; + + bool editorEvent(QEvent* event, + QAbstractItemModel* model, + const QStyleOptionViewItem & option, + const QModelIndex & index) override; + + virtual QSize sizeHint(const QStyleOptionViewItem &option, + const QModelIndex &index) const Q_DECL_OVERRIDE; +}; + +} // namespace color_widgets + +#endif // COLOR_DELEGATE_HPP diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_dialog.hpp b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_dialog.hpp new file mode 100644 index 00000000..16ac8164 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_dialog.hpp @@ -0,0 +1,162 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#ifndef COLOR_DIALOG_HPP +#define COLOR_DIALOG_HPP + +#include "colorwidgets_global.hpp" +#include "color_preview.hpp" +#include "color_wheel.hpp" + +#include + +class QAbstractButton; + +namespace color_widgets { + +class QCP_EXPORT ColorDialog : public QDialog +{ + Q_OBJECT + Q_ENUMS(ButtonMode) + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged DESIGNABLE true) + Q_PROPERTY(ColorWheel::ShapeEnum wheelShape READ wheelShape WRITE setWheelShape NOTIFY wheelShapeChanged) + Q_PROPERTY(ColorWheel::ColorSpaceEnum colorSpace READ colorSpace WRITE setColorSpace NOTIFY colorSpaceChanged) + Q_PROPERTY(bool wheelRotating READ wheelRotating WRITE setWheelRotating NOTIFY wheelRotatingChanged) + /** + * \brief whether the color alpha channel can be edited. + * + * If alpha is disabled, the selected color's alpha will always be 255. + */ + Q_PROPERTY(bool alphaEnabled READ alphaEnabled WRITE setAlphaEnabled NOTIFY alphaEnabledChanged) + +public: + enum ButtonMode { + OkCancel, + OkApplyCancel, + Close + }; + + explicit ColorDialog(QWidget *parent = 0, Qt::WindowFlags f = 0); + + ~ColorDialog(); + + /** + * Get currently selected color + */ + QColor color() const; + + /** + * Set the display mode for the color preview + */ + void setPreviewDisplayMode(ColorPreview::DisplayMode mode); + + /** + * Get the color preview diplay mode + */ + ColorPreview::DisplayMode previewDisplayMode() const; + + bool alphaEnabled() const; + + /** + * Select which dialog buttons to show + * + * There are three predefined modes: + * OkCancel - this is useful when the dialog is modal and we just want to return a color + * OkCancelApply - this is for non-modal dialogs + * Close - for non-modal dialogs with direct color updates via colorChanged signal + */ + void setButtonMode(ButtonMode mode); + ButtonMode buttonMode() const; + + QSize sizeHint() const; + + ColorWheel::ShapeEnum wheelShape() const; + ColorWheel::ColorSpaceEnum colorSpace() const; + bool wheelRotating() const; + +public Q_SLOTS: + + /** + * Change color + */ + void setColor(const QColor &c); + + /** + * Set the current color and show the dialog + */ + void showColor(const QColor &oldcolor); + + void setWheelShape(ColorWheel::ShapeEnum shape); + void setColorSpace(ColorWheel::ColorSpaceEnum space); + void setWheelRotating(bool rotating); + + /** + * Set whether the color alpha channel can be edited. + * If alpha is disabled, the selected color's alpha will always be 255. + */ + void setAlphaEnabled(bool a); + +Q_SIGNALS: + /** + * The current color was changed + */ + void colorChanged(QColor); + + /** + * The user selected the new color by pressing Ok/Apply + */ + void colorSelected(QColor); + + void wheelShapeChanged(ColorWheel::ShapeEnum shape); + void colorSpaceChanged(ColorWheel::ColorSpaceEnum space); + void wheelRotatingChanged(bool rotating); + + void alphaEnabledChanged(bool alphaEnabled); + +private Q_SLOTS: + /// Update all the Ui elements to match the selected color + void setColorInternal(const QColor &color); + /// Update from HSV sliders + void set_hsv(); + /// Update from RGB sliders + void set_rgb(); + /// Update from Alpha slider + void set_alpha(); + + void on_edit_hex_colorChanged(const QColor& color); + void on_edit_hex_colorEditingFinished(const QColor& color); + + void on_buttonBox_clicked(QAbstractButton*); + +protected: + void dragEnterEvent(QDragEnterEvent *event); + void dropEvent(QDropEvent * event); + void mouseReleaseEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + +private: + class Private; + Private * const p; +}; + +} // namespace color_widgets + +#endif // COLOR_DIALOG_HPP diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_line_edit.hpp b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_line_edit.hpp new file mode 100644 index 00000000..05643adc --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_line_edit.hpp @@ -0,0 +1,98 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#ifndef COLOR_WIDGETS_COLOR_LINE_EDIT_HPP +#define COLOR_WIDGETS_COLOR_LINE_EDIT_HPP + +#include "colorwidgets_global.hpp" +#include +#include + +namespace color_widgets { + +/** + * \brief A line edit used to define a color name + * + * Supported string formats: + * * Short hex strings #f00 + * * Long hex strings #ff0000 + * * Color names red + * * Function-like rgb(255,0,0) + * + * Additional string formats supported when showAlpha is true: + * * Long hex strings #ff0000ff + * * Function like rgba(255,0,0,255) + */ +class QCP_EXPORT ColorLineEdit : public QLineEdit +{ + Q_OBJECT + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) + /** + * \brief Whether the widget displays and edits the alpha channel + */ + Q_PROPERTY(bool showAlpha READ showAlpha WRITE setShowAlpha NOTIFY showAlphaChanged) + /** + * \brief If \b true, the background of the widget is changed to show the color + */ + Q_PROPERTY(bool previewColor READ previewColor WRITE setPreviewColor NOTIFY previewColorChanged) + +public: + explicit ColorLineEdit(QWidget* parent = nullptr); + ~ColorLineEdit(); + + QColor color() const; + bool showAlpha() const; + bool previewColor() const; + +public Q_SLOTS: + void setColor(const QColor& color); + void setShowAlpha(bool showAlpha); + void setPreviewColor(bool previewColor); + +Q_SIGNALS: + /** + * \brief Emitted when the color is changed by any means + */ + void colorChanged(const QColor& color); + /** + * \brief Emitted when the user is typing a color but has not finished yet + */ + void colorEdited(const QColor& color); + /** + * \brief Emitted when the user finished to edit a string + */ + void colorEditingFinished(const QColor& color); + + void showAlphaChanged(bool showAlpha); + void previewColorChanged(bool previewColor); + +protected: + void dragEnterEvent(QDragEnterEvent *event) Q_DECL_OVERRIDE; + void dropEvent(QDropEvent * event) Q_DECL_OVERRIDE; + void paintEvent(QPaintEvent* event) Q_DECL_OVERRIDE; + +private: + class Private; + Private* p; +}; + +} // namespace color_widgets +#endif // COLOR_WIDGETS_COLOR_LINE_EDIT_HPP diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_list_widget.hpp b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_list_widget.hpp new file mode 100644 index 00000000..1f7ca9a2 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_list_widget.hpp @@ -0,0 +1,78 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#ifndef COLOR_LIST_WIDGET_HPP +#define COLOR_LIST_WIDGET_HPP + +#include "abstract_widget_list.hpp" +#include "color_wheel.hpp" + +namespace color_widgets { + +class QCP_EXPORT ColorListWidget : public AbstractWidgetList +{ + Q_OBJECT + + Q_PROPERTY(QList colors READ colors WRITE setColors NOTIFY colorsChanged ) + Q_PROPERTY(ColorWheel::ShapeEnum wheelShape READ wheelShape WRITE setWheelShape NOTIFY wheelShapeChanged) + Q_PROPERTY(ColorWheel::ColorSpaceEnum colorSpace READ colorSpace WRITE setColorSpace NOTIFY colorSpaceChanged) + Q_PROPERTY(bool wheelRotating READ wheelRotating WRITE setWheelRotating NOTIFY wheelRotatingChanged) + +public: + explicit ColorListWidget(QWidget *parent = 0); + ~ColorListWidget(); + + QList colors() const; + void setColors(const QList& colors); + + void swap(int a, int b); + + void append(); + + ColorWheel::ShapeEnum wheelShape() const; + ColorWheel::ColorSpaceEnum colorSpace() const; + bool wheelRotating() const; + +Q_SIGNALS: + void colorsChanged(const QList&); + void wheelShapeChanged(ColorWheel::ShapeEnum shape); + void colorSpaceChanged(ColorWheel::ColorSpaceEnum space); + void wheelRotatingChanged(bool rotating); + +public Q_SLOTS: + void setWheelShape(ColorWheel::ShapeEnum shape); + void setColorSpace(ColorWheel::ColorSpaceEnum space); + void setWheelRotating(bool rotating); + +private Q_SLOTS: + void emit_changed(); + void handle_removed(int); + void color_changed(int row); + +private: + class Private; + Private * const p; + void append_widget(int col); +}; + +} // namespace color_widgets + +#endif // COLOR_LIST_WIDGET_HPP diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_names.hpp b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_names.hpp new file mode 100644 index 00000000..1273999c --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_names.hpp @@ -0,0 +1,57 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#ifndef COLOR_WIDGETS_COLOR_NAMES_HPP +#define COLOR_WIDGETS_COLOR_NAMES_HPP + +#include +#include + +#include + +namespace color_widgets { + +/** + * \brief Convert a string into a color + * + * Supported string formats: + * * Short hex strings #f00 + * * Long hex strings #ff0000 + * * Color names red + * * Function-like rgb(255,0,0) + * + * Additional string formats supported only when \p alpha is true: + * * Long hex strings #ff0000ff + * * Function like rgba(255,0,0,255) + */ +QCP_EXPORT QColor colorFromString(const QString& string, bool alpha = true); + +/** + * \brief Convert a color into a string + * + * Format: + * * If the color has full alpha: #ff0000 + * * If alpha is true and the color has non-full alpha: #ff000088 + */ +QCP_EXPORT QString stringFromColor(const QColor& color, bool alpha = true); + +} // namespace color_widgets +#endif // COLOR_WIDGETS_COLOR_NAMES_HPP diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_palette.hpp b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_palette.hpp new file mode 100644 index 00000000..55797ba8 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_palette.hpp @@ -0,0 +1,229 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#ifndef COLOR_WIDGETS_COLOR_PALETTE_HPP +#define COLOR_WIDGETS_COLOR_PALETTE_HPP + +#include +#include +#include +#include +#include +#include +#include "colorwidgets_global.hpp" + +namespace color_widgets { + +class QCP_EXPORT ColorPalette : public QObject +{ + Q_OBJECT + + /** + * \brief The list of colors + */ + Q_PROPERTY(QVector colors READ colors WRITE setColors NOTIFY colorsChanged) + /** + * \brief Name of the palette + */ + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + /** + * \brief Number of colors to display in a row, if 0 unspecified + */ + Q_PROPERTY(int columns READ columns WRITE setColumns NOTIFY columnsChanged) + /** + * \brief Number of colors + */ + Q_PROPERTY(int count READ count) + /** + * \brief Name of the file the palette has been read from + */ + Q_PROPERTY(QString fileName READ fileName WRITE setFileName NOTIFY fileNameChanged) + /** + * \brief Whether it has been modified and it might be advisable to save it + */ + Q_PROPERTY(bool dirty READ dirty WRITE setDirty NOTIFY dirtyChanged) + +public: + typedef QPair value_type; + + ColorPalette(const QVector& colors, const QString& name = QString(), int columns = 0); + ColorPalette(const QVector >& colors, const QString& name = QString(), int columns = 0); + explicit ColorPalette(const QString& name = QString()); + ColorPalette(const ColorPalette& other); + ColorPalette& operator=(const ColorPalette& other); + ~ColorPalette(); + ColorPalette(ColorPalette&& other); + ColorPalette& operator=(ColorPalette&& other); + + /** + * \brief Color at the given index + */ + Q_INVOKABLE QColor colorAt(int index) const; + + /** + * \brief Color name at the given index + */ + Q_INVOKABLE QString nameAt(int index) const; + + QVector > colors() const; + QVector onlyColors() const; + + int count() const; + int columns(); + + QString name() const; + + /** + * \brief Use a color table to set the colors + */ + Q_INVOKABLE void loadColorTable(const QVector& color_table); + + /** + * \brief Convert to a color table + */ + Q_INVOKABLE QVector colorTable() const; + + /** + * \brief Creates a ColorPalette from a color table + */ + static ColorPalette fromColorTable(const QVector& table); + + /** + * \brief Use the pixels on an image to set the palette colors + */ + Q_INVOKABLE bool loadImage(const QImage& image); + + /** + * \brief Creates a ColorPalette from a Gimp palette (gpl) file + */ + static ColorPalette fromImage(const QImage& image); + + /** + * \brief Load contents from a Gimp palette (gpl) file + * \returns \b true On Success + * \note If this function returns \b false, the palette will become empty + */ + Q_INVOKABLE bool load(const QString& name); + + /** + * \brief Creates a ColorPalette from a Gimp palette (gpl) file + */ + static ColorPalette fromFile(const QString& name); + + QString fileName() const; + + bool dirty() const; + + /** + * \brief Returns a preview image of the colors in the palette + */ + QPixmap preview(const QSize& size, const QColor& background=Qt::transparent) const; + +public Q_SLOTS: + void setColumns(int columns); + + void setColors(const QVector& colors); + void setColors(const QVector >& colors); + + /** + * \brief Change the color at the given index + */ + void setColorAt(int index, const QColor& color); + /** + * \brief Change the color at the given index + */ + void setColorAt(int index, const QColor& color, const QString& name); + /** + * \brief Change the name of a color + */ + void setNameAt(int index, const QString& name = QString()); + /** + * \brief Append a color at the end + */ + void appendColor(const QColor& color, const QString& name = QString()); + /** + * \brief Insert a color in an arbitrary location + */ + void insertColor(int index, const QColor& color, const QString& name = QString()); + /** + * \brief Remove the color at the given index + */ + void eraseColor(int index); + + /** + * \brief Change file name and save + * \returns \b true on success + */ + bool save(const QString& filename); + /** + * \brief save to file, the filename is \c fileName or determined automatically + * \returns \b true on success + */ + bool save(); + + void setName(const QString& name); + void setFileName(const QString& name); + void setDirty(bool dirty); + +Q_SIGNALS: + /** + * \brief Emitted when all the colors have changed + */ + void colorsChanged(const QVector >&); + void columnsChanged(int); + void nameChanged(const QString&); + void fileNameChanged(const QString&); + void dirtyChanged(bool); + /** + * \brief Emitted when the color or the name at the given index has been modified + */ + void colorChanged(int index); + /** + * \brief Emitted when the color at the given index has been removed + */ + void colorRemoved(int index); + /** + * \brief Emitted when a single color has been added + */ + void colorAdded(int index); + /** + * \brief Emitted when the colors have been modified with a simple operation (set, append etc.) + */ + void colorsUpdated(const QVector>&); + +private: + /** + * \brief Returns \c name if it isn't null, otherwise a default value + */ + QString unnamed(const QString& name = QString()) const; + + /** + * \brief Emit all the necessary signals when the palette has been completely overwritten + */ + void emitUpdate(); + + class Private; + Private *p; +}; + +} // namespace color_widgets + +#endif // COLOR_WIDGETS_COLOR_PALETTE_HPP diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_palette_model.hpp b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_palette_model.hpp new file mode 100644 index 00000000..045786b9 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_palette_model.hpp @@ -0,0 +1,140 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#ifndef COLOR_WIDGETS_COLOR_PALETTE_MODEL_HPP +#define COLOR_WIDGETS_COLOR_PALETTE_MODEL_HPP + +#include +#include "color_palette.hpp" + +namespace color_widgets { + +class QCP_EXPORT ColorPaletteModel : public QAbstractListModel +{ + Q_OBJECT + /** + * \brief List of directories to be scanned for palette files + */ + Q_PROPERTY(QStringList searchPaths READ searchPaths WRITE setSearchPaths NOTIFY searchPathsChanged) + + /** + * \brief Default directory to be used when saving a palette + */ + Q_PROPERTY(QString savePath READ savePath WRITE setSavePath NOTIFY savePathChanged) + + /** + * \brief Size of the icon used for the palette previews + */ + Q_PROPERTY(QSize iconSize READ iconSize WRITE setIconSize NOTIFY iconSizeChanged) + + +public: + ColorPaletteModel(); + ~ColorPaletteModel(); + int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; + bool removeRows(int row, int count, const QModelIndex & parent = QModelIndex()) Q_DECL_OVERRIDE; + + QString savePath() const; + QStringList searchPaths() const; + QSize iconSize() const; + + /** + * \brief Number of palettes + */ + int count() const; + + /** + * \brief Returns a reference to the first palette with the given name + * \pre hasPalette(name) + */ + const ColorPalette& palette(const QString& name) const; + + /** + * \brief Whether a palette with the given name exists in the model + */ + bool hasPalette(const QString& name) const; + + /** + * \brief Get the palette at the given index (row) + * \pre 0 <= index < count() + */ + const ColorPalette& palette(int index) const; + + /** + * \brief Updates an existing palette + * \param index Palette index + * \param palette New palette + * \param save Whether to save the palette to the filesystem + * + * Saving will try: (in this order) + * * To overwrite the file pointed by the old palette + * * To write to the new palette file name + * * To create a file in the save path + * If all of the above fail, the palette will be replaced interally + * but not on the filesystem + * + * \returns \b true if the palette has been successfully updated (and saved) + */ + bool updatePalette(int index, const ColorPalette& palette, bool save = true); + + /** + * \brief Remove a palette from the model and optionally from the filesystem + * \returns \b true if the palette has been successfully removed + */ + bool removePalette(int index, bool remove_file = true); + + /** + * \brief Remove a palette to the model and optionally to the filesystem + * \returns \b true if the palette has been successfully added + */ + bool addPalette(const ColorPalette& palette, bool save = true); + + /** + * \brief The index of the palette with the given file name + * \returns -1 if none is found + */ + int indexFromFile(const QString& filename) const; + +public Q_SLOTS: + void setSavePath(const QString& savePath); + void setSearchPaths(const QStringList& searchPaths); + void addSearchPath(const QString& path); + void setIconSize(const QSize& iconSize); + + /** + * \brief Load palettes files found in the search paths + */ + void load(); + +Q_SIGNALS: + void savePathChanged(const QString& savePath); + void searchPathsChanged(const QStringList& searchPaths); + void iconSizeChanged(const QSize& iconSize); + +private: + class Private; + Private* p; +}; + +} // namespace color_widgets + +#endif // COLOR_WIDGETS_COLOR_PALETTE_MODEL_HPP diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_palette_widget.hpp b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_palette_widget.hpp new file mode 100644 index 00000000..34d45de2 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_palette_widget.hpp @@ -0,0 +1,187 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#ifndef COLOR_WIDGETS_COLOR_PALETTE_WIDGET_HPP +#define COLOR_WIDGETS_COLOR_PALETTE_WIDGET_HPP + +#include +#include +#include "color_palette_model.hpp" +#include "swatch.hpp" + +namespace color_widgets { + +/** + * \brief A widget to use and modify palettes + */ +class QCP_EXPORT ColorPaletteWidget : public QWidget +{ + Q_OBJECT + + /** + * \brief Model used to store the palettes + */ + Q_PROPERTY(ColorPaletteModel* model READ model WRITE setModel NOTIFY modelChanged) + + /** + * \brief Size of a single color in the swatch widget + */ + Q_PROPERTY(QSize colorSize READ colorSize WRITE setColorSize NOTIFY colorSizeChanged) + /** + * \brief Policy for colorSize + **/ + Q_PROPERTY(color_widgets::Swatch::ColorSizePolicy colorSizePolicy READ colorSizePolicy WRITE setColorSizePolicy NOTIFY colorSizePolicyChanged) + + /** + * \brief Border around the colors + */ + Q_PROPERTY(QPen border READ border WRITE setBorder NOTIFY borderChanged) + + /** + * \brief Forces the Swatch to display that many rows of colors + * + * If there are too few elements, the widget will display less than this + * many rows. + * + * A value of0 means that the number of rows is automatic. + * + * \note Conflicts with forcedColumns + */ + Q_PROPERTY(int forcedRows READ forcedRows WRITE setForcedRows NOTIFY forcedRowsChanged) + + /** + * \brief Forces the Swatch to display that many columns of colors + * + * If there are too few elements, the widget will display less than this + * many columns. + * + * A value of 0 means that the number of columns is automatic. + * + * \note Conflicts with forcedRows + */ + Q_PROPERTY(int forcedColumns READ forcedColumns WRITE setForcedColumns NOTIFY forcedColumnsChanged) + + /** + * \brief Whether the palettes can be modified via user interaction + */ + Q_PROPERTY(bool readOnly READ readOnly WRITE setReadOnly NOTIFY readOnlyChanged) + + /** + * \brief Currently selected color + */ + Q_PROPERTY(QColor currentColor READ currentColor WRITE setCurrentColor NOTIFY currentColorChanged) + + /** + * \brief Currently selected model row + */ + Q_PROPERTY(int currentRow READ currentRow WRITE setCurrentRow NOTIFY currentRowChanged) + + /** + * \brief Palette shown by the widget + */ + Q_PROPERTY(const ColorPalette& currentPalette READ currentPalette NOTIFY currentPaletteChanged) + +public: + ColorPaletteWidget(QWidget* parent = nullptr); + ~ColorPaletteWidget(); + + ColorPaletteModel* model() const; + + /** + * \brief Currently selected palette + * \pre model() != nullptr and there is a selected palette + */ + const ColorPalette& currentPalette() const; + + QSize colorSize() const; + Swatch::ColorSizePolicy colorSizePolicy() const; + QPen border() const; + + int forcedRows() const; + int forcedColumns() const; + + bool readOnly() const; + QColor currentColor() const; + + int currentRow() const; + +public Q_SLOTS: + void setModel(ColorPaletteModel* model); + void setColorSize(const QSize& colorSize); + void setColorSizePolicy(Swatch::ColorSizePolicy colorSizePolicy); + void setBorder(const QPen& border); + void setForcedRows(int forcedRows); + void setForcedColumns(int forcedColumns); + void setReadOnly(bool readOnly); + /** + * \brief Clear the selected color + */ + void clearCurrentColor(); + /** + * \brief Attempt to select a color + * + * If the given color is available in the current palete, it will be selected + * \return \b true on success + */ + bool setCurrentColor(const QColor& color); + /** + * \brief Attempt to select a color by name + * + * If the given color is available in the current palete, it will be selected + * \return \b true on success + */ + bool setCurrentColor(const QString& name); + /** + * \brief Attempt to select a color by index + * + * If the given color is available in the current palete, it will be selected + * \return \b true on success + */ + bool setCurrentColor(int index); + /** + * \brief Set the selected row in the model + */ + void setCurrentRow(int currentRow); + +Q_SIGNALS: + void modelChanged(ColorPaletteModel* model); + void colorSizeChanged(const QSize& colorSize); + void colorSizePolicyChanged(Swatch::ColorSizePolicy colorSizePolicy); + void forcedRowsChanged(int forcedRows); + void forcedColumnsChanged(int forcedColumns); + void readOnlyChanged(bool readOnly); + void currentColorChanged(const QColor& currentColor); + void currentColorChanged(int index); + void borderChanged(const QPen& border); + void currentRowChanged(int currentRow); + void currentPaletteChanged(const ColorPalette& palette); + +private Q_SLOTS: + void on_palette_list_currentIndexChanged(int index); + void on_swatch_doubleClicked(int index); + +private: + class Private; + std::unique_ptr p; +}; + +} // namespace color_widgets +#endif // COLOR_WIDGETS_COLOR_PALETTE_WIDGET_HPP diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_preview.hpp b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_preview.hpp new file mode 100644 index 00000000..16060b82 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_preview.hpp @@ -0,0 +1,110 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * \copyright Copyright (C) 2014 Calle Laakkonen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#ifndef COLOR_PREVIEW_HPP +#define COLOR_PREVIEW_HPP + +#include "colorwidgets_global.hpp" + +#include + +namespace color_widgets { + +/** + * Simple widget that shows a preview of a color + */ +class QCP_EXPORT ColorPreview : public QWidget +{ + Q_OBJECT + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged DESIGNABLE true) + Q_PROPERTY(QColor comparisonColor READ comparisonColor WRITE setComparisonColor NOTIFY comparisonColorChanged DESIGNABLE true) + Q_PROPERTY(DisplayMode display_mode READ displayMode WRITE setDisplayMode NOTIFY displayModeChanged DESIGNABLE true) + Q_PROPERTY(QBrush background READ background WRITE setBackground NOTIFY backgroundChanged DESIGNABLE true) + Q_ENUMS(DisplayMode) +public: + enum DisplayMode + { + NoAlpha, ///< Show current color with no transparency + AllAlpha, ///< show current color with transparency + SplitAlpha, ///< Show both solid and transparent side by side + SplitColor ///< Show current and comparison colors side by side + }; + Q_ENUMS(DisplayMode) + + explicit ColorPreview(QWidget *parent = 0); + ~ColorPreview(); + + /// Get the background visible under transparent colors + QBrush background() const; + + /// Change the background visible under transparent colors + void setBackground(const QBrush &bk); + + /// Get color display mode + DisplayMode displayMode() const; + + /// Set how transparent colors are handled + void setDisplayMode(DisplayMode dm); + + /// Get current color + QColor color() const; + + /// Get the comparison color + QColor comparisonColor() const; + + QSize sizeHint () const; + + void paint(QPainter &painter, QRect rect) const; + +public Q_SLOTS: + /// Set current color + void setColor(const QColor &c); + + /// Set the comparison color + void setComparisonColor(const QColor &c); + +Q_SIGNALS: + /// Emitted when the user clicks on the widget + void clicked(); + + /// Emitted on setColor + void colorChanged(QColor); + + void comparisonColorChanged(QColor); + void displayModeChanged(DisplayMode); + void backgroundChanged(const QBrush&); + +protected: + void paintEvent(QPaintEvent *); + void resizeEvent(QResizeEvent *); + void mouseReleaseEvent(QMouseEvent *ev); + void mouseMoveEvent(QMouseEvent *ev); + +private: + class Private; + Private * const p; +}; + +} // namespace color_widgets +Q_DECLARE_METATYPE(color_widgets::ColorPreview::DisplayMode) + +#endif // COLOR_PREVIEW_HPP diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_selector.hpp b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_selector.hpp new file mode 100644 index 00000000..3e84d05f --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_selector.hpp @@ -0,0 +1,97 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#ifndef COLOR_SELECTOR_HPP +#define COLOR_SELECTOR_HPP + +#include "color_preview.hpp" +#include "color_wheel.hpp" + +namespace color_widgets { + +/** + * Color preview that opens a color dialog + */ +class QCP_EXPORT ColorSelector : public ColorPreview +{ + Q_OBJECT + Q_ENUMS(UpdateMode) + Q_PROPERTY(UpdateMode updateMode READ updateMode WRITE setUpdateMode NOTIFY updateModeChanged) + Q_PROPERTY(Qt::WindowModality dialogModality READ dialogModality WRITE setDialogModality NOTIFY dialogModalityChanged) + Q_PROPERTY(ColorWheel::ShapeEnum wheelShape READ wheelShape WRITE setWheelShape NOTIFY wheelShapeChanged) + Q_PROPERTY(ColorWheel::ColorSpaceEnum colorSpace READ colorSpace WRITE setColorSpace NOTIFY colorSpaceChanged) + Q_PROPERTY(bool wheelRotating READ wheelRotating WRITE setWheelRotating NOTIFY wheelRotatingChanged) + +public: + enum UpdateMode { + Confirm, ///< Update color only after the dialog has been accepted + Continuous ///< Update color as it's being modified in the dialog + }; + + explicit ColorSelector(QWidget *parent = 0); + ~ColorSelector(); + + void setUpdateMode(UpdateMode m); + UpdateMode updateMode() const; + + Qt::WindowModality dialogModality() const; + void setDialogModality(Qt::WindowModality m); + + ColorWheel::ShapeEnum wheelShape() const; + ColorWheel::ColorSpaceEnum colorSpace() const; + bool wheelRotating() const; + +Q_SIGNALS: + void wheelShapeChanged(ColorWheel::ShapeEnum shape); + void colorSpaceChanged(ColorWheel::ColorSpaceEnum space); + void wheelRotatingChanged(bool rotating); + void updateModeChanged(UpdateMode); + void dialogModalityChanged(Qt::WindowModality); + +public Q_SLOTS: + void showDialog(); + void setWheelShape(ColorWheel::ShapeEnum shape); + void setColorSpace(ColorWheel::ColorSpaceEnum space); + void setWheelRotating(bool rotating); + +private Q_SLOTS: + void accept_dialog(); + void reject_dialog(); + void update_old_color(const QColor &c); + +protected: + void dragEnterEvent(QDragEnterEvent *event); + void dropEvent(QDropEvent * event); + +private: + /// Connect/Disconnect colorChanged based on UpdateMode + void connect_dialog(); + + /// Disconnect from dialog update + void disconnect_dialog(); + + class Private; + Private * const p; +}; + +} // namespace color_widgets + +#endif // COLOR_SELECTOR_HPP diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_utils.hpp b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_utils.hpp new file mode 100644 index 00000000..62c884a3 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_utils.hpp @@ -0,0 +1,77 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#ifndef COLOR_UTILS_HPP +#define COLOR_UTILS_HPP + +#include +#include + +#include "QtColorWidgets/colorwidgets_global.hpp" + +namespace color_widgets { +namespace detail { + + +inline qreal color_chromaF(const QColor& c) +{ + qreal max = qMax(c.redF(), qMax(c.greenF(), c.blueF())); + qreal min = qMin(c.redF(), qMin(c.greenF(), c.blueF())); + return max - min; +} + +inline qreal color_lumaF(const QColor& c) +{ + return 0.30 * c.redF() + 0.59 * c.greenF() + 0.11 * c.blueF(); +} +QColor color_from_lch(qreal hue, qreal chroma, qreal luma, qreal alpha = 1 ); + +inline QColor rainbow_lch(qreal hue) +{ + return color_from_lch(hue,1,1); +} + +inline QColor rainbow_hsv(qreal hue) +{ + return QColor::fromHsvF(hue,1,1); +} + +inline qreal color_lightnessF(const QColor& c) +{ + return ( qMax(c.redF(),qMax(c.greenF(),c.blueF())) + + qMin(c.redF(),qMin(c.greenF(),c.blueF())) ) / 2; +} + +inline qreal color_HSL_saturationF(const QColor& col) +{ + qreal c = color_chromaF(col); + qreal l = color_lightnessF(col); + if ( qFuzzyCompare(l+1,1) || qFuzzyCompare(l+1,2) ) + return 0; + return c / (1-qAbs(2*l-1)); +} + +QCP_EXPORT QColor color_from_hsl(qreal hue, qreal sat, qreal lig, qreal alpha = 1 ); + +} // namespace detail +} // namespace color_widgets + +#endif // COLOR_UTILS_HPP diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_wheel.hpp b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_wheel.hpp new file mode 100644 index 00000000..2b6e65e3 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_wheel.hpp @@ -0,0 +1,176 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#ifndef COLOR_WHEEL_HPP +#define COLOR_WHEEL_HPP + +#include + +#include "colorwidgets_global.hpp" + + +namespace color_widgets { + +/** + * \brief Display an analog widget that allows the selection of a HSV color + * + * It has an outer wheel to select the Hue and an intenal square to select + * Saturation and Lightness. + */ +class QCP_EXPORT ColorWheel : public QWidget +{ + Q_OBJECT + + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged DESIGNABLE true STORED false ) + Q_PROPERTY(qreal hue READ hue WRITE setHue DESIGNABLE false ) + Q_PROPERTY(qreal saturation READ saturation WRITE setSaturation DESIGNABLE false ) + Q_PROPERTY(qreal value READ value WRITE setValue DESIGNABLE false ) + Q_PROPERTY(unsigned wheelWidth READ wheelWidth WRITE setWheelWidth NOTIFY wheelWidthChanged DESIGNABLE true ) + Q_PROPERTY(ShapeEnum selectorShape READ selectorShape WRITE setSelectorShape NOTIFY selectorShapeChanged DESIGNABLE true ) + Q_PROPERTY(bool rotatingSelector READ rotatingSelector WRITE setRotatingSelector NOTIFY rotatingSelectorChanged DESIGNABLE true ) + Q_PROPERTY(ColorSpaceEnum colorSpace READ colorSpace WRITE setColorSpace NOTIFY colorSpaceChanged DESIGNABLE true ) + +public: + enum ShapeEnum + { + ShapeTriangle, ///< A triangle + ShapeSquare, ///< A square + }; + + enum AngleEnum + { + AngleFixed, ///< The inner part doesn't rotate + AngleRotating, ///< The inner part follows the hue selector + }; + + enum ColorSpaceEnum + { + ColorHSV, ///< Use the HSV color space + ColorHSL, ///< Use the HSL color space + ColorLCH, ///< Use Luma Chroma Hue (Y_601') + }; + + Q_ENUM(ShapeEnum); + Q_ENUM(AngleEnum); + Q_ENUM(ColorSpaceEnum); + + explicit ColorWheel(QWidget *parent = 0); + ~ColorWheel(); + + /// Get current color + QColor color() const; + + virtual QSize sizeHint() const Q_DECL_OVERRIDE; + + /// Get current hue in the range [0-1] + qreal hue() const; + + /// Get current saturation in the range [0-1] + qreal saturation() const; + + /// Get current value in the range [0-1] + qreal value() const; + + /// Get the width in pixels of the outer wheel + unsigned int wheelWidth() const; + + /// Set the width in pixels of the outer wheel + void setWheelWidth(unsigned int w); + + /// Shape of the internal selector + ShapeEnum selectorShape() const; + + /// Whether the internal selector should rotare in accordance with the hue + bool rotatingSelector() const; + + /// Color space used to preview/edit the color + ColorSpaceEnum colorSpace() const; + +public Q_SLOTS: + + /// Set current color + void setColor(QColor c); + + /** + * @param h Hue [0-1] + */ + void setHue(qreal h); + + /** + * @param s Saturation [0-1] + */ + void setSaturation(qreal s); + + /** + * @param v Value [0-1] + */ + void setValue(qreal v); + + /// Sets the shape of the internal selector + void setSelectorShape(ShapeEnum shape); + + /// Sets whether the internal selector should rotare in accordance with the hue + void setRotatingSelector(bool rotating); + + /// Sets the color space used to preview/edit the color + void setColorSpace(ColorSpaceEnum space); + +Q_SIGNALS: + /** + * Emitted when the user selects a color or setColor is called + */ + void colorChanged(QColor); + + /** + * Emitted when the user selects a color + */ + void colorSelected(QColor); + + void wheelWidthChanged(unsigned); + + void selectorShapeChanged(ShapeEnum shape); + + void rotatingSelectorChanged(bool rotating); + + void colorSpaceChanged(ColorSpaceEnum space); + +protected: + void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE; + void mouseMoveEvent(QMouseEvent *) Q_DECL_OVERRIDE; + void mousePressEvent(QMouseEvent *) Q_DECL_OVERRIDE; + void mouseReleaseEvent(QMouseEvent *) Q_DECL_OVERRIDE; + void resizeEvent(QResizeEvent *) Q_DECL_OVERRIDE; + void dragEnterEvent(QDragEnterEvent* event) Q_DECL_OVERRIDE; + void dropEvent(QDropEvent* event) Q_DECL_OVERRIDE; + +protected: + class Private; + ColorWheel(QWidget *parent, Private* data); + Private* data() const { return p; } + +private: + Private * const p; + +}; + +} // namespace color_widgets + +#endif // COLOR_WHEEL_HPP diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_wheel_private.hpp b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_wheel_private.hpp new file mode 100644 index 00000000..60714cb7 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/color_wheel_private.hpp @@ -0,0 +1,284 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * \copyright Copyright (C) 2017 caryoscelus + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ + +#include "QtColorWidgets/color_wheel.hpp" +#include "QtColorWidgets/color_utils.hpp" + +#include +#include + +namespace color_widgets { + +enum MouseStatus +{ + Nothing, + DragCircle, + DragSquare +}; + +class ColorWheel::Private +{ +private: + ColorWheel * const w; + +public: + qreal hue, sat, val; + bool backgroundIsDark; + unsigned int wheel_width; + MouseStatus mouse_status; + QPixmap hue_ring; + QImage inner_selector; + std::vector inner_selector_buffer; + ColorSpaceEnum color_space = ColorHSV; + bool rotating_selector = true; + ShapeEnum selector_shape = ShapeTriangle; + QColor (*color_from)(qreal,qreal,qreal,qreal); + QColor (*rainbow_from_hue)(qreal); + int max_size = 128; + + Private(ColorWheel *widget) + : w(widget), hue(0), sat(0), val(0), + wheel_width(20), mouse_status(Nothing), + color_from(&QColor::fromHsvF), rainbow_from_hue(&detail::rainbow_hsv) + { + } + + void setup() + { + qreal backgroundValue = w->palette().background().color().valueF(); + backgroundIsDark = backgroundValue < 0.5; + } + + virtual ~Private(){} + + /// Calculate outer wheel radius from idget center + qreal outer_radius() const + { + return qMin(w->geometry().width(), w->geometry().height())/2; + } + + /// Calculate inner wheel radius from idget center + qreal inner_radius() const + { + return outer_radius()-wheel_width; + } + + /// Calculate the edge length of the inner square + qreal square_size() const + { + return inner_radius()*qSqrt(2); + } + + /// Calculate the height of the inner triangle + qreal triangle_height() const + { + return inner_radius()*3/2; + } + + /// Calculate the side of the inner triangle + qreal triangle_side() const + { + return inner_radius()*qSqrt(3); + } + + /// return line from center to given point + QLineF line_to_point(const QPoint &p) const + { + return QLineF (w->geometry().width()/2, w->geometry().height()/2, p.x(), p.y()); + } + + /** + * Ensures the internal image buffer is the correct size + * and that the QImage is associated to it + */ + void init_buffer(QSize size) + { + std::size_t linear_size = size.width() * size.height(); + if ( inner_selector_buffer.size() == linear_size ) + return; + inner_selector_buffer.resize(linear_size); + inner_selector = QImage( + reinterpret_cast(inner_selector_buffer.data()), + size.width(), + size.height(), + QImage::Format_RGB32 + ); + } + + void render_square() + { + int width = qMin(square_size(), max_size); + init_buffer(QSize(width, width)); + + for ( int y = 0; y < width; ++y ) + { + for ( int x = 0; x < width; ++x ) + { + QRgb color = color_from(hue,double(x)/width,double(y)/width,1).rgb(); + inner_selector_buffer[width * y + x] = color; + } + } + } + + /** + * \brief renders the selector as a triangle + * \note It's the same as a square with the edge with value=0 collapsed to a single point + */ + void render_triangle() + { + QSizeF size = selector_size(); + if ( size.height() > max_size ) + size *= max_size / size.height(); + + qreal ycenter = size.height()/2; + + QSize isize = size.toSize(); + init_buffer(isize); + + for (int x = 0; x < isize.width(); x++ ) + { + qreal pval = x / size.height(); + qreal slice_h = size.height() * pval; + for (int y = 0; y < isize.height(); y++ ) + { + qreal ymin = ycenter-slice_h/2; + qreal psat = qBound(0.0,(y-ymin)/slice_h,1.0); + QRgb color = color_from(hue,psat,pval,1).rgb(); + inner_selector_buffer[isize.width() * y + x] = color; + } + } + } + + /// Updates the inner image that displays the saturation-value selector + void render_inner_selector() + { + if ( selector_shape == ShapeTriangle ) + render_triangle(); + else + render_square(); + } + + /// Offset of the selector image + QPointF selector_image_offset() + { + if ( selector_shape == ShapeTriangle ) + return QPointF(-inner_radius(),-triangle_side()/2); + return QPointF(-square_size()/2,-square_size()/2); + } + + /** + * \brief Size of the selector when rendered to the screen + */ + QSizeF selector_size() + { + if ( selector_shape == ShapeTriangle ) + return QSizeF(triangle_height(), triangle_side()); + return QSizeF(square_size(), square_size()); + } + + + /// Rotation of the selector image + qreal selector_image_angle() + { + if ( selector_shape == ShapeTriangle ) + { + if ( rotating_selector ) + return -hue*360-60; + return -150; + } + else + { + if ( rotating_selector ) + return -hue*360-45; + else + return 180; + } + } + + /// Updates the outer ring that displays the hue selector + void render_ring() + { + hue_ring = QPixmap(outer_radius()*2,outer_radius()*2); + hue_ring.fill(Qt::transparent); + QPainter painter(&hue_ring); + painter.setRenderHint(QPainter::Antialiasing); + painter.setCompositionMode(QPainter::CompositionMode_Source); + + + const int hue_stops = 24; + QConicalGradient gradient_hue(0, 0, 0); + if ( gradient_hue.stops().size() < hue_stops ) + { + for ( double a = 0; a < 1.0; a+=1.0/(hue_stops-1) ) + { + gradient_hue.setColorAt(a,rainbow_from_hue(a)); + } + gradient_hue.setColorAt(1,rainbow_from_hue(0)); + } + + painter.translate(outer_radius(),outer_radius()); + + painter.setPen(Qt::NoPen); + painter.setBrush(QBrush(gradient_hue)); + painter.drawEllipse(QPointF(0,0),outer_radius(),outer_radius()); + + painter.setBrush(Qt::transparent);//palette().background()); + painter.drawEllipse(QPointF(0,0),inner_radius(),inner_radius()); + } + + void set_color(const QColor& c) + { + switch ( color_space ) + { + case ColorHSV: + hue = qMax(0.0, c.hsvHueF()); + sat = c.hsvSaturationF(); + val = c.valueF(); + break; + case ColorHSL: + hue = qMax(0.0, c.hueF()); + sat = detail::color_HSL_saturationF(c); + val = detail::color_lightnessF(c); + break; + case ColorLCH: + hue = qMax(0.0, c.hsvHueF()); + sat = detail::color_chromaF(c); + val = detail::color_lumaF(c); + break; + } + } + + void draw_ring_editor(double editor_hue, QPainter& painter, QColor color) { + painter.setPen(QPen(color,3)); + painter.setBrush(Qt::NoBrush); + QLineF ray(0, 0, outer_radius(), 0); + ray.setAngle(editor_hue*360); + QPointF h1 = ray.p2(); + ray.setLength(inner_radius()); + QPointF h2 = ray.p2(); + painter.drawLine(h1,h2); + } + +}; + +} // namespace color_widgets diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/colorwidgets_global.hpp b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/colorwidgets_global.hpp new file mode 100644 index 00000000..58d5fb62 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/colorwidgets_global.hpp @@ -0,0 +1,16 @@ +#ifndef QT_COLOR_WIDGETS_GLOBAL_H +#define QT_COLOR_WIDGETS_GLOBAL_H + +#include + +#define QTCOLORWIDGETS_STATICALLY_LINKED + +#if defined(QTCOLORWIDGETS_STATICALLY_LINKED) +# define QCP_EXPORT +#elif defined(QTCOLORWIDGETS_LIBRARY) +# define QCP_EXPORT Q_DECL_EXPORT +#else +# define QCP_EXPORT Q_DECL_IMPORT +#endif + +#endif // QT_COLOR_WIDGETS_GLOBAL_H diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/gradient_delegate.hpp b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/gradient_delegate.hpp new file mode 100644 index 00000000..7f2940f4 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/gradient_delegate.hpp @@ -0,0 +1,103 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ + +#ifndef COLOR_WIDGETS_GRADIENT_DELEGATE_HPP +#define COLOR_WIDGETS_GRADIENT_DELEGATE_HPP + + +#include +#include + +#include "QtColorWidgets/gradient_editor.hpp" + +namespace color_widgets { + +/** + * \brief Item delegate to edits gradients + * + * In order to make it work, return as edit data from the model a QBrush with a gradient + */ +class QCP_EXPORT GradientDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + QWidget * createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const Q_DECL_OVERRIDE + { + QVariant data = index.data(Qt::EditRole); + if ( data.canConvert() ) + { + QBrush brush = data.value(); + if ( brush.gradient() ) + { + GradientEditor* editor = new GradientEditor(parent); + editor->setStops(brush.gradient()->stops()); + return editor; + } + } + + return QStyledItemDelegate::createEditor(parent, option, index); + } + + void setModelData(QWidget * widget, QAbstractItemModel * model, const QModelIndex & index) const Q_DECL_OVERRIDE + { + if ( GradientEditor* editor = qobject_cast(widget) ) + model->setData(index, QBrush(editor->gradient()), Qt::EditRole); + else + QStyledItemDelegate::setModelData(widget, model, index); + } + + void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const Q_DECL_OVERRIDE + { + QVariant display_data = index.data(Qt::DisplayRole); + QVariant gradient_data = display_data.isValid() ? display_data : index.data(Qt::EditRole); + if ( gradient_data.canConvert() ) + { + QBrush brush = gradient_data.value(); + if ( brush.gradient() ) + { + QBrush background; + background.setTexture(QPixmap(QStringLiteral(":/color_widgets/alphaback.png"))); + painter->fillRect(option.rect, background); + + QLinearGradient g(option.rect.topLeft(), option.rect.topRight()); + g.setStops(brush.gradient()->stops()); + painter->fillRect(option.rect, g); + + if ( option.state & QStyle::State_Selected ) + { + int border = 2; + painter->setBrush(Qt::transparent); + painter->setPen(QPen(option.palette.highlight(), border)); + painter->drawRect(option.rect.adjusted(border/2, border/2, -border/2, -border/2)); + } + return; + } + } + + QStyledItemDelegate::paint(painter, option, index); + } +}; + +} // namespace color_widgets + +#endif // COLOR_WIDGETS_GRADIENT_DELEGATE_HPP diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/gradient_editor.hpp b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/gradient_editor.hpp new file mode 100644 index 00000000..196f8670 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/gradient_editor.hpp @@ -0,0 +1,123 @@ +/** + * \file gradient_editor.hpp + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#ifndef GRADIENT_EDITOR_HPP +#define GRADIENT_EDITOR_HPP + +#include "colorwidgets_global.hpp" + +#include +#include + +namespace color_widgets { + +class ColorDialog; + +/** + * \brief A slider that moves on top of a gradient + */ +class QCP_EXPORT GradientEditor : public QWidget +{ + Q_OBJECT + Q_PROPERTY(QBrush background READ background WRITE setBackground NOTIFY backgroundChanged) + Q_PROPERTY(QGradientStops stops READ stops WRITE setStops NOTIFY stopsChanged) + Q_PROPERTY(QLinearGradient gradient READ gradient WRITE setGradient) + Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation) + Q_PROPERTY(int selectedStop READ selectedStop WRITE setSelectedStop NOTIFY selectedStopChanged) + Q_PROPERTY(QColor selectedColor READ selectedColor WRITE setSelectedColor) + +public: + explicit GradientEditor(QWidget *parent = 0); + explicit GradientEditor(Qt::Orientation orientation, QWidget *parent = 0); + ~GradientEditor(); + + QSize sizeHint() const override; + + /// Get the background, it's visible for transparent gradient stops + QBrush background() const; + /// Set the background, it's visible for transparent gradient stops + void setBackground(const QBrush &bg); + + /// Get the colors that make up the gradient + QGradientStops stops() const; + /// Set the colors that make up the gradient + void setStops(const QGradientStops &colors); + + /// Get the gradient + QLinearGradient gradient() const; + /// Set the gradient + void setGradient(const QLinearGradient &gradient); + + Qt::Orientation orientation() const; + + /** + * \brief Dialog shown when double clicking a stop + */ + ColorDialog* dialog() const; + + /** + * \brief Index of the currently selected gradient stop (or -1 if there is no selection) + */ + int selectedStop() const; + + /** + * \brief Color of the selected stop + */ + QColor selectedColor() const; + +public Q_SLOTS: + void setOrientation(Qt::Orientation); + void setSelectedStop(int stop); + void setSelectedColor(const QColor& color); + void addStop(); + void removeStop(); + +Q_SIGNALS: + void backgroundChanged(const QBrush&); + void stopsChanged(const QGradientStops&); + void selectedStopChanged(int); + +protected: + void paintEvent(QPaintEvent *ev) override; + + void mousePressEvent(QMouseEvent *ev) override; + void mouseMoveEvent(QMouseEvent *ev) override; + void mouseReleaseEvent(QMouseEvent *ev) override; + void leaveEvent(QEvent * event) override; + void mouseDoubleClickEvent(QMouseEvent *event) override; + + void dragEnterEvent(QDragEnterEvent *event) override; + void dragMoveEvent(QDragMoveEvent* event) override; + void dragLeaveEvent(QDragLeaveEvent *event) override; + void dropEvent(QDropEvent* event) override; + +private Q_SLOTS: + void dialogUpdate(const QColor& c); + +private: + class Private; + Private * const p; +}; + +} // namespace color_widgets + +#endif // GRADIENT_EDITOR_HPP + diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/gradient_helper.hpp b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/gradient_helper.hpp new file mode 100644 index 00000000..a1a81112 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/gradient_helper.hpp @@ -0,0 +1,98 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#ifndef GRADIENT_HELPER_HPP +#define GRADIENT_HELPER_HPP + +#include "colorwidgets_global.hpp" + +#include + +namespace color_widgets { + +inline QColor blendColors(const QColor& a, const QColor& b, qreal ratio) +{ + return QColor::fromRgbF( + a.redF() * (1-ratio) + b.redF() * ratio, + a.greenF() * (1-ratio) + b.greenF() * ratio, + a.blueF() * (1-ratio) + b.blueF() * ratio, + a.alphaF() * (1-ratio) + b.alphaF() * ratio + ); +} + + +/** + * \brief Get an insertion point in the gradient + * \param gradient Gradient stops to look into (must be properly set up) + * \param factor Value in [0, 1] to get the color for + * \return A pair whose first element is the index to insert the new value at, and a GradientStop + */ +inline QPair Q_DECL_EXPORT gradientBlendedColorInsert(const QGradientStops& gradient, qreal factor) +{ + if ( gradient.empty() ) + return {0, {0, QColor()}}; + + if ( gradient.size() == 1 || factor <= 0 ) + return {0, gradient.front()}; + + int i = 0; + QGradientStop s1; + for ( auto s2 : gradient ) + { + if ( factor < s2.first ) + { + qreal ratio = (factor - s1.first) / (s2.first - s1.first); + return {i, {factor, blendColors(s1.second, s2.second, ratio)}}; + } + s1 = s2; + ++i; + } + + return {gradient.size(), gradient.back()}; +} + +/** + * \brief Returns a color in the gradient + * \param gradient Gradient stops to look into (must be properly set up) + * \param factor Value in [0, 1] to get the color for + */ +inline QColor Q_DECL_EXPORT gradientBlendedColor(const QGradientStops& gradient, qreal factor) +{ + return gradientBlendedColorInsert(gradient, factor).second.second; +} + +/** + * \brief Returns a color in the gradient + * \param gradient Gradient to look into + * \param factor Value in [0, 1] to get the color for + */ +inline QColor Q_DECL_EXPORT gradientBlendedColor(const QGradient& gradient, qreal factor) +{ + return gradientBlendedColor(gradient.stops(), factor); +} + +} // namespace color_widgets + + + + +#endif // GRADIENT_HELPER_HPP + diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/gradient_list_model.hpp b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/gradient_list_model.hpp new file mode 100644 index 00000000..9c711e09 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/gradient_list_model.hpp @@ -0,0 +1,173 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#ifndef COLOR_WIDGETS_GRADIENT_LIST_MODEL_HPP +#define COLOR_WIDGETS_GRADIENT_LIST_MODEL_HPP + +#include "colorwidgets_global.hpp" + +#include +#include +#include + +namespace color_widgets { + +class QCP_EXPORT GradientListModel : public QAbstractListModel +{ + Q_OBJECT + + /** + * \brief Size of the icon used for the gradient previews + */ + Q_PROPERTY(QSize iconSize READ iconSize WRITE setIconSize NOTIFY iconSizeChanged) + + Q_PROPERTY(ItemEditMode editMode READ editMode WRITE setEditMode NOTIFY editModeChanged) + +public: + enum ItemEditMode + { + EditNone = 0, + EditName, + EditGradient, + }; + + Q_ENUM(ItemEditMode); + + GradientListModel(QObject *parent = nullptr); + ~GradientListModel(); + int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; + Qt::ItemFlags flags(const QModelIndex & index) const Q_DECL_OVERRIDE; + bool setData(const QModelIndex & index, const QVariant & value, int role) Q_DECL_OVERRIDE; + + QSize iconSize() const; + + /** + * \brief Number of gradients + */ + int count() const; + + /** + * \brief Remove all gradients + */ + void clear(); + + /** + * \brief Returns a reference to the first gradient with the given name + * \pre hasGradient(name) + */ + const QLinearGradient& gradient(const QString& name) const; + + /** + * \brief Returns a reference to the first gradient with the given name + * \pre hasGradient(name) + */ + QGradientStops gradientStops(const QString& name) const; + + /** + * \brief Whether a gradient with the given name exists in the model + */ + bool hasGradient(const QString& name) const; + + /** + * \brief Get the gradient at the given index (row) + * \pre 0 <= index < count() + */ + const QLinearGradient& gradient(int index) const; + + /** + * \brief Get the gradient stops at the given index (row) + * \pre 0 <= index < count() + */ + QGradientStops gradientStops(int index) const; + + /** + * \brief Inserts or updates a gradient + * \returns The index for the new gradient + */ + int setGradient(const QString& name, const QGradient& gradient); + + int setGradient(const QString& name, const QGradientStops& gradient); + + /** + * \brief Updates the gradient at \p index + */ + bool setGradient(int index, const QGradient& gradient); + + bool setGradient(int index, const QGradientStops& gradient); + + /** + * \brief Renames the gradient at \p index + * \returns \b true on success + */ + bool rename(int index, const QString& new_name); + + /** + * \brief Renames a gradient + * \returns \b true on success + */ + bool rename(const QString& old_name, const QString& new_name); + + /** + * \brief Remove a gradient from the model + * \returns \b true if the gradient has been successfully removed + */ + bool removeGradient(const QString& name); + + + bool removeGradient(int index); + + /** + * \brief The index of the gradient with the given name + * \returns -1 if none is found + */ + int indexFromName(const QString& name) const; + + /** + * \brief Name of the gradient at index + */ + QString nameFromIndex(int index) const; + + ItemEditMode editMode() const; + + /** + * \brief Brush for a gradient + * \pre 0 <= \p index < count() + */ + QBrush gradientBrush(int index) const; + +public Q_SLOTS: + void setIconSize(const QSize& iconSize); + void setEditMode(ItemEditMode mode); + +Q_SIGNALS: + void iconSizeChanged(const QSize& iconSize); + void editModeChanged(ItemEditMode mode); + +private: + class Private; + std::unique_ptr d; +}; + +} // namespace color_widgets + +#endif // COLOR_WIDGETS_GRADIENT_LIST_MODEL_HPP + diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/gradient_slider.hpp b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/gradient_slider.hpp new file mode 100644 index 00000000..ef7908dd --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/gradient_slider.hpp @@ -0,0 +1,117 @@ +/** + * \file gradient_slider.hpp + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * \copyright Copyright (C) 2014 Calle Laakkonen + * \copyright Copyright (C) 2017 caryoscelus + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#ifndef GRADIENT_SLIDER_HPP +#define GRADIENT_SLIDER_HPP + +#include "colorwidgets_global.hpp" + +#include +#include + +namespace color_widgets { + +/** + * \brief A slider that moves on top of a gradient + */ +class QCP_EXPORT GradientSlider : public QSlider +{ + Q_OBJECT + Q_PROPERTY(QBrush background READ background WRITE setBackground NOTIFY backgroundChanged) + Q_PROPERTY(QGradientStops colors READ colors WRITE setColors DESIGNABLE false) + Q_PROPERTY(QColor firstColor READ firstColor WRITE setFirstColor STORED false) + Q_PROPERTY(QColor lastColor READ lastColor WRITE setLastColor STORED false) + Q_PROPERTY(QLinearGradient gradient READ gradient WRITE setGradient) + +public: + explicit GradientSlider(QWidget *parent = 0); + explicit GradientSlider(Qt::Orientation orientation, QWidget *parent = 0); + ~GradientSlider(); + + /// Get the background, it's visible for transparent gradient stops + QBrush background() const; + /// Set the background, it's visible for transparent gradient stops + void setBackground(const QBrush &bg); + + /// Get the colors that make up the gradient + QGradientStops colors() const; + /// Set the colors that make up the gradient + void setColors(const QGradientStops &colors); + + /// Get the gradient + QLinearGradient gradient() const; + /// Set the gradient + void setGradient(const QLinearGradient &gradient); + + /** + * Overload: create an evenly distributed gradient of the given colors + */ + void setColors(const QVector &colors); + + /** + * \brief Set the first color of the gradient + * + * If the gradient is currently empty it will create a stop with the given color + */ + void setFirstColor(const QColor &c); + + /** + * \brief Set the last color of the gradient + * + * If the gradient is has less than two colors, + * it will create a stop with the given color + */ + void setLastColor(const QColor &c); + + /** + * \brief Get the first color + * + * \returns QColor() con empty gradient + */ + QColor firstColor() const; + + /** + * \brief Get the last color + * + * \returns QColor() con empty gradient + */ + QColor lastColor() const; + +Q_SIGNALS: + void backgroundChanged(const QBrush&); + +protected: + void paintEvent(QPaintEvent *ev) override; + + void mousePressEvent(QMouseEvent *ev) override; + void mouseMoveEvent(QMouseEvent *ev) override; + void mouseReleaseEvent(QMouseEvent *ev) override; + +private: + class Private; + Private * const p; +}; + +} // namespace color_widgets + +#endif // GRADIENT_SLIDER_HPP diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/harmony_color_wheel.hpp b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/harmony_color_wheel.hpp new file mode 100644 index 00000000..72e774eb --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/harmony_color_wheel.hpp @@ -0,0 +1,95 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * \copyright Copyright (C) 2017 caryoscelus + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#ifndef HARMONY_COLOR_WHEEL_HPP +#define HARMONY_COLOR_WHEEL_HPP + + +#include "color_wheel.hpp" + +namespace color_widgets { + +/** + * \brief ColorWheel with color harmonies + */ +class QCP_EXPORT HarmonyColorWheel : public ColorWheel +{ + Q_OBJECT + +public: + explicit HarmonyColorWheel(QWidget *parent = 0); + ~HarmonyColorWheel(); + + /// Get all harmony colors (including main) + QList harmonyColors() const; + + /// Get number of harmony colors (including main) + unsigned int harmonyCount() const; + + /// Clear harmony color scheme + void clearHarmonies(); + + /** + * @brief Add harmony color + * @param hue_diff Initial hue difference (in [0-1) range) + * @param editable Whether this harmony should be editable + * @returns Index of newly added harmony + */ + unsigned addHarmony(double hue_diff, bool editable); + + /** + * @brief Add symmetric harmony color + * @param relative_to Index of other harmony that should be symmetric relative to main hue + * @returns Index of newly added harmony + * Editability is inherited from symmetric editor + */ + unsigned addSymmetricHarmony(unsigned relative_to); + + /** + * @brief Add opposite harmony color + * @param relative_to Index of other harmony that should be opposite to this + * @returns Index of newly added harmony + * Editability is inherited from opposite editor + */ + unsigned addOppositeHarmony(unsigned relative_to); + +Q_SIGNALS: + /** + * Emitted when harmony settings or harmony colors are changed (including due to main hue change) + */ + void harmonyChanged(); + +protected: + void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE; + void mouseMoveEvent(QMouseEvent *) Q_DECL_OVERRIDE; + void mousePressEvent(QMouseEvent *) Q_DECL_OVERRIDE; + void mouseReleaseEvent(QMouseEvent *) Q_DECL_OVERRIDE; + +private: + class Private; + Private * p; +}; + +} // namespace color_widgets + +#endif // COLOR_WHEEL_HPP + diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/hue_slider.hpp b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/hue_slider.hpp new file mode 100644 index 00000000..fd3fc939 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/hue_slider.hpp @@ -0,0 +1,100 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2014 Calle Laakkonen + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#ifndef HUE_SLIDER_HPP +#define HUE_SLIDER_HPP + +#include "gradient_slider.hpp" + +namespace color_widgets { + +/** + * \brief A slider for selecting a hue value + */ +class QCP_EXPORT HueSlider : public GradientSlider +{ + Q_OBJECT + /** + * \brief Saturation used in the rainbow gradient, as a [0-1] float + */ + Q_PROPERTY(qreal colorSaturation READ colorSaturation WRITE setColorSaturation NOTIFY colorSaturationChanged) + /** + * \brief Value used in the rainbow gradient, as a [0-1] float + */ + Q_PROPERTY(qreal colorValue READ colorValue WRITE setColorValue NOTIFY colorValueChanged) + /** + * \brief Alpha used in the rainbow gradient, as a [0-1] float + */ + Q_PROPERTY(qreal colorAlpha READ colorAlpha WRITE setColorAlpha NOTIFY colorAlphaChanged) + + /** + * \brief Color with corresponding color* components + */ + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) + + /** + * \brief Normalized Hue, as indicated from the slider + */ + Q_PROPERTY(qreal colorHue READ colorHue WRITE setColorHue NOTIFY colorHueChanged) + + +public: + explicit HueSlider(QWidget *parent = nullptr); + explicit HueSlider(Qt::Orientation orientation, QWidget *parent = nullptr); + ~HueSlider(); + + qreal colorSaturation() const; + qreal colorValue() const; + qreal colorAlpha() const; + QColor color() const; + qreal colorHue() const; + +public Q_SLOTS: + void setColorValue(qreal value); + void setColorSaturation(qreal value); + void setColorAlpha(qreal alpha); + void setColorHue(qreal colorHue); + /** + * \brief Set Hue Saturation and ColorValue, ignoring alpha + */ + void setColor(const QColor& color); + /** + * \brief Set Hue Saturation, ColorValue and Alpha + */ + void setFullColor(const QColor& color); + +Q_SIGNALS: + void colorHueChanged(qreal colorHue); + void colorChanged(QColor); + void colorAlphaChanged(qreal v); + void colorSaturationChanged(qreal v); + void colorValueChanged(qreal v); + +private: + class Private; + Private * const p; +}; + +} // namespace color_widgets + +#endif // HUE_SLIDER_HPP + diff --git a/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/swatch.hpp b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/swatch.hpp new file mode 100644 index 00000000..85d49047 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/include/QtColorWidgets/swatch.hpp @@ -0,0 +1,194 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#ifndef COLOR_WIDGETS_SWATCH_HPP +#define COLOR_WIDGETS_SWATCH_HPP + +#include +#include +#include "color_palette.hpp" + +namespace color_widgets { + +/** + * \brief A widget drawing a palette + */ +class QCP_EXPORT Swatch : public QWidget +{ + Q_OBJECT + + /** + * \brief Palette shown by the widget + */ + Q_PROPERTY(const ColorPalette& palette READ palette WRITE setPalette NOTIFY paletteChanged) + /** + * \brief Currently selected color (-1 if no color is selected) + */ + Q_PROPERTY(int selected READ selected WRITE setSelected NOTIFY selectedChanged) + + /** + * \brief Preferred size for a color square + */ + Q_PROPERTY(QSize colorSize READ colorSize WRITE setColorSize NOTIFY colorSizeChanged) + + Q_PROPERTY(ColorSizePolicy colorSizePolicy READ colorSizePolicy WRITE setColorSizePolicy NOTIFY colorSizePolicyChanged) + + /** + * \brief Border around the colors + */ + Q_PROPERTY(QPen border READ border WRITE setBorder NOTIFY borderChanged) + + /** + * \brief Forces the Swatch to display that many rows of colors + * + * If there are too few elements, the widget will display less than this + * many rows. + * + * A value of0 means that the number of rows is automatic. + * + * \note Conflicts with forcedColumns + */ + Q_PROPERTY(int forcedRows READ forcedRows WRITE setForcedRows NOTIFY forcedRowsChanged) + + /** + * \brief Forces the Swatch to display that many columns of colors + * + * If there are too few elements, the widget will display less than this + * many columns. + * + * A value of 0 means that the number of columns is automatic. + * + * \note Conflicts with forcedRows + */ + Q_PROPERTY(int forcedColumns READ forcedColumns WRITE setForcedColumns NOTIFY forcedColumnsChanged) + + /** + * \brief Whether the palette can be modified via user interaction + * \note Even when this is \b false, it can still be altered programmatically + */ + Q_PROPERTY(bool readOnly READ readOnly WRITE setReadOnly NOTIFY readOnlyChanged) + + +public: + enum ColorSizePolicy + { + Hint, ///< The size is just a hint + Minimum, ///< Can expand but not contract + Fixed ///< Must be exactly as specified + }; + Q_ENUMS(ColorSizePolicy) + + Swatch(QWidget* parent = 0); + ~Swatch(); + + QSize sizeHint() const Q_DECL_OVERRIDE; + QSize minimumSizeHint() const Q_DECL_OVERRIDE; + + const ColorPalette& palette() const; + ColorPalette& palette(); + int selected() const; + /** + * \brief Color at the currently selected index + */ + QColor selectedColor() const; + + /** + * \brief Color index at the given position within the widget + * \param p Point in local coordinates + * \returns -1 if the position doesn't represent any color + */ + int indexAt(const QPoint& p); + + /** + * \brief Color at the given position within the widget + * \param p Point in local coordinates + */ + QColor colorAt(const QPoint& p); + + QSize colorSize() const; + ColorSizePolicy colorSizePolicy() const; + QPen border() const; + + int forcedRows() const; + int forcedColumns() const; + + bool readOnly() const; + +public Q_SLOTS: + void setPalette(const ColorPalette& palette); + void setSelected(int selected); + void clearSelection(); + void setColorSize(const QSize& colorSize); + void setColorSizePolicy(ColorSizePolicy colorSizePolicy); + void setBorder(const QPen& border); + void setForcedRows(int forcedRows); + void setForcedColumns(int forcedColumns); + void setReadOnly(bool readOnly); + /** + * \brief Remove the currently seleceted color + **/ + void removeSelected(); + +Q_SIGNALS: + void paletteChanged(const ColorPalette& palette); + void selectedChanged(int selected); + void colorSelected(const QColor& color); + void colorSizeChanged(const QSize& colorSize); + void colorSizePolicyChanged(ColorSizePolicy colorSizePolicy); + void doubleClicked(int index); + void rightClicked(int index); + void forcedRowsChanged(int forcedRows); + void forcedColumnsChanged(int forcedColumns); + void readOnlyChanged(bool readOnly); + void borderChanged(const QPen& border); + +protected: + bool event(QEvent* event) Q_DECL_OVERRIDE; + + void paintEvent(QPaintEvent* event) Q_DECL_OVERRIDE; + + void keyPressEvent(QKeyEvent* event) Q_DECL_OVERRIDE; + + void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; + void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE; + void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE; + void mouseDoubleClickEvent(QMouseEvent *event) Q_DECL_OVERRIDE; + void wheelEvent(QWheelEvent* event) Q_DECL_OVERRIDE; + + void dragEnterEvent(QDragEnterEvent *event) Q_DECL_OVERRIDE; + void dragMoveEvent(QDragMoveEvent* event) Q_DECL_OVERRIDE; + void dragLeaveEvent(QDragLeaveEvent *event) Q_DECL_OVERRIDE; + void dropEvent(QDropEvent* event) Q_DECL_OVERRIDE; + +protected Q_SLOTS: + /** + * \brief Connected to the internal palette object to keep eveything consistent + */ + void paletteModified(); + +private: + class Private; + Private* p; +}; + + +} // namespace color_widgets +#endif // COLOR_WIDGETS_SWATCH_HPP diff --git a/thirdparty/Qt-Color-Widgets/refactor.sh b/thirdparty/Qt-Color-Widgets/refactor.sh new file mode 100644 index 00000000..c44af522 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/refactor.sh @@ -0,0 +1,99 @@ +#!/bin/bash +# +# Copyright (C) 2013-2020 Mattia Basaglia +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +################################################################################ +# This script is to refactor the old class names to the new ones # +# eg: occurrences of Color_Dialog become color_widgets::ColorDialog # +# This script does very simple text replacements and overwrites existing files # +# Use with care # +# Usage: # +# ./refactor.sh /path/to/sources # +# # +################################################################################ + +old_classes=( + Color_Delegate + Color_Dialog + Color_List_Widget + Color_Preview + Color_Selector + Color_Wheel + Gradient_Slider + Hue_Slider +) +old_enums=( + Button_Mode + Display_Mode + Update_Mode + Display_Enum + Display_Flags +) +file_extensions=( + ui + cpp + hpp + C + H + h + cxx + hxx +) + + +function new_class_name() +{ + echo "$1" | sed -e 's/_//g' -r -e 's/^/color_widgets::/' +} + + +function new_enum_name() +{ + echo "$1" | sed -e 's/_//g' +} + +directory="$1" + +if [ -z "$directory" ] +then + echo "Usage: $0 (directory)" + exit 1 +fi + +find_extensions="" +for ext in ${file_extensions[@]} +do + find_extensions="$find_extensions -o -name '*.$ext'" +done +find_extensions="$(echo "$find_extensions" | sed -r 's/ -o //')" +find_command="find \""$directory"\" -type f -a \( $find_extensions \) -print" + +files="$(bash -c "$find_command")" + +replacements="" +for class in ${old_classes[@]} +do + replacements="$replacements $class $(new_class_name $class)" +done +for enum in ${old_enums[@]} +do + replacements="$replacements $enum $(new_enum_name $enum)" +done + +for file in $files +do + replace $replacements -- "$file" +done diff --git a/thirdparty/Qt-Color-Widgets/resources/QtColorWidgets/CMakeLists.txt b/thirdparty/Qt-Color-Widgets/resources/QtColorWidgets/CMakeLists.txt new file mode 100644 index 00000000..ee533e5e --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/resources/QtColorWidgets/CMakeLists.txt @@ -0,0 +1,16 @@ +set (SOURCES + alphaback.png + color_widgets.qrc + ) + +file(RELATIVE_PATH + PREFIX + ${PROJECT_SOURCE_DIR} + ${CMAKE_CURRENT_LIST_DIR}) + + +foreach (SOURCE IN LISTS SOURCES) + target_sources (${TARGET_NAME} + PRIVATE + $) +endforeach (SOURCE IN SOURCES) diff --git a/thirdparty/Qt-Color-Widgets/resources/QtColorWidgets/alphaback.png b/thirdparty/Qt-Color-Widgets/resources/QtColorWidgets/alphaback.png new file mode 100644 index 0000000000000000000000000000000000000000..b3634a5a323f098f52899b660757717cd3ff0e0e GIT binary patch literal 1079 zcmV-71jze|P) zaB^>EX>4U6ba`-PAZ2)IW&i+q+U1r}cH}4wMgO&mSpt#}g5_Xr&gmWW^5EK ztJ?XRF)?6)C7~<1Lfg+Do&LckAx4VkIhB+Gu2^D`ib)Q~Rrj7l-23?u&dEDDT?+z{ zV$|a?rzzhc7wnfQZ&Oc&+#VE_Z7wYD7wS|bc^Wt^PD9FvfiM+Qor+YZ<#;?V$FV{x z*&mP4=Kem{_b}k5(M5Ghme?cuD8W4y%rk=Mh}$|GRp0GjhCV*ixmDnkY|0wY=i#9q zcg3yfRmUFIggNhW(;0ox5(vx{h>H0+e4(T=HFXRuTX%FbRpdSdOYy730 z?M+UPNOx`zmQ%Vczmu0Y6Bju;@%6su-nZj)H6|Q6%|o3>*=N|djA-p|B{auvA&+2> zR?sp^q5Gt z?Eya9$7^s^5VSqa83(Lk85)3pF@8%f-9alh8pflJ*w?85A%X`6XIMcXBv#|Z!9~cK z5y%Wc6|Rd$S%8JjWQCIwRTJQ#3Sb-WIPY7<P>&^-Qfh_K3e6R7 zS8S+tkwurd*pe1s@=})CgqpV4tfl5Hx6wK|$BWHw9=eKYpfROJXM+4mxx?5wf28g^T5EqKN_T7Q znHas4S9G_=zKYRbXE)ugvBwzccl-eSM@j_$b`<&-oCd|(=|UU_00006VoOIv05Skj z0I-FB&7}YU010qNS#tmY3ljhU3ljkVnw%H_000McNlirue zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{001jVL_t(I%VSJSOZ(4&2N)Y0 x + + alphaback.png + + diff --git a/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/CMakeLists.txt b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/CMakeLists.txt new file mode 100644 index 00000000..f1a6c1ba --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/CMakeLists.txt @@ -0,0 +1,37 @@ +set (SOURCES +abstract_widget_list.cpp +bound_color_selector.cpp +color_2d_slider.cpp +color_delegate.cpp +color_dialog.cpp +color_dialog.ui +color_line_edit.cpp +color_list_widget.cpp +color_names.cpp +color_palette.cpp +color_palette_model.cpp +color_palette_widget.cpp +color_palette_widget.ui +color_preview.cpp +color_selector.cpp +color_utils.cpp +color_wheel.cpp +gradient_slider.cpp +hue_slider.cpp +swatch.cpp +gradient_editor.cpp +harmony_color_wheel.cpp +gradient_list_model.cpp +) + +file(RELATIVE_PATH + PREFIX + ${PROJECT_SOURCE_DIR} + ${CMAKE_CURRENT_LIST_DIR}) + + +foreach (SOURCE IN LISTS SOURCES) + target_sources (${TARGET_NAME} + PRIVATE + $) +endforeach (SOURCE IN SOURCES) diff --git a/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/abstract_widget_list.cpp b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/abstract_widget_list.cpp new file mode 100644 index 00000000..7512d0a5 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/abstract_widget_list.cpp @@ -0,0 +1,175 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#include "QtColorWidgets/abstract_widget_list.hpp" +#include +#include +#include +#include + +class AbstractWidgetList::Private +{ +public: + QList widgets; + QSignalMapper mapper_up; + QSignalMapper mapper_down; + QSignalMapper mapper_remove; + QTableWidget *table; +}; + +AbstractWidgetList::AbstractWidgetList(QWidget *parent) : + QWidget(parent), p(new Private) +{ + connect(&p->mapper_up,SIGNAL(mapped(QWidget*)),SLOT(up_clicked(QWidget*))); + connect(&p->mapper_down,SIGNAL(mapped(QWidget*)),SLOT(down_clicked(QWidget*))); + connect(&p->mapper_remove,SIGNAL(mapped(QWidget*)),SLOT(remove_clicked(QWidget*))); + + + QVBoxLayout *verticalLayout = new QVBoxLayout(this); + verticalLayout->setContentsMargins(0, 0, 0, 0); + p->table = new QTableWidget(this); + verticalLayout->addWidget(p->table); + + + p->table->insertColumn(0); + p->table->insertColumn(1); + p->table->insertColumn(2); + p->table->insertColumn(3); + + p->table->setColumnWidth(0,128); + p->table->setColumnWidth(1,24); + p->table->setColumnWidth(2,24); + p->table->setColumnWidth(3,24); + + p->table->horizontalHeader()->hide(); + p->table->verticalHeader()->hide(); + p->table->setShowGrid(false); + + QPushButton* add_button = new QPushButton(QIcon::fromTheme(QStringLiteral("list-add")), + tr("Add New")); + + verticalLayout->addWidget(add_button); + connect(add_button,&QAbstractButton::clicked,this, &AbstractWidgetList::append); + +} + +AbstractWidgetList::~AbstractWidgetList() +{ + delete p; +} + +int AbstractWidgetList::count() const +{ + return p->widgets.size(); +} + +void AbstractWidgetList::setRowHeight(int row, int height) +{ + p->table->setRowHeight(row,height); +} + +void AbstractWidgetList::clear() +{ + p->widgets.clear(); + while(p->table->rowCount() > 0) + p->table->removeRow(0); +} + + +void AbstractWidgetList::remove(int i) +{ + if ( isValidRow(i) ) + { + p->widgets.removeAt(i); + p->table->removeRow(i); + if ( i == 0 && !p->widgets.isEmpty() ) + p->table->cellWidget(0,1)->setEnabled(false); + else if ( i != 0 && i == count() ) + p->table->cellWidget(count()-1,2)->setEnabled(false); + + Q_EMIT removed(i); + } +} + + +void AbstractWidgetList::appendWidget(QWidget *w) +{ + int row = count(); + p->table->insertRow(row); + + QWidget* b_up = create_button(w,&p->mapper_up,QStringLiteral("go-up"),tr("Move Up")); + QWidget* b_down = create_button(w,&p->mapper_down,QStringLiteral("go-down"),tr("Move Down")); + QWidget* b_remove = create_button(w,&p->mapper_remove,QStringLiteral("list-remove"),tr("Remove")); + if ( row == 0 ) + b_up->setEnabled(false); + else + p->table->cellWidget(row-1,2)->setEnabled(true); + b_down->setEnabled(false); + + p->table->setCellWidget(row,0,w); + p->table->setCellWidget(row,1,b_up); + p->table->setCellWidget(row,2,b_down); + p->table->setCellWidget(row,3,b_remove); + + p->widgets.push_back(w); +} + +QWidget *AbstractWidgetList::widget(int i) +{ + if ( isValidRow(i) ) + return p->widgets[i]; + return 0; +} + + +QWidget *AbstractWidgetList::create_button(QWidget *data, QSignalMapper *mapper, + QString icon_name, + QString text, QString tooltip) const +{ + + QToolButton* btn = new QToolButton; + btn->setIcon(QIcon::fromTheme(icon_name)); + btn->setText(text); + btn->setToolTip(tooltip.isNull() ? btn->text() : tooltip ); + connect(btn,SIGNAL(clicked()),mapper,SLOT(map())); + mapper->setMapping(btn,data); + return btn; +} + +void AbstractWidgetList::remove_clicked(QWidget *w) +{ + int row = p->widgets.indexOf(w); + remove(row); +} + +void AbstractWidgetList::up_clicked(QWidget *w) +{ + int row = p->widgets.indexOf(w); + if ( row > 0 ) + swap(row,row-1); +} + +void AbstractWidgetList::down_clicked(QWidget *w) +{ + int row = p->widgets.indexOf(w); + if ( row+1 < count() ) + swap(row,row+1); +} diff --git a/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/bound_color_selector.cpp b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/bound_color_selector.cpp new file mode 100644 index 00000000..abb801d9 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/bound_color_selector.cpp @@ -0,0 +1,38 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#include "QtColorWidgets/bound_color_selector.hpp" + +namespace color_widgets { + +BoundColorSelector::BoundColorSelector(QColor* reference, QWidget *parent) : + ColorSelector(parent), ref(reference) +{ + setColor(*reference); + connect(this,&ColorPreview::colorChanged,this, &BoundColorSelector::update_reference); +} + +void BoundColorSelector::update_reference(QColor c) +{ + *ref = c; +} + +} // namespace color_widgets diff --git a/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_2d_slider.cpp b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_2d_slider.cpp new file mode 100644 index 00000000..892765fb --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_2d_slider.cpp @@ -0,0 +1,267 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#include "QtColorWidgets/color_2d_slider.hpp" +#include "QtColorWidgets/color_utils.hpp" +#include +#include +#include +#include + +namespace color_widgets { + +static const double selector_radius = 6; + +class Color2DSlider::Private +{ +public: + qreal hue = 1, sat = 1, val = 1; + Component comp_x = Saturation; + Component comp_y = Value; + QImage square; + + qreal PixHue(float x, float y) + { + if ( comp_x == Hue ) + return x; + if ( comp_y == Hue ) + return y; + return hue; + } + + qreal PixSat(float x, float y) + { + if ( comp_x == Saturation ) + return x; + if ( comp_y == Saturation ) + return y; + return sat; + } + + qreal PixVal(float x, float y) + { + if ( comp_x == Value ) + return x; + if ( comp_y == Value ) + return y; + return val; + } + + void renderSquare(const QSize& size) + { + square = QImage(size, QImage::Format_RGB32); + + for ( int y = 0; y < size.height(); ++y ) + { + qreal yfloat = 1 - qreal(y) / size.height(); + for ( int x = 0; x < size.width(); ++x ) + { + qreal xfloat = qreal(x) / size.width(); + square.setPixel( x, y, QColor::fromHsvF( + PixHue(xfloat, yfloat), + PixSat(xfloat, yfloat), + PixVal(xfloat, yfloat) + ).rgb()); + } + } + } + + QPointF selectorPos(const QSize& size) + { + QPointF pt; + switch ( comp_x ) + { + case Hue: pt.setX(size.width()*hue); break; + case Saturation:pt.setX(size.width()*sat); break; + case Value: pt.setX(size.width()*val); break; + } + switch ( comp_y ) + { + case Hue: pt.setY(size.height()*(1-hue)); break; + case Saturation:pt.setY(size.height()*(1-sat)); break; + case Value: pt.setY(size.height()*(1-val)); break; + } + return pt; + } + + void setColorFromPos(const QPoint& pt, const QSize& size) + { + QPointF ptfloat( + qBound(0.0, qreal(pt.x()) / size.width(), 1.0), + qBound(0.0, 1 - qreal(pt.y()) / size.height(), 1.0) + ); + switch ( comp_x ) + { + case Hue: hue = ptfloat.x(); break; + case Saturation:sat = ptfloat.x(); break; + case Value: val = ptfloat.x(); break; + } + switch ( comp_y ) + { + case Hue: hue = ptfloat.y(); break; + case Saturation:sat = ptfloat.y(); break; + case Value: val = ptfloat.y(); break; + } + } +}; + +Color2DSlider::Color2DSlider(QWidget* parent) + : QWidget(parent), p(new Private) +{ + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); +} + +Color2DSlider::~Color2DSlider() +{ + delete p; +} + +QColor Color2DSlider::color() const +{ + return QColor::fromHsvF(p->hue, p->sat, p->val); +} + +QSize Color2DSlider::sizeHint() const +{ + return {128, 128}; +} + +qreal Color2DSlider::hue() const +{ + return p->hue; +} + +qreal Color2DSlider::saturation() const +{ + return p->sat; +} + +qreal Color2DSlider::value() const +{ + return p->val; +} + +Color2DSlider::Component Color2DSlider::componentX() const +{ + return p->comp_x; +} + +Color2DSlider::Component Color2DSlider::componentY() const +{ + return p->comp_y; +} + +void Color2DSlider::setColor(const QColor& c) +{ + p->hue = c.hsvHueF(); + p->sat = c.saturationF(); + p->val = c.valueF(); + p->renderSquare(size()); + update(); + Q_EMIT colorChanged(color()); +} + +void Color2DSlider::setHue(qreal h) +{ + p->hue = h; + p->renderSquare(size()); + update(); + Q_EMIT colorChanged(color()); +} + +void Color2DSlider::setSaturation(qreal s) +{ + p->sat = s; + p->renderSquare(size()); + update(); + Q_EMIT colorChanged(color()); +} + +void Color2DSlider::setValue(qreal v) +{ + p->val = v; + p->renderSquare(size()); + update(); + Q_EMIT colorChanged(color()); +} + +void Color2DSlider::setComponentX(Color2DSlider::Component componentX) +{ + if ( componentX != p->comp_x ) + { + p->comp_x = componentX; + p->renderSquare(size()); + update(); + Q_EMIT componentXChanged(p->comp_x); + } +} + +void Color2DSlider::setComponentY(Color2DSlider::Component componentY) +{ + if ( componentY != p->comp_y ) + { + p->comp_y = componentY; + p->renderSquare(size()); + update(); + Q_EMIT componentXChanged(p->comp_y); + } +} + +void Color2DSlider::paintEvent(QPaintEvent*) +{ + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + painter.drawImage(0,0,p->square); + + painter.setPen(QPen(p->val > 0.5 ? Qt::black : Qt::white, 3)); + painter.setBrush(Qt::NoBrush); + painter.drawEllipse(p->selectorPos(size()), selector_radius, selector_radius); +} + +void Color2DSlider::mousePressEvent(QMouseEvent* event) +{ + p->setColorFromPos(event->pos(), size()); + Q_EMIT colorChanged(color()); + update(); +} + +void Color2DSlider::mouseMoveEvent(QMouseEvent* event) +{ + p->setColorFromPos(event->pos(), size()); + Q_EMIT colorChanged(color()); + update(); +} + +void Color2DSlider::mouseReleaseEvent(QMouseEvent* event) +{ + p->setColorFromPos(event->pos(), size()); + Q_EMIT colorChanged(color()); + update(); +} + +void Color2DSlider::resizeEvent(QResizeEvent* event) +{ + p->renderSquare(event->size()); + update(); +} + + +} // namespace color_widgets diff --git a/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_delegate.cpp b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_delegate.cpp new file mode 100644 index 00000000..1aa89b26 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_delegate.cpp @@ -0,0 +1,94 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#include "QtColorWidgets/color_delegate.hpp" +#include "QtColorWidgets/color_selector.hpp" +#include "QtColorWidgets/color_dialog.hpp" +#include +#include + +namespace color_widgets { + +ColorDelegate::ColorDelegate(QWidget *parent) : + QAbstractItemDelegate(parent) +{ +} + +void ColorDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + if (index.data().canConvert()) + { + QStyleOptionFrame panel; + panel.initFrom(option.widget); + if (option.widget->isEnabled()) + panel.state = QStyle::State_Enabled; + panel.rect = option.rect; + panel.lineWidth = 2; + panel.midLineWidth = 0; + panel.state |= QStyle::State_Sunken; + option.widget->style()->drawPrimitive(QStyle::PE_Frame, &panel, painter, nullptr); + QRect r = option.widget->style()->subElementRect(QStyle::SE_FrameContents, &panel, nullptr); + painter->setClipRect(r); + painter->fillRect(option.rect, index.data().value()); + } +} + +bool ColorDelegate::editorEvent(QEvent* event, + QAbstractItemModel* model, + const QStyleOptionViewItem& option, + const QModelIndex& index) +{ + + if ( event->type() == QEvent::MouseButtonRelease && index.data().canConvert()) + { + QMouseEvent* mouse_event = static_cast(event); + + if ( mouse_event->button() == Qt::LeftButton && + ( index.flags() & Qt::ItemIsEditable) ) + { + ColorDialog *editor = new ColorDialog(const_cast(option.widget)); + connect(this, &QObject::destroyed, editor, &QObject::deleteLater); + editor->setMinimumSize(editor->sizeHint()); + auto original_color = index.data().value(); + editor->setColor(original_color); + auto set_color = [model, index](const QColor& color){ + model->setData(index, QVariant(color)); + }; + connect(editor, &ColorDialog::colorSelected, this, set_color); + editor->show(); + } + + return true; + } + + return QAbstractItemDelegate::editorEvent(event, model, option, index); +} + + +QSize ColorDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + Q_UNUSED(index) + Q_UNUSED(option) + return QSize(24,16); +} + +} // namespace color_widgets diff --git a/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_dialog.cpp b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_dialog.cpp new file mode 100644 index 00000000..4ac1f685 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_dialog.cpp @@ -0,0 +1,378 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * \copyright Copyright (C) 2014 Calle Laakkonen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#include "QtColorWidgets/color_dialog.hpp" +#include "ui_color_dialog.h" + +#include +#include +#include +#include +#include +#include + +namespace color_widgets { + +class ColorDialog::Private +{ +public: + Ui_ColorDialog ui; + ButtonMode button_mode; + bool pick_from_screen; + bool alpha_enabled; + QColor color; + + Private() : pick_from_screen(false), alpha_enabled(true) + {} + +}; + +ColorDialog::ColorDialog(QWidget *parent, Qt::WindowFlags f) : + QDialog(parent, f), p(new Private) +{ + p->ui.setupUi(this); + + setAcceptDrops(true); + + // Add "pick color" button + QPushButton *pickButton = p->ui.buttonBox->addButton(tr("Pick"), QDialogButtonBox::ActionRole); + pickButton->setIcon(QIcon::fromTheme(QStringLiteral("color-picker"))); + + setButtonMode(OkApplyCancel); + + connect(p->ui.wheel, &ColorWheel::colorSpaceChanged, this, &ColorDialog::colorSpaceChanged); + connect(p->ui.wheel, &ColorWheel::selectorShapeChanged, this, &ColorDialog::wheelShapeChanged); + connect(p->ui.wheel, &ColorWheel::rotatingSelectorChanged, this, &ColorDialog::wheelRotatingChanged); +} + +ColorDialog::~ColorDialog() +{ + delete p; +} + +QSize ColorDialog::sizeHint() const +{ + return QSize(400,0); +} + +QColor ColorDialog::color() const +{ + QColor col = p->color; + if ( !p->alpha_enabled ) + col.setAlpha(255); + return col; +} + +void ColorDialog::setColor(const QColor &c) +{ + p->ui.preview->setComparisonColor(c); + p->ui.edit_hex->setModified(false); + setColorInternal(c); +} + +void ColorDialog::showColor(const QColor &c) +{ + setColor(c); + show(); +} + +void ColorDialog::setPreviewDisplayMode(ColorPreview::DisplayMode mode) +{ + p->ui.preview->setDisplayMode(mode); +} + +ColorPreview::DisplayMode ColorDialog::previewDisplayMode() const +{ + return p->ui.preview->displayMode(); +} + +void ColorDialog::setAlphaEnabled(bool a) +{ + if ( a != p->alpha_enabled ) + { + p->alpha_enabled = a; + + p->ui.edit_hex->setShowAlpha(a); + p->ui.line_alpha->setVisible(a); + p->ui.label_alpha->setVisible(a); + p->ui.slide_alpha->setVisible(a); + p->ui.spin_alpha->setVisible(a); + + Q_EMIT alphaEnabledChanged(a); + } +} + +bool ColorDialog::alphaEnabled() const +{ + return p->alpha_enabled; +} + +void ColorDialog::setButtonMode(ButtonMode mode) +{ + p->button_mode = mode; + QDialogButtonBox::StandardButtons btns; + switch(mode) { + case OkCancel: btns = QDialogButtonBox::Ok | QDialogButtonBox::Cancel; break; + case OkApplyCancel: btns = QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Apply | QDialogButtonBox::Reset; break; + case Close: btns = QDialogButtonBox::Close; + } + p->ui.buttonBox->setStandardButtons(btns); +} + +ColorDialog::ButtonMode ColorDialog::buttonMode() const +{ + return p->button_mode; +} + +void ColorDialog::setColorInternal(const QColor &col) +{ + /** + * \note Unlike setColor, this is used to update the current color which + * migth differ from the final selected color + */ + p->ui.wheel->setColor(col); + + p->color = col; + + bool blocked = signalsBlocked(); + blockSignals(true); + Q_FOREACH(QWidget* w, findChildren()) + w->blockSignals(true); + + + p->ui.slide_red->setValue(col.red()); + p->ui.spin_red->setValue(p->ui.slide_red->value()); + p->ui.slide_red->setFirstColor(QColor(0,col.green(),col.blue())); + p->ui.slide_red->setLastColor(QColor(255,col.green(),col.blue())); + + p->ui.slide_green->setValue(col.green()); + p->ui.spin_green->setValue(p->ui.slide_green->value()); + p->ui.slide_green->setFirstColor(QColor(col.red(),0,col.blue())); + p->ui.slide_green->setLastColor(QColor(col.red(),255,col.blue())); + + p->ui.slide_blue->setValue(col.blue()); + p->ui.spin_blue->setValue(p->ui.slide_blue->value()); + p->ui.slide_blue->setFirstColor(QColor(col.red(),col.green(),0)); + p->ui.slide_blue->setLastColor(QColor(col.red(),col.green(),255)); + + p->ui.slide_hue->setValue(qRound(p->ui.wheel->hue()*360.0)); + p->ui.slide_hue->setColorSaturation(p->ui.wheel->saturation()); + p->ui.slide_hue->setColorValue(p->ui.wheel->value()); + p->ui.spin_hue->setValue(p->ui.slide_hue->value()); + + p->ui.slide_saturation->setValue(qRound(p->ui.wheel->saturation()*255.0)); + p->ui.spin_saturation->setValue(p->ui.slide_saturation->value()); + p->ui.slide_saturation->setFirstColor(QColor::fromHsvF(p->ui.wheel->hue(),0,p->ui.wheel->value())); + p->ui.slide_saturation->setLastColor(QColor::fromHsvF(p->ui.wheel->hue(),1,p->ui.wheel->value())); + + p->ui.slide_value->setValue(qRound(p->ui.wheel->value()*255.0)); + p->ui.spin_value->setValue(p->ui.slide_value->value()); + p->ui.slide_value->setFirstColor(QColor::fromHsvF(p->ui.wheel->hue(), p->ui.wheel->saturation(),0)); + p->ui.slide_value->setLastColor(QColor::fromHsvF(p->ui.wheel->hue(), p->ui.wheel->saturation(),1)); + + + QColor apha_color = col; + apha_color.setAlpha(0); + p->ui.slide_alpha->setFirstColor(apha_color); + apha_color.setAlpha(255); + p->ui.slide_alpha->setLastColor(apha_color); + p->ui.spin_alpha->setValue(col.alpha()); + p->ui.slide_alpha->setValue(col.alpha()); + + if ( !p->ui.edit_hex->isModified() ) + p->ui.edit_hex->setColor(col); + + p->ui.preview->setColor(col); + + blockSignals(blocked); + Q_FOREACH(QWidget* w, findChildren()) + w->blockSignals(false); + + Q_EMIT colorChanged(col); +} + +void ColorDialog::set_hsv() +{ + if ( !signalsBlocked() ) + { + QColor col = QColor::fromHsv( + p->ui.slide_hue->value(), + p->ui.slide_saturation->value(), + p->ui.slide_value->value(), + p->ui.slide_alpha->value() + ); + p->ui.wheel->setColor(col); + setColorInternal(col); + } +} + +void ColorDialog::set_alpha() +{ + if ( !signalsBlocked() ) + { + QColor col = p->color; + col.setAlpha(p->ui.slide_alpha->value()); + setColorInternal(col); + } +} + +void ColorDialog::set_rgb() +{ + if ( !signalsBlocked() ) + { + QColor col( + p->ui.slide_red->value(), + p->ui.slide_green->value(), + p->ui.slide_blue->value(), + p->ui.slide_alpha->value() + ); + if (col.saturation() == 0) + col = QColor::fromHsv(p->ui.slide_hue->value(), 0, col.value()); + p->ui.wheel->setColor(col); + setColorInternal(col); + } +} + +void ColorDialog::on_edit_hex_colorChanged(const QColor& color) +{ + setColorInternal(color); +} + +void ColorDialog::on_edit_hex_colorEditingFinished(const QColor& color) +{ + p->ui.edit_hex->setModified(false); + setColorInternal(color); +} + +void ColorDialog::on_buttonBox_clicked(QAbstractButton *btn) +{ + QDialogButtonBox::ButtonRole role = p->ui.buttonBox->buttonRole(btn); + + switch(role) { + case QDialogButtonBox::AcceptRole: + case QDialogButtonBox::ApplyRole: + // Explicitly select the color + p->ui.preview->setComparisonColor(color()); + Q_EMIT colorSelected(color()); + break; + + case QDialogButtonBox::ActionRole: + // Currently, the only action button is the "pick color" button + grabMouse(Qt::CrossCursor); + p->pick_from_screen = true; + break; + + case QDialogButtonBox::ResetRole: + // Restore old color + setColorInternal(p->ui.preview->comparisonColor()); + break; + + default: break; + } +} + +void ColorDialog::dragEnterEvent(QDragEnterEvent *event) +{ + if ( event->mimeData()->hasColor() || + ( event->mimeData()->hasText() && QColor(event->mimeData()->text()).isValid() ) ) + event->acceptProposedAction(); +} + + +void ColorDialog::dropEvent(QDropEvent *event) +{ + if ( event->mimeData()->hasColor() ) + { + setColorInternal(event->mimeData()->colorData().value()); + event->accept(); + } + else if ( event->mimeData()->hasText() ) + { + QColor col(event->mimeData()->text()); + if ( col.isValid() ) + { + setColorInternal(col); + event->accept(); + } + } +} + +static QColor get_screen_color(const QPoint &global_pos) +{ + int screenNum = QApplication::desktop()->screenNumber(global_pos); + QScreen *screen = QApplication::screens().at(screenNum); + + WId wid = QApplication::desktop()->winId(); + QImage img = screen->grabWindow(wid, global_pos.x(), global_pos.y(), 1, 1).toImage(); + + return img.pixel(0,0); +} + +void ColorDialog::mouseReleaseEvent(QMouseEvent *event) +{ + if (p->pick_from_screen) + { + setColorInternal(get_screen_color(event->globalPos())); + p->pick_from_screen = false; + releaseMouse(); + } +} + +void ColorDialog::mouseMoveEvent(QMouseEvent *event) +{ + if (p->pick_from_screen) + { + setColorInternal(get_screen_color(event->globalPos())); + } +} + +void ColorDialog::setWheelShape(ColorWheel::ShapeEnum shape) +{ + p->ui.wheel->setSelectorShape(shape); +} + +ColorWheel::ShapeEnum ColorDialog::wheelShape() const +{ + return p->ui.wheel->selectorShape(); +} + +void ColorDialog::setColorSpace(ColorWheel::ColorSpaceEnum space) +{ + p->ui.wheel->setColorSpace(space); +} + +ColorWheel::ColorSpaceEnum ColorDialog::colorSpace() const +{ + return p->ui.wheel->colorSpace(); +} + +void ColorDialog::setWheelRotating(bool rotating) +{ + p->ui.wheel->setRotatingSelector(rotating); +} + +bool ColorDialog::wheelRotating() const +{ + return p->ui.wheel->rotatingSelector(); +} + +} // namespace color_widgets diff --git a/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_dialog.ui b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_dialog.ui new file mode 100644 index 00000000..bb1bd3a7 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_dialog.ui @@ -0,0 +1,700 @@ + + + ColorDialog + + + + 0 + 0 + 491 + 380 + + + + Select Color + + + + + + + + + + + + + + + + 0 + 0 + + + + + + + + color_widgets::ColorPreview::SplitColor + + + + + + + + + + + 255 + + + Qt::Horizontal + + + + + + + Saturation + + + + + + + Hue + + + + + + + 255 + + + Qt::Horizontal + + + + + + + Hex + + + + + + + Blue + + + + + + + 255 + + + Qt::Horizontal + + + + + + + 255 + + + Qt::Horizontal + + + + + + + 255 + + + Qt::Horizontal + + + + + + + Value + + + + + + + Green + + + + + + + Alpha + + + + + + + Red + + + + + + + 255 + + + Qt::Horizontal + + + + + + + true + + + 359 + + + + + + + 255 + + + + + + + 255 + + + + + + + 255 + + + + + + + 255 + + + + + + + 255 + + + + + + + 255 + + + + + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + + + + + 0 + + + 359 + + + + + + + + Monospace + + + + true + + + + + + + + + + + QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Reset + + + + + + + + + color_widgets::ColorPreview + QWidget +

QtColorWidgets/color_preview.hpp
+ 1 + + + color_widgets::ColorWheel + QWidget +
QtColorWidgets/color_wheel.hpp
+ 1 + + colorSelected(QColor) + setColor(QColor) + +
+ + color_widgets::GradientSlider + QSlider +
QtColorWidgets/gradient_slider.hpp
+
+ + color_widgets::HueSlider + color_widgets::GradientSlider +
QtColorWidgets/hue_slider.hpp
+
+ + color_widgets::ColorLineEdit + QLineEdit +
QtColorWidgets/color_line_edit.hpp
+
+ + + + + slide_saturation + valueChanged(int) + ColorDialog + set_hsv() + + + 416 + 71 + + + 537 + 54 + + + + + slide_value + valueChanged(int) + ColorDialog + set_hsv() + + + 416 + 109 + + + 537 + 88 + + + + + slide_red + valueChanged(int) + ColorDialog + set_rgb() + + + 416 + 156 + + + 557 + 142 + + + + + slide_green + valueChanged(int) + ColorDialog + set_rgb() + + + 416 + 194 + + + 538 + 166 + + + + + slide_blue + valueChanged(int) + ColorDialog + set_rgb() + + + 416 + 232 + + + 537 + 205 + + + + + slide_alpha + valueChanged(int) + ColorDialog + set_alpha() + + + 416 + 279 + + + 531 + 251 + + + + + wheel + colorSelected(QColor) + ColorDialog + setColorInternal(QColor) + + + 175 + 101 + + + 568 + 106 + + + + + slide_saturation + valueChanged(int) + spin_saturation + setValue(int) + + + 416 + 71 + + + 480 + 62 + + + + + spin_saturation + valueChanged(int) + slide_saturation + setValue(int) + + + 461 + 55 + + + 416 + 71 + + + + + slide_value + valueChanged(int) + spin_value + setValue(int) + + + 416 + 109 + + + 480 + 91 + + + + + spin_value + valueChanged(int) + slide_value + setValue(int) + + + 480 + 91 + + + 416 + 109 + + + + + slide_red + valueChanged(int) + spin_red + setValue(int) + + + 416 + 156 + + + 482 + 162 + + + + + spin_red + valueChanged(int) + slide_red + setValue(int) + + + 482 + 162 + + + 416 + 156 + + + + + slide_green + valueChanged(int) + spin_green + setValue(int) + + + 416 + 194 + + + 482 + 200 + + + + + spin_green + valueChanged(int) + slide_green + setValue(int) + + + 482 + 200 + + + 416 + 194 + + + + + slide_alpha + valueChanged(int) + spin_alpha + setValue(int) + + + 416 + 279 + + + 482 + 285 + + + + + spin_alpha + valueChanged(int) + slide_alpha + setValue(int) + + + 482 + 285 + + + 416 + 279 + + + + + slide_blue + valueChanged(int) + spin_blue + setValue(int) + + + 416 + 232 + + + 482 + 238 + + + + + spin_blue + valueChanged(int) + slide_blue + setValue(int) + + + 482 + 238 + + + 416 + 232 + + + + + slide_hue + valueChanged(int) + spin_hue + setValue(int) + + + 405 + 20 + + + 462 + 26 + + + + + spin_hue + valueChanged(int) + slide_hue + setValue(int) + + + 448 + 18 + + + 388 + 24 + + + + + slide_hue + valueChanged(int) + ColorDialog + set_hsv() + + + 361 + 17 + + + 363 + 8 + + + + + buttonBox + accepted() + ColorDialog + accept() + + + 250 + 373 + + + 430 + 267 + + + + + buttonBox + rejected() + ColorDialog + reject() + + + 183 + 373 + + + 294 + 323 + + + + + + colorChanged(QColor) + set_rgb() + set_hsv() + setColor(QColor) + setColorInternal(QColor) + set_alpha() + + diff --git a/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_line_edit.cpp b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_line_edit.cpp new file mode 100644 index 00000000..7172253a --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_line_edit.cpp @@ -0,0 +1,211 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * \copyright Copyright (C) 2017 caryoscelus + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#include "QtColorWidgets/color_line_edit.hpp" + +#include +#include +#include +#include +#include +#include + +#include "QtColorWidgets/color_utils.hpp" +#include "QtColorWidgets/color_names.hpp" + +namespace color_widgets { + + +class ColorLineEdit::Private +{ +public: + QColor color; + bool show_alpha = false; + bool preview_color = false; + QBrush background; + + bool customAlpha() + { + return preview_color && show_alpha && color.alpha() < 255; + } + + void setPalette(const QColor& color, ColorLineEdit* parent) + { + if ( preview_color ) + { + QColor bg = customAlpha() ? Qt::transparent : color; + QColor text = detail::color_lumaF(color) > 0.5 || color.alphaF() < 0.2 ? Qt::black : Qt::white; + parent->setStyleSheet( + QString("background-color: %1; color: %2;") + .arg(bg.name()).arg(text.name()) + ); + } + } +}; + +ColorLineEdit::ColorLineEdit(QWidget* parent) + : QLineEdit(parent), p(new Private) +{ + p->background.setTexture(QPixmap(QStringLiteral(":/color_widgets/alphaback.png"))); + setColor(Qt::white); + /// \todo determine if having this connection might be useful + /*connect(this, &QLineEdit::textChanged, [this](const QString& text){ + QColor color = p->colorFromString(text); + if ( color.isValid() ) + Q_EMIT colorChanged(color); + });*/ + connect(this, &QLineEdit::textEdited, [this](const QString& text){ + QColor color = color_widgets::colorFromString(text, p->show_alpha); + if ( color.isValid() ) + { + p->color = color; + p->setPalette(color, this); + Q_EMIT colorEdited(color); + Q_EMIT colorChanged(color); + } + }); + connect(this, &QLineEdit::editingFinished, [this](){ + QColor color = color_widgets::colorFromString(text(), p->show_alpha); + if ( color.isValid() ) + { + p->color = color; + Q_EMIT colorEditingFinished(color); + Q_EMIT colorChanged(color); + } + else + { + setText(color_widgets::stringFromColor(p->color, p->show_alpha)); + Q_EMIT colorEditingFinished(p->color); + Q_EMIT colorChanged(color); + } + p->setPalette(p->color, this); + }); +} + +ColorLineEdit::~ColorLineEdit() +{ + delete p; +} + +QColor ColorLineEdit::color() const +{ + return p->color; +} + +void ColorLineEdit::setColor(const QColor& color) +{ + if ( color != p->color ) + { + p->color = color; + p->setPalette(p->color, this); + setText(color_widgets::stringFromColor(p->color, p->show_alpha)); + Q_EMIT colorChanged(p->color); + } +} + +void ColorLineEdit::setShowAlpha(bool showAlpha) +{ + if ( p->show_alpha != showAlpha ) + { + p->show_alpha = showAlpha; + p->setPalette(p->color, this); + setText(color_widgets::stringFromColor(p->color, p->show_alpha)); + Q_EMIT showAlphaChanged(p->show_alpha); + } +} + +bool ColorLineEdit::showAlpha() const +{ + return p->show_alpha; +} + +void ColorLineEdit::dragEnterEvent(QDragEnterEvent *event) +{ + if ( isReadOnly() ) + return; + + if ( event->mimeData()->hasColor() || + ( event->mimeData()->hasText() && + color_widgets::colorFromString(event->mimeData()->text(), p->show_alpha).isValid() ) ) + { + event->acceptProposedAction(); + } +} + + +void ColorLineEdit::dropEvent(QDropEvent *event) +{ + if ( isReadOnly() ) + return; + + if ( event->mimeData()->hasColor() ) + { + setColor(event->mimeData()->colorData().value()); + event->accept(); + } + else if ( event->mimeData()->hasText() ) + { + QColor col = color_widgets::colorFromString(event->mimeData()->text(), p->show_alpha); + if ( col.isValid() ) + { + setColor(col); + event->accept(); + } + } +} + +bool ColorLineEdit::previewColor() const +{ + return p->preview_color; +} + +void ColorLineEdit::setPreviewColor(bool previewColor) +{ + if ( previewColor != p->preview_color ) + { + p->preview_color = previewColor; + + if ( p->preview_color ) + p->setPalette(p->color, this); + else + setPalette(QApplication::palette()); + + Q_EMIT previewColorChanged(p->preview_color); + } +} + +void ColorLineEdit::paintEvent(QPaintEvent* event) +{ + if ( p->customAlpha() ) + { + QPainter painter(this); + QStyleOptionFrame panel; + initStyleOption(&panel); + QRect r = style()->subElementRect(QStyle::SE_LineEditContents, &panel, nullptr); + painter.fillRect(r, p->background); + painter.fillRect(r, p->color); + } + + QLineEdit::paintEvent(event); +} + +} // namespace color_widgets diff --git a/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_list_widget.cpp b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_list_widget.cpp new file mode 100644 index 00000000..79f88df1 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_list_widget.cpp @@ -0,0 +1,149 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#include "QtColorWidgets/color_list_widget.hpp" +#include "QtColorWidgets/color_selector.hpp" + +namespace color_widgets { + +class ColorListWidget::Private +{ +public: + QList colors; + QSignalMapper mapper; + ColorWheel::ShapeEnum wheel_shape = ColorWheel::ShapeTriangle; + ColorWheel::ColorSpaceEnum color_space = ColorWheel::ColorHSV; + bool wheel_rotating = true; +}; + +ColorListWidget::ColorListWidget(QWidget *parent) + : AbstractWidgetList(parent), p(new Private) +{ + connect(this, &AbstractWidgetList::removed, this, &ColorListWidget::handle_removed); + connect(&p->mapper, SIGNAL(mapped(int)), SLOT(color_changed(int))); +} + +ColorListWidget::~ColorListWidget() +{ + delete p; +} + +QList ColorListWidget::colors() const +{ + return p->colors; +} + +void ColorListWidget::setColors(const QList &colors) +{ + clear(); + p->colors = colors; + for(int i = 0;i < colors.size();i++ ) + append_widget(i); + Q_EMIT colorsChanged(colors); +} + +void ColorListWidget::swap(int a, int b) +{ + ColorSelector* sa = widget_cast(a); + ColorSelector* sb = widget_cast(b); + if ( sa && sb ) + { + QColor ca = sa->color(); + sa->setColor(sb->color()); + sb->setColor(ca); + Q_EMIT colorsChanged(p->colors); + } +} + +void ColorListWidget::append() +{ + p->colors.push_back(Qt::black); + append_widget(p->colors.size()-1); + Q_EMIT colorsChanged(p->colors); +} + +void ColorListWidget::emit_changed() +{ + Q_EMIT colorsChanged(p->colors); +} + +void ColorListWidget::handle_removed(int i) +{ + p->colors.removeAt(i); + Q_EMIT colorsChanged(p->colors); +} + +void ColorListWidget::color_changed(int row) +{ + ColorSelector *cs = widget_cast(row); + if ( cs ) + { + p->colors[row] = cs->color(); + Q_EMIT colorsChanged(p->colors); + } +} + +void ColorListWidget::append_widget(int col) +{ + ColorSelector* cbs = new ColorSelector; + cbs->setDisplayMode(ColorPreview::AllAlpha); + cbs->setColor(p->colors[col]); + //connect(cbs,SIGNAL(colorChanged(QColor)),SLOT(emit_changed())); + p->mapper.setMapping(cbs,col); + connect(cbs,SIGNAL(colorChanged(QColor)),&p->mapper,SLOT(map())); + connect(this, &ColorListWidget::wheelRotatingChanged, cbs, &ColorSelector::setWheelRotating); + connect(this, &ColorListWidget::wheelShapeChanged, cbs, &ColorSelector::setWheelShape); + connect(this, &ColorListWidget::colorSpaceChanged, cbs, &ColorSelector::setColorSpace); + appendWidget(cbs); + setRowHeight(count()-1,22); +} + +void ColorListWidget::setWheelShape(ColorWheel::ShapeEnum shape) +{ + Q_EMIT wheelShapeChanged(p->wheel_shape = shape); +} + +ColorWheel::ShapeEnum ColorListWidget::wheelShape() const +{ + return p->wheel_shape; +} + +void ColorListWidget::setColorSpace(ColorWheel::ColorSpaceEnum space) +{ + Q_EMIT colorSpaceChanged(p->color_space = space); +} + +ColorWheel::ColorSpaceEnum ColorListWidget::colorSpace() const +{ + return p->color_space; +} + +void ColorListWidget::setWheelRotating(bool rotating) +{ + Q_EMIT wheelRotatingChanged(p->wheel_rotating = rotating); +} + +bool ColorListWidget::wheelRotating() const +{ + return p->wheel_rotating; +} + +} // namespace color_widgets diff --git a/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_names.cpp b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_names.cpp new file mode 100644 index 00000000..07d7d225 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_names.cpp @@ -0,0 +1,89 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#include "QtColorWidgets/color_names.hpp" +#include + +static QRegularExpression regex_qcolor (QStringLiteral("^(?:(?:#[[:xdigit:]]{3})|(?:#[[:xdigit:]]{6})|(?:[[:alpha:]]+))$")); +static QRegularExpression regex_func_rgb (QStringLiteral(R"(^rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)$)")); +static QRegularExpression regex_hex_rgba (QStringLiteral("^#[[:xdigit:]]{8}$")); +static QRegularExpression regex_func_rgba (QStringLiteral(R"(^rgba?\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)$)")); + +namespace color_widgets { + + +QString stringFromColor(const QColor& color, bool alpha) +{ + if ( !alpha || color.alpha() == 255 ) + return color.name(); + return color.name()+QStringLiteral("%1").arg(color.alpha(), 2, 16, QChar('0')); +} + +QColor colorFromString(const QString& string, bool alpha) +{ + QString xs = string.trimmed(); + QRegularExpressionMatch match; + + match = regex_qcolor.match(xs); + if ( match.hasMatch() ) + { + return QColor(xs); + } + + match = regex_func_rgb.match(xs); + if ( match.hasMatch() ) + { + return QColor( + match.captured(1).toInt(), + match.captured(2).toInt(), + match.captured(3).toInt() + ); + } + + if ( alpha ) + { + match = regex_hex_rgba.match(xs); + if ( match.hasMatch() ) + { + return QColor( + xs.mid(1,2).toInt(nullptr,16), + xs.mid(3,2).toInt(nullptr,16), + xs.mid(5,2).toInt(nullptr,16), + xs.mid(7,2).toInt(nullptr,16) + ); + } + + match = regex_func_rgba.match(xs); + if ( match.hasMatch() ) + { + return QColor( + match.captured(1).toInt(), + match.captured(2).toInt(), + match.captured(3).toInt(), + match.captured(4).toInt() + ); + } + } + + return QColor(); +} + +} // namespace color_widgets diff --git a/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_palette.cpp b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_palette.cpp new file mode 100644 index 00000000..dbb9605e --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_palette.cpp @@ -0,0 +1,498 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#include "QtColorWidgets/color_palette.hpp" +#include +#include +#include +#include +#include +#include + +namespace color_widgets { + +class ColorPalette::Private +{ +public: + QVector > colors; + int columns; + QString name; + QString fileName; + bool dirty; + + bool valid_index(int index) + { + return index >= 0 && index < colors.size(); + } +}; + +ColorPalette::ColorPalette(const QVector& colors, + const QString& name, + int columns) + : p ( new Private ) +{ + setName(name); + setColumns(columns); + setColors(colors); +} + +ColorPalette::ColorPalette(const QString& name) + : p ( new Private ) +{ + setName(name); + p->columns = 0; + p->dirty = false; +} + +ColorPalette::ColorPalette(const QVector >& colors, + const QString& name, + int columns) +{ + setName(name); + setColumns(columns); + setColors(colors); + p->dirty = false; +} + +ColorPalette::ColorPalette(const ColorPalette& other) + : QObject(), p ( new Private(*other.p) ) +{ +} + +ColorPalette& ColorPalette::operator=(const ColorPalette& other) +{ + *p = *other.p; + emitUpdate(); + return *this; +} + +ColorPalette::~ColorPalette() +{ + delete p; +} + +ColorPalette::ColorPalette(ColorPalette&& other) + : QObject(), p ( other.p ) +{ + other.p = nullptr; +} +ColorPalette& ColorPalette::operator=(ColorPalette&& other) +{ + std::swap(p, other.p); + emitUpdate(); + return *this; +} + +void ColorPalette::emitUpdate() +{ + Q_EMIT colorsChanged(p->colors); + Q_EMIT columnsChanged(p->columns); + Q_EMIT nameChanged(p->name); + Q_EMIT fileNameChanged(p->fileName); + Q_EMIT dirtyChanged(p->dirty); +} + +QColor ColorPalette::colorAt(int index) const +{ + return p->valid_index(index) ? p->colors[index].first : QColor(); +} + +QString ColorPalette::nameAt(int index) const +{ + return p->valid_index(index) ? p->colors[index].second : QString(); +} + +QVector > ColorPalette::colors() const +{ + return p->colors; +} + +int ColorPalette::count() const +{ + return p->colors.size(); +} + +int ColorPalette::columns() +{ + return p->columns; +} + +QString ColorPalette::name() const +{ + return p->name; +} + +void ColorPalette::loadColorTable(const QVector& color_table) +{ + p->colors.clear(); + p->colors.reserve(color_table.size()); + for ( QRgb c : color_table ) + { + QColor color ( c ); + color.setAlpha(255); + p->colors.push_back(qMakePair(color,QString())); + } + Q_EMIT colorsChanged(p->colors); + setDirty(true); +} + +bool ColorPalette::loadImage(const QImage& image) +{ + if ( image.isNull() ) + return false; + setColumns(image.width()); + + p->colors.clear(); + p->colors.reserve(image.width()*image.height()); + for ( int y = 0; y < image.height(); y++ ) + { + for ( int x = 0; x < image.width(); x++ ) + { + QColor color ( image.pixel(x, y) ); + color.setAlpha(255); + p->colors.push_back(qMakePair(color,QString())); + } + } + Q_EMIT colorsChanged(p->colors); + setDirty(true); + return true; +} + +ColorPalette ColorPalette::fromImage(const QImage& image) +{ + ColorPalette p; + p.loadImage(image); + return p; +} + +bool ColorPalette::load(const QString& name) +{ + p->fileName = name; + p->colors.clear(); + p->columns = 0; + p->dirty = false; + p->name = QFileInfo(name).baseName(); + + QFile file(name); + + if ( !file.open(QFile::ReadOnly|QFile::Text) ) + { + emitUpdate(); + return false; + } + + QTextStream stream( &file ); + + if ( stream.readLine() != QLatin1String("GIMP Palette") ) + { + emitUpdate(); + return false; + } + + QString line; + + // parse properties + QHash properties; + while( !stream.atEnd() ) + { + line = stream.readLine(); + if ( line.isEmpty() ) + continue; + if ( line[0] == '#' ) + break; + int colon = line.indexOf(':'); + if ( colon == -1 ) + break; + properties[line.left(colon).toLower()] = + line.right(line.size() - colon - 1).trimmed(); + } + /// \todo Store extra properties in the palette object + setName(properties[QStringLiteral("name")]); + setColumns(properties[QStringLiteral("columns")].toInt()); + + // Skip comments + if ( !stream.atEnd() && line[0] == '#' ) + while( !stream.atEnd() ) + { + qint64 pos = stream.pos(); + line = stream.readLine(); + if ( !line.isEmpty() && line[0] != '#' ) + { + stream.seek(pos); + break; + } + } + + while( !stream.atEnd() ) + { + int r = 0, g = 0, b = 0; + stream >> r >> g >> b; + line = stream.readLine().trimmed(); + p->colors.push_back(qMakePair(QColor(r, g, b), line)); + } + + Q_EMIT colorsChanged(p->colors); + setDirty(false); + + return true; +} + +ColorPalette ColorPalette::fromFile(const QString& name) +{ + ColorPalette p; + p.load(name); + return p; +} + +bool ColorPalette::save(const QString& filename) +{ + setFileName(filename); + return save(); +} + +bool ColorPalette::save() +{ + QString filename = p->fileName; + if ( filename.isEmpty() ) + { + filename = unnamed(p->name)+".gpl"; + } + + QFile file(filename); + if ( !file.open(QFile::Text|QFile::WriteOnly) ) + return false; + + QTextStream stream(&file); + + stream << "GIMP Palette\n"; + stream << "Name: " << unnamed(p->name) << '\n'; + if ( p->columns ) + stream << "Columns: " << p->columns << '\n'; + /// \todo Options to add comments + stream << "#\n"; + + for ( int i = 0; i < p->colors.size(); i++ ) + { + stream << qSetFieldWidth(3) << p->colors[i].first.red() << qSetFieldWidth(0) << ' ' + << qSetFieldWidth(3) << p->colors[i].first.green() << qSetFieldWidth(0) << ' ' + << qSetFieldWidth(3) << p->colors[i].first.blue() << qSetFieldWidth(0) << '\t' + << unnamed(p->colors[i].second) << '\n'; + } + + if ( !file.error() ) + { + setDirty(false); + return true; + } + + return false; +} + + +QString ColorPalette::fileName() const +{ + return p->fileName; +} + + +void ColorPalette::setColumns(int columns) +{ + if ( columns <= 0 ) + columns = 0; + + if ( columns != p->columns ) + { + setDirty(true); + Q_EMIT columnsChanged( p->columns = columns ); + } +} + +void ColorPalette::setColors(const QVector& colors) +{ + p->colors.clear(); + Q_FOREACH(const QColor& col, colors) + p->colors.push_back(qMakePair(col,QString())); + setDirty(true); + Q_EMIT colorsChanged(p->colors); +} + +void ColorPalette::setColors(const QVector >& colors) +{ + p->colors = colors; + setDirty(true); + Q_EMIT colorsChanged(p->colors); +} + + +void ColorPalette::setColorAt(int index, const QColor& color) +{ + if ( !p->valid_index(index) ) + return; + + p->colors[index].first = color; + + setDirty(true); + Q_EMIT colorChanged(index); + Q_EMIT colorsUpdated(p->colors); +} + +void ColorPalette::setColorAt(int index, const QColor& color, const QString& name) +{ + if ( !p->valid_index(index) ) + return; + + p->colors[index].first = color; + p->colors[index].second = name; + setDirty(true); + Q_EMIT colorChanged(index); + Q_EMIT colorsUpdated(p->colors); +} + +void ColorPalette::setNameAt(int index, const QString& name) +{ + if ( !p->valid_index(index) ) + return; + + p->colors[index].second = name; + + setDirty(true); + Q_EMIT colorChanged(index); + Q_EMIT colorsUpdated(p->colors); +} + + +void ColorPalette::appendColor(const QColor& color, const QString& name) +{ + p->colors.push_back(qMakePair(color,name)); + setDirty(true); + Q_EMIT colorAdded(p->colors.size()-1); + Q_EMIT colorsUpdated(p->colors); +} + +void ColorPalette::insertColor(int index, const QColor& color, const QString& name) +{ + if ( index < 0 || index > p->colors.size() ) + return; + + p->colors.insert(index, qMakePair(color, name)); + + setDirty(true); + Q_EMIT colorAdded(index); + Q_EMIT colorsUpdated(p->colors); +} + +void ColorPalette::eraseColor(int index) +{ + if ( !p->valid_index(index) ) + return; + + p->colors.remove(index); + + setDirty(true); + Q_EMIT colorRemoved(index); + Q_EMIT colorsUpdated(p->colors); +} + +void ColorPalette::setName(const QString& name) +{ + setDirty(true); + p->name = name; +} + +void ColorPalette::setFileName(const QString& name) +{ + setDirty(true); + p->fileName = name; +} + +QString ColorPalette::unnamed(const QString& name) const +{ + return name.isEmpty() ? tr("Unnamed") : name; +} + + +QPixmap ColorPalette::preview(const QSize& size, const QColor& background) const +{ + if ( !size.isValid() || p->colors.empty() ) + return QPixmap(); + + QPixmap out( size ); + out.fill(background); + QPainter painter(&out); + + int count = p->colors.size(); + int columns = p->columns; + if ( !columns ) + columns = std::ceil( std::sqrt( count * float(size.width()) / size.height() ) ); + int rows = std::ceil( float(count) / columns ); + QSizeF color_size(float(size.width()) / columns, float(size.height()) / rows); + + for ( int y = 0, i = 0; y < rows && i < count; y++ ) + { + for ( int x = 0; x < columns && i < count; x++, i++ ) + { + painter.fillRect(QRectF(x*color_size.width(), y*color_size.height(), + color_size.width(), color_size.height()), + p->colors[i].first + ); + } + } + + return out; +} + +bool ColorPalette::dirty() const +{ + return p->dirty; +} + +void ColorPalette::setDirty(bool dirty) +{ + if ( dirty != p->dirty ) + Q_EMIT dirtyChanged( p->dirty = dirty ); +} + +QVector ColorPalette::onlyColors() const +{ + QVector out; + out.reserve(p->colors.size()); + for ( int i = 0; i < p->colors.size(); i++ ) + out.push_back(p->colors[i].first); + return out; +} + +QVector ColorPalette::colorTable() const +{ + QVector out; + out.reserve(p->colors.size()); + for ( const auto& color_pair : p->colors ) + out.push_back(color_pair.first.rgba()); + return out; +} + +ColorPalette ColorPalette::fromColorTable(const QVector& table) +{ + ColorPalette palette; + palette.loadColorTable(table); + return palette; +} + +} // namespace color_widgets diff --git a/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_palette_model.cpp b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_palette_model.cpp new file mode 100644 index 00000000..59b1cd87 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_palette_model.cpp @@ -0,0 +1,329 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#include "QtColorWidgets/color_palette_model.hpp" +#include +#include +#include + +namespace color_widgets { + +class ColorPaletteModel::Private +{ +public: + /// \todo Keep sorted by name (?) + QList palettes; + QSize icon_size; + QStringList search_paths; + QString save_path; + + Private() + : icon_size(32, 32) + {} + + bool acceptable(const QModelIndex& index) const + { + return acceptable(index.row()); + } + + bool acceptable(int row) const + { + return row >= 0 && row <= palettes.count(); + } + + QList::iterator find(const QString& name) + { + return std::find_if(palettes.begin(), palettes.end(), + [&name](const ColorPalette& palette) { + return palette.name() == name; + }); + } + + bool attemptSave(ColorPalette& palette, const QString& filename) + { + if ( filename.isEmpty() ) + return false; + return palette.save(filename); + } + + void fixUnnamed(ColorPalette& palette) + { + if ( palette.name().isEmpty() ) + palette.setName(ColorPaletteModel::tr("Unnamed")); + } + + bool save(ColorPalette& palette, const QString& suggested_filename = QString()) + { + // Attempt to save with the existing file names + if ( !suggested_filename.isEmpty() && attemptSave(palette, suggested_filename) ) + return true; + if ( attemptSave(palette, palette.fileName()) ) + return true; + + // Set up the save directory + QDir save_dir(save_path); + if ( !save_dir.exists() && !QDir().mkdir(save_path) ) + return false; + + // Attempt to save as (Name).gpl + QString filename = palette.name()+".gpl"; + if ( !save_dir.exists(filename) && + attemptSave(palette, save_dir.absoluteFilePath(filename)) ) + return true; + + // Get all of the files matching the pattern *.gpl + save_dir.setNameFilters(QStringList() << QStringLiteral("*.gpl")); + save_dir.setFilter(QDir::Files); + QStringList existing_files = save_dir.entryList(); + + // For all the files that match (Name)(Number).gpl, find the maximum (Number) + QRegularExpression name_regex(QRegularExpression::escape(palette.name())+"([0-9]+)\\.gpl"); + int max = 0; + for ( const auto& existing_file : existing_files ) + { + QRegularExpressionMatch match = name_regex.match(existing_file); + if ( match.hasMatch() ) + { + int num = match.captured(1).toInt(); + if ( num > max ) + max = num; + } + } + + return attemptSave(palette, + save_dir.absoluteFilePath(QStringLiteral("%1%2.gpl").arg(palette.name()).arg(max+1)) + ); + } +}; + +ColorPaletteModel::ColorPaletteModel() + : p ( new Private ) + {} + +ColorPaletteModel::~ColorPaletteModel() +{ + delete p; +} + +int ColorPaletteModel::rowCount(const QModelIndex &) const +{ + return count(); +} + +QVariant ColorPaletteModel::data(const QModelIndex &index, int role) const +{ + if ( !p->acceptable(index) ) + return QVariant(); + + const ColorPalette& palette = p->palettes[index.row()]; + switch( role ) + { + case Qt::DisplayRole: + return palette.name(); + case Qt::DecorationRole: + return palette.preview(p->icon_size); + case Qt::ToolTipRole: + return tr("%1 (%2 colors)").arg(palette.name()).arg(palette.count()); + } + + return QVariant(); +} + +bool ColorPaletteModel::removeRows(int row, int count, const QModelIndex & parent) +{ + Q_UNUSED(parent) + + if ( !p->acceptable(row) || count <= 0 ) + return false; + + auto begin = p->palettes.begin() + row; + auto end = row + count >= p->palettes.size() ? p->palettes.end() : begin + count; + for ( auto it = begin; it != end; ++it ) + { + if ( !it->fileName().isEmpty() ) + { + QFileInfo file(it->fileName()); + if ( file.isWritable() && file.isFile() ) + QFile::remove(it->fileName()); + } + } + + p->palettes.erase(begin, end); + + return true; +} + +QSize ColorPaletteModel::iconSize() const +{ + return p->icon_size; +} + +void ColorPaletteModel::setIconSize(const QSize& iconSize) +{ + if ( p->icon_size != iconSize ) + Q_EMIT iconSizeChanged( p->icon_size = iconSize ); +} + +QString ColorPaletteModel::savePath() const +{ + return p->save_path; +} + +QStringList ColorPaletteModel::searchPaths() const +{ + return p->search_paths; +} + +void ColorPaletteModel::setSavePath(const QString& savePath) +{ + if ( p->save_path != savePath ) + Q_EMIT savePathChanged( p->save_path = savePath ); +} + +void ColorPaletteModel::setSearchPaths(const QStringList& searchPaths) +{ + if ( p->search_paths != searchPaths ) + Q_EMIT searchPathsChanged( p->search_paths = searchPaths ); +} + +void ColorPaletteModel::addSearchPath(const QString& path) +{ + /// \todo Should compare canonical paths + /// and these checks should also be made in setSearchPaths + if ( !p->search_paths.contains(path) ) + { + p->search_paths.push_back(path); + Q_EMIT searchPathsChanged( p->search_paths ); + } +} + +void ColorPaletteModel::load() +{ + beginResetModel(); + p->palettes.clear(); + QStringList filters; + filters << QStringLiteral("*.gpl"); + for ( const QString& directory_name : p->search_paths ) + { + QDir directory(directory_name); + directory.setNameFilters(filters); + directory.setFilter(QDir::Files|QDir::Readable); + directory.setSorting(QDir::Name); + for ( const QFileInfo& file : directory.entryInfoList() ) + { + ColorPalette palette; + if ( palette.load(file.absoluteFilePath()) ) + { + p->palettes.push_back(palette); + } + } + } + endResetModel(); +} + +bool ColorPaletteModel::hasPalette(const QString& name) const +{ + return p->find(name) != p->palettes.end(); +} + +int ColorPaletteModel::count() const +{ + return p->palettes.size(); +} + +const ColorPalette& ColorPaletteModel::palette(const QString& name) const +{ + return *p->find(name); +} + +const ColorPalette& ColorPaletteModel::palette(int index) const +{ + return p->palettes[index]; +} + +bool ColorPaletteModel::updatePalette(int index, const ColorPalette& palette, bool save) +{ + if ( !p->acceptable(index) ) + return false; + + // Store the old file name + QString filename = p->palettes[index].fileName(); + // Update the palette + ColorPalette& local_palette = p->palettes[index] = palette; + p->fixUnnamed(local_palette); + + if ( save ) + return p->save(local_palette, filename); + + return true; +} + +bool ColorPaletteModel::removePalette(int index, bool remove_file) +{ + if ( !p->acceptable(index) ) + return false; + + QString file_name = p->palettes[index].fileName(); + + beginRemoveRows(QModelIndex(), index, index); + p->palettes.removeAt(index); + endRemoveRows(); + + if ( !file_name.isEmpty() && remove_file ) + { + QFileInfo file(file_name); + if ( file.isWritable() && file.isFile() ) + return QFile::remove(file_name); + return false; + } + + return true; +} + +bool ColorPaletteModel::addPalette(const ColorPalette& palette, bool save) +{ + beginInsertRows(QModelIndex(), p->palettes.size(), p->palettes.size()); + p->palettes.push_back(palette); + p->fixUnnamed(p->palettes.back()); + endInsertRows(); + + if ( save ) + return p->save(p->palettes.back()); + + return true; +} + + +int ColorPaletteModel::indexFromFile(const QString& filename) const +{ + QString canonical = QFileInfo(filename).canonicalFilePath(); + int i = 0; + for ( const auto& pal : p->palettes ) + { + if ( !pal.fileName().isEmpty() && + QFileInfo(pal.fileName()).canonicalFilePath() == canonical ) + return i; + i++; + } + return -1; +} + +} // namespace color_widgets diff --git a/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_palette_widget.cpp b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_palette_widget.cpp new file mode 100644 index 00000000..15616a42 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_palette_widget.cpp @@ -0,0 +1,416 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#include "QtColorWidgets/color_palette_widget.hpp" +#include "ui_color_palette_widget.h" +#include "QtColorWidgets/color_dialog.hpp" +#include +#include +#include +#include + +namespace color_widgets { + +class ColorPaletteWidget::Private : public Ui::ColorPaletteWidget +{ +public: + ColorPaletteModel* model = nullptr; + bool read_only = false; + + bool hasSelectedPalette() + { + return model && palette_list->currentIndex() != -1; + } + + const ColorPalette& selectedPalette() + { + return model->palette(palette_list->currentIndex()); + } + + void addPalette(ColorPalette& palette) + { + bool save = false; + // Save palettes in the savePath + /// \todo This currently breaks opening the right directory + /// ie: the one containing the original file. + if ( !palette.fileName().isEmpty() ) + { + QFileInfo file(palette.fileName()); + if ( file.dir().canonicalPath() != QDir(model->savePath()).canonicalPath() ) + { + palette.setFileName(QString()); + save = true; + } + } + model->addPalette(palette, save); + palette_list->setCurrentIndex(model->count()-1); + } + + bool openImage(const QString& file) + { + QImage image(file); + if ( !image.isNull() ) + { + ColorPalette palette; + palette.loadImage(image); + palette.setName(QFileInfo(file).baseName()); + palette.setFileName(file+".gpl"); + addPalette(palette); + return true; + } + return false; + } + + bool openGpl(const QString& file) + { + int existing = model->indexFromFile(file); + if ( existing != -1 ) + { + palette_list->setCurrentIndex(existing); + return true; + } + + ColorPalette palette; + if ( palette.load(file) ) + { + addPalette(palette); + return true; + } + + return false; + } + + bool openPalette(const QString& file, int type) + { + if ( type == 1 ) + return openImage(file); + return openGpl(file); + } +}; + + +ColorPaletteWidget::ColorPaletteWidget(QWidget* parent) + : QWidget(parent), p(new Private) +{ + p->setupUi(this); + + // Connext Swatch signals + connect(p->swatch, &Swatch::colorSizeChanged, this, &ColorPaletteWidget::colorSizeChanged); + connect(p->swatch, &Swatch::colorSizePolicyChanged, this, &ColorPaletteWidget::colorSizePolicyChanged); + connect(p->swatch, &Swatch::forcedRowsChanged, this, &ColorPaletteWidget::forcedRowsChanged); + connect(p->swatch, &Swatch::forcedColumnsChanged, this, &ColorPaletteWidget::forcedColumnsChanged); + connect(p->swatch, &Swatch::colorSelected, + this, (void (ColorPaletteWidget::*)(const QColor&)) &ColorPaletteWidget::currentColorChanged); + connect(p->swatch, &Swatch::selectedChanged, + this, (void (ColorPaletteWidget::*)(int)) &ColorPaletteWidget::currentColorChanged); + connect(p->swatch, &Swatch::borderChanged, this, &ColorPaletteWidget::borderChanged); + connect(p->swatch, &Swatch::paletteChanged, this, &ColorPaletteWidget::currentPaletteChanged); + + connect(&p->swatch->palette(), &ColorPalette::dirtyChanged, p->button_palette_save, &QWidget::setEnabled); + connect(&p->swatch->palette(), &ColorPalette::dirtyChanged, p->button_palette_revert, &QWidget::setEnabled); + + connect(p->palette_list, (void (QComboBox::*)(int))&QComboBox::currentIndexChanged, + this, &ColorPaletteWidget::currentRowChanged); + + // Buttons changing the colors in the current palette + connect(p->button_color_add, &QAbstractButton::clicked, [this](){ + if ( !p->read_only && p->hasSelectedPalette() ) + { + ColorDialog dialog(this); + dialog.setAlphaEnabled(false); + if ( p->swatch->selected() != -1 ) + dialog.setColor(p->swatch->selectedColor()); + if ( dialog.exec() ) + { + p->swatch->palette().appendColor(dialog.color()); + p->swatch->setSelected(p->swatch->palette().count()-1); + } + } + }); + connect(p->button_color_remove, &QAbstractButton::clicked, p->swatch, &Swatch::removeSelected); + + // Buttons modifying the current palette file + connect(p->button_palette_delete, &QAbstractButton::clicked, [this]() { + if ( !p->read_only && p->hasSelectedPalette() ) + p->model->removePalette(p->palette_list->currentIndex()); + }); + connect(p->button_palette_save, &QAbstractButton::clicked, [this](){ + if ( !p->read_only && p->hasSelectedPalette() && p->swatch->palette().dirty() ) + if ( p->model->updatePalette( p->palette_list->currentIndex(), p->swatch->palette() ) ) + { + p->swatch->palette().setDirty(false); + } + /// \todo else ask for a file name (?) + }); + connect(p->button_palette_revert, &QAbstractButton::clicked, [this](){ + if ( p->hasSelectedPalette() ) + { + p->swatch->setPalette(p->selectedPalette()); + } + }); + + // Buttons creating new palettes + connect(p->button_palette_duplicate, &QAbstractButton::clicked, [this](){ + if ( p->hasSelectedPalette() ) + { + ColorPalette new_palette = p->selectedPalette(); + new_palette.setFileName(QString()); + bool ok = false; + QString name = QInputDialog::getText(this, tr("New Palette"), + tr("Name"), QLineEdit::Normal, new_palette.name(), &ok); + if ( ok ) + { + new_palette.setName(name); + p->model->addPalette(new_palette); + p->palette_list->setCurrentIndex(p->model->count()-1); + } + } + }); + /// \todo Show a dialog that asks for the number of columns (?) + connect(p->button_palette_new, &QAbstractButton::clicked, [this](){ + if ( p->hasSelectedPalette() ) + { + bool ok = false; + QString name = QInputDialog::getText(this, tr("New Palette"), + tr("Name"), QLineEdit::Normal, QString(), &ok); + if ( ok ) + { + ColorPalette new_palette(name); + p->model->addPalette(new_palette); + p->palette_list->setCurrentIndex(p->model->count()-1); + } + } + }); + + QString image_formats; + Q_FOREACH(QByteArray ba, QImageReader::supportedImageFormats()) + image_formats += " *."+QString(ba); + + connect(p->button_palette_open, &QAbstractButton::clicked, [this, image_formats](){ + if ( p->model ) + { + QString default_dir; + if ( p->hasSelectedPalette() ) + { + const ColorPalette& palette = p->selectedPalette(); + if ( !palette.fileName().isEmpty() ) + default_dir = QFileInfo(palette.fileName()).dir().path(); + } + + QStringList file_formats = QStringList() + << tr("GIMP Palettes (*.gpl)") + << tr("Palette Image (%1)").arg(image_formats) + << tr("All Files (*)"); + QFileDialog open_dialog(this, tr("Open Palette"), default_dir); + open_dialog.setFileMode(QFileDialog::ExistingFile); + open_dialog.setAcceptMode(QFileDialog::AcceptOpen); + open_dialog.setNameFilters(file_formats); + + if ( !open_dialog.exec() ) + return; + + int type = file_formats.indexOf(open_dialog.selectedNameFilter()); + QString file_name = open_dialog.selectedFiles()[0]; + + if ( !p->openPalette( file_name, type ) ) + { + QMessageBox::warning(this, tr("Open Palette"), + tr("Failed to load the palette file\n%1").arg(file_name)); + } + } + }); +} + +ColorPaletteWidget::~ColorPaletteWidget() = default; + +ColorPaletteModel* ColorPaletteWidget::model() const +{ + return p->model; +} + +const ColorPalette& ColorPaletteWidget::currentPalette() const +{ + return p->swatch->palette(); +} + +QSize ColorPaletteWidget::colorSize() const +{ + return p->swatch->colorSize(); +} + +Swatch::ColorSizePolicy ColorPaletteWidget::colorSizePolicy() const +{ + return p->swatch->colorSizePolicy(); +} + +QPen ColorPaletteWidget::border() const +{ + return p->swatch->border(); +} + +int ColorPaletteWidget::forcedRows() const +{ + return p->swatch->forcedRows(); +} +int ColorPaletteWidget::forcedColumns() const +{ + return p->swatch->forcedColumns(); +} + +bool ColorPaletteWidget::readOnly() const +{ + return p->read_only; +} + +QColor ColorPaletteWidget::currentColor() const +{ + return p->swatch->selectedColor(); +} + +void ColorPaletteWidget::setModel(ColorPaletteModel* model) +{ + if ( model == p->model ) + return; + p->model = model; + p->swatch->setPalette(ColorPalette()); + p->palette_list->setModel(model); +} + +void ColorPaletteWidget::setColorSize(const QSize& colorSize) +{ + p->swatch->setColorSize(colorSize); +} + +void ColorPaletteWidget::setColorSizePolicy(Swatch::ColorSizePolicy colorSizePolicy) +{ + p->swatch->setColorSizePolicy(colorSizePolicy); +} + +void ColorPaletteWidget::setBorder(const QPen& border) +{ + p->swatch->setBorder(border); +} + +void ColorPaletteWidget::setForcedRows(int forcedRows) +{ + p->swatch->setForcedRows(forcedRows); +} + +void ColorPaletteWidget::setForcedColumns(int forcedColumns) +{ + p->swatch->setForcedColumns(forcedColumns); +} + +void ColorPaletteWidget::setReadOnly(bool readOnly) +{ + if ( readOnly == p->read_only ) + return; + + p->swatch->setReadOnly(readOnly); + p->group_edit_list->setVisible(!readOnly); + p->group_edit_palette->setVisible(!readOnly); + Q_EMIT readOnlyChanged(p->read_only = readOnly); +} + +bool ColorPaletteWidget::setCurrentColor(const QColor& color) +{ + const auto& palette = p->swatch->palette(); + for ( int i = 0; i < palette.count(); i++ ) + { + if ( palette.colorAt(i) == color ) + { + p->swatch->setSelected(i); + return true; + } + } + + p->swatch->clearSelection(); + return false; +} + +bool ColorPaletteWidget::setCurrentColor(const QString& name) +{ + const auto& palette = p->swatch->palette(); + for ( int i = 0; i < palette.count(); i++ ) + { + if ( palette.nameAt(i) == name ) + { + p->swatch->setSelected(i); + return true; + } + } + + p->swatch->clearSelection(); + return false; +} + +bool ColorPaletteWidget::setCurrentColor(int index) +{ + const auto& palette = p->swatch->palette(); + if ( index >= 0 && index < palette.count() ) + { + p->swatch->setSelected(index); + return true; + } + + p->swatch->clearSelection(); + return false; +} + + +void ColorPaletteWidget::on_palette_list_currentIndexChanged(int index) +{ + if ( !p->model ) + p->swatch->setPalette(ColorPalette()); + else + p->swatch->setPalette(p->model->palette(index)); + + p->swatch->palette().setDirty(false); +} + +void ColorPaletteWidget::on_swatch_doubleClicked(int index) +{ + if ( !p->read_only ) + { + ColorDialog dialog(this); + dialog.setAlphaEnabled(false); + dialog.setColor(p->swatch->palette().colorAt(index)); + if ( dialog.exec() ) + p->swatch->palette().setColorAt(index, dialog.color()); + } +} + +int ColorPaletteWidget::currentRow() const +{ + return p->palette_list->currentIndex(); +} + +void ColorPaletteWidget::setCurrentRow(int row) +{ + p->palette_list->setCurrentIndex(row); +} + +void ColorPaletteWidget::clearCurrentColor() +{ + p->swatch->clearSelection(); +} + +} // namespace color_widgets diff --git a/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_palette_widget.ui b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_palette_widget.ui new file mode 100644 index 00000000..f9bd2251 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_palette_widget.ui @@ -0,0 +1,205 @@ + + + color_widgets::ColorPaletteWidget + + + + 0 + 0 + 227 + 186 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + 0 + 0 + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Open a new palette from file + + + + + + + + + + + + Create a new palette + + + + + + + + + + + + Duplicate the current palette + + + + + + + + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Delete the current palette + + + + + + + + + + + + Revert changes to the current palette + + + + + + + + + + + + Save changes to the current palette + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Add a color to the palette + + + + + + + + + + + + Remove the selected color from the palette + + + + + + + + + + + + + + + + color_widgets::Swatch + QWidget +
QtColorWidgets/swatch.hpp
+
+
+ + +
diff --git a/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_preview.cpp b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_preview.cpp new file mode 100644 index 00000000..d9d3dcc4 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_preview.cpp @@ -0,0 +1,185 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * \copyright Copyright (C) 2014 Calle Laakkonen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#include "QtColorWidgets/color_preview.hpp" + +#include +#include +#include +#include +#include + +namespace color_widgets { + +class ColorPreview::Private +{ +public: + QColor col; ///< color to be viewed + QColor comparison; ///< comparison color + QBrush back;///< Background brush, visible on a transparent color + DisplayMode display_mode; ///< How the color(s) are to be shown + + Private() : col(Qt::red), back(Qt::darkGray, Qt::DiagCrossPattern), display_mode(NoAlpha) + {} +}; + +ColorPreview::ColorPreview(QWidget *parent) : + QWidget(parent), p(new Private) +{ + p->back.setTexture(QPixmap(QStringLiteral(":/color_widgets/alphaback.png"))); +} + +ColorPreview::~ColorPreview() +{ + delete p; +} + +void ColorPreview::setBackground(const QBrush &bk) +{ + p->back = bk; + update(); + Q_EMIT backgroundChanged(bk); +} + +QBrush ColorPreview::background() const +{ + return p->back; +} + +ColorPreview::DisplayMode ColorPreview::displayMode() const +{ + return p->display_mode; +} + +void ColorPreview::setDisplayMode(DisplayMode m) +{ + p->display_mode = m; + update(); + Q_EMIT displayModeChanged(m); +} + +QColor ColorPreview::color() const +{ + return p->col; +} + +QColor ColorPreview::comparisonColor() const +{ + return p->comparison; +} + +QSize ColorPreview::sizeHint() const +{ + return QSize(24,24); +} + +void ColorPreview::paint(QPainter &painter, QRect rect) const +{ + QColor c1, c2; + switch(p->display_mode) { + case DisplayMode::NoAlpha: + c1 = c2 = p->col.rgb(); + break; + case DisplayMode::AllAlpha: + c1 = c2 = p->col; + break; + case DisplayMode::SplitAlpha: + c1 = p->col.rgb(); + c2 = p->col; + break; + case DisplayMode::SplitColor: + c1 = p->comparison; + c2 = p->col; + break; + } + + QStyleOptionFrame panel; + panel.initFrom(this); + panel.lineWidth = 2; + panel.midLineWidth = 0; + panel.state |= QStyle::State_Sunken; + style()->drawPrimitive(QStyle::PE_Frame, &panel, &painter, this); + QRect r = style()->subElementRect(QStyle::SE_FrameContents, &panel, this); + painter.setClipRect(r); + + if ( c1.alpha() < 255 || c2.alpha() < 255 ) + painter.fillRect(0, 0, rect.width(), rect.height(), p->back); + + int w = rect.width() / 2; + int h = rect.height(); + painter.fillRect(0, 0, w, h, c1); + painter.fillRect(w, 0, w, h, c2); +} + +void ColorPreview::setColor(const QColor &c) +{ + p->col = c; + update(); + Q_EMIT colorChanged(c); +} + +void ColorPreview::setComparisonColor(const QColor &c) +{ + p->comparison = c; + update(); + Q_EMIT comparisonColorChanged(c); +} + +void ColorPreview::paintEvent(QPaintEvent *) +{ + QStylePainter painter(this); + + paint(painter, geometry()); +} + +void ColorPreview::resizeEvent(QResizeEvent *) +{ + update(); +} + +void ColorPreview::mouseReleaseEvent(QMouseEvent * ev) +{ + if ( QRect(QPoint(0,0),size()).contains(ev->pos()) ) + Q_EMIT clicked(); +} + +void ColorPreview::mouseMoveEvent(QMouseEvent *ev) +{ + + if ( ev->buttons() &Qt::LeftButton && !QRect(QPoint(0,0),size()).contains(ev->pos()) ) + { + QMimeData *data = new QMimeData; + + data->setColorData(p->col); + + QDrag* drag = new QDrag(this); + drag->setMimeData(data); + + QPixmap preview(24,24); + preview.fill(p->col); + drag->setPixmap(preview); + + drag->exec(); + } +} + +} // namespace color_widgets diff --git a/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_selector.cpp b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_selector.cpp new file mode 100644 index 00000000..ec267d3e --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_selector.cpp @@ -0,0 +1,183 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#include "QtColorWidgets/color_selector.hpp" +#include "QtColorWidgets/color_dialog.hpp" +#include +#include +#include + +namespace color_widgets { + +class ColorSelector::Private +{ +public: + UpdateMode update_mode; + ColorDialog *dialog; + QColor old_color; + + Private(QWidget *widget) : dialog(new ColorDialog(widget)) + { + dialog->setButtonMode(ColorDialog::OkCancel); + } +}; + +ColorSelector::ColorSelector(QWidget *parent) : + ColorPreview(parent), p(new Private(this)) +{ + setUpdateMode(Continuous); + p->old_color = color(); + + connect(this,&ColorPreview::clicked,this,&ColorSelector::showDialog); + connect(this,SIGNAL(colorChanged(QColor)),this,SLOT(update_old_color(QColor))); + connect(p->dialog,&QDialog::rejected,this,&ColorSelector::reject_dialog); + connect(p->dialog,&ColorDialog::colorSelected, this, &ColorSelector::accept_dialog); + + connect(p->dialog, &ColorDialog::wheelRotatingChanged, this, &ColorSelector::wheelRotatingChanged); + connect(p->dialog, &ColorDialog::wheelShapeChanged, this, &ColorSelector::wheelShapeChanged); + connect(p->dialog, &ColorDialog::colorSpaceChanged, this, &ColorSelector::colorSpaceChanged); + + setAcceptDrops(true); +} + +ColorSelector::~ColorSelector() +{ + delete p; +} + +ColorSelector::UpdateMode ColorSelector::updateMode() const +{ + return p->update_mode; +} + +void ColorSelector::setUpdateMode(UpdateMode m) +{ + p->update_mode = m; + Q_EMIT updateModeChanged(m); +} + +Qt::WindowModality ColorSelector::dialogModality() const +{ + return p->dialog->windowModality(); +} + +void ColorSelector::setDialogModality(Qt::WindowModality m) +{ + p->dialog->setWindowModality(m); + Q_EMIT dialogModalityChanged(m); +} + +void ColorSelector::setWheelShape(ColorWheel::ShapeEnum shape) +{ + p->dialog->setWheelShape(shape); +} + +ColorWheel::ShapeEnum ColorSelector::wheelShape() const +{ + return p->dialog->wheelShape(); +} + +void ColorSelector::setColorSpace(ColorWheel::ColorSpaceEnum space) +{ + p->dialog->setColorSpace(space); +} + +ColorWheel::ColorSpaceEnum ColorSelector::colorSpace() const +{ + return p->dialog->colorSpace(); +} + +void ColorSelector::setWheelRotating(bool rotating) +{ + p->dialog->setWheelRotating(rotating); +} + +bool ColorSelector::wheelRotating() const +{ + return p->dialog->wheelRotating(); +} + +void ColorSelector::showDialog() +{ + p->old_color = color(); + p->dialog->setColor(color()); + connect_dialog(); + p->dialog->show(); +} + + +void ColorSelector::connect_dialog() +{ + if (p->update_mode == Continuous) + connect(p->dialog, SIGNAL(colorChanged(QColor)), this, SLOT(setColor(QColor)), Qt::UniqueConnection); + else + disconnect_dialog(); +} + +void ColorSelector::disconnect_dialog() +{ + disconnect(p->dialog, SIGNAL(colorChanged(QColor)), this, SLOT(setColor(QColor))); +} + +void ColorSelector::accept_dialog() +{ + setColor(p->dialog->color()); + p->old_color = color(); +} + +void ColorSelector::reject_dialog() +{ + setColor(p->old_color); +} + +void ColorSelector::update_old_color(const QColor &c) +{ + if (!p->dialog->isVisible()) + p->old_color = c; +} + +void ColorSelector::dragEnterEvent(QDragEnterEvent *event) +{ + if ( event->mimeData()->hasColor() || + ( event->mimeData()->hasText() && QColor(event->mimeData()->text()).isValid() ) ) + event->acceptProposedAction(); +} + + +void ColorSelector::dropEvent(QDropEvent *event) +{ + if ( event->mimeData()->hasColor() ) + { + setColor(event->mimeData()->colorData().value()); + event->accept(); + } + else if ( event->mimeData()->hasText() ) + { + QColor col(event->mimeData()->text()); + if ( col.isValid() ) + { + setColor(col); + event->accept(); + } + } +} + +} // namespace color_widgets diff --git a/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_utils.cpp b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_utils.cpp new file mode 100644 index 00000000..d82e2dae --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_utils.cpp @@ -0,0 +1,83 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#include "QtColorWidgets/color_utils.hpp" + +namespace color_widgets { +namespace detail { + +QColor color_from_lch(qreal hue, qreal chroma, qreal luma, qreal alpha ) +{ + qreal h1 = hue*6; + qreal x = chroma*(1-qAbs(std::fmod(h1,2)-1)); + QColor col; + if ( h1 >= 0 && h1 < 1 ) + col = QColor::fromRgbF(chroma,x,0); + else if ( h1 < 2 ) + col = QColor::fromRgbF(x,chroma,0); + else if ( h1 < 3 ) + col = QColor::fromRgbF(0,chroma,x); + else if ( h1 < 4 ) + col = QColor::fromRgbF(0,x,chroma); + else if ( h1 < 5 ) + col = QColor::fromRgbF(x,0,chroma); + else if ( h1 < 6 ) + col = QColor::fromRgbF(chroma,0,x); + + qreal m = luma - color_lumaF(col); + + return QColor::fromRgbF( + qBound(0.0,col.redF()+m,1.0), + qBound(0.0,col.greenF()+m,1.0), + qBound(0.0,col.blueF()+m,1.0), + alpha); +} + +QColor color_from_hsl(qreal hue, qreal sat, qreal lig, qreal alpha ) +{ + qreal chroma = (1 - qAbs(2*lig-1))*sat; + qreal h1 = hue*6; + qreal x = chroma*(1-qAbs(std::fmod(h1,2)-1)); + QColor col; + if ( h1 >= 0 && h1 < 1 ) + col = QColor::fromRgbF(chroma,x,0); + else if ( h1 < 2 ) + col = QColor::fromRgbF(x,chroma,0); + else if ( h1 < 3 ) + col = QColor::fromRgbF(0,chroma,x); + else if ( h1 < 4 ) + col = QColor::fromRgbF(0,x,chroma); + else if ( h1 < 5 ) + col = QColor::fromRgbF(x,0,chroma); + else if ( h1 < 6 ) + col = QColor::fromRgbF(chroma,0,x); + + qreal m = lig-chroma/2; + + return QColor::fromRgbF( + qBound(0.0,col.redF()+m,1.0), + qBound(0.0,col.greenF()+m,1.0), + qBound(0.0,col.blueF()+m,1.0), + alpha); +} + +} // namespace detail +} // namespace color_widgets diff --git a/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_wheel.cpp b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_wheel.cpp new file mode 100644 index 00000000..fd0dcb6d --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/color_wheel.cpp @@ -0,0 +1,364 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#include "QtColorWidgets/color_wheel.hpp" +#include "QtColorWidgets/color_wheel_private.hpp" + +#include +#include +#include +#include + +namespace color_widgets { + + +static const double selector_radius = 6; + + +ColorWheel::ColorWheel(QWidget *parent, Private* data) : + QWidget(parent), p(data) +{ + p->setup(); + setAcceptDrops(true); +} + + +ColorWheel::ColorWheel(QWidget *parent) : + ColorWheel(parent, new Private(this)) +{ + +} + + +ColorWheel::~ColorWheel() +{ + delete p; +} + +QColor ColorWheel::color() const +{ + return p->color_from(p->hue, p->sat, p->val, 1); +} + +QSize ColorWheel::sizeHint() const +{ + return QSize(p->wheel_width*5, p->wheel_width*5); +} + +qreal ColorWheel::hue() const +{ + if ( p->color_space == ColorLCH && p->sat > 0.01 ) + return color().hueF(); + return p->hue; +} + +qreal ColorWheel::saturation() const +{ + return color().hsvSaturationF(); +} + +qreal ColorWheel::value() const +{ + return color().valueF(); +} + +unsigned int ColorWheel::wheelWidth() const +{ + return p->wheel_width; +} + +void ColorWheel::setWheelWidth(unsigned int w) +{ + p->wheel_width = w; + p->render_inner_selector(); + update(); + Q_EMIT wheelWidthChanged(w); +} + +void ColorWheel::paintEvent(QPaintEvent * ) +{ + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + painter.translate(geometry().width()/2,geometry().height()/2); + + // hue wheel + if(p->hue_ring.isNull()) + p->render_ring(); + + painter.drawPixmap(-p->outer_radius(), -p->outer_radius(), p->hue_ring); + + // hue selector + p->draw_ring_editor(p->hue, painter, Qt::black); + + // lum-sat square + if(p->inner_selector.isNull()) + p->render_inner_selector(); + + painter.rotate(p->selector_image_angle()); + painter.translate(p->selector_image_offset()); + + QPointF selector_position; + if ( p->selector_shape == ShapeSquare ) + { + qreal side = p->square_size(); + selector_position = QPointF(p->sat*side, p->val*side); + } + else if ( p->selector_shape == ShapeTriangle ) + { + qreal side = p->triangle_side(); + qreal height = p->triangle_height(); + qreal slice_h = side * p->val; + qreal ymin = side/2-slice_h/2; + + selector_position = QPointF(p->val*height, ymin + p->sat*slice_h); + QPolygonF triangle; + triangle.append(QPointF(0,side/2)); + triangle.append(QPointF(height,0)); + triangle.append(QPointF(height,side)); + QPainterPath clip; + clip.addPolygon(triangle); + painter.setClipPath(clip); + } + + painter.drawImage(QRectF(QPointF(0, 0), p->selector_size()), p->inner_selector); + painter.setClipping(false); + + // lum-sat selector + // we define the color of the selecto based on the background color of the widget + // in order to improve to contrast + if (p->backgroundIsDark) + { + bool isWhite = (p->val < 0.65 || p->sat > 0.43); + painter.setPen(QPen(isWhite ? Qt::white : Qt::black, 3)); + } + else + { + painter.setPen(QPen(p->val > 0.5 ? Qt::black : Qt::white, 3)); + } + painter.setBrush(Qt::NoBrush); + painter.drawEllipse(selector_position, selector_radius, selector_radius); + +} + +void ColorWheel::mouseMoveEvent(QMouseEvent *ev) +{ + if (p->mouse_status == DragCircle ) + { + auto hue = p->line_to_point(ev->pos()).angle()/360.0; + p->hue = hue; + p->render_inner_selector(); + + Q_EMIT colorSelected(color()); + Q_EMIT colorChanged(color()); + update(); + } + else if(p->mouse_status == DragSquare) + { + QLineF glob_mouse_ln = p->line_to_point(ev->pos()); + QLineF center_mouse_ln ( QPointF(0,0), + glob_mouse_ln.p2() - glob_mouse_ln.p1() ); + + center_mouse_ln.setAngle(center_mouse_ln.angle()+p->selector_image_angle()); + center_mouse_ln.setP2(center_mouse_ln.p2()-p->selector_image_offset()); + + if ( p->selector_shape == ShapeSquare ) + { + p->sat = qBound(0.0, center_mouse_ln.x2()/p->square_size(), 1.0); + p->val = qBound(0.0, center_mouse_ln.y2()/p->square_size(), 1.0); + } + else if ( p->selector_shape == ShapeTriangle ) + { + QPointF pt = center_mouse_ln.p2(); + + qreal side = p->triangle_side(); + p->val = qBound(0.0, pt.x() / p->triangle_height(), 1.0); + qreal slice_h = side * p->val; + + qreal ycenter = side/2; + qreal ymin = ycenter-slice_h/2; + + if ( slice_h > 0 ) + p->sat = qBound(0.0, (pt.y()-ymin)/slice_h, 1.0); + } + + Q_EMIT colorSelected(color()); + Q_EMIT colorChanged(color()); + update(); + } +} + +void ColorWheel::mousePressEvent(QMouseEvent *ev) +{ + if ( ev->buttons() & Qt::LeftButton ) + { + setFocus(); + QLineF ray = p->line_to_point(ev->pos()); + if ( ray.length() <= p->inner_radius() ) + p->mouse_status = DragSquare; + else if ( ray.length() <= p->outer_radius() ) + p->mouse_status = DragCircle; + + // Update the color + mouseMoveEvent(ev); + } +} + +void ColorWheel::mouseReleaseEvent(QMouseEvent *ev) +{ + mouseMoveEvent(ev); + p->mouse_status = Nothing; +} + +void ColorWheel::resizeEvent(QResizeEvent *) +{ + p->render_ring(); + p->render_inner_selector(); +} + +void ColorWheel::setColor(QColor c) +{ + qreal oldh = p->hue; + p->set_color(c); + if (!qFuzzyCompare(oldh+1, p->hue+1)) + p->render_inner_selector(); + update(); + Q_EMIT colorChanged(c); +} + +void ColorWheel::setHue(qreal h) +{ + p->hue = qBound(0.0, h, 1.0); + p->render_inner_selector(); + update(); +} + +void ColorWheel::setSaturation(qreal s) +{ + p->sat = qBound(0.0, s, 1.0); + update(); +} + +void ColorWheel::setValue(qreal v) +{ + p->val = qBound(0.0, v, 1.0); + update(); +} + +color_widgets::ColorWheel::ColorSpaceEnum ColorWheel::colorSpace() const +{ + return p->color_space; +} + +bool ColorWheel::rotatingSelector() const +{ + return p->rotating_selector; +} + +color_widgets::ColorWheel::ShapeEnum ColorWheel::selectorShape() const +{ + return p->selector_shape; +} + + +void ColorWheel::setColorSpace(color_widgets::ColorWheel::ColorSpaceEnum space) +{ + if ( p->color_space != space ) + { + p->color_space = space; + + QColor old_col = color(); + + switch ( space ) + { + case ColorHSL: + p->hue = old_col.hueF(); + p->sat = detail::color_HSL_saturationF(old_col); + p->val = detail::color_lightnessF(old_col); + p->color_from = &detail::color_from_hsl; + p->rainbow_from_hue = &detail::rainbow_hsv; + break; + case ColorHSV: + p->hue = old_col.hsvHueF(); + p->sat = old_col.hsvSaturationF(); + p->val = old_col.valueF(); + p->color_from = &QColor::fromHsvF; + p->rainbow_from_hue = &detail::rainbow_hsv; + break; + case ColorLCH: + p->hue = old_col.hueF(); + p->sat = detail::color_chromaF(old_col); + p->val = detail::color_lumaF(old_col); + p->color_from = &detail::color_from_lch; + p->rainbow_from_hue = &detail::rainbow_lch; + break; + } + + p->render_ring(); + p->render_inner_selector(); + update(); + Q_EMIT colorSpaceChanged(space); + } +} + +void ColorWheel::setRotatingSelector(bool rotating) +{ + p->rotating_selector = rotating; + update(); + Q_EMIT rotatingSelectorChanged(rotating); +} + +void ColorWheel::setSelectorShape(color_widgets::ColorWheel::ShapeEnum shape) +{ + if ( shape != p->selector_shape ) + { + p->selector_shape = shape; + update(); + p->render_inner_selector(); + Q_EMIT selectorShapeChanged(shape); + } +} + +void ColorWheel::dragEnterEvent(QDragEnterEvent* event) +{ + if ( event->mimeData()->hasColor() || + ( event->mimeData()->hasText() && QColor(event->mimeData()->text()).isValid() ) ) + event->acceptProposedAction(); +} + +void ColorWheel::dropEvent(QDropEvent* event) +{ + if ( event->mimeData()->hasColor() ) + { + setColor(event->mimeData()->colorData().value()); + event->accept(); + } + else if ( event->mimeData()->hasText() ) + { + QColor col(event->mimeData()->text()); + if ( col.isValid() ) + { + setColor(col); + event->accept(); + } + } +} + +} // namespace color_widgets diff --git a/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/gradient_editor.cpp b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/gradient_editor.cpp new file mode 100644 index 00000000..5390919b --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/gradient_editor.cpp @@ -0,0 +1,575 @@ +/** + * \file gradient_editor.cpp + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#include "QtColorWidgets/gradient_editor.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "QtColorWidgets/gradient_helper.hpp" +#include "QtColorWidgets/color_dialog.hpp" + +namespace color_widgets { + +class GradientEditor::Private +{ +public: + QGradientStops stops; + QBrush back; + Qt::Orientation orientation; + int highlighted = -1; + QLinearGradient gradient; + int selected = -1; + int drop_index = -1; + QColor drop_color; + qreal drop_pos = 0; + ColorDialog color_dialog; + int dialog_selected = -1; + + Private() : + back(Qt::darkGray, Qt::DiagCrossPattern) + { + back.setTexture(QPixmap(QStringLiteral(":/color_widgets/alphaback.png"))); + gradient.setCoordinateMode(QGradient::StretchToDeviceMode); + gradient.setSpread(QGradient::RepeatSpread); + } + + void refresh_gradient() + { + gradient.setStops(stops); + } + + qreal paint_pos(const QGradientStop& stop, const GradientEditor* owner) + { + return 2.5 + stop.first * (owner->geometry().width() - 5); + } + + int closest(const QPoint& p, GradientEditor* owner) + { + if ( stops.empty() ) + return -1; + if ( stops.size() == 1 || owner->geometry().width() <= 5 ) + return 0; + qreal pos = move_pos(p, owner); + + int i = 1; + for ( ; i < stops.size()-1; i++ ) + if ( stops[i].first >= pos ) + break; + + if ( stops[i].first - pos < pos - stops[i-1].first ) + return i; + return i-1; + } + + qreal move_pos(const QPoint& p, GradientEditor* owner) + { + int width; + qreal x; + if ( orientation == Qt::Horizontal ) + { + width = owner->geometry().width(); + x = p.x(); + } + else + { + width = owner->geometry().height(); + x = p.y(); + } + return (width > 5) ? qMax(qMin((x - 2.5) / (width - 5), 1.0), 0.0) : 0; + } + + void drop_event(QDropEvent* event, GradientEditor* owner) + { + drop_index = closest(event->pos(), owner); + drop_pos = move_pos(event->pos(), owner); + if ( drop_index == -1 ) + drop_index = stops.size(); + + // Gather up the color + if ( event->mimeData()->hasColor() ) + drop_color = event->mimeData()->colorData().value(); + else if ( event->mimeData()->hasText() ) + drop_color = QColor(event->mimeData()->text()); + + owner->update(); + } + + void clear_drop(GradientEditor* owner) + { + drop_index = -1; + drop_color = QColor(); + owner->update(); + } + + void add_stop_data(int& index, qreal& pos, QColor& color) + { + if ( stops.empty() ) + { + index = 0; + pos = 0; + color = Qt::black; + return; + } + if ( stops.size() == 1 ) + { + color = stops[0].second; + if ( stops[0].first == 1 ) + { + index = 0; + pos = 0.5; + } + else + { + index = 1; + pos = (stops[0].first + 1) / 2; + } + return; + } + + int i_before = selected; + if ( i_before == -1 ) + i_before = stops.size() - 1; + + if ( i_before == stops.size() - 1 ) + { + if ( stops[i_before].first < 1 ) + { + color = stops[i_before].second; + pos = (stops[i_before].first + 1) / 2; + index = stops.size(); + return; + } + i_before--; + } + + index = i_before + 1; + pos = (stops[i_before].first + stops[i_before+1].first) / 2; + color = blendColors(stops[i_before].second, stops[i_before+1].second, 0.5); + } +}; + +GradientEditor::GradientEditor(QWidget *parent) : + GradientEditor(Qt::Horizontal, parent) +{} + +GradientEditor::GradientEditor(Qt::Orientation orientation, QWidget *parent) : + QWidget(parent), p(new Private) +{ + p->orientation = orientation; + setMouseTracking(true); + resize(sizeHint()); + setAcceptDrops(true); + + p->color_dialog.setParent(this); + p->color_dialog.setWindowFlags(Qt::Dialog); + p->color_dialog.setWindowModality(Qt::WindowModal); + + connect(&p->color_dialog, &ColorDialog::colorSelected, this, &GradientEditor::dialogUpdate); +} + +GradientEditor::~GradientEditor() +{ + p->color_dialog.setParent(nullptr); + delete p; +} + +void GradientEditor::dialogUpdate(const QColor& c) +{ + if ( p->dialog_selected != -1 ) + { + p->stops[p->dialog_selected].second = c; + p->dialog_selected = -1; + p->refresh_gradient(); + Q_EMIT stopsChanged(p->stops); + update(); + } +} + +void GradientEditor::mouseDoubleClickEvent(QMouseEvent *ev) +{ + if ( ev->button() == Qt::LeftButton ) + { + ev->accept(); + if ( p->highlighted != -1 ) + { + qreal highlighted_pos = p->paint_pos(p->stops[p->highlighted], this); + qreal mouse_pos = orientation() == Qt::Vertical ? ev->pos().y() : ev->pos().x(); + qreal tolerance = 4; + if ( std::abs(mouse_pos - highlighted_pos) <= tolerance ) + { + p->dialog_selected = p->highlighted; + p->color_dialog.setColor(p->stops[p->highlighted].second); + p->color_dialog.show(); + return; + } + } + + qreal pos = p->move_pos(ev->pos(), this); + auto info = gradientBlendedColorInsert(p->stops, pos); + p->stops.insert(info.first, info.second); + p->selected = p->highlighted = info.first; + p->refresh_gradient(); + Q_EMIT selectedStopChanged(p->selected); + update(); + } + else + { + QWidget::mousePressEvent(ev); + } +} + +void GradientEditor::mousePressEvent(QMouseEvent *ev) +{ + if ( ev->button() == Qt::LeftButton ) + { + ev->accept(); + p->selected = p->highlighted = p->closest(ev->pos(), this); + emit selectedStopChanged(p->selected); + update(); + } + else + { + QWidget::mousePressEvent(ev); + } +} + +void GradientEditor::mouseMoveEvent(QMouseEvent *ev) +{ + if ( ev->buttons() & Qt::LeftButton && p->selected != -1 ) + { + ev->accept(); + qreal pos = p->move_pos(ev->pos(), this); + if ( p->selected > 0 && pos < p->stops[p->selected-1].first ) + { + std::swap(p->stops[p->selected], p->stops[p->selected-1]); + p->selected--; + emit selectedStopChanged(p->selected); + } + else if ( p->selected < p->stops.size()-1 && pos > p->stops[p->selected+1].first ) + { + std::swap(p->stops[p->selected], p->stops[p->selected+1]); + p->selected++; + emit selectedStopChanged(p->selected); + } + p->highlighted = p->selected; + p->stops[p->selected].first = pos; + p->refresh_gradient(); + update(); + } + else + { + p->highlighted = p->closest(ev->pos(), this); + update(); + } +} + +void GradientEditor::mouseReleaseEvent(QMouseEvent *ev) +{ + if ( ev->button() == Qt::LeftButton && p->selected != -1 ) + { + ev->accept(); + QRect bound_rect = rect(); + QPoint localpt = ev->localPos().toPoint(); + const int w_margin = 24; + const int h_margin = 8; + if ( !bound_rect.contains(localpt) && p->stops.size() > 1 && ( + localpt.x() < -w_margin || localpt.x() > bound_rect.width() + w_margin || + localpt.y() < -h_margin || localpt.y() > bound_rect.height() + h_margin + ) ) + { + p->stops.remove(p->selected); + p->highlighted = p->selected = p->dialog_selected = -1; + p->refresh_gradient(); + emit selectedStopChanged(p->selected); + } + emit stopsChanged(p->stops); + update(); + } + else + { + QWidget::mousePressEvent(ev); + } +} + +void GradientEditor::leaveEvent(QEvent*) +{ + p->highlighted = -1; + update(); +} + + +QBrush GradientEditor::background() const +{ + return p->back; +} + +void GradientEditor::setBackground(const QBrush &bg) +{ + p->back = bg; + update(); + Q_EMIT backgroundChanged(bg); +} + +QGradientStops GradientEditor::stops() const +{ + return p->stops; +} + +void GradientEditor::setStops(const QGradientStops &colors) +{ + p->selected = p->highlighted = p->dialog_selected = -1; + p->stops = colors; + p->refresh_gradient(); + emit selectedStopChanged(p->selected); + emit stopsChanged(p->stops); + update(); +} + +QLinearGradient GradientEditor::gradient() const +{ + return p->gradient; +} + +void GradientEditor::setGradient(const QLinearGradient &gradient) +{ + setStops(gradient.stops()); +} + +Qt::Orientation GradientEditor::orientation() const +{ + return p->orientation; +} + +void GradientEditor::setOrientation(Qt::Orientation orientation) +{ + p->orientation = orientation; + update(); +} + + +void GradientEditor::paintEvent(QPaintEvent *) +{ + QPainter painter(this); + + QStyleOptionFrame panel; + panel.initFrom(this); + panel.lineWidth = 1; + panel.midLineWidth = 0; + panel.state |= QStyle::State_Sunken; + style()->drawPrimitive(QStyle::PE_Frame, &panel, &painter, this); + QRect r = style()->subElementRect(QStyle::SE_FrameContents, &panel, this); + painter.setClipRect(r); + + + if(orientation() == Qt::Horizontal) + p->gradient.setFinalStop(1, 0); + else + p->gradient.setFinalStop(0, -1); + + painter.setPen(Qt::NoPen); + painter.setBrush(p->back); + painter.drawRect(1,1,geometry().width()-2,geometry().height()-2); + painter.setBrush(p->gradient); + painter.drawRect(1,1,geometry().width()-2,geometry().height()-2); + + /// \todo Take orientation into account + int i = 0; + for ( const QGradientStop& stop : p->stops ) + { + QColor color = stop.second; + Qt::GlobalColor border_color = Qt::black; + Qt::GlobalColor core_color = Qt::white; + + if ( color.valueF() <= 0.5 && color.alphaF() >= 0.5 ) + std::swap(core_color, border_color); + + QPointF p1 = QPointF(p->paint_pos(stop, this), 2.5); + QPointF p2 = p1 + QPointF(0, geometry().height() - 5); + if ( i == p->selected ) + { + painter.setPen(QPen(border_color, 5)); + painter.drawLine(p1, p2); + painter.setPen(QPen(core_color, 3)); + painter.drawLine(p1, p2); + } + else if ( i == p->highlighted ) + { + painter.setPen(QPen(border_color, 3)); + painter.drawLine(p1, p2); + painter.setPen(QPen(core_color, 1)); + painter.drawLine(p1, p2); + } + else + { + painter.setPen(QPen(border_color, 3)); + painter.drawLine(p1, p2); + } + + i++; + } + + if ( p->drop_index != -1 && p->drop_color.isValid() ) + { + qreal pos = p->drop_pos * (geometry().width() - 5); + painter.setPen(QPen(p->drop_color, 3)); + QPointF p1 = QPointF(2.5, 2.5) + QPointF(pos, 0); + QPointF p2 = p1 + QPointF(0, geometry().height() - 5); + painter.drawLine(p1, p2); + } + +} + +QSize GradientEditor::sizeHint() const +{ + QStyleOptionSlider opt; + opt.orientation = p->orientation; + + int w = style()->pixelMetric(QStyle::PM_SliderThickness, &opt, this); + int h = std::max(84, style()->pixelMetric(QStyle::PM_SliderLength, &opt, this)); + if ( p->orientation == Qt::Horizontal ) + { + std::swap(w, h); + } + QSlider s; + return style()->sizeFromContents(QStyle::CT_Slider, &opt, QSize(w, h), &s) + .expandedTo(QApplication::globalStrut()); +} + +int GradientEditor::selectedStop() const +{ + return p->selected; +} + +void GradientEditor::setSelectedStop(int stop) +{ + if ( stop >= -1 && stop < p->stops.size() ) + { + p->selected = stop; + emit selectedStopChanged(p->selected); + } +} + +QColor GradientEditor::selectedColor() const +{ + if ( p->selected != -1 ) + return p->stops[p->selected].second; + return {}; +} + +void GradientEditor::setSelectedColor(const QColor& color) +{ + if ( p->selected != -1 ) + { + p->stops[p->selected].second = color; + p->refresh_gradient(); + update(); + } +} + + +void GradientEditor::dragEnterEvent(QDragEnterEvent *event) +{ + p->drop_event(event, this); + + if ( p->drop_color.isValid() && p->drop_index != -1 ) + { + event->setDropAction(Qt::CopyAction); + event->accept(); + } +} + +void GradientEditor::dragMoveEvent(QDragMoveEvent* event) +{ + p->drop_event(event, this); +} + +void GradientEditor::dragLeaveEvent(QDragLeaveEvent *) +{ + p->clear_drop(this); +} + +void GradientEditor::dropEvent(QDropEvent *event) +{ + p->drop_event(event, this); + + if ( !p->drop_color.isValid() || p->drop_index == -1 ) + return; + + p->stops.insert(p->drop_index, {p->drop_pos, p->drop_color}); + p->refresh_gradient(); + p->selected = p->drop_index; + event->accept(); + p->clear_drop(this); + emit selectedStopChanged(p->selected); +} + +void GradientEditor::addStop() +{ + int index = -1; + qreal pos = 0; + QColor color; + p->add_stop_data(index, pos, color); + p->stops.insert(index, {pos, color}); + p->selected = p->highlighted = index; + p->refresh_gradient(); + update(); + emit selectedStopChanged(p->selected); +} + +void GradientEditor::removeStop() +{ + if ( p->stops.size() < 2 ) + return; + + int selected = p->selected; + if ( selected == -1 ) + selected = p->stops.size() - 1; + p->stops.remove(selected); + p->refresh_gradient(); + + if ( p->selected != -1 ) + { + p->selected = -1; + emit selectedStopChanged(p->selected); + } + + p->dialog_selected = -1; + + update(); + +} + +ColorDialog * GradientEditor::dialog() const +{ + return &p->color_dialog; +} + + +} // namespace color_widgets diff --git a/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/gradient_list_model.cpp b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/gradient_list_model.cpp new file mode 100644 index 00000000..4186c486 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/gradient_list_model.cpp @@ -0,0 +1,319 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ + +#include "QtColorWidgets/gradient_list_model.hpp" + +#include +#include +#include + +using namespace color_widgets; + +class GradientListModel::Private +{ +public: + struct Gradient + { + QLinearGradient gradient; + QString name; + }; + + QVector gradients; + QSize icon_size{48, 32}; + QBrush background; + ItemEditMode edit_mode = EditNone; + + Private() + { + background.setTexture(QPixmap(QStringLiteral(":/color_widgets/alphaback.png"))); + } + + int find(const QString& name) + { + for ( int i = 0; i < gradients.size(); i++ ) + if ( gradients[0].name == name ) + return i; + return -1; + } + + bool contains(const QString& name) + { + return find(name) != -1; + } + + bool acceptable(const QModelIndex& index) const + { + return acceptable(index.row()); + } + + bool acceptable(int row) const + { + return row >= 0 && row < gradients.size(); + } + + QPixmap preview(const QLinearGradient& grad) + { + QPixmap out (icon_size); + QPainter painter(&out); + QRect r({0, 0}, icon_size); + painter.fillRect(r, background); + painter.fillRect(r, grad); + return out; + } + + QLinearGradient make_gradient(const QGradientStops& gradient_stops) + { + QLinearGradient gradient(0, 0, 1, 0); + gradient.setCoordinateMode(QGradient::StretchToDeviceMode); + gradient.setSpread(QGradient::RepeatSpread); + gradient.setStops(gradient_stops); + return gradient; + } +}; + +GradientListModel::GradientListModel(QObject *parent) + : QAbstractListModel(parent), d(new Private()) +{ +} + +GradientListModel::~GradientListModel() = default; + + +int color_widgets::GradientListModel::count() const +{ + return d->gradients.size(); +} + +void color_widgets::GradientListModel::clear() +{ + beginResetModel(); + d->gradients.clear(); + endResetModel(); +} + +QSize color_widgets::GradientListModel::iconSize() const +{ + return d->icon_size; +} + +void color_widgets::GradientListModel::setIconSize ( const QSize& iconSize ) +{ + d->icon_size = iconSize; + Q_EMIT iconSizeChanged(d->icon_size); +} + +int color_widgets::GradientListModel::setGradient ( const QString& name, const QGradient& gradient ) +{ + return setGradient(name, gradient.stops()); +} + + +int color_widgets::GradientListModel::setGradient ( const QString& name, const QGradientStops& gradient_stops ) +{ + int index = d->find(name); + if ( index != -1 ) + { + return setGradient(index, gradient_stops); + } + + index = d->gradients.size(); + beginInsertRows(QModelIndex(), index, index); + d->gradients.push_back({d->make_gradient(gradient_stops), name}); + endInsertRows(); + return index; +} + +bool color_widgets::GradientListModel::setGradient(int index, const QGradient& gradient) +{ + return setGradient(index, gradient.stops()); +} + +bool color_widgets::GradientListModel::setGradient(int index, const QGradientStops& gradient_stops) +{ + if ( index < 0 || index > d->gradients.size() ) + return false; + + d->gradients[index].gradient.setStops(gradient_stops); + QModelIndex mindex = createIndex(index, 0); + Q_EMIT dataChanged(mindex, mindex, {Qt::DecorationRole, Qt::ToolTipRole}); + return true; +} + + + +QGradientStops color_widgets::GradientListModel::gradientStops ( const QString& name ) const +{ + auto iter = d->find(name); + if ( iter != -1 ) + return d->gradients[iter].gradient.stops(); + return {}; +} + +QGradientStops color_widgets::GradientListModel::gradientStops ( int index ) const +{ + if ( d->acceptable(index) ) + return d->gradients[index].gradient.stops(); + return {}; +} + +const QLinearGradient & color_widgets::GradientListModel::gradient ( int index ) const +{ + return d->gradients[index].gradient; +} + +const QLinearGradient & color_widgets::GradientListModel::gradient ( const QString& name ) const +{ + return d->gradients[d->find(name)].gradient; +} + +int color_widgets::GradientListModel::indexFromName ( const QString& name ) const +{ + return d->find(name); +} + +int color_widgets::GradientListModel::rowCount ( const QModelIndex& ) const +{ + return d->gradients.size(); +} + +bool color_widgets::GradientListModel::hasGradient ( const QString& name ) const +{ + return d->contains(name); +} + +bool color_widgets::GradientListModel::removeGradient ( int index ) +{ + if ( !d->acceptable(index) ) + return false; + + beginRemoveRows(QModelIndex{}, index, index); + d->gradients.erase(d->gradients.begin() + index); + endRemoveRows(); + return true; +} + +bool color_widgets::GradientListModel::removeGradient ( const QString& name ) +{ + return removeGradient(d->find(name)); +} + +QVariant color_widgets::GradientListModel::data ( const QModelIndex& index, int role ) const +{ + if ( !d->acceptable(index) ) + return QVariant(); + + + const auto& gradient = d->gradients[index.row()]; + switch( role ) + { + case Qt::DisplayRole: + return gradient.name; + case Qt::DecorationRole: + return d->preview(gradient.gradient); + case Qt::ToolTipRole: + return tr("%1 (%2 colors)").arg(gradient.name).arg(gradient.gradient.stops().size()); + case Qt::EditRole: + if ( d->edit_mode == EditGradient ) + return QBrush(gradient.gradient); + else if ( d->edit_mode == EditName ) + return gradient.name; + return {}; + } + + return QVariant(); +} + +bool color_widgets::GradientListModel::rename(int index, const QString& new_name) +{ + if ( !d->acceptable(index) || d->contains(new_name) ) + return false; + + QModelIndex mindex = createIndex(index, 0); + d->gradients[index].name = new_name; + Q_EMIT dataChanged(mindex, mindex, {Qt::DisplayRole, Qt::ToolTipRole}); + return true; +} + +bool color_widgets::GradientListModel::rename(const QString& old_name, const QString& new_name) +{ + return rename(d->find(old_name), new_name); +} + +Qt::ItemFlags color_widgets::GradientListModel::flags(const QModelIndex& index) const +{ + auto flags = QAbstractListModel::flags(index); + if ( d->edit_mode ) + flags |= Qt::ItemIsEditable; + return flags; +} + +bool color_widgets::GradientListModel::setData(const QModelIndex& index, const QVariant& value, int role) +{ + if ( !d->acceptable(index) ) + return false; + + if ( role == Qt::DisplayRole ) + { + return rename(index.row(), value.toString()); + } + else if ( role == Qt::EditRole ) + { + if ( d->edit_mode == EditName ) + return rename(index.row(), value.toString()); + + if ( d->edit_mode == EditGradient ) + { + const QGradient* grad = value.value().gradient(); + if ( !grad ) + return false; + return setGradient(index.row(), *grad); + } + } + + return false; +} + +color_widgets::GradientListModel::ItemEditMode color_widgets::GradientListModel::editMode() const +{ + return d->edit_mode; +} + +void color_widgets::GradientListModel::setEditMode(color_widgets::GradientListModel::ItemEditMode mode) +{ + d->edit_mode = mode; + Q_EMIT editModeChanged(mode); +} + +QBrush color_widgets::GradientListModel::gradientBrush(int index) const +{ + if ( d->acceptable(index) ) + return QBrush(d->gradients[index].gradient); + return {}; +} + +QString color_widgets::GradientListModel::nameFromIndex(int index) const +{ + if ( d->acceptable(index) ) + return d->gradients[index].name; + return {}; +} + diff --git a/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/gradient_slider.cpp b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/gradient_slider.cpp new file mode 100644 index 00000000..44f378dd --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/gradient_slider.cpp @@ -0,0 +1,276 @@ +/** + * \file gradient_slider.cpp + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * \copyright Copyright (C) 2014 Calle Laakkonen + * \copyright Copyright (C) 2017 caryoscelus + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#include "QtColorWidgets/gradient_slider.hpp" + +#include +#include +#include +#include +#include + +static void loadResource() +{ + static bool loaded = false; + if ( !loaded ) + { + Q_INIT_RESOURCE(color_widgets); + loaded = true; + } +} + +namespace color_widgets { + +class GradientSlider::Private +{ +public: + QLinearGradient gradient; + QBrush back; + + Private() : + back(Qt::darkGray, Qt::DiagCrossPattern) + { + loadResource(); + back.setTexture(QPixmap(QStringLiteral(":/color_widgets/alphaback.png"))); + gradient.setCoordinateMode(QGradient::StretchToDeviceMode); + gradient.setSpread(QGradient::RepeatSpread); + } + + void mouse_event(QMouseEvent *ev, GradientSlider* owner) + { + qreal pos = (owner->geometry().width() > 5) ? + static_cast(ev->pos().x() - 2.5) / (owner->geometry().width() - 5) : 0; + pos = qMax(qMin(pos, 1.0), 0.0); + owner->setSliderPosition(qRound(owner->minimum() + + pos * (owner->maximum() - owner->minimum()))); + } + +}; + +GradientSlider::GradientSlider(QWidget *parent) : + GradientSlider(Qt::Horizontal, parent) +{} + +GradientSlider::GradientSlider(Qt::Orientation orientation, QWidget *parent) : + QSlider(orientation, parent), p(new Private) +{ + setTickPosition(NoTicks); +} + +GradientSlider::~GradientSlider() +{ + delete p; +} + +void GradientSlider::mousePressEvent(QMouseEvent *ev) +{ + if ( ev->button() == Qt::LeftButton ) + { + ev->accept(); + setSliderDown(true); + p->mouse_event(ev, this); + update(); + } + else + { + QSlider::mousePressEvent(ev); + } +} + +void GradientSlider::mouseMoveEvent(QMouseEvent *ev) +{ + if ( ev->buttons() & Qt::LeftButton ) + { + ev->accept(); + p->mouse_event(ev, this); + update(); + } + else + { + QSlider::mouseMoveEvent(ev); + } +} + +void GradientSlider::mouseReleaseEvent(QMouseEvent *ev) +{ + if ( ev->button() == Qt::LeftButton ) + { + ev->accept(); + setSliderDown(false); + update(); + } + else + { + QSlider::mousePressEvent(ev); + } +} + +QBrush GradientSlider::background() const +{ + return p->back; +} + +void GradientSlider::setBackground(const QBrush &bg) +{ + p->back = bg; + update(); + Q_EMIT backgroundChanged(bg); +} + +QGradientStops GradientSlider::colors() const +{ + return p->gradient.stops(); +} + +void GradientSlider::setColors(const QGradientStops &colors) +{ + p->gradient.setStops(colors); + update(); +} + +QLinearGradient GradientSlider::gradient() const +{ + return p->gradient; +} + +void GradientSlider::setGradient(const QLinearGradient &gradient) +{ + p->gradient = gradient; + update(); +} + +void GradientSlider::setColors(const QVector &colors) +{ + QGradientStops stops; + stops.reserve(colors.size()); + + double c = colors.size() - 1; + if(c==0) { + stops.append(QGradientStop(0, colors.at(0))); + + } else { + for(int i=0;igradient.stops(); + if(stops.isEmpty()) + stops.push_back(QGradientStop(0.0, c)); + else + stops.front().second = c; + p->gradient.setStops(stops); + + update(); +} + +void GradientSlider::setLastColor(const QColor &c) +{ + QGradientStops stops = p->gradient.stops(); + if(stops.size()<2) + stops.push_back(QGradientStop(1.0, c)); + else + stops.back().second = c; + p->gradient.setStops(stops); + update(); +} + +QColor GradientSlider::firstColor() const +{ + QGradientStops s = colors(); + return s.empty() ? QColor() : s.front().second; +} + +QColor GradientSlider::lastColor() const +{ + QGradientStops s = colors(); + return s.empty() ? QColor() : s.back().second; +} + +void GradientSlider::paintEvent(QPaintEvent *) +{ + QPainter painter(this); + + QStyleOptionFrame panel; + panel.initFrom(this); + panel.lineWidth = 1; + panel.midLineWidth = 0; + panel.state |= QStyle::State_Sunken; + style()->drawPrimitive(QStyle::PE_Frame, &panel, &painter, this); + QRect r = style()->subElementRect(QStyle::SE_FrameContents, &panel, this); + painter.setClipRect(r); + + qreal gradient_direction = invertedAppearance() ? -1 : 1; + + if(orientation() == Qt::Horizontal) + p->gradient.setFinalStop(gradient_direction, 0); + else + p->gradient.setFinalStop(0, -gradient_direction); + + painter.setPen(Qt::NoPen); + painter.setBrush(p->back); + painter.drawRect(1,1,geometry().width()-2,geometry().height()-2); + painter.setBrush(p->gradient); + painter.drawRect(1,1,geometry().width()-2,geometry().height()-2); + + qreal pos = (maximum() != 0) ? + static_cast(value() - minimum()) / maximum() : 0; + QColor color; + auto stops = p->gradient.stops(); + int i; + for (i = 0; i < stops.size(); i++) { + if (stops[i].first > pos) + break; + } + if (i == 0) { + color = firstColor(); + } if (i == stops.size()) { + color = lastColor(); + } else { + auto &a = stops[i - 1]; + auto &b = stops[i]; + auto c = (b.first - a.first); + qreal q = (c != 0) ? + (pos - a.first) / c : 0; + color = QColor::fromRgbF(b.second.redF() * q + a.second.redF() * (1.0 - q), + b.second.greenF() * q + a.second.greenF() * (1.0 - q), + b.second.blueF() * q + a.second.blueF() * (1.0 - q), + b.second.alphaF() * q + a.second.alphaF() * (1.0 - q)); + } + + pos = pos * (geometry().width() - 5); + if (color.valueF() > 0.5 || color.alphaF() < 0.5) { + painter.setPen(QPen(Qt::black, 3)); + } else { + painter.setPen(QPen(Qt::white, 3)); + } + QPointF p1 = QPointF(2.5, 2.5) + QPointF(pos, 0); + QPointF p2 = p1 + QPointF(0, geometry().height() - 5); + painter.drawLine(p1, p2); +} + +} // namespace color_widgets diff --git a/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/harmony_color_wheel.cpp b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/harmony_color_wheel.cpp new file mode 100644 index 00000000..f38ab3d9 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/harmony_color_wheel.cpp @@ -0,0 +1,207 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * \copyright Copyright (C) 2017 caryoscelus + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#include "QtColorWidgets/color_wheel_private.hpp" +#include "QtColorWidgets/harmony_color_wheel.hpp" + +namespace color_widgets { + +struct RingEditor +{ + double hue_diff; + bool editable; + int symmetric_to; + int opposite_to; + RingEditor(double hue_diff, bool editable, int symmetric_to=-1, int opposite_to=-1) : + hue_diff(hue_diff), + editable(editable), + symmetric_to(symmetric_to), + opposite_to(opposite_to) + { + } +}; + +class HarmonyColorWheel::Private : public ColorWheel::Private +{ +public: + using ColorWheel::Private::Private; + + std::vector ring_editors; + int current_ring_editor = -1; + + /** + * Puts a double into [0; 1) range + */ + static inline double normalize(double angle) + { + return angle - std::floor(angle); + } +}; + + +HarmonyColorWheel::HarmonyColorWheel(QWidget *parent) : + ColorWheel(parent, new Private(this)) +{ + connect(this, SIGNAL(colorChanged(QColor)), this, SIGNAL(harmonyChanged())); + p = static_cast(data()); +} + +HarmonyColorWheel::~HarmonyColorWheel() = default; + +QList HarmonyColorWheel::harmonyColors() const +{ + QList result; + result.push_back(color()); + for (auto const& harmony : p->ring_editors) + { + auto hue = Private::normalize(p->hue+harmony.hue_diff); + result.push_back(p->color_from(hue, p->sat, p->val, 1)); + } + return result; +} + +unsigned int HarmonyColorWheel::harmonyCount() const +{ + return 1 + p->ring_editors.size(); +} + +void HarmonyColorWheel::clearHarmonies() +{ + p->ring_editors.clear(); + p->current_ring_editor = -1; + Q_EMIT harmonyChanged(); + update(); +} + +unsigned HarmonyColorWheel::addHarmony(double hue_diff, bool editable) +{ + auto count = p->ring_editors.size(); + p->ring_editors.emplace_back(Private::normalize(hue_diff), editable, -1, -1); + Q_EMIT harmonyChanged(); + update(); + return count; +} + +unsigned HarmonyColorWheel::addSymmetricHarmony(unsigned relative_to) +{ + auto count = p->ring_editors.size(); + if (relative_to >= count) + throw std::out_of_range("incorrect call to addSymmetricHarmony: harmony number out of range"); + auto& relative = p->ring_editors[relative_to]; + relative.symmetric_to = count; + p->ring_editors.emplace_back(Private::normalize(-relative.hue_diff), relative.editable, relative_to, -1); + Q_EMIT harmonyChanged(); + update(); + return count; +} + +unsigned HarmonyColorWheel::addOppositeHarmony(unsigned relative_to) +{ + auto count = p->ring_editors.size(); + if (relative_to >= count) + throw std::out_of_range("incorrect call to addOppositeHarmony: harmony number out of range"); + auto& relative = p->ring_editors[relative_to]; + relative.opposite_to = count; + p->ring_editors.emplace_back(Private::normalize(0.5+relative.hue_diff), relative.editable, -1, relative_to); + Q_EMIT harmonyChanged(); + update(); + return count; +} + +void HarmonyColorWheel::paintEvent(QPaintEvent * ev) +{ + ColorWheel::paintEvent(ev); + + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + painter.translate(geometry().width()/2,geometry().height()/2); + + for (auto const& editor : p->ring_editors) + { + auto hue = p->hue+editor.hue_diff; + // TODO: better color for uneditable indicator + auto color = editor.editable ? Qt::white : Qt::gray; + p->draw_ring_editor(hue, painter, color); + } +} + + +void HarmonyColorWheel::mousePressEvent(QMouseEvent *ev) +{ + if ( ev->buttons() & Qt::LeftButton ) + { + QLineF ray = p->line_to_point(ev->pos()); + if ( ray.length() <= p->outer_radius() && ray.length() > p->inner_radius() ) + { + p->mouse_status = DragCircle; + auto hue_diff = Private::normalize(ray.angle()/360 - p->hue); + auto i = 0; + for (auto const& editor : p->ring_editors) + { + const double eps = 1.0/64; + if (editor.editable && + editor.hue_diff <= hue_diff + eps && + editor.hue_diff >= hue_diff - eps) + { + p->current_ring_editor = i; + // no need to update color.. + return; + } + ++i; + } + } + } + ColorWheel::mousePressEvent(ev); +} + + +void HarmonyColorWheel::mouseMoveEvent(QMouseEvent *ev) +{ + if ( p->mouse_status == DragCircle && p->current_ring_editor != -1 ) + { + auto hue = p->line_to_point(ev->pos()).angle()/360.0; + auto& editor = p->ring_editors[p->current_ring_editor]; + editor.hue_diff = Private::normalize(hue - p->hue); + if (editor.symmetric_to != -1) + { + auto& symmetric = p->ring_editors[editor.symmetric_to]; + symmetric.hue_diff = Private::normalize(p->hue - hue); + } + else if (editor.opposite_to != -1) + { + auto& opposite = p->ring_editors[editor.opposite_to]; + opposite.hue_diff = Private::normalize(editor.hue_diff-0.5); + } + Q_EMIT harmonyChanged(); + update(); + return; + } + ColorWheel::mouseMoveEvent(ev); +} + +void HarmonyColorWheel::mouseReleaseEvent(QMouseEvent *ev) +{ + ColorWheel::mouseReleaseEvent(ev); + p->current_ring_editor = -1; +} + +} // namespace color_widgets diff --git a/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/hue_slider.cpp b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/hue_slider.cpp new file mode 100644 index 00000000..d95aeac6 --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/hue_slider.cpp @@ -0,0 +1,153 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2014 Calle Laakkonen + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * \copyright Copyright (C) 2017 caryoscelus + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#include "QtColorWidgets/hue_slider.hpp" + +namespace color_widgets { + +class HueSlider::Private +{ +private: + HueSlider *w; + +public: + qreal saturation = 1; + qreal value = 1; + qreal alpha = 1; + + Private(HueSlider *widget) + : w(widget) + { + w->setRange(0, 359); + connect(w, &QSlider::valueChanged, [this]{ + Q_EMIT w->colorHueChanged(w->colorHue()); + Q_EMIT w->colorChanged(w->color()); + }); + updateGradient(); + } + + void updateGradient() + { + static const double n_colors = 6; + QGradientStops colors; + colors.reserve(n_colors+1); + for ( int i = 0; i <= n_colors; ++i ) + colors.append(QGradientStop(i/n_colors, QColor::fromHsvF(i/n_colors, saturation, value))); + w->setColors(colors); + } +}; + +HueSlider::HueSlider(QWidget *parent) : + GradientSlider(parent), p(new Private(this)) +{ +} + +HueSlider::HueSlider(Qt::Orientation orientation, QWidget *parent) : + GradientSlider(orientation, parent), p(new Private(this)) +{ +} + +HueSlider::~HueSlider() +{ + delete p; +} + +qreal HueSlider::colorSaturation() const +{ + return p->saturation; +} + +void HueSlider::setColorSaturation(qreal s) +{ + p->saturation = qBound(0.0, s, 1.0); + p->updateGradient(); + Q_EMIT colorSaturationChanged(s); +} + +qreal HueSlider::colorValue() const +{ + return p->value; +} + +void HueSlider::setColorValue(qreal v) +{ + p->value = qBound(0.0, v, 1.0); + p->updateGradient(); + Q_EMIT colorValueChanged(v); +} + +qreal HueSlider::colorAlpha() const +{ + return p->alpha; +} + +void HueSlider::setColorAlpha(qreal alpha) +{ + p->alpha = alpha; + p->updateGradient(); + Q_EMIT colorAlphaChanged(alpha); +} + +QColor HueSlider::color() const +{ + return QColor::fromHsvF(colorHue(), p->saturation, p->value, p->alpha); +} + +void HueSlider::setColor(const QColor& color) +{ + p->saturation = color.saturationF(); + p->value = color.valueF(); + p->updateGradient(); + setColorHue(color.hueF()); + Q_EMIT colorValueChanged(p->alpha); + Q_EMIT colorSaturationChanged(p->alpha); +} + +void HueSlider::setFullColor(const QColor& color) +{ + p->alpha = color.alphaF(); + setColor(color); + Q_EMIT colorAlphaChanged(p->alpha); +} + +qreal HueSlider::colorHue() const +{ + if (maximum() == minimum()) + return 0; + auto hue = qreal(value() - minimum()) / (maximum() - minimum()); + if (orientation() == Qt::Vertical) + hue = 1 - hue; + return hue; +} + +void HueSlider::setColorHue(qreal colorHue) +{ + // TODO: consider supporting invertedAppearance? + if (orientation() == Qt::Vertical) + colorHue = 1 - colorHue; + setValue(minimum()+colorHue*(maximum()-minimum())); + Q_EMIT colorHueChanged(colorHue); + Q_EMIT colorChanged(color()); +} + +} // namespace color_widgets diff --git a/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/swatch.cpp b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/swatch.cpp new file mode 100644 index 00000000..bc68440d --- /dev/null +++ b/thirdparty/Qt-Color-Widgets/src/QtColorWidgets/swatch.cpp @@ -0,0 +1,792 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2020 Mattia Basaglia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#include "QtColorWidgets/swatch.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace color_widgets { + +class Swatch::Private +{ +public: + ColorPalette palette; ///< Palette with colors and related metadata + int selected; ///< Current selection index (-1 for no selection) + QSize color_size; ///< Preferred size for the color squares + ColorSizePolicy size_policy; + QPen border; + int forced_rows; + int forced_columns; + bool readonly; ///< Whether the palette can be modified via user interaction + + QPoint drag_pos; ///< Point used to keep track of dragging + int drag_index; ///< Index used by drags + int drop_index; ///< Index for a requested drop + QColor drop_color; ///< Dropped color + bool drop_overwrite; ///< Whether the drop will overwrite an existing color + + Swatch* owner; + + Private(Swatch* owner) + : selected(-1), + color_size(16,16), + size_policy(Hint), + border(Qt::black, 1), + forced_rows(0), + forced_columns(0), + readonly(false), + drag_index(-1), + drop_index(-1), + drop_overwrite(false), + owner(owner) + {} + + /** + * \brief Number of rows/columns in the palette + */ + QSize rowcols() + { + int count = palette.count(); + if ( count == 0 ) + return QSize(); + + if ( forced_rows ) + return QSize(std::ceil( float(count) / forced_rows ), forced_rows); + + int columns = palette.columns(); + + if ( forced_columns ) + columns = forced_columns; + else if ( columns == 0 ) + columns = qMin(palette.count(), owner->width() / color_size.width()); + + int rows = std::ceil( float(count) / columns ); + + return QSize(columns, rows); + } + + /** + * \brief Sets the drop properties + */ + void dropEvent(QDropEvent* event) + { + // Find the output location + drop_index = owner->indexAt(event->pos()); + if ( drop_index == -1 ) + drop_index = palette.count(); + + // Gather up the color + if ( event->mimeData()->hasColor() ) + { + drop_color = event->mimeData()->colorData().value(); + drop_color.setAlpha(255); + } + else if ( event->mimeData()->hasText() ) + { + drop_color = QColor(event->mimeData()->text()); + } + + drop_overwrite = false; + QRectF drop_rect = indexRect(drop_index); + if ( drop_index < palette.count() && drop_rect.isValid() ) + { + // 1 column => vertical style + if ( palette.columns() == 1 || forced_columns == 1 ) + { + // Dragged to the last quarter of the size of the square, add after + if ( event->posF().y() >= drop_rect.top() + drop_rect.height() * 3.0 / 4 ) + drop_index++; + // Dragged to the middle of the square, overwrite existing color + else if ( event->posF().x() > drop_rect.top() + drop_rect.height() / 4 && + ( event->dropAction() != Qt::MoveAction || event->source() != owner ) ) + drop_overwrite = true; + } + else + { + // Dragged to the last quarter of the size of the square, add after + if ( event->posF().x() >= drop_rect.left() + drop_rect.width() * 3.0 / 4 ) + drop_index++; + // Dragged to the middle of the square, overwrite existing color + else if ( event->posF().x() > drop_rect.left() + drop_rect.width() / 4 && + ( event->dropAction() != Qt::MoveAction || event->source() != owner ) ) + drop_overwrite = true; + } + } + + owner->update(); + } + + /** + * \brief Clears drop properties + */ + void clearDrop() + { + drop_index = -1; + drop_color = QColor(); + drop_overwrite = false; + + owner->update(); + } + + /** + * \brief Actual size of a color square + */ + QSizeF actualColorSize() + { + QSize rowcols = this->rowcols(); + if ( !rowcols.isValid() ) + return QSizeF(); + return actualColorSize(rowcols); + } + + /** + * \brief Actual size of a color square + * \pre rowcols.isValid() and obtained via rowcols() + */ + QSizeF actualColorSize(const QSize& rowcols) + { + return QSizeF (float(owner->width()) / rowcols.width(), + float(owner->height()) / rowcols.height()); + } + + + /** + * \brief Rectangle corresponding to the color at the given index + * \pre rowcols.isValid() and obtained via rowcols() + * \pre color_size obtained via rowlcols(rowcols) + */ + QRectF indexRect(int index, const QSize& rowcols, const QSizeF& color_size) + { + if ( index == -1 ) + return QRectF(); + + return QRectF( + index % rowcols.width() * color_size.width(), + index / rowcols.width() * color_size.height(), + color_size.width(), + color_size.height() + ); + } + /** + * \brief Rectangle corresponding to the color at the given index + */ + QRectF indexRect(int index) + { + QSize rc = rowcols(); + if ( index == -1 || !rc.isValid() ) + return QRectF(); + return indexRect(index, rc, actualColorSize(rc)); + } +}; + +Swatch::Swatch(QWidget* parent) + : QWidget(parent), p(new Private(this)) +{ + connect(&p->palette, &ColorPalette::colorsChanged, this, &Swatch::paletteModified); + connect(&p->palette, &ColorPalette::colorAdded, this, &Swatch::paletteModified); + connect(&p->palette, &ColorPalette::colorRemoved, this, &Swatch::paletteModified); + connect(&p->palette, &ColorPalette::columnsChanged, this, (void(QWidget::*)())&QWidget::update); + connect(&p->palette, &ColorPalette::colorsUpdated, this, (void(QWidget::*)())&QWidget::update); + connect(&p->palette, &ColorPalette::colorChanged, [this](int index){ + if ( index == p->selected ) + Q_EMIT colorSelected( p->palette.colorAt(index) ); + }); + setFocusPolicy(Qt::StrongFocus); + setAcceptDrops(true); + setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + setAttribute(Qt::WA_Hover, true); +} + +Swatch::~Swatch() +{ + delete p; +} + +QSize Swatch::sizeHint() const +{ + QSize rowcols = p->rowcols(); + + if ( !p->color_size.isValid() || !rowcols.isValid() ) + return QSize(); + + return QSize( + p->color_size.width() * rowcols.width(), + p->color_size.height() * rowcols.height() + ); +} + +QSize Swatch::minimumSizeHint() const +{ + if ( p->size_policy != Hint ) + return sizeHint(); + return QSize(); +} + +const ColorPalette& Swatch::palette() const +{ + return p->palette; +} + +ColorPalette& Swatch::palette() +{ + return p->palette; +} + +int Swatch::selected() const +{ + return p->selected; +} + +QColor Swatch::selectedColor() const +{ + return p->palette.colorAt(p->selected); +} + +int Swatch::indexAt(const QPoint& pt) +{ + QSize rowcols = p->rowcols(); + if ( rowcols.isEmpty() ) + return -1; + + QSizeF color_size = p->actualColorSize(rowcols); + + QPoint point( + qBound(0, pt.x() / color_size.width(), rowcols.width() - 1), + qBound(0, pt.y() / color_size.height(), rowcols.height() - 1) + ); + + int index = point.y() * rowcols.width() + point.x(); + if ( index >= p->palette.count() ) + return -1; + return index; +} + +QColor Swatch::colorAt(const QPoint& pt) +{ + return p->palette.colorAt(indexAt(pt)); +} + +void Swatch::setPalette(const ColorPalette& palette) +{ + clearSelection(); + p->palette = palette; + update(); + Q_EMIT paletteChanged(p->palette); +} + +void Swatch::setSelected(int selected) +{ + if ( selected < 0 || selected >= p->palette.count() ) + selected = -1; + + if ( selected != p->selected ) + { + Q_EMIT selectedChanged( p->selected = selected ); + if ( selected != -1 ) + Q_EMIT colorSelected( p->palette.colorAt(p->selected) ); + update(); + } +} + +void Swatch::clearSelection() +{ + setSelected(-1); +} + +void Swatch::paintEvent(QPaintEvent* event) +{ + Q_UNUSED(event) + QSize rowcols = p->rowcols(); + if ( rowcols.isEmpty() ) + return; + + QSizeF color_size = p->actualColorSize(rowcols); + QPainter painter(this); + + QStyleOptionFrame panel; + panel.initFrom(this); + panel.lineWidth = 1; + panel.midLineWidth = 0; + panel.state |= QStyle::State_Sunken; + style()->drawPrimitive(QStyle::PE_Frame, &panel, &painter, this); + QRect r = style()->subElementRect(QStyle::SE_FrameContents, &panel, this); + painter.setClipRect(r); + + int count = p->palette.count(); + painter.setPen(p->border); + for ( int y = 0, i = 0; i < count; y++ ) + { + for ( int x = 0; x < rowcols.width() && i < count; x++, i++ ) + { + painter.setBrush(p->palette.colorAt(i)); + painter.drawRect(p->indexRect(i, rowcols, color_size)); + } + } + + painter.setClipping(false); + + if ( p->drop_index != -1 ) + { + QRectF drop_area = p->indexRect(p->drop_index, rowcols, color_size); + if ( p->drop_overwrite ) + { + painter.setBrush(p->drop_color); + painter.setPen(QPen(Qt::gray)); + painter.drawRect(drop_area); + } + else if ( rowcols.width() == 1 ) + { + // 1 column => vertical style + painter.setPen(QPen(p->drop_color, 2)); + painter.setBrush(Qt::transparent); + painter.drawLine(drop_area.topLeft(), drop_area.topRight()); + } + else + { + painter.setPen(QPen(p->drop_color, 2)); + painter.setBrush(Qt::transparent); + painter.drawLine(drop_area.topLeft(), drop_area.bottomLeft()); + // Draw also on the previous line when the first item of a line is selected + if ( p->drop_index % rowcols.width() == 0 && p->drop_index != 0 ) + { + drop_area = p->indexRect(p->drop_index-1, rowcols, color_size); + drop_area.translate(color_size.width(), 0); + painter.drawLine(drop_area.topLeft(), drop_area.bottomLeft()); + } + } + } + + if ( p->selected != -1 ) + { + QRectF rect = p->indexRect(p->selected, rowcols, color_size); + painter.setBrush(Qt::transparent); + painter.setPen(QPen(Qt::darkGray, 2)); + painter.drawRect(rect); + painter.setPen(QPen(Qt::gray, 2, Qt::DotLine)); + painter.drawRect(rect); + } +} + +void Swatch::keyPressEvent(QKeyEvent* event) +{ + if ( p->palette.count() == 0 ) + QWidget::keyPressEvent(event); + + int selected = p->selected; + int count = p->palette.count(); + QSize rowcols = p->rowcols(); + int columns = rowcols.width(); + int rows = rowcols.height(); + switch ( event->key() ) + { + default: + QWidget::keyPressEvent(event); + return; + + case Qt::Key_Left: + if ( selected == -1 ) + selected = count - 1; + else if ( selected > 0 ) + selected--; + break; + + case Qt::Key_Right: + if ( selected == -1 ) + selected = 0; + else if ( selected < count - 1 ) + selected++; + break; + + case Qt::Key_Up: + if ( selected == -1 ) + selected = count - 1; + else if ( selected >= columns ) + selected -= columns; + break; + + case Qt::Key_Down: + if ( selected == -1 ) + selected = 0; + else if ( selected < count - columns ) + selected += columns; + break; + + case Qt::Key_Home: + if ( event->modifiers() & Qt::ControlModifier ) + selected = 0; + else + selected -= selected % columns; + break; + + case Qt::Key_End: + if ( event->modifiers() & Qt::ControlModifier ) + selected = count - 1; + else + selected += columns - (selected % columns) - 1; + break; + + case Qt::Key_Delete: + removeSelected(); + return; + + case Qt::Key_Backspace: + if (selected != -1 && !p->readonly ) + { + p->palette.eraseColor(selected); + if ( p->palette.count() == 0 ) + selected = -1; + else + selected = qMax(selected - 1, 0); + } + break; + + case Qt::Key_PageUp: + if ( selected == -1 ) + selected = 0; + else + selected = selected % columns; + break; + case Qt::Key_PageDown: + if ( selected == -1 ) + { + selected = count - 1; + } + else + { + selected = columns * (rows-1) + selected % columns; + if ( selected >= count ) + selected -= columns; + } + break; + } + setSelected(selected); +} + +void Swatch::removeSelected() +{ + if (p->selected != -1 && !p->readonly ) + { + int selected = p->selected; + p->palette.eraseColor(p->selected); + setSelected(qMin(selected, p->palette.count() - 1)); + } +} + +void Swatch::mousePressEvent(QMouseEvent *event) +{ + if ( event->button() == Qt::LeftButton ) + { + setSelected(indexAt(event->pos())); + p->drag_pos = event->pos(); + p->drag_index = indexAt(event->pos()); + } + else if ( event->button() == Qt::RightButton ) + { + int index = indexAt(event->pos()); + if ( index != -1 ) + Q_EMIT rightClicked(index); + } +} + +void Swatch::mouseMoveEvent(QMouseEvent *event) +{ + if ( p->drag_index != -1 && (event->buttons() & Qt::LeftButton) && + (p->drag_pos - event->pos()).manhattanLength() >= QApplication::startDragDistance() ) + { + QColor color = p->palette.colorAt(p->drag_index); + + QPixmap preview(24,24); + preview.fill(color); + + QMimeData *mimedata = new QMimeData; + mimedata->setColorData(color); + mimedata->setText(p->palette.nameAt(p->drag_index)); + + QDrag *drag = new QDrag(this); + drag->setMimeData(mimedata); + drag->setPixmap(preview); + Qt::DropActions actions = Qt::CopyAction; + if ( !p->readonly ) + actions |= Qt::MoveAction; + drag->exec(actions); + } +} + +void Swatch::mouseReleaseEvent(QMouseEvent *event) +{ + if ( event->button() == Qt::LeftButton ) + { + p->drag_index = -1; + } +} + +void Swatch::mouseDoubleClickEvent(QMouseEvent *event) +{ + if ( event->button() == Qt::LeftButton ) + { + int index = indexAt(event->pos()); + if ( index != -1 ) + Q_EMIT doubleClicked(index); + } +} + +void Swatch::wheelEvent(QWheelEvent* event) +{ + if ( event->delta() > 0 ) + p->selected = qMin(p->selected + 1, p->palette.count() - 1); + else if ( p->selected == -1 ) + p->selected = p->palette.count() - 1; + else if ( p->selected > 0 ) + p->selected--; + setSelected(p->selected); +} + +void Swatch::dragEnterEvent(QDragEnterEvent *event) +{ + if ( p->readonly ) + return; + + p->dropEvent(event); + + if ( p->drop_color.isValid() && p->drop_index != -1 ) + { + if ( event->proposedAction() == Qt::MoveAction && event->source() == this ) + event->setDropAction(Qt::MoveAction); + else + event->setDropAction(Qt::CopyAction); + + event->accept(); + } +} + +void Swatch::dragMoveEvent(QDragMoveEvent* event) +{ + if ( p->readonly ) + return; + p->dropEvent(event); +} + +void Swatch::dragLeaveEvent(QDragLeaveEvent *event) +{ + Q_UNUSED(event) + p->clearDrop(); +} + +void Swatch::dropEvent(QDropEvent *event) +{ + if ( p->readonly ) + return; + + QString name; + + // Gather up the color + if ( event->mimeData()->hasColor() && event->mimeData()->hasText() ) + name = event->mimeData()->text(); + + // Not a color, discard + if ( !p->drop_color.isValid() || p->drop_index == -1 ) + return; + + p->dropEvent(event); + + // Move unto self + if ( event->dropAction() == Qt::MoveAction && event->source() == this ) + { + // Not moved => noop + if ( p->drop_index != p->drag_index && p->drop_index != p->drag_index + 1 ) + { + // Erase the old color + p->palette.eraseColor(p->drag_index); + if ( p->drop_index > p->drag_index ) + p->drop_index--; + p->selected = p->drop_index; + // Insert the dropped color + p->palette.insertColor(p->drop_index, p->drop_color, name); + } + } + // Move into a color cell + else if ( p->drop_overwrite ) + { + p->palette.setColorAt(p->drop_index, p->drop_color, name); + } + // Insert the dropped color + else + { + p->palette.insertColor(p->drop_index, p->drop_color, name); + } + + // Finalize + event->accept(); + p->drag_index = -1; + p->clearDrop(); +} + +void Swatch::paletteModified() +{ + if ( p->selected >= p->palette.count() ) + clearSelection(); + + if ( p->size_policy != Hint ) + { + QSize size_hint = sizeHint(); + + if ( size_hint.isValid() ) + { + if ( p->size_policy == Minimum ) + setMinimumSize(size_hint); + else if ( p->size_policy == Fixed ) + setFixedSize(size_hint); + } + } + + update(); +} + +QSize Swatch::colorSize() const +{ + return p->color_size; +} + +void Swatch::setColorSize(const QSize& colorSize) +{ + if ( p->color_size != colorSize ) + Q_EMIT colorSizeChanged(p->color_size = colorSize); +} + +Swatch::ColorSizePolicy Swatch::colorSizePolicy() const +{ + return p->size_policy; +} + +void Swatch::setColorSizePolicy(ColorSizePolicy colorSizePolicy) +{ + if ( p->size_policy != colorSizePolicy ) + { + setMinimumSize(0,0); + setFixedSize(QWIDGETSIZE_MAX,QWIDGETSIZE_MAX); + Q_EMIT colorSizePolicyChanged(p->size_policy = colorSizePolicy); + paletteModified(); + } +} + +int Swatch::forcedColumns() const +{ + return p->forced_columns; +} + +int Swatch::forcedRows() const +{ + return p->forced_rows; +} + +void Swatch::setForcedColumns(int forcedColumns) +{ + if ( forcedColumns <= 0 ) + forcedColumns = 0; + + if ( forcedColumns != p->forced_columns ) + { + Q_EMIT forcedColumnsChanged(p->forced_columns = forcedColumns); + Q_EMIT forcedRowsChanged(p->forced_rows = 0); + } +} + +void Swatch::setForcedRows(int forcedRows) +{ + if ( forcedRows <= 0 ) + forcedRows = 0; + + if ( forcedRows != p->forced_rows ) + { + Q_EMIT forcedColumnsChanged(p->forced_columns = 0); + Q_EMIT forcedRowsChanged(p->forced_rows = forcedRows); + } +} + +bool Swatch::readOnly() const +{ + return p->readonly; +} + +void Swatch::setReadOnly(bool readOnly) +{ + if ( readOnly != p->readonly ) + { + Q_EMIT readOnlyChanged(p->readonly = readOnly); + setAcceptDrops(!p->readonly); + } +} + +bool Swatch::event(QEvent* event) +{ + if(event->type() == QEvent::ToolTip) + { + QHelpEvent* help_ev = static_cast(event); + int index = indexAt(help_ev->pos()); + if ( index != -1 ) + { + QColor color = p->palette.colorAt(index); + QString name = p->palette.nameAt(index); + QString message = color.name(); + if ( !name.isEmpty() ) + message = tr("%1 (%2)").arg(name).arg(message); + message = "MM "+message.toHtmlEscaped(); + QToolTip::showText(help_ev->globalPos(), message, this, + p->indexRect(index).toRect()); + event->accept(); + } + else + { + QToolTip::hideText(); + event->ignore(); + } + return true; + } + + return QWidget::event(event); +} + +QPen Swatch::border() const +{ + return p->border; +} + +void Swatch::setBorder(const QPen& border) +{ + if ( border != p->border ) + { + p->border = border; + Q_EMIT borderChanged(border); + update(); + } +} + +} // namespace color_widgets