swatch.cpp
Go to the documentation of this file.
00001 
00022 #include "swatch.hpp"
00023 
00024 #include <cmath>
00025 #include <QPainter>
00026 #include <QMouseEvent>
00027 #include <QKeyEvent>
00028 #include <QApplication>
00029 #include <QDrag>
00030 #include <QMimeData>
00031 #include <QDropEvent>
00032 #include <QDragEnterEvent>
00033 #include <QStyleOption>
00034 #include <QToolTip>
00035 
00036 namespace color_widgets {
00037 
00038 class Swatch::Private
00039 {
00040 public:
00041     ColorPalette palette;    
00042     int          selected;   
00043     QSize        color_size; 
00044     ColorSizePolicy size_policy;
00045     QPen         border;
00046     int          forced_rows;
00047     int          forced_columns;
00048     bool         readonly;  
00049 
00050     QPoint  drag_pos;       
00051     int     drag_index;     
00052     int     drop_index;     
00053     QColor  drop_color;     
00054     bool    drop_overwrite; 
00055 
00056     Swatch* owner;
00057 
00058     Private(Swatch* owner)
00059         : selected(-1),
00060           color_size(16,16),
00061           size_policy(Hint),
00062           border(Qt::black, 1),
00063           forced_rows(0),
00064           forced_columns(0),
00065           readonly(false),
00066           drag_index(-1),
00067           drop_index(-1),
00068           drop_overwrite(false),
00069           owner(owner)
00070     {}
00071 
00075     QSize rowcols()
00076     {
00077         int count = palette.count();
00078         if ( count == 0 )
00079             return QSize();
00080 
00081         if ( forced_rows )
00082             return QSize(std::ceil( float(count) / forced_rows ), forced_rows);
00083 
00084         int columns = palette.columns();
00085 
00086         if ( forced_columns )
00087             columns = forced_columns;
00088         else if ( columns == 0 )
00089             columns = qMin(palette.count(), owner->width() / color_size.width());
00090 
00091         int rows = std::ceil( float(count) / columns );
00092 
00093         return QSize(columns, rows);
00094     }
00095 
00099     void dropEvent(QDropEvent* event)
00100     {
00101         // Find the output location
00102         drop_index = owner->indexAt(event->pos());
00103         if ( drop_index == -1 )
00104             drop_index = palette.count();
00105 
00106         // Gather up the color
00107         if ( event->mimeData()->hasColor() )
00108         {
00109             drop_color = event->mimeData()->colorData().value<QColor>();
00110             drop_color.setAlpha(255);
00111         }
00112         else if ( event->mimeData()->hasText() )
00113         {
00114             drop_color = QColor(event->mimeData()->text());
00115         }
00116 
00117         drop_overwrite = false;
00118         QRectF drop_rect = indexRect(drop_index);
00119         if ( drop_index < palette.count() && drop_rect.isValid() )
00120         {
00121             // 1 column => vertical style
00122             if ( palette.columns() == 1 || forced_columns == 1 )
00123             {
00124                 // Dragged to the last quarter of the size of the square, add after
00125                 if ( event->posF().y() >= drop_rect.top() + drop_rect.height() * 3.0 / 4 )
00126                     drop_index++;
00127                 // Dragged to the middle of the square, overwrite existing color
00128                 else if ( event->posF().x() > drop_rect.top() + drop_rect.height() / 4 &&
00129                         ( event->dropAction() != Qt::MoveAction || event->source() != owner ) )
00130                     drop_overwrite = true;
00131             }
00132             else
00133             {
00134                 // Dragged to the last quarter of the size of the square, add after
00135                 if ( event->posF().x() >= drop_rect.left() + drop_rect.width() * 3.0 / 4 )
00136                     drop_index++;
00137                 // Dragged to the middle of the square, overwrite existing color
00138                 else if ( event->posF().x() > drop_rect.left() + drop_rect.width() / 4 &&
00139                         ( event->dropAction() != Qt::MoveAction || event->source() != owner ) )
00140                     drop_overwrite = true;
00141             }
00142         }
00143 
00144         owner->update();
00145     }
00146 
00150     void clearDrop()
00151     {
00152         drop_index = -1;
00153         drop_color = QColor();
00154         drop_overwrite = false;
00155 
00156         owner->update();
00157     }
00158 
00162     QSizeF actualColorSize()
00163     {
00164         QSize rowcols = this->rowcols();
00165         if ( !rowcols.isValid() )
00166             return QSizeF();
00167         return actualColorSize(rowcols);
00168     }
00169 
00174     QSizeF actualColorSize(const QSize& rowcols)
00175     {
00176         return QSizeF (float(owner->width()) / rowcols.width(),
00177                        float(owner->height()) / rowcols.height());
00178     }
00179 
00180 
00186     QRectF indexRect(int index, const QSize& rowcols, const QSizeF& color_size)
00187     {
00188         if ( index == -1 )
00189             return QRectF();
00190 
00191         return QRectF(
00192             index % rowcols.width() * color_size.width(),
00193             index / rowcols.width() * color_size.height(),
00194             color_size.width(),
00195             color_size.height()
00196         );
00197     }
00201     QRectF indexRect(int index)
00202     {
00203         QSize rc = rowcols();
00204         if ( index == -1 || !rc.isValid() )
00205             return QRectF();
00206         return indexRect(index, rc, actualColorSize(rc));
00207     }
00208 };
00209 
00210 Swatch::Swatch(QWidget* parent)
00211     : QWidget(parent), p(new Private(this))
00212 {
00213     connect(&p->palette, &ColorPalette::colorsChanged, this, &Swatch::paletteModified);
00214     connect(&p->palette, &ColorPalette::columnsChanged, this, (void(QWidget::*)())&QWidget::update);
00215     connect(&p->palette, &ColorPalette::colorsUpdated, this, (void(QWidget::*)())&QWidget::update);
00216     connect(&p->palette, &ColorPalette::colorChanged, [this](int index){
00217         if ( index == p->selected )
00218             emit colorSelected( p->palette.colorAt(index) );
00219     });
00220     connect(&p->palette, &ColorPalette::colorRemoved, [this](int index){
00221         if ( index == p->selected )
00222             clearSelection();
00223     });
00224     setFocusPolicy(Qt::StrongFocus);
00225     setAcceptDrops(true);
00226     setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
00227     setAttribute(Qt::WA_Hover, true);
00228 }
00229 
00230 Swatch::~Swatch()
00231 {
00232     delete p;
00233 }
00234 
00235 QSize Swatch::sizeHint() const
00236 {
00237     QSize rowcols = p->rowcols();
00238 
00239     if ( !p->color_size.isValid() || !rowcols.isValid() )
00240         return QSize();
00241 
00242     return QSize(
00243         p->color_size.width()  * rowcols.width(),
00244         p->color_size.height() * rowcols.height()
00245     );
00246 }
00247 
00248 QSize Swatch::minimumSizeHint() const
00249 {
00250     if ( p->size_policy != Hint )
00251         return sizeHint();
00252     return QSize();
00253 }
00254 
00255 const ColorPalette& Swatch::palette() const
00256 {
00257     return p->palette;
00258 }
00259 
00260 ColorPalette& Swatch::palette()
00261 {
00262     return p->palette;
00263 }
00264 
00265 int Swatch::selected() const
00266 {
00267     return p->selected;
00268 }
00269 
00270 QColor Swatch::selectedColor() const
00271 {
00272     return p->palette.colorAt(p->selected);
00273 }
00274 
00275 int Swatch::indexAt(const QPoint& pt)
00276 {
00277     QSize rowcols = p->rowcols();
00278     if ( rowcols.isEmpty() )
00279         return -1;
00280 
00281     QSizeF color_size = p->actualColorSize(rowcols);
00282 
00283     QPoint point(
00284         qBound<int>(0, pt.x() / color_size.width(), rowcols.width() - 1),
00285         qBound<int>(0, pt.y() / color_size.height(), rowcols.height() - 1)
00286     );
00287 
00288     int index = point.y() * rowcols.width() + point.x();
00289     if ( index >= p->palette.count() )
00290         return -1;
00291     return index;
00292 }
00293 
00294 QColor Swatch::colorAt(const QPoint& pt)
00295 {
00296     return p->palette.colorAt(indexAt(pt));
00297 }
00298 
00299 void Swatch::setPalette(const ColorPalette& palette)
00300 {
00301     clearSelection();
00302     p->palette = palette;
00303     update();
00304     emit paletteChanged(p->palette);
00305 }
00306 
00307 void Swatch::setSelected(int selected)
00308 {
00309     if ( selected < 0 || selected >= p->palette.count() )
00310         selected = -1;
00311 
00312     if ( selected != p->selected )
00313     {
00314         emit selectedChanged( p->selected = selected );
00315         if ( selected != -1 )
00316             emit colorSelected( p->palette.colorAt(p->selected) );
00317         update();
00318     }
00319 }
00320 
00321 void Swatch::clearSelection()
00322 {
00323     setSelected(-1);
00324 }
00325 
00326 void Swatch::paintEvent(QPaintEvent* )
00327 {
00328     QSize rowcols = p->rowcols();
00329     if ( rowcols.isEmpty() )
00330         return;
00331 
00332     QSizeF color_size = p->actualColorSize(rowcols);
00333     QPainter painter(this);
00334 
00335     QStyleOptionFrame panel;
00336     panel.initFrom(this);
00337     panel.lineWidth = 1;
00338     panel.midLineWidth = 0;
00339     panel.state |= QStyle::State_Sunken;
00340     style()->drawPrimitive(QStyle::PE_Frame, &panel, &painter, this);
00341     QRect r = style()->subElementRect(QStyle::SE_FrameContents, &panel, this);
00342     painter.setClipRect(r);
00343 
00344     int count = p->palette.count();
00345         painter.setPen(p->border);
00346     for ( int y = 0, i = 0; i < count; y++ )
00347     {
00348         for ( int x = 0; x < rowcols.width() && i < count; x++, i++ )
00349         {
00350             painter.setBrush(p->palette.colorAt(i));
00351             painter.drawRect(p->indexRect(i, rowcols, color_size));
00352         }
00353     }
00354 
00355     painter.setClipping(false);
00356 
00357     if ( p->drop_index != -1 )
00358     {
00359         QRectF drop_area = p->indexRect(p->drop_index, rowcols, color_size);
00360         if ( p->drop_overwrite )
00361         {
00362             painter.setBrush(p->drop_color);
00363             painter.setPen(QPen(Qt::gray));
00364             painter.drawRect(drop_area);
00365         }
00366         else if ( rowcols.width() == 1 )
00367         {
00368             // 1 column => vertical style
00369             painter.setPen(QPen(p->drop_color, 2));
00370             painter.setBrush(Qt::transparent);
00371             painter.drawLine(drop_area.topLeft(), drop_area.topRight());
00372         }
00373         else
00374         {
00375             painter.setPen(QPen(p->drop_color, 2));
00376             painter.setBrush(Qt::transparent);
00377             painter.drawLine(drop_area.topLeft(), drop_area.bottomLeft());
00378             // Draw also on the previous line when the first item of a line is selected
00379             if ( p->drop_index % rowcols.width() == 0 && p->drop_index != 0 )
00380             {
00381                 drop_area = p->indexRect(p->drop_index-1, rowcols, color_size);
00382                 drop_area.translate(color_size.width(), 0);
00383                 painter.drawLine(drop_area.topLeft(), drop_area.bottomLeft());
00384             }
00385         }
00386     }
00387 
00388     if ( p->selected != -1 )
00389     {
00390         QRectF rect = p->indexRect(p->selected, rowcols, color_size);
00391         painter.setBrush(Qt::transparent);
00392         painter.setPen(QPen(Qt::darkGray, 2));
00393         painter.drawRect(rect);
00394         painter.setPen(QPen(Qt::gray, 2, Qt::DotLine));
00395         painter.drawRect(rect);
00396     }
00397 }
00398 
00399 void Swatch::keyPressEvent(QKeyEvent* event)
00400 {
00401     if ( p->palette.count() == 0 )
00402         QWidget::keyPressEvent(event);
00403 
00404     int selected = p->selected;
00405     int count = p->palette.count();
00406     QSize rowcols = p->rowcols();
00407     int columns = rowcols.width();
00408     int rows = rowcols.height();
00409     switch ( event->key() )
00410     {
00411         default:
00412             QWidget::keyPressEvent(event);
00413             return;
00414 
00415         case Qt::Key_Left:
00416             if ( selected == -1 )
00417                 selected = count - 1;
00418             else if ( selected > 0 )
00419                 selected--;
00420             break;
00421 
00422         case Qt::Key_Right:
00423             if ( selected == -1 )
00424                 selected = 0;
00425             else if ( selected < count - 1 )
00426                 selected++;
00427             break;
00428 
00429         case Qt::Key_Up:
00430             if ( selected == -1 )
00431                 selected = count - 1;
00432             else if ( selected >= columns )
00433                 selected -= columns;
00434             break;
00435 
00436         case Qt::Key_Down:
00437             if ( selected == -1 )
00438                 selected = 0;
00439             else if ( selected < count - columns )
00440                 selected += columns;
00441             break;
00442 
00443         case Qt::Key_Home:
00444             if ( event->modifiers() & Qt::ControlModifier )
00445                 selected = 0;
00446             else
00447                 selected -= selected % columns;
00448             break;
00449 
00450         case Qt::Key_End:
00451             if ( event->modifiers() & Qt::ControlModifier )
00452                 selected = count - 1;
00453             else
00454                 selected += columns - (selected % columns) - 1;
00455             break;
00456 
00457         case Qt::Key_Delete:
00458             removeSelected();
00459             return;
00460 
00461         case Qt::Key_Backspace:
00462             if (selected != -1 && !p->readonly )
00463             {
00464                 p->palette.eraseColor(selected);
00465                 if ( p->palette.count() == 0 )
00466                     selected = -1;
00467                 else
00468                     selected = qMax(selected - 1, 0);
00469             }
00470             break;
00471 
00472         case Qt::Key_PageUp:
00473             if ( selected == -1 )
00474                 selected = 0;
00475             else
00476                 selected = selected % columns;
00477             break;
00478         case Qt::Key_PageDown:
00479             if ( selected == -1 )
00480             {
00481                 selected = count - 1;
00482             }
00483             else
00484             {
00485                 selected = columns * (rows-1) + selected % columns;
00486                 if ( selected >= count )
00487                     selected -= columns;
00488             }
00489             break;
00490     }
00491     setSelected(selected);
00492 }
00493 
00494 void Swatch::removeSelected()
00495 {
00496     if (p->selected != -1 && !p->readonly )
00497     {
00498         int selected = p->selected;
00499         p->palette.eraseColor(p->selected);
00500         setSelected(qMin(selected, p->palette.count() - 1));
00501     }
00502 }
00503 
00504 void Swatch::mousePressEvent(QMouseEvent *event)
00505 {
00506     if ( event->button() == Qt::LeftButton )
00507     {
00508         setSelected(indexAt(event->pos()));
00509         p->drag_pos = event->pos();
00510         p->drag_index = indexAt(event->pos());
00511     }
00512     else if ( event->button() == Qt::RightButton )
00513     {
00514         int index = indexAt(event->pos());
00515         if ( index != -1 )
00516             emit rightClicked(index);
00517     }
00518 }
00519 
00520 void Swatch::mouseMoveEvent(QMouseEvent *event)
00521 {
00522     if ( p->drag_index != -1 &&  (event->buttons() & Qt::LeftButton) &&
00523         (p->drag_pos - event->pos()).manhattanLength() >= QApplication::startDragDistance() )
00524     {
00525         QColor color = p->palette.colorAt(p->drag_index);
00526 
00527         QPixmap preview(24,24);
00528         preview.fill(color);
00529 
00530         QMimeData *mimedata = new QMimeData;
00531         mimedata->setColorData(color);
00532         mimedata->setText(p->palette.nameAt(p->drag_index));
00533 
00534         QDrag *drag = new QDrag(this);
00535         drag->setMimeData(mimedata);
00536         drag->setPixmap(preview);
00537         Qt::DropActions actions = Qt::CopyAction;
00538         if ( !p->readonly )
00539             actions |= Qt::MoveAction;
00540         drag->exec(actions);
00541     }
00542 }
00543 
00544 void Swatch::mouseReleaseEvent(QMouseEvent *event)
00545 {
00546     if ( event->button() == Qt::LeftButton )
00547     {
00548         p->drag_index = -1;
00549     }
00550 }
00551 
00552 void Swatch::mouseDoubleClickEvent(QMouseEvent *event)
00553 {
00554     if ( event->button() == Qt::LeftButton )
00555     {
00556         int index = indexAt(event->pos());
00557         if ( index != -1 )
00558             emit doubleClicked(index);
00559     }
00560 }
00561 
00562 void Swatch::wheelEvent(QWheelEvent* event)
00563 {
00564     if ( event->delta() > 0 )
00565         p->selected = qMin(p->selected + 1, p->palette.count() - 1);
00566     else if ( p->selected == -1 )
00567             p->selected = p->palette.count() - 1;
00568     else if ( p->selected > 0 )
00569         p->selected--;
00570     setSelected(p->selected);
00571 }
00572 
00573 void Swatch::dragEnterEvent(QDragEnterEvent *event)
00574 {
00575     if ( p->readonly )
00576         return;
00577 
00578     p->dropEvent(event);
00579 
00580     if ( p->drop_color.isValid() && p->drop_index != -1 )
00581     {
00582         if ( event->proposedAction() == Qt::MoveAction && event->source() == this )
00583             event->setDropAction(Qt::MoveAction);
00584         else
00585             event->setDropAction(Qt::CopyAction);
00586 
00587         event->accept();
00588     }
00589 }
00590 
00591 void Swatch::dragMoveEvent(QDragMoveEvent* event)
00592 {
00593     if ( p->readonly )
00594         return;
00595     p->dropEvent(event);
00596 }
00597 
00598 void Swatch::dragLeaveEvent(QDragLeaveEvent *)
00599 {
00600     p->clearDrop();
00601 }
00602 
00603 void Swatch::dropEvent(QDropEvent *event)
00604 {
00605     if ( p->readonly )
00606         return;
00607 
00608     QString name;
00609 
00610     // Gather up the color
00611     if ( event->mimeData()->hasColor() && event->mimeData()->hasText() )
00612             name = event->mimeData()->text();
00613 
00614     // Not a color, discard
00615     if ( !p->drop_color.isValid() || p->drop_index == -1 )
00616         return;
00617 
00618     p->dropEvent(event);
00619 
00620     // Move unto self
00621     if ( event->dropAction() == Qt::MoveAction && event->source() == this )
00622     {
00623         // Not moved => noop
00624         if ( p->drop_index != p->drag_index && p->drop_index != p->drag_index + 1 )
00625         {
00626             // Erase the old color
00627             p->palette.eraseColor(p->drag_index);
00628             if ( p->drop_index > p->drag_index )
00629                 p->drop_index--;
00630             p->selected = p->drop_index;
00631             // Insert the dropped color
00632             p->palette.insertColor(p->drop_index, p->drop_color, name);
00633         }
00634     }
00635     // Move into a color cell
00636     else if ( p->drop_overwrite )
00637     {
00638         p->palette.setColorAt(p->drop_index, p->drop_color, name);
00639     }
00640     // Insert the dropped color
00641     else
00642     {
00643         p->palette.insertColor(p->drop_index, p->drop_color, name);
00644     }
00645 
00646     // Finalize
00647     event->accept();
00648     p->drag_index = -1;
00649     p->clearDrop();
00650 }
00651 
00652 void Swatch::paletteModified()
00653 {
00654     if ( p->selected >= p->palette.count() )
00655         clearSelection();
00656 
00657     if ( p->size_policy == Minimum )
00658         setMinimumSize(sizeHint());
00659     else if ( p->size_policy == Fixed )
00660         setFixedSize(sizeHint());
00661 
00662     update();
00663 }
00664 
00665 QSize Swatch::colorSize() const
00666 {
00667     return p->color_size;
00668 }
00669 
00670 void Swatch::setColorSize(const QSize& colorSize)
00671 {
00672     if ( p->color_size != colorSize )
00673         emit colorSizeChanged(p->color_size = colorSize);
00674 }
00675 
00676 Swatch::ColorSizePolicy Swatch::colorSizePolicy() const
00677 {
00678     return p->size_policy;
00679 }
00680 
00681 void Swatch::setColorSizePolicy(ColorSizePolicy colorSizePolicy)
00682 {
00683     if ( p->size_policy != colorSizePolicy )
00684     {
00685         setMinimumSize(0,0);
00686         setFixedSize(QWIDGETSIZE_MAX,QWIDGETSIZE_MAX);
00687         paletteModified();
00688         emit colorSizePolicyChanged(p->size_policy = colorSizePolicy);
00689     }
00690 }
00691 
00692 int Swatch::forcedColumns() const
00693 {
00694     return p->forced_columns;
00695 }
00696 
00697 int Swatch::forcedRows() const
00698 {
00699     return p->forced_rows;
00700 }
00701 
00702 void Swatch::setForcedColumns(int forcedColumns)
00703 {
00704     if ( forcedColumns <= 0 )
00705         forcedColumns = 0;
00706 
00707     if ( forcedColumns != p->forced_columns )
00708     {
00709         emit forcedColumnsChanged(p->forced_columns = forcedColumns);
00710         emit forcedRowsChanged(p->forced_rows = 0);
00711     }
00712 }
00713 
00714 void Swatch::setForcedRows(int forcedRows)
00715 {
00716     if ( forcedRows <= 0 )
00717         forcedRows = 0;
00718 
00719     if ( forcedRows != p->forced_rows )
00720     {
00721         emit forcedColumnsChanged(p->forced_columns = 0);
00722         emit forcedRowsChanged(p->forced_rows = forcedRows);
00723     }
00724 }
00725 
00726 bool Swatch::readOnly() const
00727 {
00728     return p->readonly;
00729 }
00730 
00731 void Swatch::setReadOnly(bool readOnly)
00732 {
00733     if ( readOnly != p->readonly )
00734     {
00735         emit readOnlyChanged(p->readonly = readOnly);
00736         setAcceptDrops(!p->readonly);
00737     }
00738 }
00739 
00740 bool Swatch::event(QEvent* event)
00741 {
00742     if(event->type() == QEvent::ToolTip)
00743     {
00744         QHelpEvent* help_ev = static_cast<QHelpEvent*>(event);
00745         int index = indexAt(help_ev->pos());
00746         if ( index != -1 )
00747         {
00748             QColor color = p->palette.colorAt(index);
00749             QString name = p->palette.nameAt(index);
00750             QString message = color.name();
00751             if ( !name.isEmpty() )
00752                 message = tr("%1 (%2)").arg(name).arg(message);
00753             message = "<tt style='background-color:"+color.name()+";color:"+color.name()+";'>MM</tt> "+message.toHtmlEscaped();
00754             QToolTip::showText(help_ev->globalPos(), message, this,
00755                                p->indexRect(index).toRect());
00756             event->accept();
00757         }
00758         else
00759         {
00760             QToolTip::hideText();
00761             event->ignore();
00762         }
00763         return true;
00764     }
00765 
00766     return QWidget::event(event);
00767 }
00768 
00769 QPen Swatch::border() const
00770 {
00771     return p->border;
00772 }
00773 
00774 void Swatch::setBorder(const QPen& border)
00775 {
00776     if ( border != p->border )
00777     {
00778         p->border = border;
00779         emit borderChanged(border);
00780         update();
00781     }
00782 }
00783 
00784 } // namespace color_widgets


plotjuggler
Author(s): Davide Faconti
autogenerated on Fri Sep 1 2017 02:41:57