1 /*
2  * wangtemplateview.cpp
3  * Copyright 2017, Benjamin Trotter <bdtrotte@ucsc.edu>
4  * Copyright 2020, Thorbjørn Lindeijer <bjorn@lindeijer.nl>
5  *
6  * This file is part of Tiled.
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU General Public License as published by the Free
10  * Software Foundation; either version 2 of the License, or (at your option)
11  * any later version.
12  *
13  * This program is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
16  * more details.
17  *
18  * You should have received a copy of the GNU General Public License along with
19  * this program. If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include "wangtemplateview.h"
23 
24 #include "utils.h"
25 #include "wangoverlay.h"
26 #include "wangset.h"
27 #include "wangtemplatemodel.h"
28 #include "zoomable.h"
29 
30 #include <QAbstractItemDelegate>
31 #include <QApplication>
32 #include <QCoreApplication>
33 #include <QGesture>
34 #include <QGestureEvent>
35 #include <QPainter>
36 #include <QPinchGesture>
37 #include <QWheelEvent>
38 
39 using namespace Tiled;
40 
41 namespace {
42 
43 /**
44  * The delegate for drawing the Wang tile templates.
45  */
46 class WangTemplateDelegate : public QAbstractItemDelegate
47 {
48 public:
WangTemplateDelegate(WangTemplateView * wangtemplateView,QObject * parent=nullptr)49     WangTemplateDelegate(WangTemplateView *wangtemplateView, QObject *parent = nullptr)
50         : QAbstractItemDelegate(parent)
51         , mWangTemplateView(wangtemplateView)
52     {}
53 
54     void paint(QPainter *painter,
55                const QStyleOptionViewItem &option,
56                const QModelIndex &index) const override;
57 
58     QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
59 
60 private:
61     WangTemplateView *mWangTemplateView;
62 };
63 
paint(QPainter * painter,const QStyleOptionViewItem & option,const QModelIndex & index) const64 void WangTemplateDelegate::paint(QPainter *painter,
65                                  const QStyleOptionViewItem &option,
66                                  const QModelIndex &index) const
67 {
68     const WangTemplateModel *model = static_cast<const WangTemplateModel*>(index.model());
69     const WangId wangId = model->wangIdAt(index);
70     if (!wangId)
71         return;
72 
73     painter->setClipRect(option.rect);
74 
75     if (WangSet *wangSet = mWangTemplateView->wangSet())
76         paintWangOverlay(painter, wangId, *wangSet, option.rect, WO_Outline);
77 
78     // Highlight currently selected tile.
79     if (mWangTemplateView->currentIndex() == index) {
80         QColor high = option.palette.highlight().color();
81         painter->setBrush(Qt::NoBrush);
82         painter->setPen(QPen(high, 4));
83         painter->drawRect(option.rect);
84     }
85 
86     // Shade tile if used already
87     if (mWangTemplateView->wangIdIsUsed(wangId)) {
88         painter->setBrush(QColor(0,0,0,100));
89         painter->setPen(Qt::NoPen);
90         painter->drawRect(option.rect.adjusted(2,2,-2,-2));
91     }
92 }
93 
sizeHint(const QStyleOptionViewItem & option,const QModelIndex & index) const94 QSize WangTemplateDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
95 {
96     Q_UNUSED(option)
97     Q_UNUSED(index)
98 
99     return QSize(32 * mWangTemplateView->scale(),
100                  32 * mWangTemplateView->scale());
101 }
102 
103 } // anonymous namespace
104 
WangTemplateView(QWidget * parent)105 WangTemplateView::WangTemplateView(QWidget *parent)
106     : QListView(parent)
107     , mZoomable(new Zoomable(this))
108 {
109     setWrapping(true);
110     setFlow(QListView::LeftToRight);
111     setResizeMode(QListView::Adjust);
112     setSelectionMode(QAbstractItemView::SingleSelection);
113     setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
114     setItemDelegate(new WangTemplateDelegate(this, this));
115     setUniformItemSizes(true);
116 
117     connect(mZoomable, &Zoomable::scaleChanged,
118             this, &WangTemplateView::adjustScale);
119 }
120 
scale() const121 qreal WangTemplateView::scale() const
122 {
123     return mZoomable->scale();
124 }
125 
wangSet() const126 WangSet *WangTemplateView::wangSet() const
127 {
128     const WangTemplateModel *model = wangTemplateModel();
129     return model ? model->wangSet() : nullptr;
130 }
131 
wangIdIsUsed(WangId wangId) const132 bool WangTemplateView::wangIdIsUsed(WangId wangId) const
133 {
134     if (WangSet *set = wangSet())
135         return set->wangIdIsUsed(wangId);
136 
137     return false;
138 }
139 
event(QEvent * event)140 bool WangTemplateView::event(QEvent *event)
141 {
142     if (event->type() == QEvent::Gesture) {
143         QGestureEvent *gestureEvent = static_cast<QGestureEvent *>(event);
144         if (QGesture *gesture = gestureEvent->gesture(Qt::PinchGesture))
145             mZoomable->handlePinchGesture(static_cast<QPinchGesture *>(gesture));
146     } else if (event->type() == QEvent::ShortcutOverride) {
147         auto keyEvent = static_cast<QKeyEvent*>(event);
148         if (Utils::isZoomInShortcut(keyEvent) ||
149                 Utils::isZoomOutShortcut(keyEvent) ||
150                 Utils::isResetZoomShortcut(keyEvent)) {
151             event->accept();
152             return true;
153         }
154     }
155 
156     return QListView::event(event);
157 }
158 
keyPressEvent(QKeyEvent * event)159 void WangTemplateView::keyPressEvent(QKeyEvent *event)
160 {
161     if (Utils::isZoomInShortcut(event)) {
162         mZoomable->zoomIn();
163         return;
164     }
165     if (Utils::isZoomOutShortcut(event)) {
166         mZoomable->zoomOut();
167         return;
168     }
169     if (Utils::isResetZoomShortcut(event)) {
170         mZoomable->resetZoom();
171         return;
172     }
173     return QListView::keyPressEvent(event);
174 }
175 
wheelEvent(QWheelEvent * event)176 void WangTemplateView::wheelEvent(QWheelEvent *event)
177 {
178     if (event->modifiers() & Qt::ControlModifier &&
179             event->angleDelta().y())
180     {
181         mZoomable->handleWheelDelta(event->angleDelta().y());
182         return;
183     }
184 
185     QListView::wheelEvent(event);
186 }
187 
adjustScale()188 void WangTemplateView::adjustScale()
189 {
190     scheduleDelayedItemsLayout();
191 }
192 
193 #include "moc_wangtemplateview.cpp"
194