1 /*
2  * wangcolorview.h
3  * Copyright 2017, Benjamin Trotter <bdtrotte@ucsc.edu>
4  *
5  * This file is part of Tiled.
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the Free
9  * Software Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15  * more details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include "wangcolorview.h"
22 
23 #include "utils.h"
24 #include "wangcolormodel.h"
25 
26 #include <QAbstractProxyModel>
27 #include <QColorDialog>
28 #include <QContextMenuEvent>
29 #include <QLineEdit>
30 #include <QMenu>
31 #include <QPainter>
32 #include <QStyledItemDelegate>
33 
34 using namespace Tiled;
35 
36 namespace {
37 
38 class WangColorDelegate : public QStyledItemDelegate
39 {
40 public:
WangColorDelegate(QObject * parent=nullptr)41     WangColorDelegate(QObject *parent = nullptr)
42         : QStyledItemDelegate(parent)
43     {}
44 
45 protected:
46     void initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const override;
47 };
48 
initStyleOption(QStyleOptionViewItem * option,const QModelIndex & index) const49 void WangColorDelegate::initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const
50 {
51     QSize decorationSize = option->decorationSize;
52     QPixmap tileImage = index.data(Qt::DecorationRole).value<QPixmap>();
53 
54     QPixmap pixmap(decorationSize);
55     pixmap.fill(Qt::transparent);
56 
57     QPainter painter(&pixmap);
58 
59     // Draw the tile image in the background
60     const QSizeF size = tileImage.size();
61     if (!size.isEmpty()) {
62         const qreal scaleX = decorationSize.width() / size.width();
63         const qreal scaleY = decorationSize.height() / size.height();
64         const qreal scale = std::max(scaleX, scaleY);
65         const QSizeF targetSize = size * scale;
66 
67         painter.setRenderHint(QPainter::SmoothPixmapTransform, scale < 1.0);
68         painter.drawPixmap(QRectF(decorationSize.width() - targetSize.width(),
69                                   decorationSize.height() - targetSize.height(),
70                                   targetSize.width(),
71                                   targetSize.height()),
72                            tileImage, tileImage.rect());
73     }
74 
75     // Draw the Wang color on top
76     const QColor wangColor = index.data(WangColorModel::ColorRole).value<QColor>();
77     const QPointF topRight = QPointF(pixmap.width() * 0.75, 0);
78     const QPointF bottomLeft = QPointF(0, pixmap.height() * 0.75);
79     painter.setBrush(wangColor);
80     painter.setPen(Qt::NoPen);
81     painter.drawPolygon(QVector<QPointF> { QPointF(), topRight, bottomLeft });
82     QColor border(Qt::black);
83     border.setAlpha(128);
84     painter.setPen(QPen(border, 2.0));
85     painter.drawLine(topRight, bottomLeft);
86 
87     QStyledItemDelegate::initStyleOption(option, index);
88 
89     // Reset the icon
90     option->features |= QStyleOptionViewItem::HasDecoration;
91     option->decorationSize = decorationSize;
92     option->icon = pixmap;
93 }
94 
95 } // anonymous namespace
96 
WangColorView(QWidget * parent)97 WangColorView::WangColorView(QWidget *parent)
98     : QTreeView(parent)
99 {
100     setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
101     setHeaderHidden(true);
102     setItemsExpandable(false);
103     setIndentation(0);
104     setUniformRowHeights(true);
105     setItemDelegate(new WangColorDelegate(this));
106 }
107 
~WangColorView()108 WangColorView::~WangColorView()
109 {
110 }
111 
setTileSize(QSize size)112 void WangColorView::setTileSize(QSize size)
113 {
114     static const int minSize = 16;
115     static const int maxSize = Utils::dpiScaled(32);
116     setIconSize(QSize(qBound(minSize, size.width(), maxSize),
117                       qBound(minSize, size.height(), maxSize)));
118 }
119 
setReadOnly(bool readOnly)120 void WangColorView::setReadOnly(bool readOnly)
121 {
122     static const auto defaultTriggers = editTriggers();
123     mReadOnly = readOnly;
124     setEditTriggers(readOnly ? NoEditTriggers : defaultTriggers);
125 }
126 
contextMenuEvent(QContextMenuEvent * event)127 void WangColorView::contextMenuEvent(QContextMenuEvent *event)
128 {
129     if (mReadOnly)
130         return;
131 
132     const QAbstractProxyModel *proxyModel = static_cast<QAbstractProxyModel *>(model());
133     const WangColorModel *wangColorModel = static_cast<WangColorModel *>(proxyModel->sourceModel());
134     const QModelIndex filterModelIndex = indexAt(event->pos());
135 
136     if (!wangColorModel || !filterModelIndex.isValid())
137         return;
138 
139     const QModelIndex index = proxyModel->mapToSource(filterModelIndex);
140     mClickedWangColor = wangColorModel->wangColorAt(index);
141 
142     QMenu menu;
143 
144     QAction *pickColor = menu.addAction(tr("Pick Custom Color"));
145     connect(pickColor, &QAction::triggered,
146             this, &WangColorView::pickColor);
147 
148     menu.exec(event->globalPos());
149 }
150 
pickColor()151 void WangColorView::pickColor()
152 {
153     QColorDialog *colorPicker = new QColorDialog(this);
154     colorPicker->setAttribute(Qt::WA_DeleteOnClose);
155     colorPicker->setCurrentColor(mClickedWangColor->color());
156     connect(colorPicker, &QColorDialog::colorSelected,
157             this, &WangColorView::colorPicked);
158 
159     colorPicker->open();
160 }
161 
colorPicked(const QColor & color)162 void WangColorView::colorPicked(const QColor &color)
163 {
164     if (!mClickedWangColor)
165         return;
166 
167     if (mClickedWangColor->color() != color)
168         emit wangColorColorPicked(mClickedWangColor.data(), color);
169 
170     mClickedWangColor.clear();
171 }
172 
173 #include "moc_wangcolorview.cpp"
174