diff --git a/Qt-Color-Widgets b/Qt-Color-Widgets deleted file mode 160000 index eb6da62d..00000000 --- a/Qt-Color-Widgets +++ /dev/null @@ -1 +0,0 @@ -Subproject commit eb6da62d65c75f40437ed4964a8d4a18850f4d7f diff --git a/Qt-Color-Widgets/COPYING b/Qt-Color-Widgets/COPYING new file mode 100644 index 00000000..65c5ca88 --- /dev/null +++ b/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/Qt-Color-Widgets/LICENSE-EXCEPTION b/Qt-Color-Widgets/LICENSE-EXCEPTION new file mode 100644 index 00000000..06698128 --- /dev/null +++ b/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/Qt-Color-Widgets/README.md b/Qt-Color-Widgets/README.md new file mode 100644 index 00000000..b3c0b2a5 --- /dev/null +++ b/Qt-Color-Widgets/README.md @@ -0,0 +1,64 @@ +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 + +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-qt5. +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 + +cd build && cmake .. && make ColorWidgetsPlugin && make install + + +Latest Version +-------------- + +The latest version of the sources can be found at the following locations: + +* https://github.com/mbasaglia/Qt-Color-Widgets +* git://github.com/mbasaglia/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-2017 Mattia Basaglia diff --git a/Qt-Color-Widgets/color_widgets.pri b/Qt-Color-Widgets/color_widgets.pri new file mode 100644 index 00000000..65e05eeb --- /dev/null +++ b/Qt-Color-Widgets/color_widgets.pri @@ -0,0 +1,32 @@ +# Copyright (C) 2013-2017 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/color_wheel.cpp \ + $$PWD/src/color_utils.cpp + +HEADERS += \ + $$PWD/include/color_wheel.hpp \ + $$PWD/include/colorwidgets_global.hpp \ + $$PWD/src/color_utils.hpp + +RESOURCES += \ + $$PWD/src/color_widgets.qrc + diff --git a/Qt-Color-Widgets/color_widgets.pro b/Qt-Color-Widgets/color_widgets.pro new file mode 100644 index 00000000..4241ac2d --- /dev/null +++ b/Qt-Color-Widgets/color_widgets.pro @@ -0,0 +1,53 @@ +# +# Copyright (C) 2013-2017 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=ColorWidgets-qt5 + +VERSION=1.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/Qt-Color-Widgets/include/ColorWheel b/Qt-Color-Widgets/include/ColorWheel new file mode 100644 index 00000000..ed17f683 --- /dev/null +++ b/Qt-Color-Widgets/include/ColorWheel @@ -0,0 +1 @@ +#include "color_wheel.hpp" diff --git a/Qt-Color-Widgets/include/color_wheel.hpp b/Qt-Color-Widgets/include/color_wheel.hpp new file mode 100644 index 00000000..2725c609 --- /dev/null +++ b/Qt-Color-Widgets/include/color_wheel.hpp @@ -0,0 +1,171 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2017 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 "colorwidgets_global.hpp" + +#include + +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 DESIGNABLE true ) + Q_PROPERTY(DisplayFlags displayFlags READ displayFlags WRITE setDisplayFlags NOTIFY displayFlagsChanged DESIGNABLE true ) + +public: + enum DisplayEnum + { + SHAPE_DEFAULT = 0x000, ///< Use the default shape + SHAPE_TRIANGLE = 0x001, ///< A triangle + SHAPE_SQUARE = 0x002, ///< A square + SHAPE_FLAGS = 0x00f, ///< Mask for the shape flags + + ANGLE_DEFAULT = 0x000, ///< Use the default rotation style + ANGLE_FIXED = 0x010, ///< The inner part doesn't rotate + ANGLE_ROTATING = 0x020, ///< The inner part follows the hue selector + ANGLE_FLAGS = 0x0f0, ///< Mask for the angle flags + + COLOR_DEFAULT = 0x000, ///< Use the default colorspace + COLOR_HSV = 0x100, ///< Use the HSV color space + COLOR_HSL = 0x200, ///< Use the HSL color space + COLOR_LCH = 0x400, ///< Use Luma Chroma Hue (Y_601') + COLOR_FLAGS = 0xf00, ///< Mask for the color space flags + + FLAGS_DEFAULT = 0x000, ///< Use all defaults + FLAGS_ALL = 0xfff ///< Mask matching all flags + }; + Q_DECLARE_FLAGS(DisplayFlags, DisplayEnum) + Q_FLAGS(DisplayFlags) + + 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); + + /// Get display flags + DisplayFlags displayFlags(DisplayFlags mask = FLAGS_ALL) const; + + /// Set the default display flags + static void setDefaultDisplayFlags(DisplayFlags flags); + + /// Get default display flags + static DisplayFlags defaultDisplayFlags(DisplayFlags mask = FLAGS_ALL); + + /** + * @brief Set a specific display flag + * @param flag Flag replacing the mask + * @param mask Mask to be cleared + */ + void setDisplayFlag(DisplayFlags flag, DisplayFlags mask); + +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); + + /** + * @brief Set the display flags + * @param flags which will replace the current ones + */ + void setDisplayFlags(ColorWheel::DisplayFlags flags); + +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 displayFlagsChanged(ColorWheel::DisplayFlags flags); + + void mouseReleaseOnColor(QColor); + +protected: + void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE; + void mouseMoveEvent(QMouseEvent *) Q_DECL_OVERRIDE; + void mousePressEvent(QMouseEvent *) Q_DECL_OVERRIDE; + void resizeEvent(QResizeEvent *) Q_DECL_OVERRIDE; + void mouseReleaseEvent(QMouseEvent *) Q_DECL_OVERRIDE; + void dragEnterEvent(QDragEnterEvent* event) Q_DECL_OVERRIDE; + void dropEvent(QDropEvent* event) Q_DECL_OVERRIDE; + +private: + class Private; + Private * const p; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(ColorWheel::DisplayFlags) + +} // namespace color_widgets + +#endif // COLOR_WHEEL_HPP diff --git a/Qt-Color-Widgets/include/colorwidgets_global.hpp b/Qt-Color-Widgets/include/colorwidgets_global.hpp new file mode 100644 index 00000000..13add22d --- /dev/null +++ b/Qt-Color-Widgets/include/colorwidgets_global.hpp @@ -0,0 +1,14 @@ +#ifndef QT_COLOR_WIDGETS_GLOBAL_H +#define QT_COLOR_WIDGETS_GLOBAL_H + +#include + +#if defined(QTCOLORWIDGETS_LIBRARY) +# define QCP_EXPORT Q_DECL_EXPORT +#elif defined(QTCOLORWIDGETS_STATICALLY_LINKED) +# define QCP_EXPORT +#else +# define QCP_EXPORT Q_DECL_IMPORT +#endif + +#endif // QT_COLOR_WIDGETS_GLOBAL_H diff --git a/Qt-Color-Widgets/src/alphaback.png b/Qt-Color-Widgets/src/alphaback.png new file mode 100644 index 00000000..62d5c6e5 Binary files /dev/null and b/Qt-Color-Widgets/src/alphaback.png differ diff --git a/Qt-Color-Widgets/src/color_utils.cpp b/Qt-Color-Widgets/src/color_utils.cpp new file mode 100644 index 00000000..e0bdb793 --- /dev/null +++ b/Qt-Color-Widgets/src/color_utils.cpp @@ -0,0 +1,83 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2017 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_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/Qt-Color-Widgets/src/color_utils.hpp b/Qt-Color-Widgets/src/color_utils.hpp new file mode 100644 index 00000000..5e61cdc9 --- /dev/null +++ b/Qt-Color-Widgets/src/color_utils.hpp @@ -0,0 +1,70 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2017 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 + +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)); +} + +QColor color_from_hsl(qreal hue, qreal sat, qreal lig, qreal alpha = 1 ); + +} // namespace detail +} // namespace color_widgets diff --git a/Qt-Color-Widgets/src/color_wheel.cpp b/Qt-Color-Widgets/src/color_wheel.cpp new file mode 100644 index 00000000..3a66c6ea --- /dev/null +++ b/Qt-Color-Widgets/src/color_wheel.cpp @@ -0,0 +1,562 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2013-2017 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.hpp" + +#include +#include +#include +#include +#include +#include +#include "color_utils.hpp" + +namespace color_widgets { + +enum MouseStatus +{ + Nothing, + DragCircle, + DragSquare +}; + +static const ColorWheel::DisplayFlags hard_default_flags = ColorWheel::SHAPE_TRIANGLE|ColorWheel::ANGLE_ROTATING|ColorWheel::COLOR_HSV; +static ColorWheel::DisplayFlags default_flags = hard_default_flags; +static const double selector_radius = 6; + +class ColorWheel::Private +{ +private: + ColorWheel * const w; + +public: + qreal hue, sat, val; + unsigned int wheel_width; + MouseStatus mouse_status; + QPixmap hue_ring; + QImage inner_selector; + DisplayFlags display_flags; + 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), + display_flags(FLAGS_DEFAULT), + color_from(&QColor::fromHsvF), rainbow_from_hue(&detail::rainbow_hsv) + { } + + /// 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()); + } + + void render_square() + { + int width = qMin(square_size(), max_size); + QSize size(width, width); + inner_selector = QImage(size, QImage::Format_RGB32); + + for ( int y = 0; y < width; ++y ) + { + for ( int x = 0; x < width; ++x ) + { + inner_selector.setPixel( x, y, + color_from(hue,double(x)/width,double(y)/width,1).rgb()); + } + } + } + + /** + * \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; + inner_selector = QImage(size.toSize(), QImage::Format_RGB32); + + for (int x = 0; x < inner_selector.width(); x++ ) + { + qreal pval = x / size.height(); + qreal slice_h = size.height() * pval; + for (int y = 0; y < inner_selector.height(); y++ ) + { + qreal ymin = ycenter-slice_h/2; + qreal psat = qBound(0.0,(y-ymin)/slice_h,1.0); + + inner_selector.setPixel(x,y,color_from(hue,psat,pval,1).rgb()); + } + } + } + + /// Updates the inner image that displays the saturation-value selector + void render_inner_selector() + { + if ( display_flags & ColorWheel::SHAPE_TRIANGLE ) + render_triangle(); + else + render_square(); + } + + /// Offset of the selector image + QPointF selector_image_offset() + { + if ( display_flags & SHAPE_TRIANGLE ) + 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 ( display_flags & SHAPE_TRIANGLE ) + return QSizeF(triangle_height(), triangle_side()); + return QSizeF(square_size(), square_size()); + } + + + /// Rotation of the selector image + qreal selector_image_angle() + { + if ( display_flags & SHAPE_TRIANGLE ) + { + if ( display_flags & ANGLE_ROTATING ) + return -hue*360-60; + return -150; + } + else + { + if ( display_flags & ANGLE_ROTATING ) + 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) + { + if ( display_flags & ColorWheel::COLOR_HSV ) + { + hue = qMax(0.0, c.hsvHueF()); + sat = c.hsvSaturationF(); + val = c.valueF(); + } + else if ( display_flags & ColorWheel::COLOR_HSL ) + { + hue = qMax(0.0, c.hueF()); + sat = detail::color_HSL_saturationF(c); + val = detail::color_lightnessF(c); + } + else if ( display_flags & ColorWheel::COLOR_LCH ) + { + hue = qMax(0.0, c.hsvHueF()); + sat = detail::color_chromaF(c); + val = detail::color_lumaF(c); + } + } +}; + +ColorWheel::ColorWheel(QWidget *parent) : + QWidget(parent), p(new Private(this)) +{ + setDisplayFlags(FLAGS_DEFAULT); + setAcceptDrops(true); +} + +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->display_flags & COLOR_LCH) && 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(); +} + +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 + painter.setPen(QPen(Qt::black,3)); + painter.setBrush(Qt::NoBrush); + QLineF ray(0, 0, p->outer_radius(), 0); + ray.setAngle(p->hue*360); + QPointF h1 = ray.p2(); + ray.setLength(p->inner_radius()); + QPointF h2 = ray.p2(); + painter.drawLine(h1,h2); + + // 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->display_flags & SHAPE_SQUARE ) + { + qreal side = p->square_size(); + selector_position = QPointF(p->sat*side, p->val*side); + } + else if ( p->display_flags & SHAPE_TRIANGLE ) + { + 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 + 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 ) + { + p->hue = p->line_to_point(ev->pos()).angle()/360.0; + 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->display_flags & SHAPE_SQUARE ) + { + 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->display_flags & SHAPE_TRIANGLE ) + { + 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; + Q_EMIT mouseReleaseOnColor(color()); + +} + +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(); +} + + +void ColorWheel::setDisplayFlags(DisplayFlags flags) +{ + if ( ! (flags & COLOR_FLAGS) ) + flags |= default_flags & COLOR_FLAGS; + if ( ! (flags & ANGLE_FLAGS) ) + flags |= default_flags & ANGLE_FLAGS; + if ( ! (flags & SHAPE_FLAGS) ) + flags |= default_flags & SHAPE_FLAGS; + + if ( (flags & COLOR_FLAGS) != (p->display_flags & COLOR_FLAGS) ) + { + QColor old_col = color(); + if ( flags & ColorWheel::COLOR_HSL ) + { + 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; + } + else if ( flags & ColorWheel::COLOR_LCH ) + { + 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; + } + else + { + 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; + } + p->render_ring(); + } + + p->display_flags = flags; + p->render_inner_selector(); + update(); + Q_EMIT displayFlagsChanged(flags); +} + +ColorWheel::DisplayFlags ColorWheel::displayFlags(DisplayFlags mask) const +{ + return p->display_flags & mask; +} + +void ColorWheel::setDefaultDisplayFlags(DisplayFlags flags) +{ + if ( !(flags & COLOR_FLAGS) ) + flags |= hard_default_flags & COLOR_FLAGS; + if ( !(flags & ANGLE_FLAGS) ) + flags |= hard_default_flags & ANGLE_FLAGS; + if ( !(flags & SHAPE_FLAGS) ) + flags |= hard_default_flags & SHAPE_FLAGS; + default_flags = flags; +} + +ColorWheel::DisplayFlags ColorWheel::defaultDisplayFlags(DisplayFlags mask) +{ + return default_flags & mask; +} + +void ColorWheel::setDisplayFlag(DisplayFlags flag, DisplayFlags mask) +{ + setDisplayFlags((p->display_flags&~mask)|flag); +} + +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/Qt-Color-Widgets/src/color_widgets.qrc b/Qt-Color-Widgets/src/color_widgets.qrc new file mode 100644 index 00000000..7b8cfcd3 --- /dev/null +++ b/Qt-Color-Widgets/src/color_widgets.qrc @@ -0,0 +1,5 @@ + + + alphaback.png + +