File indexing completed on 2024-04-21 16:31:52
0001 /** 0002 * SPDX-FileCopyrightText: (C) 2005 Sébastien Laoût <slaout@linux62.org> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #ifndef KCOLORCOMBO2_H 0008 #define KCOLORCOMBO2_H 0009 0010 #include <KComboBox> 0011 0012 class QColor; 0013 class QPixmap; 0014 0015 class QDragEnterEvent; 0016 class QDropEvent; 0017 class QMouseEvent; 0018 class QKeyEvent; 0019 class QPaintEvent; 0020 0021 class KColorPopup; 0022 0023 /** 0024 * @short A combobox to display or allow user selection of a color in a user-friendly way. 0025 * 0026 * A combobox widget that popup an array of colors for the user to easily pick a common color.\n 0027 * He/she can use the popup to quickly pick a reasonable color or open a color chooser dialog for a more precise choice.\n 0028 * The user can also choose a default color (the standard background color, text color, etc... it's to the programmer to make sense of this property).\n 0029 * \n 0030 * The user is also offered some facilities: like KColorButton he/she can copy a color or paste it 0031 * (with standard keyboard shortcuts, usually Ctrl+C and Ctrl+V), and he/she can drag or drop colors. 0032 * 0033 * @par Quick usage: 0034 * Just create a new KColorCombo2() with the initial color and eventually an allowed default color 0035 * (eg. palette().color(QPalette::Base) for a background color, palette().color(QPalette::Text)...).\n 0036 * You will be noticed of the color the user selects with the signal changed(), or you can use color() to get the color at any moment.\n 0037 * Note that they can return an invalid color (see QColor::isValid()) if the user chosen the default color (if he can choose that).\n 0038 * It's then easy to save in settings, but if you want the real color (even for the default), you can get it with effectiveColor(). 0039 * 0040 * @par Notes about default color: 0041 * If you set a default color using Qt or KDE standard colors, the user can change them in the KDE Control Center, 0042 * but this widget willn't be update and will still show the old one.\n 0043 * To be noticed of such color change and then update the widget with the new standard color, you can use one of those two methods: 0044 * @code 0045 * void QWidgetDerivate::paletteChange(const QPalette &oldPalette) { // QWidgetDerivate is a parent or near custom widget 0046 * theComboBox->setDefaultColor(theNewDefaultColor); 0047 * QWidget::paletteChange(oldPalette); 0048 * } 0049 * @endcode 0050 * or connect the signal QApplication::kdisplayPaletteChanged() to a slot that will set the default color of this widget. 0051 * 0052 * @par Advanced usage: 0053 * By default, the combobox show a well balanced rainbow, OK for most usages, and you don't need to do anything for it to work.\n 0054 * You however can set your own color array by calling newColorArray() with the number of columns and rows. 0055 * Then, setColorAt() several times to fill the array.\n 0056 * This allow the most flexibility. But if you just want a rainbow with more or less colors, setRainbowPreset() is what you want.\n 0057 * If you worry about performance issues of creating a combobox with the default color array and then allocating another color array by yourself, 0058 * note that the default color array is not allocated in the constructor, but as soon as it is demanded (on first popup if no array has been 0059 * set before, or on first call of any accessors: colorAt(), columnCount(), setColorAt()...). 0060 * Finally, colorRectPixmap() and drawColorRect() allow to draw the color rounded-rectangle in other places for a consistent look. 0061 * 0062 * @see KGlobalSettings Use one of the static functions to get KDE standard colors for default values. 0063 * @see KColorButton The same, but without the rainbow popup or the choice of a default color. 0064 * @see QColorDialog The dialog that is shown when the user click the "Other..." entry. 0065 * @author Sébastien Laoût <slaout@linux62.org> 0066 * 0067 * @image html commoncolorselector.png "Common Color Selector ComboBox" 0068 */ 0069 class KColorCombo2 : public KComboBox 0070 { 0071 Q_OBJECT 0072 Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) 0073 Q_PROPERTY(QColor defaultColor READ defaultColor WRITE setDefaultColor NOTIFY defaultColorChanged) 0074 0075 public Q_SLOTS: 0076 /** 0077 * Change the selected color.\n 0078 * If the popup is open, it will not reflect the change. FIXME: Should it? 0079 * @param color The new selected color. Can be invalid to select the default one.\n 0080 * If @p color is invalid and no default color is allowed, the function will keep the old one. 0081 */ 0082 void setColor(const QColor &color); 0083 0084 /** 0085 * Change the default color. 0086 * @param color The color to return if the user choose the default one. If it is not valid, the user willn't be allowed to choose a default one. 0087 * @see defaultColor() to get it. 0088 */ 0089 void setDefaultColor(const QColor &color); 0090 private Q_SLOTS: 0091 void popupClosed(); 0092 Q_SIGNALS: 0093 /** 0094 * Emitted when the color of the widget is changed, either with setColor() or via user selection. 0095 * @see color() to know the content of @p newColor. 0096 */ 0097 void colorChanged(const QColor &newColor); 0098 0099 /** 0100 * Emitted when the default color of the widget is changed with setDefaultColor() 0101 * @see defaultColor() to know the content of @p newDefaultColor. 0102 */ 0103 void defaultColorChanged(const QColor &newDefaultColor); 0104 0105 public: 0106 /** 0107 * Constructs a color combobox with parent @p parent. 0108 * @param color The initial selected color. If it is not valid, the default one will then be selected.\n 0109 * But if @p color is invalid and there is no default color, the result is undefined. 0110 * @param defaultColor The color to return if the user choose the default one. If it is not valid, the user willn't be allowed to choose a default one. 0111 */ 0112 KColorCombo2(const QColor &color, const QColor &defaultColor, QWidget *parent = nullptr); 0113 0114 /** 0115 * Constructs a color combobox with parent @p parent.\n 0116 * The user is not allowed to choose a default color, unless you call setDefaultColor() later. 0117 * @param color The initial selected color. If it is invalid, the result is undefined. 0118 */ 0119 explicit KColorCombo2(const QColor &color, QWidget *parent = nullptr); 0120 0121 /** 0122 * Destroys the combobox. 0123 */ 0124 ~KColorCombo2() override; 0125 0126 /** 0127 * Get the color chosen by the user.\n 0128 * Can be invalid, if the user chosen the default one.\n 0129 * Ideal to store it in settings for later recall. 0130 * @see effectiveColor() if you want the color to be always valid. 0131 */ 0132 QColor color() const; 0133 0134 /** 0135 * Return the color chosen by the user.\n 0136 * If the user chosen the default color, the default one is then returned, so the returned color is always valid.\n 0137 * Ideal to directly use to draw. 0138 * @see color() if you want to be notified of a default color choice. 0139 */ 0140 QColor effectiveColor() const; 0141 0142 /** 0143 * Returns the default color or an invalid color if no default color is set (if the user isn't allowed to choose a default color). 0144 * @see setDefaultColor() to change it. 0145 */ 0146 QColor defaultColor() const; 0147 0148 /** 0149 * Allocate a new color array of the specified dimension.\n 0150 * The new array will have invalid colors: you should then assign them one by one.\n 0151 * If one or both of the dimensions are negative or null, this function do nothing (both dimensions are always ensured to be at least equal to 1). 0152 * @param columnCount The number of columns of the array. 0153 * @param rowCount The number of rows of the array. 0154 * @see setColorAt() to set all colors once the array have been created. 0155 */ 0156 void newColorArray(int columnCount, int rowCount); 0157 0158 /** 0159 * Get the number of columns in the array that the user can see to choose. 0160 * @see rowCount() for the number of rows, and colorAt() to get a color from the array. 0161 */ 0162 int columnCount() const; 0163 0164 /** 0165 * Get the number of rows in the array that the user can see to choose. 0166 * @see columnCount() for the number of columns, and colorAt() to get a color from the array. 0167 */ 0168 int rowCount() const; 0169 0170 /** 0171 * Set a color in the array at position (column,row).\n 0172 * If one or both of the indexes are out of range, this function do nothing.\n 0173 * @p column and @p row start from 0 to columnCount()-1 and columnRow()-1. 0174 * 0175 * @param column The x coordinate of the color to set or change. 0176 * @param row The y coordinate of the color to set or change. 0177 * @param color The color to assign at this position. 0178 */ 0179 void setColorAt(int column, int row, const QColor &color); 0180 0181 /** 0182 * Get a color in the array that the user can see to choose.\n 0183 * @p column and @p row start from 0 to columnCount()-1 and columnRow()-1. 0184 * 0185 * @return The asked color, or an invalid color if the index is out of limit of the array. 0186 * @see columnCount() and rowCount() to get the array dimensions. 0187 */ 0188 QColor colorAt(int column, int row) /* const*/; 0189 0190 /** 0191 * Fill the array of colors (that will be shown to the user in the popup that appears when he/she click the arrow) with a rainbow of different luminosity.\n 0192 * This rainbow representation have the advantage of being natural and well structured for a human to be able to select reasonable colors.\n 0193 * This function will allocate a color array by itself depending on the parameters (no need to call newColorArray()). 0194 * @param colorColumnCount The number of columns. The 360 possible colors of the rainbow will be split to take the wanted number of colors, equally separated. 0195 * @param lightRowCount There is always at least 1 row of colors: the "pure" colors: pure red, pure blue, pure green, pure fuchsia...\n 0196 * Additionally, you can add row on top: they will contain the same colors, but lighter.\n 0197 * The parameter @p lightRowCount specify how many different lighting grades should be shown (from near to white, but not white, to "pure"). 0198 * @param darkRowCount Finally, on bottom of the row of "pure colors", you can put any variety of dark colors (from "pure", to near to black, but not black).\n 0199 * So, the number of rows is equal to @p lightRowCount + 1 + @p darkRowCount. On top are light colors, gradually going to dark ones on bottom. 0200 * @param withGray If true, another column (so there will be @p colorColumnCount+1 columns) is added on the very-right of the popup 0201 * to show different gray values, matching the brightness of the sibling colors. 0202 * 0203 * The most acceptable parameters: 0204 * @li The default values are good to have the 7 colors of the rainbow + colors between them, and light/dark colors are well distinct. 0205 * @li If the color is a background color, you can set @p darkRowCount to 0, so only light colors are shown. 0206 * @li The inverse is true for text color choice: you can set @p lightRowCount to 0. 0207 * @li But be careful: some advanced users prefer white text on dark background, so you eg. can set @p lightRowCount to a big value and 0208 * @p darkRowCount to a small one for a fewer choice of dark colors, but at least some ones. 0209 */ 0210 void setRainbowPreset(int colorColumnCount = 12, int lightRowCount = 4, int darkRowCount = 4, bool withGray = true); 0211 // void setHsvPreset(QColor hue[], QColor saturation[], QColor value[], bool withGray = true); 0212 0213 /** 0214 * Returns a pixmap of a colored rounded-rectangle. The four corners are transparent.\n 0215 * Useful if you want to set such a rectangle as an icon for a menu entry, or for drag and drop operation... 0216 * @param color The color of the rectangle. If the color is invalid, a rainbow is then drawn (like for the "Other..." entry in the popup). 0217 * @param isDefault True if @p color is the default one and should then be draw with a diagonal line. 0218 * @param width The width of the rectangle pixmap to return. 0219 * @param height The height of the rectangle pixmap to return. 0220 * 0221 * @see drawColorRect() if you need to draw it directly: it's faster. 0222 */ 0223 QPixmap colorRectPixmap(const QColor &color, bool isDefault, int width, int height); 0224 0225 /** 0226 * Draw an image of a colored rounded-rectangle.\n 0227 * This is like colorRectPixmap() but significantly faster because there is nothing to copy, and no transparency mask to create and apply. 0228 * @param painter The painter where to draw the image. 0229 * @param x The x coordinate on the @p painter where to draw the rectangle. 0230 * @param y The y coordinate on the @p painter where to draw the rectangle. 0231 * @param color The color of the rectangle. If the color is invalid, a rainbow is then drawn (like for the "Other..." entry in the popup). 0232 * @param isDefault True if @p color is the default one and should then be draw with a diagonal line. 0233 * @param width The width of the rectangle pixmap to return. 0234 * @param height The height of the rectangle pixmap to return. 0235 * 0236 * @see colorRectPixmap() to get a transparent pixmap of the rectangle. 0237 */ 0238 void drawColorRect(QPainter &painter, int x, int y, const QColor &color, bool isDefault, int width, int height); 0239 0240 /** 0241 * Get the height of a color rectangle for this combobox.\n 0242 * This is equal to the text height, regarding to the current font of this combobox. 0243 */ 0244 int colorRectHeight() const; 0245 0246 /** 0247 * Get the width of a color rectangle, depending of the @p height of it.\n 0248 * It typically return 1.4 * @p height for decent rectangle proportions. 0249 */ 0250 int colorRectWidthForHeight(int height) const; 0251 0252 protected: 0253 void showPopup() override; 0254 void mouseMoveEvent(QMouseEvent *event) override; 0255 void dragEnterEvent(QDragEnterEvent *event) override; 0256 void dropEvent(QDropEvent *event) override; 0257 void keyPressEvent(QKeyEvent *event) override; 0258 virtual void fontChange(const QFont &oldFont); 0259 0260 private: 0261 /** 0262 * Initialization routine common to every constructors.\n 0263 * Constructors just have to initialize the KComboBox, m_color and m_defaultColor 0264 * and this function do the rest to complete the creation of this widget. 0265 */ 0266 void init(); 0267 0268 /** 0269 * Free up all memory allocated for the color array.\n 0270 * But only if an array have previously been allocated, of course. 0271 */ 0272 void deleteColorArray(); 0273 0274 /** 0275 * Update the only item of the combobox to mirror the new selected color.\n 0276 * Mainly called on init() and setColor(). 0277 */ 0278 void updateComboBox(); 0279 0280 KColorPopup *m_popup; 0281 QColor m_color; 0282 QColor m_defaultColor; 0283 QColor **m_colorArray; 0284 int m_columnCount; 0285 int m_rowCount; 0286 QPoint m_dragStartPos; 0287 0288 protected: 0289 /** 0290 * Keep place for future improvements without having to break binary compatibility.\n 0291 * Does nothing for the moment. 0292 */ 0293 void virtual_hook(int id, void *data) override; 0294 0295 private: 0296 /** 0297 * Keep place for future improvements without having to break binary compatibility. 0298 */ 0299 class KColorCombo2Private; 0300 0301 KColorCombo2Private *d; 0302 }; 0303 0304 // TODO: setColorArray(QColor **, int, int) and use signals/slots ?? 0305 0306 class KColorPopup : public QWidget 0307 { 0308 Q_OBJECT 0309 public: 0310 explicit KColorPopup(KColorCombo2 *parent); 0311 ~KColorPopup() override; 0312 void relayout(); // updateGeometry() ?? 0313 Q_SIGNALS: 0314 void closed(); 0315 0316 protected: 0317 void paintEvent(QPaintEvent * /*event*/) override; 0318 void mouseMoveEvent(QMouseEvent *event) override; 0319 void mousePressEvent(QMouseEvent *event) override; 0320 void keyPressEvent(QKeyEvent *event) override; 0321 void doSelection(); 0322 void validate(); 0323 void updateCell(int column, int row); 0324 0325 friend class KColorCombo2; 0326 0327 private: 0328 KColorCombo2 *m_selector; 0329 QPixmap *m_pixmap; 0330 int m_selectedRow; 0331 int m_selectedColumn; 0332 int m_columnOther; 0333 QColor m_otherColor; 0334 0335 static const int MARGIN; 0336 static const int FRAME_WIDTH; 0337 }; 0338 0339 #endif // KCOLORCOMBO2_H